<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[58677] trunk: HTML API: Support SELECT insertion  mode.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/58677">58677</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/58677","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>dmsnell</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2024-07-05 00:50:19 +0000 (Fri, 05 Jul 2024)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>HTML API: Support SELECT insertion  mode.

As part of work to add more spec support to the HTML API, this patch adds
support for the SELECT, OPTION, and OPTGROUP elements, including the
requisite support for the IN SELECT insertion mode.

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

Props dmsnell, jonsurrell.
See <a href="https://core.trac.wordpress.org/ticket/61576">#61576</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludeshtmlapiclasswphtmlopenelementsphp">trunk/src/wp-includes/html-api/class-wp-html-open-elements.php</a></li>
<li><a href="#trunksrcwpincludeshtmlapiclasswphtmlprocessorphp">trunk/src/wp-includes/html-api/class-wp-html-processor.php</a></li>
<li><a href="#trunktestsphpunittestshtmlapiwpHtmlProcessorphp">trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php</a></li>
<li><a href="#trunktestsphpunittestshtmlapiwpHtmlProcessorBreadcrumbsphp">trunk/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php</a></li>
<li><a href="#trunktestsphpunittestshtmlapiwpHtmlSupportRequiredHtmlProcessorphp">trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredHtmlProcessor.php</a></li>
<li><a href="#trunktestsphpunittestshtmlapiwpHtmlSupportRequiredOpenElementsphp">trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredOpenElements.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludeshtmlapiclasswphtmlopenelementsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/html-api/class-wp-html-open-elements.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/html-api/class-wp-html-open-elements.php    2024-07-04 23:14:44 UTC (rev 58676)
+++ trunk/src/wp-includes/html-api/class-wp-html-open-elements.php      2024-07-05 00:50:19 UTC (rev 58677)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -189,11 +189,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Returns whether an element is in a specific scope.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * ## HTML Support
-        *
-        * This function skips checking for the termination list because there
-        * are no supported elements which appear in the termination list.
-        *
</del><span class="cx" style="display: block; padding: 0 10px">          * @since 6.4.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @see https://html.spec.whatwg.org/#has-an-element-in-the-specific-scope
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -311,19 +306,38 @@
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Returns whether a particular element is in select scope.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @since 6.4.0
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * This test differs from the others like it, in that its rules are inverted.
+        * Instead of arriving at a match when one of any tag in a termination group
+        * is reached, this one terminates if any other tag is reached.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * > The stack of open elements is said to have a particular element in select scope when it has
+        * > that element in the specific scope consisting of all element types except the following:
+        * >   - optgroup in the HTML namespace
+        * >   - option in the HTML namespace
+        *
+        * @since 6.4.0 Stub implementation (throws).
+        * @since 6.7.0 Full implementation.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @see https://html.spec.whatwg.org/#has-an-element-in-select-scope
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @throws WP_HTML_Unsupported_Exception Always until this function is implemented.
-        *
</del><span class="cx" style="display: block; padding: 0 10px">          * @param string $tag_name Name of tag to check.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @return bool Whether given element is in scope.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @return bool Whether the given element is in SELECT scope.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function has_element_in_select_scope( $tag_name ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                throw new WP_HTML_Unsupported_Exception( 'Cannot process elements depending on select scope.' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         foreach ( $this->walk_up() as $node ) {
+                       if ( $node->node_name === $tag_name ) {
+                               return true;
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return false; // The linter requires this unreachable code until the function is implemented and can return.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if (
+                               'OPTION' !== $node->node_name &&
+                               'OPTGROUP' !== $node->node_name
+                       ) {
+                               return false;
+                       }
+               }
+
+               return false;
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="trunksrcwpincludeshtmlapiclasswphtmlprocessorphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/html-api/class-wp-html-processor.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/html-api/class-wp-html-processor.php        2024-07-04 23:14:44 UTC (rev 58676)
+++ trunk/src/wp-includes/html-api/class-wp-html-processor.php  2024-07-05 00:50:19 UTC (rev 58677)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -101,7 +101,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  *  - Containers: ADDRESS, BLOCKQUOTE, DETAILS, DIALOG, DIV, FOOTER, HEADER, MAIN, MENU, SPAN, SUMMARY.
</span><span class="cx" style="display: block; padding: 0 10px">  *  - Custom elements: All custom elements are supported. :)
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- *  - Form elements: BUTTON, DATALIST, FIELDSET, INPUT, LABEL, LEGEND, METER, PROGRESS, SEARCH.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *  - Form elements: BUTTON, DATALIST, FIELDSET, INPUT, LABEL, LEGEND, METER, OPTGROUP, OPTION, PROGRESS, SEARCH, SELECT.
</ins><span class="cx" style="display: block; padding: 0 10px">  *  - Formatting elements: B, BIG, CODE, EM, FONT, I, PRE, SMALL, STRIKE, STRONG, TT, U, WBR.
</span><span class="cx" style="display: block; padding: 0 10px">  *  - Heading elements: H1, H2, H3, H4, H5, H6, HGROUP.
</span><span class="cx" style="display: block; padding: 0 10px">  *  - Links: A.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -757,6 +757,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                case WP_HTML_Processor_State::INSERTION_MODE_IN_BODY:
</span><span class="cx" style="display: block; padding: 0 10px">                                        return $this->step_in_body();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                case WP_HTML_Processor_State::INSERTION_MODE_IN_HEAD:
+                                       return $this->step_in_head();
+
+                               case WP_HTML_Processor_State::INSERTION_MODE_IN_SELECT:
+                                       return $this->step_in_select();
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 default:
</span><span class="cx" style="display: block; padding: 0 10px">                                        $this->last_error = self::ERROR_UNSUPPORTED;
</span><span class="cx" style="display: block; padding: 0 10px">                                        throw new WP_HTML_Unsupported_Exception( "No support for parsing in the '{$this->state->insertion_mode}' state." );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1336,6 +1342,48 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        case '+TRACK':
</span><span class="cx" style="display: block; padding: 0 10px">                                $this->insert_html_element( $this->state->current_token );
</span><span class="cx" style="display: block; padding: 0 10px">                                return true;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       /*
+                        * > A start tag whose tag name is "select"
+                        */
+                       case '+SELECT':
+                               $this->reconstruct_active_formatting_elements();
+                               $this->insert_html_element( $this->state->current_token );
+                               $this->state->frameset_ok = false;
+
+                               switch ( $this->state->insertion_mode ) {
+                                       /*
+                                        * > If the insertion mode is one of "in table", "in caption", "in table body", "in row",
+                                        * > or "in cell", then switch the insertion mode to "in select in table".
+                                        */
+                                       case WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE:
+                                       case WP_HTML_Processor_State::INSERTION_MODE_IN_CAPTION:
+                                       case WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_BODY:
+                                       case WP_HTML_Processor_State::INSERTION_MODE_IN_ROW:
+                                       case WP_HTML_Processor_State::INSERTION_MODE_IN_CELL:
+                                               $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_SELECT_IN_TABLE;
+                                               break;
+
+                                       /*
+                                        * > Otherwise, switch the insertion mode to "in select".
+                                        */
+                                       default:
+                                               $this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_SELECT;
+                                               break;
+                               }
+                               return true;
+
+                       /*
+                        * > A start tag whose tag name is one of: "optgroup", "option"
+                        */
+                       case '+OPTGROUP':
+                       case '+OPTION':
+                               if ( $this->state->stack_of_open_elements->current_node_is( 'OPTION' ) ) {
+                                       $this->state->stack_of_open_elements->pop();
+                               }
+                               $this->reconstruct_active_formatting_elements();
+                               $this->insert_html_element( $this->state->current_token );
+                               return true;
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /*
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1378,8 +1426,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'NOFRAMES':
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'NOSCRIPT':
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'OBJECT':
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        case 'OPTGROUP':
-                       case 'OPTION':
</del><span class="cx" style="display: block; padding: 0 10px">                         case 'PLAINTEXT':
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'RB':
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'RP':
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1387,7 +1433,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'RTC':
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'SARCASM':
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'SCRIPT':
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        case 'SELECT':
</del><span class="cx" style="display: block; padding: 0 10px">                         case 'STYLE':
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'SVG':
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'TABLE':
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1448,6 +1493,207 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Parses next element in the 'in head' insertion mode.
+        *
+        * This internal function performs the 'in head' insertion mode
+        * logic for the generalized WP_HTML_Processor::step() function.
+        *
+        * @since 6.7.0 Stub implementation.
+        *
+        * @throws WP_HTML_Unsupported_Exception When encountering unsupported HTML input.
+        *
+        * @see https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inhead
+        * @see WP_HTML_Processor::step
+        *
+        * @return bool Whether an element was found.
+        */
+       private function step_in_head() {
+               $this->last_error = self::ERROR_UNSUPPORTED;
+               throw new WP_HTML_Unsupported_Exception( "No support for parsing in the '{$this->state->insertion_mode}' state." );
+       }
+
+       /**
+        * Parses next element in the 'in select' insertion mode.
+        *
+        * This internal function performs the 'in select' insertion mode
+        * logic for the generalized WP_HTML_Processor::step() function.
+        *
+        * @since 6.7.0
+        *
+        * @throws WP_HTML_Unsupported_Exception When encountering unsupported HTML input.
+        *
+        * @see https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inselect
+        * @see WP_HTML_Processor::step
+        *
+        * @return bool Whether an element was found.
+        */
+       private function step_in_select() {
+               $token_name = $this->get_token_name();
+               $token_type = $this->get_token_type();
+               $op_sigil   = '#tag' === $token_type ? ( parent::is_tag_closer() ? '-' : '+' ) : '';
+               $op         = "{$op_sigil}{$token_name}";
+
+               switch ( $op ) {
+                       /*
+                        * > Any other character token
+                        */
+                       case '#text':
+                               $current_token = $this->bookmarks[ $this->state->current_token->bookmark_name ];
+
+                               /*
+                                * > A character token that is U+0000 NULL
+                                *
+                                * If a text node only comprises null bytes then it should be
+                                * entirely ignored and should not return to calling code.
+                                */
+                               if (
+                                       1 <= $current_token->length &&
+                                       "\x00" === $this->html[ $current_token->start ] &&
+                                       strspn( $this->html, "\x00", $current_token->start, $current_token->length ) === $current_token->length
+                               ) {
+                                       // Parse error: ignore the token.
+                                       return $this->step();
+                               }
+
+                               $this->insert_html_element( $this->state->current_token );
+                               return true;
+
+                       /*
+                        * > A comment token
+                        */
+                       case '#comment':
+                       case '#funky-comment':
+                       case '#presumptuous-tag':
+                               $this->insert_html_element( $this->state->current_token );
+                               return true;
+
+                       /*
+                        * > A DOCTYPE token
+                        */
+                       case 'html':
+                               // Parse error: ignore the token.
+                               return $this->step();
+
+                       /*
+                        * > A start tag whose tag name is "html"
+                        */
+                       case '+HTML':
+                               return $this->step_in_body();
+
+                       /*
+                        * > A start tag whose tag name is "option"
+                        */
+                       case '+OPTION':
+                               if ( $this->state->stack_of_open_elements->current_node_is( 'OPTION' ) ) {
+                                       $this->state->stack_of_open_elements->pop();
+                               }
+                               $this->insert_html_element( $this->state->current_token );
+                               return true;
+
+                       /*
+                        * > A start tag whose tag name is "optgroup"
+                        * > A start tag whose tag name is "hr"
+                        *
+                        * These rules are identical except for the treatment of the self-closing flag and
+                        * the subsequent pop of the HR void element, all of which is handled elsewhere in the processor.
+                        */
+                       case '+OPTGROUP':
+                       case '+HR':
+                               if ( $this->state->stack_of_open_elements->current_node_is( 'OPTION' ) ) {
+                                       $this->state->stack_of_open_elements->pop();
+                               }
+
+                               if ( $this->state->stack_of_open_elements->current_node_is( 'OPTGROUP' ) ) {
+                                       $this->state->stack_of_open_elements->pop();
+                               }
+
+                               $this->insert_html_element( $this->state->current_token );
+                               return true;
+
+                       /*
+                        * > An end tag whose tag name is "optgroup"
+                        */
+                       case '-OPTGROUP':
+                               $current_node = $this->state->stack_of_open_elements->current_node();
+                               if ( $current_node && 'OPTION' === $current_node->node_name ) {
+                                       foreach ( $this->state->stack_of_open_elements->walk_up( $current_node ) as $parent ) {
+                                               break;
+                                       }
+                                       if ( $parent && 'OPTGROUP' === $parent->node_name ) {
+                                               $this->state->stack_of_open_elements->pop();
+                                       }
+                               }
+
+                               if ( $this->state->stack_of_open_elements->current_node_is( 'OPTGROUP' ) ) {
+                                       $this->state->stack_of_open_elements->pop();
+                                       return true;
+                               }
+
+                               // Parse error: ignore the token.
+                               return $this->step();
+
+                       /*
+                        * > An end tag whose tag name is "option"
+                        */
+                       case '-OPTION':
+                               if ( $this->state->stack_of_open_elements->current_node_is( 'OPTION' ) ) {
+                                       $this->state->stack_of_open_elements->pop();
+                                       return true;
+                               }
+
+                               // Parse error: ignore the token.
+                               return $this->step();
+
+                       /*
+                        * > An end tag whose tag name is "select"
+                        * > A start tag whose tag name is "select"
+                        *
+                        * > It just gets treated like an end tag.
+                        */
+                       case '-SELECT':
+                       case '+SELECT':
+                               if ( ! $this->state->stack_of_open_elements->has_element_in_select_scope( 'SELECT' ) ) {
+                                       // Parse error: ignore the token.
+                                       return $this->step();
+                               }
+                               $this->state->stack_of_open_elements->pop_until( 'SELECT' );
+                               $this->reset_insertion_mode();
+                               return true;
+
+                       /*
+                        * > A start tag whose tag name is one of: "input", "keygen", "textarea"
+                        *
+                        * All three of these tags are considered a parse error when found in this insertion mode.
+                        */
+                       case '+INPUT':
+                       case '+KEYGEN':
+                       case '+TEXTAREA':
+                               if ( ! $this->state->stack_of_open_elements->has_element_in_select_scope( 'SELECT' ) ) {
+                                       // Ignore the token.
+                                       return $this->step();
+                               }
+                               $this->state->stack_of_open_elements->pop_until( 'SELECT' );
+                               $this->reset_insertion_mode();
+                               return $this->step( self::REPROCESS_CURRENT_NODE );
+
+                       /*
+                        * > A start tag whose tag name is one of: "script", "template"
+                        * > An end tag whose tag name is "template"
+                        */
+                       case '+SCRIPT':
+                       case '+TEMPLATE':
+                       case '-TEMPLATE':
+                               return $this->step_in_head();
+               }
+
+               /*
+                * > Anything else
+                * >   Parse error: ignore the token.
+                */
+               return $this->step();
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /*
</span><span class="cx" style="display: block; padding: 0 10px">         * Internal helpers
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2036,6 +2282,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * Closes elements that have implied end tags.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 6.4.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 6.7.0 Full spec support.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @see https://html.spec.whatwg.org/#generate-implied-end-tags
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2046,12 +2293,19 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'DD',
</span><span class="cx" style="display: block; padding: 0 10px">                        'DT',
</span><span class="cx" style="display: block; padding: 0 10px">                        'LI',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'OPTGROUP',
+                       'OPTION',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'P',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'RB',
+                       'RP',
+                       'RT',
+                       'RTC',
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $current_node = $this->state->stack_of_open_elements->current_node();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $no_exclusions = ! isset( $except_for_this_element );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 while (
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $current_node && $current_node->node_name !== $except_for_this_element &&
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 ( $no_exclusions || ! $this->state->stack_of_open_elements->current_node_is( $except_for_this_element ) ) &&
</ins><span class="cx" style="display: block; padding: 0 10px">                         in_array( $this->state->stack_of_open_elements->current_node(), $elements_with_implied_end_tags, true )
</span><span class="cx" style="display: block; padding: 0 10px">                ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->state->stack_of_open_elements->pop();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2065,6 +2319,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * different from generating end tags in the normal sense.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 6.4.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 6.7.0 Full spec support.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @see WP_HTML_Processor::generate_implied_end_tags
</span><span class="cx" style="display: block; padding: 0 10px">         * @see https://html.spec.whatwg.org/#generate-implied-end-tags
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2071,10 +2326,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        private function generate_implied_end_tags_thoroughly() {
</span><span class="cx" style="display: block; padding: 0 10px">                $elements_with_implied_end_tags = array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'CAPTION',
+                       'COLGROUP',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'DD',
</span><span class="cx" style="display: block; padding: 0 10px">                        'DT',
</span><span class="cx" style="display: block; padding: 0 10px">                        'LI',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'OPTGROUP',
+                       'OPTION',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'P',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'RB',
+                       'RP',
+                       'RT',
+                       'RTC',
+                       'TBODY',
+                       'TD',
+                       'TFOOT',
+                       'TH',
+                       'THEAD',
+                       'TR',
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                while ( in_array( $this->state->stack_of_open_elements->current_node(), $elements_with_implied_end_tags, true ) ) {
</span></span></pre></div>
<a id="trunktestsphpunittestshtmlapiwpHtmlProcessorphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php    2024-07-04 23:14:44 UTC (rev 58676)
+++ trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php      2024-07-05 00:50:19 UTC (rev 58677)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -406,8 +406,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'NOFRAMES'  => array( 'NOFRAMES' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'NOSCRIPT'  => array( 'NOSCRIPT' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'OBJECT'    => array( 'OBJECT' ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'OPTGROUP'  => array( 'OPTGROUP' ),
-                       'OPTION'    => array( 'OPTION' ),
</del><span class="cx" style="display: block; padding: 0 10px">                         'PLAINTEXT' => array( 'PLAINTEXT' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'RB'        => array( 'RB' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'RP'        => array( 'RP' ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -415,7 +413,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'RTC'       => array( 'RTC' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'SARCASM'   => array( 'SARCASM' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'SCRIPT'    => array( 'SCRIPT' ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'SELECT'    => array( 'SELECT' ),
</del><span class="cx" style="display: block; padding: 0 10px">                         'STYLE'     => array( 'STYLE' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'SVG'       => array( 'SVG' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'TABLE'     => array( 'TABLE' ),
</span></span></pre></div>
<a id="trunktestsphpunittestshtmlapiwpHtmlProcessorBreadcrumbsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php 2024-07-04 23:14:44 UTC (rev 58676)
+++ trunk/tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php   2024-07-05 00:50:19 UTC (rev 58677)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -189,8 +189,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'NOFRAMES', // Neutralized.
</span><span class="cx" style="display: block; padding: 0 10px">                        'NOSCRIPT',
</span><span class="cx" style="display: block; padding: 0 10px">                        'OBJECT',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'OPTGROUP',
-                       'OPTION',
</del><span class="cx" style="display: block; padding: 0 10px">                         'PLAINTEXT', // Neutralized.
</span><span class="cx" style="display: block; padding: 0 10px">                        'RB', // Neutralized.
</span><span class="cx" style="display: block; padding: 0 10px">                        'RP',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -197,7 +195,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'RT',
</span><span class="cx" style="display: block; padding: 0 10px">                        'RTC', // Neutralized.
</span><span class="cx" style="display: block; padding: 0 10px">                        'SCRIPT',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'SELECT',
</del><span class="cx" style="display: block; padding: 0 10px">                         'STYLE',
</span><span class="cx" style="display: block; padding: 0 10px">                        'SVG',
</span><span class="cx" style="display: block; padding: 0 10px">                        'TABLE',
</span></span></pre></div>
<a id="trunktestsphpunittestshtmlapiwpHtmlSupportRequiredHtmlProcessorphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredHtmlProcessor.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredHtmlProcessor.php 2024-07-04 23:14:44 UTC (rev 58676)
+++ trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredHtmlProcessor.php   2024-07-05 00:50:19 UTC (rev 58677)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -58,8 +58,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @covers WP_HTML_Processor::generate_implied_end_tags
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_generate_implied_end_tags_needs_support() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->ensure_support_is_added_everywhere( 'OPTGROUP' );
-               $this->ensure_support_is_added_everywhere( 'OPTION' );
</del><span class="cx" style="display: block; padding: 0 10px">                 $this->ensure_support_is_added_everywhere( 'RB' );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->ensure_support_is_added_everywhere( 'RP' );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->ensure_support_is_added_everywhere( 'RT' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -79,8 +77,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_generate_implied_end_tags_thoroughly_needs_support() {
</span><span class="cx" style="display: block; padding: 0 10px">                $this->ensure_support_is_added_everywhere( 'CAPTION' );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->ensure_support_is_added_everywhere( 'COLGROUP' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->ensure_support_is_added_everywhere( 'OPTGROUP' );
-               $this->ensure_support_is_added_everywhere( 'OPTION' );
</del><span class="cx" style="display: block; padding: 0 10px">                 $this->ensure_support_is_added_everywhere( 'RB' );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->ensure_support_is_added_everywhere( 'RP' );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->ensure_support_is_added_everywhere( 'RT' );
</span></span></pre></div>
<a id="trunktestsphpunittestshtmlapiwpHtmlSupportRequiredOpenElementsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredOpenElements.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredOpenElements.php  2024-07-04 23:14:44 UTC (rev 58676)
+++ trunk/tests/phpunit/tests/html-api/wpHtmlSupportRequiredOpenElements.php    2024-07-05 00:50:19 UTC (rev 58677)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -308,9 +308,5 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * FOREIGNOBJECT, DESC, TITLE.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                $this->ensure_support_is_added_everywhere( 'SVG' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-               // These elements are specific to SELECT scope.
-               $this->ensure_support_is_added_everywhere( 'OPTGROUP' );
-               $this->ensure_support_is_added_everywhere( 'OPTION' );
</del><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre>
</div>
</div>

</body>
</html>