mirror of
https://github.com/lexmount/moli.git
synced 2026-10-08 08:01:01 +00:00
fix(workers): inherit EventTarget across worker global scopes
Build dedicated, shared and service worker globals from the native EventTarget template before context creation. Preserve immutable global prototype chains and explicitly link the Web IDL interface constructors. Cover all three worker kinds with the same regression fixture, including inherited when(), event delivery, cancellation, global error reporting, receiver branding and replacement of the public EventTarget constructor. Promote the passing worker inspect and immutable-prototype WPT cases.
This commit is contained in:
@@ -4556,6 +4556,7 @@ dom/observable/tentative/observable-first.any.js?moli-wpt-any=window
|
||||
dom/observable/tentative/observable-forEach.any.js?moli-wpt-any=dedicatedworker
|
||||
dom/observable/tentative/observable-forEach.any.js?moli-wpt-any=window
|
||||
dom/observable/tentative/observable-forEach.window.js?moli-wpt-script=window
|
||||
dom/observable/tentative/observable-inspect.any.js?moli-wpt-any=dedicatedworker
|
||||
dom/observable/tentative/observable-inspect.any.js?moli-wpt-any=window
|
||||
dom/observable/tentative/observable-last.any.js?moli-wpt-any=dedicatedworker
|
||||
dom/observable/tentative/observable-last.any.js?moli-wpt-any=window
|
||||
@@ -10020,6 +10021,7 @@ webidl/ecmascript-binding/constructors.html
|
||||
webidl/ecmascript-binding/default-iterator-object.html
|
||||
webidl/ecmascript-binding/default-toJSON-cross-realm.html
|
||||
webidl/ecmascript-binding/es-exceptions/exceptions.html
|
||||
webidl/ecmascript-binding/global-immutable-prototype.any.js?moli-wpt-any=dedicatedworker
|
||||
webidl/ecmascript-binding/global-object-implicit-this-value-cross-realm.html
|
||||
webidl/ecmascript-binding/has-instance.html
|
||||
webidl/ecmascript-binding/interface-object-set-receiver.html
|
||||
|
||||
@@ -276,7 +276,7 @@ async fn worker_global_prototype_chains_inherit_event_target_and_are_immutable()
|
||||
format!(
|
||||
r#"
|
||||
const result = ({fixture})({interface:?});
|
||||
if (result.checks !== 46 || result.failures.length) {{
|
||||
if (result.checks !== 61 || result.failures.length) {{
|
||||
throw new Error(JSON.stringify(result));
|
||||
}}
|
||||
"#
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
check(Object.prototype.toString.call(WorkerGlobalScope.prototype) === '[object WorkerGlobalScope]', 'worker tag');
|
||||
check(throwsTypeError(() => new scopeConstructor()), 'specific illegal constructor');
|
||||
check(throwsTypeError(() => new WorkerGlobalScope()), 'worker illegal constructor');
|
||||
check(typeof self.when === 'function', 'inherited when exposed');
|
||||
check(self.when === eventTarget.prototype.when, 'inherited when identity');
|
||||
for (const value of chain.slice(0, 3)) {
|
||||
check(!Object.hasOwn(value, 'when'), 'when is inherited');
|
||||
}
|
||||
test('prototype extension', () => {
|
||||
const key = Symbol('EventTarget extension');
|
||||
eventTarget.prototype[key] = 17;
|
||||
@@ -56,6 +61,33 @@
|
||||
self.dispatchEvent(new Event('worker-prototype-probe'));
|
||||
check(values.length === 1, 'global listener removal still works');
|
||||
});
|
||||
test('inherited observable events', () => {
|
||||
const controller = new AbortController();
|
||||
const values = [];
|
||||
const observable = self.when('worker-prototype-probe');
|
||||
check(observable instanceof Observable, 'when returns Observable');
|
||||
observable.subscribe(event => values.push(event), {signal: controller.signal});
|
||||
const first = new Event('worker-prototype-probe');
|
||||
self.dispatchEvent(first);
|
||||
check(values.length === 1 && values[0] === first, 'when receives global dispatch');
|
||||
check(first.target === self, 'dispatched event target is global');
|
||||
controller.abort();
|
||||
self.dispatchEvent(new Event('worker-prototype-probe'));
|
||||
check(values.length === 1, 'abort removes global listener');
|
||||
});
|
||||
test('global error reporting', () => {
|
||||
const errors = [];
|
||||
const marker = new Error('inspector abort');
|
||||
const controller = new AbortController();
|
||||
self.when('error').take(1).subscribe(event => {
|
||||
errors.push(event.error);
|
||||
event.preventDefault();
|
||||
});
|
||||
new Observable(subscriber => subscriber.next(1))
|
||||
.inspect({abort() { throw marker; }})
|
||||
.subscribe(() => controller.abort(), {signal: controller.signal});
|
||||
check(errors.length === 1 && errors[0] === marker, 'when receives reported callback error');
|
||||
});
|
||||
test('receiver branding', () => {
|
||||
let conversions = 0;
|
||||
let traps = 0;
|
||||
@@ -67,6 +99,7 @@
|
||||
getPrototypeOf() { traps++; throw new Error('author proxy trap'); }
|
||||
}), revoked.proxy]) {
|
||||
check(throwsTypeError(() => eventTarget.prototype.addEventListener.call(receiver, type, () => {})), 'addEventListener rejects forged/proxy receiver');
|
||||
check(throwsTypeError(() => eventTarget.prototype.when.call(receiver, type)), 'when rejects forged/proxy receiver');
|
||||
}
|
||||
check(conversions === 0, 'brand checked before conversion');
|
||||
check(traps === 0, 'brand checked without proxy traps');
|
||||
@@ -76,6 +109,7 @@
|
||||
try {
|
||||
check(Object.getPrototypeOf(WorkerGlobalScope.prototype) === eventTarget.prototype, 'prototype keeps intrinsic EventTarget');
|
||||
check(Object.getPrototypeOf(WorkerGlobalScope) === eventTarget, 'constructor keeps intrinsic EventTarget');
|
||||
check(self.when === eventTarget.prototype.when, 'when keeps intrinsic method');
|
||||
} finally { self.EventTarget = eventTarget; }
|
||||
});
|
||||
return {checks, failures};
|
||||
|
||||
Reference in New Issue
Block a user