Skip to content

Commit 711fa9c

Browse files
Editor: 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 `?wp_site_preview=1` 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. See related Gutenberg pull request: WordPress/gutenberg#78404. Props manhar, wildworks. Fixes #65399. git-svn-id: https://develop.svn.wordpress.org/trunk@62615 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 6b4f9af commit 711fa9c

2 files changed

Lines changed: 146 additions & 0 deletions

File tree

src/wp-includes/media.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6560,6 +6560,22 @@ function wp_set_up_cross_origin_isolation(): void {
65606560
return;
65616561
}
65626562

6563+
/*
6564+
* Skip when rendering the classic-theme home route, which shows the site
6565+
* preview in an iframe and must reach its `contentDocument` to neutralize
6566+
* interactive elements. DIP would block that same-origin access.
6567+
*
6568+
* Keyed off $pagenow rather than the current screen so the guard keeps
6569+
* working if the header set-up is ever moved to an earlier hook (such as
6570+
* admin_init) where the screen is not yet available.
6571+
*/
6572+
global $pagenow;
6573+
6574+
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
6575+
if ( 'site-editor.php' === $pagenow && ! wp_is_block_theme() && ( ! isset( $_GET['p'] ) || '/' === $_GET['p'] ) ) {
6576+
return;
6577+
}
6578+
65636579
/*
65646580
* Skip when a third-party page builder overrides the block editor.
65656581
* DIP isolates the document into its own agent cluster,

tests/phpunit/tests/media/wpCrossOriginIsolation.php

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,24 @@ class Tests_Media_wpCrossOriginIsolation extends WP_UnitTestCase {
3030
*/
3131
private ?string $original_get_action;
3232

33+
/**
34+
* Original $_GET['p'] value.
35+
*/
36+
private ?string $original_get_p;
37+
38+
/**
39+
* Original $pagenow value.
40+
*/
41+
private ?string $original_pagenow;
42+
3343
public function set_up() {
3444
parent::set_up();
3545
$this->original_user_agent = $_SERVER['HTTP_USER_AGENT'] ?? null;
3646
$this->original_http_host = $_SERVER['HTTP_HOST'] ?? null;
3747
$this->original_https = $_SERVER['HTTPS'] ?? null;
3848
$this->original_get_action = $_GET['action'] ?? null;
49+
$this->original_get_p = $_GET['p'] ?? null;
50+
$this->original_pagenow = $GLOBALS['pagenow'] ?? null;
3951
}
4052

4153
public function tear_down() {
@@ -63,11 +75,25 @@ public function tear_down() {
6375
$_GET['action'] = $this->original_get_action;
6476
}
6577

78+
if ( null === $this->original_get_p ) {
79+
unset( $_GET['p'] );
80+
} else {
81+
$_GET['p'] = $this->original_get_p;
82+
}
83+
6684
// Clean up any output buffers started during tests.
6785
while ( ob_get_level() > 1 ) {
6886
ob_end_clean();
6987
}
7088

89+
if ( null === $this->original_pagenow ) {
90+
unset( $GLOBALS['pagenow'] );
91+
} else {
92+
$GLOBALS['pagenow'] = $this->original_pagenow;
93+
}
94+
95+
$GLOBALS['current_screen'] = null;
96+
7197
remove_all_filters( 'wp_client_side_media_processing_enabled' );
7298
parent::tear_down();
7399
}
@@ -159,6 +185,110 @@ public function test_does_not_start_output_buffer_for_safari() {
159185
$this->assertSame( $level_before, $level_after, 'Output buffer should not be started for Safari.' );
160186
}
161187

188+
/**
189+
* The site editor home route on a classic theme skips DIP, because the
190+
* editor renders the front end in a same-origin iframe and must reach its
191+
* `contentDocument` to neutralize interactive elements. DIP would block
192+
* that access.
193+
*
194+
* @ticket 65399
195+
*
196+
* @dataProvider data_classic_theme_site_editor_home_routes
197+
*
198+
* @param array $get The $_GET state representing the home route.
199+
*/
200+
public function test_skips_cross_origin_isolation_for_classic_theme_site_editor_home( array $get ) {
201+
$_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';
202+
$_SERVER['HTTP_HOST'] = 'localhost';
203+
204+
wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
205+
switch_theme( 'twentytwentyone' );
206+
set_current_screen( 'site-editor' );
207+
$GLOBALS['pagenow'] = 'site-editor.php';
208+
209+
unset( $_GET['p'] );
210+
foreach ( $get as $key => $value ) {
211+
$_GET[ $key ] = $value;
212+
}
213+
214+
$level_before = ob_get_level();
215+
wp_set_up_cross_origin_isolation();
216+
$level_after = ob_get_level();
217+
218+
$this->assertSame( $level_before, $level_after, 'DIP should be skipped on the classic-theme site editor home route.' );
219+
}
220+
221+
/**
222+
* Data provider for the classic-theme site editor home route.
223+
*
224+
* @return array[]
225+
*/
226+
public function data_classic_theme_site_editor_home_routes() {
227+
return array(
228+
'no p query var' => array( array() ),
229+
'p query var is /' => array( array( 'p' => '/' ) ),
230+
);
231+
}
232+
233+
/**
234+
* The site editor on a classic theme still sets up cross-origin isolation
235+
* for routes other than the home route.
236+
*
237+
* @ticket 65399
238+
*
239+
* @runInSeparateProcess
240+
* @preserveGlobalState disabled
241+
*/
242+
public function test_sets_up_cross_origin_isolation_for_classic_theme_site_editor_non_home_route() {
243+
$_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';
244+
$_SERVER['HTTP_HOST'] = 'localhost';
245+
246+
wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
247+
switch_theme( 'twentytwentyone' );
248+
set_current_screen( 'site-editor' );
249+
$GLOBALS['pagenow'] = 'site-editor.php';
250+
251+
$_GET['p'] = '/page/about';
252+
253+
$level_before = ob_get_level();
254+
wp_set_up_cross_origin_isolation();
255+
$level_after = ob_get_level();
256+
257+
$this->assertSame( $level_before + 1, $level_after, 'DIP should be set up on a non-home site editor route.' );
258+
259+
ob_end_clean();
260+
}
261+
262+
/**
263+
* The site editor on a block theme always sets up cross-origin isolation,
264+
* including on the home route, because block themes do not render the
265+
* classic site preview iframe.
266+
*
267+
* @ticket 65399
268+
*
269+
* @runInSeparateProcess
270+
* @preserveGlobalState disabled
271+
*/
272+
public function test_sets_up_cross_origin_isolation_for_block_theme_site_editor_home() {
273+
$_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';
274+
$_SERVER['HTTP_HOST'] = 'localhost';
275+
276+
wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
277+
switch_theme( 'twentytwentyfour' );
278+
set_current_screen( 'site-editor' );
279+
$GLOBALS['pagenow'] = 'site-editor.php';
280+
281+
unset( $_GET['p'] );
282+
283+
$level_before = ob_get_level();
284+
wp_set_up_cross_origin_isolation();
285+
$level_after = ob_get_level();
286+
287+
$this->assertSame( $level_before + 1, $level_after, 'DIP should be set up on the block-theme site editor home route.' );
288+
289+
ob_end_clean();
290+
}
291+
162292
/**
163293
* @ticket 64803
164294
*/

0 commit comments

Comments
 (0)