From 5319e2ba8e5684edda17bfaf6aaa5cffe7375c75 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 17 Mar 2022 15:14:01 +0000 Subject: [PATCH] LibWeb: Parse forgiving selector-lists `` and `` 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. --- .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 56 +++++++++---------- Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 19 ++++--- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index e48dec1f09..411dcb7e1e 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -179,9 +179,18 @@ NonnullRefPtr Parser::parse_a_stylesheet(TokenStream& tokens) return CSSStyleSheet::create(rules); } -Optional Parser::parse_as_selector() +Optional 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 {}; +} + +Optional Parser::parse_as_relative_selector(SelectorParsingMode parsing_mode) +{ + auto selector_list = parse_a_relative_selector_list(m_token_stream, parsing_mode); if (!selector_list.is_error()) return selector_list.release_value(); @@ -189,28 +198,7 @@ Optional Parser::parse_as_selector() } template -Result Parser::parse_a_selector(TokenStream& tokens) -{ - return parse_a_selector_list(tokens); -} - -Optional Parser::parse_as_relative_selector() -{ - auto selector_list = parse_a_relative_selector(m_token_stream); - if (!selector_list.is_error()) - return selector_list.release_value(); - - return {}; -} - -template -Result Parser::parse_a_relative_selector(TokenStream& tokens) -{ - return parse_a_relative_selector_list(tokens); -} - -template -Result Parser::parse_a_selector_list(TokenStream& tokens) +Result Parser::parse_a_selector_list(TokenStream& tokens, SelectorParsingMode parsing_mode) { auto comma_separated_lists = parse_a_comma_separated_list_of_component_values(tokens); @@ -218,19 +206,22 @@ Result 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 -Result Parser::parse_a_relative_selector_list(TokenStream& tokens) +Result Parser::parse_a_relative_selector_list(TokenStream& tokens, SelectorParsingMode parsing_mode) { auto comma_separated_lists = parse_a_comma_separated_list_of_component_values(tokens); @@ -238,12 +229,15 @@ Result 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 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 Parser::convert_to_rule(NonnullRefPtr 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) { diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index b1fe9ef240..5039614029 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -105,9 +105,16 @@ public: Vector parse_as_list_of_component_values(); Vector> parse_as_comma_separated_list_of_component_values(); + enum class SelectorParsingMode { + Standard, + // `` and `` + // 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 parse_as_selector(); - Optional parse_as_relative_selector(); + Optional parse_as_selector(SelectorParsingMode = SelectorParsingMode::Standard); + Optional parse_as_relative_selector(SelectorParsingMode = SelectorParsingMode::Standard); NonnullRefPtrVector parse_as_media_query_list(); RefPtr parse_as_media_query(); @@ -142,13 +149,9 @@ private: template Vector> parse_a_comma_separated_list_of_component_values(TokenStream&); template - Result parse_a_selector(TokenStream&); + Result parse_a_selector_list(TokenStream&, SelectorParsingMode = SelectorParsingMode::Standard); template - Result parse_a_relative_selector(TokenStream&); - template - Result parse_a_selector_list(TokenStream&); - template - Result parse_a_relative_selector_list(TokenStream&); + Result parse_a_relative_selector_list(TokenStream&, SelectorParsingMode = SelectorParsingMode::Standard); template NonnullRefPtrVector parse_a_media_query_list(TokenStream&); template