Skip to content

Commit b6d7a93

Browse files
authored
Merge branch 'trunk' into add/grunt-clean-gutenberg
2 parents 9af0281 + 56feb3f commit b6d7a93

5 files changed

Lines changed: 199 additions & 16 deletions

File tree

.github/pull_request_template.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ Trac ticket: <!-- insert a link to the WordPress Trac ticket here -->
2323

2424
<!--
2525
You are free to use artificial intelligence (AI) tooling to contribute, but you must disclose what tooling you are using and to what extent a pull request has been authored by AI. It is your responsibility to review and take responsibility for what AI generates. See the WordPress AI Guidelines: <https://make.wordpress.org/ai/handbook/ai-guidelines/>.
26+
27+
Example disclosure:
28+
29+
AI assistance: Yes
30+
Tool(s): GitHub Copilot, ChatGPT
31+
Model(s): GPT-5.1
32+
Used for: Initial code skeleton and test suggestions; final implementation and tests were reviewed and edited by me.
2633
-->
2734

2835
---

src/wp-includes/class-wp-query.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,7 @@ public function parse_tax_query( &$query_vars ) {
11891189
'field' => 'slug',
11901190
);
11911191

1192-
if ( ! empty( $t->rewrite['hierarchical'] ) ) {
1192+
if ( is_string( $query_vars[ $t->query_var ] ) && ! empty( $t->rewrite['hierarchical'] ) ) {
11931193
$query_vars[ $t->query_var ] = wp_basename( $query_vars[ $t->query_var ] );
11941194
}
11951195

src/wp-includes/media.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6558,14 +6558,7 @@ function wp_add_crossorigin_attributes( string $html ): string {
65586558
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin.
65596559
$cross_origin_tag_attributes = array(
65606560
'AUDIO' => array( 'src' => false ),
6561-
'IMG' => array(
6562-
'src' => false,
6563-
'srcset' => true,
6564-
),
6565-
'LINK' => array(
6566-
'href' => false,
6567-
'imagesrcset' => true,
6568-
),
6561+
'LINK' => array( 'href' => false ),
65696562
'SCRIPT' => array( 'src' => false ),
65706563
'VIDEO' => array(
65716564
'src' => false,

tests/phpunit/tests/media/wpCrossOriginIsolation.php

Lines changed: 159 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,28 +186,180 @@ public function test_client_side_processing_enabled_on_localhost() {
186186
}
187187

188188
/**
189-
* This test must run in a separate process because the output buffer
190-
* callback sends HTTP headers via header(), which would fail in the
191-
* main PHPUnit process where output has already started.
189+
* Verifies that cross-origin elements get crossorigin="anonymous" added.
192190
*
193191
* @ticket 64766
194192
*
195193
* @runInSeparateProcess
196194
* @preserveGlobalState disabled
195+
*
196+
* @dataProvider data_elements_that_should_get_crossorigin
197+
*
198+
* @param string $html HTML input to process.
197199
*/
198-
public function test_output_buffer_adds_crossorigin_attributes() {
200+
public function test_output_buffer_adds_crossorigin( $html ) {
199201
$_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';
200202

201-
// Start an outer buffer to capture the callback-processed output.
202203
ob_start();
203204

204205
wp_start_cross_origin_isolation_output_buffer();
205-
echo '<img src="https://external.example.com/image.jpg" />';
206+
echo $html;
206207

207-
// Flush the inner buffer to trigger the callback, sending processed output to the outer buffer.
208208
ob_end_flush();
209209
$output = ob_get_clean();
210210

211211
$this->assertStringContainsString( 'crossorigin="anonymous"', $output );
212212
}
213+
214+
/**
215+
* Data provider for elements that should receive crossorigin="anonymous".
216+
*
217+
* @return array[]
218+
*/
219+
public function data_elements_that_should_get_crossorigin() {
220+
return array(
221+
'cross-origin script' => array(
222+
'<script src="https://external.example.com/script.js"></script>',
223+
),
224+
'cross-origin audio' => array(
225+
'<audio src="https://external.example.com/audio.mp3"></audio>',
226+
),
227+
'cross-origin video' => array(
228+
'<video src="https://external.example.com/video.mp4"></video>',
229+
),
230+
'cross-origin link stylesheet' => array(
231+
'<link rel="stylesheet" href="https://external.example.com/style.css" />',
232+
),
233+
'cross-origin source inside video' => array(
234+
'<video><source src="https://external.example.com/video.mp4" type="video/mp4" /></video>',
235+
),
236+
);
237+
}
238+
239+
/**
240+
* Verifies that certain elements do not get crossorigin="anonymous" added.
241+
*
242+
* Images are excluded because under Document-Isolation-Policy:
243+
* isolate-and-credentialless, the browser handles cross-origin images
244+
* in credentialless mode without needing explicit CORS headers.
245+
*
246+
* @ticket 64766
247+
*
248+
* @runInSeparateProcess
249+
* @preserveGlobalState disabled
250+
*
251+
* @dataProvider data_elements_that_should_not_get_crossorigin
252+
*
253+
* @param string $html HTML input to process.
254+
*/
255+
public function test_output_buffer_does_not_add_crossorigin( $html ) {
256+
$_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';
257+
258+
ob_start();
259+
260+
wp_start_cross_origin_isolation_output_buffer();
261+
echo $html;
262+
263+
ob_end_flush();
264+
$output = ob_get_clean();
265+
266+
$this->assertStringNotContainsString( 'crossorigin="anonymous"', $output );
267+
}
268+
269+
/**
270+
* Data provider for elements that should not receive crossorigin="anonymous".
271+
*
272+
* @return array[]
273+
*/
274+
public function data_elements_that_should_not_get_crossorigin() {
275+
return array(
276+
'cross-origin img' => array(
277+
'<img src="https://external.example.com/image.jpg" />',
278+
),
279+
'cross-origin img with srcset' => array(
280+
'<img src="https://external.example.com/image.jpg" srcset="https://external.example.com/image-2x.jpg 2x" />',
281+
),
282+
'link with cross-origin imagesrcset only' => array(
283+
'<link rel="preload" as="image" imagesrcset="https://external.example.com/image.jpg 1x" href="/local-fallback.jpg" />',
284+
),
285+
'relative URL script' => array(
286+
'<script src="/wp-includes/js/wp-embed.min.js"></script>',
287+
),
288+
);
289+
}
290+
291+
/**
292+
* Same-origin URLs should not get crossorigin="anonymous".
293+
*
294+
* Uses site_url() at runtime since the test domain varies by CI config.
295+
*
296+
* @ticket 64766
297+
*
298+
* @runInSeparateProcess
299+
* @preserveGlobalState disabled
300+
*/
301+
public function test_output_buffer_does_not_add_crossorigin_to_same_origin() {
302+
$_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';
303+
304+
ob_start();
305+
306+
wp_start_cross_origin_isolation_output_buffer();
307+
echo '<script src="' . site_url( '/wp-includes/js/wp-embed.min.js' ) . '"></script>';
308+
309+
ob_end_flush();
310+
$output = ob_get_clean();
311+
312+
$this->assertStringNotContainsString( 'crossorigin="anonymous"', $output );
313+
}
314+
315+
/**
316+
* Elements that already have a crossorigin attribute should not be modified.
317+
*
318+
* @ticket 64766
319+
*
320+
* @runInSeparateProcess
321+
* @preserveGlobalState disabled
322+
*/
323+
public function test_output_buffer_does_not_override_existing_crossorigin() {
324+
$_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';
325+
326+
ob_start();
327+
328+
wp_start_cross_origin_isolation_output_buffer();
329+
echo '<script src="https://external.example.com/script.js" crossorigin="use-credentials"></script>';
330+
331+
ob_end_flush();
332+
$output = ob_get_clean();
333+
334+
$this->assertStringContainsString( 'crossorigin="use-credentials"', $output, 'Existing crossorigin attribute should not be overridden.' );
335+
$this->assertStringNotContainsString( 'crossorigin="anonymous"', $output );
336+
}
337+
338+
/**
339+
* Multiple tags in the same output should each be handled correctly.
340+
*
341+
* @ticket 64766
342+
*
343+
* @runInSeparateProcess
344+
* @preserveGlobalState disabled
345+
*/
346+
public function test_output_buffer_handles_mixed_tags() {
347+
$_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';
348+
349+
ob_start();
350+
351+
wp_start_cross_origin_isolation_output_buffer();
352+
echo '<img src="https://external.example.com/image.jpg" />';
353+
echo '<script src="https://external.example.com/script.js"></script>';
354+
echo '<audio src="https://external.example.com/audio.mp3"></audio>';
355+
356+
ob_end_flush();
357+
$output = ob_get_clean();
358+
359+
// IMG should NOT have crossorigin.
360+
$this->assertStringContainsString( '<img src="https://external.example.com/image.jpg" />', $output, 'IMG should not be modified.' );
361+
362+
// Script and audio should have crossorigin.
363+
$this->assertSame( 2, substr_count( $output, 'crossorigin="anonymous"' ), 'Script and audio should both get crossorigin, but not img.' );
364+
}
213365
}

tests/phpunit/tests/query/parseQuery.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,35 @@ public function test_parse_query_attachment_id_nonscalar() {
233233

234234
$this->assertEmpty( $q->query_vars['attachment_id'] );
235235
}
236+
237+
/**
238+
* Tests that a fatal error is not thrown when a hierarchical taxonomy query var
239+
* passed to wp_basename() in ::parse_tax_query() is an array instead of a string.
240+
*
241+
* The message that we should not see:
242+
* `TypeError: urldecode(): Argument #1 ($string) must be of type string, array given`.
243+
*
244+
* @ticket 64870
245+
*/
246+
public function test_parse_query_hierarchical_taxonomy_query_var_array() {
247+
register_taxonomy(
248+
'wptests_tax',
249+
'post',
250+
array(
251+
'query_var' => 'wptests_tax',
252+
'rewrite' => array( 'hierarchical' => true ),
253+
'public' => true,
254+
)
255+
);
256+
257+
$q = new WP_Query(
258+
array(
259+
'wptests_tax' => array( 'term-a', 'term-b' ),
260+
)
261+
);
262+
263+
unregister_taxonomy( 'wptests_tax' );
264+
265+
$this->assertIsArray( $q->posts );
266+
}
236267
}

0 commit comments

Comments
 (0)