From bedb00603cc91219b09c43256132261fd18a9edd Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 6 Oct 2019 19:59:07 +0200 Subject: [PATCH] LibHTML: Add adjacent (+) and general (~) sibling combinators This patch implements two more selector features: - "div + p" matches the

sibling immediately after a

. - "div ~ p" matches all

siblings after a

. --- Base/home/anon/www/selectors.html | 16 ++++++++++++++++ Libraries/LibHTML/CSS/Selector.h | 2 ++ Libraries/LibHTML/CSS/StyleResolver.cpp | 12 ++++++++++++ Libraries/LibHTML/Dump.cpp | 6 ++++++ Libraries/LibHTML/Parser/CSSParser.cpp | 19 +++++++++++++++++-- 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Base/home/anon/www/selectors.html b/Base/home/anon/www/selectors.html index 88c248e467..bd06355085 100644 --- a/Base/home/anon/www/selectors.html +++ b/Base/home/anon/www/selectors.html @@ -15,6 +15,11 @@ div > .boo > .bee { background-color: #000000; color: #ff00ff; } +#gen_sib ~ div, +#adj_sib + div { + background-color: #0000ff; + color: #ffffff; +} @@ -45,5 +50,16 @@ div > .boo > .bee {
+ +
+
All the siblings of this <div> should be blue.
+
First sibling (should be blue)
+
Second sibling (should be blue)
+
+
+
The first sibling of this <div> should be blue.
+
First sibling (should be blue)
+
Second sibling (should not be blue)
+
diff --git a/Libraries/LibHTML/CSS/Selector.h b/Libraries/LibHTML/CSS/Selector.h index 967b9e963d..16111d6417 100644 --- a/Libraries/LibHTML/CSS/Selector.h +++ b/Libraries/LibHTML/CSS/Selector.h @@ -19,6 +19,8 @@ public: None, ImmediateChild, Descendant, + AdjacentSibling, + GeneralSibling, }; Relation relation { Relation::None }; diff --git a/Libraries/LibHTML/CSS/StyleResolver.cpp b/Libraries/LibHTML/CSS/StyleResolver.cpp index 3450cb8e12..e9cc8356a2 100644 --- a/Libraries/LibHTML/CSS/StyleResolver.cpp +++ b/Libraries/LibHTML/CSS/StyleResolver.cpp @@ -51,6 +51,18 @@ static bool matches(const Selector& selector, int component_index, const Element if (!element.parent() || !element.parent()->is_element()) return false; return matches(selector, component_index - 1, static_cast(*element.parent())); + case Selector::Component::Relation::AdjacentSibling: + ASSERT(component_index != 0); + if (auto* sibling = element.previous_element_sibling()) + return matches(selector, component_index - 1, *sibling); + return false; + case Selector::Component::Relation::GeneralSibling: + ASSERT(component_index != 0); + for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) { + if (matches(selector, component_index - 1, *sibling)) + return true; + } + return false; } ASSERT_NOT_REACHED(); } diff --git a/Libraries/LibHTML/Dump.cpp b/Libraries/LibHTML/Dump.cpp index ae1d14e4f0..0919263ae7 100644 --- a/Libraries/LibHTML/Dump.cpp +++ b/Libraries/LibHTML/Dump.cpp @@ -158,6 +158,12 @@ void dump_rule(const StyleRule& rule) case Selector::Component::Relation::Descendant: relation_description = "{Descendant}"; break; + case Selector::Component::Relation::AdjacentSibling: + relation_description = "{AdjacentSibling}"; + break; + case Selector::Component::Relation::GeneralSibling: + relation_description = "{GeneralSibling}"; + break; } dbgprintf(" %s:%s %s\n", type_description, component.value.characters(), relation_description); } diff --git a/Libraries/LibHTML/Parser/CSSParser.cpp b/Libraries/LibHTML/Parser/CSSParser.cpp index 11c90a6f1e..e9e2ff939c 100644 --- a/Libraries/LibHTML/Parser/CSSParser.cpp +++ b/Libraries/LibHTML/Parser/CSSParser.cpp @@ -84,6 +84,11 @@ public: return isalnum(ch) || ch == '-' || ch == '_' || ch == '(' || ch == ')' || ch == '@'; } + bool is_combinator(char ch) const + { + return ch == '~' || ch == '>' || ch == '+'; + } + Optional parse_selector_component() { consume_whitespace(); @@ -93,8 +98,18 @@ public: if (peek() == '{') return {}; - if (peek() == '>') { - relation = Selector::Component::Relation::ImmediateChild; + if (is_combinator(peek())) { + switch (peek()) { + case '>': + relation = Selector::Component::Relation::ImmediateChild; + break; + case '+': + relation = Selector::Component::Relation::AdjacentSibling; + break; + case '~': + relation = Selector::Component::Relation::GeneralSibling; + break; + } consume_one(); consume_whitespace(); }