diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 38b8a00069..75be8c5fdd 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -285,6 +285,80 @@ Optional Parser::parse_selector_combinator(TokenStream Parser::parse_selector_qualified_name(TokenStream& tokens, AllowWildcardName allow_wildcard_name) +{ + auto is_name = [](ComponentValue const& token) { + return token.is_delim('*') || token.is(Token::Type::Ident); + }; + auto get_name = [](ComponentValue const& token) { + if (token.is_delim('*')) + return FlyString::from_utf8("*"sv); + return FlyString::from_utf8(token.token().ident()); + }; + + // There are 3 possibilities here: + // (Where and are either an or a `*` delim) + // 1) `|` + // 2) `|` + // 3) `` + // Whitespace is forbidden between any of these parts. https://www.w3.org/TR/selectors-4/#white-space + + auto transaction = tokens.begin_transaction(); + + auto first_token = tokens.next_token(); + if (first_token.is_delim('|')) { + // Case 1: `|` + if (is_name(tokens.peek_token())) { + auto name_token = tokens.next_token(); + + if (allow_wildcard_name == AllowWildcardName::No && name_token.is_delim('*')) + return {}; + + transaction.commit(); + return Selector::SimpleSelector::QualifiedName { + .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::None, + .name = get_name(name_token).release_value_but_fixme_should_propagate_errors(), + }; + } + return {}; + } + + if (!is_name(first_token)) + return {}; + + if (tokens.peek_token().is_delim('|') && is_name(tokens.peek_token(1))) { + // Case 2: `|` + (void)tokens.next_token(); // `|` + auto namespace_ = get_name(first_token).release_value_but_fixme_should_propagate_errors(); + auto name = get_name(tokens.next_token()).release_value_but_fixme_should_propagate_errors(); + + if (allow_wildcard_name == AllowWildcardName::No && name == "*"sv) + return {}; + + auto namespace_type = namespace_ == "*"sv + ? Selector::SimpleSelector::QualifiedName::NamespaceType::Any + : Selector::SimpleSelector::QualifiedName::NamespaceType::Named; + + transaction.commit(); + return Selector::SimpleSelector::QualifiedName { + .namespace_type = namespace_type, + .namespace_ = namespace_, + .name = name, + }; + } + + // Case 3: `` + auto& name_token = first_token; + if (allow_wildcard_name == AllowWildcardName::No && name_token.is_delim('*')) + return {}; + + transaction.commit(); + return Selector::SimpleSelector::QualifiedName { + .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::Default, + .name = get_name(name_token).release_value_but_fixme_should_propagate_errors(), + }; +} + Parser::ParseErrorOr Parser::parse_attribute_simple_selector(ComponentValue const& first_value) { auto attribute_tokens = TokenStream { first_value.block().values() }; diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 7404c14a19..e4fc201b32 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -349,7 +349,11 @@ private: ParseErrorOr> parse_complex_selector(TokenStream&, SelectorType); ParseErrorOr> parse_compound_selector(TokenStream&); Optional parse_selector_combinator(TokenStream&); - + enum class AllowWildcardName { + No, + Yes, + }; + Optional parse_selector_qualified_name(TokenStream&, AllowWildcardName); ParseErrorOr parse_attribute_simple_selector(ComponentValue const&); ParseErrorOr parse_pseudo_simple_selector(TokenStream&); ParseErrorOr> parse_simple_selector(TokenStream&); diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h index 7db68beda9..0a4f92caa0 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.h +++ b/Userland/Libraries/LibWeb/CSS/Selector.h @@ -136,6 +136,31 @@ public: Vector languages {}; }; + struct Name { + Name(FlyString n) + : name(move(n)) + , lowercase_name(name.to_string().to_lowercase().release_value_but_fixme_should_propagate_errors()) + { + } + + FlyString name; + FlyString lowercase_name; + }; + + // Equivalent to `` + // https://www.w3.org/TR/selectors-4/#typedef-wq-name + struct QualifiedName { + enum class NamespaceType { + Default, // `E` + None, // `|E` + Any, // `*|E` + Named, // `ns|E` + }; + NamespaceType namespace_type { NamespaceType::Default }; + FlyString namespace_ {}; + Name name; + }; + struct Attribute { enum class MatchType { HasAttribute, @@ -157,17 +182,6 @@ public: CaseType case_type; }; - struct Name { - Name(FlyString n) - : name(move(n)) - , lowercase_name(name.to_string().to_lowercase().release_value_but_fixme_should_propagate_errors()) - { - } - - FlyString name; - FlyString lowercase_name; - }; - Type type; Variant value {};