From dffebee901bc427214d0373b821d8ad6c0ef48a8 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Mon, 18 Aug 2025 12:56:29 +0100 Subject: [PATCH] LibWeb/CSS: Use serialize_a_number() for Length serialization This changes the maximum number of decimal places from 5 to 6, but 5 was previously a guess, and not specified behaviour: > For all of the decimal changes (except color) I couldn't really find a > spec that mandates any required precision, so I just copied what > Firefox seems to do, which is limit the output to 5 decimal places. > https://github.com/SerenityOS/serenity/pull/23449 --- Libraries/LibWeb/CSS/Length.cpp | 19 ++++++-- .../Text/expected/css/calc-coverage.txt | 10 ++-- .../interpolation-longhand-properties.txt | 2 +- .../partial-layout-tree-update-4.txt | 2 +- .../animations/calc-interpolation.txt | 48 +++++++++---------- 5 files changed, 47 insertions(+), 34 deletions(-) diff --git a/Libraries/LibWeb/CSS/Length.cpp b/Libraries/LibWeb/CSS/Length.cpp index 3feb8614489..9860d4c9e3d 100644 --- a/Libraries/LibWeb/CSS/Length.cpp +++ b/Libraries/LibWeb/CSS/Length.cpp @@ -225,11 +225,24 @@ String Length::to_string(SerializationMode serialization_mode) const { if (is_auto()) return "auto"_string; + + // https://drafts.csswg.org/cssom/#serialize-a-css-value + // -> + // The component serialized as per followed by the unit in its canonical form as defined in its + // respective specification. + // FIXME: Manually skip this for px so we avoid rounding errors in absolute_length_to_px. // Maybe provide alternative functions that don't produce CSSPixels? - if (serialization_mode == SerializationMode::ResolvedValue && is_absolute() && m_type != Type::Px) - return MUST(String::formatted("{:.5}px", absolute_length_to_px())); - return MUST(String::formatted("{:.5}{}", m_value, unit_name())); + if (serialization_mode == SerializationMode::ResolvedValue && is_absolute() && m_type != Type::Px) { + StringBuilder builder; + serialize_a_number(builder, absolute_length_to_px().to_double()); + builder.append("px"sv); + return builder.to_string_without_validation(); + } + StringBuilder builder; + serialize_a_number(builder, m_value); + builder.append(unit_name()); + return builder.to_string_without_validation(); } StringView Length::unit_name() const diff --git a/Tests/LibWeb/Text/expected/css/calc-coverage.txt b/Tests/LibWeb/Text/expected/css/calc-coverage.txt index 292343e9fc2..163dac5834b 100644 --- a/Tests/LibWeb/Text/expected/css/calc-coverage.txt +++ b/Tests/LibWeb/Text/expected/css/calc-coverage.txt @@ -95,11 +95,11 @@ line-height: 'calc(2 * var(--n))' -> '64px' margin-bottom: 'calc(2px)' -> '2px' margin-bottom: 'calc(2px * var(--n))' -> '4px' margin-left: 'calc(2%)' -> '15.6875px' -margin-left: 'calc(2% * var(--n))' -> '31.35938px' +margin-left: 'calc(2% * var(--n))' -> '31.359375px' margin-right: 'calc(2px)' -> '2px' margin-right: 'calc(2px * var(--n))' -> '4px' margin-top: 'calc(2%)' -> '15.6875px' -margin-top: 'calc(2% * var(--n))' -> '31.35938px' +margin-top: 'calc(2% * var(--n))' -> '31.359375px' math-depth: 'calc(2)' -> '2' math-depth: 'calc(2 * var(--n))' -> '4' max-height: 'calc(2px)' -> '2px' @@ -121,11 +121,11 @@ outline-width: 'calc(2px * var(--n))' -> '0px' padding-bottom: 'calc(2px)' -> '2px' padding-bottom: 'calc(2px * var(--n))' -> '4px' padding-left: 'calc(2%)' -> '15.6875px' -padding-left: 'calc(2% * var(--n))' -> '31.35938px' +padding-left: 'calc(2% * var(--n))' -> '31.359375px' padding-right: 'calc(2px)' -> '2px' padding-right: 'calc(2px * var(--n))' -> '4px' padding-top: 'calc(2%)' -> '15.6875px' -padding-top: 'calc(2% * var(--n))' -> '31.35938px' +padding-top: 'calc(2% * var(--n))' -> '31.359375px' r: 'calc(2px)' -> '2px' r: 'calc(2px * var(--n))' -> '4px' right: 'calc(2%)' -> '2%' @@ -159,7 +159,7 @@ transition-duration: 'calc(2s * var(--n))' -> '4s' vertical-align: 'calc(2px)' -> '2px' vertical-align: 'calc(2px * var(--n))' -> '4px' width: 'calc(2%)' -> '15.6875px' -width: 'calc(2% * var(--n))' -> '31.35938px' +width: 'calc(2% * var(--n))' -> '31.359375px' word-spacing: 'calc(2px)' -> '2px' word-spacing: 'calc(2px * var(--n))' -> '4px' x: 'calc(2px)' -> '2px' diff --git a/Tests/LibWeb/Text/expected/interpolation-longhand-properties.txt b/Tests/LibWeb/Text/expected/interpolation-longhand-properties.txt index c6d810bae0b..600f85b64d9 100644 --- a/Tests/LibWeb/Text/expected/interpolation-longhand-properties.txt +++ b/Tests/LibWeb/Text/expected/interpolation-longhand-properties.txt @@ -6,7 +6,7 @@ At time 400: background-color: rgb(78, 88, 99) background-repeat: repeat-x bottom: auto - box-shadow: rgb(153, 0, 102) 40px 80px 126px 0px inset, rgba(0, 0, 102, 0.4) 20px 4px 8px 12px + box-shadow: rgb(153, 0, 102) 40.000001px 80.000001px 126.000002px 0px inset, rgba(0, 0, 102, 0.4) 20px 4px 8px 12px color: rgb(153, 0, 102) transform: matrix(1, 0, 0, 1, 40, 40) diff --git a/Tests/LibWeb/Text/expected/layout-tree-update/partial-layout-tree-update-4.txt b/Tests/LibWeb/Text/expected/layout-tree-update/partial-layout-tree-update-4.txt index 3cd14d2ed10..0deb55d8e7f 100644 --- a/Tests/LibWeb/Text/expected/layout-tree-update/partial-layout-tree-update-4.txt +++ b/Tests/LibWeb/Text/expected/layout-tree-update/partial-layout-tree-update-4.txt @@ -1,3 +1,3 @@ 18.8125px 36.625px -54.79688px +54.796875px diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-values/animations/calc-interpolation.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-values/animations/calc-interpolation.txt index cabb99481dc..f6affe12be7 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-values/animations/calc-interpolation.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-values/animations/calc-interpolation.txt @@ -4,34 +4,34 @@ Found 140 tests 84 Pass 56 Fail -Fail CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-8.50705e+37px] +Fail CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-8.5070575e+37px] Fail CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (0) should be [0px] -Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (0.25) should be [8.50705e+37px] -Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (0.5) should be [1.70141e+38px] -Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (0.75) should be [2.552115e+38px] -Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (1) should be [3.40282e+38px] -Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (1.25) should be [4.2535250000000006e+38px] -Fail CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-8.50705e+37px] +Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (0.25) should be [8.5070575e+37px] +Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (0.5) should be [1.7014115e+38px] +Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (0.75) should be [2.55211725e+38px] +Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (1) should be [3.402823e+38px] +Pass CSS Transitions: property from [0px] to [calc(infinity * 1px)] at (1.25) should be [4.2535287499999996e+38px] +Fail CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-8.5070575e+37px] Fail CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (0) should be [0px] -Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (0.25) should be [8.50705e+37px] -Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (0.5) should be [1.70141e+38px] -Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (0.75) should be [2.552115e+38px] -Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (1) should be [3.40282e+38px] -Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (1.25) should be [4.2535250000000006e+38px] -Fail CSS Animations: property from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-3.40282e+38px] +Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (0.25) should be [8.5070575e+37px] +Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (0.5) should be [1.7014115e+38px] +Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (0.75) should be [2.55211725e+38px] +Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (1) should be [3.402823e+38px] +Pass CSS Transitions with transition: all: property from [0px] to [calc(infinity * 1px)] at (1.25) should be [4.2535287499999996e+38px] +Fail CSS Animations: property from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-3.402823e+38px] Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (0) should be [0px] -Fail CSS Animations: property from [0px] to [calc(infinity * 1px)] at (0.25) should be [3.40282e+38px] -Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (0.5) should be [3.40282e+38px] -Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (0.75) should be [3.40282e+38px] -Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (1) should be [3.40282e+38px] -Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (1.25) should be [3.40282e+38px] -Fail Web Animations: property from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-3.40282e+38px] +Fail CSS Animations: property from [0px] to [calc(infinity * 1px)] at (0.25) should be [3.402823e+38px] +Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (0.5) should be [3.402823e+38px] +Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (0.75) should be [3.402823e+38px] +Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (1) should be [3.402823e+38px] +Pass CSS Animations: property from [0px] to [calc(infinity * 1px)] at (1.25) should be [3.402823e+38px] +Fail Web Animations: property from [0px] to [calc(infinity * 1px)] at (-0.25) should be [-3.402823e+38px] Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (0) should be [0px] -Fail Web Animations: property from [0px] to [calc(infinity * 1px)] at (0.25) should be [3.40282e+38px] -Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (0.5) should be [3.40282e+38px] -Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (0.75) should be [3.40282e+38px] -Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (1) should be [3.40282e+38px] -Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (1.25) should be [3.40282e+38px] +Fail Web Animations: property from [0px] to [calc(infinity * 1px)] at (0.25) should be [3.402823e+38px] +Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (0.5) should be [3.402823e+38px] +Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (0.75) should be [3.402823e+38px] +Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (1) should be [3.402823e+38px] +Pass Web Animations: property from [0px] to [calc(infinity * 1px)] at (1.25) should be [3.402823e+38px] Fail CSS Transitions: property from [calc(50% - 25px)] to [calc(100% - 10px)] at (-0.25) should be [-10px] Fail CSS Transitions: property from [calc(50% - 25px)] to [calc(100% - 10px)] at (0) should be [0px] Fail CSS Transitions: property from [calc(50% - 25px)] to [calc(100% - 10px)] at (0.25) should be [10px]