Make WordPress Core

Changeset 58040

Timestamp:
04/24/2024 07:43:02 AM (3 months ago)
Author:
dmsnell
Message:

HTML API: Fix detection of single-length funky comments.

Since [60428] the Tag Processor has been misidentifying single-character
funky comments. It has been asserting that the full token-length for a
funky comment must be at least three characters after the opening (e.g.
</1>), but it has been starting to look for the closing > after
those same three characters. This means that it has been skipping the
actual close of these funky comments and swallowing up the next syntax
until it finds a >, often consuming the next tag in the process.

This patch fixes the detector and restores finding the following token.

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

Follow-up to [60428].
Fixes #60170.
Props dmsnell, gziolo, jonsurrell.

Location:
trunk
Files:
3 edited

Legend:

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

    r57987 r58040  
    16301630             * https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state
    16311631             */
    1632             if ( '!' === $html[ $at + 1 ] ) {
     1632            if ( '!' === $html[ $at + 1 ] ) {
    16331633                /*
    16341634                 * `<!--` transitions to a comment state – apply further comment rules.
     
    18101810             */
    18111811            if ( '>' === $html[ $at + 1 ] ) {
     1812
     1813
     1814
     1815
     1816
     1817
    18121818                $this->parser_state         = self::STATE_PRESUMPTUOUS_TAG;
    18131819                $this->token_length         = $at + 2 - $this->token_starts_at;
     
    18201826             * See https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state
    18211827             */
    1822             if ( '?' === $html[ $at + 1 ] ) {
     1828            if ( '?' === $html[ $at + 1 ] ) {
    18231829                $closer_at = strpos( $html, '>', $at + 2 );
    18241830                if ( false === $closer_at ) {
     
    18921898                }
    18931899
    1894                 $closer_at = strpos( $html, '>', $at + 3 );
     1900                $closer_at = strpos( $html, '>', $at + );
    18951901                if ( false === $closer_at ) {
    18961902                    $this->parser_state = self::STATE_INCOMPLETE_INPUT;
  • trunk/tests/phpunit/tests/html-api/wpHtmlTagProcessor-token-scanning.php

    r57508 r58040  
    762762
    763763    /**
     764
     765
     766
     767
     768
     769
     770
     771
     772
     773
     774
     775
     776
     777
     778
     779
     780
     781
     782
     783
     784
     785
     786
     787
     788
     789
     790
     791
     792
     793
     794
     795
     796
     797
     798
     799
     800
     801
     802
     803
     804
     805
     806
     807
     808
     809
     810
     811
     812
     813
     814
     815
     816
    764817     * Test helper that wraps a string in double quotes.
    765818     *
  • trunk/tests/phpunit/tests/interactivity-api/wpInteractivityAPI.php

    r57987 r58040  
    589589     *
    590590     * @covers ::process_directives
    591      */
    592     public function test_process_directives_doesnt_change_html_if_contains_unbalanced_tags() {
     591     *
     592     * @dataProvider data_html_with_unbalanced_tags
     593     *
     594     * @param string $html HTML containing unbalanced tags and also a directive.
     595     */
     596    public function test_process_directives_doesnt_change_html_if_contains_unbalanced_tags( $html ) {
    593597        $this->interactivity->state( 'myPlugin', array( 'id' => 'some-id' ) );
    594598
    595         $html_samples = array(
    596             '<div data-wp-bind--id="myPlugin::state.id">Inner content</div></div>',
    597             '<div data-wp-bind--id="myPlugin::state.id">Inner content</div><div>',
    598             '<div><div data-wp-bind--id="myPlugin::state.id">Inner content</div>',
    599             '</div><div data-wp-bind--id="myPlugin::state.id">Inner content</div>',
    600             '<div data-wp-bind--id="myPlugin::state.id">Inner<div>content</div>',
    601             '<div data-wp-bind--id="myPlugin::state.id">Inner</div>content</div>',
    602             '<div data-wp-bind--id="myPlugin::state.id"><span>Inner content</div>',
    603             '<div data-wp-bind--id="myPlugin::state.id">Inner content</div></span>',
    604             '<div data-wp-bind--id="myPlugin::state.id"><span>Inner content</div></span>',
    605             '<div data-wp-bind--id="myPlugin::state.id">Inner conntent</ ></div>',
    606         );
    607 
    608         foreach ( $html_samples as $html ) {
    609             $processed_html = $this->interactivity->process_directives( $html );
    610             $p              = new WP_HTML_Tag_Processor( $processed_html );
    611             $p->next_tag();
    612             $this->assertNull( $p->get_attribute( 'id' ) );
    613         }
     599        $processed_html = $this->interactivity->process_directives( $html );
     600        $p              = new WP_HTML_Tag_Processor( $processed_html );
     601        $p->next_tag();
     602        $this->assertNull( $p->get_attribute( 'id' ) );
     603    }
     604
     605    /**
     606     * Data provider.
     607     *
     608     * @return array[].
     609     */
     610    public static function data_html_with_unbalanced_tags() {
     611        return array(
     612            'DIV closer after'   => array( '<div data-wp-bind--id="myPlugin::state.id">Inner content</div></div>' ),
     613            'DIV opener after'   => array( '<div data-wp-bind--id="myPlugin::state.id">Inner content</div><div>' ),
     614            'DIV opener before'  => array( '<div><div data-wp-bind--id="myPlugin::state.id">Inner content</div>' ),
     615            'DIV closer before'  => array( '</div><div data-wp-bind--id="myPlugin::state.id">Inner content</div>' ),
     616            'DIV opener inside'  => array( '<div data-wp-bind--id="myPlugin::state.id">Inner<div>content</div>' ),
     617            'DIV closer inside'  => array( '<div data-wp-bind--id="myPlugin::state.id">Inner</div>content</div>' ),
     618            'SPAN opener inside' => array( '<div data-wp-bind--id="myPlugin::state.id"><span>Inner content</div>' ),
     619            'SPAN closer after'  => array( '<div data-wp-bind--id="myPlugin::state.id">Inner content</div></span>' ),
     620            'SPAN overlapping'   => array( '<div data-wp-bind--id="myPlugin::state.id"><span>Inner content</div></span>' ),
     621        );
    614622    }
    615623
Note: See TracChangeset for help on using the changeset viewer.