From 7eef69ad4bef15d76618ccdf7cb20004fa045509 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 29 Jun 2019 21:42:07 +0200 Subject: [PATCH] LibHTML: Refactor to go from DOM -> styled tree -> layout tree. Frame::layout() drives everything now, it takes the DOM contained in the frame and puts it through the tree transformations. --- LibHTML/CSS/Default.css | 2 +- LibHTML/CSS/StyleResolver.cpp | 2 +- LibHTML/CSS/StyleResolver.h | 4 -- LibHTML/CSS/StyledNode.cpp | 15 +++++++ LibHTML/CSS/StyledNode.h | 8 ++++ LibHTML/DOM/Document.cpp | 31 +++---------- LibHTML/DOM/Document.h | 13 +++++- LibHTML/DOM/Element.cpp | 15 ------- LibHTML/DOM/Element.h | 2 - LibHTML/DOM/Node.cpp | 10 ----- LibHTML/DOM/Node.h | 9 ---- LibHTML/DOM/Text.cpp | 5 --- LibHTML/DOM/Text.h | 2 - LibHTML/Frame.cpp | 80 ++++++++++++++++++++++++++++++--- LibHTML/Frame.h | 2 + LibHTML/Layout/LayoutBlock.cpp | 4 +- LibHTML/Layout/LayoutBlock.h | 2 +- LibHTML/Layout/LayoutInline.cpp | 4 +- LibHTML/Layout/LayoutInline.h | 2 +- LibHTML/test.cpp | 51 ++++----------------- 20 files changed, 132 insertions(+), 131 deletions(-) diff --git a/LibHTML/CSS/Default.css b/LibHTML/CSS/Default.css index a1afc54130..d32efb969d 100644 --- a/LibHTML/CSS/Default.css +++ b/LibHTML/CSS/Default.css @@ -1,5 +1,5 @@ html { - display: inline; + display: block; font-family: Katica; } diff --git a/LibHTML/CSS/StyleResolver.cpp b/LibHTML/CSS/StyleResolver.cpp index 47c28e8580..88d693c374 100644 --- a/LibHTML/CSS/StyleResolver.cpp +++ b/LibHTML/CSS/StyleResolver.cpp @@ -36,7 +36,7 @@ static bool matches(const Selector& selector, const Element& element) NonnullRefPtrVector StyleResolver::collect_matching_rules(const Element& element) const { NonnullRefPtrVector matching_rules; - for (auto& sheet : m_sheets) { + for (auto& sheet : document().stylesheets()) { for (auto& rule : sheet.rules()) { for (auto& selector : rule.selectors()) { if (matches(selector, element)) { diff --git a/LibHTML/CSS/StyleResolver.h b/LibHTML/CSS/StyleResolver.h index eec7042b29..2e093b687f 100644 --- a/LibHTML/CSS/StyleResolver.h +++ b/LibHTML/CSS/StyleResolver.h @@ -18,8 +18,6 @@ public: Document& document() { return m_document; } const Document& document() const { return m_document; } - void add_sheet(const StyleSheet& sheet) { m_sheets.append(sheet); } - NonnullRefPtr create_styled_node(const Element&); NonnullRefPtr create_styled_node(const Document&); @@ -28,6 +26,4 @@ public: private: Document& m_document; - - NonnullRefPtrVector m_sheets; }; diff --git a/LibHTML/CSS/StyledNode.cpp b/LibHTML/CSS/StyledNode.cpp index ed8a1ec211..57745b9858 100644 --- a/LibHTML/CSS/StyledNode.cpp +++ b/LibHTML/CSS/StyledNode.cpp @@ -8,3 +8,18 @@ StyledNode::StyledNode(const Node* node) StyledNode::~StyledNode() { } + +Display StyledNode::display() const +{ + auto it = m_property_values.find("display"); + if (it == m_property_values.end()) + return Display::Inline; + auto value = it->value->to_string(); + if (value == "none") + return Display::None; + if (value == "block") + return Display::Block; + if (value == "inline") + return Display::Inline; + ASSERT_NOT_REACHED(); +} diff --git a/LibHTML/CSS/StyledNode.h b/LibHTML/CSS/StyledNode.h index 61d43b5156..0cef5f3f26 100644 --- a/LibHTML/CSS/StyledNode.h +++ b/LibHTML/CSS/StyledNode.h @@ -8,6 +8,12 @@ class Node; +enum class Display { + None, + Block, + Inline, +}; + class StyledNode : public TreeNode { public: static NonnullRefPtr create(const Node& node) @@ -44,6 +50,8 @@ public: m_property_values.set(name, move(value)); } + Display display() const; + protected: explicit StyledNode(const Node*); diff --git a/LibHTML/DOM/Document.cpp b/LibHTML/DOM/Document.cpp index 95bf199f0a..36c937e6d7 100644 --- a/LibHTML/DOM/Document.cpp +++ b/LibHTML/DOM/Document.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -12,31 +13,9 @@ Document::~Document() { } -static void create_layout_tree_for_node(Node& node) +StyleResolver& Document::style_resolver() { - if (auto layout_node = node.create_layout_node()) { - node.set_layout_node(*layout_node); -#ifdef DEBUG_LAYOUT_TREE_BUILD - if (node.is_element()) { - printf("created layout node for <%s>, parent is %p, parent ln is %p\n", static_cast(node).tag_name().characters(), node.parent_node(), node.parent_node()->layout_node()); - } -#endif - if (node.parent() && node.parent()->layout_node()) - node.parent()->layout_node()->append_child(*layout_node); - } - if (node.is_parent_node()) { - static_cast(node).for_each_child([&](auto& child) { - create_layout_tree_for_node(child); - }); - } -} - -void Document::build_layout_tree() -{ - create_layout_tree_for_node(*this); -} - -RefPtr Document::create_layout_node() -{ - return adopt(*new LayoutDocument(*this)); + if (!m_style_resolver) + m_style_resolver = make(*this); + return *m_style_resolver; } diff --git a/LibHTML/DOM/Document.h b/LibHTML/DOM/Document.h index 86846cfeb8..50f245175d 100644 --- a/LibHTML/DOM/Document.h +++ b/LibHTML/DOM/Document.h @@ -1,18 +1,27 @@ #pragma once #include +#include +#include +#include +#include #include class LayoutNode; +class StyleResolver; +class StyleSheet; class Document : public ParentNode { public: Document(); virtual ~Document() override; - virtual RefPtr create_layout_node() override; + StyleResolver& style_resolver(); - void build_layout_tree(); + void add_sheet(const StyleSheet& sheet) { m_sheets.append(sheet); } + const NonnullRefPtrVector& stylesheets() const { return m_sheets; } private: + OwnPtr m_style_resolver; + NonnullRefPtrVector m_sheets; }; diff --git a/LibHTML/DOM/Element.cpp b/LibHTML/DOM/Element.cpp index 7808170a60..e9329e15f3 100644 --- a/LibHTML/DOM/Element.cpp +++ b/LibHTML/DOM/Element.cpp @@ -62,18 +62,3 @@ bool Element::has_class(const StringView& class_name) const } return false; } - -RefPtr Element::create_layout_node() -{ - if (m_tag_name == "html") - return adopt(*new LayoutBlock(*this)); - if (m_tag_name == "body") - return adopt(*new LayoutBlock(*this)); - if (m_tag_name == "h1") - return adopt(*new LayoutBlock(*this)); - if (m_tag_name == "p") - return adopt(*new LayoutBlock(*this)); - if (m_tag_name == "b") - return adopt(*new LayoutInline(*this)); - return nullptr; -} diff --git a/LibHTML/DOM/Element.h b/LibHTML/DOM/Element.h index f3133656ca..2b91860ff7 100644 --- a/LibHTML/DOM/Element.h +++ b/LibHTML/DOM/Element.h @@ -42,8 +42,6 @@ public: bool has_class(const StringView&) const; - virtual RefPtr create_layout_node() override; - private: Attribute* find_attribute(const String& name); const Attribute* find_attribute(const String& name) const; diff --git a/LibHTML/DOM/Node.cpp b/LibHTML/DOM/Node.cpp index b8d2007da1..b1aef1eea7 100644 --- a/LibHTML/DOM/Node.cpp +++ b/LibHTML/DOM/Node.cpp @@ -9,13 +9,3 @@ Node::Node(NodeType type) Node::~Node() { } - -RefPtr Node::create_layout_node() -{ - return nullptr; -} - -void Node::set_layout_node(NonnullRefPtr layout_node) -{ - m_layout_node = move(layout_node); -} diff --git a/LibHTML/DOM/Node.h b/LibHTML/DOM/Node.h index 73008fcaab..e04770f01d 100644 --- a/LibHTML/DOM/Node.h +++ b/LibHTML/DOM/Node.h @@ -12,7 +12,6 @@ enum class NodeType : unsigned { DOCUMENT_NODE = 9, }; -class LayoutNode; class ParentNode; class Node : public TreeNode { @@ -25,16 +24,8 @@ public: bool is_document() const { return type() == NodeType::DOCUMENT_NODE; } bool is_parent_node() const { return is_element() || is_document(); } - virtual RefPtr create_layout_node(); - - const LayoutNode* layout_node() const { return m_layout_node; } - LayoutNode* layout_node() { return m_layout_node; } - - void set_layout_node(NonnullRefPtr); - protected: explicit Node(NodeType); NodeType m_type { NodeType::INVALID }; - RefPtr m_layout_node; }; diff --git a/LibHTML/DOM/Text.cpp b/LibHTML/DOM/Text.cpp index 8bb807270c..8d8d8ace25 100644 --- a/LibHTML/DOM/Text.cpp +++ b/LibHTML/DOM/Text.cpp @@ -10,8 +10,3 @@ Text::Text(const String& data) Text::~Text() { } - -RefPtr Text::create_layout_node() -{ - return adopt(*new LayoutText(*this)); -} diff --git a/LibHTML/DOM/Text.h b/LibHTML/DOM/Text.h index e7ce724026..db1c9e4bac 100644 --- a/LibHTML/DOM/Text.h +++ b/LibHTML/DOM/Text.h @@ -10,8 +10,6 @@ public: const String& data() const { return m_data; } - virtual RefPtr create_layout_node() override; - private: String m_data; }; diff --git a/LibHTML/Frame.cpp b/LibHTML/Frame.cpp index 73e35b0665..147216e4f0 100644 --- a/LibHTML/Frame.cpp +++ b/LibHTML/Frame.cpp @@ -1,5 +1,12 @@ +#include +#include +#include +#include +#include #include -#include +#include +#include +#include Frame::Frame() : m_size(800, 600) @@ -15,17 +22,78 @@ void Frame::set_document(Document* document) m_document = document; } +RefPtr Frame::generate_style_tree() +{ + if (!m_document) + return nullptr; + + auto& resolver = m_document->style_resolver(); + Function(const Node&, StyledNode*)> resolve_style = [&](const Node& node, StyledNode* parent_styled_node) -> RefPtr { + RefPtr styled_node; + if (node.is_element()) + styled_node = resolver.create_styled_node(static_cast(node)); + else if (node.is_document()) + styled_node = resolver.create_styled_node(static_cast(node)); + if (!styled_node) + return nullptr; + if (parent_styled_node) + parent_styled_node->append_child(*styled_node); + static_cast(node).for_each_child([&](const Node& child) { + if (!child.is_element()) + return; + auto styled_child_node = resolve_style(static_cast(child), styled_node.ptr()); + printf("Created StyledNode{%p} for Element{%p}\n", styled_child_node.ptr(), &node); + }); + return styled_node; + }; + auto styled_root = resolve_style(*m_document, nullptr); + dump_tree(*styled_root); + return styled_root; +} + void Frame::layout() { if (!m_document) return; - if (!m_document->layout_node()) - m_document->create_layout_node(); + auto styled_root = generate_style_tree(); - ASSERT(m_document->layout_node()); + auto create_layout_node = [](const StyledNode& styled_node) -> RefPtr { + switch (styled_node.display()) { + case Display::None: + return nullptr; + case Display::Block: + return adopt(*new LayoutBlock(*styled_node.node())); + case Display::Inline: + return adopt(*new LayoutInline(*styled_node.node())); + default: + ASSERT_NOT_REACHED(); + } + }; - m_document->layout_node()->style().size().set_width(m_size.width()); + Function(const StyledNode&, LayoutNode*)> resolve_layout = [&](const StyledNode& styled_node, LayoutNode* parent_layout_node) -> RefPtr { + auto layout_node = create_layout_node(styled_node); + if (!layout_node) + return nullptr; + if (parent_layout_node) + parent_layout_node->append_child(*layout_node); + if (styled_node.has_children()) { + for (auto* child = styled_node.first_child(); child; child = child->next_sibling()) { + resolve_layout(*child, layout_node.ptr()); + } + } + return layout_node; + }; - m_document->layout_node()->layout(); + auto layout_root = resolve_layout(*styled_root, nullptr); + + layout_root->style().size().set_width(m_size.width()); + + printf("\033[33;1mLayout tree before layout:\033[0m\n"); + dump_tree(*layout_root); + + layout_root->layout(); + + printf("\033[33;1mLayout tree after layout:\033[0m\n"); + dump_tree(*layout_root); } diff --git a/LibHTML/Frame.h b/LibHTML/Frame.h index 9fd4467dc0..89abbc454e 100644 --- a/LibHTML/Frame.h +++ b/LibHTML/Frame.h @@ -16,6 +16,8 @@ public: void layout(); private: + RefPtr generate_style_tree(); + RefPtr m_document; Size m_size; }; diff --git a/LibHTML/Layout/LayoutBlock.cpp b/LibHTML/Layout/LayoutBlock.cpp index 9495ffd280..fd47af1ed0 100644 --- a/LibHTML/Layout/LayoutBlock.cpp +++ b/LibHTML/Layout/LayoutBlock.cpp @@ -1,8 +1,8 @@ #include #include -LayoutBlock::LayoutBlock(Element& element) - : LayoutNode(&element) +LayoutBlock::LayoutBlock(const Node& node) + : LayoutNode(&node) { } diff --git a/LibHTML/Layout/LayoutBlock.h b/LibHTML/Layout/LayoutBlock.h index 47981eead3..cc8a283245 100644 --- a/LibHTML/Layout/LayoutBlock.h +++ b/LibHTML/Layout/LayoutBlock.h @@ -6,7 +6,7 @@ class Element; class LayoutBlock : public LayoutNode { public: - explicit LayoutBlock(Element&); + explicit LayoutBlock(const Node&); virtual ~LayoutBlock() override; virtual const char* class_name() const override { return "LayoutBlock"; } diff --git a/LibHTML/Layout/LayoutInline.cpp b/LibHTML/Layout/LayoutInline.cpp index 04c1fa12e7..ac68b2818c 100644 --- a/LibHTML/Layout/LayoutInline.cpp +++ b/LibHTML/Layout/LayoutInline.cpp @@ -1,8 +1,8 @@ #include #include -LayoutInline::LayoutInline(Element& element) - : LayoutNode(&element) +LayoutInline::LayoutInline(const Node& node) + : LayoutNode(&node) { } diff --git a/LibHTML/Layout/LayoutInline.h b/LibHTML/Layout/LayoutInline.h index cf3075921f..73def302cb 100644 --- a/LibHTML/Layout/LayoutInline.h +++ b/LibHTML/Layout/LayoutInline.h @@ -6,7 +6,7 @@ class Element; class LayoutInline : public LayoutNode { public: - explicit LayoutInline(Element&); + explicit LayoutInline(const Node&); virtual ~LayoutInline() override; virtual const char* class_name() const override { return "LayoutInline"; } diff --git a/LibHTML/test.cpp b/LibHTML/test.cpp index e3d23837d8..e3e052ff36 100644 --- a/LibHTML/test.cpp +++ b/LibHTML/test.cpp @@ -1,10 +1,12 @@ #include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include #include #include @@ -23,47 +25,12 @@ int main(int argc, char** argv) dump_sheet(sheet); String html = String::copy(f.read_all()); - auto doc = parse_html(html); - dump_tree(doc); - - StyleResolver resolver(*doc); - resolver.add_sheet(*sheet); - - Function(const Node&, StyledNode*)> resolve_style = [&](const Node& node, StyledNode* parent_styled_node) -> RefPtr { - auto styled_node = [&]() -> RefPtr { - if (node.is_element()) - return resolver.create_styled_node(static_cast(node)); - if (node.is_document()) - return resolver.create_styled_node(static_cast(node)); - return nullptr; - }(); - if (!styled_node) - return nullptr; - if (parent_styled_node) - parent_styled_node->append_child(*styled_node); - static_cast(node).for_each_child([&](const Node& child) { - if (!child.is_element()) - return; - auto styled_child_node = resolve_style(static_cast(child), styled_node.ptr()); - printf("Created StyledNode{%p} for Element{%p}\n", styled_child_node.ptr(), &node); - }); - return styled_node; - }; - auto styled_root = resolve_style(*doc, nullptr); - - dump_tree(*styled_root); - - doc->build_layout_tree(); - ASSERT(doc->layout_node()); - - printf("\033[33;1mLayout tree before layout:\033[0m\n"); - dump_tree(*doc->layout_node()); + auto document = parse_html(html); + dump_tree(document); + document->add_sheet(*sheet); auto frame = make(); - frame->set_document(doc); + frame->set_document(document); frame->layout(); - - printf("\033[33;1mLayout tree after layout:\033[0m\n"); - dump_tree(*doc->layout_node()); return 0; }