<!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>[58558] trunk/src/wp-includes/html-api: HTML API: Add missing subclass methods to HTML Processor and add token provenance.</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/58558">58558</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/58558","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-06-25 03:09:43 +0000 (Tue, 25 Jun 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: Add missing subclass methods to HTML Processor and add token provenance.

This patch introduces two related changes:

 - It adds missing subclass methods on the HTML Processor which needed
   to be implemented since it started visiting virtual nodes. These
   methods need to account for the fact that not all tokens truly exist.

 - It adds a new concept and internal method, `is_virtual()`, indicating
   if the currently-matched token comes from the raw text in the input
   HTML document or if it was the byproduct of semantic parsing rules.
   This internal method and new vocabulary around token provenance
   considerably simplifies the logic spread throughout the rest of the
   class and its subclass methods.

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

Follow-up to <a href="https://core.trac.wordpress.org/changeset/58304">[58304]</a>.

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

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludeshtmlapiclasswphtmlprocessorphp">trunk/src/wp-includes/html-api/class-wp-html-processor.php</a></li>
<li><a href="#trunksrcwpincludeshtmlapiclasswphtmlstackeventphp">trunk/src/wp-includes/html-api/class-wp-html-stack-event.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<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-06-24 19:50:00 UTC (rev 58557)
+++ trunk/src/wp-includes/html-api/class-wp-html-processor.php  2024-06-25 03:09:43 UTC (rev 58558)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -349,13 +349,19 @@
</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->set_push_handler(
</span><span class="cx" style="display: block; padding: 0 10px">                        function ( WP_HTML_Token $token ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $this->element_queue[] = new WP_HTML_Stack_Event( $token, WP_HTML_Stack_Event::PUSH );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $is_virtual            = ! isset( $this->state->current_token ) || $this->is_tag_closer();
+                               $same_node             = isset( $this->state->current_token ) && $token->node_name === $this->state->current_token->node_name;
+                               $provenance            = ( ! $same_node || $is_virtual ) ? 'virtual' : 'real';
+                               $this->element_queue[] = new WP_HTML_Stack_Event( $token, WP_HTML_Stack_Event::PUSH, $provenance );
</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="cx" style="display: block; padding: 0 10px">                $this->state->stack_of_open_elements->set_pop_handler(
</span><span class="cx" style="display: block; padding: 0 10px">                        function ( WP_HTML_Token $token ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $this->element_queue[] = new WP_HTML_Stack_Event( $token, WP_HTML_Stack_Event::POP );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $is_virtual            = ! isset( $this->state->current_token ) || ! $this->is_tag_closer();
+                               $same_node             = isset( $this->state->current_token ) && $token->node_name === $this->state->current_token->node_name;
+                               $provenance            = ( ! $same_node || $is_virtual ) ? 'virtual' : 'real';
+                               $this->element_queue[] = new WP_HTML_Stack_Event( $token, WP_HTML_Stack_Event::POP, $provenance );
</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">@@ -569,12 +575,27 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return bool Whether the current tag is a tag closer.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function is_tag_closer() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return isset( $this->current_element )
-                       ? ( WP_HTML_Stack_Event::POP === $this->current_element->operation )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return $this->is_virtual()
+                       ? ( WP_HTML_Stack_Event::POP === $this->current_element->operation && '#tag' === $this->get_token_type() )
</ins><span class="cx" style="display: block; padding: 0 10px">                         : parent::is_tag_closer();
</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">+         * Indicates if the currently-matched token is virtual, created by a stack operation
+        * while processing HTML, rather than a token found in the HTML text itself.
+        *
+        * @since 6.6.0
+        *
+        * @return bool Whether the current token is virtual.
+        */
+       private function is_virtual() {
+               return (
+                       isset( $this->current_element->provenance ) &&
+                       'virtual' === $this->current_element->provenance
+               );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Indicates if the currently-matched tag matches the given breadcrumbs.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * A "*" represents a single tag wildcard, where any tag matches, but not no tags.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1440,7 +1461,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return null;
</span><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">-                if ( isset( $this->current_element ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $this->is_virtual() ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return $this->current_element->token->node_name;
</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">@@ -1460,6 +1481,27 @@
</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">+         * Indicates if the currently matched tag contains the self-closing flag.
+        *
+        * No HTML elements ought to have the self-closing flag and for those, the self-closing
+        * flag will be ignored. For void elements this is benign because they "self close"
+        * automatically. For non-void HTML elements though problems will appear if someone
+        * intends to use a self-closing element in place of that element with an empty body.
+        * For HTML foreign elements and custom elements the self-closing flag determines if
+        * they self-close or not.
+        *
+        * This function does not determine if a tag is self-closing,
+        * but only if the self-closing flag is present in the syntax.
+        *
+        * @since 6.6.0 Subclassed for the HTML Processor.
+        *
+        * @return bool Whether the currently matched tag contains the self-closing flag.
+        */
+       public function has_self_closing_flag() {
+               return $this->is_virtual() ? false : parent::has_self_closing_flag();
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Returns the node name represented by the token.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * This matches the DOM API value `nodeName`. Some values
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1480,11 +1522,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return string|null Name of the matched token.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_token_name() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( isset( $this->current_element ) ) {
-                       return $this->current_element->token->node_name;
-               }
-
-               return parent::get_token_name();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return $this->is_virtual()
+                       ? $this->current_element->token->node_name
+                       : parent::get_token_name();
</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">@@ -1510,9 +1550,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return string|null What kind of token is matched, or null.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_token_type() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( isset( $this->current_element ) ) {
-                       $node_name = $this->current_element->token->node_name;
-                       if ( ctype_upper( $node_name[0] ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $this->is_virtual() ) {
+                       /*
+                        * This logic comes from the Tag Processor.
+                        *
+                        * @todo It would be ideal not to repeat this here, but it's not clearly
+                        *       better to allow passing a token name to `get_token_type()`.
+                        */
+                       $node_name     = $this->current_element->token->node_name;
+                       $starting_char = $node_name[0];
+                       if ( 'A' <= $starting_char && 'Z' >= $starting_char ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 return '#tag';
</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">@@ -1546,25 +1593,38 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return string|true|null Value of attribute or `null` if not available. Boolean attributes return `true`.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_attribute( $name ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( isset( $this->current_element ) ) {
-                       // Closing tokens cannot contain attributes.
-                       if ( WP_HTML_Stack_Event::POP === $this->current_element->operation ) {
-                               return null;
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return $this->is_virtual() ? null : parent::get_attribute( $name );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $node_name = $this->current_element->token->node_name;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+        * Updates or creates a new attribute on the currently matched tag with the passed value.
+        *
+        * For boolean attributes special handling is provided:
+        *  - When `true` is passed as the value, then only the attribute name is added to the tag.
+        *  - When `false` is passed, the attribute gets removed if it existed before.
+        *
+        * For string attributes, the value is escaped using the `esc_attr` function.
+        *
+        * @since 6.6.0 Subclassed for the HTML Processor.
+        *
+        * @param string      $name  The attribute name to target.
+        * @param string|bool $value The new attribute value.
+        * @return bool Whether an attribute value was set.
+        */
+       public function set_attribute( $name, $value ) {
+               return $this->is_virtual() ? false : parent::set_attribute( $name, $value );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // Only tags can contain attributes.
-                       if ( 'A' > $node_name[0] || 'Z' < $node_name[0] ) {
-                               return null;
-                       }
-
-                       if ( $this->current_element->token->bookmark_name === (string) $this->bookmark_counter ) {
-                               return parent::get_attribute( $name );
-                       }
-               }
-
-               return null;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+        * Remove an attribute from the currently-matched tag.
+        *
+        * @since 6.6.0 Subclassed for HTML Processor.
+        *
+        * @param string $name The attribute name to remove.
+        * @return bool Whether an attribute was removed.
+        */
+       public function remove_attribute( $name ) {
+               return $this->is_virtual() ? false : parent::remove_attribute( $name );
</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">@@ -1594,21 +1654,66 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array|null List of attribute names, or `null` when no tag opener is matched.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_attribute_names_with_prefix( $prefix ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( isset( $this->current_element ) ) {
-                       if ( WP_HTML_Stack_Event::POP === $this->current_element->operation ) {
-                               return null;
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return $this->is_virtual() ? null : parent::get_attribute_names_with_prefix( $prefix );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $mark = $this->bookmarks[ $this->current_element->token->bookmark_name ];
-                       if ( 0 === $mark->length ) {
-                               return null;
-                       }
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+        * Adds a new class name to the currently matched tag.
+        *
+        * @since 6.6.0 Subclassed for the HTML Processor.
+        *
+        * @param string $class_name The class name to add.
+        * @return bool Whether the class was set to be added.
+        */
+       public function add_class( $class_name ) {
+               return $this->is_virtual() ? false : parent::add_class( $class_name );
+       }
</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 parent::get_attribute_names_with_prefix( $prefix );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+        * Removes a class name from the currently matched tag.
+        *
+        * @since 6.6.0 Subclassed for the HTML Processor.
+        *
+        * @param string $class_name The class name to remove.
+        * @return bool Whether the class was set to be removed.
+        */
+       public function remove_class( $class_name ) {
+               return $this->is_virtual() ? false : parent::remove_class( $class_name );
</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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Returns if a matched tag contains the given ASCII case-insensitive class name.
+        *
+        * @since 6.6.0 Subclassed for the HTML Processor.
+        *
+        * @param string $wanted_class Look for this CSS class name, ASCII case-insensitive.
+        * @return bool|null Whether the matched tag contains the given class name, or null if not matched.
+        */
+       public function has_class( $wanted_class ) {
+               return $this->is_virtual() ? null : parent::has_class( $wanted_class );
+       }
+
+       /**
+        * Generator for a foreach loop to step through each class name for the matched tag.
+        *
+        * This generator function is designed to be used inside a "foreach" loop.
+        *
+        * Example:
+        *
+        *     $p = WP_HTML_Processor::create_fragment( "<div class='free &lt;egg&lt;\tlang-en'>" );
+        *     $p->next_tag();
+        *     foreach ( $p->class_list() as $class_name ) {
+        *         echo "{$class_name} ";
+        *     }
+        *     // Outputs: "free <egg> lang-en "
+        *
+        * @since 6.6.0 Subclassed for the HTML Processor.
+        */
+       public function class_list() {
+               return $this->is_virtual() ? null : parent::class_list();
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Returns the modifiable text for a matched token, or an empty string.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * Modifiable text is text content that may be read and changed without
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1629,17 +1734,30 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return string
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_modifiable_text() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( isset( $this->current_element ) ) {
-                       if ( WP_HTML_Stack_Event::POP === $this->current_element->operation ) {
-                               return '';
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return $this->is_virtual() ? '' : parent::get_modifiable_text();
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $mark = $this->bookmarks[ $this->current_element->token->bookmark_name ];
-                       if ( 0 === $mark->length ) {
-                               return '';
-                       }
-               }
-               return parent::get_modifiable_text();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+        * Indicates what kind of comment produced the comment node.
+        *
+        * Because there are different kinds of HTML syntax which produce
+        * comments, the Tag Processor tracks and exposes this as a type
+        * for the comment. Nominally only regular HTML comments exist as
+        * they are commonly known, but a number of unrelated syntax errors
+        * also produce comments.
+        *
+        * @see self::COMMENT_AS_ABRUPTLY_CLOSED_COMMENT
+        * @see self::COMMENT_AS_CDATA_LOOKALIKE
+        * @see self::COMMENT_AS_INVALID_HTML
+        * @see self::COMMENT_AS_HTML_COMMENT
+        * @see self::COMMENT_AS_PI_NODE_LOOKALIKE
+        *
+        * @since 6.6.0 Subclassed for the HTML Processor.
+        *
+        * @return string|null
+        */
+       public function get_comment_type() {
+               return $this->is_virtual() ? null : parent::get_comment_type();
</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="trunksrcwpincludeshtmlapiclasswphtmlstackeventphp"></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-stack-event.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-stack-event.php      2024-06-24 19:50:00 UTC (rev 58557)
+++ trunk/src/wp-includes/html-api/class-wp-html-stack-event.php        2024-06-25 03:09:43 UTC (rev 58558)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -57,13 +57,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $operation;
</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">+         * Indicates if the stack element is a real or virtual node.
+        *
+        * @since 6.6.0
+        *
+        * @var string
+        */
+       public $provenance;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Constructor function.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param WP_HTML_Token $token     Token associated with stack event, always an opening token.
-        * @param string        $operation One of self::PUSH or self::POP.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @since 6.6.0
+        *
+        * @param WP_HTML_Token $token      Token associated with stack event, always an opening token.
+        * @param string        $operation  One of self::PUSH or self::POP.
+        * @param string        $provenance "virtual" or "real".
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function __construct( $token, $operation ) {
-               $this->token     = $token;
-               $this->operation = $operation;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function __construct( $token, $operation, $provenance ) {
+               $this->token      = $token;
+               $this->operation  = $operation;
+               $this->provenance = $provenance;
</ins><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>