From 2e2f2c525f68dfe120ec5aff1290ed6dc3503f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 16 Sep 2026 22:03:59 +0200 Subject: [PATCH] Expose Worker in hydrated iframe realms --- .../native/webscene_v8_runtime.cpp | 9 +-- .../webscene_v8_runtime_cache_and_frames.inc | 1 + .../native/webscene_v8_runtime_workers.inc | 31 ++++++++ .../tests/hybrid_v8_runtime_tests.cpp | 3 + .../native_v8_runtime_inspector_tests.inc | 4 +- .../tests/native_v8_runtime_tests.cpp | 10 +++ .../iframe-worker-extension-host.html | 73 +++++++++++++++++++ .../iframe-worker-extension-host-module.js | 7 ++ .../webscene-aureon-runtime-profile.json | 11 +++ 9 files changed, 139 insertions(+), 10 deletions(-) create mode 100644 tests/WebPlatformSubset/contracts/iframe-worker-extension-host.html create mode 100644 tests/WebPlatformSubset/contracts/resources/aureon/iframe-worker-extension-host-module.js diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.cpp b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.cpp index 7df4a7f0f..9aad03ef2 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.cpp +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.cpp @@ -3745,14 +3745,7 @@ struct v8_dom_runtime::implementation final { local_context->Global()->Set(local_context, js_string(isolate, "structuredClone"), v8::Function::New(local_context, structured_clone, {}, 1).ToLocalChecked()).Check(); install_performance_timeline(local_context); - auto worker_constructor=v8::Function::New(local_context, worker_construct, {}, 1).ToLocalChecked(); - v8::Local event_target,worker_prototype,event_prototype; - if(local_context->Global()->Get(local_context,js_string(isolate,"EventTarget")).ToLocal(&event_target) - &&event_target->IsFunction() - &&worker_constructor->Get(local_context,js_string(isolate,"prototype")).ToLocal(&worker_prototype) - &&event_target.As()->Get(local_context,js_string(isolate,"prototype")).ToLocal(&event_prototype)) - worker_prototype.As()->SetPrototype(local_context,event_prototype).FromMaybe(false); - local_context->Global()->Set(local_context, js_string(isolate, "Worker"),worker_constructor).Check(); + install_worker_constructor(local_context); install_clipboard_api(local_context); install_websocket_globals(local_context); install_editor_web_platform_globals(local_context); diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_cache_and_frames.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_cache_and_frames.inc index 4488d7a12..b246d3eb1 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_cache_and_frames.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_cache_and_frames.inc @@ -1900,6 +1900,7 @@ install_websocket_globals(local_context); install_editor_web_platform_globals(local_context); install_message_channel(local_context); + install_worker_constructor(local_context); install_tree_walker_platform(local_context); install_custom_elements_platform(local_context); install_fetch_globals(local_context); 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 5b230a3f4..ef1f927cc 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,35 @@ static constexpr size_t maximum_worker_errors = 16U; static constexpr size_t maximum_worker_error_length = 4096U; + void install_worker_constructor(v8::Local local_context) { + auto worker_constructor = v8::Function::New( + local_context, + worker_construct, + {}, + 1).ToLocalChecked(); + v8::Local event_target; + v8::Local worker_prototype; + v8::Local event_prototype; + if (local_context->Global()->Get( + local_context, + js_string(isolate, "EventTarget")).ToLocal(&event_target) + && event_target->IsFunction() + && worker_constructor->Get( + local_context, + js_string(isolate, "prototype")).ToLocal(&worker_prototype) + && event_target.As()->Get( + local_context, + js_string(isolate, "prototype")).ToLocal(&event_prototype)) { + worker_prototype.As()->SetPrototype( + local_context, + event_prototype).FromMaybe(false); + } + local_context->Global()->Set( + local_context, + js_string(isolate, "Worker"), + worker_constructor).Check(); + } + static v8::MaybeLocal worker_transfer( const v8::FunctionCallbackInfo& info) { if (info.Length() < 2) return v8::Undefined(info.GetIsolate()); @@ -337,6 +366,8 @@ auto realm=child->context.Get(child->isolate);v8::Context::Scope scope(realm); if(packet&&!child->dispatch_worker_message(realm,realm->Global(),&*packet))report(child->last_error); child->drain_tasks(); + child->perform_microtask_checkpoint(); + if (!child->promote_pending_promise_error()) report(child->last_error); } {std::lock_guard lock(raw->mutex);raw->running_isolate=nullptr;} child->isolate->CancelTerminateExecution(); 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 cbb453367..fd3d5d74b 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/hybrid_v8_runtime_tests.cpp +++ b/experiments/WebScene.NativeEngine.Probe/tests/hybrid_v8_runtime_tests.cpp @@ -250,6 +250,9 @@ void test_iframe_worker_extension_host_port_bootstrap() { const child = frame.contentDocument; child.open(); child.write(` diff --git a/tests/WebPlatformSubset/contracts/resources/aureon/iframe-worker-extension-host-module.js b/tests/WebPlatformSubset/contracts/resources/aureon/iframe-worker-extension-host-module.js new file mode 100644 index 000000000..a61ce3148 --- /dev/null +++ b/tests/WebPlatformSubset/contracts/resources/aureon/iframe-worker-extension-host-module.js @@ -0,0 +1,7 @@ +postMessage('worker-started'); +self.onmessage = event => { + const port = event.data.port; + if (!(port instanceof MessagePort)) throw Error('worker port transfer failed'); + port.onmessage = message => port.postMessage(message.data + 1); + port.start(); +}; diff --git a/tests/WebPlatformSubset/webscene-aureon-runtime-profile.json b/tests/WebPlatformSubset/webscene-aureon-runtime-profile.json index 37a6744b2..c04ec0854 100644 --- a/tests/WebPlatformSubset/webscene-aureon-runtime-profile.json +++ b/tests/WebPlatformSubset/webscene-aureon-runtime-profile.json @@ -44,6 +44,17 @@ "reason": "Focused Code OSS iframe bootstrap candidate matching the extension-host MessagePort handoff.", "nativeNavigation": true }, + { + "path": "contracts/iframe-worker-extension-host.html", + "type": "contract", + "capabilities": [ + "iframe-worker-constructor", + "module-workers", + "messageport-transfer" + ], + "reason": "Code OSS-shaped regression proving its hydrated extension-host iframe can construct a module Worker and bridge the transferred bootstrap port.", + "nativeNavigation": true + }, { "path": "contracts/worker-termination-queue-messageerror.html", "type": "contract",