Make WordPress Core

Changeset 57507


Ignore:
Timestamp:
02/01/2024 12:41:40 AM (3 years 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.