Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 20 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"IS_GUTENBERG_PLUGIN": true
},
"devDependencies": {
"@playwright/test": "^1.58.2",
"@playwright/test": "^1.61.1",
"@types/react": "^18.3.27",
"@types/react-dom": "^18.3.1",
"@typescript/native-preview": "^7.0.0-dev.20260423.1",
Expand Down
70 changes: 68 additions & 2 deletions packages/editor/src/components/post-preview-button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { VisuallyHidden } from '@wordpress/ui';
*/
import { store as editorStore } from '../../store';

function writeInterstitialMessage( targetDocument ) {
function buildInterstitialMarkup() {
let markup = renderToString(
<div className="editor-post-preview-button__interstitial-message">
<SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">
Expand Down Expand Up @@ -95,11 +95,77 @@ function writeInterstitialMessage( targetDocument ) {
*/
markup = applyFilters( 'editor.PostPreview.interstitialMarkup', markup );

return markup;
}

function writeInterstitialMessage( targetDocument, markup ) {
targetDocument.write( markup );
targetDocument.title = __( 'Generating preview…' );
targetDocument.close();
}

/**
* Resolves the preview window's `document`, working around
* `Document-Isolation-Policy` (DIP) isolation.
*
* The editor screen is served with `Document-Isolation-Policy:
* isolate-and-credentialless` to enable cross-origin isolation. This places the
* editor tab and an already-open preview tab in separate agent clusters, so
* synchronous access to a reused preview tab's `document` throws a
* `SecurityError`. Navigating the reused tab back to `about:blank` returns it to
* the opener's agent cluster and restores access. That navigation is
* asynchronous and we can't attach a cross-isolation `load` listener, so poll
* the `document` access (the operation that throws) until it succeeds, up to a
* short timeout.
*
* @param {Window} previewWindow The preview window/tab.
*
* @return {?Document} The reachable preview document, or `null` if it never
* becomes reachable within the timeout.
*/
async function getPreviewDocument( previewWindow ) {
// A freshly opened tab is already on `about:blank` and accessible, so this
// succeeds on the first preview without any reset.
try {
return previewWindow.document;
} catch {
// The reused preview tab is isolated from the editor; reset it below.
}

previewWindow.location = 'about:blank';

const timeoutMs = 1000;
const intervalMs = 50;
const deadline = Date.now() + timeoutMs;
do {
await new Promise( ( resolve ) => setTimeout( resolve, intervalMs ) );
try {
return previewWindow.document;
} catch {
// Navigation to `about:blank` hasn't completed yet; keep polling.
}
} while ( Date.now() < deadline );

return null;
}

/**
* Writes the preview interstitial into the preview window, working around
* `Document-Isolation-Policy` (DIP) isolation.
*
* The interstitial is a progressive enhancement: if the document never becomes
* reachable we simply skip it, and the caller still navigates the preview to the
* real content.
*
* @param {Window} previewWindow The preview window/tab.
*/
async function writeInterstitialIntoPreviewWindow( previewWindow ) {
const previewDocument = await getPreviewDocument( previewWindow );
if ( previewDocument ) {
writeInterstitialMessage( previewDocument, buildInterstitialMarkup() );
}
}

/**
* Renders a button that opens a new window or tab for the preview,
* writes the interstitial message to this window, and then navigates
Expand Down Expand Up @@ -168,7 +234,7 @@ export default function PostPreviewButton( {
// https://html.spec.whatwg.org/multipage/interaction.html#dom-window-focus
previewWindow.focus();

writeInterstitialMessage( previewWindow.document );
await writeInterstitialIntoPreviewWindow( previewWindow );

const link = await __unstableSaveForPreview( { forceIsAutosaveable } );

Expand Down
2 changes: 1 addition & 1 deletion packages/report-flaky-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@jest/test-result": "^29.6.2",
"@octokit/types": "^6.34.0",
"@octokit/webhooks-types": "^5.8.0",
"@playwright/test": "^1.58.2",
"@playwright/test": "^1.61.1",
"jest-message-util": "^29.6.2"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/scripts/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Enhancements

- Update `stylelint` to `^16.26.1` ([#79648](https://github.com/WordPress/gutenberg/pull/79648)).
- Widen the `@playwright/test` peer dependency to `>=1` and mark it optional, so consumers aren't forced to bump Playwright or satisfy it under strict peer deps ([#78632](https://github.com/WordPress/gutenberg/pull/78632)).

## 32.5.0 (2026-06-24)

Expand Down
5 changes: 4 additions & 1 deletion packages/scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,15 @@
"rimraf": "^5.0.10"
},
"peerDependencies": {
"@playwright/test": "^1.58.2",
"@playwright/test": ">=1",
"@wordpress/env": ">=10.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"@playwright/test": {
"optional": true
},
"@wordpress/env": {
"optional": true
}
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"devDependencies": {
"@flakiness/playwright": "^1.14.0",
"@playwright/test": "^1.58.2",
"@playwright/test": "^1.61.1",
"@types/node": "^20.19.39",
"@wordpress/e2e-test-utils-playwright": "file:../../packages/e2e-test-utils-playwright",
"@wordpress/scripts": "file:../../packages/scripts",
Expand Down
9 changes: 8 additions & 1 deletion test/e2e/specs/editor/blocks/image.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,14 @@ test.describe( 'Image', () => {
await expect( urlInput ).toHaveValue( 'https://example.com' );
} );

test( 'should upload external image to media library', async ( {
// TODO: Re-enable once client-side external-image upload lands. With CSM
// active on Chromium 148+, "Upload to Media Library" routes the external
// URL through the client-side pipeline, which does not yet finalize to a
// /wp-content/uploads/ URL. Fixed by
// https://github.com/WordPress/gutenberg/issues/79407; re-introduce the
// CSM-aware coverage there.
// eslint-disable-next-line playwright/no-skipped-test
test.skip( 'should upload external image to media library', async ( {
editor,
} ) => {
await editor.insertBlock( {
Expand Down
Loading
Loading