diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-10-06 19:59:07 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-10-06 19:59:07 +0200 |
commit | bedb00603cc91219b09c43256132261fd18a9edd (patch) | |
tree | 6f424e3c3fbf7e5fd1caf0013aaac7449206c37f /Libraries | |
parent | 5a6c36dc917decc58ec5fbce7d094b82a4af4fc1 (diff) | |
download | serenity-bedb00603cc91219b09c43256132261fd18a9edd.zip |
LibHTML: Add adjacent (+) and general (~) sibling combinators
This patch implements two more selector features:
- "div + p" matches the <p> sibling immediately after a <div>.
- "div ~ p" matches all <p> siblings after a <div>.
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibHTML/CSS/Selector.h | 2 | ||||
-rw-r--r-- | Libraries/LibHTML/CSS/StyleResolver.cpp | 12 | ||||
-rw-r--r-- | Libraries/LibHTML/Dump.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibHTML/Parser/CSSParser.cpp | 19 |
4 files changed, 37 insertions, 2 deletions
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<const Element&>(*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<Selector::Component> 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(); } |