Parses next element in the ‘in foreign content’ insertion mode.
Description
This internal function performs the ‘in foreign content’ insertion mode logic for the generalized WP_HTML_Processor::step() function.
See also
Source
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;
}
Changelog
| Version | Description |
|---|---|
| 6.7.0 | Introduced. |
User Contributed Notes
You must log in before being able to contribute a note or feedback.