Skip to content

Complete ServiceWorker control and client lifecycle for Code OSS webviews #265

Description

@wieslawsoltes

Parent epic: #264. Top-level release epic: #227.

Proven gap

At VS Code OSS 645f29c, browser/pre/index.html requires navigator.serviceWorker, registers service-worker.js as a module, waits for controllerchange, calls registration.update(), and exchanges messages with the controller. Markdown Preview does not set disableServiceWorker. WebScene b81f594c explicitly reports navigator.serviceWorker unsupported (WEBSCENE1001) and has no ServiceWorker binding. Once #253 stops the earlier synchronous iframe exception, this is the next deterministic initialization failure.

This focused issue owns the control plane: ServiceWorkerContainer, module-worker registration/update, install/activate, skipWaiting, clients.claim, controller selection/change, Client/WindowClient identity and postMessage, controlled-document generations, failure reporting, unregister, navigation, and teardown. The resource/stream/cache plane is a separate dependent child.

Dependencies and boundaries

Acceptance

  • IDL descriptors, brand checks, secure-context visibility, registration matching, update, install/waiting/active state, events, controller selection, Clients get/matchAll, postMessage, skipWaiting, claim, unregister, errors, and Promise timing match selected Service Worker WPTs and Chromium.
  • A native top-level + iframe fixture proves a module service worker controls only admitted same-origin clients, survives the required reload transition, rejects cross-origin/sandbox-opaque access, and never adopts a stale navigation generation.
  • The exact VS Code prelude reaches webview-ready and obtains its expected controller without source changes.
  • 100 register/control/update/unregister cycles leave zero worker runtimes, clients, event listeners, ports, tasks, and registrations after teardown; no timing sleep determines correctness.
  • Cold register-to-controller p95 <= 1 s and warm controlled-navigation p95 <= 250 ms on the recorded macOS CI reference; the 100-cycle fixture retains <= 8 MiB after warm-up and publishes task/queue high-water counts.
  • macOS arm64, Linux x64, Windows x64 native and installed-consumer lanes pass.

Proposed PR stack

  1. IDL/state store and registration matching;
  2. module worker lifecycle and controller/Clients semantics;
  3. navigation/security/teardown and WPT/browser oracle;
  4. unchanged VS Code prelude acceptance and performance evidence.

Active status — 17 September 2026

PRs #276/#278 merged at 053a5627/aa786c0e. Exact stamped Code 645f29c reaches the version-6 controller and logs webview ready after 2.665 s. The issue remains open because cumulative PR #281 is conflicted with current main and its current-main 100-cycle gate exposes active MessagePort listener collection in #288/PR #245. Requalify and merge #281 before completing this issue or starting #266 implementation.

Activity

  1. wieslawsoltes commented on Sep 17, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Stack C is visible as draft #281, based on #278. Its direct native/Chrome gates pass; the unchanged Markdown product gate remains red for a proven consumer/package reason outside that patch.

    The exact clean-run nested document URL was:

    https://109fs6ggj4valu0kc7igovsvgcpkgfl7fc4ol18kjhko1sf9nni0.vscode-cdn.net/insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/out/vs/workbench/contrib/webview/browser/pre/index.html?...&swVersion=6&...

    Fresh network captures of those exact CDN assets show:

    • decoded index.html: SHA-256 cbdc558190c89f95610a3fa4c8c1732e2d077de9214cf24e318e3bb927902daf; its lines 233-250 listen for { channel: 'version' }, compare the returned value to query swVersion, and emit the observed reload log;
    • service-worker.js: SHA-256 99de3b00d51d8143cbea53b9eade42c64b2b0259a4630deb065712b8033b61e3; it declares VERSION=4 and replies with { channel: 'version', version: VERSION }.

    The exact package and unchanged consumer source both select that stale endpoint in server/product.json:38 / vendor/vscode/product.json:38. The current Code OSS 645f29c workbench expects service-worker version 6. The adjacent appscene-host-request kind=4 line is request-kind telemetry and is not the protocol version.

    Before #281 becomes ready, the product lane must correct the packaged webview endpoint, rerun with isolated Code and AppScene/WebScene cache roots, and prove the exact unchanged Markdown preview renders. AppScene currently forwards only compilation_cache_directory from APPSCENE_WEBSCENE_CACHE_DIRECTORY; it does not configure WebScene storage_directory, which separately explains the same run's missing global indexedDB.

  2. wieslawsoltes commented on Sep 17, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Corrected fresh-cache product gate (Stack C 031ed8c5)

    The unchanged-Code Markdown gate remains red. I ran a copied fixture and fresh Code profile plus fresh APPSCENE_WEBSCENE_CACHE_DIRECTORY, with the Stack C dylib, and closed all processes afterward.

    Evidence:

    • Fixture before input: README.md SHA-256 db3dd0a4483949b325f267734c2c58ab3101e04853f2d202e182ecbf51471cb8 (from unchanged vscode-demo HEAD:README.md). The current README contains local Markdown links but no image syntax, so there is no current image payload to validate.
    • The preview pane opened but remained visually blank after 15 seconds: /private/tmp/webscene-265-current-evidence.TQ33pF/markdown-preview.png. The wrapped text is the left source editor, not preview output.
    • Runtime log: /private/tmp/webscene-265-current-evidence.TQ33pF/app.stderr.log, lines 924-927. The nested document still used /insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/..., logged Found unexpected service worker version. Found: 4. Expected: 6, then Attempting to reload service worker; webview ready followed 115 ms later. A malformed image resource request failed about 1.2 s later.
    • A test-input v was accidentally inserted only into the copied fixture while invoking the chord. The fixture was restored afterward to the SHA above; the shared vscode-demo workspace was never edited by this run.
    • Final RSS: app 398,960 KiB; server 74,592 KiB; extension host 108,624 KiB; agent host 84,672 KiB; watcher 45,424 KiB; Copilot helper 116,144 KiB; Markdown worker 57,584 KiB. Stack C's direct bounded queue/perf gates remain the previously reported 256-message/16 MiB caps and 100-message/100-cycle measurements.

    The stale endpoint is now traced to the built Code artifacts, not WebScene cache/storage:

    • The copied product.json does contain the exact-head 645f29cc3176500b4b5762ba887cf2a7f0ffdf2c CDN template.
    • The built out/server-main.js (line 442) and out/vs/code/browser/workbench/workbench.js (lines 439 and 1023) both embed ef65ac1... and contain no 645f29c....
    • A fresh no-GUI server probe at /private/tmp/webscene-265-product-probe.cpbl7T/root.html confirms the server sends only a minimal product override, so the browser workbench retains its compiled stale template.

    Therefore fresh Code/WebScene storage cannot make this retained package an exact-head product oracle. A rebuilt package whose generated bundles embed 645f29c... is required before Stack C can be marked ready. This does not identify an additional WebScene implementation defect; PR #281 remains draft pending that corrected product gate.

  3. wieslawsoltes commented on Sep 17, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Stamped exact-head product boundary — control plane passes

    The stale-package variable is removed. I reran unchanged Code OSS against /private/tmp/webscene-265-stamped-server, whose retained stamp evidence proves all three generated bundles contain 645f29cc3176500b4b5762ba887cf2a7f0ffdf2c, contain zero ef65ac1... occurrences, and ship the exact current v6 prelude/worker hashes.

    Run isolation and fixture:

    • fresh Code profile: /private/tmp/webscene-265-stamped-profile.2jQsvy
    • fresh APPSCENE_WEBSCENE_CACHE_DIRECTORY: /private/tmp/webscene-265-stamped-cache.tZwEnv
    • copied current vscode-demo/README.md: SHA-256 db3dd0a4483949b325f267734c2c58ab3101e04853f2d202e182ecbf51471cb8
    • Stack C: 031ed8c5

    Result for this issue's boundary:

    • no Found unexpected service worker version or reload message;
    • current v6 prelude registers/activates and reaches the content path after its awaited workerReady controller gate;
    • unchanged Code reports Webview(739f08ec-adbf-46a4-a3e4-413a58a941ca): webview ready;
    • command → will update content: 653 ms; content update → ready: 2,012 ms; command → ready: 2,665 ms.

    Exact compact events: /private/tmp/webscene-265-stamped-run.A3t8FO/product-gate-events.json. Full diagnostic log: app3.stderr.log. Screenshot: preview-stamped.png. Final app RSS was 328,368 KiB. All app/server/helper processes were terminated and the copied/shared README hashes remained unchanged.

    This proves the #265 control plane reaches its unchanged-product boundary. The full Markdown preview remains blank for the dependent #266 resource plane: Markdown leaves its body empty until extension pre.js and styles arrive through webview.asWebviewUri(...), which requires Service Worker FetchEvent, Streams, and CacheStorage. Stack C deliberately does not implement those APIs. The first observed post-ready failure is a malformed CSS-derived image URL; that independent CSS escape defect is being filed separately and must not be folded into #265.

    The retained direct gates remain: native/Chrome Clients 3/3, controlled navigation generation, bounded 256-message/16 MiB queues, 100-message p95 0.095 ms, and 100 lifecycle cycles p95 4.23 ms with zero final registrations.

  4. wieslawsoltes commented on Sep 17, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Current-main integration gate: held by #288 / #245

    Bottom stack entries #276 and #278 are merged. Retargeting this tranche onto current main produced one scheduler conflict, resolved by preserving Service Worker reply delivery and #285's Worker/MessagePort/WebSocket rotation. The combined native build passes the 100-cycle Service Worker lifecycle gate, but the unchanged 100-cycle Clients/MessagePort gate exposes a pre-existing active-port garbage-collection defect around cycle 69–70.

    The old #281 build passes. Applying #245 commit 94171a32 locally is insufficient because it covers local peer reachability, while this case requires a started port with an installed handler to stay active across GC. Diagnostic replacement of port.onmessage = resolve with an extra closure makes 5/5 runs pass; that is evidence only and is not being committed as a workaround.

    The focused gap is #288, linked under #81. Its implementation path overlaps owner-controlled #245, so this PR remains open until #245 supplies the active-port lifetime fix and the cumulative Service Worker lifecycle/Clients plus WebSocket gates pass on current main. No #245 path is being changed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    vscode-oss/plannedPlanned for the AppScene/WebScene VS Code OSS integration

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions