Make WordPress Core


Ignore:
Timestamp:
07/31/2024 04:54:23 PM (2 months ago)
Author:
dmsnell
Message:

HTML API: Introduce full parsing mode in HTML Processor.

The HTML Processor has only supported a specific kind of parsing mode
called _the fragment parsing mode_, where it behaves in the same way
that node.innerHTML = html does in the DOM. This mode assumes a
context node and doesn't support parsing an entire document.

As part of work to add more spec support to the HTML API, this patch
introduces a full parsing mode, which can parse a full HTML document
from start to end, including the doctype declaration and head tags.

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

Props: dmsnell, jonsurrell.
See #61576.

File:
1 edited

Legend:

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

    r58833 r58836  
    257257    private $context_node = null;
    258258
    259     /**
    260      * Whether the parser has yet processed the context node,
    261      * if created as a fragment parser.
    262      *
    263      * The context node will be initially pushed onto the stack of open elements,
    264      * but when created as a fragment parser, this context element (and the implicit
    265      * HTML document node above it) should not be exposed as a matched token or node.
    266      *
    267      * This boolean indicates whether the processor should skip over the current
    268      * node in its initial search for the first node created from the input HTML.
    269      *
    270      * @var bool
    271      */
    272     private $has_seen_context_node = false;
    273 
    274259    /*
    275260     * Public Interface Functions
     
    313298        }
    314299
    315         $processor                        = new static( $html, self::CONSTRUCTOR_UNLOCK_CODE );
    316         $processor->state->context_node   = array( 'BODY', array() );
    317         $processor->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_BODY;
     300        $processor                             = new static( $html, self::CONSTRUCTOR_UNLOCK_CODE );
     301        $processor->state->context_node        = array( 'BODY', array() );
     302        $processor->state->insertion_mode      = WP_HTML_Processor_State::INSERTION_MODE_IN_BODY;
     303        $processor->state->encoding            = $encoding;
     304        $processor->state->encoding_confidence = 'certain';
    318305
    319306        // @todo Create "fake" bookmarks for non-existent but implied nodes.
     
    337324        $processor->context_node = $context_node;
    338325        $processor->breadcrumbs  = array( 'HTML', $context_node->node_name );
     326
     327        return $processor;
     328    }
     329
     330    /**
     331     * Creates an HTML processor in the full parsing mode.
     332     *
     333     * It's likely that a fragment parser is more appropriate, unless sending an
     334     * entire HTML document from start to finish. Consider a fragment parser with
     335     * a context node of `<body>`.
     336     *
     337     * Since UTF-8 is the only currently-accepted charset, if working with a
     338     * document that isn't UTF-8, it's important to convert the document before
     339     * creating the processor: pass in the converted HTML.
     340     *
     341     * @param string      $html                    Input HTML document to process.
     342     * @param string|null $known_definite_encoding Optional. If provided, specifies the charset used
     343     *                                             in the input byte stream. Currently must be UTF-8.
     344     * @return static|null The created processor if successful, otherwise null.
     345     */
     346    public static function create_full_parser( $html, $known_definite_encoding = 'UTF-8' ) {
     347        if ( 'UTF-8' !== $known_definite_encoding ) {
     348            return null;
     349        }
     350
     351        $processor                             = new static( $html, self::CONSTRUCTOR_UNLOCK_CODE );
     352        $processor->state->encoding            = $known_definite_encoding;
     353        $processor->state->encoding_confidence = 'certain';
    339354
    340355        return $processor;
     
    9941009     */
    9951010    private function step_initial(): bool {
    996         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_INITIAL . ' state.' );
     1011        $token_name = $this->get_token_name();
     1012        $token_type = $this->get_token_type();
     1013        $op_sigil   = '#tag' === $token_type ? ( parent::is_tag_closer() ? '-' : '+' ) : '';
     1014        $op         = "{$op_sigil}{$token_name}";
     1015
     1016        switch ( $op ) {
     1017            /*
     1018             * > A character token that is one of U+0009 CHARACTER TABULATION,
     1019             * > U+000A LINE FEED (LF), U+000C FORM FEED (FF),
     1020             * > U+000D CARRIAGE RETURN (CR), or U+0020 SPACE
     1021             *
     1022             * Parse error: ignore the token.
     1023             */
     1024            case '#text':
     1025                $text = $this->get_modifiable_text();
     1026                if ( strlen( $text ) === strspn( $text, " \t\n\f\r" ) ) {
     1027                    return $this->step();
     1028                }
     1029                goto initial_anything_else;
     1030                break;
     1031
     1032            /*
     1033             * > A comment token
     1034             */
     1035            case '#comment':
     1036            case '#funky-comment':
     1037            case '#presumptuous-tag':
     1038                $this->insert_html_element( $this->state->current_token );
     1039                return true;
     1040
     1041            /*
     1042             * > A DOCTYPE token
     1043             */
     1044            case 'html':
     1045                $contents = $this->get_modifiable_text();
     1046                if ( ' html' !== $contents ) {
     1047                    /*
     1048                     * @todo When the HTML Tag Processor fully parses the DOCTYPE declaration,
     1049                     *       this code should examine the contents to set the compatability mode.
     1050                     */
     1051                    $this->bail( 'Cannot process any DOCTYPE other than a normative HTML5 doctype.' );
     1052                }
     1053
     1054                /*
     1055                 * > Then, switch the insertion mode to "before html".
     1056                 */
     1057                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_BEFORE_HTML;
     1058                return true;
     1059        }
     1060
     1061        /*
     1062         * > Anything else
     1063         */
     1064        initial_anything_else:
     1065        $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_BEFORE_HTML;
     1066        return $this->step( self::REPROCESS_CURRENT_NODE );
    9971067    }
    9981068
     
    10031073     * logic for the generalized WP_HTML_Processor::step() function.
    10041074     *
    1005      * @since 6.7.0 Stub implementation.
     1075     * @since 6.7.0
    10061076     *
    10071077     * @throws WP_HTML_Unsupported_Exception When encountering unsupported HTML input.
     
    10131083     */
    10141084    private function step_before_html(): bool {
    1015         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_BEFORE_HTML . ' state.' );
     1085        $token_name = $this->get_token_name();
     1086        $token_type = $this->get_token_type();
     1087        $is_closer  = parent::is_tag_closer();
     1088        $op_sigil   = '#tag' === $token_type ? ( $is_closer ? '-' : '+' ) : '';
     1089        $op         = "{$op_sigil}{$token_name}";
     1090
     1091        switch ( $op ) {
     1092            /*
     1093             * > A DOCTYPE token
     1094             */
     1095            case 'html':
     1096                // Parse error: ignore the token.
     1097                return $this->step();
     1098
     1099            /*
     1100             * > A comment token
     1101             */
     1102            case '#comment':
     1103            case '#funky-comment':
     1104            case '#presumptuous-tag':
     1105                $this->insert_html_element( $this->state->current_token );
     1106                return true;
     1107
     1108            /*
     1109             * > A character token that is one of U+0009 CHARACTER TABULATION,
     1110             * > U+000A LINE FEED (LF), U+000C FORM FEED (FF),
     1111             * > U+000D CARRIAGE RETURN (CR), or U+0020 SPACE
     1112             *
     1113             * Parse error: ignore the token.
     1114             */
     1115            case '#text':
     1116                $text = $this->get_modifiable_text();
     1117                if ( strlen( $text ) === strspn( $text, " \t\n\f\r" ) ) {
     1118                    return $this->step();
     1119                }
     1120                goto before_html_anything_else;
     1121                break;
     1122
     1123            /*
     1124             * > A start tag whose tag name is "html"
     1125             */
     1126            case '+HTML':
     1127                $this->insert_html_element( $this->state->current_token );
     1128                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_BEFORE_HEAD;
     1129                return true;
     1130
     1131            /*
     1132             * > An end tag whose tag name is one of: "head", "body", "html", "br"
     1133             *
     1134             * Closing BR tags are always reported by the Tag Processor as opening tags.
     1135             */
     1136            case '-HEAD':
     1137            case '-BODY':
     1138            case '-HTML':
     1139                /*
     1140                 * > Act as described in the "anything else" entry below.
     1141                 */
     1142                goto before_html_anything_else;
     1143                break;
     1144        }
     1145
     1146        /*
     1147         * > Any other end tag
     1148         */
     1149        if ( $is_closer ) {
     1150            // Parse error: ignore the token.
     1151            return $this->step();
     1152        }
     1153
     1154        /*
     1155         * > Anything else.
     1156         *
     1157         * > Create an html element whose node document is the Document object.
     1158         * > Append it to the Document object. Put this element in the stack of open elements.
     1159         * > Switch the insertion mode to "before head", then reprocess the token.
     1160         */
     1161        before_html_anything_else:
     1162        $this->insert_virtual_node( 'HTML' );
     1163        $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_BEFORE_HEAD;
     1164        return $this->step( self::REPROCESS_CURRENT_NODE );
    10161165    }
    10171166
     
    10321181     */
    10331182    private function step_before_head(): bool {
    1034         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_BEFORE_HEAD . ' state.' );
     1183        $token_name = $this->get_token_name();
     1184        $token_type = $this->get_token_type();
     1185        $is_closer  = parent::is_tag_closer();
     1186        $op_sigil   = '#tag' === $token_type ? ( $is_closer ? '-' : '+' ) : '';
     1187        $op         = "{$op_sigil}{$token_name}";
     1188
     1189        switch ( $op ) {
     1190            /*
     1191             * > A character token that is one of U+0009 CHARACTER TABULATION,
     1192             * > U+000A LINE FEED (LF), U+000C FORM FEED (FF),
     1193             * > U+000D CARRIAGE RETURN (CR), or U+0020 SPACE
     1194             *
     1195             * Parse error: ignore the token.
     1196             */
     1197            case '#text':
     1198                $text = $this->get_modifiable_text();
     1199                if ( strlen( $text ) === strspn( $text, " \t\n\f\r" ) ) {
     1200                    return $this->step();
     1201                }
     1202                goto before_head_anything_else;
     1203                break;
     1204
     1205            /*
     1206             * > A comment token
     1207             */
     1208            case '#comment':
     1209            case '#funky-comment':
     1210            case '#presumptuous-tag':
     1211                $this->insert_html_element( $this->state->current_token );
     1212                return true;
     1213
     1214            /*
     1215             * > A DOCTYPE token
     1216             */
     1217            case 'html':
     1218                // Parse error: ignore the token.
     1219                return $this->step();
     1220
     1221            /*
     1222             * > A start tag whose tag name is "html"
     1223             */
     1224            case '+HTML':
     1225                return $this->step_in_body();
     1226
     1227            /*
     1228             * > A start tag whose tag name is "head"
     1229             */
     1230            case '+HEAD':
     1231                $this->insert_html_element( $this->state->current_token );
     1232                $this->state->head_element   = $this->state->current_token;
     1233                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_HEAD;
     1234                return true;
     1235
     1236            /*
     1237             * > An end tag whose tag name is one of: "head", "body", "html", "br"
     1238             * > Act as described in the "anything else" entry below.
     1239             *
     1240             * Closing BR tags are always reported by the Tag Processor as opening tags.
     1241             */
     1242            case '-HEAD':
     1243            case '-BODY':
     1244            case '-HTML':
     1245                goto before_head_anything_else;
     1246                break;
     1247        }
     1248
     1249        if ( $is_closer ) {
     1250            // Parse error: ignore the token.
     1251            return $this->step();
     1252        }
     1253
     1254        /*
     1255         * > Anything else
     1256         *
     1257         * > Insert an HTML element for a "head" start tag token with no attributes.
     1258         */
     1259        before_head_anything_else:
     1260        $this->state->head_element   = $this->insert_virtual_node( 'HEAD' );
     1261        $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_HEAD;
     1262        return $this->step( self::REPROCESS_CURRENT_NODE );
    10351263    }
    10361264
     
    10571285        $op         = "{$op_sigil}{$token_name}";
    10581286
    1059         /*
    1060          * > A character token that is one of U+0009 CHARACTER TABULATION,
    1061          * > U+000A LINE FEED (LF), U+000C FORM FEED (FF),
    1062          * > U+000D CARRIAGE RETURN (CR), or U+0020 SPACE
    1063          */
    1064         if ( '#text' === $op ) {
    1065             $text = $this->get_modifiable_text();
    1066             if ( '' === $text ) {
     1287        switch ( $op ) {
     1288            case '#text':
    10671289                /*
    1068                  * If the text is empty after processing HTML entities and stripping
    1069                  * U+0000 NULL bytes then ignore the token.
     1290                 * > A character token that is one of U+0009 CHARACTER TABULATION,
     1291                 * > U+000A LINE FEED (LF), U+000C FORM FEED (FF),
     1292                 * > U+000D CARRIAGE RETURN (CR), or U+0020 SPACE
    10701293                 */
    1071                 return $this->step();
    1072             }
    1073 
    1074             if ( strlen( $text ) === strspn( $text, " \t\n\f\r" ) ) {
    1075                 // Insert the character.
    1076                 $this->insert_html_element( $this->state->current_token );
    1077                 return true;
    1078             }
    1079         }
    1080 
    1081         switch ( $op ) {
     1294                $text = $this->get_modifiable_text();
     1295                if ( '' === $text ) {
     1296                    /*
     1297                     * If the text is empty after processing HTML entities and stripping
     1298                     * U+0000 NULL bytes then ignore the token.
     1299                     */
     1300                    return $this->step();
     1301                }
     1302
     1303                if ( strlen( $text ) === strspn( $text, " \t\n\f\r" ) ) {
     1304                    // Insert the character.
     1305                    $this->insert_html_element( $this->state->current_token );
     1306                    return true;
     1307                }
     1308
     1309                goto in_head_anything_else;
     1310                break;
     1311
    10821312            /*
    10831313             * > A comment token
     
    11251355                 */
    11261356                $charset = $this->get_attribute( 'charset' );
    1127                 if ( is_string( $charset ) ) {
     1357                if ( is_string( $charset ) && 'tentative' === $this->state->encoding_confidence ) {
    11281358                    $this->bail( 'Cannot yet process META tags with charset to determine encoding.' );
    11291359                }
     
    11421372                    is_string( $http_equiv ) &&
    11431373                    is_string( $content ) &&
    1144                     0 === strcasecmp( $http_equiv, 'Content-Type' )
     1374                    0 === strcasecmp( $http_equiv, 'Content-Type' ) &&
     1375                    'tentative' === $this->state->encoding_confidence
    11451376                ) {
    11461377                    $this->bail( 'Cannot yet process META tags with http-equiv Content-Type to determine encoding.' );
     
    11941425            /*
    11951426             * > An end tag whose tag name is one of: "body", "html", "br"
     1427             *
     1428             * BR tags are always reported by the Tag Processor as opening tags.
    11961429             */
    11971430            case '-BODY':
    11981431            case '-HTML':
    1199             case '-BR':
    12001432                /*
    12011433                 * > Act as described in the "anything else" entry below.
     
    12741506     */
    12751507    private function step_in_head_noscript(): bool {
    1276         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_IN_HEAD_NOSCRIPT . ' state.' );
     1508        $token_name = $this->get_token_name();
     1509        $token_type = $this->get_token_type();
     1510        $is_closer  = parent::is_tag_closer();
     1511        $op_sigil   = '#tag' === $token_type ? ( $is_closer ? '-' : '+' ) : '';
     1512        $op         = "{$op_sigil}{$token_name}";
     1513
     1514        switch ( $op ) {
     1515            /*
     1516             * > A character token that is one of U+0009 CHARACTER TABULATION,
     1517             * > U+000A LINE FEED (LF), U+000C FORM FEED (FF),
     1518             * > U+000D CARRIAGE RETURN (CR), or U+0020 SPACE
     1519             *
     1520             * Parse error: ignore the token.
     1521             */
     1522            case '#text':
     1523                $text = $this->get_modifiable_text();
     1524                if ( strlen( $text ) === strspn( $text, " \t\n\f\r" ) ) {
     1525                    return $this->step_in_head();
     1526                }
     1527
     1528                goto in_head_noscript_anything_else;
     1529                break;
     1530
     1531            /*
     1532             * > A DOCTYPE token
     1533             */
     1534            case 'html':
     1535                // Parse error: ignore the token.
     1536                return $this->step();
     1537
     1538            /*
     1539             * > A start tag whose tag name is "html"
     1540             */
     1541            case '+HTML':
     1542                return $this->step_in_body();
     1543
     1544            /*
     1545             * > An end tag whose tag name is "noscript"
     1546             */
     1547            case '-NOSCRIPT':
     1548                $this->state->stack_of_open_elements->pop();
     1549                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_HEAD;
     1550                return true;
     1551
     1552            /*
     1553             * > A comment token
     1554             * >
     1555             * > A start tag whose tag name is one of: "basefont", "bgsound",
     1556             * > "link", "meta", "noframes", "style"
     1557             */
     1558            case '#comment':
     1559            case '#funky-comment':
     1560            case '#presumptuous-tag':
     1561            case '+BASEFONT':
     1562            case '+BGSOUND':
     1563            case '+LINK':
     1564            case '+META':
     1565            case '+NOFRAMES':
     1566            case '+STYLE':
     1567                return $this->step_in_head();
     1568
     1569            /*
     1570             * > An end tag whose tag name is "br"
     1571             *
     1572             * This should never happen, as the Tag Processor prevents showing a BR closing tag.
     1573             */
     1574        }
     1575
     1576        /*
     1577         * > A start tag whose tag name is one of: "head", "noscript"
     1578         * > Any other end tag
     1579         */
     1580        if ( '+HEAD' === $op || '+NOSCRIPT' === $op || $is_closer ) {
     1581            // Parse error: ignore the token.
     1582            return $this->step();
     1583        }
     1584
     1585        /*
     1586         * > Anything else
     1587         *
     1588         * Anything here is a parse error.
     1589         */
     1590        in_head_noscript_anything_else:
     1591        $this->state->stack_of_open_elements->pop();
     1592        $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_HEAD;
     1593        return $this->step( self::REPROCESS_CURRENT_NODE );
    12771594    }
    12781595
     
    12931610     */
    12941611    private function step_after_head(): bool {
    1295         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_AFTER_HEAD . ' state.' );
     1612        $token_name = $this->get_token_name();
     1613        $token_type = $this->get_token_type();
     1614        $is_closer  = parent::is_tag_closer();
     1615        $op_sigil   = '#tag' === $token_type ? ( $is_closer ? '-' : '+' ) : '';
     1616        $op         = "{$op_sigil}{$token_name}";
     1617
     1618        switch ( $op ) {
     1619            /*
     1620             * > A character token that is one of U+0009 CHARACTER TABULATION,
     1621             * > U+000A LINE FEED (LF), U+000C FORM FEED (FF),
     1622             * > U+000D CARRIAGE RETURN (CR), or U+0020 SPACE
     1623             */
     1624            case '#text':
     1625                $text = $this->get_modifiable_text();
     1626                if ( strlen( $text ) === strspn( $text, " \t\n\f\r" ) ) {
     1627                    // Insert the character.
     1628                    $this->insert_html_element( $this->state->current_token );
     1629                    return true;
     1630                }
     1631                goto after_head_anything_else;
     1632                break;
     1633
     1634            /*
     1635             * > A comment token
     1636             */
     1637            case '#comment':
     1638            case '#funky-comment':
     1639            case '#presumptuous-tag':
     1640                $this->insert_html_element( $this->state->current_token );
     1641                return true;
     1642
     1643            /*
     1644             * > A DOCTYPE token
     1645             */
     1646            case 'html':
     1647                // Parse error: ignore the token.
     1648                return $this->step();
     1649
     1650            /*
     1651             * > A start tag whose tag name is "html"
     1652             */
     1653            case '+HTML':
     1654                return $this->step_in_body();
     1655
     1656            /*
     1657             * > A start tag whose tag name is "body"
     1658             */
     1659            case '+BODY':
     1660                $this->insert_html_element( $this->state->current_token );
     1661                $this->state->frameset_ok    = false;
     1662                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_BODY;
     1663                return true;
     1664
     1665            /*
     1666             * > A start tag whose tag name is "frameset"
     1667             */
     1668            case '+FRAMESET':
     1669                $this->insert_html_element( $this->state->current_token );
     1670                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_FRAMESET;
     1671                return true;
     1672
     1673            /*
     1674             * > A start tag whose tag name is one of: "base", "basefont", "bgsound",
     1675             * > "link", "meta", "noframes", "script", "style", "template", "title"
     1676             *
     1677             * Anything here is a parse error.
     1678             */
     1679            case '+BASE':
     1680            case '+BASEFONT':
     1681            case '+BGSOUND':
     1682            case '+LINK':
     1683            case '+META':
     1684            case '+NOFRAMES':
     1685            case '+SCRIPT':
     1686            case '+STYLE':
     1687            case '+TEMPLATE':
     1688            case '+TITLE':
     1689                /*
     1690                 * > Push the node pointed to by the head element pointer onto the stack of open elements.
     1691                 * > Process the token using the rules for the "in head" insertion mode.
     1692                 * > Remove the node pointed to by the head element pointer from the stack of open elements. (It might not be the current node at this point.)
     1693                 */
     1694                $this->bail( 'Cannot process elements after HEAD which reopen the HEAD element.' );
     1695                /*
     1696                 * Do not leave this break in when adding support; it's here to prevent
     1697                 * WPCS from getting confused at the switch structure without a return,
     1698                 * because it doesn't know that `bail()` always throws.
     1699                 */
     1700                break;
     1701
     1702            /*
     1703             * > An end tag whose tag name is "template"
     1704             */
     1705            case '-TEMPLATE':
     1706                return $this->step_in_head();
     1707
     1708            /*
     1709             * > An end tag whose tag name is one of: "body", "html", "br"
     1710             *
     1711             * Closing BR tags are always reported by the Tag Processor as opening tags.
     1712             */
     1713            case '-BODY':
     1714            case '-HTML':
     1715                /*
     1716                 * > Act as described in the "anything else" entry below.
     1717                 */
     1718                goto after_head_anything_else;
     1719                break;
     1720        }
     1721
     1722        /*
     1723         * > A start tag whose tag name is "head"
     1724         * > Any other end tag
     1725         */
     1726        if ( '+HEAD' === $op || $is_closer ) {
     1727            // Parse error: ignore the token.
     1728            return $this->step();
     1729        }
     1730
     1731        /*
     1732         * > Anything else
     1733         * > Insert an HTML element for a "body" start tag token with no attributes.
     1734         */
     1735        after_head_anything_else:
     1736        $this->insert_virtual_node( 'BODY' );
     1737        $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_BODY;
     1738        return $this->step( self::REPROCESS_CURRENT_NODE );
    12961739    }
    12971740
     
    44704913     * @param string|null $bookmark_name Optional. Name to give bookmark for created virtual node.
    44714914     *                                   Defaults to auto-creating a bookmark name.
    4472      */
    4473     private function insert_virtual_node( $token_name, $bookmark_name = null ): void {
     4915     * @return WP_HTML_Token Newly-created virtual token.
     4916     */
     4917    private function insert_virtual_node( $token_name, $bookmark_name = null ): WP_HTML_Token {
    44744918        $here = $this->bookmarks[ $this->state->current_token->bookmark_name ];
    44754919        $name = $bookmark_name ?? $this->bookmark_token();
     
    44774921        $this->bookmarks[ $name ] = new WP_HTML_Span( $here->start, 0 );
    44784922
    4479         $this->insert_html_element( new WP_HTML_Token( $name, $token_name, false ) );
     4923        $token = new WP_HTML_Token( $name, $token_name, false );
     4924        $this->insert_html_element( $token );
     4925        return $token;
    44804926    }
    44814927
     
    46345080    }
    46355081
     5082    /**
     5083     * Gets an encoding from a given string.
     5084     *
     5085     * This is an algorithm defined in the WHAT-WG specification.
     5086     *
     5087     * Example:
     5088     *
     5089     *     'UTF-8' === self::get_encoding( 'utf8' );
     5090     *     'UTF-8' === self::get_encoding( "  \tUTF-8 " );
     5091     *     null    === self::get_encoding( 'UTF-7' );
     5092     *     null    === self::get_encoding( 'utf8; charset=' );
     5093     *
     5094     * @see https://encoding.spec.whatwg.org/#concept-encoding-get
     5095     *
     5096     * @todo As this parser only supports UTF-8, only the UTF-8
     5097     *       encodings are detected. Add more as desired, but the
     5098     *       parser will bail on non-UTF-8 encodings.
     5099     *
     5100     * @since 6.7.0
     5101     *
     5102     * @param string $label A string which may specify a known encoding.
     5103     * @return string|null Known encoding if matched, otherwise null.
     5104     */
     5105    protected static function get_encoding( string $label ): ?string {
     5106        /*
     5107         * > Remove any leading and trailing ASCII whitespace from label.
     5108         */
     5109        $label = trim( $label, " \t\f\r\n" );
     5110
     5111        /*
     5112         * > If label is an ASCII case-insensitive match for any of the labels listed in the
     5113         * > table below, then return the corresponding encoding; otherwise return failure.
     5114         */
     5115        switch ( strtolower( $label ) ) {
     5116            case 'unicode-1-1-utf-8':
     5117            case 'unicode11utf8':
     5118            case 'unicode20utf8':
     5119            case 'utf-8':
     5120            case 'utf8':
     5121            case 'x-unicode20utf8':
     5122                return 'UTF-8';
     5123
     5124            default:
     5125                return null;
     5126        }
     5127    }
     5128
    46365129    /*
    46375130     * Constants that would pollute the top of the class if they were found there.
Note: See TracChangeset for help on using the changeset viewer.