diff --git a/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp b/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp index 9c0de6d5168..9a0acf899c0 100644 --- a/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp @@ -1,14 +1,14 @@ /* * Copyright (c) 2018-2022, Andreas Kling * Copyright (c) 2020-2021, the SerenityOS developers. - * Copyright (c) 2021-2024, Sam Atkins + * Copyright (c) 2021-2025, Sam Atkins * Copyright (c) 2021, Tobias Christiansen * Copyright (c) 2022, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ -#include +#include #include #include @@ -290,13 +290,19 @@ Parser::ParseErrorOr Parser::parse_attribute_simple_se attribute_tokens.discard_whitespace(); if (!attribute_tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "CSS attribute selector is empty!"); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = "Attribute selector is empty."_string, + }); return ParseError::SyntaxError; } auto maybe_qualified_name = parse_selector_qualified_name(attribute_tokens, AllowWildcardName::No); if (!maybe_qualified_name.has_value()) { - dbgln_if(CSS_PARSER_DEBUG, "Expected qualified-name for attribute name, got: '{}'", attribute_tokens.next_token().to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = MUST(String::formatted("Expected qualified-name, got: '{}'.", attribute_tokens.next_token().to_debug_string())), + }); return ParseError::SyntaxError; } auto qualified_name = maybe_qualified_name.release_value(); @@ -316,7 +322,10 @@ Parser::ParseErrorOr Parser::parse_attribute_simple_se auto const& delim_part = attribute_tokens.consume_a_token(); if (!delim_part.is(Token::Type::Delim)) { - dbgln_if(CSS_PARSER_DEBUG, "Expected a delim for attribute comparison, got: '{}'", delim_part.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = MUST(String::formatted("Expected delim for attribute comparison, got: '{}'.", delim_part.to_debug_string())), + }); return ParseError::SyntaxError; } @@ -324,13 +333,19 @@ Parser::ParseErrorOr Parser::parse_attribute_simple_se simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch; } else { if (!attribute_tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended part way through a match type."); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = "Attribute selector ended part way through a match type."_string, + }); return ParseError::SyntaxError; } auto const& delim_second_part = attribute_tokens.consume_a_token(); if (!delim_second_part.is_delim('=')) { - dbgln_if(CSS_PARSER_DEBUG, "Expected a double delim for attribute comparison, got: '{}{}'", delim_part.to_debug_string(), delim_second_part.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = MUST(String::formatted("Expected a double delim for attribute comparison, got: '{}{}'.", delim_part.to_debug_string(), delim_second_part.to_debug_string())), + }); return ParseError::SyntaxError; } switch (delim_part.token().delim()) { @@ -356,13 +371,19 @@ Parser::ParseErrorOr Parser::parse_attribute_simple_se attribute_tokens.discard_whitespace(); if (!attribute_tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended without a value to match."); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = "Attribute selector ended without a value to match."_string, + }); return ParseError::SyntaxError; } auto const& value_part = attribute_tokens.consume_a_token(); if (!value_part.is(Token::Type::Ident) && !value_part.is(Token::Type::String)) { - dbgln_if(CSS_PARSER_DEBUG, "Expected a string or ident for the value to match attribute against, got: '{}'", value_part.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = MUST(String::formatted("Expected a string or ident for the value to match attribute against, got: '{}'.", value_part.to_debug_string())), + }); return ParseError::SyntaxError; } auto const& value_string = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string(); @@ -379,11 +400,17 @@ Parser::ParseErrorOr Parser::parse_attribute_simple_se } else if (case_sensitivity.equals_ignoring_ascii_case("s"sv)) { simple_selector.attribute().case_type = Selector::SimpleSelector::Attribute::CaseType::CaseSensitiveMatch; } else { - dbgln_if(CSS_PARSER_DEBUG, "Expected a \"i\" or \"s\" attribute selector case sensitivity identifier, got: '{}'", case_sensitivity_part.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = MUST(String::formatted("Expected a \"i\" or \"s\" attribute selector case sensitivity identifier, got: '{}'.", case_sensitivity_part.to_debug_string())), + }); return ParseError::SyntaxError; } } else { - dbgln_if(CSS_PARSER_DEBUG, "Expected an attribute selector case sensitivity identifier, got: '{}'", case_sensitivity_part.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = MUST(String::formatted("Expected an attribute selector case sensitivity identifier, got: '{}'", case_sensitivity_part.to_debug_string())), + }); return ParseError::SyntaxError; } } @@ -391,7 +418,10 @@ Parser::ParseErrorOr Parser::parse_attribute_simple_se attribute_tokens.discard_whitespace(); if (attribute_tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Was not expecting anything else inside attribute selector."); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = first_value.to_string(), + .description = "Trailing tokens in attribute selector."_string, + }); return ParseError::SyntaxError; } @@ -429,7 +459,10 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec pseudo_name = name_token.function().name; is_function = true; } else { - dbgln_if(CSS_PARSER_DEBUG, "Expected an ident or function token for pseudo-element, got: '{}'", name_token.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = name_token.to_string(), + .description = MUST(String::formatted("Pseudo-element should be an ident or function, got: '{}'", name_token.to_debug_string())), + }); return ParseError::SyntaxError; } @@ -451,7 +484,11 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec Selector::PseudoElementSelector::Value value = Empty {}; if (is_function) { if (!metadata.is_valid_as_function) { - dbgln_if(CSS_PARSER_DEBUG, "Pseudo-element '::{}()' is not valid as a function.", pseudo_name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted("::{}", pseudo_name)), + .value_string = name_token.to_string(), + .description = "Not valid as a function."_string, + }); return ParseError::SyntaxError; } @@ -462,14 +499,22 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec switch (metadata.parameter_type) { case PseudoElementMetadata::ParameterType::None: if (function_tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Pseudo-element '::{}()' invalid: Should have no arguments.", pseudo_name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted("::{}", pseudo_name)), + .value_string = name_token.to_string(), + .description = "Should have no arguments."_string, + }); return ParseError::SyntaxError; } break; case PseudoElementMetadata::ParameterType::CompoundSelector: { auto compound_selector_or_error = parse_compound_selector(function_tokens); if (compound_selector_or_error.is_error() || !compound_selector_or_error.value().has_value()) { - dbgln_if(CSS_PARSER_DEBUG, "Failed to parse ::{}() parameter as a compound selector", pseudo_name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted("::{}", pseudo_name)), + .value_string = name_token.to_string(), + .description = "Failed to parse argument as a compound selector."_string, + }); return ParseError::SyntaxError; } @@ -489,12 +534,20 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec } else if (auto custom_ident = parse_custom_ident(function_tokens, {}); custom_ident.has_value()) { value = Selector::PseudoElementSelector::PTNameSelector { .value = custom_ident.release_value() }; } else { - dbgln_if(CSS_PARSER_DEBUG, "Invalid in ::{}() - expected `*` or ``, got `{}`", pseudo_name, function_tokens.next_token().to_debug_string()); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted("::{}", pseudo_name)), + .value_string = name_token.to_string(), + .description = MUST(String::formatted("Invalid - expected `*` or ``, got `{}`", function_tokens.next_token().to_debug_string())), + }); return ParseError::SyntaxError; } function_tokens.discard_whitespace(); if (function_tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid in ::{}() - trailing tokens", pseudo_name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted("::{}", pseudo_name)), + .value_string = name_token.to_string(), + .description = "Invalid - trailing tokens."_string, + }); return ParseError::SyntaxError; } break; @@ -503,7 +556,11 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec } else { if (!metadata.is_valid_as_identifier) { - dbgln_if(CSS_PARSER_DEBUG, "Pseudo-element '::{}' is not valid as an identifier.", pseudo_name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted("::{}", pseudo_name)), + .value_string = name_token.to_string(), + .description = "Only valid as a function."_string, + }); return ParseError::SyntaxError; } } @@ -543,7 +600,9 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec if (has_ignored_vendor_prefix(pseudo_name)) return ParseError::IncludesIgnoredVendorPrefix; - dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-element: '::{}'", pseudo_name); + ErrorReporter::the().report(UnknownPseudoClassOrElementError { + .name = MUST(String::formatted("::{}", pseudo_name)), + }); return ParseError::SyntaxError; } @@ -566,7 +625,11 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec if (auto pseudo_class = pseudo_class_from_string(pseudo_name); pseudo_class.has_value()) { if (!pseudo_class_metadata(pseudo_class.value()).is_valid_as_identifier) { - dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is only valid as a function", pseudo_name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_name)), + .value_string = pseudo_class_token.to_string(), + .description = "Only valid as a function."_string, + }); return ParseError::SyntaxError; } return make_pseudo_class_selector(pseudo_class.value()); @@ -594,7 +657,9 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec } } - dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class: ':{}'", pseudo_name); + ErrorReporter::the().report(UnknownPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_name)), + }); return ParseError::SyntaxError; } @@ -603,7 +668,11 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec auto tokens = TokenStream(function_values); auto nth_child_pattern = parse_a_n_plus_b_pattern(tokens); if (!nth_child_pattern.has_value()) { - dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid An+B format for {}", pseudo_class_name(pseudo_class)); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_class_name(pseudo_class))), + .value_string = tokens.dump_string(), + .description = "Invalid An+B format."_string, + }); return ParseError::SyntaxError; } @@ -644,26 +713,40 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec 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()) { - dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name); + ErrorReporter::the().report(UnknownPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_function.name)), + }); return ParseError::SyntaxError; } auto pseudo_class = maybe_pseudo_class.value(); auto metadata = pseudo_class_metadata(pseudo_class); if (!metadata.is_valid_as_function) { - dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is not valid as a function", pseudo_function.name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_function.name)), + .value_string = pseudo_class_token.to_string(), + .description = "Not valid as a function."_string, + }); return ParseError::SyntaxError; } if (pseudo_function.value.is_empty()) { - dbgln_if(CSS_PARSER_DEBUG, "Empty :{}() selector", pseudo_function.name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_function.name)), + .value_string = pseudo_class_token.to_string(), + .description = "Missing arguments."_string, + }); return ParseError::SyntaxError; } // "The :has() pseudo-class cannot be nested; :has() is not valid within :has()." // https://drafts.csswg.org/selectors/#relational if (pseudo_class == PseudoClass::Has && m_pseudo_class_context.contains_slow(PseudoClass::Has)) { - dbgln_if(CSS_PARSER_DEBUG, ":has() is not allowed inside :has()"); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_function.name)), + .value_string = pseudo_class_token.to_string(), + .description = ":has() is not allowed inside :has()."_string, + }); return ParseError::SyntaxError; } @@ -679,7 +762,11 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec auto function_token_stream = TokenStream(pseudo_function.value); auto compound_selector_or_error = parse_compound_selector(function_token_stream); if (compound_selector_or_error.is_error() || !compound_selector_or_error.value().has_value()) { - dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as a compound selector", pseudo_function.name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_function.name)), + .value_string = pseudo_class_token.to_string(), + .description = "Failed to parse argument as a compound selector."_string, + }); return ParseError::SyntaxError; } @@ -719,7 +806,11 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec auto const& maybe_ident_token = function_token_stream.consume_a_token(); function_token_stream.discard_whitespace(); if (!maybe_ident_token.is(Token::Type::Ident) || function_token_stream.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter: not an ident", pseudo_function.name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_function.name)), + .value_string = pseudo_class_token.to_string(), + .description = "Failed to parse argument as an ident."_string, + }); return ParseError::SyntaxError; } @@ -746,7 +837,11 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec language_token_stream.discard_whitespace(); auto const& language_token = language_token_stream.consume_a_token(); if (!(language_token.is(Token::Type::Ident) || language_token.is(Token::Type::String))) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid language range in :{}() - not a string/ident", pseudo_function.name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_function.name)), + .value_string = pseudo_class_token.to_string(), + .description = "Failed to parse argument as a language range: Not a string/ident."_string, + }); return ParseError::SyntaxError; } @@ -755,7 +850,11 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec language_token_stream.discard_whitespace(); if (language_token_stream.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid language range in :{}() - trailing tokens", pseudo_function.name); + ErrorReporter::the().report(InvalidPseudoClassOrElementError { + .name = MUST(String::formatted(":{}", pseudo_function.name)), + .value_string = pseudo_class_token.to_string(), + .description = "Failed to parse argument as a language range: Has trailing tokens."_string, + }); return ParseError::SyntaxError; } } @@ -787,7 +886,10 @@ Parser::ParseErrorOr Parser::parse_pseudo_simple_selec VERIFY_NOT_REACHED(); } } - dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = pseudo_class_token.to_string(), + .description = MUST(String::formatted("Pseudo-class should be an ident or function, got: '{}'", pseudo_class_token.to_debug_string())), + }); return ParseError::SyntaxError; } @@ -833,7 +935,10 @@ Parser::ParseErrorOr> Parser::parse_simple_se auto const& class_name_value = tokens.consume_a_token(); if (!class_name_value.is(Token::Type::Ident)) { - dbgln_if(CSS_PARSER_DEBUG, "Expected an ident after '.', got: {}", class_name_value.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = tokens.dump_string(), + .description = MUST(String::formatted("Expected an ident after '.', got: {}", class_name_value.to_debug_string())), + }); return ParseError::SyntaxError; } return Selector::SimpleSelector { @@ -850,14 +955,20 @@ Parser::ParseErrorOr> Parser::parse_simple_se tokens.reconsume_current_input_token(); return Optional {}; default: - dbgln_if(CSS_PARSER_DEBUG, "Unrecognized delimiter in selector: '{}'", first_value.token().to_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = tokens.dump_string(), + .description = MUST(String::formatted("Unrecognized delimiter: {}", first_value.token().to_string())), + }); return ParseError::SyntaxError; } } if (first_value.is(Token::Type::Hash)) { if (first_value.token().hash_type() != Token::HashType::Id) { - dbgln_if(CSS_PARSER_DEBUG, "Selector contains hash token that is not an id: {}", first_value.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = tokens.dump_string(), + .description = MUST(String::formatted("Hash token is not an id: {}", first_value.to_debug_string())), + }); return ParseError::SyntaxError; } return Selector::SimpleSelector { @@ -872,20 +983,16 @@ Parser::ParseErrorOr> Parser::parse_simple_se if (first_value.is(Token::Type::Colon)) return TRY(parse_pseudo_simple_selector(tokens)); - dbgln_if(CSS_PARSER_DEBUG, "Invalid simple selector: {}", first_value.to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .value_string = tokens.dump_string(), + .description = MUST(String::formatted("Invalid start of a simple selector: {}", first_value.to_debug_string())), + }); return ParseError::SyntaxError; } Optional Parser::parse_a_n_plus_b_pattern(TokenStream& values) { auto transaction = values.begin_transaction(); - auto syntax_error = [&]() -> Optional { - if constexpr (CSS_PARSER_DEBUG) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid An+B value:"); - values.dump_all_tokens(); - } - return {}; - }; auto is_sign = [](ComponentValue const& value) -> bool { return value.is(Token::Type::Delim) && (value.token().delim() == '+' || value.token().delim() == '-'); @@ -1025,7 +1132,7 @@ Optional Parser::parse_a_n_plus_b_patt return Selector::SimpleSelector::ANPlusBPattern { a, b }; } - return syntax_error(); + return {}; } // if (is_ndashdigit_dimension(first_value)) { @@ -1037,7 +1144,7 @@ Optional Parser::parse_a_n_plus_b_patt return Selector::SimpleSelector::ANPlusBPattern { a, maybe_b.value() }; } - return syntax_error(); + return {}; } // if (is_dashndashdigit_ident(first_value)) { @@ -1047,7 +1154,7 @@ Optional Parser::parse_a_n_plus_b_patt return Selector::SimpleSelector::ANPlusBPattern { -1, maybe_b.value() }; } - return syntax_error(); + return {}; } // -n // -n @@ -1090,7 +1197,7 @@ Optional Parser::parse_a_n_plus_b_patt return Selector::SimpleSelector::ANPlusBPattern { -1, b }; } - return syntax_error(); + return {}; } // All that's left now are these: @@ -1149,7 +1256,7 @@ Optional Parser::parse_a_n_plus_b_patt return Selector::SimpleSelector::ANPlusBPattern { 1, b }; } - return syntax_error(); + return {}; } // '+'?† @@ -1160,10 +1267,10 @@ Optional Parser::parse_a_n_plus_b_patt return Selector::SimpleSelector::ANPlusBPattern { 1, maybe_b.value() }; } - return syntax_error(); + return {}; } - return syntax_error(); + return {}; } Optional Parser::parse_as_page_selector_list() @@ -1197,21 +1304,32 @@ Parser::ParseErrorOr Parser::parse_a_page_selector_list(TokenS while (tokens.next_token().is(Token::Type::Colon)) { tokens.discard_a_token(); // : if (!tokens.next_token().is(Token::Type::Ident)) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: pseudo-class is not an ident: `{}`", tokens.next_token().to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .rule_name = "@page"_fly_string, + .value_string = tokens.dump_string(), + .description = "Pseudo-classes must be idents."_string, + }); return ParseError::SyntaxError; } auto pseudo_class_name = static_cast(tokens.consume_a_token()).ident(); if (auto pseudo_class = page_pseudo_class_from_string(pseudo_class_name); pseudo_class.has_value()) { pseudo_classes.append(*pseudo_class); } else { - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: unrecognized pseudo-class `:{}`", pseudo_class_name); + ErrorReporter::the().report(UnknownPseudoClassOrElementError { + .rule_name = "@page"_fly_string, + .name = MUST(String::formatted(":{}", pseudo_class_name)), + }); return ParseError::SyntaxError; } } if (!maybe_ident.has_value() && pseudo_classes.is_empty()) { // Nothing parsed - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: is empty"); + ErrorReporter::the().report(InvalidSelectorError { + .rule_name = "@page"_fly_string, + .value_string = tokens.dump_string(), + .description = "Is empty."_string, + }); return ParseError::SyntaxError; } @@ -1223,12 +1341,20 @@ Parser::ParseErrorOr Parser::parse_a_page_selector_list(TokenS tokens.discard_a_token(); // , tokens.discard_whitespace(); if (!tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: trailing comma"); + ErrorReporter::the().report(InvalidSelectorError { + .rule_name = "@page"_fly_string, + .value_string = tokens.dump_string(), + .description = "Trailing comma."_string, + }); return ParseError::SyntaxError; } } else if (tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: trailing token `{}`", tokens.next_token().to_debug_string()); + ErrorReporter::the().report(InvalidSelectorError { + .rule_name = "@page"_fly_string, + .value_string = tokens.dump_string(), + .description = "Trailing tokens."_string, + }); return ParseError::SyntaxError; } } diff --git a/Libraries/LibWeb/CSS/Selector.cpp b/Libraries/LibWeb/CSS/Selector.cpp index 94b2f36d216..91e03d3b1e2 100644 --- a/Libraries/LibWeb/CSS/Selector.cpp +++ b/Libraries/LibWeb/CSS/Selector.cpp @@ -7,6 +7,7 @@ #include "Selector.h" #include +#include #include namespace Web::CSS { @@ -728,7 +729,10 @@ Optional Selector::SimpleSelector::absolutized(Selecto if (pseudo_class.type == PseudoClass::Has) { for (auto const& selector : pseudo_class.argument_selector_list) { if (contains_invalid_contents_for_has(selector)) { - dbgln_if(CSS_PARSER_DEBUG, "After absolutizing, :has() would contain invalid contents; rejecting"); + Parser::ErrorReporter::the().report(Parser::InvalidSelectorError { + .value_string = selector->serialize(), + .description = "After absolutizing, :has() would contain invalid contents."_string, + }); return {}; } }