diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index c40f7fb6984b9..fd215c800f9dc 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -6411,14 +6411,18 @@ function wp_get_image_editor_output_format( $filename, $mime_type ) { * @return bool Whether client-side media processing is enabled. */ function wp_is_client_side_media_processing_enabled(): bool { + // This is due to SharedArrayBuffer requiring a secure context. + $host = strtolower( (string) strtok( $_SERVER['HTTP_HOST'] ?? '', ':' ) ); + $enabled = ( is_ssl() || 'localhost' === $host || str_ends_with( $host, '.localhost' ) ); + /** * Filters whether client-side media processing is enabled. * * @since 7.0.0 * - * @param bool $enabled Whether client-side media processing is enabled. Default true. + * @param bool $enabled Whether client-side media processing is enabled. Default true if the page is served in a secure context. */ - return (bool) apply_filters( 'wp_client_side_media_processing_enabled', true ); + return (bool) apply_filters( 'wp_client_side_media_processing_enabled', $enabled ); } /** @@ -6431,7 +6435,7 @@ function wp_set_client_side_media_processing_flag(): void { return; } - wp_add_inline_script( 'wp-block-editor', 'window.__clientSideMediaProcessing = true', 'before' ); + wp_add_inline_script( 'wp-block-editor', 'window.__clientSideMediaProcessing = true;', 'before' ); $chromium_version = wp_get_chromium_major_version(); @@ -6477,6 +6481,10 @@ function wp_get_chromium_major_version(): ?int { * media processing in the editor. Uses Document-Isolation-Policy * on supported browsers (Chromium 137+). * + * Skips setup when a third-party page builder overrides the block + * editor via a custom `action` query parameter, as DIP would block + * same-origin iframe access that these editors rely on. + * * @since 7.0.0 */ function wp_set_up_cross_origin_isolation(): void { @@ -6494,6 +6502,15 @@ function wp_set_up_cross_origin_isolation(): void { 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; diff --git a/tests/phpunit/tests/media/wpCrossOriginIsolation.php b/tests/phpunit/tests/media/wpCrossOriginIsolation.php index 31f2e85975ee0..4fe5723bdc426 100644 --- a/tests/phpunit/tests/media/wpCrossOriginIsolation.php +++ b/tests/phpunit/tests/media/wpCrossOriginIsolation.php @@ -6,19 +6,36 @@ * @group media * @covers ::wp_set_up_cross_origin_isolation * @covers ::wp_start_cross_origin_isolation_output_buffer + * @covers ::wp_is_client_side_media_processing_enabled */ class Tests_Media_wpCrossOriginIsolation extends WP_UnitTestCase { /** * Original HTTP_USER_AGENT value. - * - * @var string|null */ - private $original_user_agent; + private ?string $original_user_agent; + + /** + * Original HTTP_HOST value. + */ + private ?string $original_http_host; + + /** + * Original HTTPS value. + */ + private ?string $original_https; + + /** + * Original $_GET['action'] value. + */ + private ?string $original_get_action; public function set_up() { parent::set_up(); - $this->original_user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null; + $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; } public function tear_down() { @@ -28,6 +45,24 @@ public function tear_down() { $_SERVER['HTTP_USER_AGENT'] = $this->original_user_agent; } + if ( null === $this->original_http_host ) { + unset( $_SERVER['HTTP_HOST'] ); + } else { + $_SERVER['HTTP_HOST'] = $this->original_http_host; + } + + if ( null === $this->original_https ) { + unset( $_SERVER['HTTPS'] ); + } else { + $_SERVER['HTTPS'] = $this->original_https; + } + + if ( null === $this->original_get_action ) { + unset( $_GET['action'] ); + } else { + $_GET['action'] = $this->original_get_action; + } + // Clean up any output buffers started during tests. while ( ob_get_level() > 1 ) { ob_end_clean(); @@ -124,6 +159,32 @@ public function test_does_not_start_output_buffer_for_safari() { $this->assertSame( $level_before, $level_after, 'Output buffer should not be started for Safari.' ); } + /** + * @ticket 64803 + */ + public function test_client_side_processing_disabled_on_non_secure_origin() { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['HTTPS'] = ''; + + $this->assertFalse( + wp_is_client_side_media_processing_enabled(), + 'Client-side media processing should be disabled on non-secure, non-localhost origins.' + ); + } + + /** + * @ticket 64803 + */ + public function test_client_side_processing_enabled_on_localhost() { + $_SERVER['HTTP_HOST'] = 'localhost'; + $_SERVER['HTTPS'] = ''; + + $this->assertTrue( + wp_is_client_side_media_processing_enabled(), + 'Client-side media processing should be enabled on localhost.' + ); + } + /** * This test must run in a separate process because the output buffer * callback sends HTTP headers via header(), which would fail in the diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index 93cd4211c93ba..7fa4545a38d9c 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -3162,6 +3162,12 @@ static function ( $data ) use ( &$captured_data ) { * @requires function imagejpeg */ public function test_sideload_scaled_image() { + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + // Reinitialize REST server so the sideload route is registered. + global $wp_rest_server; + $wp_rest_server = new Spy_REST_Server(); + do_action( 'rest_api_init', $wp_rest_server ); + wp_set_current_user( self::$author_id ); // First, create an attachment. @@ -3215,6 +3221,12 @@ public function test_sideload_scaled_image() { * @requires function imagejpeg */ public function test_sideload_scaled_image_requires_auth() { + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + // Reinitialize REST server so the sideload route is registered. + global $wp_rest_server; + $wp_rest_server = new Spy_REST_Server(); + do_action( 'rest_api_init', $wp_rest_server ); + wp_set_current_user( self::$author_id ); // Create an attachment. @@ -3244,6 +3256,12 @@ public function test_sideload_scaled_image_requires_auth() { * @ticket 64737 */ public function test_sideload_route_includes_scaled_enum() { + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + // Reinitialize REST server so the sideload route is registered. + global $wp_rest_server; + $wp_rest_server = new Spy_REST_Server(); + do_action( 'rest_api_init', $wp_rest_server ); + $server = rest_get_server(); $routes = $server->get_routes(); @@ -3266,6 +3284,12 @@ public function test_sideload_route_includes_scaled_enum() { * @requires function imagejpeg */ public function test_sideload_scaled_unique_filename() { + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + // Reinitialize REST server so the sideload route is registered. + global $wp_rest_server; + $wp_rest_server = new Spy_REST_Server(); + do_action( 'rest_api_init', $wp_rest_server ); + wp_set_current_user( self::$author_id ); // Create an attachment. @@ -3300,6 +3324,12 @@ public function test_sideload_scaled_unique_filename() { * @requires function imagejpeg */ public function test_sideload_scaled_unique_filename_conflict() { + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + // Reinitialize REST server so the sideload route is registered. + global $wp_rest_server; + $wp_rest_server = new Spy_REST_Server(); + do_action( 'rest_api_init', $wp_rest_server ); + wp_set_current_user( self::$author_id ); // Create the first attachment. diff --git a/tests/phpunit/tests/rest-api/rest-schema-setup.php b/tests/phpunit/tests/rest-api/rest-schema-setup.php index 0e0e00b934359..01dad836981bc 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-setup.php +++ b/tests/phpunit/tests/rest-api/rest-schema-setup.php @@ -16,6 +16,9 @@ class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { public function set_up() { parent::set_up(); + // Ensure client-side media processing is enabled so the sideload route is registered. + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + /** @var WP_REST_Server $wp_rest_server */ global $wp_rest_server; $wp_rest_server = new Spy_REST_Server();