WP_HTML_Processor::step_in_body(): bool

This function’s access is marked private. This means it is not intended for use by plugin or theme developers, only by core. It is listed here for completeness.

Parses next element in the ‘in body’ insertion mode.

Description

This internal function performs the ‘in body’ insertion mode logic for the generalized WP_HTML_Processor::step() function.

See also

Return

bool Whether an element was found.

Source

private function step_in_body(): bool {
	$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 ) {
		case '#text':
			/*
			 * > A character token that is U+0000 NULL
			 *
			 * Any successive sequence of NULL bytes is ignored and won't
			 * trigger active format reconstruction. Therefore, if the text
			 * only comprises NULL bytes then the token should be ignored
			 * here, but if there are any other characters in the stream
			 * the active formats should be reconstructed.
			 */
			if ( parent::TEXT_IS_NULL_SEQUENCE === $this->text_node_classification ) {
				// Parse error: ignore the token.
				return $this->step();
			}

			$this->reconstruct_active_formatting_elements();

			/*
			 * Whitespace-only text does not affect the frameset-ok flag.
			 * It is probably inter-element whitespace, but it may also
			 * contain character references which decode only to whitespace.
			 */
			if ( parent::TEXT_IS_GENERIC === $this->text_node_classification ) {
				$this->state->frameset_ok = false;
			}

			$this->insert_html_element( $this->state->current_token );
			return true;

		case '#comment':
		case '#funky-comment':
		case '#presumptuous-tag':
			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * > A DOCTYPE token
		 * > Parse error. Ignore the token.
		 */
		case 'html':
			return $this->step();

		/*
		 * > A start tag whose tag name is "html"
		 */
		case '+HTML':
			if ( ! $this->state->stack_of_open_elements->contains( 'TEMPLATE' ) ) {
				/*
				 * > Otherwise, for each attribute on the token, check to see if the attribute
				 * > is already present on the top element of the stack of open elements. If
				 * > it is not, add the attribute and its corresponding value to that element.
				 *
				 * This parser does not currently support this behavior: ignore the token.
				 */
			}

			// Ignore the token.
			return $this->step();

		/*
		 * > A start tag whose tag name is one of: "base", "basefont", "bgsound", "link",
		 * > "meta", "noframes", "script", "style", "template", "title"
		 * >
		 * > An end tag whose tag name is "template"
		 */
		case '+BASE':
		case '+BASEFONT':
		case '+BGSOUND':
		case '+LINK':
		case '+META':
		case '+NOFRAMES':
		case '+SCRIPT':
		case '+STYLE':
		case '+TEMPLATE':
		case '+TITLE':
		case '-TEMPLATE':
			return $this->step_in_head();

		/*
		 * > A start tag whose tag name is "body"
		 *
		 * This tag in the IN BODY insertion mode is a parse error.
		 */
		case '+BODY':
			if (
				1 === $this->state->stack_of_open_elements->count() ||
				'BODY' !== ( $this->state->stack_of_open_elements->at( 2 )->node_name ?? null ) ||
				$this->state->stack_of_open_elements->contains( 'TEMPLATE' )
			) {
				// Ignore the token.
				return $this->step();
			}

			/*
			 * > Otherwise, set the frameset-ok flag to "not ok"; then, for each attribute
			 * > on the token, check to see if the attribute is already present on the body
			 * > element (the second element) on the stack of open elements, and if it is
			 * > not, add the attribute and its corresponding value to that element.
			 *
			 * This parser does not currently support this behavior: ignore the token.
			 */
			$this->state->frameset_ok = false;
			return $this->step();

		/*
		 * > A start tag whose tag name is "frameset"
		 *
		 * This tag in the IN BODY insertion mode is a parse error.
		 */
		case '+FRAMESET':
			if (
				1 === $this->state->stack_of_open_elements->count() ||
				'BODY' !== ( $this->state->stack_of_open_elements->at( 2 )->node_name ?? null ) ||
				false === $this->state->frameset_ok
			) {
				// Ignore the token.
				return $this->step();
			}

			/*
			 * > Otherwise, run the following steps:
			 */
			$this->bail( 'Cannot process non-ignored FRAMESET tags.' );
			break;

		/*
		 * > An end tag whose tag name is "body"
		 */
		case '-BODY':
			if ( ! $this->state->stack_of_open_elements->has_element_in_scope( 'BODY' ) ) {
				// Parse error: ignore the token.
				return $this->step();
			}

			/*
			 * > Otherwise, if there is a node in the stack of open elements that is not either a
			 * > dd element, a dt element, an li element, an optgroup element, an option element,
			 * > a p element, an rb element, an rp element, an rt element, an rtc element, a tbody
			 * > element, a td element, a tfoot element, a th element, a thread element, a tr
			 * > element, the body element, or the html element, then this is a parse error.
			 *
			 * There is nothing to do for this parse error, so don't check for it.
			 */

			$this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_AFTER_BODY;
			/*
			 * The BODY element is not removed from the stack of open elements.
			 * Only internal state has changed, this does not qualify as a "step"
			 * in terms of advancing through the document to another token.
			 * Nothing has been pushed or popped.
			 * Proceed to parse the next item.
			 */
			return $this->step();

		/*
		 * > An end tag whose tag name is "html"
		 */
		case '-HTML':
			if ( ! $this->state->stack_of_open_elements->has_element_in_scope( 'BODY' ) ) {
				// Parse error: ignore the token.
				return $this->step();
			}

			/*
			 * > Otherwise, if there is a node in the stack of open elements that is not either a
			 * > dd element, a dt element, an li element, an optgroup element, an option element,
			 * > a p element, an rb element, an rp element, an rt element, an rtc element, a tbody
			 * > element, a td element, a tfoot element, a th element, a thread element, a tr
			 * > element, the body element, or the html element, then this is a parse error.
			 *
			 * There is nothing to do for this parse error, so don't check for it.
			 */

			$this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_AFTER_BODY;
			return $this->step( self::REPROCESS_CURRENT_NODE );

		/*
		 * > A start tag whose tag name is one of: "address", "article", "aside",
		 * > "blockquote", "center", "details", "dialog", "dir", "div", "dl",
		 * > "fieldset", "figcaption", "figure", "footer", "header", "hgroup",
		 * > "main", "menu", "nav", "ol", "p", "search", "section", "summary", "ul"
		 */
		case '+ADDRESS':
		case '+ARTICLE':
		case '+ASIDE':
		case '+BLOCKQUOTE':
		case '+CENTER':
		case '+DETAILS':
		case '+DIALOG':
		case '+DIR':
		case '+DIV':
		case '+DL':
		case '+FIELDSET':
		case '+FIGCAPTION':
		case '+FIGURE':
		case '+FOOTER':
		case '+HEADER':
		case '+HGROUP':
		case '+MAIN':
		case '+MENU':
		case '+NAV':
		case '+OL':
		case '+P':
		case '+SEARCH':
		case '+SECTION':
		case '+SUMMARY':
		case '+UL':
			if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->close_a_p_element();
			}

			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * > A start tag whose tag name is one of: "h1", "h2", "h3", "h4", "h5", "h6"
		 */
		case '+H1':
		case '+H2':
		case '+H3':
		case '+H4':
		case '+H5':
		case '+H6':
			if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->close_a_p_element();
			}

			if (
				in_array(
					$this->state->stack_of_open_elements->current_node()->node_name,
					array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ),
					true
				)
			) {
				// @todo Indicate a parse error once it's possible.
				$this->state->stack_of_open_elements->pop();
			}

			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * > A start tag whose tag name is one of: "pre", "listing"
		 */
		case '+PRE':
		case '+LISTING':
			if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->close_a_p_element();
			}

			/*
			 * > If the next token is a U+000A LINE FEED (LF) character token,
			 * > then ignore that token and move on to the next one. (Newlines
			 * > at the start of pre blocks are ignored as an authoring convenience.)
			 *
			 * This is handled in `get_modifiable_text()`.
			 */

			$this->insert_html_element( $this->state->current_token );
			$this->state->frameset_ok = false;
			return true;

		/*
		 * > A start tag whose tag name is "form"
		 */
		case '+FORM':
			$stack_contains_template = $this->state->stack_of_open_elements->contains( 'TEMPLATE' );

			if ( isset( $this->state->form_element ) && ! $stack_contains_template ) {
				// Parse error: ignore the token.
				return $this->step();
			}

			if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->close_a_p_element();
			}

			$this->insert_html_element( $this->state->current_token );
			if ( ! $stack_contains_template ) {
				$this->state->form_element = $this->state->current_token;
			}

			return true;

		/*
		 * > A start tag whose tag name is "li"
		 * > A start tag whose tag name is one of: "dd", "dt"
		 */
		case '+DD':
		case '+DT':
		case '+LI':
			$this->state->frameset_ok = false;
			$node                     = $this->state->stack_of_open_elements->current_node();
			$is_li                    = 'LI' === $token_name;

			in_body_list_loop:
			/*
			 * The logic for LI and DT/DD is the same except for one point: LI elements _only_
			 * close other LI elements, but a DT or DD element closes _any_ open DT or DD element.
			 */
			if ( $is_li ? 'LI' === $node->node_name : ( 'DD' === $node->node_name || 'DT' === $node->node_name ) ) {
				$node_name = $is_li ? 'LI' : $node->node_name;
				$this->generate_implied_end_tags( $node_name );
				if ( ! $this->state->stack_of_open_elements->current_node_is( $node_name ) ) {
					// @todo Indicate a parse error once it's possible. This error does not impact the logic here.
				}

				$this->state->stack_of_open_elements->pop_until( $node_name );
				goto in_body_list_done;
			}

			if (
				'ADDRESS' !== $node->node_name &&
				'DIV' !== $node->node_name &&
				'P' !== $node->node_name &&
				self::is_special( $node )
			) {
				/*
				 * > If node is in the special category, but is not an address, div,
				 * > or p element, then jump to the step labeled done below.
				 */
				goto in_body_list_done;
			} else {
				/*
				 * > Otherwise, set node to the previous entry in the stack of open elements
				 * > and return to the step labeled loop.
				 */
				foreach ( $this->state->stack_of_open_elements->walk_up( $node ) as $item ) {
					$node = $item;
					break;
				}
				goto in_body_list_loop;
			}

			in_body_list_done:
			if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->close_a_p_element();
			}

			$this->insert_html_element( $this->state->current_token );
			return true;

		case '+PLAINTEXT':
			if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->close_a_p_element();
			}

			/*
			 * @todo This may need to be handled in the Tag Processor and turn into
			 *       a single self-contained tag like TEXTAREA, whose modifiable text
			 *       is the rest of the input document as plaintext.
			 */
			$this->bail( 'Cannot process PLAINTEXT elements.' );
			break;

		/*
		 * > 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->state->current_token );
			$this->state->frameset_ok = false;

			return true;

		/*
		 * > An end tag whose tag name is one of: "address", "article", "aside", "blockquote",
		 * > "button", "center", "details", "dialog", "dir", "div", "dl", "fieldset",
		 * > "figcaption", "figure", "footer", "header", "hgroup", "listing", "main",
		 * > "menu", "nav", "ol", "pre", "search", "section", "summary", "ul"
		 */
		case '-ADDRESS':
		case '-ARTICLE':
		case '-ASIDE':
		case '-BLOCKQUOTE':
		case '-BUTTON':
		case '-CENTER':
		case '-DETAILS':
		case '-DIALOG':
		case '-DIR':
		case '-DIV':
		case '-DL':
		case '-FIELDSET':
		case '-FIGCAPTION':
		case '-FIGURE':
		case '-FOOTER':
		case '-HEADER':
		case '-HGROUP':
		case '-LISTING':
		case '-MAIN':
		case '-MENU':
		case '-NAV':
		case '-OL':
		case '-PRE':
		case '-SEARCH':
		case '-SECTION':
		case '-SUMMARY':
		case '-UL':
			if ( ! $this->state->stack_of_open_elements->has_element_in_scope( $token_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_is( $token_name ) ) {
				// @todo Record parse error: this error doesn't impact parsing.
			}
			$this->state->stack_of_open_elements->pop_until( $token_name );
			return true;

		/*
		 * > An end tag whose tag name is "form"
		 */
		case '-FORM':
			if ( ! $this->state->stack_of_open_elements->contains( 'TEMPLATE' ) ) {
				$node                      = $this->state->form_element;
				$this->state->form_element = null;

				/*
				 * > If node is null or if the stack of open elements does not have node
				 * > in scope, then this is a parse error; return and ignore the token.
				 *
				 * @todo It's necessary to check if the form token itself is in scope, not
				 *       simply whether any FORM is in scope.
				 */
				if (
					null === $node ||
					! $this->state->stack_of_open_elements->has_element_in_scope( 'FORM' )
				) {
					// Parse error: ignore the token.
					return $this->step();
				}

				$this->generate_implied_end_tags();
				if ( $node !== $this->state->stack_of_open_elements->current_node() ) {
					// @todo Indicate a parse error once it's possible. This error does not impact the logic here.
					$this->bail( 'Cannot close a FORM when other elements remain open as this would throw off the breadcrumbs for the following tokens.' );
				}

				$this->state->stack_of_open_elements->remove_node( $node );
				return true;
			} else {
				/*
				 * > If the stack of open elements does not have a form element in scope,
				 * > then this is a parse error; return and ignore the token.
				 *
				 * Note that unlike in the clause above, this is checking for any FORM in scope.
				 */
				if ( ! $this->state->stack_of_open_elements->has_element_in_scope( 'FORM' ) ) {
					// Parse error: ignore the token.
					return $this->step();
				}

				$this->generate_implied_end_tags();

				if ( ! $this->state->stack_of_open_elements->current_node_is( 'FORM' ) ) {
					// @todo Indicate a parse error once it's possible. This error does not impact the logic here.
				}

				$this->state->stack_of_open_elements->pop_until( 'FORM' );
				return true;
			}
			break;

		/*
		 * > An end tag whose tag name is "p"
		 */
		case '-P':
			if ( ! $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->insert_html_element( $this->state->current_token );
			}

			$this->close_a_p_element();
			return true;

		/*
		 * > An end tag whose tag name is "li"
		 * > An end tag whose tag name is one of: "dd", "dt"
		 */
		case '-DD':
		case '-DT':
		case '-LI':
			if (
				/*
				 * An end tag whose tag name is "li":
				 * If the stack of open elements does not have an li element in list item scope,
				 * then this is a parse error; ignore the token.
				 */
				(
					'LI' === $token_name &&
					! $this->state->stack_of_open_elements->has_element_in_list_item_scope( 'LI' )
				) ||
				/*
				 * An end tag whose tag name is one of: "dd", "dt":
				 * If the stack of open elements does not have an element in scope that is an
				 * HTML element with the same tag name as that of the token, then this is a
				 * parse error; ignore the token.
				 */
				(
					'LI' !== $token_name &&
					! $this->state->stack_of_open_elements->has_element_in_scope( $token_name )
				)
			) {
				/*
				 * This is a parse error, ignore the token.
				 *
				 * @todo Indicate a parse error once it's possible.
				 */
				return $this->step();
			}

			$this->generate_implied_end_tags( $token_name );

			if ( ! $this->state->stack_of_open_elements->current_node_is( $token_name ) ) {
				// @todo Indicate a parse error once it's possible. This error does not impact the logic here.
			}

			$this->state->stack_of_open_elements->pop_until( $token_name );
			return true;

		/*
		 * > An end tag whose tag name is one of: "h1", "h2", "h3", "h4", "h5", "h6"
		 */
		case '-H1':
		case '-H2':
		case '-H3':
		case '-H4':
		case '-H5':
		case '-H6':
			if ( ! $this->state->stack_of_open_elements->has_element_in_scope( '(internal: H1 through H6 - do not use)' ) ) {
				/*
				 * This is a parse error; ignore the token.
				 *
				 * @todo Indicate a parse error once it's possible.
				 */
				return $this->step();
			}

			$this->generate_implied_end_tags();

			if ( ! $this->state->stack_of_open_elements->current_node_is( $token_name ) ) {
				// @todo Record parse error: this error doesn't impact parsing.
			}

			$this->state->stack_of_open_elements->pop_until( '(internal: H1 through H6 - do not use)' );
			return true;

		/*
		 * > A start tag whose tag name is "a"
		 */
		case '+A':
			foreach ( $this->state->active_formatting_elements->walk_up() as $item ) {
				switch ( $item->node_name ) {
					case 'marker':
						break 2;

					case 'A':
						$this->run_adoption_agency_algorithm();
						$this->state->active_formatting_elements->remove_node( $item );
						$this->state->stack_of_open_elements->remove_node( $item );
						break 2;
				}
			}

			$this->reconstruct_active_formatting_elements();
			$this->insert_html_element( $this->state->current_token );
			$this->state->active_formatting_elements->push( $this->state->current_token );
			return true;

		/*
		 * > A start tag whose tag name is one of: "b", "big", "code", "em", "font", "i",
		 * > "s", "small", "strike", "strong", "tt", "u"
		 */
		case '+B':
		case '+BIG':
		case '+CODE':
		case '+EM':
		case '+FONT':
		case '+I':
		case '+S':
		case '+SMALL':
		case '+STRIKE':
		case '+STRONG':
		case '+TT':
		case '+U':
			$this->reconstruct_active_formatting_elements();
			$this->insert_html_element( $this->state->current_token );
			$this->state->active_formatting_elements->push( $this->state->current_token );
			return true;

		/*
		 * > A start tag whose tag name is "nobr"
		 */
		case '+NOBR':
			$this->reconstruct_active_formatting_elements();

			if ( $this->state->stack_of_open_elements->has_element_in_scope( 'NOBR' ) ) {
				// Parse error.
				$this->run_adoption_agency_algorithm();
				$this->reconstruct_active_formatting_elements();
			}

			$this->insert_html_element( $this->state->current_token );
			$this->state->active_formatting_elements->push( $this->state->current_token );
			return true;

		/*
		 * > An end tag whose tag name is one of: "a", "b", "big", "code", "em", "font", "i",
		 * > "nobr", "s", "small", "strike", "strong", "tt", "u"
		 */
		case '-A':
		case '-B':
		case '-BIG':
		case '-CODE':
		case '-EM':
		case '-FONT':
		case '-I':
		case '-NOBR':
		case '-S':
		case '-SMALL':
		case '-STRIKE':
		case '-STRONG':
		case '-TT':
		case '-U':
			$this->run_adoption_agency_algorithm();
			return true;

		/*
		 * > A start tag whose tag name is one of: "applet", "marquee", "object"
		 */
		case '+APPLET':
		case '+MARQUEE':
		case '+OBJECT':
			$this->reconstruct_active_formatting_elements();
			$this->insert_html_element( $this->state->current_token );
			$this->state->active_formatting_elements->insert_marker();
			$this->state->frameset_ok = false;
			return true;

		/*
		 * > A end tag token whose tag name is one of: "applet", "marquee", "object"
		 */
		case '-APPLET':
		case '-MARQUEE':
		case '-OBJECT':
			if ( ! $this->state->stack_of_open_elements->has_element_in_scope( $token_name ) ) {
				// Parse error: ignore the token.
				return $this->step();
			}

			$this->generate_implied_end_tags();
			if ( ! $this->state->stack_of_open_elements->current_node_is( $token_name ) ) {
				// This is a parse error.
			}

			$this->state->stack_of_open_elements->pop_until( $token_name );
			$this->state->active_formatting_elements->clear_up_to_last_marker();
			return true;

		/*
		 * > A start tag whose tag name is "table"
		 */
		case '+TABLE':
			/*
			 * > If the Document is not set to quirks mode, and the stack of open elements
			 * > has a p element in button scope, then close a p element.
			 */
			if (
				WP_HTML_Tag_Processor::QUIRKS_MODE !== $this->compat_mode &&
				$this->state->stack_of_open_elements->has_p_in_button_scope()
			) {
				$this->close_a_p_element();
			}

			$this->insert_html_element( $this->state->current_token );
			$this->state->frameset_ok    = false;
			$this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE;
			return true;

		/*
		 * > An end tag whose tag name is "br"
		 *
		 * This is prevented from happening because the Tag Processor
		 * reports all closing BR tags as if they were opening tags.
		 */

		/*
		 * > A start tag whose tag name is one of: "area", "br", "embed", "img", "keygen", "wbr"
		 */
		case '+AREA':
		case '+BR':
		case '+EMBED':
		case '+IMG':
		case '+KEYGEN':
		case '+WBR':
			$this->reconstruct_active_formatting_elements();
			$this->insert_html_element( $this->state->current_token );
			$this->state->frameset_ok = false;
			return true;

		/*
		 * > A start tag whose tag name is "input"
		 */
		case '+INPUT':
			$this->reconstruct_active_formatting_elements();
			$this->insert_html_element( $this->state->current_token );

			/*
			 * > If the token does not have an attribute with the name "type", or if it does,
			 * > but that attribute's value is not an ASCII case-insensitive match for the
			 * > string "hidden", then: set the frameset-ok flag to "not ok".
			 */
			$type_attribute = $this->get_attribute( 'type' );
			if ( ! is_string( $type_attribute ) || 'hidden' !== strtolower( $type_attribute ) ) {
				$this->state->frameset_ok = false;
			}

			return true;

		/*
		 * > A start tag whose tag name is one of: "param", "source", "track"
		 */
		case '+PARAM':
		case '+SOURCE':
		case '+TRACK':
			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * > A start tag whose tag name is "hr"
		 */
		case '+HR':
			if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->close_a_p_element();
			}
			$this->insert_html_element( $this->state->current_token );
			$this->state->frameset_ok = false;
			return true;

		/*
		 * > A start tag whose tag name is "image"
		 */
		case '+IMAGE':
			/*
			 * > Parse error. Change the token's tag name to "img" and reprocess it. (Don't ask.)
			 *
			 * Note that this is handled elsewhere, so it should not be possible to reach this code.
			 */
			$this->bail( "Cannot process an IMAGE tag. (Don't ask.)" );
			break;

		/*
		 * > A start tag whose tag name is "textarea"
		 */
		case '+TEXTAREA':
			$this->insert_html_element( $this->state->current_token );

			/*
			 * > If the next token is a U+000A LINE FEED (LF) character token, then ignore
			 * > that token and move on to the next one. (Newlines at the start of
			 * > textarea elements are ignored as an authoring convenience.)
			 *
			 * This is handled in `get_modifiable_text()`.
			 */

			$this->state->frameset_ok = false;

			/*
			 * > Switch the insertion mode to "text".
			 *
			 * As a self-contained node, this behavior is handled in the Tag Processor.
			 */
			return true;

		/*
		 * > A start tag whose tag name is "xmp"
		 */
		case '+XMP':
			if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
				$this->close_a_p_element();
			}

			$this->reconstruct_active_formatting_elements();
			$this->state->frameset_ok = false;

			/*
			 * > Follow the generic raw text element parsing algorithm.
			 *
			 * As a self-contained node, this behavior is handled in the Tag Processor.
			 */
			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * A start tag whose tag name is "iframe"
		 */
		case '+IFRAME':
			$this->state->frameset_ok = false;

			/*
			 * > Follow the generic raw text element parsing algorithm.
			 *
			 * As a self-contained node, this behavior is handled in the Tag Processor.
			 */
			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * > A start tag whose tag name is "noembed"
		 * > A start tag whose tag name is "noscript", if the scripting flag is enabled
		 *
		 * The scripting flag is never enabled in this parser.
		 */
		case '+NOEMBED':
			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * > 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;

		/*
		 * > A start tag whose tag name is one of: "rb", "rtc"
		 */
		case '+RB':
		case '+RTC':
			if ( $this->state->stack_of_open_elements->has_element_in_scope( 'RUBY' ) ) {
				$this->generate_implied_end_tags();

				if ( $this->state->stack_of_open_elements->current_node_is( 'RUBY' ) ) {
					// @todo Indicate a parse error once it's possible.
				}
			}

			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * > A start tag whose tag name is one of: "rp", "rt"
		 */
		case '+RP':
		case '+RT':
			if ( $this->state->stack_of_open_elements->has_element_in_scope( 'RUBY' ) ) {
				$this->generate_implied_end_tags( 'RTC' );

				$current_node_name = $this->state->stack_of_open_elements->current_node()->node_name;
				if ( 'RTC' === $current_node_name || 'RUBY' === $current_node_name ) {
					// @todo Indicate a parse error once it's possible.
				}
			}

			$this->insert_html_element( $this->state->current_token );
			return true;

		/*
		 * > A start tag whose tag name is "math"
		 */
		case '+MATH':
			$this->reconstruct_active_formatting_elements();

			/*
			 * @todo Adjust MathML attributes for the token. (This fixes the case of MathML attributes that are not all lowercase.)
			 * @todo Adjust foreign attributes for the token. (This fixes the use of namespaced attributes, in particular XLink.)
			 *
			 * These ought to be handled in the attribute methods.
			 */
			$this->state->current_token->namespace = 'math';
			$this->insert_html_element( $this->state->current_token );
			if ( $this->state->current_token->has_self_closing_flag ) {
				$this->state->stack_of_open_elements->pop();
			}
			return true;

		/*
		 * > A start tag whose tag name is "svg"
		 */
		case '+SVG':
			$this->reconstruct_active_formatting_elements();

			/*
			 * @todo Adjust SVG attributes for the token. (This fixes the case of SVG attributes that are not all lowercase.)
			 * @todo Adjust foreign attributes for the token. (This fixes the use of namespaced attributes, in particular XLink in SVG.)
			 *
			 * These ought to be handled in the attribute methods.
			 */
			$this->state->current_token->namespace = 'svg';
			$this->insert_html_element( $this->state->current_token );
			if ( $this->state->current_token->has_self_closing_flag ) {
				$this->state->stack_of_open_elements->pop();
			}
			return true;

		/*
		 * > A start tag whose tag name is one of: "caption", "col", "colgroup",
		 * > "frame", "head", "tbody", "td", "tfoot", "th", "thead", "tr"
		 */
		case '+CAPTION':
		case '+COL':
		case '+COLGROUP':
		case '+FRAME':
		case '+HEAD':
		case '+TBODY':
		case '+TD':
		case '+TFOOT':
		case '+TH':
		case '+THEAD':
		case '+TR':
			// Parse error. Ignore the token.
			return $this->step();
	}

	if ( ! parent::is_tag_closer() ) {
		/*
		 * > Any other start tag
		 */
		$this->reconstruct_active_formatting_elements();
		$this->insert_html_element( $this->state->current_token );
		return true;
	} else {
		/*
		 * > Any other end tag
		 */

		/*
		 * Find the corresponding tag opener in the stack of open elements, if
		 * it exists before reaching a special element, which provides a kind
		 * of boundary in the stack. For example, a `</custom-tag>` should not
		 * close anything beyond its containing `P` or `DIV` element.
		 */
		foreach ( $this->state->stack_of_open_elements->walk_up() as $node ) {
			if ( 'html' === $node->namespace && $token_name === $node->node_name ) {
				break;
			}

			if ( self::is_special( $node ) ) {
				// This is a parse error, ignore the token.
				return $this->step();
			}
		}

		$this->generate_implied_end_tags( $token_name );
		if ( $node !== $this->state->stack_of_open_elements->current_node() ) {
			// @todo Record parse error: this error doesn't impact parsing.
		}

		foreach ( $this->state->stack_of_open_elements->walk_up() as $item ) {
			$this->state->stack_of_open_elements->pop();
			if ( $node === $item ) {
				return true;
			}
		}
	}

	$this->bail( 'Should not have been able to reach end of IN BODY processing. Check HTML API code.' );
	// This unnecessary return prevents tools from inaccurately reporting type errors.
	return false;
}

Changelog

VersionDescription
6.4.0Introduced.

User Contributed Notes

You must log in before being able to contribute a note or feedback.