1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:38:11 +00:00

LibHTML: Add is<ElementType> and to<ElementType> helper functions

These will help us write node-type-aware template functions.
This commit is contained in:
Andreas Kling 2019-10-06 20:37:39 +02:00
parent bedb00603c
commit f52f2736e1
12 changed files with 93 additions and 22 deletions

View file

@ -40,17 +40,17 @@ static bool matches(const Selector& selector, int component_index, const Element
case Selector::Component::Relation::Descendant: case Selector::Component::Relation::Descendant:
ASSERT(component_index != 0); ASSERT(component_index != 0);
for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) { for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) {
if (!ancestor->is_element()) if (!is<Element>(*ancestor))
continue; continue;
if (matches(selector, component_index - 1, static_cast<const Element&>(*ancestor))) if (matches(selector, component_index - 1, to<Element>(*ancestor)))
return true; return true;
} }
return false; return false;
case Selector::Component::Relation::ImmediateChild: case Selector::Component::Relation::ImmediateChild:
ASSERT(component_index != 0); ASSERT(component_index != 0);
if (!element.parent() || !element.parent()->is_element()) if (!element.parent() || !is<Element>(*element.parent()))
return false; return false;
return matches(selector, component_index - 1, static_cast<const Element&>(*element.parent())); return matches(selector, component_index - 1, to<Element>(*element.parent()));
case Selector::Component::Relation::AdjacentSibling: case Selector::Component::Relation::AdjacentSibling:
ASSERT(component_index != 0); ASSERT(component_index != 0);
if (auto* sibling = element.previous_element_sibling()) if (auto* sibling = element.previous_element_sibling())

View file

@ -29,11 +29,8 @@ StyleResolver& Document::style_resolver()
void Document::normalize() void Document::normalize()
{ {
if (first_child() != nullptr && first_child()->is_element()) { if (is<HTMLHtmlElement>(first_child()))
const Element& el = static_cast<const Element&>(*first_child()); return;
if (el.tag_name() == "html")
return;
}
NonnullRefPtr<Element> body = adopt(*new Element(*this, "body")); NonnullRefPtr<Element> body = adopt(*new Element(*this, "body"));
NonnullRefPtr<Element> html = adopt(*new Element(*this, "html")); NonnullRefPtr<Element> html = adopt(*new Element(*this, "html"));

View file

@ -80,3 +80,9 @@ private:
Color m_active_link_color { Color::Red }; Color m_active_link_color { Color::Red };
Color m_visited_link_color { Color::Magenta }; Color m_visited_link_color { Color::Magenta };
}; };
template<>
inline bool is<Document>(const Node& node)
{
return node.is_document();
}

View file

@ -26,7 +26,7 @@ public:
Element(Document&, const String& tag_name); Element(Document&, const String& tag_name);
virtual ~Element() override; virtual ~Element() override;
virtual String tag_name() const override { return m_tag_name; } virtual String tag_name() const final { return m_tag_name; }
String attribute(const String& name) const; String attribute(const String& name) const;
void set_attribute(const String& name, const String& value); void set_attribute(const String& name, const String& value);
@ -54,3 +54,9 @@ private:
String m_tag_name; String m_tag_name;
Vector<Attribute> m_attributes; Vector<Attribute> m_attributes;
}; };
template<>
inline bool is<Element>(const Node& node)
{
return node.is_element();
}

View file

@ -9,3 +9,9 @@ public:
String href() const { return attribute("href"); } String href() const { return attribute("href"); }
}; };
template<>
inline bool is<HTMLAnchorElement>(const Node& node)
{
return is<Element>(node) && to<Element>(node).tag_name().to_lowercase() == "a";
}

View file

@ -12,3 +12,9 @@ public:
private: private:
virtual bool is_html_element() const final { return true; } virtual bool is_html_element() const final { return true; }
}; };
template<>
inline bool is<HTMLElement>(const Node& node)
{
return node.is_html_element();
}

View file

@ -7,3 +7,9 @@ public:
HTMLHtmlElement(Document&, const String& tag_name); HTMLHtmlElement(Document&, const String& tag_name);
virtual ~HTMLHtmlElement() override; virtual ~HTMLHtmlElement() override;
}; };
template<>
inline bool is<HTMLHtmlElement>(const Node& node)
{
return is<Element>(node) && to<Element>(node).tag_name().to_lowercase() == "html";
}

View file

@ -56,7 +56,7 @@ RefPtr<LayoutNode> Node::create_layout_tree(const StyleResolver& resolver, const
const HTMLAnchorElement* Node::enclosing_link_element() const const HTMLAnchorElement* Node::enclosing_link_element() const
{ {
if (is_element() && tag_name().to_lowercase() == "a") if (is<HTMLAnchorElement>(*this))
return static_cast<const HTMLAnchorElement*>(this); return static_cast<const HTMLAnchorElement*>(this);
return parent() ? parent()->enclosing_link_element() : nullptr; return parent() ? parent()->enclosing_link_element() : nullptr;
} }

View file

@ -74,3 +74,41 @@ protected:
mutable LayoutNode* m_layout_node { nullptr }; mutable LayoutNode* m_layout_node { nullptr };
NodeType m_type { NodeType::INVALID }; NodeType m_type { NodeType::INVALID };
}; };
template<typename T>
inline bool is(const Node&)
{
return false;
}
template<typename T>
inline bool is(const Node* node)
{
return node && is<T>(*node);
}
template<>
inline bool is<Node>(const Node&)
{
return true;
}
template<>
inline bool is<ParentNode>(const Node& node)
{
return node.is_parent_node();
}
template<typename T>
inline const T& to(const Node& node)
{
ASSERT(is<T>(node));
return static_cast<const T&>(node);
}
template<typename T>
inline T& to(Node& node)
{
ASSERT(is<T>(node));
return static_cast<T&>(node);
}

View file

@ -19,3 +19,9 @@ private:
String m_data; String m_data;
}; };
template<>
inline bool is<Text>(const Node& node)
{
return node.is_text();
}

View file

@ -14,19 +14,19 @@ void dump_tree(const Node& node)
static int indent = 0; static int indent = 0;
for (int i = 0; i < indent; ++i) for (int i = 0; i < indent; ++i)
dbgprintf(" "); dbgprintf(" ");
if (node.is_document()) { if (is<Document>(node)) {
dbgprintf("*Document*\n"); dbgprintf("*Document*\n");
} else if (node.is_element()) { } else if (is<Element>(node)) {
dbgprintf("<%s", static_cast<const Element&>(node).tag_name().characters()); dbgprintf("<%s", to<Element>(node).tag_name().characters());
static_cast<const Element&>(node).for_each_attribute([](auto& name, auto& value) { to<Element>(node).for_each_attribute([](auto& name, auto& value) {
dbgprintf(" %s=%s", name.characters(), value.characters()); dbgprintf(" %s=%s", name.characters(), value.characters());
}); });
dbgprintf(">\n"); dbgprintf(">\n");
} else if (node.is_text()) { } 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());
} }
++indent; ++indent;
if (node.is_parent_node()) { if (is<ParentNode>(node)) {
static_cast<const ParentNode&>(node).for_each_child([](auto& child) { static_cast<const ParentNode&>(node).for_each_child([](auto& child) {
dump_tree(child); dump_tree(child);
}); });
@ -43,12 +43,12 @@ void dump_tree(const LayoutNode& layout_node)
String tag_name; String tag_name;
if (layout_node.is_anonymous()) if (layout_node.is_anonymous())
tag_name = "(anonymous)"; tag_name = "(anonymous)";
else if (layout_node.node()->is_text()) else if (is<Text>(layout_node.node()))
tag_name = "#text"; tag_name = "#text";
else if (layout_node.node()->is_document()) else if (is<Document>(layout_node.node()))
tag_name = "#document"; tag_name = "#document";
else if (layout_node.node()->is_element()) else if (is<Element>(layout_node.node()))
tag_name = static_cast<const Element&>(*layout_node.node()).tag_name(); tag_name = to<Element>(*layout_node.node()).tag_name();
else else
tag_name = "???"; tag_name = "???";

View file

@ -6,7 +6,7 @@ public:
LayoutReplaced(const Element&, NonnullRefPtr<StyleProperties>); LayoutReplaced(const Element&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutReplaced() override; virtual ~LayoutReplaced() override;
const Element& node() const { return static_cast<const Element&>(*LayoutNode::node()); } const Element& node() const { return to<Element>(*LayoutNode::node()); }
virtual bool is_replaced() const final { return true; } virtual bool is_replaced() const final { return true; }