diff --git a/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp b/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
index 01b221b58f4..42fc24d253e 100644
--- a/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
+++ b/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
@@ -118,6 +118,15 @@ void HTMLDocumentParser::process_using_the_rules_for(InsertionMode mode, HTMLTok
case InsertionMode::InTable:
handle_in_table(token);
break;
+ case InsertionMode::InTableBody:
+ handle_in_table_body(token);
+ break;
+ case InsertionMode::InRow:
+ handle_in_row(token);
+ break;
+ case InsertionMode::InCell:
+ handle_in_cell(token);
+ break;
default:
ASSERT_NOT_REACHED();
}
@@ -507,6 +516,9 @@ void HTMLDocumentParser::reconstruct_the_active_formatting_elements()
if (m_list_of_active_formatting_elements.is_empty())
return;
+ if (m_list_of_active_formatting_elements.entries().last().is_marker())
+ return;
+
if (m_stack_of_open_elements.contains(*m_list_of_active_formatting_elements.entries().last().element))
return;
@@ -853,6 +865,108 @@ void HTMLDocumentParser::handle_text(HTMLToken& token)
ASSERT_NOT_REACHED();
}
+void HTMLDocumentParser::clear_the_stack_back_to_a_table_context()
+{
+ while (!current_node().tag_name().is_one_of("table", "template", "html"))
+ m_stack_of_open_elements.pop();
+}
+
+void HTMLDocumentParser::clear_the_stack_back_to_a_table_row_context()
+{
+ while (!current_node().tag_name().is_one_of("tr", "template", "html"))
+ m_stack_of_open_elements.pop();
+}
+
+void HTMLDocumentParser::clear_the_stack_back_to_a_table_body_context()
+{
+ while (!current_node().tag_name().is_one_of("tbody", "tfoot", "thead", "template", "html"))
+ m_stack_of_open_elements.pop();
+}
+
+void HTMLDocumentParser::handle_in_row(HTMLToken& token)
+{
+ if (token.is_start_tag() && token.tag_name().is_one_of("th", "td")) {
+ clear_the_stack_back_to_a_table_row_context();
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InCell;
+ m_list_of_active_formatting_elements.add_marker();
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name() == "tr") {
+ if (!m_stack_of_open_elements.has_in_table_scope("tr")) {
+ PARSE_ERROR();
+ return;
+ }
+ clear_the_stack_back_to_a_table_row_context();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTableBody;
+ return;
+ }
+
+ TODO();
+}
+
+void HTMLDocumentParser::handle_in_cell(HTMLToken& token)
+{
+ if (token.is_end_tag() && token.tag_name().is_one_of("td", "th")) {
+ if (!m_stack_of_open_elements.has_in_table_scope(token.tag_name())) {
+ PARSE_ERROR();
+ return;
+ }
+ generate_implied_end_tags();
+
+ if (current_node().tag_name() != token.tag_name()) {
+ PARSE_ERROR();
+ }
+
+ while (current_node().tag_name() != token.tag_name())
+ m_stack_of_open_elements.pop();
+ m_stack_of_open_elements.pop();
+
+ m_list_of_active_formatting_elements.clear_up_to_the_last_marker();
+
+ m_insertion_mode = InsertionMode::InRow;
+ return;
+ }
+ if (token.is_start_tag() && token.tag_name().is_one_of("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", "thead", "tr")) {
+ TODO();
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of("body", "caption", "col", "colgroup", "html")) {
+ PARSE_ERROR();
+ return;
+ }
+
+ if (token.is_end_tag() && token.tag_name().is_one_of("table", "tbody", "tfoot", "thead", "tr")) {
+ TODO();
+ }
+
+ process_using_the_rules_for(InsertionMode::InBody, token);
+}
+
+void HTMLDocumentParser::handle_in_table_body(HTMLToken& token)
+{
+ if (token.is_start_tag() && token.tag_name() == "tr") {
+ clear_the_stack_back_to_a_table_body_context();
+ insert_html_element(token);
+ m_insertion_mode = InsertionMode::InRow;
+ return;
+ }
+
+ if ((token.is_start_tag() && token.tag_name().is_one_of("caption", "col", "colgroup", "tbody", "tfoot", "thead"))
+ || (token.is_end_tag() && token.tag_name() == "table")) {
+ // FIXME: If the stack of open elements does not have a tbody, thead, or tfoot element in table scope, this is a parse error; ignore the token.
+
+ clear_the_stack_back_to_a_table_body_context();
+ m_stack_of_open_elements.pop();
+ m_insertion_mode = InsertionMode::InTable;
+ process_using_the_rules_for(InsertionMode::InTable, token);
+ return;
+ }
+ TODO();
+}
+
void HTMLDocumentParser::handle_in_table(HTMLToken& token)
{
if (token.is_character() && current_node().tag_name().is_one_of("table", "tbody", "tfoot", "thead", "tr")) {
@@ -879,7 +993,14 @@ void HTMLDocumentParser::handle_in_table(HTMLToken& token)
TODO();
}
if (token.is_start_tag() && token.tag_name().is_one_of("td", "th", "tr")) {
- TODO();
+ clear_the_stack_back_to_a_table_context();
+ HTMLToken fake_tbody_token;
+ fake_tbody_token.m_type = HTMLToken::Type::StartTag;
+ fake_tbody_token.m_tag.tag_name.append("tbody");
+ insert_html_element(fake_tbody_token);
+ m_insertion_mode = InsertionMode::InTableBody;
+ process_using_the_rules_for(InsertionMode::InTableBody, token);
+ return;
}
if (token.is_start_tag() && token.tag_name() == "table") {
PARSE_ERROR();
diff --git a/Libraries/LibWeb/Parser/HTMLDocumentParser.h b/Libraries/LibWeb/Parser/HTMLDocumentParser.h
index 6ce358ac4cc..884380a29d8 100644
--- a/Libraries/LibWeb/Parser/HTMLDocumentParser.h
+++ b/Libraries/LibWeb/Parser/HTMLDocumentParser.h
@@ -90,6 +90,9 @@ private:
void handle_after_after_body(HTMLToken&);
void handle_text(HTMLToken&);
void handle_in_table(HTMLToken&);
+ void handle_in_table_body(HTMLToken&);
+ void handle_in_row(HTMLToken&);
+ void handle_in_cell(HTMLToken&);
void generate_implied_end_tags(const FlyString& exception = {});
bool stack_of_open_elements_has_element_with_tag_name_in_scope(const FlyString& tag_name);
@@ -108,6 +111,9 @@ private:
size_t script_nesting_level() const { return m_script_nesting_level; }
void reset_the_insertion_mode_appropriately();
void run_the_adoption_agency_algorithm(HTMLToken&);
+ void clear_the_stack_back_to_a_table_context();
+ void clear_the_stack_back_to_a_table_body_context();
+ void clear_the_stack_back_to_a_table_row_context();
InsertionMode m_insertion_mode { InsertionMode::Initial };
InsertionMode m_original_insertion_mode { InsertionMode::Initial };
diff --git a/Libraries/LibWeb/Parser/ListOfActiveFormattingElements.cpp b/Libraries/LibWeb/Parser/ListOfActiveFormattingElements.cpp
index c49a94e050c..3a52bd67572 100644
--- a/Libraries/LibWeb/Parser/ListOfActiveFormattingElements.cpp
+++ b/Libraries/LibWeb/Parser/ListOfActiveFormattingElements.cpp
@@ -71,4 +71,13 @@ void ListOfActiveFormattingElements::remove(Element& element)
});
}
+void ListOfActiveFormattingElements::clear_up_to_the_last_marker()
+{
+ while (!m_entries.is_empty()) {
+ auto entry = m_entries.take_last();
+ if (entry.is_marker())
+ break;
+ }
+}
+
}
diff --git a/Libraries/LibWeb/Parser/ListOfActiveFormattingElements.h b/Libraries/LibWeb/Parser/ListOfActiveFormattingElements.h
index 4c05f633bd9..b03756b3daa 100644
--- a/Libraries/LibWeb/Parser/ListOfActiveFormattingElements.h
+++ b/Libraries/LibWeb/Parser/ListOfActiveFormattingElements.h
@@ -56,6 +56,8 @@ public:
Element* last_element_with_tag_name_before_marker(const FlyString& tag_name);
+ void clear_up_to_the_last_marker();
+
private:
Vector m_entries;
};
diff --git a/Libraries/LibWeb/Parser/StackOfOpenElements.h b/Libraries/LibWeb/Parser/StackOfOpenElements.h
index 4240676aec4..f551addde88 100644
--- a/Libraries/LibWeb/Parser/StackOfOpenElements.h
+++ b/Libraries/LibWeb/Parser/StackOfOpenElements.h
@@ -37,6 +37,9 @@ public:
StackOfOpenElements() { }
~StackOfOpenElements();
+ Element& first() { return m_elements.first(); }
+ Element& last() { return m_elements.last(); }
+
bool is_empty() const { return m_elements.is_empty(); }
void push(NonnullRefPtr element) { m_elements.append(move(element)); }
NonnullRefPtr pop() { return m_elements.take_last(); }