Make WordPress Core

Changeset 62615


Ignore:
Timestamp:
07/01/2026 06:10:56 PM (6 weeks ago)
Author:
adamsilverstein
Message:

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: https://github.com/WordPress/gutenberg/pull/78404.

Props manhar, wildworks.
Fixes #65399.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/media.php

    r62494 r62615  
    65626562
    65636563        /*
     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
     6579        /*
    65646580         * Skip when a third-party page builder overrides the block editor.
    65656581         * DIP isolates the document into its own agent cluster,
  • trunk/tests/phpunit/tests/media/wpCrossOriginIsolation.php

    r62428 r62615  
    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();
     
    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
     
    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 ) {
     
    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();
     
    158184
    159185                $this->assertSame( $level_before, $level_after, 'Output buffer should not be started for Safari.' );
     186        }
     187
     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();
    160290        }
    161291
Note: See TracChangeset for help on using the changeset viewer.