Make WordPress Core


Ignore:
Timestamp:
07/24/2024 06:39:46 PM (11 months ago)
Author:
dmsnell
Message:

HTML API: Add TABLE support in HTML Processor.

As part of work to add more spec support to the HTML API, this patch adds
support for various table-related insertion modes. This includes support
for tables, table rows, table cells, table column groups, etc...

Developed in https://github.com/wordpress/wordpress-develop/pull/6040
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

    r58781 r58806  
    17871787             */
    17881788            case '+TABLE':
     1789                /*
     1790                 * > If the Document is not set to quirks mode, and the stack of open elements
     1791                 * > has a p element in button scope, then close a p element.
     1792                 */
    17891793                if (
    17901794                    WP_HTML_Processor_State::QUIRKS_MODE !== $this->state->document_mode &&
     
    21182122     * logic for the generalized WP_HTML_Processor::step() function.
    21192123     *
    2120      * @since 6.7.0 Stub implementation.
     2124     * @since 6.7.0
    21212125     *
    21222126     * @throws WP_HTML_Unsupported_Exception When encountering unsupported HTML input.
     
    21282132     */
    21292133    private function step_in_table(): bool {
    2130         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE . ' state.' );
     2134        $token_name = $this->get_token_name();
     2135        $token_type = $this->get_token_type();
     2136        $op_sigil   = '#tag' === $token_type ? ( parent::is_tag_closer() ? '-' : '+' ) : '';
     2137        $op         = "{$op_sigil}{$token_name}";
     2138
     2139        switch ( $op ) {
     2140            /*
     2141             * > A character token, if the current node is table,
     2142             * > tbody, template, tfoot, thead, or tr element
     2143             */
     2144            case '#text':
     2145                $current_node      = $this->state->stack_of_open_elements->current_node();
     2146                $current_node_name = $current_node ? $current_node->node_name : null;
     2147                if (
     2148                    $current_node_name && (
     2149                        'TABLE' === $current_node_name ||
     2150                        'TBODY' === $current_node_name ||
     2151                        'TEMPLATE' === $current_node_name ||
     2152                        'TFOOT' === $current_node_name ||
     2153                        'THEAD' === $current_node_name ||
     2154                        'TR' === $current_node_name
     2155                    )
     2156                ) {
     2157                    $text = $this->get_modifiable_text();
     2158                    /*
     2159                     * If the text is empty after processing HTML entities and stripping
     2160                     * U+0000 NULL bytes then ignore the token.
     2161                     */
     2162                    if ( '' === $text ) {
     2163                        return $this->step();
     2164                    }
     2165
     2166                    /*
     2167                     * This follows the rules for "in table text" insertion mode.
     2168                     *
     2169                     * Whitespace-only text nodes are inserted in-place. Otherwise
     2170                     * foster parenting is enabled and the nodes would be
     2171                     * inserted out-of-place.
     2172                     *
     2173                     * > If any of the tokens in the pending table character tokens
     2174                     * > list are character tokens that are not ASCII whitespace,
     2175                     * > then this is a parse error: reprocess the character tokens
     2176                     * > in the pending table character tokens list using the rules
     2177                     * > given in the "anything else" entry in the "in table"
     2178                     * > insertion mode.
     2179                     * >
     2180                     * > Otherwise, insert the characters given by the pending table
     2181                     * > character tokens list.
     2182                     *
     2183                     * @see https://html.spec.whatwg.org/#parsing-main-intabletext
     2184                     */
     2185                    if ( strlen( $text ) === strspn( $text, " \t\f\r\n" ) ) {
     2186                        $this->insert_html_element( $this->state->current_token );
     2187                        return true;
     2188                    }
     2189
     2190                    // Non-whitespace would trigger fostering, unsupported at this time.
     2191                    $this->bail( 'Foster parenting is not supported.' );
     2192                    break;
     2193                }
     2194                break;
     2195
     2196            /*
     2197             * > A comment token
     2198             */
     2199            case '#comment':
     2200            case '#funky-comment':
     2201            case '#presumptuous-tag':
     2202                $this->insert_html_element( $this->state->current_token );
     2203                return true;
     2204
     2205            /*
     2206             * > A DOCTYPE token
     2207             */
     2208            case 'html':
     2209                // Parse error: ignore the token.
     2210                return $this->step();
     2211
     2212            /*
     2213             * > A start tag whose tag name is "caption"
     2214             */
     2215            case '+CAPTION':
     2216                $this->state->stack_of_open_elements->clear_to_table_context();
     2217                $this->state->active_formatting_elements->insert_marker();
     2218                $this->insert_html_element( $this->state->current_token );
     2219                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_CAPTION;
     2220                return true;
     2221
     2222            /*
     2223             * > A start tag whose tag name is "colgroup"
     2224             */
     2225            case '+COLGROUP':
     2226                $this->state->stack_of_open_elements->clear_to_table_context();
     2227                $this->insert_html_element( $this->state->current_token );
     2228                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_COLUMN_GROUP;
     2229                return true;
     2230
     2231            /*
     2232             * > A start tag whose tag name is "col"
     2233             */
     2234            case '+COL':
     2235                $this->state->stack_of_open_elements->clear_to_table_context();
     2236
     2237                /*
     2238                 * > Insert an HTML element for a "colgroup" start tag token with no attributes,
     2239                 * > then switch the insertion mode to "in column group".
     2240                 */
     2241                $this->insert_virtual_node( 'COLGROUP' );
     2242                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_COLUMN_GROUP;
     2243                return $this->step( self::REPROCESS_CURRENT_NODE );
     2244
     2245            /*
     2246             * > A start tag whose tag name is one of: "tbody", "tfoot", "thead"
     2247             */
     2248            case '+TBODY':
     2249            case '+TFOOT':
     2250            case '+THEAD':
     2251                $this->state->stack_of_open_elements->clear_to_table_context();
     2252                $this->insert_html_element( $this->state->current_token );
     2253                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_BODY;
     2254                return true;
     2255
     2256            /*
     2257             * > A start tag whose tag name is one of: "td", "th", "tr"
     2258             */
     2259            case '+TD':
     2260            case '+TH':
     2261            case '+TR':
     2262                $this->state->stack_of_open_elements->clear_to_table_context();
     2263                /*
     2264                 * > Insert an HTML element for a "tbody" start tag token with no attributes,
     2265                 * > then switch the insertion mode to "in table body".
     2266                 */
     2267                $this->insert_virtual_node( 'TBODY' );
     2268                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_BODY;
     2269                return $this->step( self::REPROCESS_CURRENT_NODE );
     2270
     2271            /*
     2272             * > A start tag whose tag name is "table"
     2273             *
     2274             * This tag in the IN TABLE insertion mode is a parse error.
     2275             */
     2276            case '+TABLE':
     2277                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( 'TABLE' ) ) {
     2278                    return $this->step();
     2279                }
     2280
     2281                $this->state->stack_of_open_elements->pop_until( 'TABLE' );
     2282                $this->reset_insertion_mode();
     2283                return $this->step( self::REPROCESS_CURRENT_NODE );
     2284
     2285            /*
     2286             * > An end tag whose tag name is "table"
     2287             */
     2288            case '-TABLE':
     2289                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( 'TABLE' ) ) {
     2290                    // @todo Indicate a parse error once it's possible.
     2291                    return $this->step();
     2292                }
     2293
     2294                $this->state->stack_of_open_elements->pop_until( 'TABLE' );
     2295                $this->reset_insertion_mode();
     2296                return true;
     2297
     2298            /*
     2299             * > An end tag whose tag name is one of: "body", "caption", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr"
     2300             */
     2301            case '-BODY':
     2302            case '-CAPTION':
     2303            case '-COL':
     2304            case '-COLGROUP':
     2305            case '-HTML':
     2306            case '-TBODY':
     2307            case '-TD':
     2308            case '-TFOOT':
     2309            case '-TH':
     2310            case '-THEAD':
     2311            case '-TR':
     2312                // Parse error: ignore the token.
     2313                return $this->step();
     2314
     2315            /*
     2316             * > A start tag whose tag name is one of: "style", "script", "template"
     2317             * > An end tag whose tag name is "template"
     2318             */
     2319            case '+STYLE':
     2320            case '+SCRIPT':
     2321            case '+TEMPLATE':
     2322            case '-TEMPLATE':
     2323                /*
     2324                 * > Process the token using the rules for the "in head" insertion mode.
     2325                 */
     2326                return $this->step_in_head();
     2327
     2328            /*
     2329             * > A start tag whose tag name is "input"
     2330             *
     2331             * > If the token does not have an attribute with the name "type", or if it does, but
     2332             * > that attribute's value is not an ASCII case-insensitive match for the string
     2333             * > "hidden", then: act as described in the "anything else" entry below.
     2334             */
     2335            case '+INPUT':
     2336                $type_attribute = $this->get_attribute( 'type' );
     2337                if ( ! is_string( $type_attribute ) || 'hidden' !== strtolower( $type_attribute ) ) {
     2338                    goto anything_else;
     2339                }
     2340                // @todo Indicate a parse error once it's possible.
     2341                $this->insert_html_element( $this->state->current_token );
     2342                return true;
     2343
     2344            /*
     2345             * > A start tag whose tag name is "form"
     2346             *
     2347             * This tag in the IN TABLE insertion mode is a parse error.
     2348             */
     2349            case '+FORM':
     2350                if (
     2351                    $this->state->stack_of_open_elements->has_element_in_scope( 'TEMPLATE' ) ||
     2352                    isset( $this->state->form_element )
     2353                ) {
     2354                    return $this->step();
     2355                }
     2356
     2357                // This FORM is special because it immediately closes and cannot have other children.
     2358                $this->insert_html_element( $this->state->current_token );
     2359                $this->state->form_element = $this->state->current_token;
     2360                $this->state->stack_of_open_elements->pop();
     2361                return true;
     2362        }
     2363
     2364        /*
     2365         * > Anything else
     2366         * > Parse error. Enable foster parenting, process the token using the rules for the
     2367         * > "in body" insertion mode, and then disable foster parenting.
     2368         *
     2369         * @todo Indicate a parse error once it's possible.
     2370         */
     2371        anything_else:
     2372        $this->bail( 'Foster parenting is not supported.' );
    21312373    }
    21322374
     
    21942436     * logic for the generalized WP_HTML_Processor::step() function.
    21952437     *
    2196      * @since 6.7.0 Stub implementation.
     2438     * @since 6.7.0
    21972439     *
    21982440     * @throws WP_HTML_Unsupported_Exception When encountering unsupported HTML input.
     
    22042446     */
    22052447    private function step_in_table_body(): bool {
    2206         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_BODY . ' state.' );
     2448        $tag_name = $this->get_tag();
     2449        $op_sigil = $this->is_tag_closer() ? '-' : '+';
     2450        $op       = "{$op_sigil}{$tag_name}";
     2451
     2452        switch ( $op ) {
     2453            /*
     2454             * > A start tag whose tag name is "tr"
     2455             */
     2456            case '+TR':
     2457                $this->state->stack_of_open_elements->clear_to_table_body_context();
     2458                $this->insert_html_element( $this->state->current_token );
     2459                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_ROW;
     2460                return true;
     2461
     2462            /*
     2463             * > A start tag whose tag name is one of: "th", "td"
     2464             */
     2465            case '+TH':
     2466            case '+TD':
     2467                // @todo Indicate a parse error once it's possible.
     2468                $this->state->stack_of_open_elements->clear_to_table_body_context();
     2469                $this->insert_virtual_node( 'TR' );
     2470                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_ROW;
     2471                return $this->step( self::REPROCESS_CURRENT_NODE );
     2472
     2473            /*
     2474             * > An end tag whose tag name is one of: "tbody", "tfoot", "thead"
     2475             */
     2476            case '-TBODY':
     2477            case '-TFOOT':
     2478            case '-THEAD':
     2479                /*
     2480                 * @todo This needs to check if the element in scope is an HTML element, meaning that
     2481                 *       when SVG and MathML support is added, this needs to differentiate between an
     2482                 *       HTML element of the given name, such as `<center>`, and a foreign element of
     2483                 *       the same given name.
     2484                 */
     2485                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( $tag_name ) ) {
     2486                    // Parse error: ignore the token.
     2487                    return $this->step();
     2488                }
     2489
     2490                $this->state->stack_of_open_elements->clear_to_table_body_context();
     2491                $this->state->stack_of_open_elements->pop();
     2492                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE;
     2493                return true;
     2494
     2495            /*
     2496             * > A start tag whose tag name is one of: "caption", "col", "colgroup", "tbody", "tfoot", "thead"
     2497             * > An end tag whose tag name is "table"
     2498             */
     2499            case '+CAPTION':
     2500            case '+COL':
     2501            case '+COLGROUP':
     2502            case '+TBODY':
     2503            case '+TFOOT':
     2504            case '+THEAD':
     2505            case '-TABLE':
     2506                if (
     2507                    ! $this->state->stack_of_open_elements->has_element_in_table_scope( 'TBODY' ) &&
     2508                    ! $this->state->stack_of_open_elements->has_element_in_table_scope( 'THEAD' ) &&
     2509                    ! $this->state->stack_of_open_elements->has_element_in_table_scope( 'TFOOT' )
     2510                ) {
     2511                    // Parse error: ignore the token.
     2512                    return $this->step();
     2513                }
     2514                $this->state->stack_of_open_elements->clear_to_table_body_context();
     2515                $this->state->stack_of_open_elements->pop();
     2516                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE;
     2517                return $this->step( self::REPROCESS_CURRENT_NODE );
     2518
     2519            /*
     2520             * > An end tag whose tag name is one of: "body", "caption", "col", "colgroup", "html", "td", "th", "tr"
     2521             */
     2522            case '-BODY':
     2523            case '-CAPTION':
     2524            case '-COL':
     2525            case '-COLGROUP':
     2526            case '-HTML':
     2527            case '-TD':
     2528            case '-TH':
     2529            case '-TR':
     2530                // Parse error: ignore the token.
     2531                return $this->step();
     2532        }
     2533
     2534        /*
     2535         * > Anything else
     2536         * > Process the token using the rules for the "in table" insertion mode.
     2537         */
     2538        return $this->step_in_table();
    22072539    }
    22082540
     
    22132545     * logic for the generalized WP_HTML_Processor::step() function.
    22142546     *
    2215      * @since 6.7.0 Stub implementation.
     2547     * @since 6.7.0
    22162548     *
    22172549     * @throws WP_HTML_Unsupported_Exception When encountering unsupported HTML input.
     
    22232555     */
    22242556    private function step_in_row(): bool {
    2225         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_IN_ROW . ' state.' );
     2557        $tag_name = $this->get_tag();
     2558        $op_sigil = $this->is_tag_closer() ? '-' : '+';
     2559        $op       = "{$op_sigil}{$tag_name}";
     2560
     2561        switch ( $op ) {
     2562            /*
     2563             * > A start tag whose tag name is one of: "th", "td"
     2564             */
     2565            case '+TH':
     2566            case '+TD':
     2567                $this->state->stack_of_open_elements->clear_to_table_row_context();
     2568                $this->insert_html_element( $this->state->current_token );
     2569                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_CELL;
     2570                $this->state->active_formatting_elements->insert_marker();
     2571                return true;
     2572
     2573            /*
     2574             * > An end tag whose tag name is "tr"
     2575             */
     2576            case '-TR':
     2577                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( 'TR' ) ) {
     2578                    // Parse error: ignore the token.
     2579                    return $this->step();
     2580                }
     2581
     2582                $this->state->stack_of_open_elements->clear_to_table_row_context();
     2583                $this->state->stack_of_open_elements->pop();
     2584                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_BODY;
     2585                return true;
     2586
     2587            /*
     2588             * > A start tag whose tag name is one of: "caption", "col", "colgroup", "tbody", "tfoot", "thead", "tr"
     2589             * > An end tag whose tag name is "table"
     2590             */
     2591            case '+CAPTION':
     2592            case '+COL':
     2593            case '+COLGROUP':
     2594            case '+TBODY':
     2595            case '+TFOOT':
     2596            case '+THEAD':
     2597            case '+TR':
     2598            case '-TABLE':
     2599                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( 'TR' ) ) {
     2600                    // Parse error: ignore the token.
     2601                    return $this->step();
     2602                }
     2603
     2604                $this->state->stack_of_open_elements->clear_to_table_row_context();
     2605                $this->state->stack_of_open_elements->pop();
     2606                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_BODY;
     2607                return $this->step( self::REPROCESS_CURRENT_NODE );
     2608
     2609            /*
     2610             * > An end tag whose tag name is one of: "tbody", "tfoot", "thead"
     2611             */
     2612            case '-TBODY':
     2613            case '-TFOOT':
     2614            case '-THEAD':
     2615                /*
     2616                 * @todo This needs to check if the element in scope is an HTML element, meaning that
     2617                 *       when SVG and MathML support is added, this needs to differentiate between an
     2618                 *       HTML element of the given name, such as `<center>`, and a foreign element of
     2619                 *       the same given name.
     2620                 */
     2621                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( $tag_name ) ) {
     2622                    // Parse error: ignore the token.
     2623                    return $this->step();
     2624                }
     2625
     2626                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( 'TR' ) ) {
     2627                    // Ignore the token.
     2628                    return $this->step();
     2629                }
     2630
     2631                $this->state->stack_of_open_elements->clear_to_table_row_context();
     2632                $this->state->stack_of_open_elements->pop();
     2633                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_BODY;
     2634                return $this->step( self::REPROCESS_CURRENT_NODE );
     2635
     2636            /*
     2637             * > An end tag whose tag name is one of: "body", "caption", "col", "colgroup", "html", "td", "th"
     2638             */
     2639            case '-BODY':
     2640            case '-CAPTION':
     2641            case '-COL':
     2642            case '-COLGROUP':
     2643            case '-HTML':
     2644            case '-TD':
     2645            case '-TH':
     2646                // Parse error: ignore the token.
     2647                return $this->step();
     2648        }
     2649
     2650        /*
     2651         * > Anything else
     2652         * >   Process the token using the rules for the "in table" insertion mode.
     2653         */
     2654        return $this->step_in_table();
    22262655    }
    22272656
     
    22322661     * logic for the generalized WP_HTML_Processor::step() function.
    22332662     *
    2234      * @since 6.7.0 Stub implementation.
     2663     * @since 6.7.0
    22352664     *
    22362665     * @throws WP_HTML_Unsupported_Exception When encountering unsupported HTML input.
     
    22422671     */
    22432672    private function step_in_cell(): bool {
    2244         $this->bail( 'No support for parsing in the ' . WP_HTML_Processor_State::INSERTION_MODE_IN_CELL . ' state.' );
     2673        $tag_name = $this->get_tag();
     2674        $op_sigil = $this->is_tag_closer() ? '-' : '+';
     2675        $op       = "{$op_sigil}{$tag_name}";
     2676
     2677        switch ( $op ) {
     2678            /*
     2679             * > An end tag whose tag name is one of: "td", "th"
     2680             */
     2681            case '-TD':
     2682            case '-TH':
     2683                /*
     2684                 * @todo This needs to check if the element in scope is an HTML element, meaning that
     2685                 *       when SVG and MathML support is added, this needs to differentiate between an
     2686                 *       HTML element of the given name, such as `<center>`, and a foreign element of
     2687                 *       the same given name.
     2688                 */
     2689                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( $tag_name ) ) {
     2690                    // Parse error: ignore the token.
     2691                    return $this->step();
     2692                }
     2693
     2694                $this->generate_implied_end_tags();
     2695
     2696                /*
     2697                 * @todo This needs to check if the current node is an HTML element, meaning that
     2698                 *       when SVG and MathML support is added, this needs to differentiate between an
     2699                 *       HTML element of the given name, such as `<center>`, and a foreign element of
     2700                 *       the same given name.
     2701                 */
     2702                if ( ! $this->state->stack_of_open_elements->current_node_is( $tag_name ) ) {
     2703                    // @todo Indicate a parse error once it's possible.
     2704                }
     2705
     2706                $this->state->stack_of_open_elements->pop_until( $tag_name );
     2707                $this->state->active_formatting_elements->clear_up_to_last_marker();
     2708                $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_ROW;
     2709                return true;
     2710
     2711            /*
     2712             * > A start tag whose tag name is one of: "caption", "col", "colgroup", "tbody", "td",
     2713             * > "tfoot", "th", "thead", "tr"
     2714             */
     2715            case '+CAPTION':
     2716            case '+COL':
     2717            case '+COLGROUP':
     2718            case '+TBODY':
     2719            case '+TD':
     2720            case '+TFOOT':
     2721            case '+TH':
     2722            case '+THEAD':
     2723            case '+TR':
     2724                /*
     2725                 * > Assert: The stack of open elements has a td or th element in table scope.
     2726                 *
     2727                 * Nothing to do here, except to verify in tests that this never appears.
     2728                 */
     2729
     2730                $this->close_cell();
     2731                return $this->step( self::REPROCESS_CURRENT_NODE );
     2732
     2733            /*
     2734             * > An end tag whose tag name is one of: "body", "caption", "col", "colgroup", "html"
     2735             */
     2736            case '-BODY':
     2737            case '-CAPTION':
     2738            case '-COL':
     2739            case '-COLGROUP':
     2740            case '-HTML':
     2741                // Parse error: ignore the token.
     2742                return $this->step();
     2743
     2744            /*
     2745             * > An end tag whose tag name is one of: "table", "tbody", "tfoot", "thead", "tr"
     2746             */
     2747            case '-TABLE':
     2748            case '-TBODY':
     2749            case '-TFOOT':
     2750            case '-THEAD':
     2751            case '-TR':
     2752                /*
     2753                 * @todo This needs to check if the element in scope is an HTML element, meaning that
     2754                 *       when SVG and MathML support is added, this needs to differentiate between an
     2755                 *       HTML element of the given name, such as `<center>`, and a foreign element of
     2756                 *       the same given name.
     2757                 */
     2758                if ( ! $this->state->stack_of_open_elements->has_element_in_table_scope( $tag_name ) ) {
     2759                    // Parse error: ignore the token.
     2760                    return $this->step();
     2761                }
     2762                $this->close_cell();
     2763                return $this->step( self::REPROCESS_CURRENT_NODE );
     2764        }
     2765
     2766        /*
     2767         * > Anything else
     2768         * >   Process the token using the rules for the "in body" insertion mode.
     2769         */
     2770        return $this->step_in_body();
    22452771    }
    22462772
     
    35784104
    35794105    /**
     4106     * Runs the "close the cell" algorithm.
     4107     *
     4108     * > Where the steps above say to close the cell, they mean to run the following algorithm:
     4109     * >   1. Generate implied end tags.
     4110     * >   2. If the current node is not now a td element or a th element, then this is a parse error.
     4111     * >   3. Pop elements from the stack of open elements stack until a td element or a th element has been popped from the stack.
     4112     * >   4. Clear the list of active formatting elements up to the last marker.
     4113     * >   5. Switch the insertion mode to "in row".
     4114     *
     4115     * @see https://html.spec.whatwg.org/multipage/parsing.html#close-the-cell
     4116     *
     4117     * @since 6.7.0
     4118     */
     4119    private function close_cell(): void {
     4120        $this->generate_implied_end_tags();
     4121        // @todo Parse error if the current node is a "td" or "th" element.
     4122        foreach ( $this->state->stack_of_open_elements->walk_up() as $element ) {
     4123            $this->state->stack_of_open_elements->pop();
     4124            if ( 'TD' === $element->node_name || 'TH' === $element->node_name ) {
     4125                break;
     4126            }
     4127        }
     4128        $this->state->active_formatting_elements->clear_up_to_last_marker();
     4129        $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_ROW;
     4130    }
     4131
     4132    /**
    35804133     * Inserts an HTML element on the stack of open elements.
    35814134     *
     
    35884141    private function insert_html_element( WP_HTML_Token $token ): void {
    35894142        $this->state->stack_of_open_elements->push( $token );
     4143    }
     4144
     4145    /**
     4146     * Inserts a virtual element on the stack of open elements.
     4147     *
     4148     * @since 6.7.0
     4149     *
     4150     * @param string      $token_name    Name of token to create and insert into the stack of open elements.
     4151     * @param string|null $bookmark_name Optional. Name to give bookmark for created virtual node.
     4152     *                                   Defaults to auto-creating a bookmark name.
     4153     */
     4154    private function insert_virtual_node( $token_name, $bookmark_name = null ): void {
     4155        $here = $this->bookmarks[ $this->state->current_token->bookmark_name ];
     4156        $name = $bookmark_name ?? $this->bookmark_token();
     4157
     4158        $this->bookmarks[ $name ] = new WP_HTML_Span( $here->start, 0 );
     4159
     4160        $this->insert_html_element( new WP_HTML_Token( $name, $token_name, false ) );
    35904161    }
    35914162
Note: See TracChangeset for help on using the changeset viewer.