From 6a4a60cbd5dda9197f86cde154c038e1b6b0b7c7 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Fri, 21 Feb 2025 16:45:07 +0000 Subject: [PATCH] LibWeb/CSS: Invalidate color-stop caches when source data changes We previously only invalidated the cached color-stop data when the painted area's size changed. However, multiple elements can use the same gradient and be the same size, but have different parameters that affect the gradient stop positions, for example if a stop has an em position. This can also change for the same element over time. The new cache instead uses these parameters as the cache key. So we recompute the cache if lengths would resolve differently, or the area's size is different. The included test fails without this change. --- Libraries/LibWeb/CSS/Length.h | 8 ++++-- .../StyleValues/ConicGradientStyleValue.cpp | 10 ++++++-- .../CSS/StyleValues/ConicGradientStyleValue.h | 10 ++++++-- .../StyleValues/LinearGradientStyleValue.cpp | 16 ++++++++---- .../StyleValues/LinearGradientStyleValue.h | 11 ++++---- .../StyleValues/RadialGradientStyleValue.cpp | 20 +++++++++------ .../StyleValues/RadialGradientStyleValue.h | 10 ++++++-- .../css/gradient-em-positions-ref.html | 24 ++++++++++++++++++ .../Ref/input/css/gradient-em-positions.html | 25 +++++++++++++++++++ 9 files changed, 109 insertions(+), 25 deletions(-) create mode 100644 Tests/LibWeb/Ref/expected/css/gradient-em-positions-ref.html create mode 100644 Tests/LibWeb/Ref/input/css/gradient-em-positions.html diff --git a/Libraries/LibWeb/CSS/Length.h b/Libraries/LibWeb/CSS/Length.h index ab93b8658a5..991ff9acda4 100644 --- a/Libraries/LibWeb/CSS/Length.h +++ b/Libraries/LibWeb/CSS/Length.h @@ -79,6 +79,8 @@ public: CSSPixels cap_height; CSSPixels zero_advance; CSSPixels line_height; + + bool operator==(FontMetrics const&) const = default; }; static Optional unit_from_name(StringView); @@ -159,12 +161,14 @@ public: StringView unit_name() const; struct ResolutionContext { - [[nodiscard]] static Length::ResolutionContext for_window(HTML::Window const&); - [[nodiscard]] static Length::ResolutionContext for_layout_node(Layout::Node const&); + [[nodiscard]] static ResolutionContext for_window(HTML::Window const&); + [[nodiscard]] static ResolutionContext for_layout_node(Layout::Node const&); CSSPixelRect viewport_rect; FontMetrics font_metrics; FontMetrics root_font_metrics; + + bool operator==(ResolutionContext const&) const = default; }; [[nodiscard]] CSSPixels to_px(ResolutionContext const&) const; diff --git a/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp index 79de2c24c8e..cbd085b62c4 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.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 @@ -38,8 +38,14 @@ String ConicGradientStyleValue::to_string(SerializationMode mode) const void ConicGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSSPixelSize size) const { - if (!m_resolved.has_value()) + ResolvedDataCacheKey cache_key { + .length_resolution_context = Length::ResolutionContext::for_layout_node(node), + .size = size, + }; + if (m_resolved_data_cache_key != cache_key) { + m_resolved_data_cache_key = move(cache_key); m_resolved = ResolvedData { Painting::resolve_conic_gradient_data(node, *this), {} }; + } m_resolved->position = m_properties.position->resolved(node, CSSPixelRect { { 0, 0 }, size }); } diff --git a/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h index b3e969f84c7..2604a0ac37a 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.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 @@ -60,11 +60,17 @@ private: bool operator==(Properties const&) const = default; } m_properties; + struct ResolvedDataCacheKey { + Length::ResolutionContext length_resolution_context; + CSSPixelSize size; + bool operator==(ResolvedDataCacheKey const&) const = default; + }; + mutable Optional m_resolved_data_cache_key; + struct ResolvedData { Painting::ConicGradientData data; CSSPixelPoint position; }; - mutable Optional m_resolved; }; diff --git a/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp index 2092d886a52..681ad7bb050 100644 --- a/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp @@ -1,13 +1,14 @@ /* * 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 */ #include "LinearGradientStyleValue.h" +#include #include namespace Web::CSS { @@ -105,15 +106,20 @@ float LinearGradientStyleValue::angle_degrees(CSSPixelSize gradient_size) const void LinearGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSSPixelSize size) const { - if (m_resolved.has_value() && m_resolved->size == size) - return; - m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size }; + ResolvedDataCacheKey cache_key { + .length_resolution_context = Length::ResolutionContext::for_layout_node(node), + .size = size, + }; + if (m_resolved_data_cache_key != cache_key) { + m_resolved_data_cache_key = move(cache_key); + m_resolved = Painting::resolve_linear_gradient_data(node, size, *this); + } } void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const { VERIFY(m_resolved.has_value()); - context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type(), m_resolved->data); + context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type(), m_resolved.value()); } } diff --git a/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h index 3bd68fd2142..97911cf472d 100644 --- a/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.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 @@ -77,12 +77,13 @@ private: bool operator==(Properties const&) const = default; } m_properties; - struct ResolvedData { - Painting::LinearGradientData data; + struct ResolvedDataCacheKey { + Length::ResolutionContext length_resolution_context; CSSPixelSize size; + bool operator==(ResolvedDataCacheKey const&) const = default; }; - - mutable Optional m_resolved; + mutable Optional m_resolved_data_cache_key; + mutable Optional m_resolved; }; } diff --git a/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp index 4a32d2797e3..8616eeb59e7 100644 --- a/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.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 @@ -206,13 +206,19 @@ void RadialGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModel CSSPixelRect gradient_box { { 0, 0 }, paint_size }; auto center = m_properties.position->resolved(node, gradient_box); auto gradient_size = resolve_size(node, center, gradient_box); - if (m_resolved.has_value() && m_resolved->gradient_size == gradient_size) - return; - m_resolved = ResolvedData { - Painting::resolve_radial_gradient_data(node, gradient_size, *this), - gradient_size, - center, + + ResolvedDataCacheKey cache_key { + .length_resolution_context = Length::ResolutionContext::for_layout_node(node), + .size = paint_size, }; + if (m_resolved_data_cache_key != cache_key) { + m_resolved_data_cache_key = move(cache_key); + m_resolved = ResolvedData { + Painting::resolve_radial_gradient_data(node, gradient_size, *this), + gradient_size, + center, + }; + } } bool RadialGradientStyleValue::equals(CSSStyleValue const& other) const diff --git a/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h index 47a2fd3cffd..946e14b2bcd 100644 --- a/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.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 @@ -86,12 +86,18 @@ private: bool operator==(Properties const&) const = default; } m_properties; + struct ResolvedDataCacheKey { + Length::ResolutionContext length_resolution_context; + CSSPixelSize size; + bool operator==(ResolvedDataCacheKey const&) const = default; + }; + mutable Optional m_resolved_data_cache_key; + struct ResolvedData { Painting::RadialGradientData data; CSSPixelSize gradient_size; CSSPixelPoint center; }; - mutable Optional m_resolved; }; diff --git a/Tests/LibWeb/Ref/expected/css/gradient-em-positions-ref.html b/Tests/LibWeb/Ref/expected/css/gradient-em-positions-ref.html new file mode 100644 index 00000000000..12e0dd767cf --- /dev/null +++ b/Tests/LibWeb/Ref/expected/css/gradient-em-positions-ref.html @@ -0,0 +1,24 @@ + + +
+
+
+
\ No newline at end of file diff --git a/Tests/LibWeb/Ref/input/css/gradient-em-positions.html b/Tests/LibWeb/Ref/input/css/gradient-em-positions.html new file mode 100644 index 00000000000..a51f19dd794 --- /dev/null +++ b/Tests/LibWeb/Ref/input/css/gradient-em-positions.html @@ -0,0 +1,25 @@ + + + +
+
+
+
\ No newline at end of file