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.' );
+ }
}