Ver Fonte

LibWeb: Parse forgiving selector-lists

`<forgiving-selector-list>` and `<forgiving-relative-selector-list>` are
the same as regular selector-lists, except that an invalid selector
does not make the whole list invalid. The former is used by the `:is()`
pseudo-class.

For example:

```css
/* This entire selector-list is invalid */
.foo, .bar, !?invalid { }

/* This is valid, but the "!?invalid" selector is removed */
:is(.foo, .bar, !?invalid) { }
```

Also as part of this, I've removed the `parse_a_selector(TokenStream)`
and `parse_a_relative_selector(TokenStream)` methods as they don't add
anything useful.
Sam Atkins há 3 anos atrás
pai
commit
5319e2ba8e

+ 18 - 24
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -179,24 +179,18 @@ NonnullRefPtr<CSSStyleSheet> Parser::parse_a_stylesheet(TokenStream<T>& tokens)
     return CSSStyleSheet::create(rules);
 }
 
-Optional<SelectorList> Parser::parse_as_selector()
+Optional<SelectorList> Parser::parse_as_selector(SelectorParsingMode parsing_mode)
 {
-    auto selector_list = parse_a_selector(m_token_stream);
+    auto selector_list = parse_a_selector_list(m_token_stream, parsing_mode);
     if (!selector_list.is_error())
         return selector_list.release_value();
 
     return {};
 }
 
-template<typename T>
-Result<SelectorList, Parser::ParsingResult> Parser::parse_a_selector(TokenStream<T>& tokens)
-{
-    return parse_a_selector_list(tokens);
-}
-
-Optional<SelectorList> Parser::parse_as_relative_selector()
+Optional<SelectorList> Parser::parse_as_relative_selector(SelectorParsingMode parsing_mode)
 {
-    auto selector_list = parse_a_relative_selector(m_token_stream);
+    auto selector_list = parse_a_relative_selector_list(m_token_stream, parsing_mode);
     if (!selector_list.is_error())
         return selector_list.release_value();
 
@@ -204,13 +198,7 @@ Optional<SelectorList> Parser::parse_as_relative_selector()
 }
 
 template<typename T>
-Result<SelectorList, Parser::ParsingResult> Parser::parse_a_relative_selector(TokenStream<T>& tokens)
-{
-    return parse_a_relative_selector_list(tokens);
-}
-
-template<typename T>
-Result<SelectorList, Parser::ParsingResult> Parser::parse_a_selector_list(TokenStream<T>& tokens)
+Result<SelectorList, Parser::ParsingResult> Parser::parse_a_selector_list(TokenStream<T>& tokens, SelectorParsingMode parsing_mode)
 {
     auto comma_separated_lists = parse_a_comma_separated_list_of_component_values(tokens);
 
@@ -218,19 +206,22 @@ Result<SelectorList, Parser::ParsingResult> Parser::parse_a_selector_list(TokenS
     for (auto& selector_parts : comma_separated_lists) {
         auto stream = TokenStream(selector_parts);
         auto selector = parse_complex_selector(stream, false);
-        if (selector.is_error())
+        if (selector.is_error()) {
+            if (parsing_mode == SelectorParsingMode::Forgiving)
+                continue;
             return selector.error();
+        }
         selectors.append(selector.release_value());
     }
 
-    if (selectors.is_empty())
+    if (selectors.is_empty() && parsing_mode != SelectorParsingMode::Forgiving)
         return ParsingResult::SyntaxError;
 
     return selectors;
 }
 
 template<typename T>
-Result<SelectorList, Parser::ParsingResult> Parser::parse_a_relative_selector_list(TokenStream<T>& tokens)
+Result<SelectorList, Parser::ParsingResult> Parser::parse_a_relative_selector_list(TokenStream<T>& tokens, SelectorParsingMode parsing_mode)
 {
     auto comma_separated_lists = parse_a_comma_separated_list_of_component_values(tokens);
 
@@ -238,12 +229,15 @@ Result<SelectorList, Parser::ParsingResult> Parser::parse_a_relative_selector_li
     for (auto& selector_parts : comma_separated_lists) {
         auto stream = TokenStream(selector_parts);
         auto selector = parse_complex_selector(stream, true);
-        if (selector.is_error())
+        if (selector.is_error()) {
+            if (parsing_mode == SelectorParsingMode::Forgiving)
+                continue;
             return selector.error();
+        }
         selectors.append(selector.release_value());
     }
 
-    if (selectors.is_empty())
+    if (selectors.is_empty() && parsing_mode != SelectorParsingMode::Forgiving)
         return ParsingResult::SyntaxError;
 
     return selectors;
@@ -593,7 +587,7 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
             if (pseudo_function.name().equals_ignoring_case("not")) {
                 simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not;
                 auto function_token_stream = TokenStream(pseudo_function.values());
-                auto not_selector = parse_a_selector(function_token_stream);
+                auto not_selector = parse_a_selector_list(function_token_stream);
                 if (not_selector.is_error()) {
                     dbgln_if(CSS_PARSER_DEBUG, "Invalid selector in :not() clause");
                     return ParsingResult::SyntaxError;
@@ -2087,7 +2081,7 @@ RefPtr<CSSRule> Parser::convert_to_rule(NonnullRefPtr<StyleRule> rule)
 
     } else {
         auto prelude_stream = TokenStream(rule->m_prelude);
-        auto selectors = parse_a_selector(prelude_stream);
+        auto selectors = parse_a_selector_list(prelude_stream);
 
         if (selectors.is_error()) {
             if (selectors.error() != ParsingResult::IncludesIgnoredVendorPrefix) {

+ 11 - 8
Userland/Libraries/LibWeb/CSS/Parser/Parser.h

@@ -105,9 +105,16 @@ public:
     Vector<StyleComponentValueRule> parse_as_list_of_component_values();
     Vector<Vector<StyleComponentValueRule>> parse_as_comma_separated_list_of_component_values();
 
+    enum class SelectorParsingMode {
+        Standard,
+        // `<forgiving-selector-list>` and `<forgiving-relative-selector-list>`
+        // are handled with this parameter, not as separate functions.
+        // https://drafts.csswg.org/selectors/#forgiving-selector
+        Forgiving
+    };
     // Contrary to the name, these parse a comma-separated list of selectors, according to the spec.
-    Optional<SelectorList> parse_as_selector();
-    Optional<SelectorList> parse_as_relative_selector();
+    Optional<SelectorList> parse_as_selector(SelectorParsingMode = SelectorParsingMode::Standard);
+    Optional<SelectorList> parse_as_relative_selector(SelectorParsingMode = SelectorParsingMode::Standard);
 
     NonnullRefPtrVector<MediaQuery> parse_as_media_query_list();
     RefPtr<MediaQuery> parse_as_media_query();
@@ -142,13 +149,9 @@ private:
     template<typename T>
     Vector<Vector<StyleComponentValueRule>> parse_a_comma_separated_list_of_component_values(TokenStream<T>&);
     template<typename T>
-    Result<SelectorList, ParsingResult> parse_a_selector(TokenStream<T>&);
-    template<typename T>
-    Result<SelectorList, ParsingResult> parse_a_relative_selector(TokenStream<T>&);
-    template<typename T>
-    Result<SelectorList, ParsingResult> parse_a_selector_list(TokenStream<T>&);
+    Result<SelectorList, ParsingResult> parse_a_selector_list(TokenStream<T>&, SelectorParsingMode = SelectorParsingMode::Standard);
     template<typename T>
-    Result<SelectorList, ParsingResult> parse_a_relative_selector_list(TokenStream<T>&);
+    Result<SelectorList, ParsingResult> parse_a_relative_selector_list(TokenStream<T>&, SelectorParsingMode = SelectorParsingMode::Standard);
     template<typename T>
     NonnullRefPtrVector<MediaQuery> parse_a_media_query_list(TokenStream<T>&);
     template<typename T>