eractive elements. DIP would block that same-origin access. * * Keyed off $pagenow rather than the current screen so the guard keeps * working if the header set-up is ever moved to an earlier hook (such as * admin_init) where the screen is not yet available. */ global $pagenow; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( 'site-editor.php' === $pagenow && ! 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, * which blocks same-origin iframe access that these editors rely on. */ if ( isset( $_GET['action'] ) && 'edit' !== $_GET['action'] ) { return; } // Cross-origin isolation is not needed if users can't upload files anyway. if ( ! current_user_can( 'upload_files' ) ) { return; } wp_start_cross_origin_isolation_output_buffer(); } /** * Sends the Document-Isolation-Policy header for cross-origin isolation. * * Uses an output buffer to add crossorigin="anonymous" where needed. * * @since 7.1.0 */ function wp_start_cross_origin_isolation_output_buffer(): void { $chromium_version = wp_get_chromium_major_version(); if ( null === $chromium_version || $chromium_version < 137 ) { return; } ob_start( static function ( string $output ): string { header( 'Document-Isolation-Policy: isolate-and-credentialless' ); return wp_add_crossorigin_attributes( $output ); } ); } /** * Adds crossorigin="anonymous" to relevant tags in the given HTML string. * * @since 7.1.0 * * @param string $html HTML input. * @return string Modified HTML. */ function wp_add_crossorigin_attributes( string $html ): string { $site_url = site_url(); $processor = new WP_HTML_Tag_Processor( $html ); // See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin. $cross_origin_tag_attributes = array( 'AUDIO' => array( 'src' ), 'LINK' => array( 'href' ), 'SCRIPT' => array( 'src' ), 'VIDEO' => array( 'src', 'poster' ), 'SOURCE' => array( 'src' ), ); while ( $processor->next_tag() ) { $tag = $processor->get_tag(); if ( ! isset( $cross_origin_tag_attributes[ $tag ] ) ) { continue; } $crossorigin = $processor->get_attribute( 'crossorigin' ); if ( null !== $crossorigin ) { continue; } if ( 'AUDIO' === $tag || 'VIDEO' === $tag ) { $processor->set_bookmark( 'audio-video-parent' ); } $processor->set_bookmark( 'resume' ); $sought = false; $is_cross_origin = false; foreach ( $cross_origin_tag_attributes[ $tag ] as $attr ) { $url = $processor->get_attribute( $attr ); if ( is_string( $url ) && ! str_starts_with( $url, $site_url ) && ! str_starts_with( $url, '/' ) ) { $is_cross_origin = true; } if ( $is_cross_origin ) { break; } } if ( $is_cross_origin ) { if ( 'SOURCE' === $tag ) { $sought = $processor->seek( 'audio-video-parent' ); if ( $sought ) { $processor->set_attribute( 'crossorigin', 'anonymous' ); } } else { $processor->set_attribute( 'crossorigin', 'anonymous' ); } if ( $sought ) { $processor->seek( 'resume' ); $processor->release_bookmark( 'audio-video-parent' ); } } } return $processor->get_updated_html(); }