Sfoglia il codice sorgente

LibWeb: Implement :nth-of-type and :nth-last-of-type selectors :^)

Sam Atkins 3 anni fa
parent
commit
a57128467a

+ 22 - 14
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -585,6 +585,18 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
 
         } else if (pseudo_class_token.is_function()) {
 
+            auto parse_nth_child_pattern = [this](auto& simple_selector, auto& pseudo_function) -> bool {
+                auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values());
+                auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values);
+                if (nth_child_pattern.has_value()) {
+                    simple_selector.pseudo_class.nth_child_pattern = nth_child_pattern.value();
+                } else {
+                    dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid format for {}", pseudo_function.name());
+                    return false;
+                }
+                return true;
+            };
+
             auto& pseudo_function = pseudo_class_token.function();
             if (pseudo_function.name().equals_ignoring_case("not")) {
                 simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not;
@@ -597,24 +609,20 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
                 simple_selector.pseudo_class.not_selector = not_selector.release_value();
             } else if (pseudo_function.name().equals_ignoring_case("nth-child")) {
                 simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthChild;
-                auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values());
-                auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values);
-                if (nth_child_pattern.has_value()) {
-                    simple_selector.pseudo_class.nth_child_pattern = nth_child_pattern.value();
-                } else {
-                    dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid nth-child format");
+                if (!parse_nth_child_pattern(simple_selector, pseudo_function))
                     return ParsingResult::SyntaxError;
-                }
             } else if (pseudo_function.name().equals_ignoring_case("nth-last-child")) {
                 simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastChild;
-                auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values());
-                auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values);
-                if (nth_child_pattern.has_value()) {
-                    simple_selector.pseudo_class.nth_child_pattern = nth_child_pattern.value();
-                } else {
-                    dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid nth-child format");
+                if (!parse_nth_child_pattern(simple_selector, pseudo_function))
+                    return ParsingResult::SyntaxError;
+            } else if (pseudo_function.name().equals_ignoring_case("nth-of-type")) {
+                simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthOfType;
+                if (!parse_nth_child_pattern(simple_selector, pseudo_function))
+                    return ParsingResult::SyntaxError;
+            } else if (pseudo_function.name().equals_ignoring_case("nth-last-of-type")) {
+                simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastOfType;
+                if (!parse_nth_child_pattern(simple_selector, pseudo_function))
                     return ParsingResult::SyntaxError;
-                }
             } else {
                 dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name());
                 return ParsingResult::SyntaxError;

+ 4 - 0
Userland/Libraries/LibWeb/CSS/Selector.cpp

@@ -307,6 +307,10 @@ constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Ty
         return "last-of-type"sv;
     case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
         return "only-of-type"sv;
+    case Selector::SimpleSelector::PseudoClass::Type::NthOfType:
+        return "nth-of-type"sv;
+    case Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
+        return "nth-last-of-type"sv;
     case Selector::SimpleSelector::PseudoClass::Type::Disabled:
         return "disabled"sv;
     case Selector::SimpleSelector::PseudoClass::Type::Enabled:

+ 4 - 2
Userland/Libraries/LibWeb/CSS/Selector.h

@@ -62,13 +62,15 @@ public:
                 FirstChild,
                 LastChild,
                 OnlyChild,
+                NthChild,
+                NthLastChild,
                 Empty,
                 Root,
                 FirstOfType,
                 LastOfType,
                 OnlyOfType,
-                NthChild,
-                NthLastChild,
+                NthOfType,
+                NthLastOfType,
                 Disabled,
                 Enabled,
                 Checked,

+ 21 - 2
Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp

@@ -142,6 +142,8 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
         return true;
     case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
     case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
+    case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType:
+    case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
         auto const step_size = pseudo_class.nth_child_pattern.step_size;
         auto const offset = pseudo_class.nth_child_pattern.offset;
         if (step_size == 0 && offset == 0)
@@ -152,12 +154,29 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
             return false;
 
         int index = 1;
-        if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild) {
+        switch (pseudo_class.type) {
+        case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild: {
             for (auto* child = parent->first_child_of_type<DOM::Element>(); child && child != &element; child = child->next_element_sibling())
                 ++index;
-        } else {
+            break;
+        }
+        case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild: {
             for (auto* child = parent->last_child_of_type<DOM::Element>(); child && child != &element; child = child->previous_element_sibling())
                 ++index;
+            break;
+        }
+        case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType: {
+            for (auto* child = previous_sibling_with_same_tag_name(element); child; child = previous_sibling_with_same_tag_name(*child))
+                ++index;
+            break;
+        }
+        case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType: {
+            for (auto* child = next_sibling_with_same_tag_name(element); child; child = next_sibling_with_same_tag_name(*child))
+                ++index;
+            break;
+        }
+        default:
+            VERIFY_NOT_REACHED();
         }
 
         if (step_size < 0) {

+ 6 - 0
Userland/Libraries/LibWeb/Dump.cpp

@@ -385,6 +385,12 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
                 case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
                     pseudo_class_description = "OnlyOfType";
                     break;
+                case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType:
+                    pseudo_class_description = "NthOfType";
+                    break;
+                case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
+                    pseudo_class_description = "NthLastOfType";
+                    break;
                 case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
                     pseudo_class_description = "NthChild";
                     break;