Repository navigation
Implement Worker, transferable MessagePort, and iframe contexts for Code OSS #81
Description
Current worker and messaging checkpoint — 20 September 2026
Exact current source heads are WebScene 98077f23, AppScene 5a0d9a9, and unchanged Code OSS 645f29cc. The latest complete package uses WebScene bd366836 and now proves a real editor Worker $computeLinks request/reply plus the browser extension-host iframe, transferred MessagePort, Ready, and Initialized handshake 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:
The web worker extension host is started in a same-origin iframe!
Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes.
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
- Implement dedicated worker execution contexts using the existing V8 task/runtime ownership model.
- Support the Worker constructor, script/module loading through the admitted resource contract,
postMessage,MessageChannel, transferableMessagePort, structured clone, termination, error/messageerror events, and shutdown/navigation cancellation. - Implement the narrow iframe browsing-context behavior required by the Code OSS worker extension-host bootstrap, with same-origin enforcement and explicit rejection for unsupported navigation.
- Keep worker execution off the UI thread and enforce bounded queues, memory accounting, resource size limits, and origin policy.
- Do not emulate successful isolation by running worker code synchronously in the document realm.
- Preserve the bundled Node extension host as the supported native desktop path while browser-worker coverage is developed.
Quality and performance gates
- Add cross-platform native contracts for ordering, transfer ownership, port close, worker termination, module-load failure, and navigation/shutdown races.
- Add bounded queue and sustained message-throughput benchmarks with published budgets and memory/result-pool leak checks.
- Add selected Worker/HTML/structured-clone web-platform tests with explicit exclusions.
- Run a packaged Code OSS test that starts a Monaco language worker and a browser extension worker, verifies a request/response, and confirms the UI thread remains responsive.
Acceptance
- Code OSS no longer logs main-thread language-worker fallback.
- The browser extension-host iframe and transferred ports initialize without application source changes.
- Worker failures remain observable and cannot bypass resource/origin admission.
- Linux, macOS, and Windows package jobs and performance gates pass.
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 as 716aaf82; #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.
Activity
wieslawsoltes commentedon Sep 15, 2026
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.
wieslawsoltes commentedon Sep 15, 2026
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:
artifacts/iframe-worker-appscene-a2c15a5b/smoke/indexeddb-persistence-summary.json— SHA-256dc8698d903603e3fed4fe36749d4f1bdc26114e188d29cc8b50bde85369cc24a408b01d52df36545b6063be9eae62ea04aab4cf08b6f30181121d08c18e10a10858173598a7da4261e84d50bca303b20edd63bae4f271cd0372a55e6fbe8a645662a83d65b153a65628c0e4bd1a58d71d71ef3cb350894821ef3dcf2ec470795wieslawsoltes commentedon Sep 15, 2026
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.wieslawsoltes commentedon Sep 16, 2026
Native GitHub Stack #176 is merged atomically at shared commit
b6dff40327c9a43562c743b585ca465b4e2f0a7c: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.wieslawsoltes commentedon Sep 17, 2026
Current exact-main/package audit (WebScene
24db1a2f2c6b1865977eb613598ab6932e61236d):b6dff40327c9a43562c743b585ca465b4e2f0a7c.aaf4eae053ab33d889ea2b599988aaf18f1839ee; PR Share authentication cookies with dedicated workers #210 merged authenticated same-origin worker requests at7c74a02e23fc2fd8a81da3c136227b3ced93c52c.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.
wieslawsoltes commentedon Sep 17, 2026
PR #226 merged the remaining product-neutral editor-worker contract as
c979985de705fc0f9ef5898ad29487a6358fd257.Evidence on the exact PR head
df710505a20dbe118cf2bf48c678c7f81db1ace7: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.webscene_hybrid_v8_runtime_testsin 1.66s andwebscene_worker_object_url_testsin 0.51s.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.wieslawsoltes commentedon Sep 17, 2026
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:
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:
onmessageandonmessageerroras accessor properties onMessagePort.prototypewith per-instance handler storage;start,close, and EventTarget behavior;2→ Initialized byte1.This is independent of the CSS work in #148/#234.
wieslawsoltes commentedon 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.
wieslawsoltes commentedon Sep 17, 2026
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.
wieslawsoltes commentedon Sep 17, 2026
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:IFileService.resolvestart: 1789655985605.0[AgentHost:remote] Connected: 1789655993265The 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.wieslawsoltes commentedon Sep 17, 2026
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.wieslawsoltes commentedon Sep 17, 2026
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:
_createExtHostInitData()settle: 426.90 msResource 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:
/private/tmp/vscode-252-product-handshake-289.logd85db2b31883b5f5f7e3a6747de96945c1810e792b0293a03df104a5dee0b761/private/tmp/vscode-252-product-handshake-289-process.txt/private/tmp/webscene-289-baseline.log/private/tmp/webscene-289-chrome-baseline-verify/results.json10fc9aawieslawsoltes commentedon Sep 18, 2026
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:SCENETECH_NATIVE_EDIT_OK+ space + U+202E;.view-lines: stable marker plus Monaco's visible[U+202E]token;$computeUnicodeHighlightsrequest and correlated reply complete (548.7 ms in the query-gated trace);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.
wieslawsoltes commentedon Sep 18, 2026
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.wieslawsoltes commentedon Sep 18, 2026
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: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
wieslawsoltes commentedon Sep 18, 2026
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.wieslawsoltes commentedon Sep 19, 2026
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.wieslawsoltes commentedon Sep 20, 2026
Exact current Release evidence now reproduces the remaining packaged blocker.
Source pair:
15e30dedcaa7c6a0242c1929a8c8d6e47a1cc6005b97aac934dee450ec2b01291a004b246dc08364645f29cc3176500b4b5762ba887cf2a7f0ffdf2cc5d484fb05f98d163d86b07ab5e3732bf8291fddbacdb25dc9c3e79a97fa76d2774c1c3c89f8c74ff7bf7ebe8acf73f11405573e918cb59672ec3a6b4efc544aThe 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.
wieslawsoltes commentedon Sep 20, 2026
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.