-
Notifications
You must be signed in to change notification settings - Fork 3.5k
HTML API: Ensure that full processor can seek to earlier bookmarks #7649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
f9777aa
5d75bb7
541b4f6
dc3f1e6
89aa01b
737bf92
cb63bbc
744cf01
6106a56
851df38
9af204c
660dc85
9a0c017
d181938
d5bf14c
388bf19
f7af6e3
d5a7d5c
b95e402
90eb6e2
75ab9c2
05ca2a4
1cde425
c48be70
710c5f7
0efe691
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5324,8 +5324,7 @@ public function seek( $bookmark_name ): bool { | |
| */ | ||
| if ( 'backward' === $direction ) { | ||
| /* | ||
| * Instead of clearing the parser state and starting fresh, calling the stack methods | ||
| * maintains the proper flags in the parser. | ||
| * When moving backward, stateful stacks should be cleared. | ||
| */ | ||
| foreach ( $this->state->stack_of_open_elements->walk_up() as $item ) { | ||
| if ( 'context-node' === $item->bookmark_name ) { | ||
|
|
@@ -5343,32 +5342,65 @@ public function seek( $bookmark_name ): bool { | |
| $this->state->active_formatting_elements->remove_node( $item ); | ||
| } | ||
|
|
||
| parent::seek( 'context-node' ); | ||
| $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_BODY; | ||
| $this->state->frameset_ok = true; | ||
| $this->element_queue = array(); | ||
| $this->current_element = null; | ||
| /* | ||
| * **After** clearing stacks, more processor state can be reset. | ||
| * This must be done after clearing the stack because those stacks generate events that | ||
| * would appear on a subsequent call to `next_token()`. | ||
| */ | ||
| $this->state->frameset_ok = true; | ||
| $this->state->stack_of_template_insertion_modes = array(); | ||
| $this->state->head_element = null; | ||
| $this->state->form_element = null; | ||
| $this->state->current_token = null; | ||
|
Comment on lines
+5348
to
+5352
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to add a
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered that but ultimately decided against it. This isn't really a full reset, and the reset behaves a bit differently between the fragment/full processors. To simplify this, I'd probably create a new state instance and copy over the important properties. That may be worth exploring but that felt like a larger refactor. |
||
| $this->current_element = null; | ||
| $this->element_queue = array(); | ||
|
|
||
| if ( isset( $this->context_node ) ) { | ||
| $this->breadcrumbs = array_slice( $this->breadcrumbs, 0, 2 ); | ||
| /* | ||
| * The absence of a context node indicates a full parse. | ||
| * The presence of a context node indicates a fragment parser. | ||
| */ | ||
| if ( null === $this->context_node ) { | ||
| $this->change_parsing_namespace( 'html' ); | ||
| $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_INITIAL; | ||
| $this->breadcrumbs = array(); | ||
|
|
||
| $this->bytes_already_parsed = 0; | ||
| $this->parser_state = self::STATE_READY; | ||
| $this->next_token(); | ||
| } else { | ||
| $this->breadcrumbs = array(); | ||
| } | ||
| } | ||
| $this->change_parsing_namespace( | ||
| $this->context_node->integration_node_type | ||
| ? 'html' | ||
| : $this->context_node->namespace | ||
| ); | ||
|
|
||
| if ( 'TEMPLATE' === $this->context_node->node_name ) { | ||
| $this->state->stack_of_template_insertion_modes[] = WP_HTML_Processor_State::INSERTION_MODE_IN_TEMPLATE; | ||
| } | ||
|
|
||
| // When moving forwards, reparse the document until reaching the same location as the original bookmark. | ||
| if ( $bookmark_starts_at === $this->bookmarks[ $this->state->current_token->bookmark_name ]->start ) { | ||
| return true; | ||
| $this->reset_insertion_mode_appropriately(); | ||
|
Comment on lines
+5371
to
+5394
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic is inspired by #7348 where a fragment parser is created on arbitrary context nodes and must be initialized properly. This is very similar, the state must be reset to the appropriate initial state for the context. This follows the fragment parsing algorithm: https://html.spec.whatwg.org/multipage/parsing.html#html-fragment-parsing-algorithm |
||
| $this->breadcrumbs = array_slice( $this->breadcrumbs, 0, 2 ); | ||
| parent::seek( $this->context_node->bookmark_name ); | ||
| } | ||
| } | ||
|
|
||
| while ( $this->next_token() ) { | ||
| /* | ||
| * Here, the processor moves forward through the document until it matches the bookmark. | ||
| * do-while is used here because the processor is expected to already be stopped on | ||
| * a token than may match the bookmarked location. | ||
| */ | ||
| do { | ||
| /* | ||
| * The processor will stop on virtual tokens, but bookmarks may not be set on them. | ||
| * They should not be matched when seeking a bookmark, skip them. | ||
| */ | ||
| if ( $this->is_virtual() ) { | ||
| continue; | ||
| } | ||
| if ( $bookmark_starts_at === $this->bookmarks[ $this->state->current_token->bookmark_name ]->start ) { | ||
| while ( isset( $this->current_element ) && WP_HTML_Stack_Event::POP === $this->current_element->operation ) { | ||
| $this->current_element = array_shift( $this->element_queue ); | ||
|
sirreal marked this conversation as resolved.
|
||
| } | ||
| return true; | ||
| } | ||
| } | ||
| } while ( $this->next_token() ); | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <?php | ||
| /** | ||
| * Unit tests covering WP_HTML_Processor bookmark functionality. | ||
| * | ||
| * @package WordPress | ||
| * @subpackage HTML-API | ||
| */ | ||
|
|
||
| /** | ||
| * @group html-api | ||
| * | ||
| * @coversDefaultClass WP_HTML_Processor | ||
| */ | ||
| class Tests_HtmlApi_WpHtmlProcessor_Bookmark extends WP_UnitTestCase { | ||
| /** | ||
| * Ensures that bookmarks can be set and seeked to for the full processor. | ||
| * | ||
| * @ticket 62290 | ||
| */ | ||
| public function test_full_processor_seek() { | ||
| $bookmark_name = 'the-bookmark'; | ||
| $processor = WP_HTML_Processor::create_full_parser( '<html><body><div>' ); | ||
| $this->assertTrue( $processor->next_tag( 'BODY' ) ); | ||
| $this->assertTrue( $processor->set_bookmark( $bookmark_name ), 'Failed to set bookmark.' ); | ||
| $this->assertTrue( $processor->has_bookmark( $bookmark_name ), 'Failed has_bookmark check.' ); | ||
|
|
||
| // Move past the bookmark so it must scan backwards. | ||
| $this->assertTrue( $processor->next_tag( 'DIV' ) ); | ||
|
|
||
| // Confirm the bookmark works. | ||
| $this->assertTrue( $processor->seek( $bookmark_name ), 'Failed to seek to bookmark.' ); | ||
| $this->assertSame( 'BODY', $processor->get_tag() ); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.