Make WordPress Core

Changeset 58828


Ignore:
Timestamp:
07/29/2024 05:37:48 PM (2 years 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.