LibWeb: Support top-level tree counting functions

Adds support for `sibling-index()` and `sibling-count()` when parsing
`<number>` and `<integer>`. This is achieved by a new
`TreeCountingFunctionStyleValue` class which is converted within
`absolutized` to `NumberStyleValue` and `IntegerStyleValue` respectively

There are still a few kinks to work out in order to support these
everywhere, namely:
 - There are some `StyleValue`s which aren't absolutized (i.e. those
   which are stored within another `StyleValue` without an
   `absolutize()` method.
 - We don't have a way to represent this new `StyleValue` within
   `{Number,Integer}OrCalculated`. This would be fixed if we were to
   instead just use the `StyleValue` classes until style computation at
   which time they would be absolutized into their respective
   primitives (double, i64, etc) bypassing the need for *OrCalculated
   entirely.
This commit is contained in:
Callum Law 2025-10-01 20:02:33 +13:00 committed by Tim Ledbetter
commit 831e471444
Notes: github-actions[bot] 2025-10-20 15:13:44 +00:00
17 changed files with 184 additions and 32 deletions

View file

@ -256,6 +256,9 @@ Optional<FrequencyPercentage> Parser::parse_frequency_percentage(TokenStream<Com
Optional<IntegerOrCalculated> Parser::parse_integer(TokenStream<ComponentValue>& tokens)
{
// FIXME: We don't have a way to represent tree counting functions within IntegerOrCalculated, we should avoid
// parsing directly to IntegerOrCalculated unless tree counting functions are disallowed in the relevant
// context
if (auto value = parse_integer_value(tokens)) {
if (value->is_integer())
return value->as_integer().integer();
@ -293,6 +296,9 @@ Optional<LengthPercentage> Parser::parse_length_percentage(TokenStream<Component
Optional<NumberOrCalculated> Parser::parse_number(TokenStream<ComponentValue>& tokens)
{
// FIXME: We don't have a way to represent tree counting functions within NumberOrCalculated, we should avoid
// parsing directly to NumberOrCalculated unless tree counting functions are disallowed in the relevant
// context
if (auto value = parse_number_value(tokens)) {
if (value->is_number())
return value->as_number().number();
@ -786,6 +792,9 @@ RefPtr<StyleValue const> Parser::parse_integer_value(TokenStream<ComponentValue>
return calc;
}
if (auto tree_counting_function = parse_tree_counting_function(tokens, TreeCountingFunctionStyleValue::ComputedType::Integer); tree_counting_function)
return tree_counting_function;
return nullptr;
}
@ -802,6 +811,9 @@ RefPtr<StyleValue const> Parser::parse_number_value(TokenStream<ComponentValue>&
return calc;
}
if (auto tree_counting_function = parse_tree_counting_function(tokens, TreeCountingFunctionStyleValue::ComputedType::Number); tree_counting_function)
return tree_counting_function;
return nullptr;
}
@ -4455,6 +4467,33 @@ RefPtr<CalculationNode const> Parser::parse_a_calculation(Vector<ComponentValue>
return simplify_a_calculation_tree(*calculation_tree, context, CalculationResolutionContext {});
}
// https://drafts.csswg.org/css-values-5/#tree-counting
RefPtr<TreeCountingFunctionStyleValue const> Parser::parse_tree_counting_function(TokenStream<ComponentValue>& tokens, TreeCountingFunctionStyleValue::ComputedType computed_type)
{
if (!context_allows_tree_counting_functions())
return nullptr;
auto has_no_arguments = [](Vector<ComponentValue> const& component_values) {
return !any_of(component_values, [](ComponentValue const& value) { return !value.is(Token::Type::Whitespace); });
};
auto transaction = tokens.begin_transaction();
auto token = tokens.consume_a_token();
if (token.is_function("sibling-count"sv) && has_no_arguments(token.function().value)) {
transaction.commit();
return TreeCountingFunctionStyleValue::create(TreeCountingFunctionStyleValue::TreeCountingFunction::SiblingCount, computed_type);
}
if (token.is_function("sibling-index"sv) && has_no_arguments(token.function().value)) {
transaction.commit();
return TreeCountingFunctionStyleValue::create(TreeCountingFunctionStyleValue::TreeCountingFunction::SiblingIndex, computed_type);
}
return nullptr;
}
// https://drafts.csswg.org/css-color-4/#typedef-opacity-opacity-value
RefPtr<StyleValue const> Parser::parse_opacity_value(TokenStream<ComponentValue>& tokens)
{