Skip to content

Commit 15c61b9

Browse files
committed
LibWeb: Separate font computation logic from StyleComputer
Font computation and loading is distinct enough from style computation that it makes more sense to have this in it's own class. This will be useful later when we move the font loading process to `ComputedProperties` in order to respect animated values.
1 parent cb8c91c commit 15c61b9

File tree

14 files changed

+735
-657
lines changed

14 files changed

+735
-657
lines changed

Libraries/LibWeb/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ set(SOURCES
162162
CSS/EdgeRect.cpp
163163
CSS/Fetch.cpp
164164
CSS/Flex.cpp
165+
CSS/FontComputer.cpp
165166
CSS/FontFace.cpp
166167
CSS/FontFaceSet.cpp
167168
CSS/Frequency.cpp

Libraries/LibWeb/CSS/CSSStyleSheet.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <LibWeb/Bindings/Intrinsics.h>
1212
#include <LibWeb/CSS/CSSImportRule.h>
1313
#include <LibWeb/CSS/CSSStyleSheet.h>
14+
#include <LibWeb/CSS/FontComputer.h>
1415
#include <LibWeb/CSS/Parser/Parser.h>
1516
#include <LibWeb/CSS/StyleComputer.h>
1617
#include <LibWeb/CSS/StyleSheetList.h>
@@ -325,7 +326,7 @@ void CSSStyleSheet::add_owning_document_or_shadow_root(DOM::Node& document_or_sh
325326
// All owning documents or shadow roots must be part of the same document so we only need to load this style
326327
// sheet's fonts against the document of the first
327328
if (this->owning_documents_or_shadow_roots().size() == 1)
328-
document_or_shadow_root.document().style_computer().load_fonts_from_sheet(*this);
329+
document_or_shadow_root.document().font_computer().load_fonts_from_sheet(*this);
329330

330331
for (auto const& import_rule : m_import_rules) {
331332
if (import_rule->loaded_style_sheet())
@@ -340,7 +341,7 @@ void CSSStyleSheet::remove_owning_document_or_shadow_root(DOM::Node& document_or
340341
// All owning documents or shadow roots must be part of the same document so we only need to unload this style
341342
// sheet's fonts once we have none remaining.
342343
if (this->owning_documents_or_shadow_roots().size() == 0)
343-
document_or_shadow_root.document().style_computer().unload_fonts_from_sheet(*this);
344+
document_or_shadow_root.document().font_computer().unload_fonts_from_sheet(*this);
344345

345346
for (auto const& import_rule : m_import_rules) {
346347
if (import_rule->loaded_style_sheet())

Libraries/LibWeb/CSS/FontComputer.cpp

Lines changed: 584 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright (c) 2018-2025, Andreas Kling <[email protected]>
3+
* Copyright (c) 2021, the SerenityOS developers.
4+
* Copyright (c) 2021-2025, Sam Atkins <[email protected]>
5+
* Copyright (c) 2024, Matthew Olsson <[email protected]>
6+
* Copyright (c) 2025, Callum Law <[email protected]>
7+
*
8+
* SPDX-License-Identifier: BSD-2-Clause
9+
*/
10+
11+
#include <LibGC/CellAllocator.h>
12+
#include <LibGfx/FontCascadeList.h>
13+
#include <LibWeb/Export.h>
14+
#include <LibWeb/Forward.h>
15+
16+
#pragma once
17+
18+
namespace Web::CSS {
19+
20+
struct FontFaceKey;
21+
22+
struct OwnFontFaceKey {
23+
explicit OwnFontFaceKey(FontFaceKey const& other);
24+
25+
operator FontFaceKey() const;
26+
27+
[[nodiscard]] u32 hash() const { return pair_int_hash(family_name.hash(), pair_int_hash(weight, slope)); }
28+
[[nodiscard]] bool operator==(OwnFontFaceKey const& other) const = default;
29+
[[nodiscard]] bool operator==(FontFaceKey const& other) const;
30+
31+
FlyString family_name;
32+
int weight { 0 };
33+
int slope { 0 };
34+
};
35+
36+
struct FontMatchingAlgorithmCacheKey {
37+
FlyString family_name;
38+
int weight;
39+
int slope;
40+
float font_size_in_pt;
41+
42+
[[nodiscard]] bool operator==(FontMatchingAlgorithmCacheKey const& other) const = default;
43+
};
44+
45+
class FontLoader final : public GC::Cell {
46+
GC_CELL(FontLoader, GC::Cell);
47+
GC_DECLARE_ALLOCATOR(FontLoader);
48+
49+
public:
50+
FontLoader(FontComputer& font_computer, GC::Ptr<CSSStyleSheet> parent_style_sheet, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<URL> urls, ESCAPING Function<void(RefPtr<Gfx::Typeface const>)> on_load = {});
51+
52+
virtual ~FontLoader();
53+
54+
Vector<Gfx::UnicodeRange> const& unicode_ranges() const { return m_unicode_ranges; }
55+
RefPtr<Gfx::Typeface const> vector_font() const { return m_vector_font; }
56+
57+
RefPtr<Gfx::Font const> font_with_point_size(float point_size, Gfx::FontVariationSettings const& variations = {});
58+
void start_loading_next_url();
59+
60+
bool is_loading() const;
61+
62+
private:
63+
virtual void visit_edges(Visitor&) override;
64+
65+
ErrorOr<NonnullRefPtr<Gfx::Typeface const>> try_load_font(Fetch::Infrastructure::Response const&, ByteBuffer const&);
66+
67+
void font_did_load_or_fail(RefPtr<Gfx::Typeface const>);
68+
69+
GC::Ref<FontComputer> m_font_computer;
70+
GC::Ptr<CSSStyleSheet> m_parent_style_sheet;
71+
FlyString m_family_name;
72+
Vector<Gfx::UnicodeRange> m_unicode_ranges;
73+
RefPtr<Gfx::Typeface const> m_vector_font;
74+
Vector<URL> m_urls;
75+
GC::Ptr<Fetch::Infrastructure::FetchController> m_fetch_controller;
76+
Function<void(RefPtr<Gfx::Typeface const>)> m_on_load;
77+
};
78+
79+
class WEB_API FontComputer final : public GC::Cell {
80+
GC_CELL(FontComputer, GC::Cell);
81+
GC_DECLARE_ALLOCATOR(FontComputer);
82+
83+
public:
84+
explicit FontComputer(DOM::Document& document)
85+
: m_document(document)
86+
{
87+
}
88+
89+
~FontComputer() = default;
90+
91+
DOM::Document& document() { return m_document; }
92+
DOM::Document const& document() const { return m_document; }
93+
94+
Gfx::Font const& initial_font() const;
95+
96+
void did_load_font(FlyString const& family_name);
97+
98+
GC::Ptr<FontLoader> load_font_face(ParsedFontFace const&, ESCAPING Function<void(RefPtr<Gfx::Typeface const>)> on_load = {});
99+
100+
void load_fonts_from_sheet(CSSStyleSheet&);
101+
void unload_fonts_from_sheet(CSSStyleSheet&);
102+
103+
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;
104+
105+
size_t number_of_css_font_faces_with_loading_in_progress() const;
106+
107+
private:
108+
virtual void visit_edges(Visitor&) override;
109+
110+
struct MatchingFontCandidate;
111+
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, Gfx::FontVariationSettings const& variations, bool inclusive);
112+
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, Gfx::FontVariationSettings const& variations, bool inclusive);
113+
RefPtr<Gfx::FontCascadeList const> font_matching_algorithm(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const;
114+
RefPtr<Gfx::FontCascadeList const> font_matching_algorithm_impl(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const;
115+
116+
GC::Ref<DOM::Document> m_document;
117+
118+
using FontLoaderList = Vector<GC::Ref<FontLoader>>;
119+
HashMap<OwnFontFaceKey, FontLoaderList> m_loaded_fonts;
120+
121+
mutable HashMap<FontMatchingAlgorithmCacheKey, RefPtr<Gfx::FontCascadeList const>> m_font_matching_algorithm_cache;
122+
};
123+
124+
}

Libraries/LibWeb/CSS/FontFace.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
#include <LibJS/Runtime/Realm.h>
1717
#include <LibWeb/Bindings/FontFacePrototype.h>
1818
#include <LibWeb/Bindings/Intrinsics.h>
19+
#include <LibWeb/CSS/FontComputer.h>
1920
#include <LibWeb/CSS/FontFace.h>
2021
#include <LibWeb/CSS/Parser/Parser.h>
21-
#include <LibWeb/CSS/StyleComputer.h>
2222
#include <LibWeb/CSS/StyleValues/CustomIdentStyleValue.h>
2323
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
2424
#include <LibWeb/HTML/Window.h>
@@ -483,7 +483,7 @@ GC::Ref<WebIDL::Promise> FontFace::load()
483483
// FIXME: We should probably put the 'font cache' on the WindowOrWorkerGlobalScope instead of tying it to the document's style computer
484484
auto& global = HTML::relevant_global_object(*font);
485485
if (auto* window = as_if<HTML::Window>(global)) {
486-
auto& style_computer = const_cast<StyleComputer&>(window->document()->style_computer());
486+
auto& font_computer = const_cast<FontComputer&>(window->document()->font_computer());
487487

488488
// FIXME: The ParsedFontFace is kind of expensive to create. We should be using a shared sub-object for the data
489489
ParsedFontFace parsed_font_face {
@@ -503,7 +503,7 @@ GC::Ref<WebIDL::Promise> FontFace::load()
503503
{}, // FIXME: feature_settings
504504
{}, // FIXME: variation_settings
505505
};
506-
if (auto loader = style_computer.load_font_face(parsed_font_face, move(on_load)))
506+
if (auto loader = font_computer.load_font_face(parsed_font_face, move(on_load)))
507507
loader->start_loading_next_url();
508508
} else {
509509
// FIXME: Don't know how to load fonts in workers! They don't have a StyleComputer

Libraries/LibWeb/CSS/Length.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
#include <LibGfx/Font/Font.h>
1212
#include <LibGfx/Rect.h>
1313
#include <LibWeb/CSS/ComputedProperties.h>
14+
#include <LibWeb/CSS/FontComputer.h>
1415
#include <LibWeb/CSS/Length.h>
1516
#include <LibWeb/CSS/Percentage.h>
16-
#include <LibWeb/CSS/StyleComputer.h>
1717
#include <LibWeb/DOM/Document.h>
1818
#include <LibWeb/HTML/BrowsingContext.h>
1919
#include <LibWeb/HTML/Navigable.h>
@@ -139,7 +139,7 @@ Length::ResolutionContext Length::ResolutionContext::for_element(DOM::AbstractEl
139139

140140
Length::ResolutionContext Length::ResolutionContext::for_window(HTML::Window const& window)
141141
{
142-
auto const& initial_font = window.associated_document().style_computer().initial_font();
142+
auto const& initial_font = window.associated_document().font_computer().initial_font();
143143
Gfx::FontPixelMetrics const& initial_font_metrics = initial_font.pixel_metrics();
144144
Length::FontMetrics font_metrics { CSSPixels { initial_font.pixel_size() }, initial_font_metrics, InitialValues::line_height() };
145145
return Length::ResolutionContext {
@@ -151,7 +151,7 @@ Length::ResolutionContext Length::ResolutionContext::for_window(HTML::Window con
151151

152152
Length::ResolutionContext Length::ResolutionContext::for_document(DOM::Document const& document)
153153
{
154-
auto const& initial_font = document.style_computer().initial_font();
154+
auto const& initial_font = document.font_computer().initial_font();
155155
Gfx::FontPixelMetrics const& initial_font_metrics = initial_font.pixel_metrics();
156156
Length::FontMetrics font_metrics { CSSPixels { initial_font.pixel_size() }, initial_font_metrics, InitialValues::line_height() };
157157
CSSPixelRect viewport_rect;

0 commit comments

Comments
 (0)