LibWeb/CSS: Keep invalid parts of <forgiving-selector-list>s around

Selectors like `:is(.valid, &!?!?!invalid)` need to keep the invalid
part around, even though it will never match, for a couple of reasons:

- Serialization needs to include them
- For nesting, we care if a `&` appeared anywhere in the selector, even
  in an invalid part.

So this patch introduces an `Invalid` simple selector type, which simply
holds its original ComponentValues. We search through these looking for
`&`, and we dump them out directly when asked to serialize.
This commit is contained in:
Sam Atkins 2024-11-13 15:49:43 +00:00 committed by Andreas Kling
commit 698dd600f2
Notes: github-actions[bot] 2024-11-13 19:39:05 +00:00
8 changed files with 110 additions and 3 deletions

View file

@ -60,6 +60,29 @@ Optional<Selector::PseudoElement> Parser::parse_as_pseudo_element_selector()
return simple_selector.pseudo_element();
}
static NonnullRefPtr<Selector> create_invalid_selector(Vector<ComponentValue> component_values)
{
// Trim leading and trailing whitespace
while (!component_values.is_empty() && component_values.first().is(Token::Type::Whitespace)) {
component_values.take_first();
}
while (!component_values.is_empty() && component_values.last().is(Token::Type::Whitespace)) {
component_values.take_last();
}
Selector::SimpleSelector simple {
.type = Selector::SimpleSelector::Type::Invalid,
.value = Selector::SimpleSelector::Invalid {
.component_values = move(component_values),
}
};
Selector::CompoundSelector compound {
.combinator = Selector::Combinator::None,
.simple_selectors = { move(simple) }
};
return Selector::create({ move(compound) });
}
template<typename T>
Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<T>& tokens, SelectorType mode, SelectorParsingMode parsing_mode)
{
@ -70,8 +93,11 @@ Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<T>&
auto stream = TokenStream(selector_parts);
auto selector = parse_complex_selector(stream, mode);
if (selector.is_error()) {
if (parsing_mode == SelectorParsingMode::Forgiving)
if (parsing_mode == SelectorParsingMode::Forgiving) {
// Keep the invalid selector around for serialization and nesting
selectors.append(create_invalid_selector(move(selector_parts)));
continue;
}
return selector.error();
}
selectors.append(selector.release_value());