diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_workers.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_workers.inc index ef1f927c..c74f5d17 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_workers.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_workers.inc @@ -35,6 +35,56 @@ static constexpr size_t maximum_worker_errors = 16U; static constexpr size_t maximum_worker_error_length = 4096U; + void install_dedicated_worker_global_scope(v8::Local realm) { + constexpr std::string_view source = R"JS( + (() => { + function WorkerGlobalScope() { + throw new TypeError('Illegal constructor'); + } + function DedicatedWorkerGlobalScope() { + throw new TypeError('Illegal constructor'); + } + Object.setPrototypeOf( + WorkerGlobalScope.prototype, + EventTarget.prototype); + Object.setPrototypeOf(WorkerGlobalScope, EventTarget); + Object.setPrototypeOf( + DedicatedWorkerGlobalScope.prototype, + WorkerGlobalScope.prototype); + Object.setPrototypeOf( + DedicatedWorkerGlobalScope, + WorkerGlobalScope); + Object.defineProperty( + WorkerGlobalScope.prototype, + Symbol.toStringTag, + {value:'WorkerGlobalScope', configurable:true}); + Object.defineProperty( + DedicatedWorkerGlobalScope.prototype, + Symbol.toStringTag, + {value:'DedicatedWorkerGlobalScope', configurable:true}); + Object.defineProperties(globalThis, { + WorkerGlobalScope: { + value: WorkerGlobalScope, + writable: true, + configurable: true + }, + DedicatedWorkerGlobalScope: { + value: DedicatedWorkerGlobalScope, + writable: true, + configurable: true + } + }); + Object.setPrototypeOf( + globalThis, + DedicatedWorkerGlobalScope.prototype); + })(); + )JS"; + auto script = v8::Script::Compile( + realm, + js_dom_string(isolate, std::string(source))).ToLocalChecked(); + script->Run(realm).ToLocalChecked(); + } + void install_worker_constructor(v8::Local local_context) { auto worker_constructor = v8::Function::New( local_context, @@ -342,6 +392,7 @@ global->Set(realm,js_string(child->isolate,"name"),js_dom_string(child->isolate,worker_name)).Check(); global->Delete(realm,js_string(child->isolate,"document")).FromMaybe(false); global->Delete(realm,js_string(child->isolate,"window")).FromMaybe(false); + child->install_dedicated_worker_global_scope(realm); std::string source,resolved,error; if (embedded_source) { source = *embedded_source; resolved = url; } if(!embedded_source && !child->load_text_resource(url,{},WEBSCENE_RESOURCE_SCRIPT,source,resolved))report("Unable to load worker: "+url); diff --git a/experiments/WebScene.NativeEngine.Probe/tests/hybrid_v8_runtime_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/hybrid_v8_runtime_tests.cpp index 5ecaeb7b..dc1ff51b 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/hybrid_v8_runtime_tests.cpp +++ b/experiments/WebScene.NativeEngine.Probe/tests/hybrid_v8_runtime_tests.cpp @@ -207,6 +207,14 @@ void test_worker_message_port_transfer_and_throughput() { if (!(port instanceof MessagePort)) throw Error('Transferred value is not a MessagePort'); if (typeof document !== 'undefined' || typeof importScripts !== 'function') throw Error('Worker global shape changed'); + const isWebWorker = typeof self === 'object' && self.constructor + && self.constructor.name === 'DedicatedWorkerGlobalScope'; + if (!isWebWorker + || !(self instanceof DedicatedWorkerGlobalScope) + || !(self instanceof WorkerGlobalScope) + || !(self instanceof EventTarget) + || Object.prototype.toString.call(self) !== '[object DedicatedWorkerGlobalScope]') + throw Error('DedicatedWorkerGlobalScope identity changed'); port.onmessage = message => port.postMessage(message.data + 1); port.start(); }`; diff --git a/tests/WebPlatformSubset/contracts/module-worker-clone.html b/tests/WebPlatformSubset/contracts/module-worker-clone.html index b33c6c5a..1dd8983b 100644 --- a/tests/WebPlatformSubset/contracts/module-worker-clone.html +++ b/tests/WebPlatformSubset/contracts/module-worker-clone.html @@ -32,6 +32,11 @@ if(messages.length===2){ record('Worker has isolated module state and transfers output',messages[0][0]===42); record('Worker transfer detaches sender and has no document',messages[1].detached&&messages[1].documentType==='undefined'); + record('Worker exposes DedicatedWorkerGlobalScope identity', + messages[1].isWebWorker&&messages[1].dedicatedIdentity + &&messages[1].workerIdentity&&messages[1].eventTargetIdentity + &&messages[1].stringTag==='[object DedicatedWorkerGlobalScope]' + &&messages[1].workerScopeConstructorsReject); w.terminate();w.terminate();finish(); } }; diff --git a/tests/WebPlatformSubset/contracts/resources/aureon/worker.js b/tests/WebPlatformSubset/contracts/resources/aureon/worker.js index c5b7f6f9..8dd2adf0 100644 --- a/tests/WebPlatformSubset/contracts/resources/aureon/worker.js +++ b/tests/WebPlatformSubset/contracts/resources/aureon/worker.js @@ -1,6 +1,21 @@ -import { count } from './module.js'; +const isWebWorker = typeof self === 'object' && self.constructor + && self.constructor.name === 'DedicatedWorkerGlobalScope'; +const { count } = await import(isWebWorker ? './module.js' : 'module'); self.onmessage = e => { e.data[0] += count; postMessage(e.data, [e.data.buffer]); - postMessage({ detached: e.data.byteLength === 0, documentType: typeof document }); + let workerScopeConstructorsReject = false; + try { new DedicatedWorkerGlobalScope(); } catch (error) { + workerScopeConstructorsReject = error instanceof TypeError; + } + postMessage({ + detached: e.data.byteLength === 0, + documentType: typeof document, + isWebWorker, + dedicatedIdentity: self instanceof DedicatedWorkerGlobalScope, + workerIdentity: self instanceof WorkerGlobalScope, + eventTargetIdentity: self instanceof EventTarget, + stringTag: Object.prototype.toString.call(self), + workerScopeConstructorsReject + }); };