diff --git a/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp b/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
index 052364a84f..19d1f2ecf0 100644
--- a/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
+++ b/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
@@ -141,6 +141,12 @@ void HTMLDocumentParser::process_using_the_rules_for(InsertionMode mode, HTMLTok
case InsertionMode::InTableText:
handle_in_table_text(token);
break;
+ case InsertionMode::InSelectInTable:
+ handle_in_select_in_table(token);
+ break;
+ case InsertionMode::InSelect:
+ handle_in_select(token);
+ break;
default:
ASSERT_NOT_REACHED();
}
@@ -220,6 +226,11 @@ Element& HTMLDocumentParser::current_node()
return m_stack_of_open_elements.current_node();
}
+Element& HTMLDocumentParser::node_before_current_node()
+{
+ return m_stack_of_open_elements.elements().at(m_stack_of_open_elements.elements().size() - 2);
+}
+
RefPtr HTMLDocumentParser::find_appropriate_place_for_inserting_node()
{
auto& target = current_node();
@@ -1201,7 +1212,22 @@ void HTMLDocumentParser::handle_in_body(HTMLToken& token)
}
if (token.is_start_tag() && token.tag_name() == "select") {
- TODO();
+ reconstruct_the_active_formatting_elements();
+ insert_html_element(token);
+ m_frameset_ok = false;
+ 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;
}
if (token.is_start_tag() && token.tag_name().is_one_of("optgroup", "option")) {
@@ -1539,7 +1565,14 @@ void HTMLDocumentParser::handle_in_table_body(HTMLToken& token)
}
if (token.is_end_tag() && token.tag_name().is_one_of("tbody", "tfoot", "thead")) {
- TODO();
+ if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+ clear_the_stack_back_to_a_table_body_context();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTable;
+ return;
}
if ((token.is_start_tag() && token.tag_name().is_one_of("caption", "col", "colgroup", "tbody", "tfoot", "thead"))
@@ -1621,6 +1654,132 @@ void HTMLDocumentParser::handle_in_table(HTMLToken& token)
TODO();
}
+void HTMLDocumentParser::handle_in_select_in_table(HTMLToken& token)
+{
+ (void)token;
+ TODO();
+}
+
+void HTMLDocumentParser::handle_in_select(HTMLToken& token)
+{
+ if (token.is_character()) {
+ if (token.codepoint() == 0) {
+ PARSE_ERROR();
+ return;
+ }
+ insert_character(token.codepoint());
+ return;
+ }
+
+ if (token.is_comment()) {
+ insert_comment(token);
+ return;
+ }
+
+ if (token.is_doctype()) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == "html") {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == "option") {
+ if (current_node().tag_name() == "option") {
+ m_stack_of_open_elements.pop();
+ }
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == "optgroup") {
+ if (current_node().tag_name() == "option") {
+ m_stack_of_open_elements.pop();
+ }
+ if (current_node().tag_name() == "optgroup") {
+ m_stack_of_open_elements.pop();
+ }
+ insert_html_element(token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == "optgroup") {
+ if (current_node().tag_name() == "option" && node_before_current_node().tag_name() == "optgroup")
+ m_stack_of_open_elements.pop();
+
+ if (current_node().tag_name() == "optgroup") {
+ m_stack_of_open_elements.pop();
+ } else {
+ PARSE_ERROR();
+ return;
+ }
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == "option") {
+ if (current_node().tag_name() == "option") {
+ m_stack_of_open_elements.pop();
+ } else {
+ PARSE_ERROR();
+ return;
+ }
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == "select") {
+ if (m_stack_of_open_elements.has_in_select_scope("select")) {
+ PARSE_ERROR();
+ return;
+ }
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped("select");
+ reset_the_insertion_mode_appropriately();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name() == "select") {
+ PARSE_ERROR();
+
+ if (!m_stack_of_open_elements.has_in_select_scope("select"))
+ return;
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped("select");
+ reset_the_insertion_mode_appropriately();
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of("input", "keygen", "textarea")) {
+ PARSE_ERROR();
+
+ if (!m_stack_of_open_elements.has_in_select_scope("select")) {
+ return;
+ }
+
+ m_stack_of_open_elements.pop_until_an_element_with_tag_name_has_been_popped("select");
+ reset_the_insertion_mode_appropriately();
+ process_using_the_rules_for(m_insertion_mode, token);
+ return;
+ }
+
+ if (token.is_start_tag() && token.tag_name().is_one_of("script", "template")) {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == "template") {
+ process_using_the_rules_for(InsertionMode::InHead, token);
+ return;
+ }
+
+ if (token.is_end_of_file()) {
+ process_using_the_rules_for(InsertionMode::InBody, token);
+ return;
+ }
+
+ PARSE_ERROR();
+}
+
void HTMLDocumentParser::reset_the_insertion_mode_appropriately()
{
for (ssize_t i = m_stack_of_open_elements.elements().size() - 1; i >= 0; --i) {
diff --git a/Libraries/LibWeb/Parser/HTMLDocumentParser.h b/Libraries/LibWeb/Parser/HTMLDocumentParser.h
index 9f8fd8829d..0a334bf31c 100644
--- a/Libraries/LibWeb/Parser/HTMLDocumentParser.h
+++ b/Libraries/LibWeb/Parser/HTMLDocumentParser.h
@@ -96,6 +96,8 @@ private:
void handle_in_row(HTMLToken&);
void handle_in_cell(HTMLToken&);
void handle_in_table_text(HTMLToken&);
+ void handle_in_select_in_table(HTMLToken&);
+ void handle_in_select(HTMLToken&);
void stop_parsing() { m_stop_parsing = true; }
@@ -105,6 +107,7 @@ private:
RefPtr find_appropriate_place_for_inserting_node();
RefPtr insert_html_element(HTMLToken&);
Element& current_node();
+ Element& node_before_current_node();
void insert_character(u32 data);
void insert_comment(HTMLToken&);
void reconstruct_the_active_formatting_elements();
diff --git a/Libraries/LibWeb/Parser/StackOfOpenElements.cpp b/Libraries/LibWeb/Parser/StackOfOpenElements.cpp
index cf63bccd43..f3c3925870 100644
--- a/Libraries/LibWeb/Parser/StackOfOpenElements.cpp
+++ b/Libraries/LibWeb/Parser/StackOfOpenElements.cpp
@@ -94,6 +94,14 @@ bool StackOfOpenElements::has_in_list_item_scope(const FlyString& tag_name) cons
return has_in_scope_impl(tag_name, list);
}
+bool StackOfOpenElements::has_in_select_scope(const FlyString& tag_name) const
+{
+ auto list = s_base_list;
+ list.append("option");
+ list.append("optgroup");
+ return has_in_scope_impl(tag_name, list);
+}
+
bool StackOfOpenElements::contains(const Element& element) const
{
for (auto& element_on_stack : m_elements) {
diff --git a/Libraries/LibWeb/Parser/StackOfOpenElements.h b/Libraries/LibWeb/Parser/StackOfOpenElements.h
index 6fb4455def..9e5076102f 100644
--- a/Libraries/LibWeb/Parser/StackOfOpenElements.h
+++ b/Libraries/LibWeb/Parser/StackOfOpenElements.h
@@ -51,6 +51,7 @@ public:
bool has_in_button_scope(const FlyString& tag_name) const;
bool has_in_table_scope(const FlyString& tag_name) const;
bool has_in_list_item_scope(const FlyString& tag_name) const;
+ bool has_in_select_scope(const FlyString& tag_name) const;
bool has_in_scope(const Element&) const;