From 53d2f4df70f51d5be3bf5006d7ddd84c2cafb547 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 24 May 2020 19:24:36 +0200 Subject: [PATCH] LibWeb: Factor out the "stack of open elements" into its own class This will allow us to write more expressive parsing code. :^) --- Libraries/LibWeb/CMakeLists.txt | 1 + .../LibWeb/Parser/HTMLDocumentParser.cpp | 39 +++++--------- Libraries/LibWeb/Parser/HTMLDocumentParser.h | 5 +- .../LibWeb/Parser/StackOfOpenElements.cpp | 23 ++++++++ Libraries/LibWeb/Parser/StackOfOpenElements.h | 53 +++++++++++++++++++ 5 files changed, 93 insertions(+), 28 deletions(-) create mode 100644 Libraries/LibWeb/Parser/StackOfOpenElements.cpp create mode 100644 Libraries/LibWeb/Parser/StackOfOpenElements.h diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index e2789da5aa..540958cf12 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -88,6 +88,7 @@ set(SOURCES Parser/HTMLParser.cpp Parser/HTMLToken.cpp Parser/HTMLTokenizer.cpp + Parser/StackOfOpenElements.cpp ResourceLoader.cpp StylePropertiesModel.cpp URLEncoder.cpp diff --git a/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp b/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp index d3092bec42..3ebd4e329e 100644 --- a/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp +++ b/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp @@ -109,21 +109,21 @@ void HTMLDocumentParser::handle_before_html(HTMLToken& token) if (token.is_start_tag() && token.tag_name() == "html") { auto element = create_element_for(token); document().append_child(element); - m_stack_of_open_elements.append(element); + m_stack_of_open_elements.push(move(element)); m_insertion_mode = InsertionMode::BeforeHead; return; } ASSERT_NOT_REACHED(); } -NonnullRefPtr HTMLDocumentParser::current_node() +Element& HTMLDocumentParser::current_node() { - return m_stack_of_open_elements.last(); + return m_stack_of_open_elements.current_node(); } RefPtr HTMLDocumentParser::find_appropriate_place_for_inserting_node() { - auto target = current_node(); + auto& target = current_node(); if (m_foster_parenting) { ASSERT_NOT_REACHED(); } @@ -145,7 +145,7 @@ RefPtr HTMLDocumentParser::insert_html_element(HTMLToken& token) auto element = create_element_for(token); // FIXME: Check if it's possible to insert `element` at `adjusted_insertion_location` adjusted_insertion_location->append_child(element); - m_stack_of_open_elements.append(element); + m_stack_of_open_elements.push(element); return element; } @@ -164,14 +164,14 @@ void HTMLDocumentParser::handle_in_head(HTMLToken& token) { if (token.is_start_tag() && token.tag_name() == "meta") { auto element = insert_html_element(token); - m_stack_of_open_elements.take_last(); + m_stack_of_open_elements.pop(); if (token.is_self_closing()) { ASSERT_NOT_REACHED(); } return; } if (token.is_end_tag() && token.tag_name() == "head") { - m_stack_of_open_elements.take_last(); + m_stack_of_open_elements.pop(); m_insertion_mode = InsertionMode::AfterHead; return; } @@ -243,21 +243,8 @@ AnythingElse: void HTMLDocumentParser::generate_implied_end_tags() { Vector names { "dd", "dt", "li", "optgroup", "option", "p", "rb", "rp", "rt", "rtc" }; - while (names.contains_slow(current_node()->tag_name())) - m_stack_of_open_elements.take_last(); -} - -bool HTMLDocumentParser::stack_of_open_elements_has_element_with_tag_name_in_scope(const FlyString& tag_name) -{ - Vector list { "applet", "caption", "html", "table", "td", "th", "marquee", "object", "template" }; - for (ssize_t i = m_stack_of_open_elements.size() - 1; i >= 0; --i) { - auto& node = m_stack_of_open_elements.at(i); - if (node.tag_name() == tag_name) - return true; - if (list.contains_slow(node.tag_name())) - return false; - } - ASSERT_NOT_REACHED(); + while (names.contains_slow(current_node().tag_name())) + m_stack_of_open_elements.pop(); } void HTMLDocumentParser::handle_after_body(HTMLToken& token) @@ -284,7 +271,7 @@ void HTMLDocumentParser::handle_after_after_body(HTMLToken& token) void HTMLDocumentParser::handle_in_body(HTMLToken& token) { if (token.is_end_tag() && token.tag_name() == "body") { - if (!stack_of_open_elements_has_element_with_tag_name_in_scope("body")) { + if (!m_stack_of_open_elements.has_in_scope("body")) { ASSERT_NOT_REACHED(); } @@ -310,17 +297,17 @@ void HTMLDocumentParser::handle_in_body(HTMLToken& token) if (token.is_end_tag() && names.contains_slow(token.tag_name())) { // FIXME: If the stack of open elements has a p element in button scope, then close a p element. - if (!stack_of_open_elements_has_element_with_tag_name_in_scope(token.tag_name())) { + if (!m_stack_of_open_elements.has_in_scope(token.tag_name())) { ASSERT_NOT_REACHED(); } generate_implied_end_tags(); - if (current_node()->tag_name() != token.tag_name()) { + if (current_node().tag_name() != token.tag_name()) { ASSERT_NOT_REACHED(); } - m_stack_of_open_elements.take_last(); + m_stack_of_open_elements.pop(); return; } } diff --git a/Libraries/LibWeb/Parser/HTMLDocumentParser.h b/Libraries/LibWeb/Parser/HTMLDocumentParser.h index e846b10a58..a31aa96dd0 100644 --- a/Libraries/LibWeb/Parser/HTMLDocumentParser.h +++ b/Libraries/LibWeb/Parser/HTMLDocumentParser.h @@ -29,6 +29,7 @@ #include #include #include +#include #define ENUMERATE_INSERTION_MODES \ __ENUMERATE_INSERTION_MODE(Initial) \ @@ -93,10 +94,10 @@ private: NonnullRefPtr create_element_for(HTMLToken&); RefPtr find_appropriate_place_for_inserting_node(); RefPtr insert_html_element(HTMLToken&); - NonnullRefPtr current_node(); + Element& current_node(); InsertionMode m_insertion_mode { InsertionMode::Initial }; - NonnullRefPtrVector m_stack_of_open_elements; + StackOfOpenElements m_stack_of_open_elements; HTMLTokenizer m_tokenizer; diff --git a/Libraries/LibWeb/Parser/StackOfOpenElements.cpp b/Libraries/LibWeb/Parser/StackOfOpenElements.cpp new file mode 100644 index 0000000000..ad607254d6 --- /dev/null +++ b/Libraries/LibWeb/Parser/StackOfOpenElements.cpp @@ -0,0 +1,23 @@ +#include +#include + +namespace Web { + +StackOfOpenElements::~StackOfOpenElements() +{ +} + +bool StackOfOpenElements::has_in_scope(const FlyString& tag_name) const +{ + static Vector list { "applet", "caption", "html", "table", "td", "th", "marquee", "object", "template" }; + for (ssize_t i = m_elements.size() - 1; i >= 0; --i) { + auto& node = m_elements.at(i); + if (node.tag_name() == tag_name) + return true; + if (list.contains_slow(node.tag_name())) + return false; + } + ASSERT_NOT_REACHED(); +} + +} diff --git a/Libraries/LibWeb/Parser/StackOfOpenElements.h b/Libraries/LibWeb/Parser/StackOfOpenElements.h new file mode 100644 index 0000000000..56eaad3115 --- /dev/null +++ b/Libraries/LibWeb/Parser/StackOfOpenElements.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include + +namespace Web { + +class StackOfOpenElements { +public: + StackOfOpenElements() { } + ~StackOfOpenElements(); + + 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(); } + + const Element& current_node() const { return m_elements.last(); } + Element& current_node() { return m_elements.last(); } + + bool has_in_scope(const FlyString& tag_name) const; + +private: + NonnullRefPtrVector m_elements; +}; + +}