Changeset 56685
- Timestamp:
- 09/25/2023 07:13:27 PM (14 months ago)
- Location:
- branches/6.3
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/6.3/src/wp-includes/html-api/class-wp-html-tag-processor.php
r56564 r56685 406 406 */ 407 407 private $attributes = array(); 408 409 /** 410 * Tracks spans of duplicate attributes on a given tag, used for removing 411 * all copies of an attribute when calling `remove_attribute()`. 412 * 413 * @since 6.3.2 414 * 415 * @var (WP_HTML_Span[])[]|null 416 */ 417 private $duplicate_attributes = null; 408 418 409 419 /** … … 1287 1297 ! $has_value 1288 1298 ); 1299 1300 return true; 1301 } 1302 1303 /* 1304 * Track the duplicate attributes so if we remove it, all disappear together. 1305 * 1306 * While `$this->duplicated_attributes` could always be stored as an `array()`, 1307 * which would simplify the logic here, storing a `null` and only allocating 1308 * an array when encountering duplicates avoids needless allocations in the 1309 * normative case of parsing tags with no duplicate attributes. 1310 */ 1311 $duplicate_span = new WP_HTML_Span( $attribute_start, $attribute_end ); 1312 if ( null === $this->duplicate_attributes ) { 1313 $this->duplicate_attributes = array( $comparable_name => array( $duplicate_span ) ); 1314 } elseif ( ! array_key_exists( $comparable_name, $this->duplicate_attributes ) ) { 1315 $this->duplicate_attributes[ $comparable_name ] = array( $duplicate_span ); 1316 } else { 1317 $this->duplicate_attributes[ $comparable_name ][] = $duplicate_span; 1289 1318 } 1290 1319 … … 1308 1337 private function after_tag() { 1309 1338 $this->get_updated_html(); 1310 $this->tag_name_starts_at = null; 1311 $this->tag_name_length = null; 1312 $this->tag_ends_at = null; 1313 $this->is_closing_tag = null; 1314 $this->attributes = array(); 1339 $this->tag_name_starts_at = null; 1340 $this->tag_name_length = null; 1341 $this->tag_ends_at = null; 1342 $this->is_closing_tag = null; 1343 $this->attributes = array(); 1344 $this->duplicate_attributes = null; 1315 1345 } 1316 1346 … … 2081 2111 ); 2082 2112 2113 // Removes any duplicated attributes if they were also present. 2114 if ( null !== $this->duplicate_attributes && array_key_exists( $name, $this->duplicate_attributes ) ) { 2115 foreach ( $this->duplicate_attributes[ $name ] as $attribute_token ) { 2116 $this->lexical_updates[] = new WP_HTML_Text_Replacement( 2117 $attribute_token->start, 2118 $attribute_token->end, 2119 '' 2120 ); 2121 } 2122 } 2123 2083 2124 return true; 2084 2125 } -
branches/6.3/tests/phpunit/tests/html-api/wpHtmlTagProcessor.php
r56564 r56685 1050 1050 * all its instances and output just `<div />`. 1051 1051 * 1052 * Today, however, WP_HTML_Tag_Processor only removes the first such attribute. It seems like a corner case 1053 * and introducing additional complexity to correctly handle this scenario doesn't seem to be worth it. 1054 * Let's revisit if and when this becomes a problem. 1055 * 1056 * This test is in place to confirm this behavior, which while incorrect, is well-defined. 1057 * A later fix introduced to the Tag Processor should update this test to reflect the 1058 * wanted and correct behavior. 1059 * 1060 * @ticket 56299 1052 * @since 6.3.2 Removes all duplicated attributes as expected. 1053 * 1054 * @ticket 58119 1061 1055 * 1062 1056 * @covers WP_HTML_Tag_Processor::remove_attribute … … 1067 1061 $p->remove_attribute( 'id' ); 1068 1062 1069 $this->assertS ame(1070 ' <div id="ignored-id"><span id="second">Text</span></div>',1063 $this->assertStringNotContainsString( 1064 'update-me', 1071 1065 $p->get_updated_html(), 1072 1066 'First attribute (when duplicates exist) was not removed' … … 1088 1082 $p->get_updated_html(), 1089 1083 'Attribute was not removed' 1084 ); 1085 } 1086 1087 /** 1088 * @ticket 58119 1089 * 1090 * @since 6.3.2 Removes all duplicated attributes as expected. 1091 * 1092 * @covers WP_HTML_Tag_Processor::remove_attribute 1093 * 1094 * @dataProvider data_html_with_duplicated_attributes 1095 */ 1096 public function test_remove_attribute_with_duplicated_attributes_removes_all_of_them( $html_with_duplicate_attributes, $attribute_to_remove ) { 1097 $p = new WP_HTML_Tag_Processor( $html_with_duplicate_attributes ); 1098 $p->next_tag(); 1099 1100 $p->remove_attribute( $attribute_to_remove ); 1101 $this->assertNull( $p->get_attribute( $attribute_to_remove ), 'Failed to remove all copies of an attribute when duplicated in modified source.' ); 1102 1103 // Recreate a tag processor with the updated HTML after removing the attribute. 1104 $p = new WP_HTML_Tag_Processor( $p->get_updated_html() ); 1105 $p->next_tag(); 1106 $this->assertNull( $p->get_attribute( $attribute_to_remove ), 'Failed to remove all copies of duplicated attributes when getting updated HTML.' ); 1107 } 1108 1109 /** 1110 * @ticket 58119 1111 * 1112 * @since 6.3.2 Removes all duplicated attributes as expected. 1113 * 1114 * @covers WP_HTML_Tag_Processor::remove_attribute 1115 */ 1116 public function test_previous_duplicated_attributes_are_not_removed_on_successive_tag_removal() { 1117 $p = new WP_HTML_Tag_Processor( '<span id=one id=two id=three><span id=four>' ); 1118 $p->next_tag(); 1119 $p->next_tag(); 1120 $p->remove_attribute( 'id' ); 1121 1122 $this->assertSame( '<span id=one id=two id=three><span >', $p->get_updated_html() ); 1123 } 1124 1125 /** 1126 * Data provider. 1127 * 1128 * @ticket 58119 1129 * 1130 * @return array[]. 1131 */ 1132 public function data_html_with_duplicated_attributes() { 1133 return array( 1134 'Double attributes' => array( '<div id=one id=two>', 'id' ), 1135 'Triple attributes' => array( '<div id=one id=two id=three>', 'id' ), 1136 'Duplicates around another' => array( '<img src="test.png" alt="kites flying in the wind" src="kites.jpg">', 'src' ), 1137 'Case-variants of attribute' => array( '<button disabled inert DISABLED dISaBled INERT DisABleD>', 'disabled' ), 1138 'Case-variants of attribute name' => array( '<button disabled inert DISABLED dISaBled INERT DisABleD>', 'DISABLED' ), 1090 1139 ); 1091 1140 }
Note: See TracChangeset
for help on using the changeset viewer.