summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2022-02-17 22:43:22 +0100
committerAndreas Kling <kling@serenityos.org>2022-02-18 01:49:32 +0100
commit7c33a084fb6e8e751173b6c959c30533b3f02f23 (patch)
treee5e70482a2a9d383e02b1e443f6806cdd0e47d24
parentb9b24cb1c1c73fbcbdc9bec6b9b2bf8e47efa4a6 (diff)
downloadserenity-7c33a084fb6e8e751173b6c959c30533b3f02f23.zip
LibWeb: Support CSS :only-of-type selector
This matches any element that doesn't have a sibling with the same tag name as itself.
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp2
-rw-r--r--Userland/Libraries/LibWeb/CSS/Selector.cpp3
-rw-r--r--Userland/Libraries/LibWeb/CSS/Selector.h1
-rw-r--r--Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp32
-rw-r--r--Userland/Libraries/LibWeb/Dump.cpp3
5 files changed, 31 insertions, 10 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
index d5154baed4..e5ace9fa06 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -551,6 +551,8 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Link;
} else if (pseudo_name.equals_ignoring_case("only-child")) {
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::OnlyChild;
+ } else if (pseudo_name.equals_ignoring_case("only-of-type")) {
+ simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::OnlyOfType;
} else if (pseudo_name.equals_ignoring_case("root")) {
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Root;
} else if (pseudo_name.equals_ignoring_case("visited")) {
diff --git a/Userland/Libraries/LibWeb/CSS/Selector.cpp b/Userland/Libraries/LibWeb/CSS/Selector.cpp
index d88c18b186..6b9e27d2f9 100644
--- a/Userland/Libraries/LibWeb/CSS/Selector.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Selector.cpp
@@ -135,6 +135,7 @@ String Selector::SimpleSelector::serialize() const
case Selector::SimpleSelector::PseudoClass::Type::Root:
case Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
case Selector::SimpleSelector::PseudoClass::Type::LastOfType:
+ case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
case Selector::SimpleSelector::PseudoClass::Type::Disabled:
case Selector::SimpleSelector::PseudoClass::Type::Enabled:
case Selector::SimpleSelector::PseudoClass::Type::Checked:
@@ -281,6 +282,8 @@ constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Ty
return "first-of-type"sv;
case Selector::SimpleSelector::PseudoClass::Type::LastOfType:
return "last-of-type"sv;
+ case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
+ return "only-of-type"sv;
case Selector::SimpleSelector::PseudoClass::Type::Disabled:
return "disabled"sv;
case Selector::SimpleSelector::PseudoClass::Type::Enabled:
diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h
index 9cca6a0b2b..ce5b7dc109 100644
--- a/Userland/Libraries/LibWeb/CSS/Selector.h
+++ b/Userland/Libraries/LibWeb/CSS/Selector.h
@@ -57,6 +57,7 @@ public:
Root,
FirstOfType,
LastOfType,
+ OnlyOfType,
NthChild,
NthLastChild,
Disabled,
diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp
index cb13ee86ed..40be51417f 100644
--- a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp
+++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp
@@ -53,6 +53,24 @@ static inline bool matches_attribute(CSS::Selector::SimpleSelector::Attribute co
return false;
}
+static inline DOM::Element const* previous_sibling_with_same_tag_name(DOM::Element const& element)
+{
+ for (auto const* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
+ if (sibling->tag_name() == element.tag_name())
+ return sibling;
+ }
+ return nullptr;
+}
+
+static inline DOM::Element const* next_sibling_with_same_tag_name(DOM::Element const& element)
+{
+ for (auto const* sibling = element.next_element_sibling(); sibling; sibling = sibling->next_element_sibling()) {
+ if (sibling->tag_name() == element.tag_name())
+ return sibling;
+ }
+ return nullptr;
+}
+
static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, DOM::Element const& element)
{
switch (pseudo_class.type) {
@@ -80,17 +98,11 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
case CSS::Selector::SimpleSelector::PseudoClass::Type::Root:
return is<HTML::HTMLHtmlElement>(element);
case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
- for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
- if (sibling->tag_name() == element.tag_name())
- return false;
- }
- return true;
+ return !previous_sibling_with_same_tag_name(element);
case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType:
- for (auto* sibling = element.next_element_sibling(); sibling; sibling = sibling->next_element_sibling()) {
- if (sibling->tag_name() == element.tag_name())
- return false;
- }
- return true;
+ return !next_sibling_with_same_tag_name(element);
+ case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
+ return !previous_sibling_with_same_tag_name(element) && !next_sibling_with_same_tag_name(element);
case CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled:
if (!element.tag_name().equals_ignoring_case(HTML::TagNames::input))
return false;
diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp
index ce4764b603..cee57d23d4 100644
--- a/Userland/Libraries/LibWeb/Dump.cpp
+++ b/Userland/Libraries/LibWeb/Dump.cpp
@@ -382,6 +382,9 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType:
pseudo_class_description = "LastOfType";
break;
+ case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
+ pseudo_class_description = "OnlyOfType";
+ break;
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
pseudo_class_description = "NthChild";
break;