summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-10-06 19:59:07 +0200
committerAndreas Kling <awesomekling@gmail.com>2019-10-06 19:59:07 +0200
commitbedb00603cc91219b09c43256132261fd18a9edd (patch)
tree6f424e3c3fbf7e5fd1caf0013aaac7449206c37f /Libraries
parent5a6c36dc917decc58ec5fbce7d094b82a4af4fc1 (diff)
downloadserenity-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.h2
-rw-r--r--Libraries/LibHTML/CSS/StyleResolver.cpp12
-rw-r--r--Libraries/LibHTML/Dump.cpp6
-rw-r--r--Libraries/LibHTML/Parser/CSSParser.cpp19
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();
}