瀏覽代碼

LibWeb: Use generated PseudoClass data

Everything should behave the same as before.
Sam Atkins 2 年之前
父節點
當前提交
b314a115ca

+ 52 - 107
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -31,6 +31,7 @@
 #include <LibWeb/CSS/Parser/Function.h>
 #include <LibWeb/CSS/Parser/Parser.h>
 #include <LibWeb/CSS/Parser/Rule.h>
+#include <LibWeb/CSS/PseudoClass.h>
 #include <LibWeb/CSS/Selector.h>
 #include <LibWeb/CSS/StyleValue.h>
 #include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
@@ -532,71 +533,17 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
         auto make_pseudo_class_selector = [](auto pseudo_class) {
             return Selector::SimpleSelector {
                 .type = Selector::SimpleSelector::Type::PseudoClass,
-                .value = Selector::SimpleSelector::PseudoClass {
-                    .type = pseudo_class }
+                .value = Selector::SimpleSelector::PseudoClassSelector { .type = pseudo_class }
             };
         };
 
-        if (pseudo_name.equals_ignoring_ascii_case("active"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Active);
-        if (pseudo_name.equals_ignoring_ascii_case("buffering"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Buffering);
-        if (pseudo_name.equals_ignoring_ascii_case("checked"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Checked);
-        if (pseudo_name.equals_ignoring_ascii_case("indeterminate"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Indeterminate);
-        if (pseudo_name.equals_ignoring_ascii_case("defined"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Defined);
-        if (pseudo_name.equals_ignoring_ascii_case("disabled"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Disabled);
-        if (pseudo_name.equals_ignoring_ascii_case("empty"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Empty);
-        if (pseudo_name.equals_ignoring_ascii_case("enabled"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Enabled);
-        if (pseudo_name.equals_ignoring_ascii_case("first-child"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FirstChild);
-        if (pseudo_name.equals_ignoring_ascii_case("first-of-type"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FirstOfType);
-        if (pseudo_name.equals_ignoring_ascii_case("focus"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Focus);
-        if (pseudo_name.equals_ignoring_ascii_case("focus-visible"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FocusVisible);
-        if (pseudo_name.equals_ignoring_ascii_case("focus-within"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FocusWithin);
-        if (pseudo_name.equals_ignoring_ascii_case("hover"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Hover);
-        if (pseudo_name.equals_ignoring_ascii_case("last-child"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::LastChild);
-        if (pseudo_name.equals_ignoring_ascii_case("last-of-type"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::LastOfType);
-        if (pseudo_name.equals_ignoring_ascii_case("link"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Link);
-        if (pseudo_name.equals_ignoring_ascii_case("muted"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Muted);
-        if (pseudo_name.equals_ignoring_ascii_case("only-child"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::OnlyChild);
-        if (pseudo_name.equals_ignoring_ascii_case("only-of-type"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::OnlyOfType);
-        if (pseudo_name.equals_ignoring_ascii_case("playing"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Playing);
-        if (pseudo_name.equals_ignoring_ascii_case("paused"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Paused);
-        if (pseudo_name.equals_ignoring_ascii_case("root"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Root);
-        if (pseudo_name.equals_ignoring_ascii_case("seeking"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Seeking);
-        if (pseudo_name.equals_ignoring_ascii_case("stalled"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Stalled);
-        if (pseudo_name.equals_ignoring_ascii_case("target"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Target);
-        if (pseudo_name.equals_ignoring_ascii_case("host"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Host);
-        if (pseudo_name.equals_ignoring_ascii_case("visited"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Visited);
-        if (pseudo_name.equals_ignoring_ascii_case("volume-locked"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::VolumeLocked);
-        if (pseudo_name.equals_ignoring_ascii_case("scope"sv))
-            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Scope);
+        if (auto pseudo_class = pseudo_class_from_string(pseudo_name); pseudo_class.has_value()) {
+            if (!pseudo_class_metadata(pseudo_class.value()).is_valid_as_identifier) {
+                dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is only valid as a function", pseudo_name);
+                return ParseError::SyntaxError;
+            }
+            return make_pseudo_class_selector(pseudo_class.value());
+        }
 
         // Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
         // https://www.w3.org/TR/selectors/#pseudo-element-syntax
@@ -632,7 +579,7 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
             if (!tokens.has_next_token()) {
                 return Selector::SimpleSelector {
                     .type = Selector::SimpleSelector::Type::PseudoClass,
-                    .value = Selector::SimpleSelector::PseudoClass {
+                    .value = Selector::SimpleSelector::PseudoClassSelector {
                         .type = pseudo_class,
                         .nth_child_pattern = nth_child_pattern.release_value() }
                 };
@@ -655,7 +602,7 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
 
             return Selector::SimpleSelector {
                 .type = Selector::SimpleSelector::Type::PseudoClass,
-                .value = Selector::SimpleSelector::PseudoClass {
+                .value = Selector::SimpleSelector::PseudoClassSelector {
                     .type = pseudo_class,
                     .nth_child_pattern = nth_child_pattern.release_value(),
                     .argument_selector_list = move(selector_list) }
@@ -663,69 +610,67 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
         };
 
         auto const& pseudo_function = pseudo_class_token.function();
-        if (pseudo_function.name().equals_ignoring_ascii_case("is"sv)
-            || pseudo_function.name().equals_ignoring_ascii_case("where"sv)) {
+        auto maybe_pseudo_class = pseudo_class_from_string(pseudo_function.name());
+        if (!maybe_pseudo_class.has_value()) {
+            dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name());
+            return ParseError::SyntaxError;
+        }
+        auto pseudo_class = maybe_pseudo_class.value();
+        auto metadata = pseudo_class_metadata(pseudo_class);
+
+        if (!metadata.is_valid_as_function) {
+            dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is not valid as a function", pseudo_function.name());
+            return ParseError::SyntaxError;
+        }
+
+        if (pseudo_function.values().is_empty()) {
+            dbgln_if(CSS_PARSER_DEBUG, "Empty :{}() selector", pseudo_function.name());
+            return ParseError::SyntaxError;
+        }
+
+        switch (metadata.parameter_type) {
+        case PseudoClassMetadata::ParameterType::ANPlusB:
+            return parse_nth_child_selector(pseudo_class, pseudo_function.values(), false);
+        case PseudoClassMetadata::ParameterType::ANPlusBOf:
+            return parse_nth_child_selector(pseudo_class, pseudo_function.values(), true);
+        case PseudoClassMetadata::ParameterType::ForgivingSelectorList: {
             auto function_token_stream = TokenStream(pseudo_function.values());
             // NOTE: Because it's forgiving, even complete garbage will parse OK as an empty selector-list.
             auto argument_selector_list = MUST(parse_a_selector_list(function_token_stream, SelectorType::Standalone, SelectorParsingMode::Forgiving));
 
             return Selector::SimpleSelector {
                 .type = Selector::SimpleSelector::Type::PseudoClass,
-                .value = Selector::SimpleSelector::PseudoClass {
-                    .type = pseudo_function.name().equals_ignoring_ascii_case("is"sv)
-                        ? Selector::SimpleSelector::PseudoClass::Type::Is
-                        : Selector::SimpleSelector::PseudoClass::Type::Where,
+                .value = Selector::SimpleSelector::PseudoClassSelector {
+                    .type = pseudo_class,
                     .argument_selector_list = move(argument_selector_list) }
             };
         }
-        if (pseudo_function.name().equals_ignoring_ascii_case("not"sv)) {
-            auto function_token_stream = TokenStream(pseudo_function.values());
-            auto not_selector = TRY(parse_a_selector_list(function_token_stream, SelectorType::Standalone));
-
+        case PseudoClassMetadata::ParameterType::LanguageRanges: {
+            // FIXME: Support multiple, comma-separated, language ranges.
+            Vector<FlyString> languages;
+            languages.append(pseudo_function.values().first().token().to_string().release_value_but_fixme_should_propagate_errors());
             return Selector::SimpleSelector {
                 .type = Selector::SimpleSelector::Type::PseudoClass,
-                .value = Selector::SimpleSelector::PseudoClass {
-                    .type = Selector::SimpleSelector::PseudoClass::Type::Not,
-                    .argument_selector_list = move(not_selector) }
+                .value = Selector::SimpleSelector::PseudoClassSelector {
+                    .type = pseudo_class,
+                    .languages = move(languages) }
             };
         }
-        if (pseudo_function.name().equals_ignoring_ascii_case("host"sv)) {
+        case PseudoClassMetadata::ParameterType::SelectorList: {
             auto function_token_stream = TokenStream(pseudo_function.values());
-            auto host_selector = TRY(parse_a_selector_list(function_token_stream, SelectorType::Standalone));
+            auto not_selector = TRY(parse_a_selector_list(function_token_stream, SelectorType::Standalone));
 
             return Selector::SimpleSelector {
                 .type = Selector::SimpleSelector::Type::PseudoClass,
-                .value = Selector::SimpleSelector::PseudoClass {
-                    .type = Selector::SimpleSelector::PseudoClass::Type::Host,
-                    .argument_selector_list = move(host_selector) }
+                .value = Selector::SimpleSelector::PseudoClassSelector {
+                    .type = pseudo_class,
+                    .argument_selector_list = move(not_selector) }
             };
         }
-        if (pseudo_function.name().equals_ignoring_ascii_case("lang"sv)) {
-            if (pseudo_function.values().is_empty()) {
-                dbgln_if(CSS_PARSER_DEBUG, "Empty :lang() selector");
-                return ParseError::SyntaxError;
-            }
-            // FIXME: Support multiple, comma-separated, language ranges.
-            Vector<FlyString> languages;
-            languages.append(pseudo_function.values().first().token().to_string().release_value_but_fixme_should_propagate_errors());
-            return Selector::SimpleSelector {
-                .type = Selector::SimpleSelector::Type::PseudoClass,
-                .value = Selector::SimpleSelector::PseudoClass {
-                    .type = Selector::SimpleSelector::PseudoClass::Type::Lang,
-                    .languages = move(languages) }
-            };
+        case PseudoClassMetadata::ParameterType::None:
+            // `None` means this is not a function-type pseudo-class, so this state should be impossible.
+            VERIFY_NOT_REACHED();
         }
-        if (pseudo_function.name().equals_ignoring_ascii_case("nth-child"sv))
-            return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthChild, pseudo_function.values(), true);
-        if (pseudo_function.name().equals_ignoring_ascii_case("nth-last-child"sv))
-            return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthLastChild, pseudo_function.values(), true);
-        if (pseudo_function.name().equals_ignoring_ascii_case("nth-of-type"sv))
-            return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthOfType, pseudo_function.values(), false);
-        if (pseudo_function.name().equals_ignoring_ascii_case("nth-last-of-type"sv))
-            return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthLastOfType, pseudo_function.values(), false);
-
-        dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name());
-        return ParseError::SyntaxError;
     }
     dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string());
     return ParseError::SyntaxError;

+ 51 - 51
Userland/Libraries/LibWeb/CSS/Selector.cpp

@@ -72,15 +72,15 @@ u32 Selector::specificity() const
             case SimpleSelector::Type::PseudoClass: {
                 auto& pseudo_class = simple_selector.pseudo_class();
                 switch (pseudo_class.type) {
-                case SimpleSelector::PseudoClass::Type::Is:
-                case SimpleSelector::PseudoClass::Type::Not: {
+                case PseudoClass::Is:
+                case PseudoClass::Not: {
                     // The specificity of an :is(), :not(), or :has() pseudo-class is replaced by the
                     // specificity of the most specific complex selector in its selector list argument.
                     count_specificity_of_most_complex_selector(pseudo_class.argument_selector_list);
                     break;
                 }
-                case SimpleSelector::PseudoClass::Type::NthChild:
-                case SimpleSelector::PseudoClass::Type::NthLastChild: {
+                case PseudoClass::NthChild:
+                case PseudoClass::NthLastChild: {
                     // Analogously, the specificity of an :nth-child() or :nth-last-child() selector
                     // is the specificity of the pseudo class itself (counting as one pseudo-class selector)
                     // plus the specificity of the most specific complex selector in its selector list argument (if any).
@@ -88,7 +88,7 @@ u32 Selector::specificity() const
                     count_specificity_of_most_complex_selector(pseudo_class.argument_selector_list);
                     break;
                 }
-                case SimpleSelector::PseudoClass::Type::Where:
+                case PseudoClass::Where:
                     // The specificity of a :where() pseudo-class is replaced by zero.
                     break;
                 default:
@@ -229,66 +229,66 @@ ErrorOr<String> Selector::SimpleSelector::serialize() const
         auto& pseudo_class = this->pseudo_class();
 
         switch (pseudo_class.type) {
-        case Selector::SimpleSelector::PseudoClass::Type::Link:
-        case Selector::SimpleSelector::PseudoClass::Type::Visited:
-        case Selector::SimpleSelector::PseudoClass::Type::Hover:
-        case Selector::SimpleSelector::PseudoClass::Type::Focus:
-        case Selector::SimpleSelector::PseudoClass::Type::FocusVisible:
-        case Selector::SimpleSelector::PseudoClass::Type::FocusWithin:
-        case Selector::SimpleSelector::PseudoClass::Type::FirstChild:
-        case Selector::SimpleSelector::PseudoClass::Type::LastChild:
-        case Selector::SimpleSelector::PseudoClass::Type::OnlyChild:
-        case Selector::SimpleSelector::PseudoClass::Type::Empty:
-        case Selector::SimpleSelector::PseudoClass::Type::Root:
-        case Selector::SimpleSelector::PseudoClass::Type::Host:
-        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:
-        case Selector::SimpleSelector::PseudoClass::Type::Indeterminate:
-        case Selector::SimpleSelector::PseudoClass::Type::Active:
-        case Selector::SimpleSelector::PseudoClass::Type::Scope:
-        case Selector::SimpleSelector::PseudoClass::Type::Defined:
-        case Selector::SimpleSelector::PseudoClass::Type::Playing:
-        case Selector::SimpleSelector::PseudoClass::Type::Paused:
-        case Selector::SimpleSelector::PseudoClass::Type::Seeking:
-        case Selector::SimpleSelector::PseudoClass::Type::Muted:
-        case Selector::SimpleSelector::PseudoClass::Type::VolumeLocked:
-        case Selector::SimpleSelector::PseudoClass::Type::Buffering:
-        case Selector::SimpleSelector::PseudoClass::Type::Stalled:
-        case Selector::SimpleSelector::PseudoClass::Type::Target:
+        case PseudoClass::Link:
+        case PseudoClass::Visited:
+        case PseudoClass::Hover:
+        case PseudoClass::Focus:
+        case PseudoClass::FocusVisible:
+        case PseudoClass::FocusWithin:
+        case PseudoClass::FirstChild:
+        case PseudoClass::LastChild:
+        case PseudoClass::OnlyChild:
+        case PseudoClass::Empty:
+        case PseudoClass::Root:
+        case PseudoClass::Host:
+        case PseudoClass::FirstOfType:
+        case PseudoClass::LastOfType:
+        case PseudoClass::OnlyOfType:
+        case PseudoClass::Disabled:
+        case PseudoClass::Enabled:
+        case PseudoClass::Checked:
+        case PseudoClass::Indeterminate:
+        case PseudoClass::Active:
+        case PseudoClass::Scope:
+        case PseudoClass::Defined:
+        case PseudoClass::Playing:
+        case PseudoClass::Paused:
+        case PseudoClass::Seeking:
+        case PseudoClass::Muted:
+        case PseudoClass::VolumeLocked:
+        case PseudoClass::Buffering:
+        case PseudoClass::Stalled:
+        case PseudoClass::Target:
             // If the pseudo-class does not accept arguments append ":" (U+003A), followed by the name of the pseudo-class, to s.
             TRY(s.try_append(':'));
             TRY(s.try_append(pseudo_class_name(pseudo_class.type)));
             break;
-        case Selector::SimpleSelector::PseudoClass::Type::NthChild:
-        case Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
-        case Selector::SimpleSelector::PseudoClass::Type::NthOfType:
-        case Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
-        case Selector::SimpleSelector::PseudoClass::Type::Not:
-        case Selector::SimpleSelector::PseudoClass::Type::Is:
-        case Selector::SimpleSelector::PseudoClass::Type::Where:
-        case Selector::SimpleSelector::PseudoClass::Type::Lang:
+        case PseudoClass::NthChild:
+        case PseudoClass::NthLastChild:
+        case PseudoClass::NthOfType:
+        case PseudoClass::NthLastOfType:
+        case PseudoClass::Not:
+        case PseudoClass::Is:
+        case PseudoClass::Where:
+        case PseudoClass::Lang:
             // Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028),
             // followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s.
             TRY(s.try_append(':'));
             TRY(s.try_append(pseudo_class_name(pseudo_class.type)));
             TRY(s.try_append('('));
-            if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthChild
-                || pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthLastChild
-                || pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthOfType
-                || pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthLastOfType) {
+            if (pseudo_class.type == PseudoClass::NthChild
+                || pseudo_class.type == PseudoClass::NthLastChild
+                || pseudo_class.type == PseudoClass::NthOfType
+                || pseudo_class.type == PseudoClass::NthLastOfType) {
                 // The result of serializing the value using the rules to serialize an <an+b> value.
                 TRY(s.try_append(TRY(pseudo_class.nth_child_pattern.serialize())));
-            } else if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Not
-                || pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Is
-                || pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Where) {
+            } else if (pseudo_class.type == PseudoClass::Not
+                || pseudo_class.type == PseudoClass::Is
+                || pseudo_class.type == PseudoClass::Where) {
                 // The result of serializing the value using the rules for serializing a group of selectors.
                 // NOTE: `:is()` and `:where()` aren't in the spec for this yet, but it should be!
                 TRY(s.try_append(TRY(serialize_a_group_of_selectors(pseudo_class.argument_selector_list))));
-            } else if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Lang) {
+            } else if (pseudo_class.type == PseudoClass::Lang) {
                 // The serialization of a comma-separated list of each argument’s serialization as a string, preserving relative order.
                 s.join(", "sv, pseudo_class.languages);
             }

+ 6 - 128
Userland/Libraries/LibWeb/CSS/Selector.h

@@ -11,6 +11,7 @@
 #include <AK/RefCounted.h>
 #include <AK/String.h>
 #include <AK/Vector.h>
+#include <LibWeb/CSS/PseudoClass.h>
 
 namespace Web::CSS {
 
@@ -84,48 +85,8 @@ public:
             }
         };
 
-        struct PseudoClass {
-            enum class Type {
-                Link,
-                Visited,
-                Hover,
-                Focus,
-                FocusVisible,
-                FocusWithin,
-                FirstChild,
-                LastChild,
-                OnlyChild,
-                NthChild,
-                NthLastChild,
-                Empty,
-                Root,
-                Host,
-                FirstOfType,
-                LastOfType,
-                OnlyOfType,
-                NthOfType,
-                NthLastOfType,
-                Disabled,
-                Enabled,
-                Checked,
-                Indeterminate,
-                Is,
-                Not,
-                Where,
-                Active,
-                Lang,
-                Scope,
-                Defined,
-                Playing,
-                Paused,
-                Seeking,
-                Muted,
-                VolumeLocked,
-                Buffering,
-                Stalled,
-                Target,
-            };
-            Type type;
+        struct PseudoClassSelector {
+            PseudoClass type;
 
             // FIXME: We don't need this field on every single SimpleSelector, but it's also annoying to malloc it somewhere.
             // Only used when "pseudo_class" is "NthChild" or "NthLastChild".
@@ -184,12 +145,12 @@ public:
         };
 
         Type type;
-        Variant<Empty, Attribute, PseudoClass, PseudoElement, Name, QualifiedName> value {};
+        Variant<Empty, Attribute, PseudoClassSelector, PseudoElement, Name, QualifiedName> value {};
 
         Attribute const& attribute() const { return value.get<Attribute>(); }
         Attribute& attribute() { return value.get<Attribute>(); }
-        PseudoClass const& pseudo_class() const { return value.get<PseudoClass>(); }
-        PseudoClass& pseudo_class() { return value.get<PseudoClass>(); }
+        PseudoClassSelector const& pseudo_class() const { return value.get<PseudoClassSelector>(); }
+        PseudoClassSelector& pseudo_class() { return value.get<PseudoClassSelector>(); }
         PseudoElement const& pseudo_element() const { return value.get<PseudoElement>(); }
         PseudoElement& pseudo_element() { return value.get<PseudoElement>(); }
 
@@ -268,89 +229,6 @@ constexpr StringView pseudo_element_name(Selector::PseudoElement pseudo_element)
 
 Optional<Selector::PseudoElement> pseudo_element_from_string(StringView);
 
-constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Type pseudo_class)
-{
-    switch (pseudo_class) {
-    case Selector::SimpleSelector::PseudoClass::Type::Link:
-        return "link"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Visited:
-        return "visited"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Hover:
-        return "hover"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Focus:
-        return "focus"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::FocusVisible:
-        return "focus-visible"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::FocusWithin:
-        return "focus-within"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::FirstChild:
-        return "first-child"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::LastChild:
-        return "last-child"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::OnlyChild:
-        return "only-child"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Empty:
-        return "empty"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Root:
-        return "root"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Host:
-        return "host"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
-        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::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:
-        return "enabled"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Checked:
-        return "checked"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Indeterminate:
-        return "indeterminate"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Active:
-        return "active"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::NthChild:
-        return "nth-child"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
-        return "nth-last-child"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Is:
-        return "is"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Not:
-        return "not"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Where:
-        return "where"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Lang:
-        return "lang"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Scope:
-        return "scope"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Defined:
-        return "defined"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Playing:
-        return "playing"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Paused:
-        return "paused"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Seeking:
-        return "seeking"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Muted:
-        return "muted"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::VolumeLocked:
-        return "volume-locked"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Buffering:
-        return "buffering"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Stalled:
-        return "stalled"sv;
-    case Selector::SimpleSelector::PseudoClass::Type::Target:
-        return "target"sv;
-    }
-    VERIFY_NOT_REACHED();
-}
-
 ErrorOr<String> serialize_a_group_of_selectors(Vector<NonnullRefPtr<Selector>> const& selectors);
 
 }

+ 43 - 43
Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp

@@ -207,34 +207,34 @@ static inline DOM::Element const* next_sibling_with_same_tag_name(DOM::Element c
     return nullptr;
 }
 
-static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
+static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClassSelector const& pseudo_class, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
 {
     switch (pseudo_class.type) {
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Link:
+    case CSS::PseudoClass::Link:
         return matches_link_pseudo_class(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Visited:
+    case CSS::PseudoClass::Visited:
         // FIXME: Maybe match this selector sometimes?
         return false;
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Active:
+    case CSS::PseudoClass::Active:
         return element.is_active();
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Hover:
+    case CSS::PseudoClass::Hover:
         return matches_hover_pseudo_class(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Focus:
+    case CSS::PseudoClass::Focus:
         return element.is_focused();
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::FocusVisible:
+    case CSS::PseudoClass::FocusVisible:
         // FIXME: We should only apply this when a visible focus is useful. Decide when that is!
         return element.is_focused();
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::FocusWithin: {
+    case CSS::PseudoClass::FocusWithin: {
         auto* focused_element = element.document().focused_element();
         return focused_element && element.is_inclusive_ancestor_of(*focused_element);
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstChild:
+    case CSS::PseudoClass::FirstChild:
         return !element.previous_element_sibling();
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::LastChild:
+    case CSS::PseudoClass::LastChild:
         return !element.next_element_sibling();
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyChild:
+    case CSS::PseudoClass::OnlyChild:
         return !(element.previous_element_sibling() || element.next_element_sibling());
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Empty: {
+    case CSS::PseudoClass::Empty: {
         if (!element.has_children())
             return true;
         if (element.first_child_of_type<DOM::Element>())
@@ -251,53 +251,53 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
         });
         return !has_nonempty_text_child;
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Root:
+    case CSS::PseudoClass::Root:
         return is<HTML::HTMLHtmlElement>(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Host:
+    case CSS::PseudoClass::Host:
         // FIXME: Implement :host selector.
         return false;
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Scope:
+    case CSS::PseudoClass::Scope:
         return scope ? &element == scope : is<HTML::HTMLHtmlElement>(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
+    case CSS::PseudoClass::FirstOfType:
         return !previous_sibling_with_same_tag_name(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType:
+    case CSS::PseudoClass::LastOfType:
         return !next_sibling_with_same_tag_name(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
+    case CSS::PseudoClass::OnlyOfType:
         return !previous_sibling_with_same_tag_name(element) && !next_sibling_with_same_tag_name(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Lang:
+    case CSS::PseudoClass::Lang:
         return matches_lang_pseudo_class(element, pseudo_class.languages);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled:
+    case CSS::PseudoClass::Disabled:
         // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-disabled
         // The :disabled pseudo-class must match any element that is actually disabled.
         return element.is_actually_disabled();
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Enabled:
+    case CSS::PseudoClass::Enabled:
         // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-enabled
         // The :enabled pseudo-class must match any button, input, select, textarea, optgroup, option, fieldset element, or form-associated custom element that is not actually disabled.
         return (is<HTML::HTMLButtonElement>(element) || is<HTML::HTMLInputElement>(element) || is<HTML::HTMLSelectElement>(element) || is<HTML::HTMLTextAreaElement>(element) || is<HTML::HTMLOptGroupElement>(element) || is<HTML::HTMLOptionElement>(element) || is<HTML::HTMLFieldSetElement>(element))
             && !element.is_actually_disabled();
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Checked:
+    case CSS::PseudoClass::Checked:
         return matches_checked_pseudo_class(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Indeterminate:
+    case CSS::PseudoClass::Indeterminate:
         return matches_indeterminate_pseudo_class(element);
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Defined:
+    case CSS::PseudoClass::Defined:
         return element.is_defined();
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Is:
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Where:
+    case CSS::PseudoClass::Is:
+    case CSS::PseudoClass::Where:
         for (auto& selector : pseudo_class.argument_selector_list) {
             if (matches(selector, style_sheet_for_rule, element))
                 return true;
         }
         return false;
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
+    case CSS::PseudoClass::Not:
         for (auto& selector : pseudo_class.argument_selector_list) {
             if (matches(selector, style_sheet_for_rule, element))
                 return false;
         }
         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: {
+    case CSS::PseudoClass::NthChild:
+    case CSS::PseudoClass::NthLastChild:
+    case CSS::PseudoClass::NthOfType:
+    case CSS::PseudoClass::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)
@@ -320,7 +320,7 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
 
         int index = 1;
         switch (pseudo_class.type) {
-        case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild: {
+        case CSS::PseudoClass::NthChild: {
             if (!matches_selector_list(pseudo_class.argument_selector_list, element))
                 return false;
             for (auto* child = parent->first_child_of_type<DOM::Element>(); child && child != &element; child = child->next_element_sibling()) {
@@ -329,7 +329,7 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
             }
             break;
         }
-        case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild: {
+        case CSS::PseudoClass::NthLastChild: {
             if (!matches_selector_list(pseudo_class.argument_selector_list, element))
                 return false;
             for (auto* child = parent->last_child_of_type<DOM::Element>(); child && child != &element; child = child->previous_element_sibling()) {
@@ -338,12 +338,12 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
             }
             break;
         }
-        case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType: {
+        case CSS::PseudoClass::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: {
+        case CSS::PseudoClass::NthLastOfType: {
             for (auto* child = next_sibling_with_same_tag_name(element); child; child = next_sibling_with_same_tag_name(*child))
                 ++index;
             break;
@@ -384,48 +384,48 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
         // Otherwise, we start at "offset" and count forwards.
         return index >= offset && canonical_modulo(index - offset, step_size) == 0;
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Playing: {
+    case CSS::PseudoClass::Playing: {
         if (!is<HTML::HTMLMediaElement>(element))
             return false;
         auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
         return !media_element.paused();
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Paused: {
+    case CSS::PseudoClass::Paused: {
         if (!is<HTML::HTMLMediaElement>(element))
             return false;
         auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
         return media_element.paused();
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Seeking: {
+    case CSS::PseudoClass::Seeking: {
         if (!is<HTML::HTMLMediaElement>(element))
             return false;
         auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
         return media_element.seeking();
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Muted: {
+    case CSS::PseudoClass::Muted: {
         if (!is<HTML::HTMLMediaElement>(element))
             return false;
         auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
         return media_element.muted();
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::VolumeLocked: {
+    case CSS::PseudoClass::VolumeLocked: {
         // FIXME: Currently we don't allow the user to specify an override volume, so this is always false.
         //        Once we do, implement this!
         return false;
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Buffering: {
+    case CSS::PseudoClass::Buffering: {
         if (!is<HTML::HTMLMediaElement>(element))
             return false;
         auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
         return media_element.blocked();
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Stalled: {
+    case CSS::PseudoClass::Stalled: {
         if (!is<HTML::HTMLMediaElement>(element))
             return false;
         auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
         return media_element.stalled();
     }
-    case CSS::Selector::SimpleSelector::PseudoClass::Type::Target:
+    case CSS::PseudoClass::Target:
         return element.is_target();
     }
 

+ 25 - 135
Userland/Libraries/LibWeb/Dump.cpp

@@ -16,6 +16,7 @@
 #include <LibWeb/CSS/CSSStyleSheet.h>
 #include <LibWeb/CSS/CSSSupportsRule.h>
 #include <LibWeb/CSS/PropertyID.h>
+#include <LibWeb/CSS/PseudoClass.h>
 #include <LibWeb/DOM/Comment.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Element.h>
@@ -458,7 +459,7 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
                 type_description = "Attribute";
                 break;
             case CSS::Selector::SimpleSelector::Type::PseudoClass:
-                type_description = "PseudoClass";
+                type_description = "PseudoClassSelector";
                 break;
             case CSS::Selector::SimpleSelector::Type::PseudoElement:
                 type_description = "PseudoElement";
@@ -477,141 +478,14 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
             if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {
                 auto const& pseudo_class = simple_selector.pseudo_class();
 
-                char const* pseudo_class_description = "";
-                switch (pseudo_class.type) {
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Link:
-                    pseudo_class_description = "Link";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Visited:
-                    pseudo_class_description = "Visited";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Active:
-                    pseudo_class_description = "Active";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Root:
-                    pseudo_class_description = "Root";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Host:
-                    pseudo_class_description = "Host";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
-                    pseudo_class_description = "FirstOfType";
-                    break;
-                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::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;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
-                    pseudo_class_description = "NthLastChild";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Focus:
-                    pseudo_class_description = "Focus";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::FocusVisible:
-                    pseudo_class_description = "FocusVisible";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::FocusWithin:
-                    pseudo_class_description = "FocusWithin";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Empty:
-                    pseudo_class_description = "Empty";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Hover:
-                    pseudo_class_description = "Hover";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::LastChild:
-                    pseudo_class_description = "LastChild";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstChild:
-                    pseudo_class_description = "FirstChild";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyChild:
-                    pseudo_class_description = "OnlyChild";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled:
-                    pseudo_class_description = "Disabled";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Enabled:
-                    pseudo_class_description = "Enabled";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Checked:
-                    pseudo_class_description = "Checked";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Indeterminate:
-                    pseudo_class_description = "Indeterminate";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
-                    pseudo_class_description = "Not";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Is:
-                    pseudo_class_description = "Is";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Where:
-                    pseudo_class_description = "Where";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Lang:
-                    pseudo_class_description = "Lang";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Scope:
-                    pseudo_class_description = "Scope";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Defined:
-                    pseudo_class_description = "Defined";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Playing:
-                    pseudo_class_description = "Playing";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Paused:
-                    pseudo_class_description = "Paused";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Seeking:
-                    pseudo_class_description = "Seeking";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Muted:
-                    pseudo_class_description = "Muted";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::VolumeLocked:
-                    pseudo_class_description = "VolumeLocked";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Buffering:
-                    pseudo_class_description = "Buffering";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Stalled:
-                    pseudo_class_description = "Stalled";
-                    break;
-                case CSS::Selector::SimpleSelector::PseudoClass::Type::Target:
-                    pseudo_class_description = "Target";
-                    break;
-                }
+                builder.appendff(" pseudo_class={}", CSS::pseudo_class_name(pseudo_class.type));
+                auto pseudo_class_metadata = CSS::pseudo_class_metadata(pseudo_class.type);
 
-                builder.appendff(" pseudo_class={}", pseudo_class_description);
-                if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Lang) {
-                    builder.append('(');
-                    builder.join(',', pseudo_class.languages);
-                    builder.append(')');
-                } else if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Not
-                    || pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Host
-                    || pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Is
-                    || pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Where) {
-                    builder.append("(["sv);
-                    for (auto& selector : pseudo_class.argument_selector_list)
-                        dump_selector(builder, selector);
-                    builder.append("])"sv);
-                } else if ((pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild)
-                    || (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild)
-                    || (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType)
-                    || (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType)) {
+                switch (pseudo_class_metadata.parameter_type) {
+                case CSS::PseudoClassMetadata::ParameterType::None:
+                    break;
+                case CSS::PseudoClassMetadata::ParameterType::ANPlusB:
+                case CSS::PseudoClassMetadata::ParameterType::ANPlusBOf: {
                     builder.appendff("(step={}, offset={}", pseudo_class.nth_child_pattern.step_size, pseudo_class.nth_child_pattern.offset);
                     if (!pseudo_class.argument_selector_list.is_empty()) {
                         builder.append(", selectors=["sv);
@@ -620,6 +494,22 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
                         builder.append("]"sv);
                     }
                     builder.append(")"sv);
+                    break;
+                }
+                case CSS::PseudoClassMetadata::ParameterType::ForgivingSelectorList:
+                case CSS::PseudoClassMetadata::ParameterType::SelectorList: {
+                    builder.append("(["sv);
+                    for (auto& selector : pseudo_class.argument_selector_list)
+                        dump_selector(builder, selector);
+                    builder.append("])"sv);
+                    break;
+                }
+                case CSS::PseudoClassMetadata::ParameterType::LanguageRanges: {
+                    builder.append('(');
+                    builder.join(',', pseudo_class.languages);
+                    builder.append(')');
+                    break;
+                }
                 }
             }