diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php
index 10f3ee3e2dd0f..35248c16fbc1c 100644
--- a/src/wp-includes/html-api/class-wp-html-processor.php
+++ b/src/wp-includes/html-api/class-wp-html-processor.php
@@ -2921,8 +2921,7 @@ private function step_in_body(): bool {
case '-STRONG':
case '-TT':
case '-U':
- $this->run_adoption_agency_algorithm();
- return true;
+ return $this->run_adoption_agency_algorithm();
/*
* > A start tag whose tag name is one of: "applet", "marquee", "object"
@@ -6246,8 +6245,10 @@ private function reset_insertion_mode_appropriately(): void {
* @throws WP_HTML_Unsupported_Exception When encountering unsupported HTML input.
*
* @see https://html.spec.whatwg.org/#adoption-agency-algorithm
+ *
+ * @return bool Whether the current token was handled without exhausting input.
*/
- private function run_adoption_agency_algorithm(): void {
+ private function run_adoption_agency_algorithm(): bool {
$budget = 1000;
$subject = $this->get_tag();
$current_node = $this->state->stack_of_open_elements->current_node();
@@ -6259,13 +6260,13 @@ private function run_adoption_agency_algorithm(): void {
! $this->state->active_formatting_elements->contains_node( $current_node )
) {
$this->state->stack_of_open_elements->pop();
- return;
+ return true;
}
$outer_loop_counter = 0;
while ( $budget-- > 0 ) {
if ( $outer_loop_counter++ >= 8 ) {
- return;
+ return true;
}
/*
@@ -6286,20 +6287,20 @@ private function run_adoption_agency_algorithm(): void {
}
}
- // > If there is no such element, then return and instead act as described in the "any other end tag" entry above.
+ // > If there is no such element, then act as described in the "any other end tag" entry above and return.
if ( null === $formatting_element ) {
- $this->bail( 'Cannot run adoption agency when "any other end tag" is required.' );
+ return $this->step_in_body_any_other_end_tag();
}
// > If formatting element is not in the stack of open elements, then this is a parse error; remove the element from the list, and return.
if ( ! $this->state->stack_of_open_elements->contains_node( $formatting_element ) ) {
$this->state->active_formatting_elements->remove_node( $formatting_element );
- return;
+ return true;
}
// > If formatting element is in the stack of open elements, but the element is not in scope, then this is a parse error; return.
if ( ! $this->state->stack_of_open_elements->has_element_in_scope( $formatting_element->node_name ) ) {
- return;
+ return true;
}
/*
@@ -6335,7 +6336,7 @@ private function run_adoption_agency_algorithm(): void {
if ( $formatting_element->bookmark_name === $item->bookmark_name ) {
$this->state->active_formatting_elements->remove_node( $formatting_element );
- return;
+ return true;
}
}
}
diff --git a/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php b/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php
index d9d7d7c13394a..c0aaa905e93d9 100644
--- a/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php
+++ b/tests/phpunit/tests/html-api/wpHtmlProcessor-serialize.php
@@ -281,6 +281,47 @@ public function test_unexpected_closing_tags_are_removed() {
);
}
+ /**
+ * Ensures that unexpected closing formatting tags are ignored.
+ *
+ * @ticket 65383
+ *
+ * @dataProvider data_formatting_tag_names
+ *
+ * @param string $formatting_tag_name Formatting tag name with no active formatting element.
+ */
+ public function test_unexpected_closing_formatting_tags_are_ignored( string $formatting_tag_name ) {
+ $this->assertSame(
+ 'onetwo',
+ WP_HTML_Processor::normalize( "one{$formatting_tag_name}>two" ),
+ "Should have ignored unexpected {$formatting_tag_name} closer."
+ );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array
<{$tag_name}>
{$tag_name}>" ); + + $this->assertTrue( $processor->next_tag( $tag_name ), "Failed to find the {$tag_name} opener before it is popped by the P closer." ); + $this->assertTrue( $processor->next_tag( 'SPAN' ), "Failed to advance past the inactive {$tag_name} closer to the following SPAN opener." ); + $this->assertSame( array( 'HTML', 'BODY', 'SPAN' ), $processor->get_breadcrumbs(), "Expected SPAN to be a BODY child after the inactive {$tag_name} closer." ); + } + + /** + * Data provider. + * + * @return array