1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-25 21:35:07 +00:00

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.
This commit is contained in:
Andreas Kling 2019-06-29 21:42:07 +02:00
parent 6e95b11395
commit 7eef69ad4b
20 changed files with 132 additions and 131 deletions

View file

@ -1,5 +1,5 @@
html {
display: inline;
display: block;
font-family: Katica;
}

View file

@ -36,7 +36,7 @@ static bool matches(const Selector& selector, const Element& element)
NonnullRefPtrVector<StyleRule> StyleResolver::collect_matching_rules(const Element& element) const
{
NonnullRefPtrVector<StyleRule> 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)) {

View file

@ -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<StyledNode> create_styled_node(const Element&);
NonnullRefPtr<StyledNode> create_styled_node(const Document&);
@ -28,6 +26,4 @@ public:
private:
Document& m_document;
NonnullRefPtrVector<StyleSheet> m_sheets;
};

View file

@ -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();
}

View file

@ -8,6 +8,12 @@
class Node;
enum class Display {
None,
Block,
Inline,
};
class StyledNode : public TreeNode<StyledNode> {
public:
static NonnullRefPtr<StyledNode> create(const Node& node)
@ -44,6 +50,8 @@ public:
m_property_values.set(name, move(value));
}
Display display() const;
protected:
explicit StyledNode(const Node*);

View file

@ -1,3 +1,4 @@
#include <LibHTML/CSS/StyleResolver.h>
#include <LibHTML/DOM/Document.h>
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Layout/LayoutDocument.h>
@ -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<const Element&>(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<ParentNode&>(node).for_each_child([&](auto& child) {
create_layout_tree_for_node(child);
});
}
}
void Document::build_layout_tree()
{
create_layout_tree_for_node(*this);
}
RefPtr<LayoutNode> Document::create_layout_node()
{
return adopt(*new LayoutDocument(*this));
if (!m_style_resolver)
m_style_resolver = make<StyleResolver>(*this);
return *m_style_resolver;
}

View file

@ -1,18 +1,27 @@
#pragma once
#include <AK/AKString.h>
#include <AK/NonnullRefPtrVector.h>
#include <AK/OwnPtr.h>
#include <LibHTML/CSS/StyleResolver.h>
#include <LibHTML/CSS/StyleSheet.h>
#include <LibHTML/DOM/ParentNode.h>
class LayoutNode;
class StyleResolver;
class StyleSheet;
class Document : public ParentNode {
public:
Document();
virtual ~Document() override;
virtual RefPtr<LayoutNode> create_layout_node() override;
StyleResolver& style_resolver();
void build_layout_tree();
void add_sheet(const StyleSheet& sheet) { m_sheets.append(sheet); }
const NonnullRefPtrVector<StyleSheet>& stylesheets() const { return m_sheets; }
private:
OwnPtr<StyleResolver> m_style_resolver;
NonnullRefPtrVector<StyleSheet> m_sheets;
};

View file

@ -62,18 +62,3 @@ bool Element::has_class(const StringView& class_name) const
}
return false;
}
RefPtr<LayoutNode> 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;
}

View file

@ -42,8 +42,6 @@ public:
bool has_class(const StringView&) const;
virtual RefPtr<LayoutNode> create_layout_node() override;
private:
Attribute* find_attribute(const String& name);
const Attribute* find_attribute(const String& name) const;

View file

@ -9,13 +9,3 @@ Node::Node(NodeType type)
Node::~Node()
{
}
RefPtr<LayoutNode> Node::create_layout_node()
{
return nullptr;
}
void Node::set_layout_node(NonnullRefPtr<LayoutNode> layout_node)
{
m_layout_node = move(layout_node);
}

View file

@ -12,7 +12,6 @@ enum class NodeType : unsigned {
DOCUMENT_NODE = 9,
};
class LayoutNode;
class ParentNode;
class Node : public TreeNode<Node> {
@ -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<LayoutNode> create_layout_node();
const LayoutNode* layout_node() const { return m_layout_node; }
LayoutNode* layout_node() { return m_layout_node; }
void set_layout_node(NonnullRefPtr<LayoutNode>);
protected:
explicit Node(NodeType);
NodeType m_type { NodeType::INVALID };
RefPtr<LayoutNode> m_layout_node;
};

View file

@ -10,8 +10,3 @@ Text::Text(const String& data)
Text::~Text()
{
}
RefPtr<LayoutNode> Text::create_layout_node()
{
return adopt(*new LayoutText(*this));
}

View file

@ -10,8 +10,6 @@ public:
const String& data() const { return m_data; }
virtual RefPtr<LayoutNode> create_layout_node() override;
private:
String m_data;
};

View file

@ -1,5 +1,12 @@
#include <AK/Function.h>
#include <LibHTML/CSS/StyleResolver.h>
#include <LibHTML/CSS/StyledNode.h>
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Dump.h>
#include <LibHTML/Frame.h>
#include <LibHTML/Layout/LayoutNode.h>
#include <LibHTML/Layout/LayoutBlock.h>
#include <LibHTML/Layout/LayoutInline.h>
#include <stdio.h>
Frame::Frame()
: m_size(800, 600)
@ -15,17 +22,78 @@ void Frame::set_document(Document* document)
m_document = document;
}
RefPtr<StyledNode> Frame::generate_style_tree()
{
if (!m_document)
return nullptr;
auto& resolver = m_document->style_resolver();
Function<RefPtr<StyledNode>(const Node&, StyledNode*)> resolve_style = [&](const Node& node, StyledNode* parent_styled_node) -> RefPtr<StyledNode> {
RefPtr<StyledNode> styled_node;
if (node.is_element())
styled_node = resolver.create_styled_node(static_cast<const Element&>(node));
else if (node.is_document())
styled_node = resolver.create_styled_node(static_cast<const Document&>(node));
if (!styled_node)
return nullptr;
if (parent_styled_node)
parent_styled_node->append_child(*styled_node);
static_cast<const ParentNode&>(node).for_each_child([&](const Node& child) {
if (!child.is_element())
return;
auto styled_child_node = resolve_style(static_cast<const Element&>(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<LayoutNode> {
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<RefPtr<LayoutNode>(const StyledNode&, LayoutNode*)> resolve_layout = [&](const StyledNode& styled_node, LayoutNode* parent_layout_node) -> RefPtr<LayoutNode> {
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);
}

View file

@ -16,6 +16,8 @@ public:
void layout();
private:
RefPtr<StyledNode> generate_style_tree();
RefPtr<Document> m_document;
Size m_size;
};

View file

@ -1,8 +1,8 @@
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Layout/LayoutBlock.h>
LayoutBlock::LayoutBlock(Element& element)
: LayoutNode(&element)
LayoutBlock::LayoutBlock(const Node& node)
: LayoutNode(&node)
{
}

View file

@ -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"; }

View file

@ -1,8 +1,8 @@
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Layout/LayoutInline.h>
LayoutInline::LayoutInline(Element& element)
: LayoutNode(&element)
LayoutInline::LayoutInline(const Node& node)
: LayoutNode(&node)
{
}

View file

@ -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"; }

View file

@ -1,10 +1,12 @@
#include <LibCore/CFile.h>
#include <LibHTML/Dump.h>
#include <LibHTML/Frame.h>
#include <LibHTML/Parser/CSSParser.h>
#include <LibHTML/CSS/StyleResolver.h>
#include <LibHTML/CSS/StyledNode.h>
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Dump.h>
#include <LibHTML/Frame.h>
#include <LibHTML/Layout/LayoutBlock.h>
#include <LibHTML/Layout/LayoutInline.h>
#include <LibHTML/Parser/CSSParser.h>
#include <LibHTML/Parser/HTMLParser.h>
#include <stdio.h>
@ -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<RefPtr<StyledNode>(const Node&, StyledNode*)> resolve_style = [&](const Node& node, StyledNode* parent_styled_node) -> RefPtr<StyledNode> {
auto styled_node = [&]() -> RefPtr<StyledNode> {
if (node.is_element())
return resolver.create_styled_node(static_cast<const Element&>(node));
if (node.is_document())
return resolver.create_styled_node(static_cast<const Document&>(node));
return nullptr;
}();
if (!styled_node)
return nullptr;
if (parent_styled_node)
parent_styled_node->append_child(*styled_node);
static_cast<const ParentNode&>(node).for_each_child([&](const Node& child) {
if (!child.is_element())
return;
auto styled_child_node = resolve_style(static_cast<const Element&>(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>();
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;
}