Repository navigation
Implement Worker, transferable MessagePort, and iframe contexts for Code OSS #81
Description
Activity
Packaged-app validation found a narrower remaining blocker after the initial Worker/MessagePort work landed:
webWorkerExtensionHostIframe.htmlran in a hydrated iframe realm whereWorkerwas absent. Diagnostics showedReferenceError: Worker is not definedatcreateWorker, followed byLocalWebWorkertermination code 81. The earlier focused WPT only constructed Worker in the top-level realm, so it did not cover this app path.Draft PR #95 now contains commit
cbc4b487on top of consolidation96876a98. It installs Worker per iframe realm, preserves realm-local EventTarget identity/base URL behavior, and advances module-worker microtasks for Code OSS's blob module with top-levelawait import(...).New acceptance coverage mirrors Code OSS: iframe -> blob module Worker -> dynamic import -> transferred MessagePort bridge. Current validation passes 28/28 graphics native tests, 25/25 non-graphics native tests, and 6/6 Aureon candidate documents with 31/31 assertions. The focused hybrid suite remains below one second while retaining the 10,000-message and bounded-queue gates.
Packaged Code OSS must still be rebuilt with the updated runtime to close the issue acceptance item and verify that the code-81 diagnostic is absent.
Packaged AppScene acceptance now passes with this commit.
I copied the validated graphics runtime into a duplicate of the current
native-41cf43ccCode OSS AppScene bundle, ad-hoc signed the duplicate, and ran the two-process IndexedDB persistence smoke against it. Write and restart/read both passed in 9.196 seconds on different loopback origins (:55920then:55976).Neither native report contains
Worker is not defined, LocalWebWorker code 81, unexpected worker termination, or extension-host startup errors. This closes the concrete packaged-app regression that prompted the follow-up.Evidence:
- summary:
artifacts/iframe-worker-appscene-a2c15a5b/smoke/indexeddb-persistence-summary.json— SHA-256dc8698d903603e3fed4fe36749d4f1bdc26114e188d29cc8b50bde85369cc24a - write report — SHA-256
408b01d52df36545b6063be9eae62ea04aab4cf08b6f30181121d08c18e10a10 - restart/read report — SHA-256
858173598a7da4261e84d50bca303b20edd63bae4f271cd0372a55e6fbe8a645 - embedded runtime — SHA-256
662a83d65b153a65628c0e4bd1a58d71d71ef3cb350894821ef3dcf2ec470795
- summary:
The iframe Worker fix remains validated after correcting a timing race in the existing detached-frame Inspector regression. The failed Linux package job counted a legal MessagePort delivery before iframe removal as though it ran after context destruction. Focused commit
274bb1dbnow gates the counter at the synchronous removal boundary; consolidation commitd807f7abcarries the identical patch. Local gates pass: 10 repeated native regressions, full graphics CTest 28/28, and Aureon Worker/iframe WPT 6/6 documents (31/31 subtests). Exact-head CI is running again.Native GitHub Stack #176 is merged atomically at shared commit
b6dff40327c9a43562c743b585ca465b4e2f0a7c:- Implement Worker, transferable MessagePort, and iframe transport #172 — Worker, transferable MessagePort, structured clone, iframe transport, lifecycle and resource bounds
- Expose Worker in hydrated iframe realms #173 — Worker in hydrated iframe realms
- Expose DedicatedWorkerGlobalScope identity #174 — DedicatedWorkerGlobalScope / WorkerGlobalScope identity
- Pin focused upstream MessagePort contracts #175 — pinned upstream MessagePort WPT cumulative top
Cumulative-top evidence: precompiled/V8 run 35148591616 passed; native document/compiler and installed-SDK run 35148587878 passed. Fresh local hybrid and Inspector-detach filters passed, as did eight focused Worker/iframe/MessagePort documents with 35/35 assertions. Throughput, queue/byte/binding admission, termination, navigation cleanup, and warmed memory bounds are included. Lower-layer and post-merge duplicate jobs were canceled.
The runtime implementation is complete on
main. This issue remains open for the deferred packaged Code OSS acceptance: Monaco language-worker request/response, browser-extension iframe → module Worker → transferred bootstrap port, UI responsiveness, visible admission/origin failures, and clean navigation/shutdown without late callbacks.Current exact-main/package audit (WebScene
24db1a2f2c6b1865977eb613598ab6932e61236d):- Native Stack #176 merged PRs Implement Worker, transferable MessagePort, and iframe transport #172 → Expose Worker in hydrated iframe realms #173 → Expose DedicatedWorkerGlobalScope identity #174 → Pin focused upstream MessagePort contracts #175 atomically at
b6dff40327c9a43562c743b585ca465b4e2f0a7c. - Follow-up PR Share Blob URLs across nested workers #208 merged nested Blob URL ownership at
aaf4eae053ab33d889ea2b599988aaf18f1839ee; PR Share authentication cookies with dedicated workers #210 merged authenticated same-origin worker requests at7c74a02e23fc2fd8a81da3c136227b3ced93c52c. - The final macOS package built from this main passes smoke in 7.302 seconds with zero LocalWebWorker failure diagnostics. It no longer reports
Worker is not defined, authenticatedForbidden./EOF failures, unexpected LocalWebWorker termination, or Monaco's main-thread worker fallback.
The issue remains open because the current packaged smoke edits a plaintext untitled model and does not make a strict Monaco language-worker RPC or explicitly verify the browser extension-host bootstrap response. Those are acceptance-test gaps, not an identified missing WebScene primitive. A focused packaged probe is being added before closure.
- Native Stack #176 merged PRs Implement Worker, transferable MessagePort, and iframe transport #172 → Expose Worker in hydrated iframe realms #173 → Expose DedicatedWorkerGlobalScope identity #174 → Pin focused upstream MessagePort contracts #175 atomically at
PR #226 merged the remaining product-neutral editor-worker contract as
c979985de705fc0f9ef5898ad29487a6358fd257.Evidence on the exact PR head
df710505a20dbe118cf2bf48c678c7f81db1ace7:- Chromium 153 passed all 8 assertions across
editor-worker-rpc.htmland the correctediframe-worker-extension-host.htmloracle. The latter now waits for both iframe bootstrap and module-worker readiness before transferring its port. - The Linux native package job in run 35192307906, job 105107518343, passed
webscene_hybrid_v8_runtime_testsin 1.66s andwebscene_worker_object_url_testsin 0.51s. - The macOS native job 105107518377 independently passed the same two gates in 2.18s and 0.30s.
- Both jobs stayed red only because the unrelated existing
webscene_native_engine_testscatalog check failed onHostBridge.Services; the two worker gates directly related to this change passed on both runners.
The contract now covers the Code OSS worker protocol shape: queued pre-ready traffic,
vscode-worker-ready,$initialize,$computeStringDiff, exact request/reply correlation, a serialized missing-method rejection, visiblerole=alertfailure UI, and main-document timer progress while the worker computes. Issue #225 closed with the merge.Keeping #81 open: this proves the reusable WebScene browser/native worker path, but the packaged Code OSS application still needs downstream evidence from its actual
IEditorWorkerServiceand browser extension-worker paths, including the user-visible failure state.- Chromium 153 passed all 8 assertions across
Packaged acceptance update — 2026-09-17
The fresh VS Code OSS 1.137.0 payload on AppScene
a20cf890+ WebScene7cd66b3enow reaches the real same-origin browser extension-host iframe and transfers itsMessagePort. The query-gated observer records:{ "extensionIframeBootstrap": true, "extensionPortTransferred": true, "extensionPortObserverInstalled": false }The observer fails closed because WebScene creates each native port wrapper with an own writable data property:
wrapper->Set(realm, js_string(isolate, "onmessage"), v8::Null(isolate)).Check();
MessagePort.prototypehas no browser-compatibleonmessageaccessor descriptor. This differs from the Web IDL surface and prevents a synchronous observer from wrapping VS Code's laterport.onmessage = listenerassignment without starting/consuming the port or adding a competing listener.Proposed focused completion:
- expose
onmessageandonmessageerroras accessor properties onMessagePort.prototypewith per-instance handler storage; - retain current queued delivery, transfer ownership,
start,close, and EventTarget behavior; - add descriptor-shape and assignment/reassignment/null tests across the source and transferred wrapper paths;
- rerun the packaged sequence: iframe bootstrap → transferred port → observer installed → Ready byte
2→ Initialized byte1.
- expose
- addedvscode-oss/plannedPlanned for the AppScene/WebScene VS Code OSS integrationPlanned for the AppScene/WebScene VS Code OSS integration
on Sep 17, 2026 Focused iframe/webview prerequisite #253 is now scheduled under release epic #227.
The unchanged Code OSS Markdown preview fails before its webview initializes because
webviewElement.tscreates an iframe and synchronously callselement.sandbox.add(...); current WebScene exposesHTMLIFrameElementbranding but returnsundefinedforsandbox. #253 owns the reusable liveDOMTokenList, sandbox token/navigation/security semantics, WPT/browser/native/performance coverage, and exact README visual/resource acceptance.#81 retains Worker, transferable MessagePort, iframe-context lifecycle, extension-host, and packaged Monaco scope. The focused sandbox fix should merge independently, then #81 consumes it in its broader iframe acceptance.
The complete Code OSS webview audit is now tracked by child epic #264. This issue remains authoritative for Worker execution, transferable MessagePort/structured-clone behavior, the missing prototype
onmessageaccessor, and browser extension-host/Monaco acceptance.#253, #265, #266, #267, and #268 own the separate sandbox, ServiceWorker, resource broker, nested-document security/lifecycle, and webview interaction stages. #81 is consumed by #265–#268; none of those tickets duplicates its port/worker implementation scope.
The unchanged Code OSS remote-workspace trace from #280/#252 supplies the remaining packaged extension-host timing evidence for this issue.
Exact candidate inputs: WebScene local
0f92cf47on mainaa786c0e, AppScene1420e227, Code OSS645f29cc, Node 24.18.1. The #287 task-source fairness candidate passes its direct 256-frame FileReader gate but does not materially change this product timeline.Correlating the query-gated workspace observer's
performance.now()values with the terminal console timestamp gives:- workspace observer start: inferred console timestamp 1789655981628.9
- direct
IFileService.resolvestart: 1789655985605.0 - Code OSS reports “Extension host (Remote) is unresponsive”: 1789655989676
[AgentHost:remote] Connected: 1789655993265- exact five-child resolve completes: inferred 1789655993354.9, about 90 ms after AgentHost connected
- first five Explorer rows paint / terminal observer record: 1789655993417, about 152 ms after AgentHost connected
The earlier extension-host socket transport itself opened after 1,247 ms, but the remote AgentHost did not become connected until roughly 16.6 seconds after the socket-create request. Provider/Explorer output becomes ready immediately after that connection boundary.
This makes #81's existing packaged browser extension-host/Worker/MessagePort acceptance the earliest remaining owner boundary for #280/#252. The next focused trace should retain iframe bootstrap, transferred port Ready/Initialized bytes, Worker creation/module import, remote socket open, protocol handshake milestones, and the
AgentHost:remote Connectedevent under a bounded latency/queue/lifecycle gate. It should avoid changing Code OSS behavior and coordinate with #288/PR #245 for MessagePort lifetime ownership.Product log:
/private/tmp/vscode-252-product-navigation-287-candidate.log. Functional output remained exact: selected/root URI, provider registration, five resolved/model children, five rows, and 64 Explorer DOM nodes.The exact packaged phase trace is complete and the distinct remote-handshake latency is now tracked as native subissue #289.
The local iframe/Worker/transferred-port path succeeded in order: iframe document → Worker created 8 ms; Worker → transferred port 130 ms; port → first byte 2 ms; first byte → Ready 1 ms; Ready → Initialized 708 ms. Resource fetch itself was 0.835 ms for the iframe and 2.964 ms for the 1.93 MB worker module.
The remote extension host breached the bound later: connected transport → Ready 1,177 ms and Ready → Initialized 4,379 ms. The separate AgentHost service connected 9,682 ms later; exact five-row Explorer output followed 99 ms afterward. This corrects the earlier inference that the AgentHost log itself marked extension-host readiness.
#288 remains the active-port forced-GC owner. #289 owns product-neutral remote protocol timing and consumes #81. Dependency for workspace requalification is now #289 → #280 → #252.
Local diagnostics only: vscode-demo
e6f5c6a(not pushed). Evidence:/private/tmp/vscode-252-product-extension-host-81.log; structured JSON SHA-2561a53ca07dfa317aa7fdfcd6b598617e8cfd41105770e9ad7fe082dac1172e851. Peak process-group RSS was 845,184 KiB; phase/resource queues were bounded at 21/96 and 8/64 with no dropped phase record. All app/server/helper processes are closed.Packaged receiver-boundary trace on unchanged Code OSS
645f29cc, AppScene1420e227, exact Node 24.18.1, and the current diagnostic package now separates the 4.2 s remote Ready → Initialized interval.Second workspace document:
- remote transport connected → Ready: 1,288.17 ms
- Ready → next Promise microtask: 0.116 ms
_createExtHostInitData()settle: 426.90 ms- JSON + UTF-8 serialization: 8.61 ms for 1,071,424 bytes
- init-data send → remote Initialized: 3,788.40 ms
- total Ready → Initialized: 4,224.01 ms
Resource delivery remains fast: the iframe response was 0.899 ms / 6,786 bytes and the worker module response was 2.803 ms / 1,932,148 bytes. The exact selected URI/root, provider registration, five resolved/model children, five rendered rows, and 64 maximum Explorer DOM nodes all passed functionally. Peak app process-group RSS was 837,376 KiB. The bounded run terminated its complete process group and verification found no app/server/helper descendants.
The independent 20-cycle native product-neutral oracle is also fast (Ready p95 5.944 ms; Initialized p95 5.408 ms; socket dispatch p95 5 ms; FileReader p95 2.536 ms; Promise/microtask p95 0.0078 ms; serialization p95 0.213 ms). Chromium passes the same 43/43 contract (Ready and Initialized p95 1.7 ms). Therefore no generic WebScene implementation is justified from this evidence yet. The next trace must split the receiver side after the 1.07 MB init payload is sent: receipt/parse, init-data hydration, extension scan/activation, and Initialized response.
Retained evidence:
- product log:
/private/tmp/vscode-252-product-handshake-289.log - product log SHA-256:
d85db2b31883b5f5f7e3a6747de96945c1810e792b0293a03df104a5dee0b761 - process/RSS summary:
/private/tmp/vscode-252-product-handshake-289-process.txt - native oracle:
/private/tmp/webscene-289-baseline.log - Chromium oracle:
/private/tmp/webscene-289-chrome-baseline-verify/results.json - query-gated local vscode-demo observer commit (not pushed):
10fc9aa
Exact current-package blocker — 18 September 2026
The installed-SDK Release from AppScene
d4a73888, WebScenedd39f118, and unchanged Code OSS645f29ccreaches a real workbench and now passes the corrected Monaco visual oracle:- raw model:
SCENETECH_NATIVE_EDIT_OK+ space + U+202E; - visible
.view-lines: stable marker plus Monaco's visible[U+202E]token; - editor Worker constructed;
$computeUnicodeHighlightsrequest and correlated reply complete (548.7 ms in the query-gated trace);- UI timer remains responsive.
The next fail-closed step reproduces this issue:
extensionIframeBootstrap=true, butextensionPortTransferredis absent and acceptance fails withThe browser extension host did not transfer its MessagePort.A second run with
CODEOSS_APPSCENE_EXTENSION_HOST_DIAGNOSTICS=1narrows the boundary. The local path records iframe create → src assignment → append and serveswebWorkerExtensionHostIframe.htmlwith HTTP 200 (6,786 bytes in 0.855-1.266 ms). It does not record nested document/Worker creation or port-transfer phases. The independent remote Node extension host is healthy: transport → Ready is about 1.27 s, 1.07 MiB init-data creation/serialization/send takes about 12.56 ms, and Initialized arrives about 82 ms later in the navigation run (about 37 ms in the isolated run).This is the current release blocker before MCP/reload lifecycle can execute. It is isolated from #266's ServiceWorker/Streams/CacheStorage work. Retained local evidence:
/private/tmp/vscode-demo-acceptance-extension-trace-dd39f11-d4a7388/report.json/private/tmp/vscode-demo-acceptance-extension-trace-dd39f11-d4a7388/stderr.log/private/tmp/vscode-demo-extension-host-dd39f11-d4a7388/
The next focused #81 slice should reduce the iframe bootstrap-NLS → nested Worker/port boundary on current main, add a browser/WPT oracle for the exact query-bearing iframe URL and transferred-port identity, then rerun the packaged gate. Do not assign this to #266 or the #246 CSS/icon lane.
- raw model:
Query-bearing extension-host iframe boundary fixed — merged #424
PR #424 merged to
mainate523daa11ae3ac4d06a9cbba64561ec06bd271f3from exact tested headf38ba61e592b1c1c57ad538418ee40eb5ba4917aand closed #421.The exact generic reduction explains the installed-package evidence. The fetched
webWorkerExtensionHostIframe.html?&vscodeWebWorkerExtHostId=...document did run and postedvscode.bootstrap.nls, but WebScene exposed its replaced inner Window asMessageEvent.source. Unchanged Code OSS therefore rejected the message becauseevent.source !== iframe.contentWindowand never sent Worker configuration. A second boundary appeared after correcting source identity:iframe.contentWindow.postMessage(...)still targeted the owner context. WebScene now preserves the stable iframe WindowProxy as event source and routes messages through that proxy to the current child document realm.The new regression covers the full fetched-query iframe → Blob module Worker → transferred MessagePort → reply path. Native and Chrome pass the same 7/7 assertions. The native lifecycle gate passes 20 teardown cycles at 156.10 ms p95 / 156.63 ms max, returns V8 used heap to baseline after low-memory collection, and stays within native-node, wrapper, and 64 MiB RSS budgets. The adjacent 100-cycle iframe navigation lifecycle gate also passes. Hosted portable V8 passed; irrelevant package/AOT/broad and post-merge tails were canceled.
This removes the generic blocker identified by the AppScene
d4a73888/ WebScenedd39f118trace. #81 remains open for cumulative packaged unchanged-consumer acceptance and the remaining Worker/MessagePort lifecycle scope already tracked here and in #288. WebScene #76 was not touched.Exact packaged acceptance update — 18 September 2026
A fresh installed-SDK/CLI Release built from WebScene
fc71e92f8fcfb38f7385ff355b64381240ebdda5, AppScened4a738883464077767efff69e9848a99fe47c768, and unchanged Code OSS645f29cc3176500b4b5762ba887cf2a7f0ffdf2cadvances past the former WindowProxy blocker:- editor model/render/edit succeeds;
- editor Worker constructs, sends, and replies in 1,252.41 ms;
- extension iframe bootstrap succeeds;
- extension port transfer succeeds;
- UI responsiveness records 46 ticks.
The next exact failure is now reduced to #430: the transferred
MessagePortworks, but WebScene exposesonmessageas an own data property instead of an observable EventHandler accessor onMessagePort.prototype. The unchanged extension-host bootstrap therefore refuses to install its observer.Dependency order: merge #430 with browser/WPT/native/performance/lifecycle evidence, rebuild the exact package, rerun Ready → Initialized acceptance, then resume the remaining #288 GC/lifetime matrix.\n
MessagePort lifetime progress: #436 merged as
ac235def2417569b949af14ff18fd99153c88dbd, on top of the #430 accessor fix. Active transferred dedicated-Worker ports now survive child-isolate forced GC while started listeners exist, and release on all tested listener/transfer/close/termination/navigation/teardown boundaries. Chrome/native pass 3/3; the 100-cycle native gate leaves zero retained bindings and queue bytes, and the exact-head hosted portable V8 contract passed. The next #81 action is the fresh installed-package unchanged-consumer run on this merged head.Implementation-first re-audit at WebScene
59d143e1confirms the generic Worker/iframe/structured-clone/MessagePort boundaries used by unchanged Code OSS are implemented, including active cross-isolate port reachability and exact release. The next #81 action is fresh installed-package acceptance; broader lifecycle hardening remains #288 without a newly reduced defect. No code or validation was run.Exact current Release evidence now reproduces the remaining packaged blocker.
Source pair:
- AppScene
15e30dedcaa7c6a0242c1929a8c8d6e47a1cc600 - WebScene
5b97aac934dee450ec2b01291a004b246dc08364 - unchanged Code OSS
645f29cc3176500b4b5762ba887cf2a7f0ffdf2c - SDK archive SHA-256
c5d484fb05f98d163d86b07ab5e3732bf8291fddbacdb25dc9c3e79a97fa76d2 - packaged executable SHA-256
774c1c3c89f8c74ff7bf7ebe8acf73f11405573e918cb59672ec3a6b4efc544a
The app launches without browser technology, reaches the native DOM/workbench, renders a real editor edit, and completes an editor-worker request/reply in 1,628.51 ms while recording 2,662 UI-responsive ticks. The browser extension-host path still fails with
The browser extension host did not transfer its MessagePort, so packaged acceptance remains false.This isolates the next implementation to the iframe → browser extension host transferred-port handshake. The separate Monaco cross-document Range failure observed in the same run is tracked in #863.
- AppScene
Focused root cause #864 is fixed by merged PR #865 (
8b8fd4a8). WebScene now calculates and compares the exact SHA-256 digest for inline scripts governed by CSP hash sources; the VS Code-shaped iframe/Worker/MessagePort regression passes a matching hash and rejects a mismatched hash even whenunsafe-inlineis present. The 20-cycle gate retained identical V8 heap before/after.The current package predates this merge. Exact unchanged Code OSS acceptance is scheduled immediately after Range ownership PR #866 lands, so one installed SDK/package can test both the extension-host bootstrap and Monaco geometry trace. Keep #81 open until that package receives the local bootstrap message, transfers the port, and completes Ready/Initialized without timeout.
Current worker and messaging checkpoint — 20 September 2026
Exact current source heads are WebScene
98077f23, AppScene5a0d9a9, and unchanged Code OSS645f29cc. The latest complete package uses WebScenebd366836and now proves a real editor Worker$computeLinksrequest/reply plus the browser extension-host iframe, transferred MessagePort,Ready, andInitializedhandshake while UI timers continue.This removes the prior macOS package blocker. The issue remains open for its stated module-load failure, termination/navigation, forced-GC, sustained throughput/memory, and Linux/Windows package gates; #288 owns the active-port forced-GC matrix. No new Worker implementation is scheduled until those exact gates expose a reduced failure.
Problem
The packaged Code OSS 1.137.0 native smoke renders and passes editor create/type/render/undo through AppScene/WebScene, and its bundled Node extension host connects successfully. Runtime diagnostics still report:
WebScene currently does not provide the iframe/Worker/transferable MessagePort path expected by upstream Code OSS browser extension and Monaco language-worker code. Main-thread fallback reduces responsiveness and does not cover extensions that require worker isolation.
Required design
postMessage,MessageChannel, transferableMessagePort, structured clone, termination, error/messageerror events, and shutdown/navigation cancellation.Quality and performance gates
Acceptance
Current dependency status — 18 September 2026
Structural CSS PR #245 merged as
4040058e, so its former collision is retired. #288's first active-listener reachability fix merged through PR #296 as716aaf82; #288 remains open for dedicated Worker, ServiceWorker, and iframe identity/reachability, transfer/release, inactive-port collection, navigation, termination, and teardown acceptance.This issue owns Worker, structured clone, transferable MessagePort, and the browser extension-host worker bootstrap. General iframe navigation, realms, CSP, document replacement, and teardown belong solely to #267 under #264. Final unchanged Code OSS Worker/Monaco/browser-extension acceptance consumes #267 rather than duplicating it.
The remote Ready→Initialized scheduler slice merged through #289/PR #347. Its remaining product proof belongs to the next current-package #252 run; do not add another scheduler fix here without a new reduced failure.