Media: Use Document-Isolation-Policy for cross-origin isolation on Chromium 137+#75991
Media: Use Document-Isolation-Policy for cross-origin isolation on Chromium 137+#75991
Conversation
Use Document-Isolation-Policy (DIP) header on Chrome 137+ instead of COEP/COOP to achieve cross-origin isolation without breaking plugin iframes (e.g. Elementor). Falls back to existing COEP/COOP for older browsers. Skips credentialless iframe modifications and embed preview filtering when DIP is active.
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: -403 B (-0.01%) Total Size: 6.87 MB
ℹ️ View Unchanged
|
Non-Chrome browsers (Firefox, Safari) no longer receive Cross-Origin-Opener-Policy or Cross-Origin-Embedder-Policy headers. Only Chrome 137+ gets Document-Isolation-Policy. Fixes Yoda condition coding standards violations and removes COEP-specific E2E tests that no longer apply.
Skip the output buffer entirely when DIP is not active. The crossorigin attributes added by the buffer cause CORS failures for embeds when no isolation headers are sent.
DIP isolates the document into its own agent cluster, blocking same-origin iframe access that page builders like Elementor rely on for their preview iframes.
Core's wp_set_up_cross_origin_isolation sends COEP/COOP headers that break third-party editors like Elementor. Gutenberg's DIP-based approach replaces these and also skips non-block-editor screens.
|
🔥 |
Since the PHP now only sends Document-Isolation-Policy headers (no COEP/COOP fallback), skip the Cross-origin isolation tests on Chrome 137+ and let the DIP-specific tests cover that path. Extract getChromeMajorVersion helper to deduplicate version checks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4078029 to
29c3770
Compare
|
Confirming this does fix it for ACF WYSIWYG fields. Are there any known side effects of putting this header on the main page? Some searching seems to indicate that it could cause some 3rd party scripts to not load, though I haven't actually seen that in my testing. I tried embedding a few things from 3rd parties and it all seemed to work okay. |
probably fewer limitations than the previous approach. see https://developer.chrome.com/blog/document-isolation-policy |
The test did not wait for the image upload to complete before interacting with focal point controls, causing timeouts with DIP cross-origin isolation timing changes.
|
I'm going to remove the COEP/COOP support for now since using the feature is disabled by default in browsers without DIP support (namely firefox and safari). It rather make this opt-in for those users than ship them a degraded experience over their current server support. I will move that implementation over to a simple plugin users can install that will enable the feature along with the previous COEP/COOP + credentialless support. |
|
I started working on a plugin to re-enable in firefox/safari using the COEP/COOP header approach in https://github.com/adamsilverstein/client-side-media-experiments/ |
|
Just wanted to add that some blocks in my plugin Tainacan are also affected by the |
@adamsilverstein Is this Chrome or Chromium? Will it work in Edge? Is there a Can I Use feature for this? |
|
I don't really understand what's going on here after reading the PR description. Why did we need the previous headers, and why do we need the new ones? Why do we need |
|
Flaky tests detected in 54e1806. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/22708208484
|
DIP replaces COEP/COOP and no longer breaks TinyMCE iframe initialization, so the workaround is no longer needed.
DIP replaces COEP/COOP and no longer breaks preview popup window reuse, so the workaround is no longer needed.
DIP replaces COEP/COOP and no longer breaks page navigations during template creation, so the workaround is no longer needed.
This reverts commit b38ce3b.
The headers achieve the same result: enabling SharedArrayBuffer which we need for client side media to operate in the background in a worker thread and pass data efficiently back and forth to the main thread. Document Isolation Policy replaces the need for COEP/COOP headers and credentialless iframes. it works more seamlessly that COEP/COOP (as evidenced by the fixed issues).
Primarily so we can decide very early what headers to send.
No worries! we discussed it earlier on this or the core backport ticket (i'll look for that) and came up with idea of a plugin to opt in for those browsers. using the COEP/COOP headers can cause issues for 3p iframes so until those browsers support a solution that is more fully compatible, it makes sense to leave media uploads as is - using the server path. |
DIP replaces COEP/COOP so these iframed block, plugin, and autocomplete tests no longer need the workaround.
|
@Mamaduka I removed the exceptions I could and will add some better doc blocks to explain why the remaining exceptions are needed. |
Replace vague "cross-origin isolation" comments with specific explanations of why each test needs the media processing exception disabled: Document-Isolation-Policy creates agent cluster mismatches when tests navigate to pages without the DIP header.
|
The matching core PR is approved WordPress/wordpress-develop#11098 |
Mamaduka
left a comment
There was a problem hiding this comment.
Can't say I understand everything that's going on in this PR, but here are my test results:
- Confirmed that the Classic block works again. Tested with Chrome/Safari.
- YouTube embeds don't work in Safari. There's an error regarding
allow-presentation. Not sure if it's related or just a general problem. - A couple of times, I saw an error for RTC endpoints, but I wasn't able to reproduce it consistently.
Approving because it improves the current state and fixes a couple of bugs, so it's better to get this into beta 3.
Screenshot
| function ( string $output ) use ( $coep ): string { | ||
| header( 'Cross-Origin-Opener-Policy: same-origin' ); | ||
| header( "Cross-Origin-Embedder-Policy: $coep" ); | ||
| function ( string $output ): string { |
There was a problem hiding this comment.
| function ( string $output ): string { | |
| static function ( string $output ): string { |
I think, based on coding guidelines, closures should be static.
There was a problem hiding this comment.
We can fix this CS in a follow-up.
|
Merging without waiting for the perf tests since we're under time pressure. |
…romium 137+ (#75991) Co-authored-by: adamsilverstein <adamsilverstein@git.wordpress.org> Co-authored-by: Mamaduka <mamaduka@git.wordpress.org> Co-authored-by: louiswol94 <louiswol94@git.wordpress.org> Co-authored-by: johnstonphilip <johnstonphilip@git.wordpress.org> Co-authored-by: mateuswetah <tainacan@git.wordpress.org> Co-authored-by: westonruter <westonruter@git.wordpress.org> Co-authored-by: ellatrix <ellatrix@git.wordpress.org>
|
I just cherry-picked this PR to the wp/7.0 branch to get it included in the next release: e7b8c0c |
Only includes the following commit: WordPress/gutenberg@e7b8c0c. "Media: Use Document-Isolation-Policy for cross-origin isolation on Chromium 137+" (WordPress/gutenberg#75991) See #64595. See #64766. git-svn-id: https://develop.svn.wordpress.org/trunk@61846 602fd350-edb4-49c9-b593-d223f7449a82
|
@Mamaduka thanks for the feedback. i will work on a follow up issue/pr
The feature is supposed to be disabled with Safari by default. Did you enable it with a filter? of did it just break embeds without any changes? can you clarify - I assume you were on this PR (now merged), was core on 6.9 or trunk? To enable in Safari we need the old COEP/COOP approach for now, try this plugin to re-enable. |
Only includes the following commit: WordPress/gutenberg@e7b8c0c. "Media: Use Document-Isolation-Policy for cross-origin isolation on Chromium 137+" (WordPress/gutenberg#75991) See #64595. See #64766. Built from https://develop.svn.wordpress.org/trunk@61846 git-svn-id: http://core.svn.wordpress.org/trunk@61133 1a063a9b-81f0-0310-95a4-ce76da25c4cd
|
I was testing this branch with 7.0-lighly (using beta tester plugin). I've not changed any settings, just the browser and tested the same thing. It could be a different issue with Safari; we should do better Embeds testing with different browsers. |
I was able to reproduce the Safari issue. I see the notice that the feature is disabled in the console:
Image uploads work with the server path (although the preview is missing which could be a regression, I need to check 6.9). YouTube embeds do not. I see
|
|
@Mamaduka I also see an error using youtube embeds with safari in wp 6.9 (no plugins), do you see this?
|
|
@Mamaduka I started working on disabling non supporting browsers even earlier in #76227 but didn't notice much difference. youtube videos load in the editor in firefox after a bunch of console warnings, even in 6.9 with no plugins active. They refuse to load in safari at all. Do you see the same? We can probably bring in code to use placeholders for these elements when we see the embeds fail. |
| WASM-based image optimization requires `SharedArrayBuffer` support, which in turn requires [cross-origin isolation](https://web.dev/articles/cross-origin-isolation-guide). | ||
|
|
||
| Once the page is served with these headers, `SharedArrayBuffer` will be available in the browser, and WASM-based image optimization will work as expected. However, all embedded resources (e.g., images, iframes, scripts) must also be served with appropriate CORS headers (or iframe with `iframe-credentialless` for supporting browsers) to ensure cross-origin isolation is maintained. For third party embeds (for example a YouTube video), the plugin uses [iframe `credentialless` attribute](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/IFrame_credentialless) to help with this. For browsers that do not support this attribute, embeds will show an information pane instead of a live preview. | ||
| This is achieved using the [`Document-Isolation-Policy`](https://github.com/nicolo-ribaudo/tc39-proposal-structs/blob/main/test262-filtering/isolation-explainer.md) header, which provides per-document cross-origin isolation without affecting other iframes on the page. This avoids the breakage that the older `Cross-Origin-Embedder-Policy` / `Cross-Origin-Opener-Policy` headers caused for third-party plugins and embeds. |
There was a problem hiding this comment.



Summary
Replaces
Cross-Origin-Embedder-Policy/Cross-Origin-Opener-Policy(COEP/COOP) headers withDocument-Isolation-Policy: isolate-and-credentiallessfor cross-origin isolation in the block editor.DIP is a per-document isolation mechanism available in Chromium 137+ that avoids the breakage COEP/COOP caused for third-party plugins whose iframes lost credentials and DOM access.
Note that we have already decided to disable client-side media for non chromium browsers for now. Those users can re-enable (plus add back coep/coop headers) using a plugin.
What changed
credentiallessattribute on iframes, iframe content observation, and embed preview filtering are all removed — DIP doesn't need them.__documentIsolationPolicyJS flag: No longer needed since there's only one code path.wp_set_up_cross_origin_isolationhooks are replaced with DIP equivalents.crossorigin="anonymous"still added to subresources via mutation observer.gutenberg_use_document_isolation_policyfilter available for customization.Browser support
Key files
lib/media/load.php— Chromium version detection, DIP header, filterpackages/block-editor/src/hooks/cross-origin-isolation.js— Simplified mutation observer (no iframe/credentialless logic)packages/upload-media/src/feature-detection.ts— Removed credentialless iframe checklib/media/docs/client-side-media-docs.md— Updated browser matrixTest plan
Document-Isolation-Policy: isolate-and-credentiallessresponse header (no COEP/COOP)window.crossOriginIsolated === truein consolenpm run test:unit -- --testPathPattern="cross-origin-isolation|feature-detection"npm run test:e2e -- test/e2e/specs/editor/various/cross-origin-isolation.spec.jsTrac ticket: https://core.trac.wordpress.org/ticket/64766
Backport PR: WordPress/wordpress-develop#11098