From 8a0e21b22bce771e3587b964b969975aba5873da Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 15 Jun 2019 22:49:44 +0200 Subject: [PATCH] LibHTML: Start fleshing out a basic layout tree. --- LibHTML/Document.cpp | 31 ++++++++++++++++++++++ LibHTML/Document.h | 7 ++++- LibHTML/Dump.cpp | 28 ++++++++++++++++---- LibHTML/Dump.h | 4 ++- LibHTML/Element.cpp | 17 ++++++++++++ LibHTML/Element.h | 4 ++- LibHTML/LayoutBlock.cpp | 11 ++++++++ LibHTML/LayoutBlock.h | 15 +++++++++++ LibHTML/LayoutDocument.cpp | 10 +++++++ LibHTML/LayoutDocument.h | 16 +++++++++++ LibHTML/LayoutInline.cpp | 11 ++++++++ LibHTML/LayoutInline.h | 15 +++++++++++ LibHTML/LayoutNode.cpp | 33 +++++++++++++++++++++++ LibHTML/LayoutNode.h | 54 ++++++++++++++++++++++++++++++++++++++ LibHTML/LayoutText.cpp | 10 +++++++ LibHTML/LayoutText.h | 17 ++++++++++++ LibHTML/Makefile | 5 ++++ LibHTML/Node.cpp | 12 ++++++++- LibHTML/Node.h | 25 +++++++++++++++--- LibHTML/ParentNode.cpp | 1 + LibHTML/ParentNode.h | 18 +++++++++---- LibHTML/Text.cpp | 6 ++++- LibHTML/Text.h | 2 ++ LibHTML/test.cpp | 4 +++ 24 files changed, 338 insertions(+), 18 deletions(-) create mode 100644 LibHTML/LayoutBlock.cpp create mode 100644 LibHTML/LayoutBlock.h create mode 100644 LibHTML/LayoutDocument.cpp create mode 100644 LibHTML/LayoutDocument.h create mode 100644 LibHTML/LayoutInline.cpp create mode 100644 LibHTML/LayoutInline.h create mode 100644 LibHTML/LayoutNode.cpp create mode 100644 LibHTML/LayoutNode.h create mode 100644 LibHTML/LayoutText.cpp create mode 100644 LibHTML/LayoutText.h diff --git a/LibHTML/Document.cpp b/LibHTML/Document.cpp index a7ba17c2b3..721000e38f 100644 --- a/LibHTML/Document.cpp +++ b/LibHTML/Document.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include Document::Document() : ParentNode(NodeType::DOCUMENT_NODE) @@ -9,3 +12,31 @@ Document::~Document() { } +static void create_layout_tree_for_node(Node& node) +{ + 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() && node.parent_node()->layout_node()) + node.parent_node()->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); +} + +RetainPtr Document::create_layout_node() +{ + return adopt(*new LayoutDocument(*this)); +} diff --git a/LibHTML/Document.h b/LibHTML/Document.h index cf31d766b9..b06e5cdc9c 100644 --- a/LibHTML/Document.h +++ b/LibHTML/Document.h @@ -3,11 +3,16 @@ #include #include +class LayoutNode; + class Document : public ParentNode { public: Document(); virtual ~Document() override; + virtual RetainPtr create_layout_node() override; + + void build_layout_tree(); + private: }; - diff --git a/LibHTML/Dump.cpp b/LibHTML/Dump.cpp index 595769fa8f..db50e4ecc2 100644 --- a/LibHTML/Dump.cpp +++ b/LibHTML/Dump.cpp @@ -1,10 +1,12 @@ #include #include #include +#include +#include #include #include -void dump_tree(Node& node) +void dump_tree(const Node& node) { static int indent = 0; for (int i = 0; i < indent; ++i) @@ -12,19 +14,35 @@ void dump_tree(Node& node) if (node.is_document()) { printf("*Document*\n"); } else if (node.is_element()) { - printf("<%s", static_cast(node).tag_name().characters()); - static_cast(node).for_each_attribute([](auto& name, auto& value) { + printf("<%s", static_cast(node).tag_name().characters()); + static_cast(node).for_each_attribute([](auto& name, auto& value) { printf(" %s=%s", name.characters(), value.characters()); }); printf(">\n"); } else if (node.is_text()) { - printf("\"%s\"\n", static_cast(node).data().characters()); + printf("\"%s\"\n", static_cast(node).data().characters()); } ++indent; if (node.is_parent_node()) { - static_cast(node).for_each_child([](Node& child) { + static_cast(node).for_each_child([](auto& child) { dump_tree(child); }); } --indent; } + +void dump_tree(const LayoutNode& node) +{ + static int indent = 0; + for (int i = 0; i < indent; ++i) + printf(" "); + printf("%s{%p}", node.class_name(), &node); + if (node.is_text()) + printf(" \"%s\"", static_cast(node).node().data().characters()); + printf("\n"); + ++indent; + node.for_each_child([](auto& child) { + dump_tree(child); + }); + --indent; +} diff --git a/LibHTML/Dump.h b/LibHTML/Dump.h index 09b7de697e..cb5bfe64fb 100644 --- a/LibHTML/Dump.h +++ b/LibHTML/Dump.h @@ -1,5 +1,7 @@ #pragma once class Node; +class LayoutNode; -void dump_tree(Node&); +void dump_tree(const Node&); +void dump_tree(const LayoutNode&); diff --git a/LibHTML/Element.cpp b/LibHTML/Element.cpp index 263774409d..66473fe0c4 100644 --- a/LibHTML/Element.cpp +++ b/LibHTML/Element.cpp @@ -1,4 +1,6 @@ #include +#include +#include Element::Element(const String& tag_name) : ParentNode(NodeType::ELEMENT_NODE) @@ -47,3 +49,18 @@ void Element::set_attributes(Vector&& attributes) { m_attributes = move(attributes); } + +RetainPtr 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/Element.h b/LibHTML/Element.h index c21c0f085b..08a87e7233 100644 --- a/LibHTML/Element.h +++ b/LibHTML/Element.h @@ -34,12 +34,14 @@ public: void set_attributes(Vector&&); template - void for_each_attribute(Callback callback) + void for_each_attribute(Callback callback) const { for (auto& attribute : m_attributes) callback(attribute.name(), attribute.value()); } + virtual RetainPtr create_layout_node() override; + private: Attribute* find_attribute(const String& name); const Attribute* find_attribute(const String& name) const; diff --git a/LibHTML/LayoutBlock.cpp b/LibHTML/LayoutBlock.cpp new file mode 100644 index 0000000000..65a94f7d9b --- /dev/null +++ b/LibHTML/LayoutBlock.cpp @@ -0,0 +1,11 @@ +#include +#include + +LayoutBlock::LayoutBlock(Element& element) + : LayoutNode(&element) +{ +} + +LayoutBlock::~LayoutBlock() +{ +} diff --git a/LibHTML/LayoutBlock.h b/LibHTML/LayoutBlock.h new file mode 100644 index 0000000000..e953642d67 --- /dev/null +++ b/LibHTML/LayoutBlock.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +class Element; + +class LayoutBlock : public LayoutNode { +public: + explicit LayoutBlock(Element&); + virtual ~LayoutBlock() override; + + virtual const char* class_name() const override { return "LayoutBlock"; } + +private: +}; diff --git a/LibHTML/LayoutDocument.cpp b/LibHTML/LayoutDocument.cpp new file mode 100644 index 0000000000..738227c604 --- /dev/null +++ b/LibHTML/LayoutDocument.cpp @@ -0,0 +1,10 @@ +#include + +LayoutDocument::LayoutDocument(const Document& document) + : LayoutNode(&document) +{ +} + +LayoutDocument::~LayoutDocument() +{ +} diff --git a/LibHTML/LayoutDocument.h b/LibHTML/LayoutDocument.h new file mode 100644 index 0000000000..3cee3b341e --- /dev/null +++ b/LibHTML/LayoutDocument.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +class LayoutDocument : public LayoutNode { +public: + explicit LayoutDocument(const Document&); + virtual ~LayoutDocument() override; + + const Document& node() const { return static_cast(*LayoutNode::node()); } + + virtual const char* class_name() const override { return "LayoutDocument"; } + +private: +}; diff --git a/LibHTML/LayoutInline.cpp b/LibHTML/LayoutInline.cpp new file mode 100644 index 0000000000..bd9c88acde --- /dev/null +++ b/LibHTML/LayoutInline.cpp @@ -0,0 +1,11 @@ +#include +#include + +LayoutInline::LayoutInline(Element& element) + : LayoutNode(&element) +{ +} + +LayoutInline::~LayoutInline() +{ +} diff --git a/LibHTML/LayoutInline.h b/LibHTML/LayoutInline.h new file mode 100644 index 0000000000..bb8637aecd --- /dev/null +++ b/LibHTML/LayoutInline.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +class Element; + +class LayoutInline : public LayoutNode { +public: + explicit LayoutInline(Element&); + virtual ~LayoutInline() override; + + virtual const char* class_name() const override { return "LayoutInline"; } + +private: +}; diff --git a/LibHTML/LayoutNode.cpp b/LibHTML/LayoutNode.cpp new file mode 100644 index 0000000000..ececf68096 --- /dev/null +++ b/LibHTML/LayoutNode.cpp @@ -0,0 +1,33 @@ +#include + +LayoutNode::LayoutNode(const Node* node) + : m_node(node) +{ +} + +LayoutNode::~LayoutNode() +{ +} + +void LayoutNode::retain() +{ + ASSERT(m_retain_count); + ++m_retain_count; +} + +void LayoutNode::release() +{ + ASSERT(m_retain_count); + if (!--m_retain_count) + delete this; +} + +void LayoutNode::append_child(Retained node) +{ + if (m_last_child) + m_last_child->set_next_sibling(node.ptr()); + node->m_parent_node = this; + m_last_child = &node.leak_ref(); + if (!m_first_child) + m_first_child = m_last_child; +} diff --git a/LibHTML/LayoutNode.h b/LibHTML/LayoutNode.h new file mode 100644 index 0000000000..cc83db6fd9 --- /dev/null +++ b/LibHTML/LayoutNode.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +class Node; + +class LayoutNode { +public: + virtual ~LayoutNode(); + + void retain(); + void release(); + int retain_count() const { return m_retain_count; } + + const Node* node() const { return m_node; } + + LayoutNode* next_sibling() { return m_next_sibling; } + LayoutNode* previous_sibling() { return m_previous_sibling; } + LayoutNode* first_child() { return m_first_child; } + LayoutNode* last_child() { return m_last_child; } + const LayoutNode* next_sibling() const { return m_next_sibling; } + const LayoutNode* previous_sibling() const { return m_previous_sibling; } + const LayoutNode* first_child() const { return m_first_child; } + const LayoutNode* last_child() const { return m_last_child; } + + void append_child(Retained); + + void set_next_sibling(LayoutNode* node) { m_next_sibling = node; } + void set_previous_sibling(LayoutNode* node) { m_previous_sibling = node; } + + template + inline void for_each_child(Callback callback) const + { + for (auto* node = first_child(); node; node = node->next_sibling()) + callback(*node); + } + + virtual const char* class_name() const { return "LayoutNode"; } + virtual bool is_text() const { return false; } + +protected: + explicit LayoutNode(const Node*); + +private: + int m_retain_count { 1 }; + const Node* m_node { nullptr }; + LayoutNode* m_parent_node { nullptr }; + LayoutNode* m_first_child { nullptr }; + LayoutNode* m_last_child { nullptr }; + LayoutNode* m_next_sibling { nullptr }; + LayoutNode* m_previous_sibling { nullptr }; +}; + diff --git a/LibHTML/LayoutText.cpp b/LibHTML/LayoutText.cpp new file mode 100644 index 0000000000..e71c0cd8b4 --- /dev/null +++ b/LibHTML/LayoutText.cpp @@ -0,0 +1,10 @@ +#include + +LayoutText::LayoutText(const Text& text) + : LayoutNode(&text) +{ +} + +LayoutText::~LayoutText() +{ +} diff --git a/LibHTML/LayoutText.h b/LibHTML/LayoutText.h new file mode 100644 index 0000000000..42127c0778 --- /dev/null +++ b/LibHTML/LayoutText.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +class LayoutText : public LayoutNode { +public: + explicit LayoutText(const Text&); + virtual ~LayoutText() override; + + const Text& node() const { return static_cast(*LayoutNode::node()); } + + virtual const char* class_name() const override { return "LayoutText"; } + virtual bool is_text() const final { return true; } + +private: +}; diff --git a/LibHTML/Makefile b/LibHTML/Makefile index ca947be04f..66b48fd839 100644 --- a/LibHTML/Makefile +++ b/LibHTML/Makefile @@ -7,6 +7,11 @@ LIBHTML_OBJS = \ Document.o \ Text.o \ Parser.o \ + LayoutNode.o \ + LayoutText.o \ + LayoutBlock.o \ + LayoutInline.o \ + LayoutDocument.o \ Dump.o TEST_OBJS = test.o diff --git a/LibHTML/Node.cpp b/LibHTML/Node.cpp index 961699d554..b7c4e3747d 100644 --- a/LibHTML/Node.cpp +++ b/LibHTML/Node.cpp @@ -1,5 +1,5 @@ -#include #include +#include Node::Node(NodeType type) : m_type(type) @@ -22,3 +22,13 @@ void Node::release() if (!--m_retain_count) delete this; } + +RetainPtr Node::create_layout_node() +{ + return nullptr; +} + +void Node::set_layout_node(Retained layout_node) +{ + m_layout_node = move(layout_node); +} diff --git a/LibHTML/Node.h b/LibHTML/Node.h index 3650782929..3867bddbac 100644 --- a/LibHTML/Node.h +++ b/LibHTML/Node.h @@ -1,6 +1,7 @@ #pragma once -#include +#include +#include #include enum class NodeType : unsigned { @@ -10,6 +11,9 @@ enum class NodeType : unsigned { DOCUMENT_NODE = 9, }; +class LayoutNode; +class ParentNode; + class Node { public: virtual ~Node(); @@ -18,6 +22,11 @@ public: void release(); int retain_count() const { return m_retain_count; } + ParentNode* parent_node() { return m_parent_node; } + const ParentNode* parent_node() const { return m_parent_node; } + + void set_parent_node(Badge, ParentNode* parent_node) { m_parent_node = parent_node; } + NodeType type() const { return m_type; } bool is_element() const { return type() == NodeType::ELEMENT_NODE; } bool is_text() const { return type() == NodeType::TEXT_NODE; } @@ -26,16 +35,26 @@ public: Node* next_sibling() { return m_next_sibling; } Node* previous_sibling() { return m_previous_sibling; } + const Node* next_sibling() const { return m_next_sibling; } + const Node* previous_sibling() const { return m_previous_sibling; } + void set_next_sibling(Node* node) { m_next_sibling = node; } void set_previous_sibling(Node* node) { m_previous_sibling = node; } + virtual RetainPtr create_layout_node(); + + const LayoutNode* layout_node() const { return m_layout_node; } + LayoutNode* layout_node() { return m_layout_node; } + + void set_layout_node(Retained); + protected: explicit Node(NodeType); int m_retain_count { 1 }; NodeType m_type { NodeType::INVALID }; - Vector m_children; + ParentNode* m_parent_node { nullptr }; Node* m_next_sibling { nullptr }; Node* m_previous_sibling { nullptr }; + RetainPtr m_layout_node; }; - diff --git a/LibHTML/ParentNode.cpp b/LibHTML/ParentNode.cpp index d241032375..23c46692e7 100644 --- a/LibHTML/ParentNode.cpp +++ b/LibHTML/ParentNode.cpp @@ -4,6 +4,7 @@ void ParentNode::append_child(Retained node) { if (m_last_child) m_last_child->set_next_sibling(node.ptr()); + node->set_parent_node({}, this); m_last_child = &node.leak_ref(); if (!m_first_child) m_first_child = m_last_child; diff --git a/LibHTML/ParentNode.h b/LibHTML/ParentNode.h index 357315e3d7..a28ef494ba 100644 --- a/LibHTML/ParentNode.h +++ b/LibHTML/ParentNode.h @@ -8,7 +8,10 @@ public: Node* first_child() { return m_first_child; } Node* last_child() { return m_last_child; } + const Node* first_child() const { return m_first_child; } + const Node* last_child() const { return m_last_child; } + template void for_each_child(F) const; template void for_each_child(F); protected: @@ -22,11 +25,16 @@ private: Node* m_last_child { nullptr }; }; -template -inline void ParentNode::for_each_child(F func) +template +inline void ParentNode::for_each_child(Callback callback) const { - for (auto* node = first_child(); node; node = node->next_sibling()) { - func(*node); - } + for (auto* node = first_child(); node; node = node->next_sibling()) + callback(*node); } +template +inline void ParentNode::for_each_child(Callback callback) +{ + for (auto* node = first_child(); node; node = node->next_sibling()) + callback(*node); +} diff --git a/LibHTML/Text.cpp b/LibHTML/Text.cpp index 67ec381f73..c21a0f54b0 100644 --- a/LibHTML/Text.cpp +++ b/LibHTML/Text.cpp @@ -1,4 +1,5 @@ #include +#include Text::Text(const String& data) : Node(NodeType::TEXT_NODE) @@ -10,4 +11,7 @@ Text::~Text() { } - +RetainPtr Text::create_layout_node() +{ + return adopt(*new LayoutText(*this)); +} diff --git a/LibHTML/Text.h b/LibHTML/Text.h index bdc9f86ec2..76e7d7bca4 100644 --- a/LibHTML/Text.h +++ b/LibHTML/Text.h @@ -10,6 +10,8 @@ public: const String& data() const { return m_data; } + virtual RetainPtr create_layout_node() override; + private: String m_data; }; diff --git a/LibHTML/test.cpp b/LibHTML/test.cpp index ebeda969e6..7e55b5a869 100644 --- a/LibHTML/test.cpp +++ b/LibHTML/test.cpp @@ -14,5 +14,9 @@ int main(int argc, char** argv) String html = String::copy(f.read_all()); auto doc = parse(html); dump_tree(doc); + + doc->build_layout_tree(); + ASSERT(doc->layout_node()); + dump_tree(*doc->layout_node()); return 0; }