mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-31 13:19:05 +00:00
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.
This commit is contained in:
parent
3216da062a
commit
6a4a60cbd5
Notes:
github-actions[bot]
2025-02-28 12:52:25 +00:00
Author: https://github.com/AtkinsSJ
Commit: 6a4a60cbd5
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3644
9 changed files with 109 additions and 25 deletions
|
@ -79,6 +79,8 @@ public:
|
||||||
CSSPixels cap_height;
|
CSSPixels cap_height;
|
||||||
CSSPixels zero_advance;
|
CSSPixels zero_advance;
|
||||||
CSSPixels line_height;
|
CSSPixels line_height;
|
||||||
|
|
||||||
|
bool operator==(FontMetrics const&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Optional<Type> unit_from_name(StringView);
|
static Optional<Type> unit_from_name(StringView);
|
||||||
|
@ -159,12 +161,14 @@ public:
|
||||||
StringView unit_name() const;
|
StringView unit_name() const;
|
||||||
|
|
||||||
struct ResolutionContext {
|
struct ResolutionContext {
|
||||||
[[nodiscard]] static Length::ResolutionContext for_window(HTML::Window const&);
|
[[nodiscard]] static ResolutionContext for_window(HTML::Window const&);
|
||||||
[[nodiscard]] static Length::ResolutionContext for_layout_node(Layout::Node const&);
|
[[nodiscard]] static ResolutionContext for_layout_node(Layout::Node const&);
|
||||||
|
|
||||||
CSSPixelRect viewport_rect;
|
CSSPixelRect viewport_rect;
|
||||||
FontMetrics font_metrics;
|
FontMetrics font_metrics;
|
||||||
FontMetrics root_font_metrics;
|
FontMetrics root_font_metrics;
|
||||||
|
|
||||||
|
bool operator==(ResolutionContext const&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] CSSPixels to_px(ResolutionContext const&) const;
|
[[nodiscard]] CSSPixels to_px(ResolutionContext const&) const;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
|
||||||
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* 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
|
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 = ResolvedData { Painting::resolve_conic_gradient_data(node, *this), {} };
|
||||||
|
}
|
||||||
m_resolved->position = m_properties.position->resolved(node, CSSPixelRect { { 0, 0 }, size });
|
m_resolved->position = m_properties.position->resolved(node, CSSPixelRect { { 0, 0 }, size });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
|
||||||
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -60,11 +60,17 @@ private:
|
||||||
bool operator==(Properties const&) const = default;
|
bool operator==(Properties const&) const = default;
|
||||||
} m_properties;
|
} m_properties;
|
||||||
|
|
||||||
|
struct ResolvedDataCacheKey {
|
||||||
|
Length::ResolutionContext length_resolution_context;
|
||||||
|
CSSPixelSize size;
|
||||||
|
bool operator==(ResolvedDataCacheKey const&) const = default;
|
||||||
|
};
|
||||||
|
mutable Optional<ResolvedDataCacheKey> m_resolved_data_cache_key;
|
||||||
|
|
||||||
struct ResolvedData {
|
struct ResolvedData {
|
||||||
Painting::ConicGradientData data;
|
Painting::ConicGradientData data;
|
||||||
CSSPixelPoint position;
|
CSSPixelPoint position;
|
||||||
};
|
};
|
||||||
|
|
||||||
mutable Optional<ResolvedData> m_resolved;
|
mutable Optional<ResolvedData> m_resolved;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
|
||||||
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "LinearGradientStyleValue.h"
|
#include "LinearGradientStyleValue.h"
|
||||||
|
#include <LibWeb/Layout/Node.h>
|
||||||
#include <LibWeb/Painting/DisplayListRecorder.h>
|
#include <LibWeb/Painting/DisplayListRecorder.h>
|
||||||
|
|
||||||
namespace Web::CSS {
|
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
|
void LinearGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSSPixelSize size) const
|
||||||
{
|
{
|
||||||
if (m_resolved.has_value() && m_resolved->size == size)
|
ResolvedDataCacheKey cache_key {
|
||||||
return;
|
.length_resolution_context = Length::ResolutionContext::for_layout_node(node),
|
||||||
m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size };
|
.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
|
void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const
|
||||||
{
|
{
|
||||||
VERIFY(m_resolved.has_value());
|
VERIFY(m_resolved.has_value());
|
||||||
context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type<int>(), m_resolved->data);
|
context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type<int>(), m_resolved.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
|
||||||
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -77,12 +77,13 @@ private:
|
||||||
bool operator==(Properties const&) const = default;
|
bool operator==(Properties const&) const = default;
|
||||||
} m_properties;
|
} m_properties;
|
||||||
|
|
||||||
struct ResolvedData {
|
struct ResolvedDataCacheKey {
|
||||||
Painting::LinearGradientData data;
|
Length::ResolutionContext length_resolution_context;
|
||||||
CSSPixelSize size;
|
CSSPixelSize size;
|
||||||
|
bool operator==(ResolvedDataCacheKey const&) const = default;
|
||||||
};
|
};
|
||||||
|
mutable Optional<ResolvedDataCacheKey> m_resolved_data_cache_key;
|
||||||
mutable Optional<ResolvedData> m_resolved;
|
mutable Optional<Painting::LinearGradientData> m_resolved;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
|
||||||
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -206,13 +206,19 @@ void RadialGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModel
|
||||||
CSSPixelRect gradient_box { { 0, 0 }, paint_size };
|
CSSPixelRect gradient_box { { 0, 0 }, paint_size };
|
||||||
auto center = m_properties.position->resolved(node, gradient_box);
|
auto center = m_properties.position->resolved(node, gradient_box);
|
||||||
auto gradient_size = resolve_size(node, center, gradient_box);
|
auto gradient_size = resolve_size(node, center, gradient_box);
|
||||||
if (m_resolved.has_value() && m_resolved->gradient_size == gradient_size)
|
|
||||||
return;
|
ResolvedDataCacheKey cache_key {
|
||||||
m_resolved = ResolvedData {
|
.length_resolution_context = Length::ResolutionContext::for_layout_node(node),
|
||||||
Painting::resolve_radial_gradient_data(node, gradient_size, *this),
|
.size = paint_size,
|
||||||
gradient_size,
|
|
||||||
center,
|
|
||||||
};
|
};
|
||||||
|
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
|
bool RadialGradientStyleValue::equals(CSSStyleValue const& other) const
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
|
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
|
||||||
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -86,12 +86,18 @@ private:
|
||||||
bool operator==(Properties const&) const = default;
|
bool operator==(Properties const&) const = default;
|
||||||
} m_properties;
|
} m_properties;
|
||||||
|
|
||||||
|
struct ResolvedDataCacheKey {
|
||||||
|
Length::ResolutionContext length_resolution_context;
|
||||||
|
CSSPixelSize size;
|
||||||
|
bool operator==(ResolvedDataCacheKey const&) const = default;
|
||||||
|
};
|
||||||
|
mutable Optional<ResolvedDataCacheKey> m_resolved_data_cache_key;
|
||||||
|
|
||||||
struct ResolvedData {
|
struct ResolvedData {
|
||||||
Painting::RadialGradientData data;
|
Painting::RadialGradientData data;
|
||||||
CSSPixelSize gradient_size;
|
CSSPixelSize gradient_size;
|
||||||
CSSPixelPoint center;
|
CSSPixelPoint center;
|
||||||
};
|
};
|
||||||
|
|
||||||
mutable Optional<ResolvedData> m_resolved;
|
mutable Optional<ResolvedData> m_resolved;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
24
Tests/LibWeb/Ref/expected/css/gradient-em-positions-ref.html
Normal file
24
Tests/LibWeb/Ref/expected/css/gradient-em-positions-ref.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
width: 250px;
|
||||||
|
height: 250px;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
.linear-gradient.a {
|
||||||
|
background: linear-gradient(transparent, blue 10px);
|
||||||
|
}
|
||||||
|
.linear-gradient.b {
|
||||||
|
background: linear-gradient(transparent, blue 100px);
|
||||||
|
}
|
||||||
|
.radial-gradient.a {
|
||||||
|
background: radial-gradient(transparent, blue 10px);
|
||||||
|
}
|
||||||
|
.radial-gradient.b {
|
||||||
|
background: radial-gradient(transparent, blue 100px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="linear-gradient a"></div>
|
||||||
|
<div class="linear-gradient b"></div>
|
||||||
|
<div class="radial-gradient a"></div>
|
||||||
|
<div class="radial-gradient b"></div>
|
25
Tests/LibWeb/Ref/input/css/gradient-em-positions.html
Normal file
25
Tests/LibWeb/Ref/input/css/gradient-em-positions.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<link rel="match" href="../../expected/css/gradient-em-positions-ref.html" />
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
width: 250px;
|
||||||
|
height: 250px;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
.linear-gradient {
|
||||||
|
background: linear-gradient(transparent, blue 1em);
|
||||||
|
}
|
||||||
|
.radial-gradient {
|
||||||
|
background: radial-gradient(transparent, blue 1em);
|
||||||
|
}
|
||||||
|
.a {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
.b {
|
||||||
|
font-size: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="linear-gradient a"></div>
|
||||||
|
<div class="linear-gradient b"></div>
|
||||||
|
<div class="radial-gradient a"></div>
|
||||||
|
<div class="radial-gradient b"></div>
|
Loading…
Add table
Add a link
Reference in a new issue