Changeset 56380
- Timestamp:
- 08/10/2023 08:35:55 AM (14 months ago)
- Location:
- trunk
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/html-api/class-wp-html-open-elements.php
r56363 r56380 114 114 foreach ( $this->walk_up() as $node ) { 115 115 if ( $node->node_name === $tag_name ) { 116 return true; 117 } 118 119 switch ( $node->node_name ) { 120 case 'HTML': 121 return false; 122 } 123 124 if ( in_array( $node->node_name, $termination_list, true ) ) { 116 125 return true; 117 126 } … … 176 185 */ 177 186 public function has_element_in_button_scope( $tag_name ) { 178 return $this->has_element_in_specific_scope( 179 $tag_name, 180 array( 181 182 /* 183 * Because it's not currently possible to encounter 184 * one of the termination elements, they don't need 185 * to be listed here. If they were, they would be 186 * unreachable and only waste CPU cycles while 187 * scanning through HTML. 188 */ 189 ) 190 ); 187 return $this->has_element_in_specific_scope( $tag_name, array( 'BUTTON' ) ); 191 188 } 192 189 … … 395 392 */ 396 393 switch ( $item->node_name ) { 394 case 'BUTTON': 395 $this->has_p_in_button_scope = false; 396 break; 397 397 398 case 'P': 398 399 $this->has_p_in_button_scope = true; … … 420 421 */ 421 422 switch ( $item->node_name ) { 423 case 'BUTTON': 424 $this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' ); 425 break; 426 422 427 case 'P': 423 428 $this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' ); -
trunk/src/wp-includes/html-api/class-wp-html-processor-state.php
r56274 r56380 109 109 110 110 /** 111 * The frameset-ok flag indicates if a `FRAMESET` element is allowed in the current state. 112 * 113 * > The frameset-ok flag is set to "ok" when the parser is created. It is set to "not ok" after certain tokens are seen. 114 * 115 * @since 6.4.0 116 * 117 * @see https://html.spec.whatwg.org/#frameset-ok-flag 118 * 119 * @var bool 120 */ 121 public $frameset_ok = true; 122 123 /** 111 124 * Constructor - creates a new and empty state value. 112 125 * -
trunk/src/wp-includes/html-api/class-wp-html-processor.php
r56376 r56380 350 350 public function next_tag( $query = null ) { 351 351 if ( null === $query ) { 352 return $this->step(); 352 while ( $this->step() ) { 353 if ( ! $this->is_tag_closer() ) { 354 return true; 355 } 356 } 357 358 return false; 353 359 } 354 360 … … 367 373 368 374 if ( ! ( array_key_exists( 'breadcrumbs', $query ) && is_array( $query['breadcrumbs'] ) ) ) { 369 return $this->step(); 375 while ( $this->step() ) { 376 if ( ! $this->is_tag_closer() ) { 377 return true; 378 } 379 } 380 381 return false; 370 382 } 371 383 … … 384 396 $crumb = end( $breadcrumbs ); 385 397 $target = strtoupper( $crumb ); 386 while ( $ this->step() ) {398 while ( $match_offset > 0 && $this->step() ) { 387 399 if ( $target !== $this->get_tag() ) { 388 400 continue; … … 396 408 397 409 $crumb = prev( $breadcrumbs ); 398 if ( false === $crumb && 0 === --$match_offset ) {410 if ( false === $crumb && 0 === --$match_offset && ! $this->is_tag_closer() ) { 399 411 return true; 400 412 } … … 511 523 512 524 switch ( $op ) { 525 /* 526 * > A start tag whose tag name is "button" 527 */ 528 case '+BUTTON': 529 if ( $this->state->stack_of_open_elements->has_element_in_scope( 'BUTTON' ) ) { 530 // @TODO: Indicate a parse error once it's possible. This error does not impact the logic here. 531 $this->generate_implied_end_tags(); 532 $this->state->stack_of_open_elements->pop_until( 'BUTTON' ); 533 } 534 535 $this->reconstruct_active_formatting_elements(); 536 $this->insert_html_element( $this->current_token ); 537 $this->state->frameset_ok = false; 538 539 return true; 540 513 541 /* 514 542 * > A start tag whose tag name is one of: "address", "article", "aside", … … 536 564 */ 537 565 case '-BLOCKQUOTE': 566 case '-BUTTON': 538 567 case '-DIV': 539 568 case '-FIGCAPTION': 540 569 case '-FIGURE': 541 570 if ( ! $this->state->stack_of_open_elements->has_element_in_scope( $tag_name ) ) { 571 // @TODO: Report parse error. 542 572 // Ignore the token. 543 573 return $this->step(); … … 545 575 546 576 $this->generate_implied_end_tags(); 577 if ( $this->state->stack_of_open_elements->current_node()->node_name !== $tag_name ) { 578 // @TODO: Record parse error: this error doesn't impact parsing. 579 } 547 580 $this->state->stack_of_open_elements->pop_until( $tag_name ); 548 581 return true; -
trunk/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php
r56331 r56380 40 40 'B', 41 41 'BIG', 42 'BUTTON', 42 43 'CODE', 43 44 'DIV', … … 112 113 'BODY', 113 114 'BR', 114 'BUTTON',115 115 'CANVAS', 116 116 'CAPTION', -
trunk/tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php
r56331 r56380 16 16 * RULES FOR "IN BODY" MODE 17 17 *******************************************************************/ 18 19 /** 20 * Verifies that when encountering an end tag for which there is no corresponding 21 * element in scope, that it skips the tag entirely. 22 * 23 * @ticket 58961 24 * 25 * @since 6.4.0 26 * 27 * @throws Exception 28 */ 29 public function test_in_body_skips_unexpected_button_closer() { 30 $p = WP_HTML_Processor::createFragment( '<div>Test</button></div>' ); 31 32 $p->step(); 33 $this->assertEquals( 'DIV', $p->get_tag(), 'Did not stop at initial DIV tag.' ); 34 $this->assertFalse( $p->is_tag_closer(), 'Did not find that initial DIV tag is an opener.' ); 35 36 /* 37 * When encountering the BUTTON closing tag, there is no BUTTON in the stack of open elements. 38 * It should be ignored as there's no BUTTON to close. 39 */ 40 $this->assertTrue( $p->step(), 'Found no further tags when it should have found the closing DIV' ); 41 $this->assertEquals( 'DIV', $p->get_tag(), "Did not skip unexpected BUTTON; stopped at {$p->get_tag()}." ); 42 $this->assertTrue( $p->is_tag_closer(), 'Did not find that the terminal DIV tag is a closer.' ); 43 } 44 45 /** 46 * Verifies insertion of a BUTTON element when no existing BUTTON is already in scope. 47 * 48 * @ticket 58961 49 * 50 * @since 6.4.0 51 * 52 * @throws WP_HTML_Unsupported_Exception 53 */ 54 public function test_in_body_button_with_no_button_in_scope() { 55 $p = WP_HTML_Processor::createFragment( '<div><p>Click the button <button one>here</button>!</p></div><button two>not here</button>' ); 56 57 $this->assertTrue( $p->next_tag( 'BUTTON' ), 'Could not find expected first button.' ); 58 $this->assertTrue( $p->get_attribute( 'one' ), 'Failed to match expected attribute on first button.' ); 59 $this->assertSame( array( 'HTML', 'BODY', 'DIV', 'P', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for first button.' ); 60 61 /* 62 * There's nothing special about this HTML construction, but it's important to verify that 63 * the HTML Processor can find a BUTTON under normal and normative scenarios, not just the 64 * malformed and unexpected ones. 65 */ 66 $this->assertTrue( $p->next_tag( 'BUTTON' ), 'Could not find expected second button.' ); 67 $this->assertTrue( $p->get_attribute( 'two' ), 'Failed to match expected attribute on second button.' ); 68 $this->assertSame( array( 'HTML', 'BODY', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for second button.' ); 69 } 70 71 /** 72 * Verifies what when inserting a BUTTON element, when a BUTTON is already in scope, 73 * that the open button is closed with all other elements inside of it. 74 * 75 * @ticket 58961 76 * 77 * @since 6.4.0 78 * 79 * @throws WP_HTML_Unsupported_Exception 80 */ 81 public function test_in_body_button_with_button_in_scope_as_parent() { 82 $p = WP_HTML_Processor::createFragment( '<div><p>Click the button <button one>almost<button two>here</button>!</p></div><button three>not here</button>' ); 83 84 $this->assertTrue( $p->next_tag( 'BUTTON' ), 'Could not find expected first button.' ); 85 $this->assertTrue( $p->get_attribute( 'one' ), 'Failed to match expected attribute on first button.' ); 86 $this->assertSame( array( 'HTML', 'BODY', 'DIV', 'P', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for first button.' ); 87 88 /* 89 * A naive parser might skip the second BUTTON because it's looking for the close of the first one, 90 * or it may place it as a child of the first one, but it implicitly closes the open BUTTON. 91 */ 92 $this->assertTrue( $p->next_tag( 'BUTTON' ), 'Could not find expected second button.' ); 93 $this->assertTrue( $p->get_attribute( 'two' ), 'Failed to match expected attribute on second button.' ); 94 $this->assertSame( array( 'HTML', 'BODY', 'DIV', 'P', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for second button.' ); 95 96 /* 97 * This is another form of the test for the second button, but from a different side. The test is 98 * looking for proper handling of the open and close sequence for the BUTTON tags. 99 */ 100 $this->assertTrue( $p->next_tag( 'BUTTON' ), 'Could not find expected third button.' ); 101 $this->assertTrue( $p->get_attribute( 'three' ), 'Failed to match expected attribute on third button.' ); 102 $this->assertSame( array( 'HTML', 'BODY', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for third button.' ); 103 } 104 105 /** 106 * Verifies what when inserting a BUTTON element, when a BUTTON is already in scope, 107 * that the open button is closed with all other elements inside of it, even if the 108 * BUTTON in scope is not a direct parent of the new BUTTON element. 109 * 110 * @ticket 58961 111 * 112 * @since 6.4.0 113 * 114 * @throws WP_HTML_Unsupported_Exception 115 */ 116 public function test_in_body_button_with_button_in_scope_as_ancestor() { 117 $p = WP_HTML_Processor::createFragment( '<div><button one><p>Click the button <span><button two>here</button>!</span></p></div><button three>not here</button>' ); 118 119 // This button finds itself normally nesting inside the DIV. 120 $this->assertTrue( $p->next_tag( 'BUTTON' ), 'Could not find expected first button.' ); 121 $this->assertTrue( $p->get_attribute( 'one' ), 'Failed to match expected attribute on first button.' ); 122 $this->assertSame( array( 'HTML', 'BODY', 'DIV', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for first button.' ); 123 124 /* 125 * Because the second button appears while a BUTTON is in scope, it generates implied end tags and closes 126 * the BUTTON, P, and SPAN elements. It looks like the BUTTON is inside the SPAN, but it's another case 127 * of an unexpected closing SPAN tag because the SPAN was closed by the second BUTTON. This element finds 128 * itself a child of the most-recent open element above the most-recent BUTTON, or the DIV. 129 */ 130 $this->assertTrue( $p->next_tag( 'BUTTON' ), 'Could not find expected second button.' ); 131 $this->assertTrue( $p->get_attribute( 'two' ), 'Failed to match expected attribute on second button.' ); 132 $this->assertSame( array( 'HTML', 'BODY', 'DIV', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for second button.' ); 133 134 // The third button is back to normal, because everything has been implicitly or explicitly closed by now. 135 $this->assertTrue( $p->next_tag( 'BUTTON' ), 'Could not find expected third button.' ); 136 $this->assertTrue( $p->get_attribute( 'three' ), 'Failed to match expected attribute on third button.' ); 137 $this->assertSame( array( 'HTML', 'BODY', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for third button.' ); 138 } 18 139 19 140 /* … … 58 179 $this->assertSame( array( 'HTML', 'BODY', 'DIV', 'SPAN', 'CODE' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting.' ); 59 180 60 $this->assertTrue( $p-> next_tag(), 'Failed to advance past CODE tag to expected SPAN closer.' );181 $this->assertTrue( $p->step(), 'Failed to advance past CODE tag to expected SPAN closer.' ); 61 182 $this->assertTrue( $p->is_tag_closer(), 'Expected to find closing SPAN, but found opener instead.' ); 62 183 $this->assertSame( array( 'HTML', 'BODY', 'DIV' ), $p->get_breadcrumbs(), 'Failed to advance past CODE tag to expected DIV opener.' ); -
trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredOpenElements.php
r56299 r56380 177 177 $this->ensure_support_is_added_everywhere( 'DESC' ); 178 178 $this->ensure_support_is_added_everywhere( 'TITLE' ); 179 180 $this->ensure_support_is_added_everywhere( 'BUTTON' );181 179 } 182 180 … … 219 217 $this->ensure_support_is_added_everywhere( 'DESC' ); 220 218 $this->ensure_support_is_added_everywhere( 'TITLE' ); 221 222 // This element is specific to BUTTON scope.223 $this->ensure_support_is_added_everywhere( 'BUTTON' );224 219 } 225 220 … … 262 257 $this->ensure_support_is_added_everywhere( 'DESC' ); 263 258 $this->ensure_support_is_added_everywhere( 'TITLE' ); 264 265 $this->ensure_support_is_added_everywhere( 'BUTTON' );266 259 } 267 260
Note: See TracChangeset
for help on using the changeset viewer.