Make WordPress Core

Changeset 59721


Ignore:
Timestamp:
01/28/2025 02:46:18 PM (34 hours ago)
Author:
jorbin
Message:

HTML API: Ensure that full processor can seek to earlier bookmarks.

When the HTML Processor seeks to an earlier place, it returns the the beginning of the document and proceeds forward until it reaches the appropriate location. This requires resetting internal state so that the processor can correctly proceed from the beginning of the document.

The seeking reset logic was not adapted to account for the full processor (i.e. when created via WP_HTML_Processor::create_full_parser()). This change updates the seek logic to account for the full and fragment parsers as well as other state that has been introduced in the interim and should be reset.

Reviewed by westonruter, jorbin.
Merges [59391] to the 6.7 branch.

Props jonsurrell, dmsnell, westonruter, mi5t4n, bernhard-reiter.
Fixes #62290.

Location:
branches/6.7
Files:
4 edited
1 copied

Legend:

Unmodified
Added
Removed
  • branches/6.7

  • branches/6.7/src/wp-includes/html-api/class-wp-html-open-elements.php

    r58992 r59721  
    521521        }
    522522
    523         if ( 'context-node' === $item->bookmark_name ) {
    524             $this->stack[] = $item;
    525             return false;
    526         }
    527 
    528523        $this->after_element_pop( $item );
    529524        return true;
     
    586581     */
    587582    public function remove_node( WP_HTML_Token $token ): bool {
    588         if ( 'context-node' === $token->bookmark_name ) {
    589             return false;
    590         }
    591 
    592583        foreach ( $this->walk_up() as $position_from_end => $item ) {
    593584            if ( $token->bookmark_name !== $item->bookmark_name ) {
  • branches/6.7/src/wp-includes/html-api/class-wp-html-processor.php

    r59694 r59721  
    53355335         */
    53365336        if ( 'backward' === $direction ) {
    5337             /*
    5338              * Instead of clearing the parser state and starting fresh, calling the stack methods
    5339              * maintains the proper flags in the parser.
     5337
     5338            /*
     5339             * When moving backward, stateful stacks should be cleared.
    53405340             */
    53415341            foreach ( $this->state->stack_of_open_elements->walk_up() as $item ) {
    5342                 if ( 'context-node' === $item->bookmark_name ) {
    5343                     break;
    5344                 }
    5345 
    53465342                $this->state->stack_of_open_elements->remove_node( $item );
    53475343            }
    53485344
    53495345            foreach ( $this->state->active_formatting_elements->walk_up() as $item ) {
    5350                 if ( 'context-node' === $item->bookmark_name ) {
    5351                     break;
    5352                 }
    5353 
    53545346                $this->state->active_formatting_elements->remove_node( $item );
    53555347            }
    53565348
    5357             parent::seek( 'context-node' );
    5358             $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_BODY;
    5359             $this->state->frameset_ok    = true;
    5360             $this->element_queue         = array();
    5361             $this->current_element       = null;
    5362 
    5363             if ( isset( $this->context_node ) ) {
     5349            /*
     5350             * **After** clearing stacks, more processor state can be reset.
     5351             * This must be done after clearing the stack because those stacks generate events that
     5352             * would appear on a subsequent call to `next_token()`.
     5353             */
     5354            $this->state->frameset_ok                       = true;
     5355            $this->state->stack_of_template_insertion_modes = array();
     5356            $this->state->head_element                      = null;
     5357            $this->state->form_element                      = null;
     5358            $this->state->current_token                     = null;
     5359            $this->current_element                          = null;
     5360            $this->element_queue                            = array();
     5361
     5362            /*
     5363             * The absence of a context node indicates a full parse.
     5364             * The presence of a context node indicates a fragment parser.
     5365             */
     5366            if ( null === $this->context_node ) {
     5367                $this->change_parsing_namespace( 'html' );
     5368                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_INITIAL;
     5369                $this->breadcrumbs           = array();
     5370
     5371                $this->bookmarks['initial'] = new WP_HTML_Span( 0, 0 );
     5372                parent::seek( 'initial' );
     5373                unset( $this->bookmarks['initial'] );
     5374            } else {
     5375
     5376                /*
     5377                 * Push the root-node (HTML) back onto the stack of open elements.
     5378                 *
     5379                 * Fragment parsers require this extra bit of setup.
     5380                 * It's handled in full parsers by advancing the processor state.
     5381                 */
     5382                $this->state->stack_of_open_elements->push(
     5383                    new WP_HTML_Token(
     5384                        'root-node',
     5385                        'HTML',
     5386                        false
     5387                    )
     5388                );
     5389
     5390                $this->change_parsing_namespace(
     5391                    $this->context_node->integration_node_type
     5392                        ? 'html'
     5393                        : $this->context_node->namespace
     5394                );
     5395
     5396                if ( 'TEMPLATE' === $this->context_node->node_name ) {
     5397                    $this->state->stack_of_template_insertion_modes[] = WP_HTML_Processor_State::INSERTION_MODE_IN_TEMPLATE;
     5398                }
     5399
     5400                $this->reset_insertion_mode_appropriately();
    53645401                $this->breadcrumbs = array_slice( $this->breadcrumbs, 0, 2 );
    5365             } else {
    5366                 $this->breadcrumbs = array();
     5402                parent::seek( $this->context_node->bookmark_name );
    53675403            }
    53685404        }
    53695405
    5370         // When moving forwards, reparse the document until reaching the same location as the original bookmark.
    5371         if ( $bookmark_starts_at === $this->bookmarks[ $this->state->current_token->bookmark_name ]->start ) {
    5372             return true;
    5373         }
    5374 
    5375         while ( $this->next_token() ) {
     5406        /*
     5407         * Here, the processor moves forward through the document until it matches the bookmark.
     5408         * do-while is used here because the processor is expected to already be stopped on
     5409         * a token than may match the bookmarked location.
     5410         */
     5411        do {
     5412            /*
     5413             * The processor will stop on virtual tokens, but bookmarks may not be set on them.
     5414             * They should not be matched when seeking a bookmark, skip them.
     5415             */
     5416            if ( $this->is_virtual() ) {
     5417                continue;
     5418            }
    53765419            if ( $bookmark_starts_at === $this->bookmarks[ $this->state->current_token->bookmark_name ]->start ) {
    5377                 while ( isset( $this->current_element ) && WP_HTML_Stack_Event::POP === $this->current_element->operation ) {
    5378                     $this->current_element = array_shift( $this->element_queue );
    5379                 }
    53805420                return true;
    53815421            }
    5382         }
     5422        } while ( $this->next_token() );
    53835423
    53845424        return false;
  • branches/6.7/tests/phpunit/tests/html-api/wpHtmlProcessor.php

    r59694 r59721  
    134134            // Create a bookmark inside of that stack.
    135135            if ( null !== $processor->get_attribute( 'two' ) ) {
    136                 $processor->set_bookmark( 'two' );
     136                $this->assertTrue( $processor->set_bookmark( 'two' ) );
    137137                break;
    138138            }
Note: See TracChangeset for help on using the changeset viewer.