mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 03:47:34 +00:00
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.
This commit is contained in:
parent
7628f1f233
commit
5319e2ba8e
2 changed files with 36 additions and 39 deletions
|
@ -179,9 +179,18 @@ NonnullRefPtr<CSSStyleSheet> Parser::parse_a_stylesheet(TokenStream<T>& tokens)
|
||||||
return CSSStyleSheet::create(rules);
|
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 {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<SelectorList> 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())
|
if (!selector_list.is_error())
|
||||||
return selector_list.release_value();
|
return selector_list.release_value();
|
||||||
|
|
||||||
|
@ -189,28 +198,7 @@ Optional<SelectorList> Parser::parse_as_selector()
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Result<SelectorList, Parser::ParsingResult> Parser::parse_a_selector(TokenStream<T>& tokens)
|
Result<SelectorList, Parser::ParsingResult> Parser::parse_a_selector_list(TokenStream<T>& tokens, SelectorParsingMode parsing_mode)
|
||||||
{
|
|
||||||
return parse_a_selector_list(tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<SelectorList> 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<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)
|
|
||||||
{
|
{
|
||||||
auto comma_separated_lists = parse_a_comma_separated_list_of_component_values(tokens);
|
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) {
|
for (auto& selector_parts : comma_separated_lists) {
|
||||||
auto stream = TokenStream(selector_parts);
|
auto stream = TokenStream(selector_parts);
|
||||||
auto selector = parse_complex_selector(stream, false);
|
auto selector = parse_complex_selector(stream, false);
|
||||||
if (selector.is_error())
|
if (selector.is_error()) {
|
||||||
|
if (parsing_mode == SelectorParsingMode::Forgiving)
|
||||||
|
continue;
|
||||||
return selector.error();
|
return selector.error();
|
||||||
|
}
|
||||||
selectors.append(selector.release_value());
|
selectors.append(selector.release_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectors.is_empty())
|
if (selectors.is_empty() && parsing_mode != SelectorParsingMode::Forgiving)
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
|
|
||||||
return selectors;
|
return selectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
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);
|
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) {
|
for (auto& selector_parts : comma_separated_lists) {
|
||||||
auto stream = TokenStream(selector_parts);
|
auto stream = TokenStream(selector_parts);
|
||||||
auto selector = parse_complex_selector(stream, true);
|
auto selector = parse_complex_selector(stream, true);
|
||||||
if (selector.is_error())
|
if (selector.is_error()) {
|
||||||
|
if (parsing_mode == SelectorParsingMode::Forgiving)
|
||||||
|
continue;
|
||||||
return selector.error();
|
return selector.error();
|
||||||
|
}
|
||||||
selectors.append(selector.release_value());
|
selectors.append(selector.release_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectors.is_empty())
|
if (selectors.is_empty() && parsing_mode != SelectorParsingMode::Forgiving)
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
|
|
||||||
return selectors;
|
return selectors;
|
||||||
|
@ -593,7 +587,7 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
|
||||||
if (pseudo_function.name().equals_ignoring_case("not")) {
|
if (pseudo_function.name().equals_ignoring_case("not")) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not;
|
||||||
auto function_token_stream = TokenStream(pseudo_function.values());
|
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()) {
|
if (not_selector.is_error()) {
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "Invalid selector in :not() clause");
|
dbgln_if(CSS_PARSER_DEBUG, "Invalid selector in :not() clause");
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
|
@ -2087,7 +2081,7 @@ RefPtr<CSSRule> Parser::convert_to_rule(NonnullRefPtr<StyleRule> rule)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auto prelude_stream = TokenStream(rule->m_prelude);
|
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.is_error()) {
|
||||||
if (selectors.error() != ParsingResult::IncludesIgnoredVendorPrefix) {
|
if (selectors.error() != ParsingResult::IncludesIgnoredVendorPrefix) {
|
||||||
|
|
|
@ -105,9 +105,16 @@ public:
|
||||||
Vector<StyleComponentValueRule> parse_as_list_of_component_values();
|
Vector<StyleComponentValueRule> parse_as_list_of_component_values();
|
||||||
Vector<Vector<StyleComponentValueRule>> parse_as_comma_separated_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.
|
// 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_selector(SelectorParsingMode = SelectorParsingMode::Standard);
|
||||||
Optional<SelectorList> parse_as_relative_selector();
|
Optional<SelectorList> parse_as_relative_selector(SelectorParsingMode = SelectorParsingMode::Standard);
|
||||||
|
|
||||||
NonnullRefPtrVector<MediaQuery> parse_as_media_query_list();
|
NonnullRefPtrVector<MediaQuery> parse_as_media_query_list();
|
||||||
RefPtr<MediaQuery> parse_as_media_query();
|
RefPtr<MediaQuery> parse_as_media_query();
|
||||||
|
@ -142,13 +149,9 @@ private:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Vector<Vector<StyleComponentValueRule>> parse_a_comma_separated_list_of_component_values(TokenStream<T>&);
|
Vector<Vector<StyleComponentValueRule>> parse_a_comma_separated_list_of_component_values(TokenStream<T>&);
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Result<SelectorList, ParsingResult> parse_a_selector(TokenStream<T>&);
|
Result<SelectorList, ParsingResult> parse_a_selector_list(TokenStream<T>&, SelectorParsingMode = SelectorParsingMode::Standard);
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Result<SelectorList, ParsingResult> parse_a_relative_selector(TokenStream<T>&);
|
Result<SelectorList, ParsingResult> parse_a_relative_selector_list(TokenStream<T>&, SelectorParsingMode = SelectorParsingMode::Standard);
|
||||||
template<typename T>
|
|
||||||
Result<SelectorList, ParsingResult> parse_a_selector_list(TokenStream<T>&);
|
|
||||||
template<typename T>
|
|
||||||
Result<SelectorList, ParsingResult> parse_a_relative_selector_list(TokenStream<T>&);
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
NonnullRefPtrVector<MediaQuery> parse_a_media_query_list(TokenStream<T>&);
|
NonnullRefPtrVector<MediaQuery> parse_a_media_query_list(TokenStream<T>&);
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue