From ca6fbefbc9dbe4da6541976b4c76e473f534a885 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 30 May 2020 19:58:52 +0200 Subject: [PATCH] LibWeb: Support parsing "select" elements (outside of tables) --- .../LibWeb/Parser/HTMLDocumentParser.cpp | 163 +++++++++++++++++- Libraries/LibWeb/Parser/HTMLDocumentParser.h | 3 + .../LibWeb/Parser/StackOfOpenElements.cpp | 8 + Libraries/LibWeb/Parser/StackOfOpenElements.h | 1 + 4 files changed, 173 insertions(+), 2 deletions(-) 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;