Prechádzať zdrojové kódy

LibWeb: Support CSS :only-of-type selector

This matches any element that doesn't have a sibling with the same tag
name as itself.
Andreas Kling 3 rokov pred
rodič
commit
7c33a084fb

+ 2 - 0
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")) {

+ 3 - 0
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:

+ 1 - 0
Userland/Libraries/LibWeb/CSS/Selector.h

@@ -57,6 +57,7 @@ public:
                 Root,
                 FirstOfType,
                 LastOfType,
+                OnlyOfType,
                 NthChild,
                 NthLastChild,
                 Disabled,

+ 22 - 10
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;

+ 3 - 0
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;