Skip to content

Commit

Permalink
LibWeb: Parse forgiving selector-lists
Browse files Browse the repository at this point in the history
`<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.
  • Loading branch information
AtkinsSJ authored and awesomekling committed Mar 18, 2022
1 parent 7628f1f commit 5319e2b
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 32 deletions.
42 changes: 18 additions & 24 deletions Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,71 +179,65 @@ 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();

return {};
}

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);

NonnullRefPtrVector<Selector> selectors;
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);

NonnullRefPtrVector<Selector> selectors;
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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
19 changes: 11 additions & 8 deletions Userland/Libraries/LibWeb/CSS/Parser/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,16 @@ class Parser {
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();
Expand Down Expand Up @@ -142,13 +149,9 @@ class Parser {
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>
Expand Down

0 comments on commit 5319e2b

Please sign in to comment.