LibWeb: Cache parsed font descriptors on FontFace objects

This commit is contained in:
Tim Ledbetter
2026-04-01 16:31:04 +01:00
committed by Andreas Kling
parent 9526dfbef3
commit 333df3b399
Notes: github-actions[bot] 2026-04-04 22:14:40 +00:00
2 changed files with 64 additions and 23 deletions

View File

@@ -22,8 +22,14 @@
#include <LibWeb/CSS/FontFace.h>
#include <LibWeb/CSS/FontFaceSet.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/StyleComputer.h>
#include <LibWeb/CSS/StyleValues/ComputationContext.h>
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
#include <LibWeb/CSS/StyleValues/FontStyleStyleValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
#include <LibWeb/CSS/StyleValues/StringStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
#include <LibWeb/CSS/StyleValues/UnicodeRangeStyleValue.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/Window.h>
@@ -46,6 +52,32 @@ static bool is_unsupported_source(ParsedFontFace::Source const& source)
return source.local_or_url.get<URL>().url().ends_with_bytes(".eot"sv);
}
static FontWeightRange compute_weight_range(StyleValue const& value)
{
if (value.to_keyword() == Keyword::Auto || value.to_keyword() == Keyword::Normal)
return { 400, 400 };
auto& weight_values = value.as_value_list().values();
if (weight_values.size() == 1) {
auto one_weight = static_cast<int>(StyleComputer::compute_font_weight(weight_values[0], {})->as_number().number());
return { one_weight, one_weight };
}
if (weight_values.size() == 2) {
auto first = static_cast<int>(StyleComputer::compute_font_weight(weight_values[0], {})->as_number().number());
auto second = static_cast<int>(StyleComputer::compute_font_weight(weight_values[1], {})->as_number().number());
return { min(first, second), max(first, second) };
}
return { 400, 400 };
}
static int compute_slope(StyleValue const& value)
{
if (value.to_keyword() == Keyword::Auto || value.to_keyword() == Keyword::Normal)
return 0;
return StyleComputer::compute_font_style(value)->as_font_style().to_font_slope();
}
static NonnullRefPtr<Core::Promise<NonnullRefPtr<Gfx::Typeface const>>> load_vector_font(JS::Realm& realm, ByteBuffer const& data)
{
auto promise = Core::Promise<NonnullRefPtr<Gfx::Typeface const>>::construct();
@@ -95,29 +127,25 @@ GC::Ref<FontFace> FontFace::construct_impl(JS::Realm& realm, String family, Font
// Otherwise, set font faces corresponding attributes to the serialization of the parsed values.
Parser::ParsingParams parsing_params { realm };
auto try_parse_descriptor = [&parsing_params, &font_face, &realm](DescriptorID descriptor_id, String const& string) -> String {
auto try_set_descriptor = [&](DescriptorID descriptor_id, String const& string, auto setter_impl) {
auto result = parse_css_descriptor(parsing_params, AtRuleID::FontFace, DescriptorNameAndID::from_id(descriptor_id), string);
if (!result) {
font_face->reject_status_promise(WebIDL::SyntaxError::create(realm, Utf16String::formatted("FontFace constructor: Invalid {}", to_string(descriptor_id))));
return {};
return;
}
if (result->is_custom_ident())
return result->as_custom_ident().custom_ident().to_string();
return result->to_string(SerializationMode::Normal);
(font_face.ptr()->*setter_impl)(result.release_nonnull());
};
font_face->m_family = try_parse_descriptor(DescriptorID::FontFamily, family);
font_face->m_style = try_parse_descriptor(DescriptorID::FontStyle, descriptors.style);
font_face->m_weight = try_parse_descriptor(DescriptorID::FontWeight, descriptors.weight);
font_face->m_stretch = try_parse_descriptor(DescriptorID::FontWidth, descriptors.stretch);
font_face->m_unicode_range = try_parse_descriptor(DescriptorID::UnicodeRange, descriptors.unicode_range);
font_face->m_feature_settings = try_parse_descriptor(DescriptorID::FontFeatureSettings, descriptors.feature_settings);
font_face->m_variation_settings = try_parse_descriptor(DescriptorID::FontVariationSettings, descriptors.variation_settings);
font_face->m_display = try_parse_descriptor(DescriptorID::FontDisplay, descriptors.display);
font_face->m_ascent_override = try_parse_descriptor(DescriptorID::AscentOverride, descriptors.ascent_override);
font_face->m_descent_override = try_parse_descriptor(DescriptorID::DescentOverride, descriptors.descent_override);
font_face->m_line_gap_override = try_parse_descriptor(DescriptorID::LineGapOverride, descriptors.line_gap_override);
try_set_descriptor(DescriptorID::FontFamily, family, &FontFace::set_family_impl);
try_set_descriptor(DescriptorID::FontStyle, descriptors.style, &FontFace::set_style_impl);
try_set_descriptor(DescriptorID::FontWeight, descriptors.weight, &FontFace::set_weight_impl);
try_set_descriptor(DescriptorID::FontWidth, descriptors.stretch, &FontFace::set_stretch_impl);
try_set_descriptor(DescriptorID::UnicodeRange, descriptors.unicode_range, &FontFace::set_unicode_range_impl);
try_set_descriptor(DescriptorID::FontFeatureSettings, descriptors.feature_settings, &FontFace::set_feature_settings_impl);
try_set_descriptor(DescriptorID::FontVariationSettings, descriptors.variation_settings, &FontFace::set_variation_settings_impl);
try_set_descriptor(DescriptorID::FontDisplay, descriptors.display, &FontFace::set_display_impl);
try_set_descriptor(DescriptorID::AscentOverride, descriptors.ascent_override, &FontFace::set_ascent_override_impl);
try_set_descriptor(DescriptorID::DescentOverride, descriptors.descent_override, &FontFace::set_descent_override_impl);
try_set_descriptor(DescriptorID::LineGapOverride, descriptors.line_gap_override, &FontFace::set_line_gap_override_impl);
RefPtr<StyleValue const> parsed_source;
if (auto* source_string = source.get_pointer<String>()) {
parsed_source = parse_css_descriptor(parsing_params, AtRuleID::FontFace, DescriptorNameAndID::from_id(DescriptorID::Src), *source_string);
@@ -252,9 +280,13 @@ void FontFace::reparse_connected_css_font_face_rule_descriptors()
{
auto const& descriptors = m_css_font_face_rule->descriptors();
ComputationContext computation_context {
.length_resolution_context = Length::ResolutionContext::for_document(*descriptors->parent_rule()->parent_style_sheet()->owning_document())
};
set_family_impl(*descriptors->descriptor(DescriptorNameAndID::from_id(DescriptorID::FontFamily)));
set_style_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontStyle)));
set_weight_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontWeight)));
set_style_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontStyle))->absolutized(computation_context));
set_weight_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontWeight))->absolutized(computation_context));
set_stretch_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontWidth)));
set_unicode_range_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::UnicodeRange)));
set_feature_settings_impl(*descriptors->descriptor_or_initial_value(DescriptorNameAndID::from_id(DescriptorID::FontFeatureSettings)));
@@ -275,9 +307,8 @@ ParsedFontFace FontFace::parsed_font_face() const
// Create a dummy CSSFontFaceRule so that we load relative to the document's base URL
CSSFontFaceRule::create(realm(), CSSFontFaceDescriptors::create(realm(), {})),
m_family,
// FIXME: Actually parse this as we're supposed to.
m_weight.to_number<int>().map([](auto weight) { return FontWeightRange { weight, weight }; }),
0, // FIXME: slope
m_cached_weight_range,
m_cached_slope,
Gfx::FontWidth::Normal, // FIXME: width
m_urls,
m_unicode_ranges,
@@ -381,6 +412,7 @@ WebIDL::ExceptionOr<void> FontFace::set_style(String const& string)
void FontFace::set_style_impl(NonnullRefPtr<StyleValue const> const& value)
{
m_style = value->to_string(SerializationMode::Normal);
m_cached_slope = compute_slope(*value);
}
// https://drafts.csswg.org/css-font-loading/#dom-fontface-weight
@@ -405,6 +437,7 @@ WebIDL::ExceptionOr<void> FontFace::set_weight(String const& string)
void FontFace::set_weight_impl(NonnullRefPtr<StyleValue const> const& value)
{
m_weight = value->to_string(SerializationMode::Normal);
m_cached_weight_range = compute_weight_range(*value);
}
// https://drafts.csswg.org/css-font-loading/#dom-fontface-stretch
@@ -454,6 +487,11 @@ WebIDL::ExceptionOr<void> FontFace::set_unicode_range(String const& string)
void FontFace::set_unicode_range_impl(NonnullRefPtr<StyleValue const> const& value)
{
m_unicode_range = value->to_string(SerializationMode::Normal);
auto const& ranges = value->as_value_list().values();
m_unicode_ranges.clear_with_capacity();
m_unicode_ranges.ensure_capacity(ranges.size());
for (auto const& range : ranges)
m_unicode_ranges.unchecked_append(range->as_unicode_range().unicode_range());
}
// https://drafts.csswg.org/css-font-loading/#dom-fontface-featuresettings