diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php
index 168b8ace4c394..302b524e96902 100644
--- a/src/wp-includes/html-api/class-wp-html-processor.php
+++ b/src/wp-includes/html-api/class-wp-html-processor.php
@@ -800,7 +800,7 @@ public function expects_closer( WP_HTML_Token $node = null ): ?bool {
// Void elements.
self::is_void( $token_name ) ||
// Special atomic elements.
- ( 'html' === $token_namespace && in_array( $token_name, array( 'IFRAME', 'NOEMBED', 'NOFRAMES', 'SCRIPT', 'STYLE', 'TEXTAREA', 'TITLE', 'XMP' ), true ) ) ||
+ parent::is_self_contained_tag() ||
// Self-closing elements in foreign content.
( 'html' !== $token_namespace && $token_has_self_closing )
);
diff --git a/src/wp-includes/html-api/class-wp-html-tag-processor.php b/src/wp-includes/html-api/class-wp-html-tag-processor.php
index 72307cb3920b6..6be123fdc3774 100644
--- a/src/wp-includes/html-api/class-wp-html-tag-processor.php
+++ b/src/wp-includes/html-api/class-wp-html-tag-processor.php
@@ -3179,6 +3179,31 @@ public function has_self_closing_flag(): bool {
return '/' === $this->html[ $this->token_starts_at + $this->token_length - 2 ];
}
+ /**
+ * Indicates if the currently-matched token is a self-contained element,
+ * i.e. a SCRIPT, STYLE, TEXTAREA, or TITLE element etc…
+ *
+ * When the HTML API finds one of these elements it will consume the entire
+ * content up to and including the closing tag. They act like void tags, even
+ * though they expect closing tags in input HTML. They are treated this way
+ * because they cannot contain other HTML tags inside of them; their input is
+ * always text content only, with or without replacement of character
+ * references depending on which element it is.
+ *
+ * To get the contents of these elements, call {@see static::get_modifiable_text}.
+ *
+ * @since 6.7.0
+ *
+ * @return bool Whether the currently-matched token is a self-contained element.
+ */
+ public function is_self_contained_tag(): bool {
+ return (
+ self::STATE_MATCHED_TAG === $this->parser_state &&
+ 'html' === $this->get_namespace() &&
+ in_array( $this->get_tag(), array( 'IFRAME', 'NOEMBED', 'NOFRAMES', 'SCRIPT', 'STYLE', 'TEXTAREA', 'TITLE', 'XMP' ), true )
+ );
+ }
+
/**
* Indicates if the current tag token is a tag closer.
*