diff --git a/Tests/LibWeb/Text/expected/css/custom-ident-parsing.txt b/Tests/LibWeb/Text/expected/css/custom-ident-parsing.txt new file mode 100644 index 00000000000..875984af2a7 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/custom-ident-parsing.txt @@ -0,0 +1,16 @@ + Before testing: none +badger: badger +none: none +BANANA: BANANA +NONE: none +InHeRiT: none +revert: none +initial: none +unset: none +george: george +REVERT: none +NaCl: NaCl +default: INVALID +string: string +32: INVALID +done: done diff --git a/Tests/LibWeb/Text/input/css/custom-ident-parsing.html b/Tests/LibWeb/Text/input/css/custom-ident-parsing.html new file mode 100644 index 00000000000..99a247c9316 --- /dev/null +++ b/Tests/LibWeb/Text/input/css/custom-ident-parsing.html @@ -0,0 +1,14 @@ + +
+ diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 06e73d1d495..9d3cf46b252 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -1562,6 +1562,37 @@ RefPtr Parser::parse_builtin_value(ComponentValue const& component_v return nullptr; } +// https://www.w3.org/TR/css-values-4/#custom-idents +RefPtr Parser::parse_custom_ident_value(TokenStream& tokens, std::initializer_list blacklist) +{ + auto transaction = tokens.begin_transaction(); + tokens.skip_whitespace(); + + auto token = tokens.next_token(); + if (!token.is(Token::Type::Ident)) + return nullptr; + auto custom_ident = token.token().ident(); + + // The CSS-wide keywords are not valid s. + if (is_css_wide_keyword(custom_ident)) + return nullptr; + + // The default keyword is reserved and is also not a valid . + if (custom_ident.equals_ignoring_ascii_case("default"sv)) + return nullptr; + + // Specifications using must specify clearly what other keywords are excluded from , + // if any—for example by saying that any pre-defined keywords in that property’s value definition are excluded. + // Excluded keywords are excluded in all ASCII case permutations. + for (auto& value : blacklist) { + if (custom_ident.equals_ignoring_ascii_case(value)) + return nullptr; + } + + transaction.commit(); + return CustomIdentStyleValue::create(custom_ident); +} + RefPtr Parser::parse_calculated_value(ComponentValue const& component_value) { if (!component_value.is_function()) @@ -2896,7 +2927,7 @@ RefPtr Parser::parse_color_value(TokenStream& tokens // https://drafts.csswg.org/css-lists-3/#counter-functions RefPtr Parser::parse_counter_value(TokenStream& tokens) { - auto parse_counter_name = [](TokenStream& tokens) -> Optional { + auto parse_counter_name = [this](TokenStream& tokens) -> Optional { // https://drafts.csswg.org/css-lists-3/#typedef-counter-name // Counters are referred to in CSS syntax using the type, which represents // their name as a . A name cannot match the keyword none; @@ -2904,8 +2935,8 @@ RefPtr Parser::parse_counter_value(TokenStream& toke auto transaction = tokens.begin_transaction(); tokens.skip_whitespace(); - auto& token = tokens.next_token(); - if (!token.is(Token::Type::Ident) || token.token().ident() == "none"sv) + auto counter_name = parse_custom_ident_value(tokens, { "none"sv }); + if (!counter_name) return {}; tokens.skip_whitespace(); @@ -2913,10 +2944,10 @@ RefPtr Parser::parse_counter_value(TokenStream& toke return {}; transaction.commit(); - return token.token().ident(); + return counter_name->custom_ident(); }; - auto parse_counter_style = [](TokenStream& tokens) -> RefPtr { + auto parse_counter_style = [this](TokenStream& tokens) -> RefPtr { // https://drafts.csswg.org/css-counter-styles-3/#typedef-counter-style // = | // For now we just support , found here: @@ -2925,8 +2956,8 @@ RefPtr Parser::parse_counter_value(TokenStream& toke auto transaction = tokens.begin_transaction(); tokens.skip_whitespace(); - auto& token = tokens.next_token(); - if (!token.is(Token::Type::Ident) || token.token().ident() == "none"sv) + auto counter_style_name = parse_custom_ident_value(tokens, { "none"sv }); + if (!counter_style_name) return {}; tokens.skip_whitespace(); @@ -2934,7 +2965,7 @@ RefPtr Parser::parse_counter_value(TokenStream& toke return {}; transaction.commit(); - return CustomIdentStyleValue::create(token.token().ident()); + return counter_style_name.release_nonnull(); }; auto transaction = tokens.begin_transaction(); @@ -6507,11 +6538,9 @@ RefPtr Parser::parse_grid_track_placement(TokenStream bool { + auto parse_custom_ident = [this](auto& tokens) { // The additionally excludes the keywords span and auto. - if (token.is(Token::Type::Ident) && !token.is_ident("span"sv) && !token.is_ident("auto"sv)) - return true; - return false; + return parse_custom_ident_value(tokens, { "span"sv, "auto"sv }); }; auto transaction = tokens.begin_transaction(); @@ -6519,6 +6548,10 @@ RefPtr Parser::parse_grid_track_placement(TokenStreamcustom_ident().to_string())); + } auto& token = tokens.next_token(); if (auto maybe_calculated = parse_calculated_value(token); maybe_calculated && maybe_calculated->resolves_to_number()) { transaction.commit(); @@ -6536,10 +6569,6 @@ RefPtr Parser::parse_grid_track_placement(TokenStream(token.token().number_value()), {})); } - if (is_custom_ident(token)) { - transaction.commit(); - return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line({}, token.token().ident().to_string())); - } return nullptr; } @@ -6563,10 +6592,10 @@ RefPtr Parser::parse_grid_track_placement(TokenStream(tokens.next_token().token().to_integer()); continue; } - if (is_custom_ident(token)) { + if (auto custom_ident = parse_custom_ident(tokens)) { if (!identifier_value.is_empty()) return nullptr; - identifier_value = tokens.next_token().token().ident().to_string(); + identifier_value = custom_ident->custom_ident().to_string(); continue; } break; @@ -7244,8 +7273,8 @@ Optional Parser::parse_css_value_for_properties(Readon // Custom idents if (auto property = any_property_accepts_type(property_ids, ValueType::CustomIdent); property.has_value()) { - (void)tokens.next_token(); - return PropertyAndValue { *property, CustomIdentStyleValue::create(peek_token.token().ident()) }; + if (auto custom_ident = parse_custom_ident_value(tokens, {})) + return PropertyAndValue { *property, custom_ident }; } } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 598fd6cd58b..7fa229fa276 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -279,6 +279,7 @@ private: Optional parse_css_value_for_properties(ReadonlySpan, TokenStream&); RefPtr parse_builtin_value(ComponentValue const&); RefPtr parse_calculated_value(ComponentValue const&); + RefPtr parse_custom_ident_value(TokenStream&, std::initializer_list blacklist); // NOTE: Implemented in generated code. (GenerateCSSMathFunctions.cpp) OwnPtr parse_math_function(PropertyID, Function const&); OwnPtr parse_a_calc_function_node(Function const&);