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&);