@@ -1217,7 +1217,7 @@ public function class_list() {
12171217 return ;
12181218 }
12191219
1220- $ name = str_replace ( "\x00" , "\u{FFFD}" , substr ( $ class , $ at , $ length ) );
1220+ $ name = substr ( $ class , $ at , $ length );
12211221 if ( $ is_quirks ) {
12221222 $ name = strtolower ( $ name );
12231223 }
@@ -2261,8 +2261,13 @@ private function parse_next_attribute(): bool {
22612261 * - HTML 5 spec
22622262 *
22632263 * @see https://html.spec.whatwg.org/multipage/syntax.html#attributes-2:ascii-case-insensitive
2264+ *
2265+ * The tokenizer replaces U+0000 NULL bytes in
2266+ * attribute names with U+FFFD.
2267+ *
2268+ * @see https://html.spec.whatwg.org/#attribute-name-state
22642269 */
2265- $ comparable_name = strtolower ( $ attribute_name );
2270+ $ comparable_name = strtolower ( str_replace ( "\x00" , "\u{FFFD}" , $ attribute_name ) );
22662271
22672272 // If an attribute is listed many times, only use the first declaration and ignore the rest.
22682273 if ( ! isset ( $ this ->attributes [ $ comparable_name ] ) ) {
@@ -2389,13 +2394,7 @@ private function class_name_updates_to_attributes_updates(): void {
23892394 }
23902395
23912396 if ( false === $ existing_class && isset ( $ this ->attributes ['class ' ] ) ) {
2392- $ existing_class = WP_HTML_Decoder::decode_attribute (
2393- substr (
2394- $ this ->html ,
2395- $ this ->attributes ['class ' ]->value_starts_at ,
2396- $ this ->attributes ['class ' ]->value_length
2397- )
2398- );
2397+ $ existing_class = $ this ->get_decoded_attribute_value ( $ this ->attributes ['class ' ] );
23992398 }
24002399
24012400 if ( false === $ existing_class ) {
@@ -2823,7 +2822,7 @@ public function get_attribute( $name ) {
28232822 * attribute values. If any exist, those enqueued class changes must first be flushed out
28242823 * into an attribute value update.
28252824 */
2826- if ( 'class ' === $ name ) {
2825+ if ( 'class ' === $ comparable ) {
28272826 $ this ->class_name_updates_to_attributes_updates ();
28282827 }
28292828
@@ -2854,8 +2853,28 @@ public function get_attribute( $name ) {
28542853 return true ;
28552854 }
28562855
2857- $ raw_value = substr ( $ this ->html , $ attribute ->value_starts_at , $ attribute ->value_length );
2856+ return $ this ->get_decoded_attribute_value ( $ attribute );
2857+ }
28582858
2859+ /**
2860+ * Decode an attribute value from source.
2861+ *
2862+ * This method applies the following transformations that the processor defers:
2863+ * - Normalize newlines (input stream preprocessing)
2864+ * - Replace NULL bytes (tokenization)
2865+ * - Decode character references (tokenization)
2866+ *
2867+ * @since 7.1.0
2868+ * @ignore
2869+ *
2870+ * @param WP_HTML_Attribute_Token $attribute Attribute token from the input document.
2871+ * @return string Decoded attribute value.
2872+ */
2873+ private function get_decoded_attribute_value ( WP_HTML_Attribute_Token $ attribute ): string {
2874+ $ raw_value = substr ( $ this ->html , $ attribute ->value_starts_at , $ attribute ->value_length );
2875+ $ raw_value = str_replace ( "\r\n" , "\n" , $ raw_value );
2876+ $ raw_value = str_replace ( "\r" , "\n" , $ raw_value );
2877+ $ raw_value = str_replace ( "\x00" , "\u{FFFD}" , $ raw_value );
28592878 return WP_HTML_Decoder::decode_attribute ( $ raw_value );
28602879 }
28612880
@@ -2936,7 +2955,7 @@ public function get_tag(): ?string {
29362955 return null ;
29372956 }
29382957
2939- $ tag_name = substr ( $ this ->html , $ this ->tag_name_starts_at , $ this ->tag_name_length );
2958+ $ tag_name = str_replace ( "\x00" , "\u{FFFD}" , substr ( $ this ->html , $ this ->tag_name_starts_at , $ this ->tag_name_length ) );
29402959
29412960 if ( self ::STATE_MATCHED_TAG === $ this ->parser_state ) {
29422961 return strtoupper ( $ tag_name );
@@ -4798,14 +4817,14 @@ private function matches(): bool {
47984817 }
47994818
48004819 // Does the tag name match the requested tag name in a case-insensitive manner?
4801- if (
4802- isset ( $ this ->sought_tag_name ) &&
4803- (
4804- strlen ( $ this ->sought_tag_name ) !== $ this -> tag_name_length ||
4805- 0 !== substr_compare ( $ this -> html , $ this ->sought_tag_name , $ this -> tag_name_starts_at , $ this -> tag_name_length , true )
4806- )
4807- ) {
4808- return false ;
4820+ if ( isset ( $ this -> sought_tag_name ) ) {
4821+ $ tag_name = $ this ->get_tag ();
4822+ if (
4823+ strlen ( $ this ->sought_tag_name ) !== strlen ( $ tag_name ) ||
4824+ 0 !== substr_compare ( $ tag_name , $ this ->sought_tag_name , 0 , null , true )
4825+ ) {
4826+ return false ;
4827+ }
48094828 }
48104829
48114830 if ( null !== $ this ->sought_class_name && ! $ this ->has_class ( $ this ->sought_class_name ) ) {
0 commit comments