LibWeb: Relax HTML parser to allow more tags inside <select>
Some checks failed
Close stale PRs / stale (push) Has been cancelled
Package the js repl as a binary artifact / Linux, arm64 (push) Has been cancelled
Package the js repl as a binary artifact / macOS, arm64 (push) Has been cancelled
Package the js repl as a binary artifact / Linux, x86_64 (push) Has been cancelled
Label PRs with merge conflicts / auto-labeler (push) Has been cancelled

This implements parsing part of customizable <select> spec update.
See whatwg/html PR #10548.

Two failing subtests in `html5lib_innerHTML_tests_innerHTML_1.html`
and `customizable-select/select-parsing.html` are due to the spec
still disallowing `<input>` inside `<select>`, even though Chrome
has already implemented this behavoir (see whatwg/html#11288).
This commit is contained in:
Feng Yu
2025-10-23 20:44:10 -07:00
committed by Sam Atkins
parent 315ffb91af
commit d2029b1814
Notes: github-actions[bot] 2025-12-04 17:18:25 +00:00
17 changed files with 379 additions and 384 deletions

View File

@@ -484,12 +484,6 @@ void HTMLParser::process_using_the_rules_for(InsertionMode mode, HTMLToken& toke
case InsertionMode::InTableText: case InsertionMode::InTableText:
handle_in_table_text(token); handle_in_table_text(token);
break; break;
case InsertionMode::InSelectInTable:
handle_in_select_in_table(token);
break;
case InsertionMode::InSelect:
handle_in_select(token);
break;
case InsertionMode::InCaption: case InsertionMode::InCaption:
handle_in_caption(token); handle_in_caption(token);
break; break;
@@ -1570,12 +1564,14 @@ void HTMLParser::handle_after_head(HTMLToken& token)
} }
} }
// https://html.spec.whatwg.org/multipage/parsing.html#generate-implied-end-tags
void HTMLParser::generate_implied_end_tags(FlyString const& exception) void HTMLParser::generate_implied_end_tags(FlyString const& exception)
{ {
while (current_node()->local_name() != exception && current_node()->local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc)) while (current_node()->local_name() != exception && current_node()->local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc))
(void)m_stack_of_open_elements.pop(); (void)m_stack_of_open_elements.pop();
} }
// https://html.spec.whatwg.org/multipage/parsing.html#generate-implied-end-tags
void HTMLParser::generate_all_implied_end_tags_thoroughly() void HTMLParser::generate_all_implied_end_tags_thoroughly()
{ {
while (current_node()->local_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::colgroup, HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr)) while (current_node()->local_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::colgroup, HTML::TagNames::dd, HTML::TagNames::dt, HTML::TagNames::li, HTML::TagNames::optgroup, HTML::TagNames::option, HTML::TagNames::p, HTML::TagNames::rb, HTML::TagNames::rp, HTML::TagNames::rt, HTML::TagNames::rtc, HTML::TagNames::tbody, HTML::TagNames::td, HTML::TagNames::tfoot, HTML::TagNames::th, HTML::TagNames::thead, HTML::TagNames::tr))
@@ -2450,8 +2446,10 @@ void HTMLParser::handle_in_body(HTMLToken& token)
return; return;
} }
// -> 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" // -> An end tag whose tag name is one of: "address", "article", "aside", "blockquote", "button", "center",
if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::address, HTML::TagNames::article, HTML::TagNames::aside, HTML::TagNames::blockquote, HTML::TagNames::button, HTML::TagNames::center, HTML::TagNames::details, HTML::TagNames::dialog, HTML::TagNames::dir, HTML::TagNames::div, HTML::TagNames::dl, HTML::TagNames::fieldset, HTML::TagNames::figcaption, HTML::TagNames::figure, HTML::TagNames::footer, HTML::TagNames::header, HTML::TagNames::hgroup, HTML::TagNames::listing, HTML::TagNames::main, HTML::TagNames::menu, HTML::TagNames::nav, HTML::TagNames::ol, HTML::TagNames::pre, HTML::TagNames::search, HTML::TagNames::section, HTML::TagNames::summary, HTML::TagNames::ul)) { // "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup",
// "listing", "main", "menu", "nav", "ol", "pre", "search", "section", "select", "summary", "ul"
if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::address, HTML::TagNames::article, HTML::TagNames::aside, HTML::TagNames::blockquote, HTML::TagNames::button, HTML::TagNames::center, HTML::TagNames::details, HTML::TagNames::dialog, HTML::TagNames::dir, HTML::TagNames::div, HTML::TagNames::dl, HTML::TagNames::fieldset, HTML::TagNames::figcaption, HTML::TagNames::figure, HTML::TagNames::footer, HTML::TagNames::header, HTML::TagNames::hgroup, HTML::TagNames::listing, HTML::TagNames::main, HTML::TagNames::menu, HTML::TagNames::nav, HTML::TagNames::ol, HTML::TagNames::pre, HTML::TagNames::search, HTML::TagNames::section, HTML::TagNames::select, HTML::TagNames::summary, HTML::TagNames::ul)) {
// 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. // 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.
if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) { if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) {
log_parse_error(); log_parse_error();
@@ -2766,6 +2764,26 @@ void HTMLParser::handle_in_body(HTMLToken& token)
// -> A start tag whose tag name is "input" // -> A start tag whose tag name is "input"
if (token.is_start_tag() && token.tag_name() == HTML::TagNames::input) { if (token.is_start_tag() && token.tag_name() == HTML::TagNames::input) {
// If the parser was created as part of the HTML fragment parsing algorithm (fragment case) and the context
// element passed to that algorithm is a select element:
if (m_parsing_fragment && m_context_element->local_name() == HTML::TagNames::select) {
// 1. Parse error.
// 2. Ignore the token.
log_parse_error();
// 3. Return.
return;
}
// If the stack of open elements has a select element in scope:
if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::select)) {
// 1. Parse error.
log_parse_error();
// 2. Pop elements from the stack of open elements until a select element has been popped from the stack.
m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
}
// Reconstruct the active formatting elements, if any. // Reconstruct the active formatting elements, if any.
reconstruct_the_active_formatting_elements(); reconstruct_the_active_formatting_elements();
@@ -2801,6 +2819,17 @@ void HTMLParser::handle_in_body(HTMLToken& token)
if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p)) if (m_stack_of_open_elements.has_in_button_scope(HTML::TagNames::p))
close_a_p_element(); close_a_p_element();
// If the stack of open elements has a select element in scope:
if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::select)) {
// 1. Generate implied end tags.
generate_implied_end_tags();
// 2. If the stack of open elements has an option element in scope or has an optgroup element in scope, then
// this is a parse error.
if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::option) || m_stack_of_open_elements.has_in_scope(HTML::TagNames::optgroup))
log_parse_error();
}
// Insert an HTML element for the token. Immediately pop the current node off the stack of open elements. // Insert an HTML element for the token. Immediately pop the current node off the stack of open elements.
(void)insert_html_element(token); (void)insert_html_element(token);
(void)m_stack_of_open_elements.pop(); (void)m_stack_of_open_elements.pop();
@@ -2884,36 +2913,77 @@ void HTMLParser::handle_in_body(HTMLToken& token)
// -> A start tag whose tag name is "select" // -> A start tag whose tag name is "select"
if (token.is_start_tag() && token.tag_name() == HTML::TagNames::select) { if (token.is_start_tag() && token.tag_name() == HTML::TagNames::select) {
// If the parser was created as part of the HTML fragment parsing algorithm (fragment case)
// and the context element passed to that algorithm is a select element:
if (m_parsing_fragment && m_context_element->local_name() == HTML::TagNames::select) {
// 1. Parse error.
// 2. Ignore the token.
log_parse_error();
}
// Otherwise, if the stack of open elements has a select element in scope:
else if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::select)) {
// 1. Parse error.
// 2. Ignore the token.
log_parse_error();
// 3. Pop elements from the stack of open elements until a select element has been popped from the stack.
m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
}
// Otherwise:
else {
// 1. Reconstruct the active formatting elements, if any.
reconstruct_the_active_formatting_elements();
// 2. Insert an HTML element for the token.
(void)insert_html_element(token);
// 3. Set the frameset-ok flag to "not ok".
m_frameset_ok = false;
}
return;
}
// -> A start tag whose tag name is "option"
if (token.is_start_tag() && token.tag_name() == HTML::TagNames::option) {
// If the stack of open elements has a select element in scope:
if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::select)) {
// 1. Generate implied end tags except for optgroup elements.
generate_implied_end_tags(HTML::TagNames::optgroup);
// 2. If the stack of open elements has an option element in scope, then this is a parse error.
if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::option))
log_parse_error();
}
// Otherwise, if the current node is an option element, then pop the current node from the stack of open elements.
else if (current_node()->local_name() == HTML::TagNames::option) {
(void)m_stack_of_open_elements.pop();
}
// Reconstruct the active formatting elements, if any. // Reconstruct the active formatting elements, if any.
reconstruct_the_active_formatting_elements(); reconstruct_the_active_formatting_elements();
// Insert an HTML element for the token. // Insert an HTML element for the token.
(void)insert_html_element(token); (void)insert_html_element(token);
// Set the frameset-ok flag to "not ok".
m_frameset_ok = false;
// 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". Otherwise, switch the insertion mode to "in select".
switch (m_insertion_mode) {
case InsertionMode::InTable:
case InsertionMode::InCaption:
case InsertionMode::InTableBody:
case InsertionMode::InRow:
case InsertionMode::InCell:
m_insertion_mode = InsertionMode::InSelectInTable;
break;
default:
m_insertion_mode = InsertionMode::InSelect;
break;
}
return; return;
} }
// -> A start tag whose tag name is one of: "optgroup", "option" // -> A start tag whose tag name is "optgroup"
if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::optgroup, HTML::TagNames::option)) { if (token.is_start_tag() && token.tag_name() == HTML::TagNames::optgroup) {
// If the current node is an option element, then pop the current node off the stack of open elements.
if (current_node()->local_name() == HTML::TagNames::option) // If the stack of open elements has a select element in scope:
if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::select)) {
// 1. Generate implied end tags.
generate_implied_end_tags();
// 2. If the stack of open elements has an option element in scope or has an optgroup element in scope, then
// this is a parse error.
if (m_stack_of_open_elements.has_in_scope(HTML::TagNames::option) || m_stack_of_open_elements.has_in_scope(HTML::TagNames::optgroup))
log_parse_error();
}
// Otherwise, if the current node is an option element, then pop the current node from the stack of open elements.
else if (current_node()->local_name() == HTML::TagNames::option) {
(void)m_stack_of_open_elements.pop(); (void)m_stack_of_open_elements.pop();
}
// Reconstruct the active formatting elements, if any. // Reconstruct the active formatting elements, if any.
reconstruct_the_active_formatting_elements(); reconstruct_the_active_formatting_elements();
@@ -3939,250 +4009,6 @@ void HTMLParser::handle_in_table(HTMLToken& token)
} }
} }
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inselectintable
void HTMLParser::handle_in_select_in_table(HTMLToken& token)
{
// -> A start tag whose tag name is one of: "caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"
if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::td, HTML::TagNames::th)) {
// Parse error.
log_parse_error();
// Pop elements from the stack of open elements until a select element has been popped from the stack.
m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
// Reset the insertion mode appropriately.
reset_the_insertion_mode_appropriately();
// Reprocess the token.
process_using_the_rules_for(m_insertion_mode, token);
return;
}
// -> An end tag whose tag name is one of: "caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"
if (token.is_end_tag() && token.tag_name().is_one_of(HTML::TagNames::caption, HTML::TagNames::table, HTML::TagNames::tbody, HTML::TagNames::tfoot, HTML::TagNames::thead, HTML::TagNames::tr, HTML::TagNames::td, HTML::TagNames::th)) {
// Parse error.
log_parse_error();
// If the stack of open elements does not have an element in table scope that is an HTML element with the same
// tag name as that of the token, then ignore the token.
if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name()))
return;
// Otherwise:
// 1. Pop elements from the stack of open elements until a select element has been popped from the stack.
m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
// 2. Reset the insertion mode appropriately.
reset_the_insertion_mode_appropriately();
// 3. Reprocess the token.
process_using_the_rules_for(m_insertion_mode, token);
return;
}
// -> Anything else
// Process the token using the rules for the "in select" insertion mode.
process_using_the_rules_for(InsertionMode::InSelect, token);
}
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inselect
void HTMLParser::handle_in_select(HTMLToken& token)
{
if (token.is_character()) {
// -> A character token that is U+0000 NULL
if (token.code_point() == 0) {
// Parse error. Ignore the token.
log_parse_error();
return;
}
// -> Any other character token
// Insert the token's character.
insert_character(token.code_point());
return;
}
// -> A comment token
if (token.is_comment()) {
// Insert a comment.
insert_comment(token);
return;
}
// -> A DOCTYPE token
if (token.is_doctype()) {
// Parse error. Ignore the token.
log_parse_error();
return;
}
// -> A start tag whose tag name is "html"
if (token.is_start_tag() && token.tag_name() == HTML::TagNames::html) {
// Process the token using the rules for the "in body" insertion mode.
process_using_the_rules_for(InsertionMode::InBody, token);
return;
}
// -> A start tag whose tag name is "option"
if (token.is_start_tag() && token.tag_name() == HTML::TagNames::option) {
// If the current node is an option element, pop that node from the stack of open elements.
if (current_node()->local_name() == HTML::TagNames::option)
(void)m_stack_of_open_elements.pop();
// Insert an HTML element for the token.
(void)insert_html_element(token);
return;
}
// -> A start tag whose tag name is "optgroup"
if (token.is_start_tag() && token.tag_name() == HTML::TagNames::optgroup) {
// If the current node is an option element, pop that node from the stack of open elements.
if (current_node()->local_name() == HTML::TagNames::option)
(void)m_stack_of_open_elements.pop();
// If the current node is an optgroup element, pop that node from the stack of open elements.
if (current_node()->local_name() == HTML::TagNames::optgroup)
(void)m_stack_of_open_elements.pop();
// Insert an HTML element for the token.
(void)insert_html_element(token);
return;
}
// -> A start tag whose tag name is "hr"
if (token.is_start_tag() && token.tag_name() == HTML::TagNames::hr) {
// If the current node is an option element, pop that node from the stack of open elements.
if (current_node()->local_name() == HTML::TagNames::option)
(void)m_stack_of_open_elements.pop();
// If the current node is an optgroup element, pop that node from the stack of open elements.
if (current_node()->local_name() == HTML::TagNames::optgroup)
(void)m_stack_of_open_elements.pop();
// Insert an HTML element for the token. Immediately pop the current node off the stack of open elements.
(void)insert_html_element(token);
(void)m_stack_of_open_elements.pop();
// Acknowledge the token's self-closing flag, if it is set.
token.acknowledge_self_closing_flag_if_set();
return;
}
// -> An end tag whose tag name is "optgroup"
if (token.is_end_tag() && token.tag_name() == HTML::TagNames::optgroup) {
// First, if the current node is an option element, and the node immediately before it in the stack of open
// elements is an optgroup element, then pop the current node from the stack of open elements.
if (current_node()->local_name() == HTML::TagNames::option && node_before_current_node()->local_name() == HTML::TagNames::optgroup)
(void)m_stack_of_open_elements.pop();
// If the current node is an optgroup element, then pop that node from the stack of open elements.
// Otherwise, this is a parse error; ignore the token.
if (current_node()->local_name() == HTML::TagNames::optgroup) {
(void)m_stack_of_open_elements.pop();
} else {
log_parse_error();
return;
}
return;
}
// -> An end tag whose tag name is "option"
if (token.is_end_tag() && token.tag_name() == HTML::TagNames::option) {
// If the current node is an option element, then pop that node from the stack of open elements.
// Otherwise, this is a parse error; ignore the token.
if (current_node()->local_name() == HTML::TagNames::option) {
(void)m_stack_of_open_elements.pop();
} else {
log_parse_error();
return;
}
return;
}
// -> An end tag whose tag name is "select"
if (token.is_end_tag() && token.tag_name() == HTML::TagNames::select) {
// If the stack of open elements does not have a select element in select scope, this is a parse error; ignore
// the token. (fragment case)
if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) {
VERIFY(m_parsing_fragment);
log_parse_error();
return;
}
// Otherwise:
// 1. Pop elements from the stack of open elements until a select element has been popped from the stack.
m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
// 2. Reset the insertion mode appropriately.
reset_the_insertion_mode_appropriately();
return;
}
// -> A start tag whose tag name is "select"
if (token.is_start_tag() && token.tag_name() == HTML::TagNames::select) {
// Parse error.
log_parse_error();
// If the stack of open elements does not have a select element in select scope, ignore the token. (fragment case)
if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) {
VERIFY(m_parsing_fragment);
return;
}
// Otherwise:
// 1. Pop elements from the stack of open elements until a select element has been popped from the stack.
m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
// 2. Reset the insertion mode appropriately.
reset_the_insertion_mode_appropriately();
return;
}
// -> A start tag whose tag name is one of: "input", "keygen", "textarea"
if (token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::input, HTML::TagNames::keygen, HTML::TagNames::textarea)) {
// Parse error.
log_parse_error();
// If the stack of open elements does not have a select element in select scope, ignore the token. (fragment case)
if (!m_stack_of_open_elements.has_in_select_scope(HTML::TagNames::select)) {
VERIFY(m_parsing_fragment);
return;
}
// Otherwise:
// 1. Pop elements from the stack of open elements until a select element has been popped from the stack.
m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped(HTML::TagNames::select);
// 2. Reset the insertion mode appropriately.
reset_the_insertion_mode_appropriately();
// 3. Reprocess the token.
process_using_the_rules_for(m_insertion_mode, token);
return;
}
// -> A start tag whose tag name is one of: "script", "template"
// -> An end tag whose tag name is "template"
if ((token.is_start_tag() && token.tag_name().is_one_of(HTML::TagNames::script, HTML::TagNames::template_))
|| (token.is_end_tag() && token.tag_name() == HTML::TagNames::template_)) {
// Process the token using the rules for the "in head" insertion mode.
process_using_the_rules_for(InsertionMode::InHead, token);
return;
}
// -> An end-of-file token
if (token.is_end_of_file()) {
// Process the token using the rules for the "in body" insertion mode.
process_using_the_rules_for(InsertionMode::InBody, token);
return;
}
// -> Anything else
// Parse error. Ignore the token.
log_parse_error();
}
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-incaption // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-incaption
void HTMLParser::handle_in_caption(HTMLToken& token) void HTMLParser::handle_in_caption(HTMLToken& token)
{ {
@@ -4885,97 +4711,68 @@ void HTMLParser::reset_the_insertion_mode_appropriately()
// NOTE: The following steps only apply to HTML elements, so skip ones that aren't. // NOTE: The following steps only apply to HTML elements, so skip ones that aren't.
if (node->namespace_uri() == Namespace::HTML) { if (node->namespace_uri() == Namespace::HTML) {
// 4. If node is a select element, run these substeps: // 4. If node is a td or th element and last is false, then switch the insertion mode to "in cell" and return.
if (node->local_name() == HTML::TagNames::select) {
// 1. If last is true, jump to the step below labeled done.
if (!last) {
// 2. Let ancestor be node.
// 3. Loop: If ancestor is the first node in the stack of open elements, jump to the step below labeled done.
for (ssize_t j = i; j > 0; --j) {
// 4. Let ancestor be the node before ancestor in the stack of open elements.
auto& ancestor = m_stack_of_open_elements.elements().at(j - 1);
// 5. If ancestor is a template node, jump to the step below labeled done.
if (is<HTMLTemplateElement>(*ancestor))
break;
// 6. If ancestor is a table node, switch the insertion mode to "in select in table" and return.
if (is<HTMLTableElement>(*ancestor)) {
m_insertion_mode = InsertionMode::InSelectInTable;
return;
}
// 7. Jump back to the step labeled loop.
}
}
// 8. Done: Switch the insertion mode to "in select" and return.
m_insertion_mode = InsertionMode::InSelect;
return;
}
// 5. If node is a td or th element and last is false, then switch the insertion mode to "in cell" and return.
if (!last && node->local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) { if (!last && node->local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th)) {
m_insertion_mode = InsertionMode::InCell; m_insertion_mode = InsertionMode::InCell;
return; return;
} }
// 6. If node is a tr element, then switch the insertion mode to "in row" and return. // 5. If node is a tr element, then switch the insertion mode to "in row" and return.
if (node->local_name() == HTML::TagNames::tr) { if (node->local_name() == HTML::TagNames::tr) {
m_insertion_mode = InsertionMode::InRow; m_insertion_mode = InsertionMode::InRow;
return; return;
} }
// 7. If node is a tbody, thead, or tfoot element, then switch the insertion mode to "in table body" and return. // 6. If node is a tbody, thead, or tfoot element, then switch the insertion mode to "in table body" and return.
if (node->local_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::thead, HTML::TagNames::tfoot)) { if (node->local_name().is_one_of(HTML::TagNames::tbody, HTML::TagNames::thead, HTML::TagNames::tfoot)) {
m_insertion_mode = InsertionMode::InTableBody; m_insertion_mode = InsertionMode::InTableBody;
return; return;
} }
// 8. If node is a caption element, then switch the insertion mode to "in caption" and return. // 7. If node is a caption element, then switch the insertion mode to "in caption" and return.
if (node->local_name() == HTML::TagNames::caption) { if (node->local_name() == HTML::TagNames::caption) {
m_insertion_mode = InsertionMode::InCaption; m_insertion_mode = InsertionMode::InCaption;
return; return;
} }
// 9. If node is a colgroup element, then switch the insertion mode to "in column group" and return. // 8. If node is a colgroup element, then switch the insertion mode to "in column group" and return.
if (node->local_name() == HTML::TagNames::colgroup) { if (node->local_name() == HTML::TagNames::colgroup) {
m_insertion_mode = InsertionMode::InColumnGroup; m_insertion_mode = InsertionMode::InColumnGroup;
return; return;
} }
// 10. If node is a table element, then switch the insertion mode to "in table" and return. // 9. If node is a table element, then switch the insertion mode to "in table" and return.
if (node->local_name() == HTML::TagNames::table) { if (node->local_name() == HTML::TagNames::table) {
m_insertion_mode = InsertionMode::InTable; m_insertion_mode = InsertionMode::InTable;
return; return;
} }
// 11. If node is a template element, then switch the insertion mode to the current template insertion mode and return. // 10. If node is a template element, then switch the insertion mode to the current template insertion mode and return.
if (node->local_name() == HTML::TagNames::template_) { if (node->local_name() == HTML::TagNames::template_) {
m_insertion_mode = m_stack_of_template_insertion_modes.last(); m_insertion_mode = m_stack_of_template_insertion_modes.last();
return; return;
} }
// 12. If node is a head element and last is false, then switch the insertion mode to "in head" and return. // 11. If node is a head element and last is false, then switch the insertion mode to "in head" and return.
if (!last && node->local_name() == HTML::TagNames::head) { if (!last && node->local_name() == HTML::TagNames::head) {
m_insertion_mode = InsertionMode::InHead; m_insertion_mode = InsertionMode::InHead;
return; return;
} }
// 13. If node is a body element, then switch the insertion mode to "in body" and return. // 12. If node is a body element, then switch the insertion mode to "in body" and return.
if (node->local_name() == HTML::TagNames::body) { if (node->local_name() == HTML::TagNames::body) {
m_insertion_mode = InsertionMode::InBody; m_insertion_mode = InsertionMode::InBody;
return; return;
} }
// 14. If node is a frameset element, then switch the insertion mode to "in frameset" and return. (fragment case) // 13. If node is a frameset element, then switch the insertion mode to "in frameset" and return. (fragment case)
if (node->local_name() == HTML::TagNames::frameset) { if (node->local_name() == HTML::TagNames::frameset) {
VERIFY(m_parsing_fragment); VERIFY(m_parsing_fragment);
m_insertion_mode = InsertionMode::InFrameset; m_insertion_mode = InsertionMode::InFrameset;
return; return;
} }
// 15. If node is an html element, run these substeps: // 14. If node is an html element, run these substeps:
if (node->local_name() == HTML::TagNames::html) { if (node->local_name() == HTML::TagNames::html) {
// 1. If the head element pointer is null, switch the insertion mode to "before head" and return. (fragment case) // 1. If the head element pointer is null, switch the insertion mode to "before head" and return. (fragment case)
if (!m_head_element) { if (!m_head_element) {
@@ -4990,15 +4787,15 @@ void HTMLParser::reset_the_insertion_mode_appropriately()
} }
} }
// 16. If last is true, then switch the insertion mode to "in body" and return. (fragment case) // 15. If last is true, then switch the insertion mode to "in body" and return. (fragment case)
if (last) { if (last) {
VERIFY(m_parsing_fragment); VERIFY(m_parsing_fragment);
m_insertion_mode = InsertionMode::InBody; m_insertion_mode = InsertionMode::InBody;
return; return;
} }
// 17. Let node now be the node before node in the stack of open elements. // 16. Let node now be the node before node in the stack of open elements.
// 18. Return to the step labeled loop. // 17. Return to the step labeled loop.
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();

View File

@@ -43,8 +43,6 @@ namespace Web::HTML {
__ENUMERATE_INSERTION_MODE(InTableBody) \ __ENUMERATE_INSERTION_MODE(InTableBody) \
__ENUMERATE_INSERTION_MODE(InRow) \ __ENUMERATE_INSERTION_MODE(InRow) \
__ENUMERATE_INSERTION_MODE(InCell) \ __ENUMERATE_INSERTION_MODE(InCell) \
__ENUMERATE_INSERTION_MODE(InSelect) \
__ENUMERATE_INSERTION_MODE(InSelectInTable) \
__ENUMERATE_INSERTION_MODE(InTemplate) \ __ENUMERATE_INSERTION_MODE(InTemplate) \
__ENUMERATE_INSERTION_MODE(AfterBody) \ __ENUMERATE_INSERTION_MODE(AfterBody) \
__ENUMERATE_INSERTION_MODE(InFrameset) \ __ENUMERATE_INSERTION_MODE(InFrameset) \
@@ -129,8 +127,6 @@ private:
void handle_in_row(HTMLToken&); void handle_in_row(HTMLToken&);
void handle_in_cell(HTMLToken&); void handle_in_cell(HTMLToken&);
void handle_in_table_text(HTMLToken&); void handle_in_table_text(HTMLToken&);
void handle_in_select_in_table(HTMLToken&);
void handle_in_select(HTMLToken&);
void handle_in_caption(HTMLToken&); void handle_in_caption(HTMLToken&);
void handle_in_column_group(HTMLToken&); void handle_in_column_group(HTMLToken&);
void handle_in_template(HTMLToken&); void handle_in_template(HTMLToken&);

View File

@@ -13,7 +13,18 @@
namespace Web::HTML { namespace Web::HTML {
static Vector<FlyString> s_base_list { "applet"_fly_string, "caption"_fly_string, "html"_fly_string, "table"_fly_string, "td"_fly_string, "th"_fly_string, "marquee"_fly_string, "object"_fly_string, "template"_fly_string }; static Vector<FlyString> s_base_list {
"applet"_fly_string,
"caption"_fly_string,
"html"_fly_string,
"table"_fly_string,
"td"_fly_string,
"th"_fly_string,
"marquee"_fly_string,
"object"_fly_string,
"select"_fly_string,
"template"_fly_string
};
StackOfOpenElements::~StackOfOpenElements() = default; StackOfOpenElements::~StackOfOpenElements() = default;
@@ -22,6 +33,7 @@ void StackOfOpenElements::visit_edges(JS::Cell::Visitor& visitor)
visitor.visit(m_elements); visitor.visit(m_elements);
} }
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-the-specific-scope
bool StackOfOpenElements::has_in_scope_impl(FlyString const& tag_name, Vector<FlyString> const& list, CheckMathAndSVG check_math_and_svg) const bool StackOfOpenElements::has_in_scope_impl(FlyString const& tag_name, Vector<FlyString> const& list, CheckMathAndSVG check_math_and_svg) const
{ {
for (auto const& element : m_elements.in_reverse()) { for (auto const& element : m_elements.in_reverse()) {
@@ -37,11 +49,13 @@ bool StackOfOpenElements::has_in_scope_impl(FlyString const& tag_name, Vector<Fl
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-scope
bool StackOfOpenElements::has_in_scope(FlyString const& tag_name) const bool StackOfOpenElements::has_in_scope(FlyString const& tag_name) const
{ {
return has_in_scope_impl(tag_name, s_base_list, CheckMathAndSVG::Yes); return has_in_scope_impl(tag_name, s_base_list, CheckMathAndSVG::Yes);
} }
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-the-specific-scope
bool StackOfOpenElements::has_in_scope_impl(DOM::Element const& target_node, Vector<FlyString> const& list) const bool StackOfOpenElements::has_in_scope_impl(DOM::Element const& target_node, Vector<FlyString> const& list) const
{ {
for (auto& element : m_elements.in_reverse()) { for (auto& element : m_elements.in_reverse()) {
@@ -57,11 +71,13 @@ bool StackOfOpenElements::has_in_scope_impl(DOM::Element const& target_node, Vec
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-scope
bool StackOfOpenElements::has_in_scope(DOM::Element const& target_node) const bool StackOfOpenElements::has_in_scope(DOM::Element const& target_node) const
{ {
return has_in_scope_impl(target_node, s_base_list); return has_in_scope_impl(target_node, s_base_list);
} }
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-button-scope
bool StackOfOpenElements::has_in_button_scope(FlyString const& tag_name) const bool StackOfOpenElements::has_in_button_scope(FlyString const& tag_name) const
{ {
auto list = s_base_list; auto list = s_base_list;
@@ -69,11 +85,13 @@ bool StackOfOpenElements::has_in_button_scope(FlyString const& tag_name) const
return has_in_scope_impl(tag_name, list, CheckMathAndSVG::Yes); return has_in_scope_impl(tag_name, list, CheckMathAndSVG::Yes);
} }
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-table-scope
bool StackOfOpenElements::has_in_table_scope(FlyString const& tag_name) const bool StackOfOpenElements::has_in_table_scope(FlyString const& tag_name) const
{ {
return has_in_scope_impl(tag_name, { "html"_fly_string, "table"_fly_string, "template"_fly_string }, CheckMathAndSVG::No); return has_in_scope_impl(tag_name, { "html"_fly_string, "table"_fly_string, "template"_fly_string }, CheckMathAndSVG::No);
} }
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-list-item-scope
bool StackOfOpenElements::has_in_list_item_scope(FlyString const& tag_name) const bool StackOfOpenElements::has_in_list_item_scope(FlyString const& tag_name) const
{ {
auto list = s_base_list; auto list = s_base_list;
@@ -82,31 +100,6 @@ bool StackOfOpenElements::has_in_list_item_scope(FlyString const& tag_name) cons
return has_in_scope_impl(tag_name, list, CheckMathAndSVG::Yes); return has_in_scope_impl(tag_name, list, CheckMathAndSVG::Yes);
} }
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-select-scope
// The stack of open elements is said to have a particular element in select scope
// when it has that element in the specific scope consisting of all element types except the following:
// - optgroup in the HTML namespace
// - option in the HTML namespace
// NOTE: In this case it's "all element types _except_"
bool StackOfOpenElements::has_in_select_scope(FlyString const& tag_name) const
{
// https://html.spec.whatwg.org/multipage/parsing.html#has-an-element-in-the-specific-scope
// 1. Initialize node to be the current node (the bottommost node of the stack).
for (auto& node : m_elements.in_reverse()) {
// 2. If node is target node, terminate in a match state.
if (node->local_name() == tag_name)
return true;
// 3. Otherwise, if node is one of the element types in list, terminate in a failure state.
// NOTE: Here "list" refers to all elements except option and optgroup
if (node->local_name() != HTML::TagNames::option && node->local_name() != HTML::TagNames::optgroup)
return false;
// 4. Otherwise, set node to the previous entry in the stack of open elements and return to step 2.
}
// NOTE: This will never fail, since the loop will always terminate in the previous step if the top of the stack
// — an html element — is reached.
VERIFY_NOT_REACHED();
}
bool StackOfOpenElements::contains(DOM::Element const& element) const bool StackOfOpenElements::contains(DOM::Element const& element) const
{ {
for (auto& element_on_stack : m_elements) { for (auto& element_on_stack : m_elements) {

View File

@@ -39,7 +39,6 @@ public:
bool has_in_button_scope(FlyString const& tag_name) const; bool has_in_button_scope(FlyString const& tag_name) const;
bool has_in_table_scope(FlyString const& tag_name) const; bool has_in_table_scope(FlyString const& tag_name) const;
bool has_in_list_item_scope(FlyString const& tag_name) const; bool has_in_list_item_scope(FlyString const& tag_name) const;
bool has_in_select_scope(FlyString const& tag_name) const;
bool has_in_scope(DOM::Element const&) const; bool has_in_scope(DOM::Element const&) const;
@@ -49,7 +48,7 @@ public:
auto const& elements() const { return m_elements; } auto const& elements() const { return m_elements; }
auto& elements() { return m_elements; } auto& elements() { return m_elements; }
void pop_until_an_element_with_tag_name_has_been_popped(FlyString const& local_name); void pop_until_an_element_with_tag_name_has_been_popped(FlyString const& tag_name);
GC::Ptr<DOM::Element> topmost_special_node_below(DOM::Element const&); GC::Ptr<DOM::Element> topmost_special_node_below(DOM::Element const&);

View File

@@ -0,0 +1,23 @@
Harness status: OK
Found 17 tests
16 Pass
1 Fail
Pass <div>s, <button>s, and <datalist>s should be allowed in <select>
Pass </select> should close <button>
Pass </select> should close <datalist>
Pass <select> in <button> in <select> should remove inner <select>
Pass <select> in <select><button><div> should remove inner <select>
Pass JS added nested <select> should be ignored
Pass JS added nested <select>s should be ignored
Pass Divs and imgs should be allowed as direct children of select and within options without a datalist
Pass Input tags should not parse inside select instead of closing the select
Pass textarea tags should parse inside select instead of closing the select
Fail Input tags should parse inside select if nested in another tag
Pass Input tags should close select when directly inside an <option>
Pass The last test should not leave any tags open after parsing
Pass Nested selects should be retained 1
Pass Nested selects should be retained 2
Pass JS added nested select should be ignored
Pass JS added nested selects should be ignored

View File

@@ -2,7 +2,8 @@ Harness status: OK
Found 81 tests Found 81 tests
81 Pass 80 Pass
1 Fail
Pass html5lib_innerHTML_tests_innerHTML_1.html 7a9e287595dd570e0f19b7eec0ac424228908daf Pass html5lib_innerHTML_tests_innerHTML_1.html 7a9e287595dd570e0f19b7eec0ac424228908daf
Pass html5lib_innerHTML_tests_innerHTML_1.html 6f766fa07c8697a5379c5542adbba2a42f913004 Pass html5lib_innerHTML_tests_innerHTML_1.html 6f766fa07c8697a5379c5542adbba2a42f913004
Pass html5lib_innerHTML_tests_innerHTML_1.html dbbe75ae41228f9264d56a018e620217ec87fd32 Pass html5lib_innerHTML_tests_innerHTML_1.html dbbe75ae41228f9264d56a018e620217ec87fd32
@@ -78,7 +79,7 @@ Pass html5lib_innerHTML_tests_innerHTML_1.html a23b70f1f246ba08d13b570319391b4a5
Pass html5lib_innerHTML_tests_innerHTML_1.html 9d5e0c25bfe921df9ea2897c027f42bc88950e69 Pass html5lib_innerHTML_tests_innerHTML_1.html 9d5e0c25bfe921df9ea2897c027f42bc88950e69
Pass html5lib_innerHTML_tests_innerHTML_1.html 9210d577d6deecf5ab3505af86c501c5befa0b50 Pass html5lib_innerHTML_tests_innerHTML_1.html 9210d577d6deecf5ab3505af86c501c5befa0b50
Pass html5lib_innerHTML_tests_innerHTML_1.html c34af491c0a339db6ba63fcc478108533347319b Pass html5lib_innerHTML_tests_innerHTML_1.html c34af491c0a339db6ba63fcc478108533347319b
Pass html5lib_innerHTML_tests_innerHTML_1.html 2c4284e6b2bb480daa50bca43bcbe29cfcdeeab4 Fail html5lib_innerHTML_tests_innerHTML_1.html 2c4284e6b2bb480daa50bca43bcbe29cfcdeeab4
Pass html5lib_innerHTML_tests_innerHTML_1.html d75277b65d0118463afeb66b478509d4e27565ab Pass html5lib_innerHTML_tests_innerHTML_1.html d75277b65d0118463afeb66b478509d4e27565ab
Pass html5lib_innerHTML_tests_innerHTML_1.html b354df69dbe9b3ef0c42177648e3aace114cf8ea Pass html5lib_innerHTML_tests_innerHTML_1.html b354df69dbe9b3ef0c42177648e3aace114cf8ea
Pass html5lib_innerHTML_tests_innerHTML_1.html fd3be386292ea1f411cea8e86e29595deb177d28 Pass html5lib_innerHTML_tests_innerHTML_1.html fd3be386292ea1f411cea8e86e29595deb177d28

View File

@@ -1,8 +1,9 @@
Harness status: OK Harness status: OK
Found 30 tests Found 41 tests
30 Pass 39 Pass
2 Fail
Pass html5lib_webkit02.html f50b8c15847159a6d2c6ecc2bd1e4a944ba5aae6 Pass html5lib_webkit02.html f50b8c15847159a6d2c6ecc2bd1e4a944ba5aae6
Pass html5lib_webkit02.html 326328ea805a2ebdde707e08567713f88a4cf8ab Pass html5lib_webkit02.html 326328ea805a2ebdde707e08567713f88a4cf8ab
Pass html5lib_webkit02.html 05138397908cfdad69a3bfe5da5a06098320b504 Pass html5lib_webkit02.html 05138397908cfdad69a3bfe5da5a06098320b504
@@ -33,3 +34,14 @@ Pass html5lib_webkit02.html cee2230c74671c594a1140a68d16e3d3e5ae005a
Pass html5lib_webkit02.html 22b9fe36797d70a3b71a6aadc6ad7cff23c3fc90 Pass html5lib_webkit02.html 22b9fe36797d70a3b71a6aadc6ad7cff23c3fc90
Pass html5lib_webkit02.html a82c3bf49c381b5f58c5c8a4bbbe0cef2458e28a Pass html5lib_webkit02.html a82c3bf49c381b5f58c5c8a4bbbe0cef2458e28a
Pass html5lib_webkit02.html 61f8d527795dc8044a95a3e2437de81e16597ceb Pass html5lib_webkit02.html 61f8d527795dc8044a95a3e2437de81e16597ceb
Pass html5lib_webkit02.html 3a112027586c5c0fb506c49aa01d3f695ca0a5fa
Pass html5lib_webkit02.html 3e95ae8a102dc5d0ae04764420418cb19a288fb7
Pass html5lib_webkit02.html 4bd119721a0eb84aa72a30ea83fc5beb5843ca68
Pass html5lib_webkit02.html a15e2f18330327cc4dcfe8c10af07714903f773a
Pass html5lib_webkit02.html 29539f494ab6b37b18213f062da16b0f0c9d00d9
Pass html5lib_webkit02.html a6c50b1f6bfbe3c55102d8cad0950d0b68cc6729
Pass html5lib_webkit02.html 411f313a1b92ac7be549c41ee6758f952dc2dced
Pass html5lib_webkit02.html 84467597648753feeb78793e2cc9196bc75857c2
Pass html5lib_webkit02.html ae6f2e0a014f620269920ceb12660ec708236846
Fail html5lib_webkit02.html fd7aea4db6702879b9f8f410b0400d9300ae9c05
Fail html5lib_webkit02.html bad5cceffaffe98e3a1522be5f7df3e3e179d500

View File

@@ -0,0 +1,174 @@
<!DOCTYPE html>
<link rel=author href="mailto:jarhar@chromium.org">
<link rel=help href="https://github.com/whatwg/html/issues/9799">
<script src="../../../../../resources/testharness.js"></script>
<script src="../../../../../resources/testharnessreport.js"></script>
<body>
<select class=test
data-description='<div>s, <button>s, and <datalist>s should be allowed in <select>'
data-expect='
<div>div 1</div>
<button>button</button>
<div>div 2</div>
<datalist>
<option>option</option>
</datalist>
<div>div 3</div>
'>
<div>div 1</div>
<button>button</button>
<div>div 2</div>
<datalist>
<option>option</option>
</datalist>
<div>div 3</div>
</select>
<select class=test
data-description='</select> should close <button>'
data-expect='<button>button</button>'>
<button>button
</select>
<select class=test
data-description='</select> should close <datalist>'
data-expect='<datalist>datalist</datalist>'>
<datalist>datalist
</select>
<select id=nested1 class=test
data-description='<select> in <button> in <select> should remove inner <select>'
data-expect='<button></button>'>
<button>
<select id=expectafter1></select>
<div id=expectafter1b></div>
</button>
</select>
<select id=nested2 class=test
data-description='<select> in <select><button><div> should remove inner <select>'
data-expect='<button><div></div></button>'>
<button>
<div>
<select id=expectafter2>
</select>
<select
id=nested3
class=test
data-description='JS added nested <select> should be ignored'
data-expect='<option>The Initial Option</option>'
>
<option>The Initial Option</option>
</select>
<select
id=nested4
class=test
data-description='JS added nested <select>s should be ignored'
data-expect='<option>The Initial Option</option>'
>
<option>The Initial Option</option>
</select>
<select class=test
data-description='Divs and imgs should be allowed as direct children of select and within options without a datalist'
data-expect='
<div>
<option><img>option</option>
</div>'>
<div>
<option><img>option</option>
</div>
</select>
<select class=test
data-description='Input tags should not parse inside select instead of closing the select'
data-expect=''>
<input>
</select>
<select class=test
data-description='textarea tags should parse inside select instead of closing the select'
data-expect='<textarea></textarea>'>
<textarea></textarea>
</select>
<select class=test
data-description='Input tags should parse inside select if nested in another tag'
data-expect='<div><input></div>'>
<div>
<input>
</div>
</select>
<select class=test
data-description='Input tags should close select when directly inside an <option>'
data-expect='<option></option>'>
<option>
<input>
</option>
</select>
<div id=afterlast>
keep this div after the last test case
</div>
<script>
function removeWhitespace(t) {
return t.replace(/\s/g,'');
}
document.querySelectorAll('select.test').forEach(s => {
assert_true(!!s.dataset.description.length);
test(() => {
// The document.body check here and in the other tests is to make sure that a
// previous test case didn't leave the HTML parser open on another element.
assert_equals(s.parentNode, document.body);
assert_equals(removeWhitespace(s.innerHTML),removeWhitespace(s.dataset.expect));
},s.dataset.description)
});
test(() => {
assert_equals(document.getElementById('afterlast').parentNode, document.body);
}, 'The last test should not leave any tags open after parsing');
test(() => {
const outerSelect = document.getElementById('nested1');
const innerSelect = document.getElementById('expectafter1');
const nextDiv = document.getElementById('expectafter1b');
assert_true(!!outerSelect);
assert_equals(innerSelect, null,'Nested select should be removed');
assert_equals(outerSelect.nextElementSibling, nextDiv,'Subsequent content is there too');
}, 'Nested selects should be retained 1');
test(() => {
const outerSelect = document.getElementById('nested2');
const innerSelect = document.getElementById('expectafter2');
assert_true(!!outerSelect);
assert_equals(innerSelect, null,'Nested select should be pushed out as the next sibling');
}, 'Nested selects should be retained 2');
test(() => {
assert_true(!!nested3);
nested3.innerHTML = '<select id="ignored"><option>The New Option</option></select>';
const ignored = document.getElementById('ignored');
assert_equals(ignored, null);
assert_equals(nested3.innerHTML, '<option>The New Option</option>');
}, 'JS added nested select should be ignored');
test(() => {
assert_true(!!nested4);
nested4.innerHTML = '<select id="ignore1"><select id="ignore2"><option>The New Option</option></select></select>';
const ignored1 = document.getElementById('ignored1');
assert_equals(ignored1, null);
const ignored2 = document.getElementById('ignored2');
assert_equals(ignored2, null);
assert_equals(nested4.innerHTML, '<option>The New Option</option>');
}, 'JS added nested selects should be ignored');
</script>

View File

@@ -17,7 +17,7 @@
var num_iframes = 8; var num_iframes = 8;
var order = ['cac5528d0cbea4d15babba38304646e3903324a6','bafeef55f21b568ab89a91082464614e4ebe7c2f','9461cfc6d9d4f08b05b3a95bbe5baa264f868a44','c2c4647447354abc154f1917a7fbefa4a679d5fb',]; var order = ['cac5528d0cbea4d15babba38304646e3903324a6','bafeef55f21b568ab89a91082464614e4ebe7c2f','9461cfc6d9d4f08b05b3a95bbe5baa264f868a44','c2c4647447354abc154f1917a7fbefa4a679d5fb',];
var tests = { var tests = {
"cac5528d0cbea4d15babba38304646e3903324a6":[async_test('html5lib_innerHTML_webkit02.html cac5528d0cbea4d15babba38304646e3903324a6'), "%3Cb%3E%3Cem%3E%3Cdcell%3E%3Cpostfield%3E%3Cpostfield%3E%3Cpostfield%3E%3Cpostfield%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Chkern%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cdcell%3E%0A%7C%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Chkern%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"bafeef55f21b568ab89a91082464614e4ebe7c2f":[async_test('html5lib_innerHTML_webkit02.html bafeef55f21b568ab89a91082464614e4ebe7c2f'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"9461cfc6d9d4f08b05b3a95bbe5baa264f868a44":[async_test('html5lib_innerHTML_webkit02.html 9461cfc6d9d4f08b05b3a95bbe5baa264f868a44'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoob%3E%3Cfoob%3E%3Cfoob%3E%3Cfoob%3E%3Cfooc%3E%3Cfooc%3E%3Cfooc%3E%3Cfooc%3E%3Cfood%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfood%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"c2c4647447354abc154f1917a7fbefa4a679d5fb":[async_test('html5lib_innerHTML_webkit02.html c2c4647447354abc154f1917a7fbefa4a679d5fb'), "%3Coption%3E%3CXH%3Coptgroup%3E%3C/optgroup%3E", "%23document%0A%7C%20%3Coption%3E", 'select'], "cac5528d0cbea4d15babba38304646e3903324a6":[async_test('html5lib_innerHTML_webkit02.html cac5528d0cbea4d15babba38304646e3903324a6'), "%3Cb%3E%3Cem%3E%3Cdcell%3E%3Cpostfield%3E%3Cpostfield%3E%3Cpostfield%3E%3Cpostfield%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Cmissing_glyph%3E%3Chkern%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cdcell%3E%0A%7C%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cpostfield%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmissing_glyph%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Chkern%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"bafeef55f21b568ab89a91082464614e4ebe7c2f":[async_test('html5lib_innerHTML_webkit02.html bafeef55f21b568ab89a91082464614e4ebe7c2f'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Cfoo%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoo%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"9461cfc6d9d4f08b05b3a95bbe5baa264f868a44":[async_test('html5lib_innerHTML_webkit02.html 9461cfc6d9d4f08b05b3a95bbe5baa264f868a44'), "%3Cb%3E%3Cem%3E%3Cfoo%3E%3Cfoob%3E%3Cfoob%3E%3Cfoob%3E%3Cfoob%3E%3Cfooc%3E%3Cfooc%3E%3Cfooc%3E%3Cfooc%3E%3Cfood%3E%3Caside%3E%3C/b%3E%3C/em%3E", "%23document%0A%7C%20%3Cb%3E%0A%7C%20%20%20%3Cem%3E%0A%7C%20%20%20%20%20%3Cfoo%3E%0A%7C%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfoob%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfooc%3E%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cfood%3E%0A%7C%20%3Caside%3E%0A%7C%20%20%20%3Cb%3E", 'div'],"c2c4647447354abc154f1917a7fbefa4a679d5fb":[async_test('html5lib_innerHTML_webkit02.html c2c4647447354abc154f1917a7fbefa4a679d5fb'), "%3Coption%3E%3CXH%3Coptgroup%3E%3C/optgroup%3E", "%23document%0A%7C%20%3Coption%3E%0A%7C%20%20%20%3Cxh%3Coptgroup%3E", 'select'],
} }
init_tests("innerHTML"); init_tests("innerHTML");
</script> </script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long