[Remote] Re-enable client-side media processing via Document-Isolation-Policy#3515
Conversation
Broadens the service worker's cross-origin-isolation handling so that a scope is marked as isolated whenever its response includes a `Document-Isolation-Policy` header — not only when the SW rewrites COEP/COOP to DIP. This is needed because Gutenberg PR #75991 (merged Mar 2026, shipped in 22.6+) now sends DIP directly on editor screens in Chromium 137+ and stopped sending COEP/COOP. Without this change, the existing COEP→DIP rewrite path never fires, so `empty.html` (the block editor's inner iframe) is not served with DIP and parent/child DIP parity breaks, crashing the editor. Rename `rewriteCoopHeadersToDocumentIsolationPolicy` to `applyCrossOriginIsolationHeaders` to reflect the broadened scope. Prepares the ground for re-enabling client-side media processing (WordPress#3514).
Removes the `wp_client_side_media_processing_enabled => __return_false` filter that was introduced in WordPress#3312 as a temporary workaround after the editor was crashing when Gutenberg sent COEP/COOP. The workaround is no longer necessary: - Gutenberg PR #75991 now sends `Document-Isolation-Policy` directly (Chromium 137+) instead of COEP/COOP, avoiding the embed/iframe breakage that COEP/COOP caused. - The companion service worker change makes Playground treat responses that already carry DIP the same way it treats rewritten COEP/COOP responses, so the block editor's inner `empty.html` iframe continues to get DIP and parent/child DIP parity is preserved. Adds e2e tests that verify, inside the WP admin iframe on `/wp-admin/post-new.php` with the Gutenberg plugin and the `gutenberg-media-processing` experiment enabled: - `window.crossOriginIsolated === true` - `SharedArrayBuffer` is available - Gutenberg's `window.__clientSideMediaProcessing` is set to `true` Non-Chromium browsers are skipped — DIP and Gutenberg's client-side media path are Chromium-only today. Editor rendering under DIP is still covered by the existing `document-isolation-policy.spec.ts` suite. Fixes WordPress#3514.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Re-enables Gutenberg’s client-side media processing in Playground by tracking cross-origin isolation when Document-Isolation-Policy (DIP) is already present, while preserving the existing COEP/COOP→DIP rewrite path; also removes the MU-plugin workaround that disabled the feature and adds an E2E regression test.
Changes:
- Update the service worker to track scopes when responses already include
Document-Isolation-Policy, and rename/generalize the header handling helper. - Remove the MU-plugin filter that forcibly disabled
wp_client_side_media_processing_enabled. - Add Playwright E2E coverage asserting cross-origin isolation +
SharedArrayBuffer+ Gutenberg enablement flag in Chromium.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/playground/website/playwright/e2e/client-side-media.spec.ts | Adds Chromium-only E2E coverage for cross-origin isolation and client-side media enablement. |
| packages/playground/remote/src/lib/playground-mu-plugin/0-playground.php | Removes temporary filter that disabled client-side media processing. |
| packages/playground/remote/service-worker.ts | Broadens header handling to support direct DIP responses and keeps COEP/COOP rewrite behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Opt the client-side-media spec into `channel: 'chromium'`: Playwright's default `chromium_headless_shell` does not honor Document-Isolation-Policy, so `window.crossOriginIsolated` returns false even though Gutenberg sends the DIP response header. The full Chromium channel (already installed by CI via `playwright install chromium --with-deps`) supports DIP and makes the test assertions meaningful. - Correct the Gutenberg version reference in the service worker docblock (21.8 -> 22.6), where Gutenberg PR #75991 actually shipped. - Extract a small `skipNonChromium` helper to avoid duplicating the skip guard across the two tests in the spec.
Move the Chromium-only guard from per-test `test.skip()` in the test
body to a file-level `test.skip(condition)` modifier. The body-level
skip fires after fixtures/browser launch, so WebKit and Firefox workers
errored with "Unsupported <browser> channel 'chromium'" when the
file-level `test.use({ channel: 'chromium' })` tried to apply before
the skip.
A file-level `test.skip()` is evaluated before the worker launches the
browser, so non-Chromium projects no longer try to combine
`channel: 'chromium'` with an incompatible browser and now report as
skipped cleanly.
|
AVIF file for testing: |
bgrgicak
left a comment
There was a problem hiding this comment.
Thanks for working on this @adamsilverstein! I manually tested and everything works as expected.
Motivation for the change, related issues
Fixes #3514.
Playground currently disables Gutenberg's client-side media processing experiment via an
__return_falsefilter in the mu-plugin (0-playground.php#L265-L274). That workaround was added in #3312 because, at the time, Gutenberg was emitting COEP/COOP for the editor and — even when rewritten toDocument-Isolation-Policyby the service worker — the editor's inner iframe was crashing.Gutenberg PR #75991 (shipped in 22.6+) changed the isolation strategy: on Chromium 137+ Gutenberg now sends
Document-Isolation-Policy: isolate-and-credentiallessdirectly on editor screens and no longer sends COEP/COOP. That removes the root cause of the breakage — but it also means Playground's existing COEP→DIP rewrite path never fires on current Gutenberg, so the scope is never added toscopesWithCrossOriginIsolation,empty.htmldoesn't receive DIP, and parent/child DIP parity breaks inside the block editor canvas (see #3320 for why parity matters).Implementation details
Two small, coordinated changes:
packages/playground/remote/service-worker.ts— broaden the post-response handler (nowapplyCrossOriginIsolationHeaders) so that:Document-Isolation-Policy, the scope is added toscopesWithCrossOriginIsolationand the response passes through unchanged. This is the modern path once Gutenberg serves DIP directly.wp_set_up_cross_origin_isolation, or custom plugins).The existing
empty.htmlbranch (which adds DIP to the inner editor iframe when the scope is tracked) needs no change — it now gets populated in both cases.packages/playground/remote/src/lib/playground-mu-plugin/0-playground.php— removeadd_filter('wp_client_side_media_processing_enabled', '__return_false')and its comment block. The workaround is no longer necessary.Tests
New Playwright spec:
packages/playground/website/playwright/e2e/client-side-media.spec.tsgutenberg-media-processingexperiment on/wp-admin/post-new.phpand asserts, inside the WP admin iframe:window.crossOriginIsolated === truetypeof SharedArrayBuffer !== 'undefined'window.__clientSideMediaProcessing === true(Gutenberg's own enablement flag)Editor rendering under DIP is already covered by the existing
document-isolation-policy.spec.tssuite, which implicitly verifies parent/child DIP parity.Testing instructions
Manual (Chromium ≥ 137):
npm run devand openhttp://127.0.0.1:5400/website-server/.#):{ "$schema": "https://playground.wordpress.net/blueprint-schema.json", "landingPage": "/wp-admin/post-new.php", "plugins": ["gutenberg"], "login": true, "steps": [ { "step": "runPHP", "code": "<?php require '/wordpress/wp-load.php'; update_option('gutenberg-experiments', array('gutenberg-media-processing' => true));" } ] }crossOriginIsolated→truetypeof SharedArrayBuffer→"function"window.__clientSideMediaProcessing→trueDocument-Isolation-Policy: isolate-and-credentiallessand no COEP/COOP.Automated:
Notes on backwards compatibility
false, headers pass through, and Gutenberg's JS feature detection falls back the same as before.