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:
parent
63814ffebf
commit
04b9dc2d30
328 changed files with 36 additions and 36 deletions
1
Libraries/LibHTML/CSS/.gitignore
vendored
Normal file
1
Libraries/LibHTML/CSS/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
DefaultStyleSheetSource.cpp
|
41
Libraries/LibHTML/CSS/Default.css
Normal file
41
Libraries/LibHTML/CSS/Default.css
Normal 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;
|
||||
}
|
35
Libraries/LibHTML/CSS/Length.h
Normal file
35
Libraries/LibHTML/CSS/Length.h
Normal 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 };
|
||||
};
|
10
Libraries/LibHTML/CSS/LengthBox.h
Normal file
10
Libraries/LibHTML/CSS/LengthBox.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibHTML/CSS/Length.h>
|
||||
|
||||
struct LengthBox {
|
||||
Length top;
|
||||
Length right;
|
||||
Length bottom;
|
||||
Length left;
|
||||
};
|
35
Libraries/LibHTML/CSS/Selector.cpp
Normal file
35
Libraries/LibHTML/CSS/Selector.cpp
Normal 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 };
|
||||
}
|
29
Libraries/LibHTML/CSS/Selector.h
Normal file
29
Libraries/LibHTML/CSS/Selector.h
Normal 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;
|
||||
};
|
34
Libraries/LibHTML/CSS/Specificity.h
Normal file
34
Libraries/LibHTML/CSS/Specificity.h
Normal 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 };
|
||||
};
|
11
Libraries/LibHTML/CSS/StyleDeclaration.cpp
Normal file
11
Libraries/LibHTML/CSS/StyleDeclaration.cpp
Normal 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()
|
||||
{
|
||||
}
|
23
Libraries/LibHTML/CSS/StyleDeclaration.h
Normal file
23
Libraries/LibHTML/CSS/StyleDeclaration.h
Normal 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;
|
||||
};
|
71
Libraries/LibHTML/CSS/StyleResolver.cpp
Normal file
71
Libraries/LibHTML/CSS/StyleResolver.cpp
Normal 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;
|
||||
}
|
29
Libraries/LibHTML/CSS/StyleResolver.h
Normal file
29
Libraries/LibHTML/CSS/StyleResolver.h
Normal 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;
|
||||
};
|
11
Libraries/LibHTML/CSS/StyleRule.cpp
Normal file
11
Libraries/LibHTML/CSS/StyleRule.cpp
Normal 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()
|
||||
{
|
||||
}
|
31
Libraries/LibHTML/CSS/StyleRule.h
Normal file
31
Libraries/LibHTML/CSS/StyleRule.h
Normal 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;
|
||||
};
|
10
Libraries/LibHTML/CSS/StyleSheet.cpp
Normal file
10
Libraries/LibHTML/CSS/StyleSheet.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <LibHTML/CSS/StyleSheet.h>
|
||||
|
||||
StyleSheet::StyleSheet(NonnullRefPtrVector<StyleRule>&& rules)
|
||||
: m_rules(move(rules))
|
||||
{
|
||||
}
|
||||
|
||||
StyleSheet::~StyleSheet()
|
||||
{
|
||||
}
|
21
Libraries/LibHTML/CSS/StyleSheet.h
Normal file
21
Libraries/LibHTML/CSS/StyleSheet.h
Normal 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;
|
||||
};
|
10
Libraries/LibHTML/CSS/StyleValue.cpp
Normal file
10
Libraries/LibHTML/CSS/StyleValue.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include "StyleValue.h"
|
||||
|
||||
StyleValue::StyleValue(Type type)
|
||||
: m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
StyleValue::~StyleValue()
|
||||
{
|
||||
}
|
70
Libraries/LibHTML/CSS/StyleValue.h
Normal file
70
Libraries/LibHTML/CSS/StyleValue.h
Normal 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;
|
||||
};
|
25
Libraries/LibHTML/CSS/StyledNode.cpp
Normal file
25
Libraries/LibHTML/CSS/StyledNode.cpp
Normal 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();
|
||||
}
|
61
Libraries/LibHTML/CSS/StyledNode.h
Normal file
61
Libraries/LibHTML/CSS/StyledNode.h
Normal 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;
|
||||
};
|
21
Libraries/LibHTML/DOM/Document.cpp
Normal file
21
Libraries/LibHTML/DOM/Document.cpp
Normal 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;
|
||||
}
|
27
Libraries/LibHTML/DOM/Document.h
Normal file
27
Libraries/LibHTML/DOM/Document.h
Normal 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;
|
||||
};
|
64
Libraries/LibHTML/DOM/Element.cpp
Normal file
64
Libraries/LibHTML/DOM/Element.cpp
Normal 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;
|
||||
}
|
52
Libraries/LibHTML/DOM/Element.h
Normal file
52
Libraries/LibHTML/DOM/Element.h
Normal 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;
|
||||
};
|
||||
|
11
Libraries/LibHTML/DOM/Node.cpp
Normal file
11
Libraries/LibHTML/DOM/Node.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <LibHTML/DOM/Node.h>
|
||||
#include <LibHTML/Layout/LayoutNode.h>
|
||||
|
||||
Node::Node(NodeType type)
|
||||
: m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
}
|
31
Libraries/LibHTML/DOM/Node.h
Normal file
31
Libraries/LibHTML/DOM/Node.h
Normal 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 };
|
||||
};
|
2
Libraries/LibHTML/DOM/ParentNode.cpp
Normal file
2
Libraries/LibHTML/DOM/ParentNode.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
#include <LibHTML/DOM/ParentNode.h>
|
||||
|
29
Libraries/LibHTML/DOM/ParentNode.h
Normal file
29
Libraries/LibHTML/DOM/ParentNode.h
Normal 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);
|
||||
}
|
12
Libraries/LibHTML/DOM/Text.cpp
Normal file
12
Libraries/LibHTML/DOM/Text.cpp
Normal 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()
|
||||
{
|
||||
}
|
15
Libraries/LibHTML/DOM/Text.h
Normal file
15
Libraries/LibHTML/DOM/Text.h
Normal 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
140
Libraries/LibHTML/Dump.cpp
Normal 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
13
Libraries/LibHTML/Dump.h
Normal 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
102
Libraries/LibHTML/Frame.cpp
Normal 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
23
Libraries/LibHTML/Frame.h
Normal 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;
|
||||
};
|
9
Libraries/LibHTML/Layout/ComputedStyle.cpp
Normal file
9
Libraries/LibHTML/Layout/ComputedStyle.cpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include <LibHTML/Layout/ComputedStyle.h>
|
||||
|
||||
ComputedStyle::ComputedStyle()
|
||||
{
|
||||
}
|
||||
|
||||
ComputedStyle::~ComputedStyle()
|
||||
{
|
||||
}
|
47
Libraries/LibHTML/Layout/ComputedStyle.h
Normal file
47
Libraries/LibHTML/Layout/ComputedStyle.h
Normal 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 };
|
||||
};
|
28
Libraries/LibHTML/Layout/LayoutBlock.cpp
Normal file
28
Libraries/LibHTML/Layout/LayoutBlock.cpp
Normal 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()
|
||||
{
|
||||
}
|
21
Libraries/LibHTML/Layout/LayoutBlock.h
Normal file
21
Libraries/LibHTML/Layout/LayoutBlock.h
Normal 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();
|
||||
};
|
16
Libraries/LibHTML/Layout/LayoutDocument.cpp
Normal file
16
Libraries/LibHTML/Layout/LayoutDocument.cpp
Normal 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();
|
||||
}
|
15
Libraries/LibHTML/Layout/LayoutDocument.h
Normal file
15
Libraries/LibHTML/Layout/LayoutDocument.h
Normal 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:
|
||||
};
|
11
Libraries/LibHTML/Layout/LayoutInline.cpp
Normal file
11
Libraries/LibHTML/Layout/LayoutInline.cpp
Normal 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()
|
||||
{
|
||||
}
|
15
Libraries/LibHTML/Layout/LayoutInline.h
Normal file
15
Libraries/LibHTML/Layout/LayoutInline.h
Normal 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:
|
||||
};
|
29
Libraries/LibHTML/Layout/LayoutNode.cpp
Normal file
29
Libraries/LibHTML/Layout/LayoutNode.cpp
Normal 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;
|
||||
}
|
58
Libraries/LibHTML/Layout/LayoutNode.h
Normal file
58
Libraries/LibHTML/Layout/LayoutNode.h
Normal 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;
|
||||
};
|
39
Libraries/LibHTML/Layout/LayoutText.cpp
Normal file
39
Libraries/LibHTML/Layout/LayoutText.cpp
Normal 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();
|
||||
}
|
30
Libraries/LibHTML/Layout/LayoutText.h
Normal file
30
Libraries/LibHTML/Layout/LayoutText.h
Normal 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;
|
||||
};
|
21
Libraries/LibHTML/Makefile
Normal file
21
Libraries/LibHTML/Makefile
Normal 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
|
||||
|
20
Libraries/LibHTML/Makefile.host
Normal file
20
Libraries/LibHTML/Makefile.host
Normal 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)
|
||||
|
47
Libraries/LibHTML/Makefile.shared
Normal file
47
Libraries/LibHTML/Makefile.shared
Normal 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)
|
||||
|
151
Libraries/LibHTML/Parser/CSSParser.cpp
Normal file
151
Libraries/LibHTML/Parser/CSSParser.cpp
Normal 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));
|
||||
}
|
7
Libraries/LibHTML/Parser/CSSParser.h
Normal file
7
Libraries/LibHTML/Parser/CSSParser.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibHTML/CSS/StyleSheet.h>
|
||||
|
||||
NonnullRefPtr<StyleSheet> parse_css(const String&);
|
||||
|
236
Libraries/LibHTML/Parser/HTMLParser.cpp
Normal file
236
Libraries/LibHTML/Parser/HTMLParser.cpp
Normal 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;
|
||||
}
|
7
Libraries/LibHTML/Parser/HTMLParser.h
Normal file
7
Libraries/LibHTML/Parser/HTMLParser.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibHTML/DOM/Document.h>
|
||||
|
||||
NonnullRefPtr<Document> parse_html(const String&);
|
||||
|
10
Libraries/LibHTML/Scripts/GenerateStyleSheetSource.sh
Executable file
10
Libraries/LibHTML/Scripts/GenerateStyleSheetSource.sh
Executable 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 "\";"
|
||||
|
61
Libraries/LibHTML/TreeNode.h
Normal file
61
Libraries/LibHTML/TreeNode.h
Normal 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;
|
||||
}
|
36
Libraries/LibHTML/test.cpp
Normal file
36
Libraries/LibHTML/test.cpp
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue