Make WordPress Core

Changeset 58992


Ignore:
Timestamp:
09/04/2024 07:23:48 PM (5 months ago)
Author:
dmsnell
Message:

HTML API: Only examine HTML nodes in pop_until() instack of open elements.

The pop_until( $tag_name ) method in the stack of open elements should only be examining HTML elements, but it has only been checking the tag name. This has led to closing the wrong tags when run from inside foreign content. A very specific situation where this may arise is when a TEMPLATE closer is found inside foreign content, inside another template.

HTML:template   SVG:template                 HTML:/template
<template><svg><template><foreignObject><div></template><div>
╰──< this outer TEMPLATE is closed by this one >───╯

This patch constains the method to checking for elements matching the tag name which are in the HTML namespace so that the proper detection occurs.

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

Follow-up to [58867].

Props dmsnell, jonsurrell.
See #61576.

Location:
trunk
Files:
3 edited

Legend:

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

    r58867 r58992  
    531531
    532532    /**
    533      * Pops nodes off of the stack of open elements until one with the given tag name has been popped.
     533     * Pops nodes off of the stack of open elements until an HTML tag with the given name has been popped.
    534534     *
    535535     * @since 6.4.0
     
    537537     * @see WP_HTML_Open_Elements::pop
    538538     *
    539      * @param string $tag_name Name of tag that needs to be popped off of the stack of open elements.
     539     * @param string $html_tag_name Name of tag that needs to be popped off of the stack of open elements.
    540540     * @return bool Whether a tag of the given name was found and popped off of the stack of open elements.
    541541     */
    542     public function pop_until( string $tag_name ): bool {
     542    public function pop_until( string $html_tag_name ): bool {
    543543        foreach ( $this->walk_up() as $item ) {
    544             if ( 'context-node' === $item->bookmark_name ) {
    545                 return true;
    546             }
    547 
    548544            $this->pop();
    549545
     546            if ( 'html' !== $item->namespace ) {
     547                continue;
     548            }
     549
    550550            if (
    551                 '(internal: H1 through H6 - do not use)' === $tag_name &&
     551                '(internal: H1 through H6 - do not use)' === $html_tag_name &&
    552552                in_array( $item->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
    553553            ) {
     
    555555            }
    556556
    557             if ( $tag_name === $item->node_name ) {
     557            if ( $html_tag_name === $item->node_name ) {
    558558                return true;
    559559            }
  • trunk/src/wp-includes/html-api/class-wp-html-processor.php

    r58985 r58992  
    54295429            }
    54305430
     5431            // All of the following rules are for matching HTML elements.
     5432            if ( 'html' !== $node->namespace ) {
     5433                continue;
     5434            }
     5435
    54315436            switch ( $node->node_name ) {
    54325437                /*
     
    54445449                    if ( ! $last ) {
    54455450                        foreach ( $this->state->stack_of_open_elements->walk_up( $node ) as $ancestor ) {
     5451                            if ( 'html' !== $ancestor->namespace ) {
     5452                                continue;
     5453                            }
     5454
    54465455                            switch ( $ancestor->node_name ) {
    54475456                                /*
  • trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php

    r58985 r58992  
    522522
    523523    /**
     524     * Ensures that the HTML Processor correctly handles TEMPLATE tag closing and namespaces.
     525     *
     526     * This is a tricky test case that corresponds to the html5lib tests "template/line1466".
     527     *
     528     * When the `</template>` token is reached it is in the HTML namespace (thanks to the
     529     * SVG `foreignObject` element). It is not handled as foreign content; therefore, it
     530     * closes the open HTML `TEMPLATE` element (the first `<template>` token) - _not_ the
     531     * SVG `TEMPLATE` element (the second `<template>` token).
     532     *
     533     * The test is included here because it may show up as unsupported markup and be skipped by
     534     * the html5lib test suite.
     535     *
     536     * @ticket 61576
     537     */
     538    public function test_template_tag_closes_html_template_element() {
     539        $processor = WP_HTML_Processor::create_fragment( '<template><svg><template><foreignObject><div></template><div>' );
     540
     541        $this->assertTrue( $processor->next_tag( 'DIV' ) );
     542        $this->assertSame( array( 'HTML', 'BODY', 'TEMPLATE', 'SVG', 'TEMPLATE', 'FOREIGNOBJECT', 'DIV' ), $processor->get_breadcrumbs() );
     543        $this->assertTrue( $processor->next_tag( 'DIV' ) );
     544        $this->assertSame( array( 'HTML', 'BODY', 'DIV' ), $processor->get_breadcrumbs() );
     545    }
     546
     547    /**
    524548     * Ensures that the tag processor is case sensitive when removing CSS classes in no-quirks mode.
    525549     *
Note: See TracChangeset for help on using the changeset viewer.