Make WordPress Core

Changeset 59391


Ignore:
Timestamp:
11/12/2024 10:30:26 AM (4 weeks ago)
Author:
Bernhard Reiter
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.

Props jonsurrell, dmsnell, westonruter, mi5t4n.
Fixes #62290.

Location:
trunk
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/html-api/class-wp-html-open-elements.php

    r58992 r59391  
    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 ) {
  • trunk/src/wp-includes/html-api/class-wp-html-processor.php

    r59364 r59391  
    53295329         */
    53305330        if ( 'backward' === $direction ) {
    5331             /*
    5332              * Instead of clearing the parser state and starting fresh, calling the stack methods
    5333              * maintains the proper flags in the parser.
     5331
     5332            /*
     5333             * When moving backward, stateful stacks should be cleared.
    53345334             */
    53355335            foreach ( $this->state->stack_of_open_elements->walk_up() as $item ) {
    5336                 if ( 'context-node' === $item->bookmark_name ) {
    5337                     break;
    5338                 }
    5339 
    53405336                $this->state->stack_of_open_elements->remove_node( $item );
    53415337            }
    53425338
    53435339            foreach ( $this->state->active_formatting_elements->walk_up() as $item ) {
    5344                 if ( 'context-node' === $item->bookmark_name ) {
    5345                     break;
    5346                 }
    5347 
    53485340                $this->state->active_formatting_elements->remove_node( $item );
    53495341            }
    53505342
    5351             parent::seek( 'context-node' );
    5352             $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_BODY;
    5353             $this->state->frameset_ok    = true;
    5354             $this->element_queue         = array();
    5355             $this->current_element       = null;
    5356 
    5357             if ( isset( $this->context_node ) ) {
     5343            /*
     5344             * **After** clearing stacks, more processor state can be reset.
     5345             * This must be done after clearing the stack because those stacks generate events that
     5346             * would appear on a subsequent call to `next_token()`.
     5347             */
     5348            $this->state->frameset_ok                       = true;
     5349            $this->state->stack_of_template_insertion_modes = array();
     5350            $this->state->head_element                      = null;
     5351            $this->state->form_element                      = null;
     5352            $this->state->current_token                     = null;
     5353            $this->current_element                          = null;
     5354            $this->element_queue                            = array();
     5355
     5356            /*
     5357             * The absence of a context node indicates a full parse.
     5358             * The presence of a context node indicates a fragment parser.
     5359             */
     5360            if ( null === $this->context_node ) {
     5361                $this->change_parsing_namespace( 'html' );
     5362                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_INITIAL;
     5363                $this->breadcrumbs           = array();
     5364
     5365                $this->bookmarks['initial'] = new WP_HTML_Span( 0, 0 );
     5366                parent::seek( 'initial' );
     5367                unset( $this->bookmarks['initial'] );
     5368            } else {
     5369
     5370                /*
     5371                 * Push the root-node (HTML) back onto the stack of open elements.
     5372                 *
     5373                 * Fragment parsers require this extra bit of setup.
     5374                 * It's handled in full parsers by advancing the processor state.
     5375                 */
     5376                $this->state->stack_of_open_elements->push(
     5377                    new WP_HTML_Token(
     5378                        'root-node',
     5379                        'HTML',
     5380                        false
     5381                    )
     5382                );
     5383
     5384                $this->change_parsing_namespace(
     5385                    $this->context_node->integration_node_type
     5386                        ? 'html'
     5387                        : $this->context_node->namespace
     5388                );
     5389
     5390                if ( 'TEMPLATE' === $this->context_node->node_name ) {
     5391                    $this->state->stack_of_template_insertion_modes[] = WP_HTML_Processor_State::INSERTION_MODE_IN_TEMPLATE;
     5392                }
     5393
     5394                $this->reset_insertion_mode_appropriately();
    53585395                $this->breadcrumbs = array_slice( $this->breadcrumbs, 0, 2 );
    5359             } else {
    5360                 $this->breadcrumbs = array();
     5396                parent::seek( $this->context_node->bookmark_name );
    53615397            }
    53625398        }
    53635399
    5364         // When moving forwards, reparse the document until reaching the same location as the original bookmark.
    5365         if ( $bookmark_starts_at === $this->bookmarks[ $this->state->current_token->bookmark_name ]->start ) {
    5366             return true;
    5367         }
    5368 
    5369         while ( $this->next_token() ) {
     5400        /*
     5401         * Here, the processor moves forward through the document until it matches the bookmark.
     5402         * do-while is used here because the processor is expected to already be stopped on
     5403         * a token than may match the bookmarked location.
     5404         */
     5405        do {
     5406            /*
     5407             * The processor will stop on virtual tokens, but bookmarks may not be set on them.
     5408             * They should not be matched when seeking a bookmark, skip them.
     5409             */
     5410            if ( $this->is_virtual() ) {
     5411                continue;
     5412            }
    53705413            if ( $bookmark_starts_at === $this->bookmarks[ $this->state->current_token->bookmark_name ]->start ) {
    5371                 while ( isset( $this->current_element ) && WP_HTML_Stack_Event::POP === $this->current_element->operation ) {
    5372                     $this->current_element = array_shift( $this->element_queue );
    5373                 }
    53745414                return true;
    53755415            }
    5376         }
     5416        } while ( $this->next_token() );
    53775417
    53785418        return false;
  • trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php

    r59364 r59391  
    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.