mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 06:48:12 +00:00
LibHTML: Add basic <!DOCTYPE> parsing and a DocumentType class
Plus, Document::fixup() will now make sure that the document always starts with a doctype node, followed by an <html> element.
This commit is contained in:
parent
850955053f
commit
fc53867937
9 changed files with 84 additions and 6 deletions
|
@ -2,6 +2,7 @@
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibHTML/CSS/StyleResolver.h>
|
#include <LibHTML/CSS/StyleResolver.h>
|
||||||
#include <LibHTML/DOM/Document.h>
|
#include <LibHTML/DOM/Document.h>
|
||||||
|
#include <LibHTML/DOM/DocumentType.h>
|
||||||
#include <LibHTML/DOM/Element.h>
|
#include <LibHTML/DOM/Element.h>
|
||||||
#include <LibHTML/DOM/HTMLBodyElement.h>
|
#include <LibHTML/DOM/HTMLBodyElement.h>
|
||||||
#include <LibHTML/DOM/HTMLHeadElement.h>
|
#include <LibHTML/DOM/HTMLHeadElement.h>
|
||||||
|
@ -29,11 +30,14 @@ StyleResolver& Document::style_resolver()
|
||||||
|
|
||||||
void Document::fixup()
|
void Document::fixup()
|
||||||
{
|
{
|
||||||
if (is<HTMLHtmlElement>(first_child()))
|
if (!is<DocumentType>(first_child()))
|
||||||
|
prepend_child(adopt(*new DocumentType(*this)));
|
||||||
|
|
||||||
|
if (is<HTMLHtmlElement>(first_child()->next_sibling()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
NonnullRefPtr<Element> body = adopt(*new Element(*this, "body"));
|
auto body = adopt(*new HTMLBodyElement(*this, "body"));
|
||||||
NonnullRefPtr<Element> html = adopt(*new Element(*this, "html"));
|
auto html = adopt(*new HTMLHtmlElement(*this, "html"));
|
||||||
html->append_child(body);
|
html->append_child(body);
|
||||||
this->donate_all_children_to(body);
|
this->donate_all_children_to(body);
|
||||||
this->append_child(html);
|
this->append_child(html);
|
||||||
|
|
10
Libraries/LibHTML/DOM/DocumentType.cpp
Normal file
10
Libraries/LibHTML/DOM/DocumentType.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include <LibHTML/DOM/DocumentType.h>
|
||||||
|
|
||||||
|
DocumentType::DocumentType(Document& document)
|
||||||
|
: Node(document, NodeType::DOCUMENT_TYPE_NODE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentType::~DocumentType()
|
||||||
|
{
|
||||||
|
}
|
17
Libraries/LibHTML/DOM/DocumentType.h
Normal file
17
Libraries/LibHTML/DOM/DocumentType.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibHTML/DOM/Node.h>
|
||||||
|
|
||||||
|
class DocumentType final : public Node {
|
||||||
|
public:
|
||||||
|
explicit DocumentType(Document&);
|
||||||
|
virtual ~DocumentType() override;
|
||||||
|
|
||||||
|
virtual String tag_name() const override { return "!DOCTYPE"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline bool is<DocumentType>(const Node& node)
|
||||||
|
{
|
||||||
|
return node.type() == NodeType::DOCUMENT_TYPE_NODE;
|
||||||
|
}
|
|
@ -97,3 +97,8 @@ const Element* Node::previous_element_sibling() const
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefPtr<LayoutNode> Node::create_layout_node(const StyleResolver&, const StyleProperties*) const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ enum class NodeType : unsigned {
|
||||||
ELEMENT_NODE = 1,
|
ELEMENT_NODE = 1,
|
||||||
TEXT_NODE = 3,
|
TEXT_NODE = 3,
|
||||||
DOCUMENT_NODE = 9,
|
DOCUMENT_NODE = 9,
|
||||||
|
DOCUMENT_TYPE_NODE = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Document;
|
class Document;
|
||||||
|
@ -30,9 +31,10 @@ public:
|
||||||
bool is_element() const { return type() == NodeType::ELEMENT_NODE; }
|
bool is_element() const { return type() == NodeType::ELEMENT_NODE; }
|
||||||
bool is_text() const { return type() == NodeType::TEXT_NODE; }
|
bool is_text() const { return type() == NodeType::TEXT_NODE; }
|
||||||
bool is_document() const { return type() == NodeType::DOCUMENT_NODE; }
|
bool is_document() const { return type() == NodeType::DOCUMENT_NODE; }
|
||||||
|
bool is_document_type() const { return type() == NodeType::DOCUMENT_TYPE_NODE; }
|
||||||
bool is_parent_node() const { return is_element() || is_document(); }
|
bool is_parent_node() const { return is_element() || is_document(); }
|
||||||
|
|
||||||
virtual RefPtr<LayoutNode> create_layout_node(const StyleResolver&, const StyleProperties* parent_style) const = 0;
|
virtual RefPtr<LayoutNode> create_layout_node(const StyleResolver&, const StyleProperties* parent_style) const;
|
||||||
RefPtr<LayoutNode> create_layout_tree(const StyleResolver&, const StyleProperties* parent_style) const;
|
RefPtr<LayoutNode> create_layout_tree(const StyleResolver&, const StyleProperties* parent_style) const;
|
||||||
|
|
||||||
virtual String tag_name() const = 0;
|
virtual String tag_name() const = 0;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <AK/Utf8View.h>
|
#include <AK/Utf8View.h>
|
||||||
#include <LibHTML/CSS/StyleSheet.h>
|
#include <LibHTML/CSS/StyleSheet.h>
|
||||||
#include <LibHTML/DOM/Document.h>
|
#include <LibHTML/DOM/Document.h>
|
||||||
|
#include <LibHTML/DOM/DocumentType.h>
|
||||||
#include <LibHTML/DOM/Element.h>
|
#include <LibHTML/DOM/Element.h>
|
||||||
#include <LibHTML/DOM/Text.h>
|
#include <LibHTML/DOM/Text.h>
|
||||||
#include <LibHTML/Dump.h>
|
#include <LibHTML/Dump.h>
|
||||||
|
@ -24,6 +25,8 @@ void dump_tree(const Node& node)
|
||||||
dbgprintf(">\n");
|
dbgprintf(">\n");
|
||||||
} else if (is<Text>(node)) {
|
} else if (is<Text>(node)) {
|
||||||
dbgprintf("\"%s\"\n", static_cast<const Text&>(node).data().characters());
|
dbgprintf("\"%s\"\n", static_cast<const Text&>(node).data().characters());
|
||||||
|
} else if (is<DocumentType>(node)) {
|
||||||
|
dbgprintf("<!DOCTYPE>\n");
|
||||||
}
|
}
|
||||||
++indent;
|
++indent;
|
||||||
if (is<ParentNode>(node)) {
|
if (is<ParentNode>(node)) {
|
||||||
|
|
|
@ -16,6 +16,7 @@ LIBHTML_OBJS = \
|
||||||
DOM/HTMLLinkElement.o \
|
DOM/HTMLLinkElement.o \
|
||||||
DOM/Document.o \
|
DOM/Document.o \
|
||||||
DOM/Text.o \
|
DOM/Text.o \
|
||||||
|
DOM/DocumentType.o \
|
||||||
CSS/Selector.o \
|
CSS/Selector.o \
|
||||||
CSS/StyleSheet.o \
|
CSS/StyleSheet.o \
|
||||||
CSS/StyleRule.o \
|
CSS/StyleRule.o \
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <AK/Function.h>
|
#include <AK/Function.h>
|
||||||
#include <AK/NonnullRefPtrVector.h>
|
#include <AK/NonnullRefPtrVector.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
|
#include <LibHTML/DOM/DocumentType.h>
|
||||||
#include <LibHTML/DOM/Element.h>
|
#include <LibHTML/DOM/Element.h>
|
||||||
#include <LibHTML/DOM/HTMLAnchorElement.h>
|
#include <LibHTML/DOM/HTMLAnchorElement.h>
|
||||||
#include <LibHTML/DOM/HTMLBodyElement.h>
|
#include <LibHTML/DOM/HTMLBodyElement.h>
|
||||||
|
@ -10,9 +11,9 @@
|
||||||
#include <LibHTML/DOM/HTMLHeadingElement.h>
|
#include <LibHTML/DOM/HTMLHeadingElement.h>
|
||||||
#include <LibHTML/DOM/HTMLHtmlElement.h>
|
#include <LibHTML/DOM/HTMLHtmlElement.h>
|
||||||
#include <LibHTML/DOM/HTMLImageElement.h>
|
#include <LibHTML/DOM/HTMLImageElement.h>
|
||||||
|
#include <LibHTML/DOM/HTMLLinkElement.h>
|
||||||
#include <LibHTML/DOM/HTMLStyleElement.h>
|
#include <LibHTML/DOM/HTMLStyleElement.h>
|
||||||
#include <LibHTML/DOM/HTMLTitleElement.h>
|
#include <LibHTML/DOM/HTMLTitleElement.h>
|
||||||
#include <LibHTML/DOM/HTMLLinkElement.h>
|
|
||||||
#include <LibHTML/DOM/Text.h>
|
#include <LibHTML/DOM/Text.h>
|
||||||
#include <LibHTML/Parser/HTMLParser.h>
|
#include <LibHTML/Parser/HTMLParser.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
@ -106,10 +107,12 @@ NonnullRefPtr<Document> parse_html(const StringView& html, const URL& url)
|
||||||
Vector<char, 256> attribute_value_buffer;
|
Vector<char, 256> attribute_value_buffer;
|
||||||
|
|
||||||
bool is_slash_tag = false;
|
bool is_slash_tag = false;
|
||||||
|
bool is_exclamation_tag = false;
|
||||||
|
|
||||||
auto move_to_state = [&](State new_state) {
|
auto move_to_state = [&](State new_state) {
|
||||||
if (new_state == State::BeforeTagName) {
|
if (new_state == State::BeforeTagName) {
|
||||||
is_slash_tag = false;
|
is_slash_tag = false;
|
||||||
|
is_exclamation_tag = false;
|
||||||
tag_name_buffer.clear();
|
tag_name_buffer.clear();
|
||||||
attributes.clear();
|
attributes.clear();
|
||||||
}
|
}
|
||||||
|
@ -142,8 +145,19 @@ NonnullRefPtr<Document> parse_html(const StringView& html, const URL& url)
|
||||||
close_tag();
|
close_tag();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto handle_exclamation_tag = [&] {
|
||||||
|
auto name = String::copy(tag_name_buffer);
|
||||||
|
tag_name_buffer.clear();
|
||||||
|
ASSERT(name == "DOCTYPE");
|
||||||
|
if (node_stack.size() != 1)
|
||||||
|
node_stack[node_stack.size() - 2].append_child(adopt(*new DocumentType(document)), false);
|
||||||
|
close_tag();
|
||||||
|
};
|
||||||
|
|
||||||
auto commit_tag = [&] {
|
auto commit_tag = [&] {
|
||||||
if (is_slash_tag)
|
if (is_exclamation_tag)
|
||||||
|
handle_exclamation_tag();
|
||||||
|
else if (is_slash_tag)
|
||||||
close_tag();
|
close_tag();
|
||||||
else
|
else
|
||||||
open_tag();
|
open_tag();
|
||||||
|
@ -159,6 +173,7 @@ NonnullRefPtr<Document> parse_html(const StringView& html, const URL& url)
|
||||||
case State::Free:
|
case State::Free:
|
||||||
if (ch == '<') {
|
if (ch == '<') {
|
||||||
is_slash_tag = false;
|
is_slash_tag = false;
|
||||||
|
is_exclamation_tag = false;
|
||||||
move_to_state(State::BeforeTagName);
|
move_to_state(State::BeforeTagName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -193,6 +208,10 @@ NonnullRefPtr<Document> parse_html(const StringView& html, const URL& url)
|
||||||
is_slash_tag = true;
|
is_slash_tag = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (ch == '!') {
|
||||||
|
is_exclamation_tag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (ch == '>') {
|
if (ch == '>') {
|
||||||
move_to_state(State::Free);
|
move_to_state(State::Free);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
const T* first_child() const { return m_first_child; }
|
const T* first_child() const { return m_first_child; }
|
||||||
const T* last_child() const { return m_last_child; }
|
const T* last_child() const { return m_last_child; }
|
||||||
|
|
||||||
|
void prepend_child(NonnullRefPtr<T> node, bool call_inserted_into = true);
|
||||||
void append_child(NonnullRefPtr<T> node, bool call_inserted_into = true);
|
void append_child(NonnullRefPtr<T> node, bool call_inserted_into = true);
|
||||||
void donate_all_children_to(T& node);
|
void donate_all_children_to(T& node);
|
||||||
|
|
||||||
|
@ -64,6 +65,22 @@ inline void TreeNode<T>::append_child(NonnullRefPtr<T> node, bool call_inserted_
|
||||||
(void)node.leak_ref();
|
(void)node.leak_ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void TreeNode<T>::prepend_child(NonnullRefPtr<T> node, bool call_inserted_into)
|
||||||
|
{
|
||||||
|
ASSERT(!node->m_parent);
|
||||||
|
if (m_first_child)
|
||||||
|
m_first_child->m_previous_sibling = node.ptr();
|
||||||
|
node->m_next_sibling = m_first_child;
|
||||||
|
node->m_parent = static_cast<T*>(this);
|
||||||
|
m_first_child = node.ptr();
|
||||||
|
if (!m_last_child)
|
||||||
|
m_last_child = m_first_child;
|
||||||
|
if (call_inserted_into)
|
||||||
|
node->inserted_into(static_cast<T&>(*this));
|
||||||
|
(void)node.leak_ref();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline void TreeNode<T>::donate_all_children_to(T& node)
|
inline void TreeNode<T>::donate_all_children_to(T& node)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue