LibWeb: Account for animated values when computing font

Computing the font for an element in `compute_font` is premature since
we are yet to apply animated properties - instead we should compute the
value on the fly (with a cache to avoid unnecessary work) to ensure we
are respecting the latest values
This commit is contained in:
Callum Law 2025-11-09 00:39:08 +13:00 committed by Sam Atkins
parent 6c236d04d8
commit dca80ad5eb
Notes: github-actions[bot] 2025-12-05 10:04:25 +00:00
12 changed files with 69 additions and 43 deletions

View file

@ -11,6 +11,7 @@
#include <LibGC/CellAllocator.h>
#include <LibWeb/CSS/Clip.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/FontComputer.h>
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.h>
@ -142,11 +143,19 @@ void ComputedProperties::set_property(PropertyID id, NonnullRefPtr<StyleValue co
set_property_inherited(id, inherited);
}
static bool property_affects_computed_font_list(PropertyID id)
{
return first_is_one_of(id, PropertyID::FontFamily, PropertyID::FontSize, PropertyID::FontStyle, PropertyID::FontWeight, PropertyID::FontWidth, PropertyID::FontVariationSettings);
}
void ComputedProperties::set_property_without_modifying_flags(PropertyID id, NonnullRefPtr<StyleValue const> value)
{
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
m_property_values[to_underlying(id) - to_underlying(first_longhand_property_id)] = move(value);
if (property_affects_computed_font_list(id))
clear_computed_font_list_cache();
}
void ComputedProperties::revert_property(PropertyID id, ComputedProperties const& style_for_revert)
@ -173,6 +182,9 @@ void ComputedProperties::set_animated_property(PropertyID id, NonnullRefPtr<Styl
m_animated_property_values.set(id, move(value));
set_animated_property_inherited(id, inherited);
set_animated_property_result_of_transition(id, animated_property_result_of_transition);
if (property_affects_computed_font_list(id))
clear_computed_font_list_cache();
}
void ComputedProperties::remove_animated_property(PropertyID id)
@ -198,7 +210,7 @@ StyleValue const& ComputedProperties::property(PropertyID property_id, WithAnima
return *animated_value.value();
}
// By the time we call this method, all properties have values assigned.
// By the time we call this method, the property should have been assigned
return *m_property_values[to_underlying(property_id) - to_underlying(first_longhand_property_id)];
}
@ -2543,6 +2555,27 @@ WillChange ComputedProperties::will_change() const
return WillChange(move(will_change_entries));
}
ValueComparingNonnullRefPtr<Gfx::FontCascadeList const> ComputedProperties::computed_font_list(FontComputer const& font_computer) const
{
if (!m_cached_computed_font_list) {
const_cast<ComputedProperties*>(this)->m_cached_computed_font_list = font_computer.compute_font_for_style_values(property(PropertyID::FontFamily), font_size(), font_slope(), font_weight(), font_width(), font_variation_settings());
VERIFY(!m_cached_computed_font_list->is_empty());
}
return *m_cached_computed_font_list;
}
ValueComparingNonnullRefPtr<Gfx::Font const> ComputedProperties::first_available_computed_font(FontComputer const& font_computer) const
{
if (!m_cached_first_available_computed_font) {
// https://drafts.csswg.org/css-fonts/#first-available-font
// First font for which the character U+0020 (space) is not excluded by a unicode-range
const_cast<ComputedProperties*>(this)->m_cached_first_available_computed_font = computed_font_list(font_computer)->font_for_code_point(' ');
}
return *m_cached_first_available_computed_font;
}
CSSPixels ComputedProperties::font_size() const
{
return property(PropertyID::FontSize).as_length().length().absolute_length_to_px();

View file

@ -234,25 +234,9 @@ public:
WillChange will_change() const;
Gfx::FontCascadeList const& computed_font_list() const
{
VERIFY(m_font_list);
return *m_font_list;
}
Gfx::Font const& first_available_computed_font() const
{
VERIFY(m_first_available_computed_font);
return *m_first_available_computed_font;
}
void set_computed_font_list(NonnullRefPtr<Gfx::FontCascadeList const> font_list)
{
m_font_list = move(font_list);
// https://drafts.csswg.org/css-fonts/#first-available-font
// First font for which the character U+0020 (space) is not excluded by a unicode-range
m_first_available_computed_font = m_font_list->font_for_code_point(' ');
}
ValueComparingRefPtr<Gfx::FontCascadeList const> cached_computed_font_list() const { return m_cached_computed_font_list; }
ValueComparingNonnullRefPtr<Gfx::FontCascadeList const> computed_font_list(FontComputer const&) const;
ValueComparingNonnullRefPtr<Gfx::Font const> first_available_computed_font(FontComputer const&) const;
[[nodiscard]] CSSPixels line_height() const;
[[nodiscard]] CSSPixels font_size() const;
@ -306,8 +290,14 @@ private:
Display m_display_before_box_type_transformation { InitialValues::display() };
int m_math_depth { InitialValues::math_depth() };
RefPtr<Gfx::FontCascadeList const> m_font_list;
RefPtr<Gfx::Font const> m_first_available_computed_font;
RefPtr<Gfx::FontCascadeList const> m_cached_computed_font_list;
RefPtr<Gfx::Font const> m_cached_first_available_computed_font;
void clear_computed_font_list_cache()
{
m_cached_computed_font_list = nullptr;
m_cached_first_available_computed_font = nullptr;
}
Optional<CSSPixels> m_line_height;

View file

@ -354,7 +354,7 @@ RefPtr<Gfx::FontCascadeList const> FontComputer::font_matching_algorithm_impl(Fl
return {};
}
RefPtr<Gfx::FontCascadeList const> FontComputer::compute_font_for_style_values(StyleValue const& font_family, CSSPixels const& font_size, int slope, double font_weight, Percentage const& font_width, HashMap<FlyString, double> const& font_variation_settings) const
NonnullRefPtr<Gfx::FontCascadeList const> FontComputer::compute_font_for_style_values(StyleValue const& font_family, CSSPixels const& font_size, int slope, double font_weight, Percentage const& font_width, HashMap<FlyString, double> const& font_variation_settings) const
{
// FIXME: We round to int here as that is what is expected by our font infrastructure below
auto width = round_to<int>(font_width.value());

View file

@ -100,7 +100,7 @@ public:
void load_fonts_from_sheet(CSSStyleSheet&);
void unload_fonts_from_sheet(CSSStyleSheet&);
RefPtr<Gfx::FontCascadeList const> compute_font_for_style_values(StyleValue const& font_family, CSSPixels const& font_size, int font_slope, double font_weight, Percentage const& font_width, HashMap<FlyString, double> const& font_variation_settings) const;
NonnullRefPtr<Gfx::FontCascadeList const> compute_font_for_style_values(StyleValue const& font_family, CSSPixels const& font_size, int font_slope, double font_weight, Percentage const& font_width, HashMap<FlyString, double> const& font_variation_settings) const;
size_t number_of_css_font_faces_with_loading_in_progress() const;

View file

@ -132,8 +132,8 @@ Length::ResolutionContext Length::ResolutionContext::for_element(DOM::AbstractEl
return Length::ResolutionContext {
.viewport_rect = element.element().navigable()->viewport_rect(),
.font_metrics = { element.computed_properties()->font_size(), element.computed_properties()->first_available_computed_font().pixel_metrics(), element.computed_properties()->line_height() },
.root_font_metrics = { root_element->computed_properties()->font_size(), root_element->computed_properties()->first_available_computed_font().pixel_metrics(), element.computed_properties()->line_height() }
.font_metrics = { element.computed_properties()->font_size(), element.computed_properties()->first_available_computed_font(element.document().font_computer())->pixel_metrics(), element.computed_properties()->line_height() },
.root_font_metrics = { root_element->computed_properties()->font_size(), root_element->computed_properties()->first_available_computed_font(element.document().font_computer())->pixel_metrics(), element.computed_properties()->line_height() }
};
}

View file

@ -680,7 +680,7 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
Length::FontMetrics font_metrics {
computed_properties.font_size(),
computed_properties.first_available_computed_font().pixel_metrics(),
computed_properties.first_available_computed_font(document().font_computer())->pixel_metrics(),
computed_properties.line_height()
};
@ -794,7 +794,7 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
.viewport_rect = viewport_rect(),
.font_metrics = {
computed_properties.font_size(),
computed_properties.first_available_computed_font().pixel_metrics(),
computed_properties.first_available_computed_font(document().font_computer())->pixel_metrics(),
inheritance_parent_has_computed_properties ? inheritance_parent->computed_properties()->line_height() : InitialValues::line_height() },
.root_font_metrics = m_root_element_font_metrics },
.abstract_element = abstract_element
@ -1417,7 +1417,7 @@ Length::FontMetrics StyleComputer::calculate_root_element_font_metrics(ComputedP
{
auto const& root_value = style.property(CSS::PropertyID::FontSize);
auto font_pixel_metrics = style.first_available_computed_font().pixel_metrics();
auto font_pixel_metrics = style.first_available_computed_font(document().font_computer())->pixel_metrics();
Length::FontMetrics font_metrics { m_default_font_metrics.font_size, font_pixel_metrics, InitialValues::line_height() };
font_metrics.font_size = root_value.as_length().length().to_px(viewport_rect(), font_metrics, font_metrics);
font_metrics.line_height = style.line_height();
@ -1527,15 +1527,7 @@ void StyleComputer::compute_font(ComputedProperties& style, Optional<DOM::Abstra
PropertyID::FontVariationSettings,
compute_font_variation_settings(font_variation_settings_value, font_computation_context));
auto const& font_family = style.property(CSS::PropertyID::FontFamily);
auto font_list = document().font_computer().compute_font_for_style_values(font_family, style.font_size(), style.font_slope(), style.font_weight(), style.font_width(), style.font_variation_settings());
VERIFY(font_list);
VERIFY(!font_list->is_empty());
RefPtr<Gfx::Font const> const found_font = font_list->first();
style.set_computed_font_list(*font_list);
RefPtr<Gfx::Font const> const found_font = style.first_available_computed_font(m_document->font_computer());
Length::FontMetrics line_height_font_metrics {
style.font_size(),
@ -1606,7 +1598,7 @@ void StyleComputer::compute_property_values(ComputedProperties& style, Optional<
{
Length::FontMetrics font_metrics {
style.font_size(),
style.first_available_computed_font().pixel_metrics(),
style.first_available_computed_font(document().font_computer())->pixel_metrics(),
style.line_height()
};