Skip to content

Expose HTMLIFrameElement.sandbox for Code OSS webviews and Markdown Preview #253

Description

@wieslawsoltes

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

#265–#268 own the later ServiceWorker, resource, nested-document/CSP, and interaction stages. This issue remains the first synchronous iframe/sandbox blocker and its sandbox enforcement boundary; release-wide visual, workbench, accessibility, and WPT-ledger gates are #259, #260, #262, and #263.

Problem

Code OSS 1.137.0 constructs every workbench webview with an iframe and synchronously executes:

const element = document.createElement('iframe');
element.sandbox.add('allow-scripts', 'allow-same-origin', 'allow-forms', 'allow-pointer-lock', 'allow-downloads');

At WebScene b81f594c, document.createElement('iframe') is branded as HTMLIFrameElement, but iframe.sandbox is absent. The call throws before Code OSS assigns the iframe src or initializes the webview message channel. Markdown Preview therefore reaches the generic editor placeholder rather than creating a webview.

This is a reusable iframe DOM contract required by Markdown Preview and other Code OSS webview consumers. It belongs in WebScene rather than a Markdown extension or AppScene workaround.

Exact evidence

Checked revisions:

  • WebScene b81f594cfe0effe742942b8ccf643c2e1f42bc6a
  • AppScene 9f434e0deea632fe28962ce32e2f4ddede4aa14c
  • Code OSS 645f29cc3176500b4b5762ba887cf2a7f0ffdf2c (1.137.0)

The pinned Code OSS source calls sandbox.add(...) in webviewElement.ts. The checked-out WebScene runtime maps HTMLIFrameElement to the generic element constructor and contains no sandbox property binding.

A reduced native-runtime probe reports:

{
  "before": {
    "tag": "[object HTMLElement]",
    "instance": true,
    "sandboxType": "undefined",
    "classListType": "object"
  },
  "error": {
    "name": "TypeError",
    "message": "Cannot read properties of undefined (reading 'add')"
  },
  "attribute": null
}

The available standalone probe artifact was built at WebScene bd139b26. A source diff through the exact current b81f594c head confirms that iframe branding was added but no sandbox binding exists, so the relevant contract remains unchanged. Rebuilding that standalone artifact is currently blocked locally by its deleted historical SDK cache. The reported packaged app displays the generic editor-open failure; its retained logs do not contain the synchronous editor exception.

The user supplied /Users/wieslawsoltes/GitHub/vscode-demo/README.md as the current reproduction fixture. The earlier screenshot visibly showed the WebScene README instead, so retain both facts in evidence rather than treating the contents as identical. The current demo README has headings, tables, fenced shell blocks, local Markdown links, and external links. It has no inline image. Image-resource acceptance therefore needs a deterministic adjacent fixture or the screenshot's vendor/webscene/README.md, whose first image is docs/assets/webscene-logo.jpg.

Server logs only show missing optional proprietary vsda and language-detection assets. Those requests do not own this failure: the synchronous sandbox.add exception occurs before iframe navigation, service-worker bootstrap, Markdown rendering, Mermaid loading, or preview resource/image requests.

Related to #81 because both exercise iframes, but this issue owns the reusable HTMLIFrameElement.sandbox DOM surface and sandbox behavior. Do not fold it into #81's Worker/MessagePort execution-context scope.

Required implementation

  • Expose HTMLIFrameElement.prototype.sandbox as a stable, correctly branded DOMTokenList reflected to the sandbox content attribute.
  • Make token mutation and direct attribute mutation stay synchronized, including add, remove, toggle, replace, contains, supports where applicable, iteration, length, indexed access, and value.
  • Enforce Web IDL receiver, token validation, duplicate, whitespace, casing, and exception behavior compatible with Chromium for the supported surface.
  • Reuse the reflected-token-list implementation for iframe/webview consumers instead of special-casing Markdown Preview or the five Code OSS tokens.
  • Define and test iframe navigation/security behavior for the accepted sandbox tokens. Do not expose a token list that claims isolation which the runtime does not enforce. Unsupported combinations must fail visibly and deterministically.
  • Preserve same-origin resource admission, message-channel startup, navigation cancellation, and teardown ownership.

Acceptance gates

Reduced and standards-derived contracts

  • Add a browser-oracle test for the exact Code OSS five-token statement and compare the resulting property brand, tokens, serialized attribute, exceptions, and mutation/reflection behavior with Chromium.
  • Add the smallest applicable WPT-derived iframe.sandbox / reflected DOMTokenList cases, recording upstream paths and explicit exclusions.
  • Add cross-platform native tests for stable object identity, mutation in both directions, invalid tokens, duplicate tokens, removeAttribute/re-add, detached and connected iframes, navigation/reload, and teardown races.
  • Exercise allowed and rejected navigation/script/form/pointer-lock/download behavior for the supported tokens. Verify origin/resource admission and observable failure; do not infer security from attribute serialization alone.

Unchanged Code OSS product

  • Build from the exact pinned heads above and open Markdown Preview to the side for the current vscode-demo/README.md without patching VS Code or its Markdown extension.
  • Verify the preview renders the current README's heading hierarchy, table, fenced shell blocks, local links, and external links; editing the source updates the preview and closing/reopening/reloading leaves no stale iframe or port.
  • Verify local and external preview resources under the existing Code OSS content-security and resource-root policy. Because the current demo README has no inline image, use a checked-in deterministic adjacent Markdown/image fixture or additionally open vendor/webscene/README.md and verify its local docs/assets/webscene-logo.jpg request, intrinsic size, decoded pixels, presented bounds, and failure diagnostics. Do not silently replace a missing image with successful acceptance.
  • Repeat the same flows in Chromium at the same viewport, device scale, theme, and font inputs. Compare visible content, heading/table/code/image bounds, scroll extent, and a versioned screenshot/perceptual-diff budget. Fail on the editor placeholder, blank preview, broken image, or resource-policy mismatch.

Performance and lifecycle

  • Record command-to-first-preview-frame and edit-to-updated-frame median and p95 for native and Chromium across a fixed warm/cold sample set; publish and enforce versioned absolute and regression budgets before closure.
  • Run repeated open/update/reload/close cycles and bound iframe realms, message ports, resource requests, scene publications, retained nodes, result-pool/memory growth, and shutdown time.
  • Verify hidden/closed preview idle frame demand returns to zero and no worker, iframe, resource, or server helper survives package shutdown.
  • Pass Linux, macOS, and Windows native/runtime suites plus the packaged macOS acceptance used by vscode-demo.

Non-goals

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