Skip to content

[Website] Rewrite COEP/COOP headers as Document-Isolation-Policy in supporting browsers - #3028

Merged
adamziel merged 13 commits into
trunkfrom
feature-detect-document-isolation-policy
Dec 16, 2025
Merged

adamziel merged 13 commits into
trunkfrom
feature-detect-document-isolation-policy

Conversation

@adamziel

@adamziel adamziel commented Dec 12, 2025 •

Copy link
Copy Markdown
Collaborator

Summary

This PR enables cross-origin isolation (required for SharedArrayBuffer) without breaking external embeds like YouTube videos.

Playground's client-side media processing experiment relies on SharedArrayBuffer, which requires cross-origin isolation. The traditional approach uses Cross-Origin-Embedder-Policy (COEP) and Cross-Origin-Opener-Policy (COOP) headers, but these are viral – once set, they affect the entire site and break external embeds that don't set matching headers.

Document-Isolation-Policy is a newer specification that provides cross-origin isolation on a per-document basis without these limitations. This PR feature-detects browser support for Document-Isolation-Policy and rewrites COEP/COOP headers to it automatically in supporting browsers.

Feature detection

This PR detects the Document-Isolation-Policy support by:

  1. Loading a hidden iframe with the Document-Isolation-Policy header
  2. Checking if SharedArrayBuffer is available in that iframe
  3. Sending the result to the service worker
  4. Rewriting headers on all subsequent scoped requests

As more browsers implement the specification, they'll automatically receive the better experience without code changes.

Handling cross-origin iframe restrictions

Document-Isolation-Policy introduces a side effect: the parent frame can no longer access iframe.contentWindow.location.href due to cross-origin restrictions. This would break Playground's URL bar synchronization.

To work around this, a WordPress MU plugin now posts the current URL to the parent frame on each page load. The boot script listens for these messages and updates the address bar accordingly, alongside the existing iframe load event handling.

Test plan

  • Verify SharedArrayBuffer works in block editor with media processing enabled (Chrome 137+)
  • Verify external embeds like YouTube still work in the block editor
  • Verify URL bar updates correctly when navigating within WordPress
  • Verify no regressions in browsers that don't support Document-Isolation-Policy

Here's a Blueprint that takes you directly to the post editor with the Gutenberg plugin installed and the media processing experiment enabled:

{
	"$schema": "https://playground.wordpress.net/blueprint-schema.json",
	"meta": {
		"title": "Enable all three Dataview Experiments in Gutenberg",
		"author": "bph",
		"description": "Blueprint example to enable multiple Experiments within the Gutenberg plugin ",
		"categories": ["Gutenberg", "Experiments"]
	},
	"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, 'gutenberg-new-posts-dashboard' => true, 'gutenberg-quick-edit-dataviews' => true )  );"
		}
	]
}

Related to #2954

…-Isolation-Policy

When Document-Isolation-Policy is enabled, the parent frame can no longer access
iframe.contentWindow.location.href due to cross-origin restrictions. This breaks
Playground's URL bar synchronization.

This commit adds a WordPress MU plugin that posts the current URL to the parent
frame on page load, and updates the boot script to listen for these messages
alongside the existing iframe load event handling.
Comment thread packages/playground/remote/service-worker.ts Outdated
@adamziel
adamziel requested a review from a team as a code owner December 12, 2025 19:23
@adamziel adamziel changed the title Rewrite COEP/COOP headers as Document-Isolation-Policy in supporting browsers [Website] Rewrite COEP/COOP headers as Document-Isolation-Policy in supporting browsers Dec 12, 2025
@adamziel

Copy link
Copy Markdown
Collaborator Author

Somehow I don't see the client-side media control. But at least the editor doesn't blow up. Still, I wonder what am I missing.

@adamziel

Copy link
Copy Markdown
Collaborator Author

The failing tests are more likely related to the test setup than a shortcoming of this PR. Let's fix them and let's also not block a review on these failures.

@JanJakes

Copy link
Copy Markdown
Member

@adamziel Nice! 👏 It bugs me that there is no way to indicate "ignore COOP/COEP headers when DIP is supported," not even an easier way to detect the feature, but here we are. After all, this might be temporary if DIP is generally adopted.

const newHeaders = new Headers(response.headers);
newHeaders.delete('cross-origin-embedder-policy');
newHeaders.delete('cross-origin-opener-policy');
newHeaders.set('document-isolation-policy', 'isolate-and-credentialless');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we read the value of cross-origin-opener-policy and set either isolate-and-require-corp or isolate-and-credentialless accordingly?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh good call! Yes, let's do that.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 4b194ba. Let's merge!

…lements

The tests were failing in CI because they checked for the 'Add title' textbox
before WordPress admin finished loading. In CI, loading takes longer than
locally, so the tests would time out.

This fix adds an explicit wait for the admin menu (#adminmenu) before checking
for editor-specific elements. It also increases timeouts for tests that install
Gutenberg, since plugin installation adds additional load time.
…nd-require-corp or isolate-and-credentialless accordingly
Skip Firefox/WebKit tests since Document-Isolation-Policy is only
supported in Chromium. Simplify assertions to use body text content
checks instead of specific element visibility which can be fragile
across Gutenberg versions.
The Document-Isolation-Policy header on empty.html was being added unconditionally,
which broke REST API authentication when the parent page didn't have COEP/COOP
headers. The 'isolate-and-credentialless' policy causes cross-origin requests to
be sent without credentials, resulting in "Session expired" errors.

This change tracks which scopes have COEP headers rewritten to Document-Isolation-Policy,
and only adds the header to empty.html when the scope requires cross-origin isolation.

Also updated E2E test assertions to check for the admin menu visibility instead of
"Add New Post" text, which may not be visible in the modern block editor.
@adamziel
adamziel merged commit bc1b749 into trunk Dec 16, 2025
33 checks passed
@adamziel
adamziel deleted the feature-detect-document-isolation-policy branch December 16, 2025 09:56
adamziel added a commit that referenced this pull request Dec 16, 2025
…cy rewriter (#3044)

## Summary

Minor code quality improvement following #3028:
- Rename `dipValue` to `documentIsolationPolicy` for better clarity
- Simplify the COOP comment to be more concise
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants