mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 13:50:00 +00:00
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:
parent
6c236d04d8
commit
dca80ad5eb
Notes:
github-actions[bot]
2025-12-05 10:04:25 +00:00
Author: https://github.com/Calme1709
Commit: dca80ad5eb
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6757
Reviewed-by: https://github.com/AtkinsSJ ✅
Reviewed-by: https://github.com/gmta
12 changed files with 69 additions and 43 deletions
|
|
@ -11,6 +11,7 @@
|
||||||
#include <LibGC/CellAllocator.h>
|
#include <LibGC/CellAllocator.h>
|
||||||
#include <LibWeb/CSS/Clip.h>
|
#include <LibWeb/CSS/Clip.h>
|
||||||
#include <LibWeb/CSS/ComputedProperties.h>
|
#include <LibWeb/CSS/ComputedProperties.h>
|
||||||
|
#include <LibWeb/CSS/FontComputer.h>
|
||||||
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ColorSchemeStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/CounterDefinitionsStyleValue.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);
|
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)
|
void ComputedProperties::set_property_without_modifying_flags(PropertyID id, NonnullRefPtr<StyleValue const> value)
|
||||||
{
|
{
|
||||||
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
|
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);
|
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)
|
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));
|
m_animated_property_values.set(id, move(value));
|
||||||
set_animated_property_inherited(id, inherited);
|
set_animated_property_inherited(id, inherited);
|
||||||
set_animated_property_result_of_transition(id, animated_property_result_of_transition);
|
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)
|
void ComputedProperties::remove_animated_property(PropertyID id)
|
||||||
|
|
@ -198,7 +210,7 @@ StyleValue const& ComputedProperties::property(PropertyID property_id, WithAnima
|
||||||
return *animated_value.value();
|
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)];
|
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));
|
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
|
CSSPixels ComputedProperties::font_size() const
|
||||||
{
|
{
|
||||||
return property(PropertyID::FontSize).as_length().length().absolute_length_to_px();
|
return property(PropertyID::FontSize).as_length().length().absolute_length_to_px();
|
||||||
|
|
|
||||||
|
|
@ -234,25 +234,9 @@ public:
|
||||||
|
|
||||||
WillChange will_change() const;
|
WillChange will_change() const;
|
||||||
|
|
||||||
Gfx::FontCascadeList const& computed_font_list() const
|
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;
|
||||||
VERIFY(m_font_list);
|
ValueComparingNonnullRefPtr<Gfx::Font const> first_available_computed_font(FontComputer const&) const;
|
||||||
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(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] CSSPixels line_height() const;
|
[[nodiscard]] CSSPixels line_height() const;
|
||||||
[[nodiscard]] CSSPixels font_size() const;
|
[[nodiscard]] CSSPixels font_size() const;
|
||||||
|
|
@ -306,8 +290,14 @@ private:
|
||||||
Display m_display_before_box_type_transformation { InitialValues::display() };
|
Display m_display_before_box_type_transformation { InitialValues::display() };
|
||||||
|
|
||||||
int m_math_depth { InitialValues::math_depth() };
|
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;
|
Optional<CSSPixels> m_line_height;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@ RefPtr<Gfx::FontCascadeList const> FontComputer::font_matching_algorithm_impl(Fl
|
||||||
return {};
|
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
|
// 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());
|
auto width = round_to<int>(font_width.value());
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ public:
|
||||||
void load_fonts_from_sheet(CSSStyleSheet&);
|
void load_fonts_from_sheet(CSSStyleSheet&);
|
||||||
void unload_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;
|
size_t number_of_css_font_faces_with_loading_in_progress() const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,8 +132,8 @@ Length::ResolutionContext Length::ResolutionContext::for_element(DOM::AbstractEl
|
||||||
|
|
||||||
return Length::ResolutionContext {
|
return Length::ResolutionContext {
|
||||||
.viewport_rect = element.element().navigable()->viewport_rect(),
|
.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() },
|
.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().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() }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -680,7 +680,7 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
|
||||||
|
|
||||||
Length::FontMetrics font_metrics {
|
Length::FontMetrics font_metrics {
|
||||||
computed_properties.font_size(),
|
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()
|
computed_properties.line_height()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -794,7 +794,7 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
|
||||||
.viewport_rect = viewport_rect(),
|
.viewport_rect = viewport_rect(),
|
||||||
.font_metrics = {
|
.font_metrics = {
|
||||||
computed_properties.font_size(),
|
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() },
|
inheritance_parent_has_computed_properties ? inheritance_parent->computed_properties()->line_height() : InitialValues::line_height() },
|
||||||
.root_font_metrics = m_root_element_font_metrics },
|
.root_font_metrics = m_root_element_font_metrics },
|
||||||
.abstract_element = abstract_element
|
.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 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() };
|
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.font_size = root_value.as_length().length().to_px(viewport_rect(), font_metrics, font_metrics);
|
||||||
font_metrics.line_height = style.line_height();
|
font_metrics.line_height = style.line_height();
|
||||||
|
|
@ -1527,15 +1527,7 @@ void StyleComputer::compute_font(ComputedProperties& style, Optional<DOM::Abstra
|
||||||
PropertyID::FontVariationSettings,
|
PropertyID::FontVariationSettings,
|
||||||
compute_font_variation_settings(font_variation_settings_value, font_computation_context));
|
compute_font_variation_settings(font_variation_settings_value, font_computation_context));
|
||||||
|
|
||||||
auto const& font_family = style.property(CSS::PropertyID::FontFamily);
|
RefPtr<Gfx::Font const> const found_font = style.first_available_computed_font(m_document->font_computer());
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Length::FontMetrics line_height_font_metrics {
|
Length::FontMetrics line_height_font_metrics {
|
||||||
style.font_size(),
|
style.font_size(),
|
||||||
|
|
@ -1606,7 +1598,7 @@ void StyleComputer::compute_property_values(ComputedProperties& style, Optional<
|
||||||
{
|
{
|
||||||
Length::FontMetrics font_metrics {
|
Length::FontMetrics font_metrics {
|
||||||
style.font_size(),
|
style.font_size(),
|
||||||
style.first_available_computed_font().pixel_metrics(),
|
style.first_available_computed_font(document().font_computer())->pixel_metrics(),
|
||||||
style.line_height()
|
style.line_height()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -696,7 +696,7 @@ static CSS::RequiredInvalidationAfterStyleChange compute_required_invalidation(C
|
||||||
{
|
{
|
||||||
CSS::RequiredInvalidationAfterStyleChange invalidation;
|
CSS::RequiredInvalidationAfterStyleChange invalidation;
|
||||||
|
|
||||||
if (!old_style.computed_font_list().equals(new_style.computed_font_list()))
|
if (old_style.cached_computed_font_list() != new_style.cached_computed_font_list())
|
||||||
invalidation.relayout = true;
|
invalidation.relayout = true;
|
||||||
|
|
||||||
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
|
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ void HTMLInputElement::adjust_computed_style(CSS::ComputedProperties& style)
|
||||||
// NOTE: Other browsers apply a minimum height of a single line's line-height to single-line input elements.
|
// NOTE: Other browsers apply a minimum height of a single line's line-height to single-line input elements.
|
||||||
if (is_single_line() && style.property(CSS::PropertyID::Height).has_auto()) {
|
if (is_single_line() && style.property(CSS::PropertyID::Height).has_auto()) {
|
||||||
auto current_line_height = style.line_height().to_double();
|
auto current_line_height = style.line_height().to_double();
|
||||||
auto minimum_line_height = style.first_available_computed_font().pixel_size() * CSS::ComputedProperties::normal_line_height_scale;
|
auto minimum_line_height = style.first_available_computed_font(document().font_computer())->pixel_size() * CSS::ComputedProperties::normal_line_height_scale;
|
||||||
|
|
||||||
// FIXME: Instead of overriding line-height, we should set height here instead.
|
// FIXME: Instead of overriding line-height, we should set height here instead.
|
||||||
if (current_line_height < minimum_line_height)
|
if (current_line_height < minimum_line_height)
|
||||||
|
|
|
||||||
|
|
@ -397,7 +397,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
|
||||||
// NOTE: We have to be careful that font-related properties get set in the right order.
|
// NOTE: We have to be careful that font-related properties get set in the right order.
|
||||||
// m_font is used by Length::to_px() when resolving sizes against this layout node.
|
// m_font is used by Length::to_px() when resolving sizes against this layout node.
|
||||||
// That's why it has to be set before everything else.
|
// That's why it has to be set before everything else.
|
||||||
computed_values.set_font_list(computed_style.computed_font_list());
|
computed_values.set_font_list(computed_style.computed_font_list(document().font_computer()));
|
||||||
computed_values.set_font_size(computed_style.font_size());
|
computed_values.set_font_size(computed_style.font_size());
|
||||||
computed_values.set_font_weight(computed_style.font_weight());
|
computed_values.set_font_weight(computed_style.font_weight());
|
||||||
computed_values.set_line_height(computed_style.line_height());
|
computed_values.set_line_height(computed_style.line_height());
|
||||||
|
|
|
||||||
|
|
@ -540,7 +540,7 @@ void ConnectionFromClient::inspect_dom_node(u64 page_id, WebView::DOMNodePropert
|
||||||
auto serialize_used_fonts = [&]() {
|
auto serialize_used_fonts = [&]() {
|
||||||
JsonArray serialized;
|
JsonArray serialized;
|
||||||
|
|
||||||
properties->computed_font_list().for_each_font_entry([&](Gfx::FontCascadeList::Entry const& entry) {
|
properties->computed_font_list(node->document().font_computer())->for_each_font_entry([&](Gfx::FontCascadeList::Entry const& entry) {
|
||||||
auto const& font = *entry.font;
|
auto const& font = *entry.font;
|
||||||
|
|
||||||
JsonObject font_object;
|
JsonObject font_object;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<div style="font-size: 15px">TEST</div>
|
||||||
9
Tests/LibWeb/Ref/input/css/animated-font-size.html
Normal file
9
Tests/LibWeb/Ref/input/css/animated-font-size.html
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<link rel="match" href="../../expected/css/animated-font-size-ref.html" />
|
||||||
|
<div id="foo" style="font-size: 10px">TEST</div>
|
||||||
|
<script>
|
||||||
|
const anim = foo.animate([{ fontSize: "10px" }, { fontSize: "20px" }], 1000);
|
||||||
|
anim.pause();
|
||||||
|
anim.currentTime = 500;
|
||||||
|
document.documentElement.classList = "";
|
||||||
|
</script>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue