diff --git a/src/wp-includes/html-api/class-wp-html-tag-processor.php b/src/wp-includes/html-api/class-wp-html-tag-processor.php index 1b4db41bcee12..b437595bd9e4d 100644 --- a/src/wp-includes/html-api/class-wp-html-tag-processor.php +++ b/src/wp-includes/html-api/class-wp-html-tag-processor.php @@ -2335,6 +2335,7 @@ public function seek( $bookmark_name ) { // Point this tag processor before the sought tag opener and consume it. $this->bytes_already_parsed = $this->bookmarks[ $bookmark_name ]->start; + $this->parser_state = self::STATE_READY; return $this->next_token(); } diff --git a/tests/phpunit/tests/html-api/wpHtmlTagProcessor-bookmark.php b/tests/phpunit/tests/html-api/wpHtmlTagProcessor-bookmark.php index 1041dabd439d8..a0a3b2aa44b4b 100644 --- a/tests/phpunit/tests/html-api/wpHtmlTagProcessor-bookmark.php +++ b/tests/phpunit/tests/html-api/wpHtmlTagProcessor-bookmark.php @@ -435,4 +435,49 @@ public function test_limits_the_number_of_seek_calls() { $this->setExpectedIncorrectUsage( 'WP_HTML_Tag_Processor::seek' ); $this->assertFalse( $processor->seek( 'bookmark' ), "$i-th seek() to the bookmark succeeded, even though it should exceed the allowed limit" ); } + + /** + * Ensures that it's possible to seek to an earlier location in a document even + * after reaching the end of a document, when most functionality shuts down. + * + * @ticket 60428 + * + * @dataProvider data_incomplete_html_with_target_nodes_for_seeking + * + * @param string $html_with_target_element HTML string containing a tag with a `target` attribute. + */ + public function test_can_seek_after_document_ends( $html_with_target_element ) { + $processor = new WP_HTML_Tag_Processor( $html_with_target_element ); + + $sought_tag_name = null; + while ( $processor->next_tag() ) { + if ( null !== $processor->get_attribute( 'target' ) ) { + $processor->set_bookmark( 'target' ); + $sought_tag_name = $processor->get_tag(); + } + } + + $this->assertTrue( + $processor->seek( 'target' ), + 'Should have been able to seek to the target bookmark after reaching the end of the document.' + ); + + $this->assertSame( + $sought_tag_name, + $processor->get_tag(), + "Should have found original target node instead of {$processor->get_tag()}." + ); + } + + /** + * Data provider. + * + * @return array[]. + */ + public static function data_incomplete_html_with_target_nodes_for_seeking() { + return array( + 'Compete document' => array( '