From 84902ef7caf72c56583109d5c8ae314dab9733b6 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Thu, 28 May 2026 13:56:43 -0400 Subject: [PATCH 1/2] Media: Skip Document-Isolation-Policy on the classic-theme site preview. The site editor renders the front end of a classic theme in a same-origin iframe and must reach the iframe's `contentDocument` to neutralize its interactive elements. Document-Isolation-Policy isolates the editor into its own agent cluster, which blocks that same-origin access. Skip cross-origin isolation in `wp_set_up_cross_origin_isolation()` for the classic-theme site editor home route so the preview keeps working. Block themes and other routes are unaffected. Backport of WordPress/gutenberg#78404. --- src/wp-includes/media.php | 9 ++ .../tests/media/wpCrossOriginIsolation.php | 115 ++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index d318a275a9607..a40cd7e7768b6 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -6549,6 +6549,15 @@ function wp_set_up_cross_origin_isolation(): void { return; } + /* + * Skip when rendering the classic-theme home route, which shows the site + * preview in an iframe and must reach its `contentDocument` to neutralize + * interactive elements. DIP would block that same-origin access. + */ + if ( 'site-editor' === $screen->id && ! wp_is_block_theme() && ( ! isset( $_GET['p'] ) || '/' === $_GET['p'] ) ) { + return; + } + /* * Skip when a third-party page builder overrides the block editor. * DIP isolates the document into its own agent cluster, diff --git a/tests/phpunit/tests/media/wpCrossOriginIsolation.php b/tests/phpunit/tests/media/wpCrossOriginIsolation.php index 3ec4231d5bede..d0328b17dcb55 100644 --- a/tests/phpunit/tests/media/wpCrossOriginIsolation.php +++ b/tests/phpunit/tests/media/wpCrossOriginIsolation.php @@ -30,12 +30,18 @@ class Tests_Media_wpCrossOriginIsolation extends WP_UnitTestCase { */ private ?string $original_get_action; + /** + * Original $_GET['p'] value. + */ + private ?string $original_get_p; + public function set_up() { parent::set_up(); $this->original_user_agent = $_SERVER['HTTP_USER_AGENT'] ?? null; $this->original_http_host = $_SERVER['HTTP_HOST'] ?? null; $this->original_https = $_SERVER['HTTPS'] ?? null; $this->original_get_action = $_GET['action'] ?? null; + $this->original_get_p = $_GET['p'] ?? null; } public function tear_down() { @@ -63,11 +69,19 @@ public function tear_down() { $_GET['action'] = $this->original_get_action; } + if ( null === $this->original_get_p ) { + unset( $_GET['p'] ); + } else { + $_GET['p'] = $this->original_get_p; + } + // Clean up any output buffers started during tests. while ( ob_get_level() > 1 ) { ob_end_clean(); } + $GLOBALS['current_screen'] = null; + remove_all_filters( 'wp_client_side_media_processing_enabled' ); parent::tear_down(); } @@ -159,6 +173,107 @@ public function test_does_not_start_output_buffer_for_safari() { $this->assertSame( $level_before, $level_after, 'Output buffer should not be started for Safari.' ); } + /** + * The site editor home route on a classic theme skips DIP, because the + * editor renders the front end in a same-origin iframe and must reach its + * `contentDocument` to neutralize interactive elements. DIP would block + * that access. + * + * @ticket 64766 + * + * @dataProvider data_classic_theme_site_editor_home_routes + * + * @param array $get The $_GET state representing the home route. + */ + public function test_skips_cross_origin_isolation_for_classic_theme_site_editor_home( array $get ) { + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'; + $_SERVER['HTTP_HOST'] = 'localhost'; + + wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + switch_theme( 'twentytwentyone' ); + set_current_screen( 'site-editor' ); + + unset( $_GET['p'] ); + foreach ( $get as $key => $value ) { + $_GET[ $key ] = $value; + } + + $level_before = ob_get_level(); + wp_set_up_cross_origin_isolation(); + $level_after = ob_get_level(); + + $this->assertSame( $level_before, $level_after, 'DIP should be skipped on the classic-theme site editor home route.' ); + } + + /** + * Data provider for the classic-theme site editor home route. + * + * @return array[] + */ + public function data_classic_theme_site_editor_home_routes() { + return array( + 'no p query var' => array( array() ), + 'p query var is /' => array( array( 'p' => '/' ) ), + ); + } + + /** + * The site editor on a classic theme still sets up cross-origin isolation + * for routes other than the home route. + * + * @ticket 64766 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_sets_up_cross_origin_isolation_for_classic_theme_site_editor_non_home_route() { + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'; + $_SERVER['HTTP_HOST'] = 'localhost'; + + wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + switch_theme( 'twentytwentyone' ); + set_current_screen( 'site-editor' ); + + $_GET['p'] = '/page/about'; + + $level_before = ob_get_level(); + wp_set_up_cross_origin_isolation(); + $level_after = ob_get_level(); + + $this->assertSame( $level_before + 1, $level_after, 'DIP should be set up on a non-home site editor route.' ); + + ob_end_clean(); + } + + /** + * The site editor on a block theme always sets up cross-origin isolation, + * including on the home route, because block themes do not render the + * classic site preview iframe. + * + * @ticket 64766 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_sets_up_cross_origin_isolation_for_block_theme_site_editor_home() { + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'; + $_SERVER['HTTP_HOST'] = 'localhost'; + + wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + switch_theme( 'twentytwentyfour' ); + set_current_screen( 'site-editor' ); + + unset( $_GET['p'] ); + + $level_before = ob_get_level(); + wp_set_up_cross_origin_isolation(); + $level_after = ob_get_level(); + + $this->assertSame( $level_before + 1, $level_after, 'DIP should be set up on the block-theme site editor home route.' ); + + ob_end_clean(); + } + /** * @ticket 64803 */ From eabd2c2e89b8123234d4f8d97bed3709e034315f Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Wed, 3 Jun 2026 10:16:34 +0200 Subject: [PATCH 2/2] Update @ticket references to 65399 for the classic-theme site preview tests --- tests/phpunit/tests/media/wpCrossOriginIsolation.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/media/wpCrossOriginIsolation.php b/tests/phpunit/tests/media/wpCrossOriginIsolation.php index d0328b17dcb55..3ba8aadd9c6c0 100644 --- a/tests/phpunit/tests/media/wpCrossOriginIsolation.php +++ b/tests/phpunit/tests/media/wpCrossOriginIsolation.php @@ -179,7 +179,7 @@ public function test_does_not_start_output_buffer_for_safari() { * `contentDocument` to neutralize interactive elements. DIP would block * that access. * - * @ticket 64766 + * @ticket 65399 * * @dataProvider data_classic_theme_site_editor_home_routes * @@ -221,7 +221,7 @@ public function data_classic_theme_site_editor_home_routes() { * The site editor on a classic theme still sets up cross-origin isolation * for routes other than the home route. * - * @ticket 64766 + * @ticket 65399 * * @runInSeparateProcess * @preserveGlobalState disabled @@ -250,7 +250,7 @@ public function test_sets_up_cross_origin_isolation_for_classic_theme_site_edito * including on the home route, because block themes do not render the * classic site preview iframe. * - * @ticket 64766 + * @ticket 65399 * * @runInSeparateProcess * @preserveGlobalState disabled