Make WordPress Core

Changeset 57507


Ignore:
Timestamp:
02/01/2024 12:41:40 AM (16 months ago)
Author:
dmsnell
Message:

HTML API: Fix void tag nesting with next_token

When next_token() was introduced, it introduced a regression in the HTML
Processor whereby void tags remain on the stack of open elements when they
shouldn't. This led to invalid values returned from get_breadcrumbs().

The reason was that calling next_token() works through a different code path
than the HTML Processor runs everything else. To solve this, its sub-classed
next_token() called step( self::REPROCESS_CURRENT_TOKEN ) so that the proper
HTML accounting takes place.

Unfortunately that same reprocessing code path skipped the step whereby void
and self-closing elements are popped from the stack of open elements.

In this patch, that step is run with a third mode for step(), which is the
new self::PROCESS_CURRENT_TOKEN. This mode acts as if self::PROCESS_NEXT_NODE
were called, except it doesn't advance the parser.

Developed in https://github.com/WordPress/wordpress-develop/pull/5975
Discussed in https://core.trac.wordpress.org/ticket/60382

Follow-up to [57348]

Props dmsnell, jonsurrell
Fixes #60382

Location:
trunk
Files:
2 edited

Legend:

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

    r57348 r57507  
    432432
    433433        if ( '#tag' === $this->get_token_type() ) {
    434             $this->step( self::REPROCESS_CURRENT_NODE );
     434            $this->step( self::PROCESS_CURRENT_NODE );
    435435        }
    436436
     
    514514        }
    515515
    516         if ( self::PROCESS_NEXT_NODE === $node_to_process ) {
     516        if ( self::REPROCESS_CURRENT_NODE !== $node_to_process ) {
    517517            /*
    518518             * Void elements still hop onto the stack of open elements even though
     
    533533                $this->state->stack_of_open_elements->pop();
    534534            }
    535 
     535        }
     536
     537        if ( self::PROCESS_NEXT_NODE === $node_to_process ) {
    536538            while ( parent::next_token() && '#tag' !== $this->get_token_type() ) {
    537539                continue;
     
    17831785
    17841786    /**
     1787     * Indicates that the current HTML token should be processed without advancing the parser.
     1788     *
     1789     * @since 6.5.0
     1790     *
     1791     * @var string
     1792     */
     1793    const PROCESS_CURRENT_NODE = 'process-current-node';
     1794
     1795    /**
    17851796     * Indicates that the parser encountered unsupported markup and has bailed.
    17861797     *
  • trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php

    r57343 r57507  
    179179        $this->assertTrue(
    180180            $processor->next_tag(),
     181            'Should have found the DIV as the second tag.'
     182        );
     183
     184        $this->assertSame(
     185            array( 'HTML', 'BODY', 'DIV' ),
     186            $processor->get_breadcrumbs(),
     187            "DIV should have been a sibling of the {$tag_name}."
     188        );
     189    }
     190
     191    /**
     192     * Ensure non-nesting tags do not nest when processing tokens.
     193     *
     194     * @ticket 60382
     195     *
     196     * @dataProvider data_void_tags
     197     *
     198     * @param string $tag_name Name of void tag under test.
     199     */
     200    public function test_cannot_nest_void_tags_next_token( $tag_name ) {
     201        $processor = WP_HTML_Processor::create_fragment( "<{$tag_name}><div>" );
     202
     203        /*
     204         * This HTML represents the same as the following HTML,
     205         * assuming that it were provided `<img>` as the tag:
     206         *
     207         *     <html>
     208         *         <body>
     209         *             <img>
     210         *             <div></div>
     211         *         </body>
     212         *     </html>
     213         */
     214
     215        $found_tag = $processor->next_token();
     216
     217        if ( WP_HTML_Processor::ERROR_UNSUPPORTED === $processor->get_last_error() ) {
     218            $this->markTestSkipped( "Tag {$tag_name} is not supported." );
     219        }
     220
     221        $this->assertTrue(
     222            $found_tag,
     223            "Could not find first {$tag_name}."
     224        );
     225
     226        $this->assertSame(
     227            array( 'HTML', 'BODY', $tag_name ),
     228            $processor->get_breadcrumbs(),
     229            'Found incorrect nesting of first element.'
     230        );
     231
     232        $this->assertTrue(
     233            $processor->next_token(),
    181234            'Should have found the DIV as the second tag.'
    182235        );
Note: See TracChangeset for help on using the changeset viewer.