Skip to content

Commit 39a121f

Browse files
authored
Merge branch 'WordPress:trunk' into trunk
2 parents 9e5ccd0 + 717e02d commit 39a121f

8 files changed

Lines changed: 184 additions & 20 deletions

File tree

src/wp-admin/includes/template.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1780,7 +1780,8 @@ function do_settings_sections( $page ) {
17801780
}
17811781

17821782
if ( $section['title'] ) {
1783-
echo "<h2>{$section['title']}</h2>\n";
1783+
$unique_id = wp_unique_id( 'wp-settings-section-' . $section['id'] . '-' );
1784+
echo '<h2 id="' . esc_attr( $unique_id ) . '">' . $section['title'] . "</h2>\n";
17841785
}
17851786

17861787
if ( $section['callback'] ) {

src/wp-admin/network/settings.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@
153153
<h1><?php echo esc_html( $title ); ?></h1>
154154
<form method="post" action="settings.php" novalidate="novalidate">
155155
<?php wp_nonce_field( 'siteoptions' ); ?>
156-
<h2><?php _e( 'Operational Settings' ); ?></h2>
156+
<h2 id="wp-settings-section-operational-settings"><?php _e( 'Operational Settings' ); ?></h2>
157157
<table class="form-table" role="presentation">
158158
<tr>
159159
<th scope="row"><label for="site_name"><?php _e( 'Network Title' ); ?></label></th>
@@ -197,7 +197,7 @@
197197
</td>
198198
</tr>
199199
</table>
200-
<h2><?php _e( 'Registration Settings' ); ?></h2>
200+
<h2 id="wp-settings-section-registration-settings"><?php _e( 'Registration Settings' ); ?></h2>
201201
<table class="form-table" role="presentation">
202202
<?php $new_registrations_settings_title = __( 'Allow new registrations' ); ?>
203203
<tr>
@@ -315,7 +315,7 @@
315315
</tr>
316316

317317
</table>
318-
<h2><?php _e( 'New Site Settings' ); ?></h2>
318+
<h2 id="wp-settings-section-new-site-settings"><?php _e( 'New Site Settings' ); ?></h2>
319319
<table class="form-table" role="presentation">
320320

321321
<tr>
@@ -396,7 +396,7 @@
396396
</td>
397397
</tr>
398398
</table>
399-
<h2><?php _e( 'Upload Settings' ); ?></h2>
399+
<h2 id="wp-settings-section-upload-settings"><?php _e( 'Upload Settings' ); ?></h2>
400400
<table class="form-table" role="presentation">
401401
<tr>
402402
<th scope="row"><?php _e( 'Site upload space' ); ?></th>
@@ -454,7 +454,7 @@
454454
$translations = wp_get_available_translations();
455455
if ( ! empty( $languages ) || ! empty( $translations ) ) {
456456
?>
457-
<h2><?php _e( 'Language Settings' ); ?></h2>
457+
<h2 id="wp-settings-section-language-settings"><?php _e( 'Language Settings' ); ?></h2>
458458
<table class="form-table" role="presentation">
459459
<tr>
460460
<th><label for="WPLANG"><?php _e( 'Default Language' ); ?><span class="dashicons dashicons-translation" aria-hidden="true"></span></label></th>
@@ -504,7 +504,7 @@
504504

505505
if ( $menu_items ) :
506506
?>
507-
<h2><?php _e( 'Menu Settings' ); ?></h2>
507+
<h2 id="wp-settings-section-menu-settings"><?php _e( 'Menu Settings' ); ?></h2>
508508
<table id="menu" class="form-table">
509509
<?php $enable_administration_menus_title = __( 'Enable administration menus' ); ?>
510510
<tr>

src/wp-admin/options-discussion.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@
211211
<?php do_settings_fields( 'discussion', 'default' ); ?>
212212
</table>
213213

214-
<h2 class="title"><?php _e( 'Avatars' ); ?></h2>
214+
<h2 id="wp-settings-section-avatars" class="title"><?php _e( 'Avatars' ); ?></h2>
215215

216216
<p><?php _e( 'An avatar is an image that can be associated with a user across multiple websites. In this area, you can choose to display avatars of users who interact with the site.' ); ?></p>
217217

src/wp-admin/options-media.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
<form action="options.php" method="post">
5353
<?php settings_fields( 'media' ); ?>
5454

55-
<h2 class="title"><?php _e( 'Image sizes' ); ?></h2>
55+
<h2 id="wp-settings-section-image-sizes" class="title"><?php _e( 'Image sizes' ); ?></h2>
5656
<p><?php _e( 'The sizes listed below determine the maximum dimensions in pixels to use when adding an image to the Media Library.' ); ?></p>
5757

5858
<table class="form-table" role="presentation">
@@ -104,14 +104,14 @@
104104
*/
105105
if ( isset( $GLOBALS['wp_settings']['media']['embeds'] ) ) :
106106
?>
107-
<h2 class="title"><?php _e( 'Embeds' ); ?></h2>
107+
<h2 id="wp-settings-section-embeds" class="title"><?php _e( 'Embeds' ); ?></h2>
108108
<table class="form-table" role="presentation">
109109
<?php do_settings_fields( 'media', 'embeds' ); ?>
110110
</table>
111111
<?php endif; ?>
112112

113113
<?php if ( ! is_multisite() ) : ?>
114-
<h2 class="title"><?php _e( 'Uploading Files' ); ?></h2>
114+
<h2 id="wp-settings-section-uploading-files" class="title"><?php _e( 'Uploading Files' ); ?></h2>
115115
<table class="form-table" role="presentation">
116116
<?php
117117
/*

src/wp-admin/options-permalink.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@
314314
/* translators: %s: Permalink structure tag. */
315315
$tag_already_used = __( '%s (already used in permalink structure)' );
316316
?>
317-
<h2 class="title"><?php _e( 'Common Settings' ); ?></h2>
317+
<h2 id="wp-settings-section-common-settings" class="title"><?php _e( 'Common Settings' ); ?></h2>
318318
<p>
319319
<?php
320320
printf(
@@ -405,7 +405,7 @@ class="button button-secondary"
405405
</tbody>
406406
</table>
407407

408-
<h2 class="title"><?php _e( 'Optional' ); ?></h2>
408+
<h2 id="wp-settings-section-optional" class="title"><?php _e( 'Optional' ); ?></h2>
409409
<p class="permalink-structure-optional-description">
410410
<?php
411411
printf(

src/wp-admin/options-writing.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
/** This filter is documented in wp-admin/options.php */
142142
if ( apply_filters( 'enable_post_by_email_configuration', true ) ) {
143143
?>
144-
<h2 class="title"><?php _e( 'Post via email' ); ?></h2>
144+
<h2 id="wp-settings-section-post-via-email" class="title"><?php _e( 'Post via email' ); ?></h2>
145145
<p>
146146
<?php
147147
printf(
@@ -212,7 +212,7 @@
212212
*/
213213
if ( apply_filters( 'enable_update_services_configuration', true ) ) {
214214
?>
215-
<h2 class="title"><?php _e( 'Update Services' ); ?></h2>
215+
<h2 id="wp-settings-section-update-services" class="title"><?php _e( 'Update Services' ); ?></h2>
216216

217217
<?php if ( '1' === get_option( 'blog_public' ) ) : ?>
218218

src/wp-includes/html-api/class-wp-html-decoder.php

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class WP_HTML_Decoder {
1717
* of how it might be encoded in HTML. For instance, `http:` could be represented as `http:`
1818
* or as `http&colon;` or as `&#x68;ttp:` or as `h&#116;tp&colon;`, or in many other ways.
1919
*
20+
* This is equivalent to a byte-prefix test against the decoded attribute value, without
21+
* the need to allocate and decode the full string.
22+
*
2023
* Example:
2124
*
2225
* $value = 'http&colon;//wordpress.org/';
@@ -53,24 +56,71 @@ public static function attribute_starts_with( $haystack, $search_text, $case_sen
5356
return false;
5457
}
5558

56-
// If there's no character reference but the character do match, then it could still match.
59+
// If there's no character reference but the characters do match, then it could still match.
5760
if ( null === $next_chunk && $chars_match ) {
5861
++$haystack_at;
5962
++$search_at;
6063
continue;
6164
}
6265

63-
// If there is a character reference, then the decoded value must exactly match what follows in the search string.
64-
if ( 0 !== substr_compare( $search_text, $next_chunk, $search_at, strlen( $next_chunk ), $loose_case ) ) {
66+
/**
67+
* The decoded character reference in `$next_chunk` must be compared with the
68+
* corresponding `$search_text` bytes checking for matching prefixes. The remaining
69+
* search text may be shorter than the decoded chunk, in which case a partial match
70+
* satisfies the prefix. Otherwise, if the decoded chunk is fully matched, the
71+
* comparison must continue after advancing the appropriate byte lengths: the character
72+
* reference token length in the haystack and the decoded chunk length in the
73+
* search text.
74+
*
75+
* For example, consider searches that have reached the character reference
76+
* `&fjlig;` (7 bytes), decoded into the 2-byte chunk `fj`:
77+
*
78+
* $haystack_at
79+
* │
80+
* │ ┌─after matching `fj` continue here
81+
* │ │ (advance by $token_length, 7 bytes)
82+
* ↓ ↓
83+
* Haystack: start&fjlig;ord
84+
* ╰──┬──╯
85+
* fj - the decoded chunk, tested against the search text.
86+
*
87+
* $search_at
88+
* │
89+
* │ ┌─after matching `fj` continue here
90+
* │ │ (advance by $match_length, 2 bytes)
91+
* ↓ ↓
92+
* Search A: startfjord Compare 2 bytes: `fj` matches,
93+
* continue matching at `o`.
94+
*
95+
* $search_at
96+
* ↓
97+
* Search B: startf Compare 1 byte: `f` matches and the
98+
* search text is exhausted — prefix confirmed.
99+
*
100+
* $search_at
101+
* ↓
102+
* Search C: startfr Compare 2 bytes: `fj` differs
103+
* from `fr`, no match is possible.
104+
*
105+
* The `min()` is required in both directions: Search A fails if the
106+
* comparison length comes from the search text, Search B if it comes
107+
* from the chunk.
108+
*
109+
* After a match each cursor must advance by the appropriate length, the haystack
110+
* cursor by the character reference token length, and the search cursor by the
111+
* matched length.
112+
*/
113+
$match_length = min( strlen( $next_chunk ), $search_length - $search_at );
114+
if ( 0 !== substr_compare( $search_text, $next_chunk, $search_at, $match_length, $loose_case ) ) {
65115
return false;
66116
}
67117

68118
// The character reference matched, so continue checking.
69119
$haystack_at += $token_length;
70-
$search_at += strlen( $next_chunk );
120+
$search_at += $match_length;
71121
}
72122

73-
return true;
123+
return $search_at === $search_length;
74124
}
75125

76126
/**

tests/phpunit/tests/html-api/wpHtmlDecoder.php

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,119 @@ public static function data_case_variants_of_attribute_prefixes() {
346346
}
347347
}
348348

349+
/**
350+
* Ensures that `attribute_starts_with` checks the full search string.
351+
*
352+
* @ticket 65372
353+
*
354+
* @dataProvider data_attribute_starts_with_search_string_boundaries
355+
*
356+
* @param string $attribute_value Raw attribute value from HTML string.
357+
* @param string $search_string Prefix contained or not contained in encoded attribute value.
358+
* @param string $case_sensitivity Whether to search with ASCII case sensitivity;
359+
* 'ascii-case-insensitive' or 'case-sensitive'.
360+
* @param bool $is_match Whether the search string is a prefix for the attribute value.
361+
*/
362+
public function test_attribute_starts_with_checks_search_string_boundaries(
363+
string $attribute_value,
364+
string $search_string,
365+
string $case_sensitivity,
366+
bool $is_match
367+
): void {
368+
if ( $is_match ) {
369+
$this->assertTrue(
370+
WP_HTML_Decoder::attribute_starts_with( $attribute_value, $search_string, $case_sensitivity ),
371+
'Should have matched attribute prefix.'
372+
);
373+
} else {
374+
$this->assertFalse(
375+
WP_HTML_Decoder::attribute_starts_with( $attribute_value, $search_string, $case_sensitivity ),
376+
'Should not have matched attribute with prefix.'
377+
);
378+
}
379+
}
380+
381+
/**
382+
* Data provider.
383+
*
384+
* @return Generator<string, array{string, string, string, bool}> Test cases.
385+
*/
386+
public static function data_attribute_starts_with_search_string_boundaries(): Generator {
387+
yield 'Empty attribute does not match non-empty prefix' => array( '', 'http', 'case-sensitive', false );
388+
yield 'Short attribute does not match longer prefix' => array(
389+
'java',
390+
'javascript',
391+
'case-sensitive',
392+
false,
393+
);
394+
yield 'Attribute ending in a character reference does not match a longer prefix' => array(
395+
'&amp;',
396+
'&&',
397+
'case-sensitive',
398+
false,
399+
);
400+
yield 'Longer attribute matches shorter prefix' => array(
401+
'javascript',
402+
'java',
403+
'case-sensitive',
404+
true,
405+
);
406+
yield "&fjlig; (decodes to 2-codepoint 'fj') starts with f" => array(
407+
'&fjlig; is literally "f" followed by "j"',
408+
'f',
409+
'case-sensitive',
410+
true,
411+
);
412+
yield "&nvlt; (decodes to 2-codepoint '<⃒') starts with '<'" => array(
413+
'&nvlt;script>',
414+
'<',
415+
'case-sensitive',
416+
true,
417+
);
418+
yield "Combining character references (¬̸) full match on '¬̸' prefix" => array(
419+
'&not;&#x338; A negated not?',
420+
'¬̸',
421+
'case-sensitive',
422+
true,
423+
);
424+
yield "Combining character references (¬̸) partial match on '¬' prefix" => array(
425+
'&not;&#x338; A negated not?',
426+
'¬',
427+
'case-sensitive',
428+
true,
429+
);
430+
yield 'Search A: prefix continues past a decoded character reference' => array(
431+
'start&fjlig;ord',
432+
'startfjord',
433+
'case-sensitive',
434+
true,
435+
);
436+
yield 'Search B: prefix ends part-way through a decoded character reference' => array(
437+
'start&fjlig;ord',
438+
'startf',
439+
'case-sensitive',
440+
true,
441+
);
442+
yield 'Search C: prefix mismatches within a decoded character reference' => array(
443+
'start&fjlig;ord',
444+
'startfr',
445+
'case-sensitive',
446+
false,
447+
);
448+
yield 'ASCII-case-insensitive prefix ends part-way through a decoded character reference' => array(
449+
'start&fjlig;ord',
450+
'STARTF',
451+
'ascii-case-insensitive',
452+
true,
453+
);
454+
yield 'ASCII-case-insensitive prefix mismatches within a decoded character reference' => array(
455+
'start&fjlig;ord',
456+
'STARTFR',
457+
'ascii-case-insensitive',
458+
false,
459+
);
460+
}
461+
349462
/**
350463
* Ensures that `attribute_starts_with` respects the case sensitivity argument.
351464
*

0 commit comments

Comments
 (0)