mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-09 01:20:46 +00:00
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 :^)
This commit is contained in:
parent
2b4eea5a50
commit
e971f5604c
|
@ -1,5 +1,8 @@
|
|||
#include <LibHTML/CSS/StyleResolver.h>
|
||||
#include <LibHTML/CSS/StyleSheet.h>
|
||||
#include <LibHTML/DOM/Element.h>
|
||||
#include <LibHTML/Dump.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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<StyleRule> StyleResolver::collect_matching_rules(const Element& element) const
|
||||
{
|
||||
NonnullRefPtrVector<StyleRule> 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<LayoutStyle> StyleResolver::resolve_document_style(const Document& document)
|
||||
{
|
||||
UNUSED_PARAM(document);
|
||||
return nullptr;
|
||||
return make<LayoutStyle>();
|
||||
}
|
||||
|
||||
OwnPtr<LayoutStyle> StyleResolver::resolve_element_style(const Element& element)
|
||||
{
|
||||
UNUSED_PARAM(element);
|
||||
return nullptr;
|
||||
auto style = make<LayoutStyle>();
|
||||
auto matching_rules = collect_matching_rules(element);
|
||||
return style;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
class Document;
|
||||
class Element;
|
||||
class StyleRule;
|
||||
class StyleSheet;
|
||||
|
||||
class StyleResolver {
|
||||
|
@ -21,6 +22,9 @@ public:
|
|||
OwnPtr<LayoutStyle> resolve_element_style(const Element&);
|
||||
OwnPtr<LayoutStyle> resolve_document_style(const Document&);
|
||||
|
||||
NonnullRefPtrVector<StyleRule> collect_matching_rules(const Element&) const;
|
||||
|
||||
|
||||
private:
|
||||
Document& m_document;
|
||||
|
||||
|
|
|
@ -50,6 +50,19 @@ 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;
|
||||
}
|
||||
|
||||
RefPtr<LayoutNode> Element::create_layout_node()
|
||||
{
|
||||
if (m_tag_name == "html")
|
||||
|
|
|
@ -40,6 +40,8 @@ public:
|
|||
callback(attribute.name(), attribute.value());
|
||||
}
|
||||
|
||||
bool has_class(const StringView&) const;
|
||||
|
||||
virtual RefPtr<LayoutNode> create_layout_node() override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <LibHTML/Frame.h>
|
||||
#include <LibHTML/Parser/CSSParser.h>
|
||||
#include <LibHTML/CSS/StyleResolver.h>
|
||||
#include <LibHTML/DOM/Element.h>
|
||||
#include <LibHTML/Parser/HTMLParser.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
@ -29,6 +30,17 @@ int main(int argc, char** argv)
|
|||
|
||||
auto doc_style = resolver.resolve_document_style(*doc);
|
||||
|
||||
Function<void(const ParentNode&)> 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<const Element&>(node));
|
||||
printf("Resolved LayoutStyle{%p} for Element{%p}\n", style.ptr(), &node);
|
||||
resolve_style(static_cast<const Element&>(child));
|
||||
});
|
||||
};
|
||||
resolve_style(*doc);
|
||||
|
||||
doc->build_layout_tree();
|
||||
ASSERT(doc->layout_node());
|
||||
|
||||
|
|
Loading…
Reference in a new issue