1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 09:07:45 +00:00

Libraries: Create top level directory for libraries.

Things were getting a little crowded in the project root, so this patch
moves the Lib*/ directories into Libraries/.
This commit is contained in:
Andreas Kling 2019-07-04 16:16:50 +02:00
parent 63814ffebf
commit 04b9dc2d30
328 changed files with 36 additions and 36 deletions

1
Libraries/LibHTML/CSS/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
DefaultStyleSheetSource.cpp

View file

@ -0,0 +1,41 @@
html {
display: block;
font-family: Katica;
}
head, link, meta, script, style, title {
display: none;
}
body {
display: block;
margin-left: 8;
margin-top: 8;
margin-right: 8;
margin-bottom: 8;
}
h1 {
display: block;
font-family: Pebbleton;
font-weight: bold;
}
pre {
display: block;
font-family: Csilla;
}
u, ins {
text-decoration: underline;
}
strong, b {
font-family: KaticaBold;
}
p {
display: block;
margin-bottom: 8;
margin-top: 8;
}

View file

@ -0,0 +1,35 @@
#pragma once
#include <AK/AKString.h>
class Length {
public:
enum class Type {
Auto,
Absolute,
};
Length() {}
Length(int value, Type type)
: m_type(type)
, m_value(value)
{
}
~Length() {}
bool is_auto() const { return m_type == Type::Auto; }
bool is_absolute() const { return m_type == Type::Absolute; }
int value() const { return m_value; }
String to_string() const
{
if (is_auto())
return "auto";
return String::format("%d [Length/Absolute]", m_value);
}
private:
Type m_type { Type::Auto };
int m_value { 0 };
};

View file

@ -0,0 +1,10 @@
#pragma once
#include <LibHTML/CSS/Length.h>
struct LengthBox {
Length top;
Length right;
Length bottom;
Length left;
};

View file

@ -0,0 +1,35 @@
#include <LibHTML/CSS/Selector.h>
Selector::Selector(Vector<Component>&& components)
: m_components(move(components))
{
}
Selector::~Selector()
{
}
Specificity Selector::specificity() const
{
unsigned ids = 0;
unsigned tag_names = 0;
unsigned classes = 0;
for (auto& component : m_components) {
switch (component.type) {
case Component::Type::Id:
++ids;
break;
case Component::Type::Class:
++classes;
break;
case Component::Type::TagName:
++tag_names;
break;
default:
break;
}
}
return { ids, classes, tag_names };
}

View file

@ -0,0 +1,29 @@
#pragma once
#include <AK/AKString.h>
#include <AK/Vector.h>
#include <LibHTML/CSS/Specificity.h>
class Selector {
public:
struct Component {
enum class Type {
Invalid,
TagName,
Id,
Class
};
Type type { Type::Invalid };
String value;
};
explicit Selector(Vector<Component>&&);
~Selector();
const Vector<Component>& components() const { return m_components; }
Specificity specificity() const;
private:
Vector<Component> m_components;
};

View file

@ -0,0 +1,34 @@
#pragma once
class Specificity {
public:
Specificity(unsigned ids, unsigned classes, unsigned tag_names)
: m_ids(ids)
, m_classes(classes)
, m_tag_names(tag_names)
{
}
unsigned ids() const { return m_ids; }
unsigned classes() const { return m_classes; }
unsigned tag_names() const { return m_tag_names; }
bool operator<(const Specificity& other) const
{
return m_ids < other.m_ids
|| m_classes < other.m_classes
|| m_tag_names < other.m_tag_names;
}
bool operator==(const Specificity& other) const
{
return m_ids == other.m_ids
|| m_classes < other.m_classes
|| m_tag_names < other.m_tag_names;
}
private:
unsigned m_ids { 0 };
unsigned m_classes { 0 };
unsigned m_tag_names { 0 };
};

View file

@ -0,0 +1,11 @@
#include <LibHTML/CSS/StyleDeclaration.h>
StyleDeclaration::StyleDeclaration(const String& property_name, NonnullRefPtr<StyleValue>&& value)
: m_property_name(property_name)
, m_value(move(value))
{
}
StyleDeclaration::~StyleDeclaration()
{
}

View file

@ -0,0 +1,23 @@
#pragma once
#include <AK/AKString.h>
#include <LibHTML/CSS/StyleValue.h>
class StyleDeclaration : public RefCounted<StyleDeclaration> {
public:
static NonnullRefPtr<StyleDeclaration> create(const String& property_name, NonnullRefPtr<StyleValue>&& value)
{
return adopt(*new StyleDeclaration(property_name, move(value)));
}
~StyleDeclaration();
const String& property_name() const { return m_property_name; }
const StyleValue& value() const { return *m_value; }
public:
StyleDeclaration(const String& property_name, NonnullRefPtr<StyleValue>&&);
String m_property_name;
NonnullRefPtr<StyleValue> m_value;
};

View file

@ -0,0 +1,71 @@
#include <LibHTML/CSS/StyleResolver.h>
#include <LibHTML/CSS/StyleSheet.h>
#include <LibHTML/CSS/StyledNode.h>
#include <LibHTML/DOM/Document.h>
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Dump.h>
#include <stdio.h>
StyleResolver::StyleResolver(Document& document)
: m_document(document)
{
}
StyleResolver::~StyleResolver()
{
}
static bool matches(const Selector& selector, const Element& element)
{
// FIXME: Support compound selectors.
ASSERT(selector.components().size() == 1);
auto& component = selector.components().first();
switch (component.type) {
case Selector::Component::Type::Id:
return component.value == element.attribute("id");
case Selector::Component::Type::Class:
return element.has_class(component.value);
case Selector::Component::Type::TagName:
return component.value == element.tag_name();
default:
ASSERT_NOT_REACHED();
}
}
NonnullRefPtrVector<StyleRule> StyleResolver::collect_matching_rules(const Element& element) const
{
NonnullRefPtrVector<StyleRule> matching_rules;
for (auto& sheet : document().stylesheets()) {
for (auto& rule : sheet.rules()) {
for (auto& selector : rule.selectors()) {
if (matches(selector, element)) {
matching_rules.append(rule);
break;
}
}
}
}
printf("Rules matching Element{%p}\n", &element);
for (auto& rule : matching_rules) {
dump_rule(rule);
}
return matching_rules;
}
NonnullRefPtr<StyledNode> StyleResolver::create_styled_node(const Document& document)
{
return StyledNode::create(document);
}
NonnullRefPtr<StyledNode> StyleResolver::create_styled_node(const Element& element)
{
auto style = StyledNode::create(element);
auto matching_rules = collect_matching_rules(element);
for (auto& rule : matching_rules) {
for (auto& declaration : rule.declarations()) {
style->set_property(declaration.property_name(), declaration.value());
}
}
return style;
}

View file

@ -0,0 +1,29 @@
#pragma once
#include <AK/OwnPtr.h>
#include <AK/NonnullRefPtrVector.h>
class Document;
class Element;
class ParentNode;
class StyleRule;
class StyleSheet;
class StyledNode;
class StyleResolver {
public:
explicit StyleResolver(Document&);
~StyleResolver();
Document& document() { return m_document; }
const Document& document() const { return m_document; }
NonnullRefPtr<StyledNode> create_styled_node(const Element&);
NonnullRefPtr<StyledNode> create_styled_node(const Document&);
NonnullRefPtrVector<StyleRule> collect_matching_rules(const Element&) const;
private:
Document& m_document;
};

View file

@ -0,0 +1,11 @@
#include <LibHTML/CSS/StyleRule.h>
StyleRule::StyleRule(Vector<Selector>&& selectors, NonnullRefPtrVector<StyleDeclaration>&& declarations)
: m_selectors(move(selectors))
, m_declarations(move(declarations))
{
}
StyleRule::~StyleRule()
{
}

View file

@ -0,0 +1,31 @@
#pragma once
#include <AK/NonnullRefPtrVector.h>
#include <LibHTML/CSS/Selector.h>
#include <LibHTML/CSS/StyleDeclaration.h>
class StyleRule : public RefCounted<StyleRule> {
public:
static NonnullRefPtr<StyleRule> create(Vector<Selector>&& selectors, NonnullRefPtrVector<StyleDeclaration>&& declarations)
{
return adopt(*new StyleRule(move(selectors), move(declarations)));
}
~StyleRule();
const Vector<Selector>& selectors() const { return m_selectors; }
const NonnullRefPtrVector<StyleDeclaration>& declarations() const { return m_declarations; }
template<typename C>
void for_each_declaration(C callback) const
{
for (auto& declaration : m_declarations)
callback(declaration);
}
private:
StyleRule(Vector<Selector>&&, NonnullRefPtrVector<StyleDeclaration>&&);
Vector<Selector> m_selectors;
NonnullRefPtrVector<StyleDeclaration> m_declarations;
};

View file

@ -0,0 +1,10 @@
#include <LibHTML/CSS/StyleSheet.h>
StyleSheet::StyleSheet(NonnullRefPtrVector<StyleRule>&& rules)
: m_rules(move(rules))
{
}
StyleSheet::~StyleSheet()
{
}

View file

@ -0,0 +1,21 @@
#pragma once
#include <AK/NonnullRefPtrVector.h>
#include <LibHTML/CSS/StyleRule.h>
class StyleSheet : public RefCounted<StyleSheet> {
public:
static NonnullRefPtr<StyleSheet> create(NonnullRefPtrVector<StyleRule>&& rules)
{
return adopt(*new StyleSheet(move(rules)));
}
~StyleSheet();
const NonnullRefPtrVector<StyleRule>& rules() const { return m_rules; }
private:
explicit StyleSheet(NonnullRefPtrVector<StyleRule>&&);
NonnullRefPtrVector<StyleRule> m_rules;
};

View file

@ -0,0 +1,10 @@
#include "StyleValue.h"
StyleValue::StyleValue(Type type)
: m_type(type)
{
}
StyleValue::~StyleValue()
{
}

View file

@ -0,0 +1,70 @@
#pragma once
#include <AK/AKString.h>
#include <AK/RefCounted.h>
#include <AK/RefPtr.h>
#include <AK/StringView.h>
#include <LibHTML/CSS/Length.h>
class StyleValue : public RefCounted<StyleValue> {
public:
virtual ~StyleValue();
enum class Type {
Invalid,
Inherit,
Initial,
String,
Length,
};
Type type() const { return m_type; }
virtual String to_string() const = 0;
protected:
explicit StyleValue(Type);
private:
Type m_type { Type::Invalid };
};
class StringStyleValue : public StyleValue {
public:
static NonnullRefPtr<StringStyleValue> create(const String& string)
{
return adopt(*new StringStyleValue(string));
}
virtual ~StringStyleValue() override {}
String to_string() const override { return m_string; }
private:
explicit StringStyleValue(const String& string)
: StyleValue(Type::String)
, m_string(string)
{
}
String m_string;
};
class LengthStyleValue : public StyleValue {
public:
static NonnullRefPtr<LengthStyleValue> create(const Length& length)
{
return adopt(*new LengthStyleValue(length));
}
virtual ~LengthStyleValue() override {}
String to_string() const override { return m_length.to_string(); }
private:
explicit LengthStyleValue(const Length& length)
: StyleValue(Type::Length)
, m_length(length)
{
}
Length m_length;
};

View file

@ -0,0 +1,25 @@
#include <LibHTML/CSS/StyledNode.h>
StyledNode::StyledNode(const Node* node)
: m_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

@ -0,0 +1,61 @@
#pragma once
#include <AK/HashMap.h>
#include <AK/NonnullRefPtr.h>
#include <AK/AKString.h>
#include <LibHTML/TreeNode.h>
#include <LibHTML/CSS/StyleValue.h>
class Node;
enum class Display {
None,
Block,
Inline,
};
class StyledNode : public TreeNode<StyledNode> {
public:
static NonnullRefPtr<StyledNode> create(const Node& node)
{
return adopt(*new StyledNode(&node));
}
~StyledNode();
const Node* node() const { return m_node; }
template<typename Callback>
inline void for_each_child(Callback callback) const
{
for (auto* node = first_child(); node; node = node->next_sibling())
callback(*node);
}
template<typename Callback>
inline void for_each_child(Callback callback)
{
for (auto* node = first_child(); node; node = node->next_sibling())
callback(*node);
}
template<typename Callback>
inline void for_each_property(Callback callback) const
{
for (auto& it : m_property_values)
callback(it.key, *it.value);
}
void set_property(const String& name, NonnullRefPtr<StyleValue> value)
{
m_property_values.set(name, move(value));
}
Display display() const;
protected:
explicit StyledNode(const Node*);
private:
const Node* m_node { nullptr };
HashMap<String, NonnullRefPtr<StyleValue>> m_property_values;
};

View file

@ -0,0 +1,21 @@
#include <LibHTML/CSS/StyleResolver.h>
#include <LibHTML/DOM/Document.h>
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Layout/LayoutDocument.h>
#include <stdio.h>
Document::Document()
: ParentNode(NodeType::DOCUMENT_NODE)
{
}
Document::~Document()
{
}
StyleResolver& Document::style_resolver()
{
if (!m_style_resolver)
m_style_resolver = make<StyleResolver>(*this);
return *m_style_resolver;
}

View file

@ -0,0 +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;
StyleResolver& style_resolver();
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

@ -0,0 +1,64 @@
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Layout/LayoutBlock.h>
#include <LibHTML/Layout/LayoutInline.h>
Element::Element(const String& tag_name)
: ParentNode(NodeType::ELEMENT_NODE)
, m_tag_name(tag_name)
{
}
Element::~Element()
{
}
Attribute* Element::find_attribute(const String& name)
{
for (auto& attribute : m_attributes) {
if (attribute.name() == name)
return &attribute;
}
return nullptr;
}
const Attribute* Element::find_attribute(const String& name) const
{
for (auto& attribute : m_attributes) {
if (attribute.name() == name)
return &attribute;
}
return nullptr;
}
String Element::attribute(const String& name) const
{
if (auto* attribute = find_attribute(name))
return attribute->value();
return { };
}
void Element::set_attribute(const String& name, const String& value)
{
if (auto* attribute = find_attribute(name))
attribute->set_value(value);
else
m_attributes.append({ name, value });
}
void Element::set_attributes(Vector<Attribute>&& attributes)
{
m_attributes = move(attributes);
}
bool Element::has_class(const StringView& class_name) const
{
auto value = attribute("class");
if (value.is_empty())
return false;
auto parts = value.split_view(' ');
for (auto& part : parts) {
if (part == class_name)
return true;
}
return false;
}

View file

@ -0,0 +1,52 @@
#pragma once
#include <LibHTML/DOM/ParentNode.h>
#include <AK/AKString.h>
class Attribute {
public:
Attribute(const String& name, const String& value)
: m_name(name)
, m_value(value)
{
}
const String& name() const { return m_name; }
const String& value() const { return m_value; }
void set_value(const String& value) { m_value = value; }
private:
String m_name;
String m_value;
};
class Element : public ParentNode {
public:
explicit Element(const String& tag_name);
virtual ~Element() override;
const String& tag_name() const { return m_tag_name; }
String attribute(const String& name) const;
void set_attribute(const String& name, const String& value);
void set_attributes(Vector<Attribute>&&);
template<typename Callback>
void for_each_attribute(Callback callback) const
{
for (auto& attribute : m_attributes)
callback(attribute.name(), attribute.value());
}
bool has_class(const StringView&) const;
private:
Attribute* find_attribute(const String& name);
const Attribute* find_attribute(const String& name) const;
String m_tag_name;
Vector<Attribute> m_attributes;
};

View file

@ -0,0 +1,11 @@
#include <LibHTML/DOM/Node.h>
#include <LibHTML/Layout/LayoutNode.h>
Node::Node(NodeType type)
: m_type(type)
{
}
Node::~Node()
{
}

View file

@ -0,0 +1,31 @@
#pragma once
#include <AK/Badge.h>
#include <AK/RefPtr.h>
#include <AK/Vector.h>
#include <LibHTML/TreeNode.h>
enum class NodeType : unsigned {
INVALID = 0,
ELEMENT_NODE = 1,
TEXT_NODE = 3,
DOCUMENT_NODE = 9,
};
class ParentNode;
class Node : public TreeNode<Node> {
public:
virtual ~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; }
bool is_document() const { return type() == NodeType::DOCUMENT_NODE; }
bool is_parent_node() const { return is_element() || is_document(); }
protected:
explicit Node(NodeType);
NodeType m_type { NodeType::INVALID };
};

View file

@ -0,0 +1,2 @@
#include <LibHTML/DOM/ParentNode.h>

View file

@ -0,0 +1,29 @@
#pragma once
#include <LibHTML/DOM/Node.h>
class ParentNode : public Node {
public:
template<typename F> void for_each_child(F) const;
template<typename F> void for_each_child(F);
protected:
explicit ParentNode(NodeType type)
: Node(type)
{
}
};
template<typename Callback>
inline void ParentNode::for_each_child(Callback callback) const
{
for (auto* node = first_child(); node; node = node->next_sibling())
callback(*node);
}
template<typename Callback>
inline void ParentNode::for_each_child(Callback callback)
{
for (auto* node = first_child(); node; node = node->next_sibling())
callback(*node);
}

View file

@ -0,0 +1,12 @@
#include <LibHTML/DOM/Text.h>
#include <LibHTML/Layout/LayoutText.h>
Text::Text(const String& data)
: Node(NodeType::TEXT_NODE)
, m_data(data)
{
}
Text::~Text()
{
}

View file

@ -0,0 +1,15 @@
#pragma once
#include <AK/AKString.h>
#include <LibHTML/DOM/Node.h>
class Text final : public Node {
public:
explicit Text(const String&);
virtual ~Text() override;
const String& data() const { return m_data; }
private:
String m_data;
};

140
Libraries/LibHTML/Dump.cpp Normal file
View file

@ -0,0 +1,140 @@
#include <LibHTML/CSS/StyleSheet.h>
#include <LibHTML/CSS/StyledNode.h>
#include <LibHTML/DOM/Document.h>
#include <LibHTML/DOM/Element.h>
#include <LibHTML/DOM/Text.h>
#include <LibHTML/Dump.h>
#include <LibHTML/Layout/LayoutNode.h>
#include <LibHTML/Layout/LayoutText.h>
#include <stdio.h>
void dump_tree(const Node& node)
{
static int indent = 0;
for (int i = 0; i < indent; ++i)
printf(" ");
if (node.is_document()) {
printf("*Document*\n");
} else if (node.is_element()) {
printf("<%s", static_cast<const Element&>(node).tag_name().characters());
static_cast<const Element&>(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<const Text&>(node).data().characters());
}
++indent;
if (node.is_parent_node()) {
static_cast<const ParentNode&>(node).for_each_child([](auto& child) {
dump_tree(child);
});
}
--indent;
}
void dump_tree(const LayoutNode& layout_node)
{
static int indent = 0;
for (int i = 0; i < indent; ++i)
printf(" ");
String tag_name;
if (layout_node.is_anonymous())
tag_name = "(anonymous)";
else if (layout_node.node()->is_text())
tag_name = "#text";
else if (layout_node.node()->is_document())
tag_name = "#document";
else if (layout_node.node()->is_element())
tag_name = static_cast<const Element&>(*layout_node.node()).tag_name();
else
tag_name = "???";
printf("%s {%s} at (%d,%d) size %dx%d",
layout_node.class_name(),
tag_name.characters(),
layout_node.rect().x(),
layout_node.rect().y(),
layout_node.rect().width(),
layout_node.rect().height());
if (layout_node.is_text())
printf(" \"%s\"", static_cast<const LayoutText&>(layout_node).text().characters());
printf("\n");
++indent;
layout_node.for_each_child([](auto& child) {
dump_tree(child);
});
--indent;
}
void dump_tree(const StyledNode& styled_node)
{
static int indent = 0;
for (int i = 0; i < indent; ++i)
printf(" ");
String tag_name;
auto& node = *styled_node.node();
if (node.is_text())
tag_name = "#text";
else if (node.is_document())
tag_name = "#document";
else if (node.is_element())
tag_name = static_cast<const Element&>(node).tag_name();
else
tag_name = "???";
printf("%s", tag_name.characters());
printf("\n");
styled_node.for_each_property([&](auto& key, auto& value) {
for (int i = 0; i < indent; ++i)
printf(" ");
printf(" (%s: %s)\n", key.characters(), value.to_string().characters());
});
++indent;
styled_node.for_each_child([](auto& child) {
dump_tree(child);
});
--indent;
}
void dump_rule(const StyleRule& rule)
{
printf("Rule:\n");
for (auto& selector : rule.selectors()) {
printf(" Selector:\n");
for (auto& component : selector.components()) {
const char* type_description = "Unknown";
switch (component.type) {
case Selector::Component::Type::Invalid:
type_description = "Invalid";
break;
case Selector::Component::Type::Id:
type_description = "Id";
break;
case Selector::Component::Type::Class:
type_description = "Class";
break;
case Selector::Component::Type::TagName:
type_description = "TagName";
break;
}
printf(" %s:%s\n", type_description, component.value.characters());
}
}
printf(" Declarations:\n");
for (auto& declaration : rule.declarations()) {
printf(" '%s': '%s'\n", declaration.property_name().characters(), declaration.value().to_string().characters());
}
}
void dump_sheet(const StyleSheet& sheet)
{
printf("StyleSheet{%p}: %d rule(s)\n", &sheet, sheet.rules().size());
for (auto& rule : sheet.rules()) {
dump_rule(rule);
}
}

13
Libraries/LibHTML/Dump.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
class Node;
class LayoutNode;
class StyleRule;
class StyleSheet;
class StyledNode;
void dump_tree(const Node&);
void dump_tree(const StyledNode&);
void dump_tree(const LayoutNode&);
void dump_sheet(const StyleSheet&);
void dump_rule(const StyleRule&);

102
Libraries/LibHTML/Frame.cpp Normal file
View file

@ -0,0 +1,102 @@
#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/LayoutBlock.h>
#include <LibHTML/Layout/LayoutDocument.h>
#include <LibHTML/Layout/LayoutInline.h>
#include <stdio.h>
Frame::Frame()
: m_size(800, 600)
{
}
Frame::~Frame()
{
}
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;
auto styled_root = generate_style_tree();
auto create_layout_node = [](const StyledNode& styled_node) -> RefPtr<LayoutNode> {
if (styled_node.node() && styled_node.node()->is_document())
return adopt(*new LayoutDocument(static_cast<const Document&>(*styled_node.node()), styled_node));
switch (styled_node.display()) {
case Display::None:
return nullptr;
case Display::Block:
return adopt(*new LayoutBlock(*styled_node.node(), styled_node));
case Display::Inline:
return adopt(*new LayoutInline(*styled_node.node(), styled_node));
default:
ASSERT_NOT_REACHED();
}
};
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;
};
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);
}

23
Libraries/LibHTML/Frame.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include <LibHTML/DOM/Document.h>
#include <SharedGraphics/Size.h>
class Frame {
public:
Frame();
~Frame();
Document* document() { return m_document.ptr(); }
const Document* document() const { return m_document.ptr(); }
void set_document(Document*);
void layout();
private:
RefPtr<StyledNode> generate_style_tree();
RefPtr<Document> m_document;
Size m_size;
};

View file

@ -0,0 +1,9 @@
#include <LibHTML/Layout/ComputedStyle.h>
ComputedStyle::ComputedStyle()
{
}
ComputedStyle::~ComputedStyle()
{
}

View file

@ -0,0 +1,47 @@
#pragma once
#include <LibHTML/CSS/LengthBox.h>
#include <SharedGraphics/Color.h>
#include <SharedGraphics/Size.h>
enum FontStyle {
Normal,
Bold,
};
class ComputedStyle {
public:
ComputedStyle();
~ComputedStyle();
Color text_color() const { return m_text_color; }
Color background_color() const { return m_background_color; }
LengthBox& offset() { return m_offset; }
LengthBox& margin() { return m_margin; }
LengthBox& padding() { return m_padding; }
LengthBox& border() { return m_border; }
const LengthBox& offset() const { return m_offset; }
const LengthBox& margin() const { return m_margin; }
const LengthBox& padding() const { return m_padding; }
const LengthBox& border() const { return m_border; }
FontStyle font_style() const { return m_font_style; }
const Size& size() const { return m_size; }
Size& size() { return m_size; }
private:
Color m_text_color;
Color m_background_color;
LengthBox m_offset;
LengthBox m_margin;
LengthBox m_padding;
LengthBox m_border;
Size m_size;
FontStyle m_font_style { FontStyle::Normal };
};

View file

@ -0,0 +1,28 @@
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Layout/LayoutBlock.h>
LayoutBlock::LayoutBlock(const Node& node, const StyledNode& styled_node)
: LayoutNode(&node, styled_node)
{
}
LayoutBlock::~LayoutBlock()
{
}
void LayoutBlock::layout()
{
compute_width();
LayoutNode::layout();
compute_height();
}
void LayoutBlock::compute_width()
{
}
void LayoutBlock::compute_height()
{
}

View file

@ -0,0 +1,21 @@
#pragma once
#include <LibHTML/Layout/LayoutNode.h>
class Element;
class LayoutBlock : public LayoutNode {
public:
LayoutBlock(const Node&, const StyledNode&);
virtual ~LayoutBlock() override;
virtual const char* class_name() const override { return "LayoutBlock"; }
virtual void layout() override;
private:
virtual bool is_block() const override { return true; }
void compute_width();
void compute_height();
};

View file

@ -0,0 +1,16 @@
#include <LibHTML/Layout/LayoutDocument.h>
LayoutDocument::LayoutDocument(const Document& document, const StyledNode& styled_node)
: LayoutBlock(document, styled_node)
{
}
LayoutDocument::~LayoutDocument()
{
}
void LayoutDocument::layout()
{
rect().set_width(style().size().width());
LayoutNode::layout();
}

View file

@ -0,0 +1,15 @@
#pragma once
#include <LibHTML/Layout/LayoutBlock.h>
#include <LibHTML/DOM/Document.h>
class LayoutDocument final : public LayoutBlock {
public:
LayoutDocument(const Document&, const StyledNode&);
virtual ~LayoutDocument() override;
const Document& node() const { return static_cast<const Document&>(*LayoutNode::node()); }
virtual const char* class_name() const override { return "LayoutDocument"; }
virtual void layout() override;
private:
};

View file

@ -0,0 +1,11 @@
#include <LibHTML/DOM/Element.h>
#include <LibHTML/Layout/LayoutInline.h>
LayoutInline::LayoutInline(const Node& node, const StyledNode& styled_node)
: LayoutNode(&node, styled_node)
{
}
LayoutInline::~LayoutInline()
{
}

View file

@ -0,0 +1,15 @@
#pragma once
#include <LibHTML/Layout/LayoutNode.h>
class Element;
class LayoutInline : public LayoutNode {
public:
LayoutInline(const Node&, const StyledNode&);
virtual ~LayoutInline() override;
virtual const char* class_name() const override { return "LayoutInline"; }
private:
};

View file

@ -0,0 +1,29 @@
#include <LibHTML/Layout/LayoutBlock.h>
#include <LibHTML/Layout/LayoutNode.h>
#include <LibHTML/CSS/StyledNode.h>
LayoutNode::LayoutNode(const Node* node, const StyledNode& styled_node)
: m_node(node)
, m_styled_node(styled_node)
{
}
LayoutNode::~LayoutNode()
{
}
void LayoutNode::layout()
{
for_each_child([](auto& child) {
child.layout();
});
}
const LayoutBlock* LayoutNode::containing_block() const
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (ancestor->is_block())
return static_cast<const LayoutBlock*>(ancestor);
}
return nullptr;
}

View file

@ -0,0 +1,58 @@
#pragma once
#include <AK/NonnullRefPtr.h>
#include <AK/Vector.h>
#include <LibHTML/Layout/ComputedStyle.h>
#include <LibHTML/TreeNode.h>
#include <SharedGraphics/Rect.h>
class Node;
class LayoutBlock;
class StyledNode;
class LayoutNode : public TreeNode<LayoutNode> {
public:
virtual ~LayoutNode();
const Rect& rect() const { return m_rect; }
Rect& rect() { return m_rect; }
void set_rect(const Rect& rect) { m_rect = rect; }
ComputedStyle& style() { return m_style; }
const ComputedStyle& style() const { return m_style; }
bool is_anonymous() const { return !m_node; }
const Node* node() const { return m_node; }
template<typename Callback>
inline void for_each_child(Callback callback) const
{
for (auto* node = first_child(); node; node = node->next_sibling())
callback(*node);
}
template<typename Callback>
inline void for_each_child(Callback callback)
{
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; }
virtual bool is_block() const { return false; }
virtual void layout();
const LayoutBlock* containing_block() const;
protected:
explicit LayoutNode(const Node*, const StyledNode&);
private:
const Node* m_node { nullptr };
NonnullRefPtr<StyledNode> m_styled_node;
ComputedStyle m_style;
Rect m_rect;
};

View file

@ -0,0 +1,39 @@
#include <LibHTML/Layout/LayoutText.h>
#include <ctype.h>
LayoutText::LayoutText(const Text& text, const StyledNode& styled_node)
: LayoutNode(&text, styled_node)
{
}
LayoutText::~LayoutText()
{
}
static bool is_all_whitespace(const String& string)
{
for (int i = 0; i < string.length(); ++i) {
if (!isspace(string[i]))
return false;
}
return true;
}
const String& LayoutText::text() const
{
static String one_space = " ";
if (is_all_whitespace(node().data()))
return one_space;
return node().data();
}
void LayoutText::compute_runs()
{
}
void LayoutText::layout()
{
ASSERT(!has_children());
compute_runs();
}

View file

@ -0,0 +1,30 @@
#pragma once
#include <LibHTML/Layout/LayoutNode.h>
#include <LibHTML/DOM/Text.h>
class LayoutText : public LayoutNode {
public:
LayoutText(const Text&, const StyledNode&);
virtual ~LayoutText() override;
const Text& node() const { return static_cast<const Text&>(*LayoutNode::node()); }
const String& text() const;
virtual const char* class_name() const override { return "LayoutText"; }
virtual bool is_text() const final { return true; }
virtual void layout() override;
struct Run {
Point pos;
String text;
};
const Vector<Run>& runs() const { return m_runs; }
private:
void compute_runs();
Vector<Run> m_runs;
};

View file

@ -0,0 +1,21 @@
include ../../Makefile.common
LIBRARY = libhtml.a
all: $(LIBRARY) tho
include Makefile.shared
tho: $(TEST_OBJS) $(LIBRARY)
$(LD) -o $@ $(LDFLAGS) -L. $(TEST_OBJS) -lhtml -lgui -lcore -lc
$(LIBRARY): $(LIBHTML_OBJS)
@echo "LIB $@"; $(AR) rcs $@ $(LIBHTML_OBJS)
install: $(LIBRARY)
mkdir -p ../Root/usr/include/LibHTML
# Copy headers
rsync -r -a --include '*/' --include '*.h' --exclude '*' . ../Root/usr/include/LibHTML
# Install the library
cp $(LIBRARY) ../Root/usr/lib

View file

@ -0,0 +1,20 @@
all: tho
CXXFLAGS = -W -Wall -O -g -I. -I../ -std=c++17
EXTRA_OBJS = \
../AK/StringImpl.o \
../AK/String.o \
../AK/StringBuilder.o \
../AK/StringView.o \
../LibCore/CEventLoop.o \
../LibCore/CObject.o \
../LibCore/CEvent.o \
../LibCore/CIODevice.o \
../LibCore/CFile.o
include Makefile.shared
tho: $(OBJS)
$(CXX) -o $@ $(LDFLAGS) $(OBJS)

View file

@ -0,0 +1,47 @@
LIBHTML_OBJS = \
DOM/Node.o \
DOM/ParentNode.o \
DOM/Element.o \
DOM/Document.o \
DOM/Text.o \
CSS/Selector.o \
CSS/StyleSheet.o \
CSS/StyleRule.o \
CSS/StyleDeclaration.o \
CSS/StyleValue.o \
CSS/StyledNode.o \
CSS/StyleResolver.o \
CSS/DefaultStyleSheetSource.o \
Parser/HTMLParser.o \
Parser/CSSParser.o \
Layout/LayoutNode.o \
Layout/LayoutText.o \
Layout/LayoutBlock.o \
Layout/LayoutInline.o \
Layout/LayoutDocument.o \
Layout/ComputedStyle.o \
Frame.o \
Dump.o
GENERATED_SOURCES = \
CSS/DefaultStyleSheetSource.cpp
TEST_OBJS = test.o
TEST_PROGRAM = tho
OBJS = $(EXTRA_OBJS) $(LIBHTML_OBJS) $(TEST_OBJS)
LIBRARY = libhtml.a
DEFINES += -DUSERLAND
CSS/DefaultStyleSheetSource.cpp: CSS/Default.css Scripts/GenerateStyleSheetSource.sh
@echo "GENERATE $@"; Scripts/GenerateStyleSheetSource.sh default_stylesheet_source $< > $@
.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
-include $(OBJS:%.o=%.d)
clean:
@echo "CLEAN"; rm -f $(TEST_PROGRAM) $(LIBRARY) $(OBJS) *.d $(GENERATED_SOURCES)

View file

@ -0,0 +1,151 @@
#include <LibHTML/CSS/StyleSheet.h>
#include <LibHTML/Parser/CSSParser.h>
#include <ctype.h>
#include <stdio.h>
NonnullRefPtr<StyleValue> parse_css_value(const StringView& view)
{
String string(view);
bool ok;
int as_int = string.to_int(ok);
if (ok)
return LengthStyleValue::create(Length(as_int, Length::Type::Absolute));
unsigned as_uint = string.to_uint(ok);
if (ok)
return LengthStyleValue::create(Length(as_uint, Length::Type::Absolute));
if (string == "auto")
return LengthStyleValue::create(Length());
return StringStyleValue::create(string);
}
NonnullRefPtr<StyleSheet> parse_css(const String& css)
{
NonnullRefPtrVector<StyleRule> rules;
enum class State {
Free,
InSelectorComponent,
InPropertyName,
InPropertyValue,
};
struct CurrentRule {
Vector<Selector> selectors;
NonnullRefPtrVector<StyleDeclaration> declarations;
};
CurrentRule current_rule;
Vector<char> buffer;
int index = 0;
auto peek = [&]() -> char {
if (index < css.length())
return css[index];
return 0;
};
auto consume_specific = [&](char ch) {
ASSERT(peek() == ch);
++index;
return ch;
};
auto consume_one = [&]() -> char {
return css[index++];
};
auto consume_whitespace = [&] {
while (isspace(peek()))
++index;
};
auto is_valid_selector_char = [](char ch) {
return isalnum(ch) || ch == '-' || ch == '_';
};
auto parse_selector = [&] {
consume_whitespace();
Selector::Component::Type type;
if (peek() == '.')
type = Selector::Component::Type::Class;
else if (peek() == '#')
type = Selector::Component::Type::Id;
else
type = Selector::Component::Type::TagName;
while (is_valid_selector_char(peek()))
buffer.append(consume_one());
ASSERT(!buffer.is_null());
auto component_string = String::copy(buffer);
Vector<Selector::Component> components;
components.append({ type, component_string });
buffer.clear();
current_rule.selectors.append(Selector(move(components)));
};
auto parse_selector_list = [&] {
for (;;) {
parse_selector();
consume_whitespace();
if (peek() == ',') {
consume_one();
continue;
}
if (peek() == '{')
break;
}
};
auto is_valid_property_name_char = [](char ch) {
return !isspace(ch) && ch != ':';
};
auto is_valid_property_value_char = [](char ch) {
return !isspace(ch) && ch != ';';
};
auto parse_declaration = [&] {
consume_whitespace();
buffer.clear();
while (is_valid_property_name_char(peek()))
buffer.append(consume_one());
auto property_name = String::copy(buffer);
buffer.clear();
consume_whitespace();
consume_specific(':');
consume_whitespace();
while (is_valid_property_value_char(peek()))
buffer.append(consume_one());
auto property_value = String::copy(buffer);
buffer.clear();
consume_specific(';');
current_rule.declarations.append(StyleDeclaration::create(property_name, parse_css_value(property_value)));
};
auto parse_declarations = [&] {
for (;;) {
parse_declaration();
consume_whitespace();
if (peek() == '}')
break;
}
};
auto parse_rule = [&] {
parse_selector_list();
consume_specific('{');
parse_declarations();
consume_specific('}');
rules.append(StyleRule::create(move(current_rule.selectors), move(current_rule.declarations)));
};
while (index < css.length()) {
parse_rule();
}
return StyleSheet::create(move(rules));
}

View file

@ -0,0 +1,7 @@
#pragma once
#include <AK/NonnullRefPtr.h>
#include <LibHTML/CSS/StyleSheet.h>
NonnullRefPtr<StyleSheet> parse_css(const String&);

View file

@ -0,0 +1,236 @@
#include <AK/NonnullRefPtrVector.h>
#include <LibHTML/DOM/Element.h>
#include <LibHTML/DOM/Text.h>
#include <LibHTML/Parser/HTMLParser.h>
#include <ctype.h>
#include <stdio.h>
static NonnullRefPtr<Element> create_element(const String& tag_name)
{
return adopt(*new Element(tag_name));
}
static bool is_valid_in_attribute_name(char ch)
{
return isalnum(ch) || ch == '_' || ch == '-';
}
static bool is_self_closing_tag(const String& tag_name)
{
return tag_name == "area"
|| tag_name == "base"
|| tag_name == "br"
|| tag_name == "col"
|| tag_name == "embed"
|| tag_name == "hr"
|| tag_name == "img"
|| tag_name == "input"
|| tag_name == "link"
|| tag_name == "meta"
|| tag_name == "param"
|| tag_name == "source"
|| tag_name == "track"
|| tag_name == "wbr";
}
NonnullRefPtr<Document> parse_html(const String& html)
{
NonnullRefPtrVector<ParentNode> node_stack;
auto doc = adopt(*new Document);
node_stack.append(doc);
enum class State {
Free = 0,
BeforeTagName,
InTagName,
InAttributeList,
InAttributeName,
BeforeAttributeValue,
InAttributeValueNoQuote,
InAttributeValueSingleQuote,
InAttributeValueDoubleQuote,
};
auto state = State::Free;
Vector<char, 256> text_buffer;
Vector<char, 32> tag_name_buffer;
Vector<Attribute> attributes;
Vector<char, 256> attribute_name_buffer;
Vector<char, 256> attribute_value_buffer;
bool is_slash_tag = false;
auto move_to_state = [&](State new_state) {
if (new_state == State::BeforeTagName) {
is_slash_tag = false;
tag_name_buffer.clear();
attributes.clear();
}
if (new_state == State::InAttributeName)
attribute_name_buffer.clear();
if (new_state == State::BeforeAttributeValue)
attribute_value_buffer.clear();
if (state == State::Free && !text_buffer.is_empty()) {
auto text_node = adopt(*new Text(String::copy(text_buffer)));
text_buffer.clear();
node_stack.last().append_child(text_node);
}
state = new_state;
text_buffer.clear();
};
auto close_tag = [&] {
if (node_stack.size() > 1)
node_stack.take_last();
};
auto open_tag = [&] {
auto new_element = create_element(String::copy(tag_name_buffer));
tag_name_buffer.clear();
new_element->set_attributes(move(attributes));
node_stack.append(new_element);
if (node_stack.size() != 1)
node_stack[node_stack.size() - 2].append_child(new_element);
if (is_self_closing_tag(new_element->tag_name()))
close_tag();
};
auto commit_tag = [&] {
if (is_slash_tag)
close_tag();
else
open_tag();
};
auto commit_attribute = [&] {
attributes.append({ String::copy(attribute_name_buffer), String::copy(attribute_value_buffer) });
};
for (int i = 0; i < html.length(); ++i) {
char ch = html[i];
switch (state) {
case State::Free:
if (ch == '<') {
is_slash_tag = false;
move_to_state(State::BeforeTagName);
break;
}
text_buffer.append(ch);
break;
case State::BeforeTagName:
if (ch == '/') {
is_slash_tag = true;
break;
}
if (ch == '>') {
move_to_state(State::Free);
break;
}
if (!isalpha(ch))
break;
move_to_state(State::InTagName);
[[fallthrough]];
case State::InTagName:
if (isspace(ch)) {
move_to_state(State::InAttributeList);
break;
}
if (ch == '>') {
commit_tag();
move_to_state(State::Free);
break;
}
tag_name_buffer.append(ch);
break;
case State::InAttributeList:
if (ch == '>') {
commit_tag();
move_to_state(State::Free);
break;
}
if (!isalpha(ch))
break;
move_to_state(State::InAttributeName);
[[fallthrough]];
case State::InAttributeName:
if (is_valid_in_attribute_name(ch)) {
attribute_name_buffer.append(ch);
break;
}
if (isspace(ch)) {
commit_attribute();
break;
}
if (ch == '>') {
commit_tag();
move_to_state(State::Free);
break;
}
if (ch == '=') {
move_to_state(State::BeforeAttributeValue);
break;
}
break;
case State::BeforeAttributeValue:
if (ch == '\'') {
move_to_state(State::InAttributeValueSingleQuote);
break;
}
if (ch == '"') {
move_to_state(State::InAttributeValueDoubleQuote);
break;
}
if (ch == '>') {
commit_tag();
move_to_state(State::Free);
break;
}
if (isspace(ch)) {
commit_attribute();
move_to_state(State::InAttributeList);
break;
}
break;
case State::InAttributeValueSingleQuote:
if (ch == '\'') {
commit_attribute();
move_to_state(State::InAttributeList);
break;
}
attribute_value_buffer.append(ch);
break;
case State::InAttributeValueDoubleQuote:
if (ch == '"') {
commit_attribute();
move_to_state(State::InAttributeList);
break;
}
attribute_value_buffer.append(ch);
break;
case State::InAttributeValueNoQuote:
if (isspace(ch)) {
commit_attribute();
move_to_state(State::InAttributeList);
break;
}
if (ch == '>') {
commit_tag();
move_to_state(State::Free);
break;
}
attribute_value_buffer.append(ch);
break;
default:
fprintf(stderr, "Unhandled state %d\n", (int)state);
ASSERT_NOT_REACHED();
}
}
return doc;
}

View file

@ -0,0 +1,7 @@
#pragma once
#include <AK/NonnullRefPtr.h>
#include <LibHTML/DOM/Document.h>
NonnullRefPtr<Document> parse_html(const String&);

View file

@ -0,0 +1,10 @@
#!/bin/bash
echo "extern const char $1[];"
echo "const char $1[] = \"\\"
IFS=$'\n'
for line in $(cat $2); do
echo $line"\\"
done
echo "\";"

View file

@ -0,0 +1,61 @@
#pragma once
#include <AK/Assertions.h>
#include <AK/NonnullRefPtr.h>
template<typename T>
class TreeNode {
public:
void ref()
{
ASSERT(m_ref_count);
++m_ref_count;
}
void deref()
{
ASSERT(m_ref_count);
if (!--m_ref_count)
delete static_cast<T*>(this);
}
int ref_count() const { return m_ref_count; }
T* parent() { return m_parent; }
const T* parent() const { return m_parent; }
bool has_children() const { return m_first_child; }
T* next_sibling() { return m_next_sibling; }
T* previous_sibling() { return m_previous_sibling; }
T* first_child() { return m_first_child; }
T* last_child() { return m_last_child; }
const T* next_sibling() const { return m_next_sibling; }
const T* previous_sibling() const { return m_previous_sibling; }
const T* first_child() const { return m_first_child; }
const T* last_child() const { return m_last_child; }
void append_child(NonnullRefPtr<T> node);
protected:
TreeNode() { }
private:
int m_ref_count { 1 };
T* m_parent { nullptr };
T* m_first_child { nullptr };
T* m_last_child { nullptr };
T* m_next_sibling { nullptr };
T* m_previous_sibling { nullptr };
};
template<typename T>
inline void TreeNode<T>::append_child(NonnullRefPtr<T> node)
{
ASSERT(!node->m_parent);
if (m_last_child)
m_last_child->m_next_sibling = node.ptr();
node->m_previous_sibling = m_last_child;
node->m_parent = static_cast<T*>(this);
m_last_child = &node.leak_ref();
if (!m_first_child)
m_first_child = m_last_child;
}

View file

@ -0,0 +1,36 @@
#include <LibCore/CFile.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>
int main(int argc, char** argv)
{
CFile f(argc == 1 ? "/home/anon/small.html" : argv[1]);
if (!f.open(CIODevice::ReadOnly)) {
fprintf(stderr, "Error: %s\n", f.error_string());
return 1;
}
extern const char default_stylesheet_source[];
String css = default_stylesheet_source;
auto sheet = parse_css(css);
dump_sheet(sheet);
String html = String::copy(f.read_all());
auto document = parse_html(html);
dump_tree(document);
document->add_sheet(*sheet);
auto frame = make<Frame>();
frame->set_document(document);
frame->layout();
return 0;
}