Title: WP_HTML_Processor::step_in_foreign_content
Published: February 24, 2026

---

# WP_HTML_Processor::step_in_foreign_content(): bool

## In this article

 * [Description](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#description)
    - [See also](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#see-also)
 * [Return](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#return)
 * [Source](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#source)
 * [Related](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#related)
 * [Changelog](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#changelog)

[ Back to top](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#wp--skip-link--target)

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 foreign content’ insertion mode.

## 󠀁[Description](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#description)󠁿

This internal function performs the ‘in foreign content’ insertion mode logic for
the generalized [WP_HTML_Processor::step()](https://developer.wordpress.org/reference/classes/wp_html_processor/step/)
function.

### 󠀁[See also](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#see-also)󠁿

 * [https://html.spec.whatwg.org/#parsing-main-inforeign](https://html.spec.whatwg.org/#parsing-main-inforeign/)
 * [WP_HTML_Processor::step](https://developer.wordpress.org/reference/classes/WP_HTML_Processor/step/)

## 󠀁[Return](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#return)󠁿

 bool Whether an element was found.

## 󠀁[Source](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#source)󠁿

    ```php
    private function step_in_foreign_content(): bool {
    	$tag_name   = $this->get_token_name();
    	$token_type = $this->get_token_type();
    	$op_sigil   = '#tag' === $token_type ? ( $this->is_tag_closer() ? '-' : '+' ) : '';
    	$op         = "{$op_sigil}{$tag_name}";

    	/*
    	 * > A start tag whose name is "font", if the token has any attributes named "color", "face", or "size"
    	 *
    	 * This section drawn out above the switch to more easily incorporate
    	 * the additional rules based on the presence of the attributes.
    	 */
    	if (
    		'+FONT' === $op &&
    		(
    			null !== $this->get_attribute( 'color' ) ||
    			null !== $this->get_attribute( 'face' ) ||
    			null !== $this->get_attribute( 'size' )
    		)
    	) {
    		$op = '+FONT with attributes';
    	}

    	switch ( $op ) {
    		case '#text':
    			/*
    			 * > A character token that is U+0000 NULL
    			 *
    			 * This is handled by `get_modifiable_text()`.
    			 */

    			/*
    			 * 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_foreign_element( $this->state->current_token, false );
    			return true;

    		/*
    		 * CDATA sections are alternate wrappers for text content and therefore
    		 * ought to follow the same rules as text nodes.
    		 */
    		case '#cdata-section':
    			/*
    			 * NULL bytes and whitespace do not change the frameset-ok flag.
    			 */
    			$current_token        = $this->bookmarks[ $this->state->current_token->bookmark_name ];
    			$cdata_content_start  = $current_token->start + 9;
    			$cdata_content_length = $current_token->length - 12;
    			if ( strspn( $this->html, "\0 \t\n\f\r", $cdata_content_start, $cdata_content_length ) !== $cdata_content_length ) {
    				$this->state->frameset_ok = false;
    			}

    			$this->insert_foreign_element( $this->state->current_token, false );
    			return true;

    		/*
    		 * > A comment token
    		 */
    		case '#comment':
    		case '#funky-comment':
    		case '#presumptuous-tag':
    			$this->insert_foreign_element( $this->state->current_token, false );
    			return true;

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

    		/*
    		 * > A start tag whose tag name is "b", "big", "blockquote", "body", "br", "center",
    		 * > "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", "h4", "h5",
    		 * > "h6", "head", "hr", "i", "img", "li", "listing", "menu", "meta", "nobr", "ol",
    		 * > "p", "pre", "ruby", "s", "small", "span", "strong", "strike", "sub", "sup",
    		 * > "table", "tt", "u", "ul", "var"
    		 *
    		 * > A start tag whose name is "font", if the token has any attributes named "color", "face", or "size"
    		 *
    		 * > An end tag whose tag name is "br", "p"
    		 *
    		 * Closing BR tags are always reported by the Tag Processor as opening tags.
    		 */
    		case '+B':
    		case '+BIG':
    		case '+BLOCKQUOTE':
    		case '+BODY':
    		case '+BR':
    		case '+CENTER':
    		case '+CODE':
    		case '+DD':
    		case '+DIV':
    		case '+DL':
    		case '+DT':
    		case '+EM':
    		case '+EMBED':
    		case '+H1':
    		case '+H2':
    		case '+H3':
    		case '+H4':
    		case '+H5':
    		case '+H6':
    		case '+HEAD':
    		case '+HR':
    		case '+I':
    		case '+IMG':
    		case '+LI':
    		case '+LISTING':
    		case '+MENU':
    		case '+META':
    		case '+NOBR':
    		case '+OL':
    		case '+P':
    		case '+PRE':
    		case '+RUBY':
    		case '+S':
    		case '+SMALL':
    		case '+SPAN':
    		case '+STRONG':
    		case '+STRIKE':
    		case '+SUB':
    		case '+SUP':
    		case '+TABLE':
    		case '+TT':
    		case '+U':
    		case '+UL':
    		case '+VAR':
    		case '+FONT with attributes':
    		case '-BR':
    		case '-P':
    			// @todo Indicate a parse error once it's possible.
    			foreach ( $this->state->stack_of_open_elements->walk_up() as $current_node ) {
    				if (
    					'math' === $current_node->integration_node_type ||
    					'html' === $current_node->integration_node_type ||
    					'html' === $current_node->namespace
    				) {
    					break;
    				}

    				$this->state->stack_of_open_elements->pop();
    			}
    			goto in_foreign_content_process_in_current_insertion_mode;
    	}

    	/*
    	 * > Any other start tag
    	 */
    	if ( ! $this->is_tag_closer() ) {
    		$this->insert_foreign_element( $this->state->current_token, false );

    		/*
    		 * > If the token has its self-closing flag set, then run
    		 * > the appropriate steps from the following list:
    		 * >
    		 * >   ↪ the token's tag name is "script", and the new current node is in the SVG namespace
    		 * >         Acknowledge the token's self-closing flag, and then act as
    		 * >         described in the steps for a "script" end tag below.
    		 * >
    		 * >   ↪ Otherwise
    		 * >         Pop the current node off the stack of open elements and
    		 * >         acknowledge the token's self-closing flag.
    		 *
    		 * Since the rules for SCRIPT below indicate to pop the element off of the stack of
    		 * open elements, which is the same for the Otherwise condition, there's no need to
    		 * separate these checks. The difference comes when a parser operates with the scripting
    		 * flag enabled, and executes the script, which this parser does not support.
    		 */
    		if ( $this->state->current_token->has_self_closing_flag ) {
    			$this->state->stack_of_open_elements->pop();
    		}
    		return true;
    	}

    	/*
    	 * > An end tag whose name is "script", if the current node is an SVG script element.
    	 */
    	if ( $this->is_tag_closer() && 'SCRIPT' === $this->state->current_token->node_name && 'svg' === $this->state->current_token->namespace ) {
    		$this->state->stack_of_open_elements->pop();
    		return true;
    	}

    	/*
    	 * > Any other end tag
    	 */
    	if ( $this->is_tag_closer() ) {
    		$node = $this->state->stack_of_open_elements->current_node();
    		if ( $tag_name !== $node->node_name ) {
    			// @todo Indicate a parse error once it's possible.
    		}
    		in_foreign_content_end_tag_loop:
    		if ( $node === $this->state->stack_of_open_elements->at( 1 ) ) {
    			return true;
    		}

    		/*
    		 * > If node's tag name, converted to ASCII lowercase, is the same as the tag name
    		 * > of the token, pop elements from the stack of open elements until node has
    		 * > been popped from the stack, and then return.
    		 */
    		if ( 0 === strcasecmp( $node->node_name, $tag_name ) ) {
    			foreach ( $this->state->stack_of_open_elements->walk_up() as $item ) {
    				$this->state->stack_of_open_elements->pop();
    				if ( $node === $item ) {
    					return true;
    				}
    			}
    		}

    		foreach ( $this->state->stack_of_open_elements->walk_up( $node ) as $item ) {
    			$node = $item;
    			break;
    		}

    		if ( 'html' !== $node->namespace ) {
    			goto in_foreign_content_end_tag_loop;
    		}

    		in_foreign_content_process_in_current_insertion_mode:
    		switch ( $this->state->insertion_mode ) {
    			case WP_HTML_Processor_State::INSERTION_MODE_INITIAL:
    				return $this->step_initial();

    			case WP_HTML_Processor_State::INSERTION_MODE_BEFORE_HTML:
    				return $this->step_before_html();

    			case WP_HTML_Processor_State::INSERTION_MODE_BEFORE_HEAD:
    				return $this->step_before_head();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_HEAD:
    				return $this->step_in_head();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_HEAD_NOSCRIPT:
    				return $this->step_in_head_noscript();

    			case WP_HTML_Processor_State::INSERTION_MODE_AFTER_HEAD:
    				return $this->step_after_head();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_BODY:
    				return $this->step_in_body();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE:
    				return $this->step_in_table();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_TEXT:
    				return $this->step_in_table_text();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_CAPTION:
    				return $this->step_in_caption();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_COLUMN_GROUP:
    				return $this->step_in_column_group();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_TABLE_BODY:
    				return $this->step_in_table_body();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_ROW:
    				return $this->step_in_row();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_CELL:
    				return $this->step_in_cell();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_SELECT:
    				return $this->step_in_select();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_SELECT_IN_TABLE:
    				return $this->step_in_select_in_table();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_TEMPLATE:
    				return $this->step_in_template();

    			case WP_HTML_Processor_State::INSERTION_MODE_AFTER_BODY:
    				return $this->step_after_body();

    			case WP_HTML_Processor_State::INSERTION_MODE_IN_FRAMESET:
    				return $this->step_in_frameset();

    			case WP_HTML_Processor_State::INSERTION_MODE_AFTER_FRAMESET:
    				return $this->step_after_frameset();

    			case WP_HTML_Processor_State::INSERTION_MODE_AFTER_AFTER_BODY:
    				return $this->step_after_after_body();

    			case WP_HTML_Processor_State::INSERTION_MODE_AFTER_AFTER_FRAMESET:
    				return $this->step_after_after_frameset();

    			// This should be unreachable but PHP doesn't have total type checking on switch.
    			default:
    				$this->bail( "Unaware of the requested parsing mode: '{$this->state->insertion_mode}'." );
    		}
    	}

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

[View all references](https://developer.wordpress.org/reference/files/wp-includes/html-api/class-wp-html-processor.php/)
[View on Trac](https://core.trac.wordpress.org/browser/tags/6.9.4/src/wp-includes/html-api/class-wp-html-processor.php#L4789)
[View on GitHub](https://github.com/WordPress/wordpress-develop/blob/6.9.4/src/wp-includes/html-api/class-wp-html-processor.php#L4789-L5091)

## 󠀁[Related](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#related)󠁿

| Uses | Description | 
| [WP_HTML_Processor::insert_foreign_element()](https://developer.wordpress.org/reference/classes/wp_html_processor/insert_foreign_element/)`wp-includes/html-api/class-wp-html-processor.php` |

Inserts a foreign element on to the stack of open elements.

  | 
| [WP_HTML_Processor::step_after_head()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_after_head/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘after head’ insertion mode.

  | 
| [WP_HTML_Processor::step_in_table()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_table/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_in_table_text()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_table_text/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘in table text’ insertion mode.

  | 
| [WP_HTML_Processor::step_in_caption()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_caption/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_in_column_group()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_column_group/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘in column group’ insertion mode.

  | 
| [WP_HTML_Processor::step_in_table_body()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_table_body/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_in_row()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_row/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_in_cell()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_cell/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_in_select()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_select/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_in_select_in_table()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_select_in_table/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘in select in table’ insertion mode.

  | 
| [WP_HTML_Processor::step_in_template()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_template/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_after_body()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_after_body/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_in_frameset()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_frameset/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_after_frameset()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_after_frameset/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘after frameset’ insertion mode.

  | 
| [WP_HTML_Processor::step_after_after_body()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_after_after_body/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_after_after_frameset()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_after_after_frameset/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘after after frameset’ insertion mode.

  | 
| [WP_HTML_Processor::step_initial()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_initial/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘initial’ insertion mode.

  | 
| [WP_HTML_Processor::step_before_html()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_before_html/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘before html’ insertion mode.

  | 
| [WP_HTML_Processor::step_before_head()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_before_head/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘before head’ insertion mode.

  | 
| [WP_HTML_Processor::step_in_head()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_head/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  | 
| [WP_HTML_Processor::step_in_head_noscript()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_head_noscript/)`wp-includes/html-api/class-wp-html-processor.php` |

Parses next element in the ‘in head noscript’ insertion mode.

  | 
| [WP_HTML_Processor::bail()](https://developer.wordpress.org/reference/classes/wp_html_processor/bail/)`wp-includes/html-api/class-wp-html-processor.php` |

Stops the parser and terminates its execution when encountering unsupported markup.

  | 
| [WP_HTML_Processor::get_attribute()](https://developer.wordpress.org/reference/classes/wp_html_processor/get_attribute/)`wp-includes/html-api/class-wp-html-processor.php` |

Returns the value of a requested attribute from a matched tag opener if that attribute exists.

  | 
| [WP_HTML_Processor::get_token_name()](https://developer.wordpress.org/reference/classes/wp_html_processor/get_token_name/)`wp-includes/html-api/class-wp-html-processor.php` |

Returns the node name represented by the token.

  | 
| [WP_HTML_Processor::get_token_type()](https://developer.wordpress.org/reference/classes/wp_html_processor/get_token_type/)`wp-includes/html-api/class-wp-html-processor.php` |

Indicates the kind of matched token, if any.

  | 
| [WP_HTML_Processor::is_tag_closer()](https://developer.wordpress.org/reference/classes/wp_html_processor/is_tag_closer/)`wp-includes/html-api/class-wp-html-processor.php` |

Indicates if the current tag token is a tag closer.

  | 
| [WP_HTML_Processor::step()](https://developer.wordpress.org/reference/classes/wp_html_processor/step/)`wp-includes/html-api/class-wp-html-processor.php` |

Steps through the HTML document and stop at the next tag, if any.

  | 
| [WP_HTML_Processor::step_in_body()](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_body/)`wp-includes/html-api/class-wp-html-processor.php` |

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

  |

[Show 24 more](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#)
[Show less](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#)

| Used by | Description | 
| [WP_HTML_Processor::step()](https://developer.wordpress.org/reference/classes/wp_html_processor/step/)`wp-includes/html-api/class-wp-html-processor.php` |

Steps through the HTML document and stop at the next tag, if any.

  |

## 󠀁[Changelog](https://developer.wordpress.org/reference/classes/wp_html_processor/step_in_foreign_content/?output_format=md#changelog)󠁿

| Version | Description | 
| [6.7.0](https://developer.wordpress.org/reference/since/6.7.0/) | Introduced. |

## User Contributed Notes

You must [log in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Fclasses%2Fwp_html_processor%2Fstep_in_foreign_content%2F)
before being able to contribute a note or feedback.