LibWeb: Support tree counting functions within calc()

This commit is contained in:
Callum Law 2025-10-01 21:32:14 +13:00 committed by Tim Ledbetter
parent 55bcdcf824
commit e9036c7c75
15 changed files with 62 additions and 44 deletions

View File

@ -4314,6 +4314,10 @@ RefPtr<CalculationNode const> Parser::convert_to_calculation_node(CalcParsing::N
if (component_value->is(Token::Type::Percentage))
return NumericCalculationNode::create(Percentage { component_value->token().percentage() }, context);
auto tree_counting_function_tokens = TokenStream<ComponentValue>::of_single_token(component_value);
if (auto tree_counting_function = parse_tree_counting_function(tree_counting_function_tokens, TreeCountingFunctionStyleValue::ComputedType::Number))
return NonMathFunctionCalculationNode::create(tree_counting_function.release_nonnull(), NumericType {});
// NOTE: If we get here, then we have a ComponentValue that didn't get replaced with something else,
// so the calc() is invalid.
ErrorReporter::the().report(InvalidValueError {

View File

@ -5,6 +5,7 @@
*/
#include "TreeCountingFunctionStyleValue.h"
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
#include <LibWeb/CSS/StyleValues/IntegerStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
@ -34,6 +35,14 @@ size_t TreeCountingFunctionStyleValue::resolve(TreeCountingFunctionResolutionCon
VERIFY_NOT_REACHED();
}
RefPtr<CalculationNode const> TreeCountingFunctionStyleValue::resolve_to_calculation_node(CalculationContext const& calculation_context, CalculationResolutionContext const& calculation_resolution_context) const
{
if (!calculation_resolution_context.tree_counting_function_resolution_context.has_value())
return nullptr;
return NumericCalculationNode::create(Number { Number::Type::Number, static_cast<double>(resolve(calculation_resolution_context.tree_counting_function_resolution_context.value())) }, calculation_context);
}
ValueComparingNonnullRefPtr<StyleValue const> TreeCountingFunctionStyleValue::absolutized(ComputationContext const& computation_context) const
{
// FIXME: We should clamp this value in case it falls outside the valid range for the context it is in

View File

@ -6,11 +6,11 @@
#pragma once
#include <LibWeb/CSS/StyleValues/StyleValue.h>
#include <LibWeb/CSS/StyleValues/AbstractNonMathCalcFunctionStyleValue.h>
namespace Web::CSS {
class TreeCountingFunctionStyleValue final : public StyleValue {
class TreeCountingFunctionStyleValue final : public AbstractNonMathCalcFunctionStyleValue {
public:
enum class TreeCountingFunction : u8 {
SiblingCount,
@ -32,13 +32,14 @@ public:
size_t resolve(TreeCountingFunctionResolutionContext const&) const;
virtual RefPtr<CalculationNode const> resolve_to_calculation_node(CalculationContext const&, CalculationResolutionContext const&) const override;
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
virtual bool equals(StyleValue const& other) const override;
private:
TreeCountingFunctionStyleValue(TreeCountingFunction function, ComputedType computed_type)
: StyleValue(Type::TreeCountingFunction)
: AbstractNonMathCalcFunctionStyleValue(Type::TreeCountingFunction)
, m_function(function)
, m_computed_type(computed_type)
{

View File

@ -2,8 +2,8 @@ Harness status: OK
Found 22 tests
20 Pass
2 Fail
21 Pass
1 Fail
Pass Property transition-timing-function value 'linear'
Pass Property transition-timing-function value 'ease'
Pass Property transition-timing-function value 'ease-in'
@ -21,7 +21,7 @@ Pass Property transition-timing-function value 'steps(2, jump-start)'
Pass Property transition-timing-function value 'steps(2, jump-end)'
Pass Property transition-timing-function value 'steps(2, jump-both)'
Pass Property transition-timing-function value 'steps(2, jump-none)'
Fail Property transition-timing-function value 'steps(calc(2 * sibling-index()), jump-none)'
Pass Property transition-timing-function value 'steps(calc(2 * sibling-index()), jump-none)'
Fail Property transition-timing-function value 'steps(sibling-index(), jump-none)'
Pass Property transition-timing-function value 'steps(calc(2 * sign(1em - 1000px)), jump-none)'
Pass Property transition-timing-function value 'steps(calc(2 * sign(1em - 1000px)), start)'

View File

@ -2,8 +2,8 @@ Harness status: OK
Found 45 tests
36 Pass
9 Fail
39 Pass
6 Fail
Pass acos(1) should be used-value-equivalent to 0deg
Pass atan(0) should be used-value-equivalent to 0deg
Pass asin(0) should be used-value-equivalent to 0deg
@ -24,9 +24,9 @@ Pass calc(atan2(0,1)) should be used-value-equivalent to 0deg
Pass calc(atan2(0,-1) / 4) should be used-value-equivalent to 45deg
Pass calc(atan2(1,-1)) should be used-value-equivalent to 135deg
Pass calc(atan2(-1,1)) should be used-value-equivalent to -45deg
Fail calc(asin(sin(180deg * sibling-index()))) should be used-value-equivalent to 0deg
Fail calc(acos(cos(180deg * sibling-index()))) should be used-value-equivalent to 180deg
Fail calc(atan(tan(180deg * sibling-index()))) should be used-value-equivalent to 0deg
Pass calc(asin(sin(180deg * sibling-index()))) should be used-value-equivalent to 0deg
Pass calc(acos(cos(180deg * sibling-index()))) should be used-value-equivalent to 180deg
Pass calc(atan(tan(180deg * sibling-index()))) should be used-value-equivalent to 0deg
Pass calc(cos(sin(acos(cos(pi))))) should be used-value-equivalent to 1
Pass atan2(1px, -1px) should be used-value-equivalent to 135deg
Pass atan2(1cm, -1cm) should be used-value-equivalent to 135deg

View File

@ -2,8 +2,7 @@ Harness status: OK
Found 47 tests
44 Pass
3 Fail
47 Pass
Pass pow(1,1) should be used-value-equivalent to 1
Pass sqrt(1) should be used-value-equivalent to 1
Pass hypot(1) should be used-value-equivalent to 1
@ -27,9 +26,9 @@ Pass hypot(0% + 3px, 0% + 4px) should be used-value-equivalent to 5px
Pass hypot(0% + 772.333px) should be used-value-equivalent to calc(0% + 772.333px)
Pass hypot(0% + 772.35px) should be used-value-equivalent to calc(0% + 772.35px)
Pass hypot(0% + 600px, 0% + 800px) should be used-value-equivalent to 1000px
Fail sqrt(sibling-index()) should be used-value-equivalent to 2
Fail calc(1px * sqrt(sibling-index())) should be used-value-equivalent to 2px
Fail sqrt(pow(sibling-index(), 2)) should be used-value-equivalent to 4
Pass sqrt(sibling-index()) should be used-value-equivalent to 2
Pass calc(1px * sqrt(sibling-index())) should be used-value-equivalent to 2px
Pass sqrt(pow(sibling-index(), 2)) should be used-value-equivalent to 4
Pass hypot(1px) should be used-value-equivalent to 1px
Pass hypot(1cm) should be used-value-equivalent to 1cm
Pass hypot(1mm) should be used-value-equivalent to 1mm

View File

@ -2,15 +2,14 @@ Harness status: OK
Found 10 tests
6 Pass
4 Fail
Fail e.style['left'] = "calc(1px * sibling-index())" should set the property value
Fail e.style['left'] = "calc(1px * sibling-index( ))" should set the property value
10 Pass
Pass e.style['left'] = "calc(1px * sibling-index())" should set the property value
Pass e.style['left'] = "calc(1px * sibling-index( ))" should set the property value
Pass e.style['z-index'] = "sibling-index()" should set the property value
Pass e.style['left'] = "calc(1px * sibling-index(100px))" should not set the property value
Pass e.style['left'] = "calc(1px * sibling-index(1))" should not set the property value
Fail e.style['left'] = "calc(1px * sibling-count())" should set the property value
Fail e.style['left'] = "calc(1px * sibling-count( ))" should set the property value
Pass e.style['left'] = "calc(1px * sibling-count())" should set the property value
Pass e.style['left'] = "calc(1px * sibling-count( ))" should set the property value
Pass e.style['z-index'] = "sibling-count()" should set the property value
Pass e.style['left'] = "calc(1px * sibling-count(100px))" should not set the property value
Pass e.style['left'] = "calc(1px * sibling-count(1))" should not set the property value

View File

@ -2,11 +2,10 @@ Harness status: OK
Found 6 tests
1 Pass
5 Fail
Fail basic sibling-index() test
Fail basic sibling-count() test
Fail sibling-index() in calc() with percentage
Fail sibling-count() on pseudo-element
Fail sibling-index() on root
6 Pass
Pass basic sibling-index() test
Pass basic sibling-count() test
Pass sibling-index() in calc() with percentage
Pass sibling-count() on pseudo-element
Pass sibling-index() on root
Pass sibling-count() on root

View File

@ -2,8 +2,9 @@ Harness status: OK
Found 4 tests
4 Fail
Fail Initially 6th sibling
2 Pass
2 Fail
Pass Initially 6th sibling
Fail 5th sibling after removal
Fail Initially 6 siblings
Pass Initially 6 siblings
Fail 5 siblings after removal

View File

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests
2 Fail
Fail Initially, the sibling-index() is 3 for #target
1 Pass
1 Fail
Pass Initially, the sibling-index() is 3 for #target
Fail Removing a preceding sibling of #target reduces the sibling-index()

View File

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests
2 Fail
Fail Initially, the sibling-index() is 3 for #target
1 Pass
1 Fail
Pass Initially, the sibling-index() is 3 for #target
Fail Removing a preceding sibling of #target reduces the sibling-index()

View File

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests
2 Fail
Fail Initially, the sibling-index() is 2 for #target
1 Pass
1 Fail
Pass Initially, the sibling-index() is 2 for #target
Fail Removing a preceding sibling of #target reduces the sibling-index()

View File

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests
2 Fail
Fail Initially, the sibling-index() is 3 for #target
1 Pass
1 Fail
Pass Initially, the sibling-index() is 3 for #target
Fail Removing a preceding sibling of #target reduces the sibling-index()

View File

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests
2 Fail
Fail Initially, the sibling-index() is 3 for #target
1 Pass
1 Fail
Pass Initially, the sibling-index() is 3 for #target
Fail Removing a preceding sibling of #target reduces the sibling-index()

View File

@ -2,6 +2,7 @@ Harness status: OK
Found 2 tests
2 Fail
Fail Initially, the sibling-index() is 3 for #target
1 Pass
1 Fail
Pass Initially, the sibling-index() is 3 for #target
Fail Removing a preceding sibling of #target reduces the sibling-index()