diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index fd215c800f9dc..9bf36efae81f9 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -6558,14 +6558,7 @@ function wp_add_crossorigin_attributes( string $html ): string { // See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin. $cross_origin_tag_attributes = array( 'AUDIO' => array( 'src' => false ), - 'IMG' => array( - 'src' => false, - 'srcset' => true, - ), - 'LINK' => array( - 'href' => false, - 'imagesrcset' => true, - ), + 'LINK' => array( 'href' => false ), 'SCRIPT' => array( 'src' => false ), 'VIDEO' => array( 'src' => false, diff --git a/tests/phpunit/tests/media/wpCrossOriginIsolation.php b/tests/phpunit/tests/media/wpCrossOriginIsolation.php index 4fe5723bdc426..3ec4231d5bede 100644 --- a/tests/phpunit/tests/media/wpCrossOriginIsolation.php +++ b/tests/phpunit/tests/media/wpCrossOriginIsolation.php @@ -186,28 +186,180 @@ public function test_client_side_processing_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 - * main PHPUnit process where output has already started. + * Verifies that cross-origin elements get crossorigin="anonymous" added. * * @ticket 64766 * * @runInSeparateProcess * @preserveGlobalState disabled + * + * @dataProvider data_elements_that_should_get_crossorigin + * + * @param string $html HTML input to process. */ - public function test_output_buffer_adds_crossorigin_attributes() { + public function test_output_buffer_adds_crossorigin( $html ) { $_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'; - // Start an outer buffer to capture the callback-processed output. ob_start(); wp_start_cross_origin_isolation_output_buffer(); - echo ''; + echo $html; - // Flush the inner buffer to trigger the callback, sending processed output to the outer buffer. ob_end_flush(); $output = ob_get_clean(); $this->assertStringContainsString( 'crossorigin="anonymous"', $output ); } + + /** + * Data provider for elements that should receive crossorigin="anonymous". + * + * @return array[] + */ + public function data_elements_that_should_get_crossorigin() { + return array( + 'cross-origin script' => array( + '', + ), + 'cross-origin audio' => array( + '', + ), + 'cross-origin video' => array( + '', + ), + 'cross-origin link stylesheet' => array( + '', + ), + 'cross-origin source inside video' => array( + '', + ), + ); + } + + /** + * Verifies that certain elements do not get crossorigin="anonymous" added. + * + * Images are excluded because under Document-Isolation-Policy: + * isolate-and-credentialless, the browser handles cross-origin images + * in credentialless mode without needing explicit CORS headers. + * + * @ticket 64766 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + * + * @dataProvider data_elements_that_should_not_get_crossorigin + * + * @param string $html HTML input to process. + */ + public function test_output_buffer_does_not_add_crossorigin( $html ) { + $_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'; + + ob_start(); + + wp_start_cross_origin_isolation_output_buffer(); + echo $html; + + ob_end_flush(); + $output = ob_get_clean(); + + $this->assertStringNotContainsString( 'crossorigin="anonymous"', $output ); + } + + /** + * Data provider for elements that should not receive crossorigin="anonymous". + * + * @return array[] + */ + public function data_elements_that_should_not_get_crossorigin() { + return array( + 'cross-origin img' => array( + '', + ), + 'cross-origin img with srcset' => array( + '', + ), + 'link with cross-origin imagesrcset only' => array( + '', + ), + 'relative URL script' => array( + '', + ), + ); + } + + /** + * Same-origin URLs should not get crossorigin="anonymous". + * + * Uses site_url() at runtime since the test domain varies by CI config. + * + * @ticket 64766 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_output_buffer_does_not_add_crossorigin_to_same_origin() { + $_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'; + + ob_start(); + + wp_start_cross_origin_isolation_output_buffer(); + echo ''; + + ob_end_flush(); + $output = ob_get_clean(); + + $this->assertStringNotContainsString( 'crossorigin="anonymous"', $output ); + } + + /** + * Elements that already have a crossorigin attribute should not be modified. + * + * @ticket 64766 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_output_buffer_does_not_override_existing_crossorigin() { + $_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'; + + ob_start(); + + wp_start_cross_origin_isolation_output_buffer(); + echo ''; + + ob_end_flush(); + $output = ob_get_clean(); + + $this->assertStringContainsString( 'crossorigin="use-credentials"', $output, 'Existing crossorigin attribute should not be overridden.' ); + $this->assertStringNotContainsString( 'crossorigin="anonymous"', $output ); + } + + /** + * Multiple tags in the same output should each be handled correctly. + * + * @ticket 64766 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_output_buffer_handles_mixed_tags() { + $_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'; + + ob_start(); + + wp_start_cross_origin_isolation_output_buffer(); + echo ''; + echo ''; + echo ''; + + ob_end_flush(); + $output = ob_get_clean(); + + // IMG should NOT have crossorigin. + $this->assertStringContainsString( '', $output, 'IMG should not be modified.' ); + + // Script and audio should have crossorigin. + $this->assertSame( 2, substr_count( $output, 'crossorigin="anonymous"' ), 'Script and audio should both get crossorigin, but not img.' ); + } }