From 7de17dce9df6407ac3c1e7ee853a13e38829e83e Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 18 Nov 2025 15:25:03 +0000 Subject: [PATCH] LibWeb/CSS: Absolutize grid-related StyleValues --- Libraries/LibWeb/CSS/GridTrackPlacement.cpp | 32 ++++++++ Libraries/LibWeb/CSS/GridTrackPlacement.h | 3 + Libraries/LibWeb/CSS/GridTrackSize.cpp | 75 +++++++++++++++++++ Libraries/LibWeb/CSS/GridTrackSize.h | 7 ++ .../GridTrackPlacementStyleValue.cpp | 10 ++- .../GridTrackPlacementStyleValue.h | 4 +- .../GridTrackSizeListStyleValue.cpp | 10 ++- .../StyleValues/GridTrackSizeListStyleValue.h | 4 +- .../parsing/grid-auto-columns-computed.txt | 12 +-- .../parsing/grid-auto-rows-computed.txt | 12 +-- .../grid-template-columns-computed-nogrid.txt | 9 +-- .../grid-template-rows-computed-nogrid.txt | 9 +-- 12 files changed, 161 insertions(+), 26 deletions(-) diff --git a/Libraries/LibWeb/CSS/GridTrackPlacement.cpp b/Libraries/LibWeb/CSS/GridTrackPlacement.cpp index 2d3b0210da3..dc0eaccee38 100644 --- a/Libraries/LibWeb/CSS/GridTrackPlacement.cpp +++ b/Libraries/LibWeb/CSS/GridTrackPlacement.cpp @@ -1,12 +1,14 @@ /* * Copyright (c) 2023, Aliaksandr Kalenik * Copyright (c) 2022, Martin Falisse + * Copyright (c) 2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #include "GridTrackPlacement.h" #include +#include namespace Web::CSS { @@ -38,4 +40,34 @@ String GridTrackPlacement::to_string(SerializationMode mode) const return MUST(builder.to_string()); } +GridTrackPlacement GridTrackPlacement::absolutized(ComputationContext const& context) const +{ + auto absolutize_integer_or_calculated = [&context](IntegerOrCalculated const& integer_or_calculated) { + if (!integer_or_calculated.is_calculated()) + return integer_or_calculated; + auto absolutized = integer_or_calculated.calculated()->absolutized(context); + if (absolutized->is_calculated()) + return IntegerOrCalculated { absolutized->as_calculated() }; + VERIFY(absolutized->is_integer()); + return IntegerOrCalculated { absolutized->as_integer().integer() }; + }; + + return m_value.visit( + [this](Auto const&) { + return *this; + }, + [&](AreaOrLine const& area_or_line) -> GridTrackPlacement { + return AreaOrLine { + .line_number = area_or_line.line_number.map(absolutize_integer_or_calculated), + .name = area_or_line.name, + }; + }, + [&](Span const& span) -> GridTrackPlacement { + return Span { + .value = absolutize_integer_or_calculated(span.value), + .name = span.name, + }; + }); +} + } diff --git a/Libraries/LibWeb/CSS/GridTrackPlacement.h b/Libraries/LibWeb/CSS/GridTrackPlacement.h index ee37c6c6df3..d3f4f6d21a4 100644 --- a/Libraries/LibWeb/CSS/GridTrackPlacement.h +++ b/Libraries/LibWeb/CSS/GridTrackPlacement.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2023, Aliaksandr Kalenik * Copyright (c) 2022, Martin Falisse + * Copyright (c) 2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -55,6 +56,8 @@ public: String to_string(SerializationMode mode) const; + GridTrackPlacement absolutized(ComputationContext const&) const; + bool operator==(GridTrackPlacement const& other) const = default; private: diff --git a/Libraries/LibWeb/CSS/GridTrackSize.cpp b/Libraries/LibWeb/CSS/GridTrackSize.cpp index d3a4e57173e..d13ee3dcba5 100644 --- a/Libraries/LibWeb/CSS/GridTrackSize.cpp +++ b/Libraries/LibWeb/CSS/GridTrackSize.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2022, Martin Falisse * Copyright (c) 2025, Aliaksandr Kalenik + * Copyright (c) 2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -110,6 +111,41 @@ String GridSize::to_string(SerializationMode mode) const return m_value.visit([mode](auto const& it) { return it.to_string(mode); }); } +GridSize GridSize::absolutized(ComputationContext const& context) const +{ + auto absolutize_length_percentage = [&context](LengthPercentage const& length_percentage) -> Optional { + if (length_percentage.is_length()) { + auto length = length_percentage.length().absolutize(context.length_resolution_context.viewport_rect, context.length_resolution_context.font_metrics, context.length_resolution_context.root_font_metrics); + if (length.has_value()) + return length.release_value(); + return {}; + } + + if (length_percentage.is_calculated()) + return LengthPercentage::from_style_value(length_percentage.calculated()->absolutized(context)); + + return {}; + }; + return m_value.visit( + [&](Size const& size) -> GridSize { + if (size.is_length_percentage()) { + if (auto result = absolutize_length_percentage(size.length_percentage()); result.has_value()) + return Size::make_length_percentage(result.release_value()); + } + + if (size.is_fit_content() && size.fit_content_available_space().has_value()) { + if (auto result = absolutize_length_percentage(size.fit_content_available_space().value()); result.has_value()) { + return Size::make_fit_content(result.release_value()); + } + } + + return GridSize { size }; + }, + [](Flex const& flex) { + return GridSize { flex }; + }); +} + GridMinMax::GridMinMax(GridSize min_grid_size, GridSize max_grid_size) : m_min_grid_size(move(min_grid_size)) , m_max_grid_size(move(max_grid_size)) @@ -127,6 +163,14 @@ String GridMinMax::to_string(SerializationMode mode) const return MUST(builder.to_string()); } +GridMinMax GridMinMax::absolutized(ComputationContext const& context) const +{ + return GridMinMax { + m_min_grid_size.absolutized(context), + m_max_grid_size.absolutized(context), + }; +} + GridRepeat::GridRepeat(GridRepeatType grid_repeat_type, GridTrackSizeList&& grid_track_size_list, size_t repeat_count) : m_type(grid_repeat_type) , m_grid_track_size_list(move(grid_track_size_list)) @@ -162,6 +206,15 @@ String GridRepeat::to_string(SerializationMode mode) const return MUST(builder.to_string()); } +GridRepeat GridRepeat::absolutized(ComputationContext const& context) const +{ + return GridRepeat { + m_type, + m_grid_track_size_list.absolutized(context), + m_repeat_count, + }; +} + ExplicitGridTrack::ExplicitGridTrack(Variant&& value) : m_value(move(value)) { @@ -174,6 +227,13 @@ String ExplicitGridTrack::to_string(SerializationMode mode) const }); } +ExplicitGridTrack ExplicitGridTrack::absolutized(ComputationContext const& context) const +{ + return m_value.visit([&](auto const& it) { + return ExplicitGridTrack { it.absolutized(context) }; + }); +} + String GridLineNames::to_string() const { StringBuilder builder; @@ -239,4 +299,19 @@ void GridTrackSizeList::append(ExplicitGridTrack&& explicit_track) m_list.append(move(explicit_track)); } +GridTrackSizeList GridTrackSizeList::absolutized(ComputationContext const& context) const +{ + GridTrackSizeList result; + for (auto const& item : m_list) { + item.visit( + [&result, &context](ExplicitGridTrack const& track) { + result.append(track.absolutized(context)); + }, + [&result](GridLineNames names) { + result.append(move(names)); + }); + } + return result; +} + } diff --git a/Libraries/LibWeb/CSS/GridTrackSize.h b/Libraries/LibWeb/CSS/GridTrackSize.h index 30e290a489b..cd8ecdfadd5 100644 --- a/Libraries/LibWeb/CSS/GridTrackSize.h +++ b/Libraries/LibWeb/CSS/GridTrackSize.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2022, Martin Falisse * Copyright (c) 2025, Aliaksandr Kalenik + * Copyright (c) 2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -40,6 +41,7 @@ public: bool is_definite() const; String to_string(SerializationMode) const; + GridSize absolutized(ComputationContext const&) const; bool operator==(GridSize const& other) const = default; private: @@ -54,6 +56,7 @@ public: GridSize const& max_grid_size() const& { return m_max_grid_size; } String to_string(SerializationMode) const; + GridMinMax absolutized(ComputationContext const&) const; bool operator==(GridMinMax const& other) const = default; private: @@ -97,6 +100,8 @@ public: void append(GridLineNames&&); void append(ExplicitGridTrack&&); + GridTrackSizeList absolutized(ComputationContext const&) const; + private: Vector> m_list; }; @@ -129,6 +134,7 @@ public: GridRepeatType type() const& { return m_type; } String to_string(SerializationMode) const; + GridRepeat absolutized(ComputationContext const&) const; bool operator==(GridRepeat const& other) const = default; private: @@ -151,6 +157,7 @@ public: GridSize const& grid_size() const { return m_value.get(); } String to_string(SerializationMode) const; + ExplicitGridTrack absolutized(ComputationContext const&) const; bool operator==(ExplicitGridTrack const& other) const = default; private: diff --git a/Libraries/LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.cpp index a3cdf96e9cb..5d5dc1a0c98 100644 --- a/Libraries/LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Tobias Christiansen - * Copyright (c) 2021-2023, Sam Atkins + * Copyright (c) 2021-2025, Sam Atkins * Copyright (c) 2022-2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause @@ -21,4 +21,12 @@ String GridTrackPlacementStyleValue::to_string(SerializationMode mode) const return m_grid_track_placement.to_string(mode); } +ValueComparingNonnullRefPtr GridTrackPlacementStyleValue::absolutized(ComputationContext const& context) const +{ + auto absolutized_placement = m_grid_track_placement.absolutized(context); + if (absolutized_placement == m_grid_track_placement) + return *this; + return create(move(absolutized_placement)); +} + } diff --git a/Libraries/LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h index e0a55f6a413..61a2ebc9099 100644 --- a/Libraries/LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Tobias Christiansen - * Copyright (c) 2021-2023, Sam Atkins + * Copyright (c) 2021-2025, Sam Atkins * Copyright (c) 2022-2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause @@ -22,6 +22,8 @@ public: GridTrackPlacement const& grid_track_placement() const { return m_grid_track_placement; } virtual String to_string(SerializationMode) const override; + virtual ValueComparingNonnullRefPtr absolutized(ComputationContext const&) const override; + bool properties_equal(GridTrackPlacementStyleValue const& other) const { return m_grid_track_placement == other.m_grid_track_placement; } private: diff --git a/Libraries/LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.cpp index 881df86a994..a8a1221eac7 100644 --- a/Libraries/LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Tobias Christiansen - * Copyright (c) 2021-2023, Sam Atkins + * Copyright (c) 2021-2025, Sam Atkins * Copyright (c) 2022-2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause @@ -31,4 +31,12 @@ ValueComparingNonnullRefPtr GridTrackSizeList return adopt_ref(*new (nothrow) GridTrackSizeListStyleValue(CSS::GridTrackSizeList())); } +ValueComparingNonnullRefPtr GridTrackSizeListStyleValue::absolutized(ComputationContext const& context) const +{ + auto absolutized = m_grid_track_size_list.absolutized(context); + if (absolutized == m_grid_track_size_list) + return *this; + return create(move(absolutized)); +} + } diff --git a/Libraries/LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.h index 9286b4f56f6..a70cb482483 100644 --- a/Libraries/LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/GridTrackSizeListStyleValue.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Tobias Christiansen - * Copyright (c) 2021-2023, Sam Atkins + * Copyright (c) 2021-2025, Sam Atkins * Copyright (c) 2022-2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause @@ -26,6 +26,8 @@ public: virtual String to_string(SerializationMode) const override; + virtual ValueComparingNonnullRefPtr absolutized(ComputationContext const&) const override; + bool properties_equal(GridTrackSizeListStyleValue const& other) const { return m_grid_track_size_list == other.m_grid_track_size_list; } private: diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-auto-columns-computed.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-auto-columns-computed.txt index 7ee7f09e9bd..7cebcde5f40 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-auto-columns-computed.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-auto-columns-computed.txt @@ -2,10 +2,10 @@ Harness status: OK Found 25 tests -18 Pass -7 Fail +22 Pass +3 Fail Pass Property grid-auto-columns value '1px' -Fail Property grid-auto-columns value 'calc(10px + 0.5em)' +Pass Property grid-auto-columns value 'calc(10px + 0.5em)' Fail Property grid-auto-columns value 'calc(10px - 0.5em)' Pass Property grid-auto-columns value '4%' Pass Property grid-auto-columns value '5fr' @@ -13,13 +13,13 @@ Pass Property grid-auto-columns value 'min-content' Pass Property grid-auto-columns value 'max-content' Pass Property grid-auto-columns value 'auto' Pass Property grid-auto-columns value 'minmax(1px, 5fr)' -Fail Property grid-auto-columns value 'minmax(calc(10px + 0.5em), max-content)' +Pass Property grid-auto-columns value 'minmax(calc(10px + 0.5em), max-content)' Fail Property grid-auto-columns value 'minmax(calc(10px - 0.5em), max-content)' Pass Property grid-auto-columns value 'minmax(4%, auto)' -Fail Property grid-auto-columns value 'minmax(min-content, calc(10px + 0.5em))' +Pass Property grid-auto-columns value 'minmax(min-content, calc(10px + 0.5em))' Pass Property grid-auto-columns value 'minmax(auto, 4%)' Pass Property grid-auto-columns value 'fit-content(1px)' -Fail Property grid-auto-columns value 'fit-content(calc(10px + 0.5em))' +Pass Property grid-auto-columns value 'fit-content(calc(10px + 0.5em))' Fail Property grid-auto-columns value 'fit-content(calc(10px - 0.5em))' Pass Property grid-auto-columns value 'fit-content(4%)' Pass Property grid-auto-columns value '0px' diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-auto-rows-computed.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-auto-rows-computed.txt index 6c1f0348376..d6f3073b9a0 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-auto-rows-computed.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-auto-rows-computed.txt @@ -2,10 +2,10 @@ Harness status: OK Found 25 tests -18 Pass -7 Fail +22 Pass +3 Fail Pass Property grid-auto-rows value '1px' -Fail Property grid-auto-rows value 'calc(10px + 0.5em)' +Pass Property grid-auto-rows value 'calc(10px + 0.5em)' Fail Property grid-auto-rows value 'calc(10px - 0.5em)' Pass Property grid-auto-rows value '4%' Pass Property grid-auto-rows value '5fr' @@ -13,13 +13,13 @@ Pass Property grid-auto-rows value 'min-content' Pass Property grid-auto-rows value 'max-content' Pass Property grid-auto-rows value 'auto' Pass Property grid-auto-rows value 'minmax(1px, 5fr)' -Fail Property grid-auto-rows value 'minmax(calc(10px + 0.5em), max-content)' +Pass Property grid-auto-rows value 'minmax(calc(10px + 0.5em), max-content)' Fail Property grid-auto-rows value 'minmax(calc(10px - 0.5em), max-content)' Pass Property grid-auto-rows value 'minmax(4%, auto)' -Fail Property grid-auto-rows value 'minmax(min-content, calc(10px + 0.5em))' +Pass Property grid-auto-rows value 'minmax(min-content, calc(10px + 0.5em))' Pass Property grid-auto-rows value 'minmax(auto, 4%)' Pass Property grid-auto-rows value 'fit-content(1px)' -Fail Property grid-auto-rows value 'fit-content(calc(10px + 0.5em))' +Pass Property grid-auto-rows value 'fit-content(calc(10px + 0.5em))' Fail Property grid-auto-rows value 'fit-content(calc(10px - 0.5em))' Pass Property grid-auto-rows value 'fit-content(4%)' Pass Property grid-auto-rows value '0px' diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-template-columns-computed-nogrid.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-template-columns-computed-nogrid.txt index f80ef0fe8fb..dcaafce2690 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-template-columns-computed-nogrid.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-template-columns-computed-nogrid.txt @@ -2,8 +2,7 @@ Harness status: OK Found 32 tests -29 Pass -3 Fail +32 Pass Pass Property grid-template-columns value 'none' Pass Property grid-template-columns value '1px' Pass Property grid-template-columns value '1px [a]' @@ -28,9 +27,9 @@ Pass Property grid-template-columns value '[a] 1px repeat(auto-fit, 2px [b] 3px) Pass Property grid-template-rows value '100% [a] repeat(1, [b] 200% [c]) [d] 300%' Pass Property grid-template-rows value '100% [a] repeat(auto-fill, [b] 200% [c]) [d] 300%' Pass Property grid-template-rows value '100% [a] repeat(auto-fit, [b] 200% [c]) [d] 300%' -Fail Property grid-template-columns value '[a] 1em repeat(1, 2em [b] 3em) 4em [d]' -Fail Property grid-template-columns value '[a] 1em repeat(auto-fill, 2em [b] 3em) 4em [d]' -Fail Property grid-template-columns value '[a] 1em repeat(auto-fit, 2em [b] 3em) 4em [d]' +Pass Property grid-template-columns value '[a] 1em repeat(1, 2em [b] 3em) 4em [d]' +Pass Property grid-template-columns value '[a] 1em repeat(auto-fill, 2em [b] 3em) 4em [d]' +Pass Property grid-template-columns value '[a] 1em repeat(auto-fit, 2em [b] 3em) 4em [d]' Pass Property grid-template-columns value 'repeat(1, 2px [a] 3px) [b] repeat(auto-fill, [c] 200% [d]) [e] 300%' Pass Property grid-template-columns value '[a] repeat(auto-fill, [b] 200% [c]) repeat(1, 2px [d] 3px) [e] 300%' Pass Property grid-template-columns value 'repeat(1, [a] 2px [b] 3px) [b] repeat(auto-fill, [c] 200% [d]) [e] 300%' diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-template-rows-computed-nogrid.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-template-rows-computed-nogrid.txt index 63f0689d025..c0ec8760ff3 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-template-rows-computed-nogrid.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-grid/parsing/grid-template-rows-computed-nogrid.txt @@ -2,8 +2,7 @@ Harness status: OK Found 24 tests -21 Pass -3 Fail +24 Pass Pass Property grid-template-rows value 'none' Pass Property grid-template-rows value '1px' Pass Property grid-template-rows value '1px [a]' @@ -25,6 +24,6 @@ Pass Property grid-template-rows value '[a] 1px repeat(auto-fit, 2px [b] 3px) 4p Pass Property grid-template-rows value '100% [a] repeat(1, [b] 200% [c]) [d] 300%' Pass Property grid-template-rows value '100% [a] repeat(auto-fill, [b] 200% [c]) [d] 300%' Pass Property grid-template-rows value '100% [a] repeat(auto-fit, [b] 200% [c]) [d] 300%' -Fail Property grid-template-rows value '[a] 1em repeat(1, 2em [b] 3em) 4em [d]' -Fail Property grid-template-rows value '[a] 1em repeat(auto-fill, 2em [b] 3em) 4em [d]' -Fail Property grid-template-rows value '[a] 1em repeat(auto-fit, 2em [b] 3em) 4em [d]' \ No newline at end of file +Pass Property grid-template-rows value '[a] 1em repeat(1, 2em [b] 3em) 4em [d]' +Pass Property grid-template-rows value '[a] 1em repeat(auto-fill, 2em [b] 3em) 4em [d]' +Pass Property grid-template-rows value '[a] 1em repeat(auto-fit, 2em [b] 3em) 4em [d]' \ No newline at end of file