From e7890429aaa9e1afbcb2584df25289e1c2eb43ab Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 12 Aug 2025 11:55:35 +0100 Subject: [PATCH] LibWeb/CSS: Add support for pseudo-classes taking `#` --- .../LibWeb/CSS/Parser/SelectorParsing.cpp | 33 +++++++++++++++++++ Libraries/LibWeb/CSS/Selector.cpp | 5 +++ Libraries/LibWeb/Dump.cpp | 10 ++++++ .../LibWeb/GenerateCSSPseudoClass.cpp | 3 ++ 4 files changed, 51 insertions(+) diff --git a/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp b/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp index a9bbdcf6db6..2883b4c38fb 100644 --- a/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp @@ -710,6 +710,37 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec }; }; + auto parse_an_plus_b_list_selector = [this](auto pseudo_class, Vector const& function_values) -> ParseErrorOr { + TokenStream tokens { function_values }; + auto list = parse_a_comma_separated_list_of_component_values(tokens); + Vector an_plus_b_patterns; + + for (auto const& values : list) { + TokenStream pattern_tokens { values }; + auto an_plus_b_pattern = parse_a_n_plus_b_pattern(pattern_tokens); + if (!an_plus_b_pattern.has_value()) { + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_class_name(pseudo_class))), + .value_string = pattern_tokens.dump_string(), + .description = "Invalid An+B format."_string, + }); + return ParseError::SyntaxError; + } + an_plus_b_patterns.append(an_plus_b_pattern.release_value()); + } + + tokens.discard_whitespace(); + if (tokens.has_next_token()) + return ParseError::SyntaxError; + + return Selector::SimpleSelector { + .type = Selector::SimpleSelector::Type::PseudoClass, + .value = Selector::SimpleSelector::PseudoClassSelector { + .type = pseudo_class, + .an_plus_b_patterns = move(an_plus_b_patterns) } + }; + }; + auto const& pseudo_function = pseudo_class_token.function(); auto maybe_pseudo_class = pseudo_class_from_string(pseudo_function.name); if (!maybe_pseudo_class.has_value()) { @@ -756,6 +787,8 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec switch (metadata.parameter_type) { case PseudoClassMetadata::ParameterType::ANPlusB: return parse_an_plus_b_selector(pseudo_class, pseudo_function.value, false); + case PseudoClassMetadata::ParameterType::ANPlusBList: + return parse_an_plus_b_list_selector(pseudo_class, pseudo_function.value); case PseudoClassMetadata::ParameterType::ANPlusBOf: return parse_an_plus_b_selector(pseudo_class, pseudo_function.value, true); case PseudoClassMetadata::ParameterType::CompoundSelector: { diff --git a/Libraries/LibWeb/CSS/Selector.cpp b/Libraries/LibWeb/CSS/Selector.cpp index 34be49f512c..2d85ec69f8d 100644 --- a/Libraries/LibWeb/CSS/Selector.cpp +++ b/Libraries/LibWeb/CSS/Selector.cpp @@ -462,6 +462,11 @@ String Selector::SimpleSelector::serialize() const // The result of serializing the value using the rules to serialize an value. s.append(pseudo_class.an_plus_b_patterns.first().serialize()); break; + case PseudoClassMetadata::ParameterType::ANPlusBList: + serialize_a_comma_separated_list(s, pseudo_class.an_plus_b_patterns, [](auto& builder, ANPlusBPattern const& an_plus_b) { + builder.append(an_plus_b.serialize()); + }); + break; case PseudoClassMetadata::ParameterType::CompoundSelector: case PseudoClassMetadata::ParameterType::ForgivingSelectorList: case PseudoClassMetadata::ParameterType::ForgivingRelativeSelectorList: diff --git a/Libraries/LibWeb/Dump.cpp b/Libraries/LibWeb/Dump.cpp index 918331ee4d1..d103a8b7c16 100644 --- a/Libraries/LibWeb/Dump.cpp +++ b/Libraries/LibWeb/Dump.cpp @@ -575,6 +575,16 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector, int in builder.append(")"sv); break; } + case CSS::PseudoClassMetadata::ParameterType::ANPlusBList: { + builder.append("([\n"sv); + for (auto& an_plus_b : pseudo_class.an_plus_b_patterns) { + indent(builder, indent_levels + 2); + builder.appendff("(step={}, offset={})\n", an_plus_b.step_size, an_plus_b.offset); + } + indent(builder, indent_levels + 1); + builder.append("])"sv); + break; + } case CSS::PseudoClassMetadata::ParameterType::CompoundSelector: case CSS::PseudoClassMetadata::ParameterType::ForgivingSelectorList: case CSS::PseudoClassMetadata::ParameterType::ForgivingRelativeSelectorList: diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPseudoClass.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPseudoClass.cpp index aed4abe6adc..1bf225a9d3c 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPseudoClass.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPseudoClass.cpp @@ -70,6 +70,7 @@ struct PseudoClassMetadata { enum class ParameterType { None, ANPlusB, + ANPlusBList, ANPlusBOf, CompoundSelector, ForgivingSelectorList, @@ -168,6 +169,8 @@ PseudoClassMetadata pseudo_class_metadata(PseudoClass pseudo_class) if (is_valid_as_function) { if (argument_string == ""sv) { parameter_type = "ANPlusB"_string; + } else if (argument_string == "#"sv) { + parameter_type = "ANPlusBList"_string; } else if (argument_string == ""sv) { parameter_type = "ANPlusBOf"_string; } else if (argument_string == ""sv) {