From e971f5604c0d94919bd85194b16245b2c3269533 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 27 Jun 2019 20:40:21 +0200 Subject: [PATCH] LibHTML: Implement some very simple selector matching. We walk the entire DOM and check all selectors against all elements. Only id, class and tag name are checked right now. There's no ancestor stack or compound selectors. All in good time :^) --- LibHTML/CSS/StyleResolver.cpp | 48 +++++++++++++++++++++++++++-- LibHTML/CSS/StyleResolver.h | 4 +++ LibHTML/DOM/Element.cpp | 13 ++++++++ LibHTML/DOM/Element.h | 2 ++ LibHTML/Dump.cpp | 57 +++++++++++++++++++---------------- LibHTML/Dump.h | 2 ++ LibHTML/test.cpp | 12 ++++++++ 7 files changed, 109 insertions(+), 29 deletions(-) diff --git a/LibHTML/CSS/StyleResolver.cpp b/LibHTML/CSS/StyleResolver.cpp index 07e9e636d9..15aef1ba51 100644 --- a/LibHTML/CSS/StyleResolver.cpp +++ b/LibHTML/CSS/StyleResolver.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include StyleResolver::StyleResolver(Document& document) : m_document(document) @@ -10,14 +13,53 @@ 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 StyleResolver::collect_matching_rules(const Element& element) const +{ + NonnullRefPtrVector matching_rules; + for (auto& sheet : m_sheets) { + 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; +} + OwnPtr StyleResolver::resolve_document_style(const Document& document) { UNUSED_PARAM(document); - return nullptr; + return make(); } OwnPtr StyleResolver::resolve_element_style(const Element& element) { - UNUSED_PARAM(element); - return nullptr; + auto style = make(); + auto matching_rules = collect_matching_rules(element); + return style; } diff --git a/LibHTML/CSS/StyleResolver.h b/LibHTML/CSS/StyleResolver.h index 2875d81700..a11133e0f9 100644 --- a/LibHTML/CSS/StyleResolver.h +++ b/LibHTML/CSS/StyleResolver.h @@ -6,6 +6,7 @@ class Document; class Element; +class StyleRule; class StyleSheet; class StyleResolver { @@ -21,6 +22,9 @@ public: OwnPtr resolve_element_style(const Element&); OwnPtr resolve_document_style(const Document&); + NonnullRefPtrVector collect_matching_rules(const Element&) const; + + private: Document& m_document; diff --git a/LibHTML/DOM/Element.cpp b/LibHTML/DOM/Element.cpp index e291fc3dd0..7808170a60 100644 --- a/LibHTML/DOM/Element.cpp +++ b/LibHTML/DOM/Element.cpp @@ -50,6 +50,19 @@ void Element::set_attributes(Vector&& 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; +} + RefPtr Element::create_layout_node() { if (m_tag_name == "html") diff --git a/LibHTML/DOM/Element.h b/LibHTML/DOM/Element.h index 1331d20c7e..f3133656ca 100644 --- a/LibHTML/DOM/Element.h +++ b/LibHTML/DOM/Element.h @@ -40,6 +40,8 @@ public: callback(attribute.name(), attribute.value()); } + bool has_class(const StringView&) const; + virtual RefPtr create_layout_node() override; private: diff --git a/LibHTML/Dump.cpp b/LibHTML/Dump.cpp index e9c2a2b313..4d9632509f 100644 --- a/LibHTML/Dump.cpp +++ b/LibHTML/Dump.cpp @@ -67,36 +67,41 @@ void dump_tree(const LayoutNode& layout_node) --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()) { - 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", type_description, component.value.characters()); - } - } - printf(" Declarations:\n"); - rule.for_each_declaration([](auto& declaration) { - printf(" '%s': '%s'\n", declaration.property_name().characters(), declaration.value().to_string().characters()); - }); + dump_rule(rule); } } diff --git a/LibHTML/Dump.h b/LibHTML/Dump.h index b7049f861a..546e556ba7 100644 --- a/LibHTML/Dump.h +++ b/LibHTML/Dump.h @@ -2,8 +2,10 @@ class Node; class LayoutNode; +class StyleRule; class StyleSheet; void dump_tree(const Node&); void dump_tree(const LayoutNode&); void dump_sheet(const StyleSheet&); +void dump_rule(const StyleRule&); diff --git a/LibHTML/test.cpp b/LibHTML/test.cpp index c5ba6d73fd..07eed25dc1 100644 --- a/LibHTML/test.cpp +++ b/LibHTML/test.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,17 @@ int main(int argc, char** argv) auto doc_style = resolver.resolve_document_style(*doc); + Function resolve_style = [&](const ParentNode& node) { + node.for_each_child([&](const Node& child) { + if (!child.is_element()) + return; + auto style = resolver.resolve_element_style(static_cast(node)); + printf("Resolved LayoutStyle{%p} for Element{%p}\n", style.ptr(), &node); + resolve_style(static_cast(child)); + }); + }; + resolve_style(*doc); + doc->build_layout_tree(); ASSERT(doc->layout_node());