Make WordPress Core

Changeset 58828


Ignore:
Timestamp:
07/29/2024 05:37:48 PM (8 months ago)
Author:
dmsnell
Message:

HTML API: Close all elements at the end of a document.

When the model of breadcrumb generation in the HTML Processor and node
traversal was simplified, the change introduced a bug whereby unclosed
nodes at the end of a document would remain unvisited and unclosed.

In this patch, a fix is applied to ensure that all open elements close
while traversing a document. A couple of minor documentation typos are
fixed in the patch as well.

Developed in https://github.com/wordpress/wordpress-develop/pull/7085
Discussed in https://core.trac.wordpress.org/ticket/61576

Follow-up to [58713].

Props: dmsnell, gziolo, jonsurrell.
See #61576.

Location:
trunk
Files:
2 edited

Legend:

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

    r58806 r58828  
    610610         *       tokens works in the meantime and isn't obviously wrong.
    611611         */
    612         while ( empty( $this->element_queue ) && $this->step() ) {
    613             continue;
     612        if ( empty( $this->element_queue ) && $this->step() ) {
     613            return $this->next_token();
    614614        }
    615615
     
    617617        $this->current_element = array_shift( $this->element_queue );
    618618        if ( ! isset( $this->current_element ) ) {
    619             return false;
     619            // There are no tokens left, so close all remaining open elements.
     620            while ( $this->state->stack_of_open_elements->pop() ) {
     621                continue;
     622            }
     623
     624            return empty( $this->element_queue ) ? false : $this->next_token();
    620625        }
    621626
     
    624629        /*
    625630         * The root node only exists in the fragment parser, and closing it
    626          * indicates that the parse is complete. Stop before popping if from
     631         * indicates that the parse is complete. Stop before popping it from
    627632         * the breadcrumbs.
    628633         */
    629634        if ( 'root-node' === $this->current_element->token->bookmark_name ) {
    630             return ! $is_pop && $this->next_token();
     635            return $this->next_token();
    631636        }
    632637
     
    639644
    640645        // Avoid sending close events for elements which don't expect a closing.
    641         if ( $is_pop && ! static::expects_closer( $this->current_element->token ) ) {
     646        if ( $is_pop && ! $this->expects_closer( $this->current_element->token ) ) {
    642647            return $this->next_token();
    643648        }
  • trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php

    r58779 r58828  
    477477
    478478    /**
     479     * Ensures that elements which are unopened at the end of a document are implicitly closed.
     480     *
     481     * @ticket 61576
     482     */
     483    public function test_closes_unclosed_elements() {
     484        $processor = WP_HTML_Processor::create_fragment( '<div><p><span>' );
     485
     486        $this->assertTrue(
     487            $processor->next_tag( 'SPAN' ),
     488            'Could not find SPAN element: check test setup.'
     489        );
     490
     491        // This is the end of the document, but there should be three closing events.
     492        $processor->next_token();
     493        $this->assertSame(
     494            'SPAN',
     495            $processor->get_tag(),
     496            'Should have found implicit SPAN closing tag.'
     497        );
     498
     499        $processor->next_token();
     500        $this->assertSame(
     501            'P',
     502            $processor->get_tag(),
     503            'Should have found implicit P closing tag.'
     504        );
     505
     506        $processor->next_token();
     507        $this->assertSame(
     508            'DIV',
     509            $processor->get_tag(),
     510            'Should have found implicit DIV closing tag.'
     511        );
     512
     513        $this->assertFalse(
     514            $processor->next_token(),
     515            "Should have failed to find any more tokens but found a '{$processor->get_token_name()}'"
     516        );
     517    }
     518
     519    /**
    479520     * Ensures that subclasses can be created from ::create_fragment method.
    480521     *
Note: See TracChangeset for help on using the changeset viewer.