Skip to content

Commit 44abb52

Browse files
committed
Merge updated PR #41: same-name opener virtual-closer test and identity invariant comment
2 parents 206ea84 + 10b3976 commit 44abb52

2 files changed

Lines changed: 52 additions & 0 deletions

File tree

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,13 @@ private function queue_virtual_closer_after_non_lifo_removal(): bool {
951951
return false;
952952
}
953953

954+
/*
955+
* The depth and node-name checks above cannot distinguish the removed
956+
* element from a same-named element at the same depth; identity is
957+
* recovered here. If a queued POP closes a different element with the
958+
* same name, that element owns the current breadcrumb and the virtual
959+
* closer must wait for it.
960+
*/
954961
$next_event = reset( $this->element_queue );
955962
if (
956963
false !== $next_event &&

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,51 @@ public function test_visits_outer_anchor_virtual_closer_after_mathml_text_integr
649649
);
650650
}
651651

652+
/**
653+
* Ensures that the removed outer A element's virtual closer is visited
654+
* before a new same-name opener immediately following the subtree.
655+
*
656+
* This is the one input where the adjusted-current-node guard and the
657+
* same-name next-event lookahead in the virtual-closer queueing must
658+
* cooperate: the new A opener shares the removed element's tag name, but
659+
* the virtual closer must still fire first so the new element opens as a
660+
* sibling, not a child.
661+
*
662+
* @ticket 61576
663+
*
664+
* @covers WP_HTML_Processor::get_breadcrumbs
665+
* @covers WP_HTML_Processor::is_tag_closer
666+
*/
667+
public function test_visits_outer_anchor_virtual_closer_before_same_name_opener() {
668+
$processor = WP_HTML_Processor::create_fragment( '<a><math><mi>x<a>y</a></mi></math><a>z' );
669+
670+
$visits = array();
671+
while ( $processor->next_tag(
672+
array(
673+
'tag_name' => 'A',
674+
'tag_closers' => 'visit',
675+
)
676+
) ) {
677+
$visits[] = array(
678+
$processor->is_tag_closer() ? 'closer' : 'opener',
679+
$processor->get_breadcrumbs(),
680+
);
681+
}
682+
683+
$this->assertSame(
684+
array(
685+
array( 'opener', array( 'HTML', 'BODY', 'A' ) ),
686+
array( 'opener', array( 'HTML', 'BODY', 'A', 'MATH', 'MI', 'A' ) ),
687+
array( 'closer', array( 'HTML', 'BODY', 'A', 'MATH', 'MI' ) ),
688+
array( 'closer', array( 'HTML', 'BODY' ) ),
689+
array( 'opener', array( 'HTML', 'BODY', 'A' ) ),
690+
array( 'closer', array( 'HTML', 'BODY' ) ),
691+
),
692+
$visits,
693+
'Expected the removed outer A virtual closer to be visited before the new same-name A opener.'
694+
);
695+
}
696+
652697
/**
653698
* Ensures that an outer A element removed from the stack of open elements
654699
* remains visitable as a virtual closer when the fragment ends inside its

0 commit comments

Comments
 (0)