diff --git a/wp-includes/html-api/class-wp-html-open-elements.php b/wp-includes/html-api/class-wp-html-open-elements.php
index 0518ccb8bf..035717b444 100644
--- a/wp-includes/html-api/class-wp-html-open-elements.php
+++ b/wp-includes/html-api/class-wp-html-open-elements.php
@@ -115,6 +115,15 @@ class WP_HTML_Open_Elements {
if ( $node->node_name === $tag_name ) {
return true;
}
+
+ switch ( $node->node_name ) {
+ case 'HTML':
+ return false;
+ }
+
+ if ( in_array( $node->node_name, $termination_list, true ) ) {
+ return true;
+ }
}
return false;
@@ -175,19 +184,7 @@ class WP_HTML_Open_Elements {
* @return bool Whether given element is in scope.
*/
public function has_element_in_button_scope( $tag_name ) {
- return $this->has_element_in_specific_scope(
- $tag_name,
- array(
-
- /*
- * Because it's not currently possible to encounter
- * one of the termination elements, they don't need
- * to be listed here. If they were, they would be
- * unreachable and only waste CPU cycles while
- * scanning through HTML.
- */
- )
- );
+ return $this->has_element_in_specific_scope( $tag_name, array( 'BUTTON' ) );
}
/**
@@ -394,6 +391,10 @@ class WP_HTML_Open_Elements {
* cases where the precalculated value needs to change.
*/
switch ( $item->node_name ) {
+ case 'BUTTON':
+ $this->has_p_in_button_scope = false;
+ break;
+
case 'P':
$this->has_p_in_button_scope = true;
break;
@@ -419,6 +420,10 @@ class WP_HTML_Open_Elements {
* cases where the precalculated value needs to change.
*/
switch ( $item->node_name ) {
+ case 'BUTTON':
+ $this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' );
+ break;
+
case 'P':
$this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' );
break;
diff --git a/wp-includes/html-api/class-wp-html-processor-state.php b/wp-includes/html-api/class-wp-html-processor-state.php
index b9fa53d0bd..3fe5192431 100644
--- a/wp-includes/html-api/class-wp-html-processor-state.php
+++ b/wp-includes/html-api/class-wp-html-processor-state.php
@@ -107,6 +107,19 @@ class WP_HTML_Processor_State {
*/
public $context_node = null;
+ /**
+ * The frameset-ok flag indicates if a `FRAMESET` element is allowed in the current state.
+ *
+ * > The frameset-ok flag is set to "ok" when the parser is created. It is set to "not ok" after certain tokens are seen.
+ *
+ * @since 6.4.0
+ *
+ * @see https://html.spec.whatwg.org/#frameset-ok-flag
+ *
+ * @var bool
+ */
+ public $frameset_ok = true;
+
/**
* Constructor - creates a new and empty state value.
*
diff --git a/wp-includes/html-api/class-wp-html-processor.php b/wp-includes/html-api/class-wp-html-processor.php
index 7a8eb34596..6e1723494c 100644
--- a/wp-includes/html-api/class-wp-html-processor.php
+++ b/wp-includes/html-api/class-wp-html-processor.php
@@ -349,7 +349,13 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
*/
public function next_tag( $query = null ) {
if ( null === $query ) {
- return $this->step();
+ while ( $this->step() ) {
+ if ( ! $this->is_tag_closer() ) {
+ return true;
+ }
+ }
+
+ return false;
}
if ( is_string( $query ) ) {
@@ -366,7 +372,13 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
}
if ( ! ( array_key_exists( 'breadcrumbs', $query ) && is_array( $query['breadcrumbs'] ) ) ) {
- return $this->step();
+ while ( $this->step() ) {
+ if ( ! $this->is_tag_closer() ) {
+ return true;
+ }
+ }
+
+ return false;
}
if ( isset( $query['tag_closers'] ) && 'visit' === $query['tag_closers'] ) {
@@ -383,7 +395,7 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
$crumb = end( $breadcrumbs );
$target = strtoupper( $crumb );
- while ( $this->step() ) {
+ while ( $match_offset > 0 && $this->step() ) {
if ( $target !== $this->get_tag() ) {
continue;
}
@@ -395,7 +407,7 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
}
$crumb = prev( $breadcrumbs );
- if ( false === $crumb && 0 === --$match_offset ) {
+ if ( false === $crumb && 0 === --$match_offset && ! $this->is_tag_closer() ) {
return true;
}
}
@@ -510,6 +522,22 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
$op = "{$op_sigil}{$tag_name}";
switch ( $op ) {
+ /*
+ * > A start tag whose tag name is "button"
+ */
+ case '+BUTTON':
+ if ( $this->state->stack_of_open_elements->has_element_in_scope( 'BUTTON' ) ) {
+ // @TODO: Indicate a parse error once it's possible. This error does not impact the logic here.
+ $this->generate_implied_end_tags();
+ $this->state->stack_of_open_elements->pop_until( 'BUTTON' );
+ }
+
+ $this->reconstruct_active_formatting_elements();
+ $this->insert_html_element( $this->current_token );
+ $this->state->frameset_ok = false;
+
+ return true;
+
/*
* > A start tag whose tag name is one of: "address", "article", "aside",
* > "blockquote", "center", "details", "dialog", "dir", "div", "dl",
@@ -535,15 +563,20 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
* > "menu", "nav", "ol", "pre", "search", "section", "summary", "ul"
*/
case '-BLOCKQUOTE':
+ case '-BUTTON':
case '-DIV':
case '-FIGCAPTION':
case '-FIGURE':
if ( ! $this->state->stack_of_open_elements->has_element_in_scope( $tag_name ) ) {
+ // @TODO: Report parse error.
// Ignore the token.
return $this->step();
}
$this->generate_implied_end_tags();
+ if ( $this->state->stack_of_open_elements->current_node()->node_name !== $tag_name ) {
+ // @TODO: Record parse error: this error doesn't impact parsing.
+ }
$this->state->stack_of_open_elements->pop_until( $tag_name );
return true;
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 456790bc59..7618d87979 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
-$wp_version = '6.4-alpha-56379';
+$wp_version = '6.4-alpha-56380';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.