LibWeb: Serialize a non-math function at the root of a calc() tree

Previously, math function serialization assumed the calculation tree
root was a numeric value or a calc-operator node, and otherwise emitted
the root's name followed by its comma-separated children.
A non-math function node such as `sibling-index()` or `anchor()` has no
children, so a `calc()` whose entire contents was such a function
serialized to an empty "calc()". We now serialize the function directly
instead.
This commit is contained in:
Tim Ledbetter 2026-06-14 21:42:04 +01:00 committed by Sam Atkins
parent e4a5957bc6
commit b62a099e91
3 changed files with 108 additions and 12 deletions

View file

@ -297,6 +297,13 @@ static void serialize_a_math_function(StringBuilder& builder, CalculationNode co
return;
}
// AD-HOC: A non-math function like sibling-index() or anchor() has no calc() wrapper. Serialize it directly per
// the normal rules for it.
if (fn.type() == CalculationNode::Type::NonMathFunction) {
serialize_a_calculation_tree(builder, fn, context, serialization_mode, EmitOuterParentheses::No);
return;
}
// 3. If the calculation trees root node is a numeric value, or a calc-operator node, let s be a string initially
// containing "calc(".
// Otherwise, let s be a string initially containing the name of the root node, lowercased (such as "sin" or

View file

@ -1,15 +1,53 @@
Harness status: OK
Found 10 tests
Found 47 tests
10 Pass
45 Pass
2 Fail
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(1em * sibling-index())" should set the property value
Pass e.style['left'] = "calc(1rem * sibling-count())" should set the property value
Pass e.style['left'] = "calc(1vh * sibling-index())" should set the property value
Pass e.style['left'] = "calc(1ch * sibling-count())" should set the property value
Pass e.style['left'] = "calc(1% * sibling-index())" should set the property value
Pass e.style['left'] = "calc(5% * sibling-count())" 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
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['left'] = "calc(10 * sibling-index())" should not set the property value
Pass e.style['rotate'] = "calc(10deg * sibling-index())" should set the property value
Fail e.style['rotate'] = "calc(sibling-index() * 2rad * pi)" should set the property value
Pass e.style['rotate'] = "calc(1turn * sibling-count())" should set the property value
Fail e.style['rotate'] = "calc(sibling-count() * 1rad * pi)" should set the property value
Pass e.style['rotate'] = "calc(1px * sibling-index())" should not set the property value
Pass e.style['rotate'] = "calc(1s * sibling-index())" should not set the property value
Pass e.style['z-index'] = "sibling-index()" 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
Pass e.style['z-index'] = "calc(2 * sibling-index())" should set the property value
Pass e.style['z-index'] = "calc(sibling-index())" should set the property value
Pass e.style['z-index'] = "calc(sibling-count())" should set the property value
Pass e.style['z-index'] = "sibling-index(100px)" should not set the property value
Pass e.style['z-index'] = "sibling-count(1)" should not set the property value
Pass e.style['opacity'] = "calc(0.1 * sibling-index())" should set the property value
Pass e.style['opacity'] = "calc(0.5 * sibling-count())" should set the property value
Pass e.style['scale'] = "calc(2 * sibling-index())" should set the property value
Pass e.style['scale'] = "calc(0.5 * sibling-count())" should set the property value
Pass e.style['opacity'] = "calc(1ms * sibling-index())" should not set the property value
Pass e.style['opacity'] = "calc(1px * sibling-count())" should not set the property value
Pass e.style['animation-duration'] = "calc(1s * sibling-index())" should set the property value
Pass e.style['animation-duration'] = "calc(1s * sibling-count( ))" should set the property value
Pass e.style['animation-duration'] = "calc(100ms * sibling-count())" should set the property value
Pass e.style['animation-duration'] = "calc(0.5s * sibling-index())" should set the property value
Pass e.style['animation-duration'] = "calc(250ms * sibling-index())" should set the property value
Pass e.style['animation-duration'] = "calc(1px * sibling-index())" should not set the property value
Pass e.style['animation-duration'] = "calc(1deg * sibling-index())" should not set the property value
Pass e.style['color'] = "color(srgb calc(0.1 * sibling-index()) 0.5 0.5)" should set the property value
Pass e.style['color'] = "color(srgb 0 calc(0.1 * sibling-count()) 0.5)" should set the property value
Pass e.style['color'] = "color(srgb 0 0 calc(0.1 * sibling-index()))" should set the property value
Pass e.style['color'] = "color(srgb 0.5 0.5 0.5 / calc(0.1 * sibling-count()))" should set the property value
Pass e.style['color'] = "oklch(calc(0.1 * sibling-index()) 0.2 180)" should set the property value
Pass e.style['color'] = "oklch(0.5 calc(0.05 * sibling-count()) 180)" should set the property value
Pass e.style['color'] = "oklch(0.5 0.2 calc(30deg * sibling-index()))" should set the property value
Pass e.style['color'] = "oklch(0.5 0.2 180 / calc(0.1 * sibling-index()))" should set the property value
Pass e.style['color'] = "color(srgb calc(1px * sibling-index()) 0 0)" should not set the property value
Pass e.style['color'] = "oklch(calc(1px * sibling-index()) 0 0)" should not set the property value

View file

@ -9,19 +9,70 @@
</head>
<body>
<script>
// <length>
test_valid_value('left', 'calc(1px * sibling-index())');
test_valid_value('left', 'calc(1px * sibling-index( ))', 'calc(1px * sibling-index())');
test_valid_value('z-index', 'sibling-index()');
test_valid_value('left', 'calc(1em * sibling-index())');
test_valid_value('left', 'calc(1rem * sibling-count())');
test_valid_value('left', 'calc(1vh * sibling-index())');
test_valid_value('left', 'calc(1ch * sibling-count())');
test_valid_value('left', 'calc(1% * sibling-index())');
test_valid_value('left', 'calc(5% * sibling-count())');
test_invalid_value('left', 'calc(1px * sibling-index(100px))');
test_invalid_value('left', 'calc(1px * sibling-index(1))');
test_invalid_value('left', 'calc(10 * sibling-index())');
test_valid_value('left', 'calc(1px * sibling-count())');
test_valid_value('left', 'calc(1px * sibling-count( ))', 'calc(1px * sibling-count())');
// <angle>
test_valid_value('rotate', 'calc(10deg * sibling-index())');
test_valid_value('rotate', 'calc(sibling-index() * 2rad * pi)', 'calc(360deg * sibling-index())');
test_valid_value('rotate', 'calc(1turn * sibling-count())', 'calc(360deg * sibling-count())');
test_valid_value('rotate', 'calc(sibling-count() * 1rad * pi)', 'calc(180deg * sibling-count())');
test_invalid_value('rotate', 'calc(1px * sibling-index())');
test_invalid_value('rotate', 'calc(1s * sibling-index())');
// <integer> (which accepts these functions without calc())
test_valid_value('z-index', 'sibling-index()');
test_valid_value('z-index', 'sibling-count()');
test_valid_value('z-index', 'calc(2 * sibling-index())');
test_valid_value('z-index', 'calc(sibling-index())', 'sibling-index()');
test_valid_value('z-index', 'calc(sibling-count())', 'sibling-count()');
test_invalid_value('left', 'calc(1px * sibling-count(100px))');
test_invalid_value('left', 'calc(1px * sibling-count(1))');
test_invalid_value('z-index', 'sibling-index(100px)');
test_invalid_value('z-index', 'sibling-count(1)');
// <number>
test_valid_value('opacity', 'calc(0.1 * sibling-index())');
test_valid_value('opacity', 'calc(0.5 * sibling-count())');
test_valid_value('scale', 'calc(2 * sibling-index())');
test_valid_value('scale', 'calc(0.5 * sibling-count())');
test_invalid_value('opacity', 'calc(1ms * sibling-index())');
test_invalid_value('opacity', 'calc(1px * sibling-count())');
// <time>
test_valid_value('animation-duration', 'calc(1s * sibling-index())');
test_valid_value('animation-duration', 'calc(1s * sibling-count( ))', 'calc(1s * sibling-count())');
test_valid_value('animation-duration', 'calc(100ms * sibling-count())', 'calc(0.1s * sibling-count())');
test_valid_value('animation-duration', 'calc(0.5s * sibling-index())');
test_valid_value('animation-duration', 'calc(250ms * sibling-index())', 'calc(0.25s * sibling-index())');
test_invalid_value('animation-duration', 'calc(1px * sibling-index())');
test_invalid_value('animation-duration', 'calc(1deg * sibling-index())');
// <color> functions
test_valid_value('color', 'color(srgb calc(0.1 * sibling-index()) 0.5 0.5)');
test_valid_value('color', 'color(srgb 0 calc(0.1 * sibling-count()) 0.5)');
test_valid_value('color', 'color(srgb 0 0 calc(0.1 * sibling-index()))');
test_valid_value('color', 'color(srgb 0.5 0.5 0.5 / calc(0.1 * sibling-count()))');
test_valid_value('color', 'oklch(calc(0.1 * sibling-index()) 0.2 180)');
test_valid_value('color', 'oklch(0.5 calc(0.05 * sibling-count()) 180)');
test_valid_value('color', 'oklch(0.5 0.2 calc(30deg * sibling-index()))');
test_valid_value('color', 'oklch(0.5 0.2 180 / calc(0.1 * sibling-index()))');
test_invalid_value('color', 'color(srgb calc(1px * sibling-index()) 0 0)');
test_invalid_value('color', 'oklch(calc(1px * sibling-index()) 0 0)');
</script>
</body>
</html>