LibWeb/CSS: Use CSS::URL for font-fetching

ParsedFontFace and FontLoader now both keep track of which
CSSStyleSheet (if any) was the source of the font-face, so the URLs can
be completed correctly.
This commit is contained in:
Sam Atkins 2025-05-02 12:07:22 +01:00
commit 9e2e796f2d
Notes: github-actions[bot] 2025-05-03 11:02:47 +00:00
9 changed files with 38 additions and 31 deletions

View file

@ -483,6 +483,7 @@ GC::Ref<WebIDL::Promise> FontFace::load()
// FIXME: The ParsedFontFace is kind of expensive to create. We should be using a shared sub-object for the data // FIXME: The ParsedFontFace is kind of expensive to create. We should be using a shared sub-object for the data
ParsedFontFace parsed_font_face { ParsedFontFace parsed_font_face {
nullptr,
font->m_family, font->m_family,
font->m_weight.to_number<int>(), font->m_weight.to_number<int>(),
0, // FIXME: slope 0, // FIXME: slope

View file

@ -6,6 +6,7 @@
*/ */
#include <LibWeb/CSS/CSSFontFaceDescriptors.h> #include <LibWeb/CSS/CSSFontFaceDescriptors.h>
#include <LibWeb/CSS/CSSRule.h>
#include <LibWeb/CSS/ParsedFontFace.h> #include <LibWeb/CSS/ParsedFontFace.h>
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h> #include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h> #include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
@ -38,7 +39,7 @@ Vector<ParsedFontFace::Source> ParsedFontFace::sources_from_style_value(CSSStyle
[&](FontSourceStyleValue::Local const& local) { [&](FontSourceStyleValue::Local const& local) {
sources.empend(extract_font_name(local.name), OptionalNone {}); sources.empend(extract_font_name(local.name), OptionalNone {});
}, },
[&](::URL::URL const& url) { [&](URL const& url) {
// FIXME: tech() // FIXME: tech()
sources.empend(url, font_source.format()); sources.empend(url, font_source.format());
}); });
@ -171,6 +172,7 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de
} }
return ParsedFontFace { return ParsedFontFace {
descriptors.parent_rule()->parent_style_sheet(),
move(font_family), move(font_family),
move(weight), move(weight),
move(slope), move(slope),
@ -188,8 +190,9 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de
}; };
} }
ParsedFontFace::ParsedFontFace(FlyString font_family, Optional<int> weight, Optional<int> slope, Optional<int> width, Vector<Source> sources, Vector<Gfx::UnicodeRange> unicode_ranges, Optional<Percentage> ascent_override, Optional<Percentage> descent_override, Optional<Percentage> line_gap_override, FontDisplay font_display, Optional<FlyString> font_named_instance, Optional<FlyString> font_language_override, Optional<OrderedHashMap<FlyString, i64>> font_feature_settings, Optional<OrderedHashMap<FlyString, double>> font_variation_settings) ParsedFontFace::ParsedFontFace(GC::Ptr<CSSStyleSheet> parent_style_sheet, FlyString font_family, Optional<int> weight, Optional<int> slope, Optional<int> width, Vector<Source> sources, Vector<Gfx::UnicodeRange> unicode_ranges, Optional<Percentage> ascent_override, Optional<Percentage> descent_override, Optional<Percentage> line_gap_override, FontDisplay font_display, Optional<FlyString> font_named_instance, Optional<FlyString> font_language_override, Optional<OrderedHashMap<FlyString, i64>> font_feature_settings, Optional<OrderedHashMap<FlyString, double>> font_variation_settings)
: m_font_family(move(font_family)) : m_parent_style_sheet(parent_style_sheet)
, m_font_family(move(font_family))
, m_font_named_instance(move(font_named_instance)) , m_font_named_instance(move(font_named_instance))
, m_weight(weight) , m_weight(weight)
, m_slope(slope) , m_slope(slope)

View file

@ -10,16 +10,16 @@
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <LibGfx/Font/UnicodeRange.h> #include <LibGfx/Font/UnicodeRange.h>
#include <LibURL/URL.h>
#include <LibWeb/CSS/Enums.h> #include <LibWeb/CSS/Enums.h>
#include <LibWeb/CSS/Percentage.h> #include <LibWeb/CSS/Percentage.h>
#include <LibWeb/CSS/URL.h>
namespace Web::CSS { namespace Web::CSS {
class ParsedFontFace { class ParsedFontFace {
public: public:
struct Source { struct Source {
Variant<FlyString, ::URL::URL> local_or_url; Variant<FlyString, URL> local_or_url;
// FIXME: Do we need to keep this around, or is it only needed to discard unwanted formats during parsing? // FIXME: Do we need to keep this around, or is it only needed to discard unwanted formats during parsing?
Optional<FlyString> format; Optional<FlyString> format;
}; };
@ -27,9 +27,10 @@ public:
static Vector<Source> sources_from_style_value(CSSStyleValue const&); static Vector<Source> sources_from_style_value(CSSStyleValue const&);
static ParsedFontFace from_descriptors(CSSFontFaceDescriptors const&); static ParsedFontFace from_descriptors(CSSFontFaceDescriptors const&);
ParsedFontFace(FlyString font_family, Optional<int> weight, Optional<int> slope, Optional<int> width, Vector<Source> sources, Vector<Gfx::UnicodeRange> unicode_ranges, Optional<Percentage> ascent_override, Optional<Percentage> descent_override, Optional<Percentage> line_gap_override, FontDisplay font_display, Optional<FlyString> font_named_instance, Optional<FlyString> font_language_override, Optional<OrderedHashMap<FlyString, i64>> font_feature_settings, Optional<OrderedHashMap<FlyString, double>> font_variation_settings); ParsedFontFace(GC::Ptr<CSSStyleSheet> parent_style_sheet, FlyString font_family, Optional<int> weight, Optional<int> slope, Optional<int> width, Vector<Source> sources, Vector<Gfx::UnicodeRange> unicode_ranges, Optional<Percentage> ascent_override, Optional<Percentage> descent_override, Optional<Percentage> line_gap_override, FontDisplay font_display, Optional<FlyString> font_named_instance, Optional<FlyString> font_language_override, Optional<OrderedHashMap<FlyString, i64>> font_feature_settings, Optional<OrderedHashMap<FlyString, double>> font_variation_settings);
~ParsedFontFace() = default; ~ParsedFontFace() = default;
GC::Ptr<CSSStyleSheet> parent_style_sheet() const { return m_parent_style_sheet; }
Optional<Percentage> ascent_override() const { return m_ascent_override; } Optional<Percentage> ascent_override() const { return m_ascent_override; }
Optional<Percentage> descent_override() const { return m_descent_override; } Optional<Percentage> descent_override() const { return m_descent_override; }
FontDisplay font_display() const { return m_font_display; } FontDisplay font_display() const { return m_font_display; }
@ -46,6 +47,7 @@ public:
Vector<Gfx::UnicodeRange> const& unicode_ranges() const { return m_unicode_ranges; } Vector<Gfx::UnicodeRange> const& unicode_ranges() const { return m_unicode_ranges; }
private: private:
GC::Ptr<CSSStyleSheet> m_parent_style_sheet;
FlyString m_font_family; FlyString m_font_family;
Optional<FlyString> m_font_named_instance; Optional<FlyString> m_font_named_instance;
Optional<int> m_weight; Optional<int> m_weight;

View file

@ -3813,10 +3813,6 @@ RefPtr<FontSourceStyleValue const> Parser::parse_font_source_value(TokenStream<C
auto url = parse_url_function(tokens); auto url = parse_url_function(tokens);
if (!url.has_value()) if (!url.has_value())
return nullptr; return nullptr;
// FIXME: Stop completing the URL here
auto completed_url = complete_url(url->url());
if (!completed_url.has_value())
return nullptr;
Optional<FlyString> format; Optional<FlyString> format;
@ -3859,7 +3855,7 @@ RefPtr<FontSourceStyleValue const> Parser::parse_font_source_value(TokenStream<C
// FIXME: [ tech( <font-tech>#)]? // FIXME: [ tech( <font-tech>#)]?
transaction.commit(); transaction.commit();
return FontSourceStyleValue::create(completed_url.release_value(), move(format)); return FontSourceStyleValue::create(url.release_value(), move(format));
} }
NonnullRefPtr<CSSStyleValue const> Parser::resolve_unresolved_style_value(ParsingParams const& context, DOM::Element& element, Optional<PseudoElement> pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved) NonnullRefPtr<CSSStyleValue const> Parser::resolve_unresolved_style_value(ParsingParams const& context, DOM::Element& element, Optional<PseudoElement> pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved)

View file

@ -187,8 +187,9 @@ StyleComputer::StyleComputer(DOM::Document& document)
StyleComputer::~StyleComputer() = default; StyleComputer::~StyleComputer() = default;
FontLoader::FontLoader(StyleComputer& style_computer, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<::URL::URL> urls, Function<void(RefPtr<Gfx::Typeface const>)> on_load) FontLoader::FontLoader(StyleComputer& style_computer, GC::Ptr<CSSStyleSheet> parent_style_sheet, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<URL> urls, Function<void(RefPtr<Gfx::Typeface const>)> on_load)
: m_style_computer(style_computer) : m_style_computer(style_computer)
, m_parent_style_sheet(parent_style_sheet)
, m_family_name(move(family_name)) , m_family_name(move(family_name))
, m_unicode_ranges(move(unicode_ranges)) , m_unicode_ranges(move(unicode_ranges))
, m_urls(move(urls)) , m_urls(move(urls))
@ -225,8 +226,8 @@ void FontLoader::start_loading_next_url()
// To fetch a font given a selected <url> url for @font-face rule, fetch url, with stylesheet being rules parent // To fetch a font given a selected <url> url for @font-face rule, fetch url, with stylesheet being rules parent
// CSS style sheet, destination "font", CORS mode "cors", and processResponse being the following steps given // CSS style sheet, destination "font", CORS mode "cors", and processResponse being the following steps given
// response res and null, failure or a byte stream stream: // response res and null, failure or a byte stream stream:
// FIXME: Get the rule's parent style sheet from somewhere auto style_sheet_or_document = m_parent_style_sheet ? StyleSheetOrDocument { *m_parent_style_sheet } : StyleSheetOrDocument { m_style_computer.document() };
auto maybe_fetch_controller = fetch_a_style_resource(m_urls.take_first(), GC::Ref { m_style_computer.document() }, Fetch::Infrastructure::Request::Destination::Font, CorsMode::Cors, auto maybe_fetch_controller = fetch_a_style_resource(m_urls.take_first(), style_sheet_or_document, Fetch::Infrastructure::Request::Destination::Font, CorsMode::Cors,
[weak_loader = make_weak_ptr()](auto response, auto stream) { [weak_loader = make_weak_ptr()](auto response, auto stream) {
// NB: If the FontLoader died before this fetch completed, nobody wants the data. // NB: If the FontLoader died before this fetch completed, nobody wants the data.
if (weak_loader.is_null()) if (weak_loader.is_null())
@ -3049,11 +3050,10 @@ Optional<FontLoader&> StyleComputer::load_font_face(ParsedFontFace const& font_f
}; };
// FIXME: Pass the sources directly, so the font loader can make use of the format information, or load local fonts. // FIXME: Pass the sources directly, so the font loader can make use of the format information, or load local fonts.
Vector<::URL::URL> urls; Vector<URL> urls;
for (auto const& source : font_face.sources()) { for (auto const& source : font_face.sources()) {
// FIXME: These should be loaded relative to the stylesheet URL instead of the document URL. if (source.local_or_url.has<URL>())
if (source.local_or_url.has<::URL::URL>()) urls.append(source.local_or_url.get<URL>());
urls.append(*m_document->encoding_parse_url(source.local_or_url.get<::URL::URL>().to_string()));
// FIXME: Handle local() // FIXME: Handle local()
} }
@ -3063,7 +3063,7 @@ Optional<FontLoader&> StyleComputer::load_font_face(ParsedFontFace const& font_f
return {}; return {};
} }
auto loader = make<FontLoader>(*this, font_face.font_family(), font_face.unicode_ranges(), move(urls), move(on_load)); auto loader = make<FontLoader>(*this, font_face.parent_style_sheet(), font_face.font_family(), font_face.unicode_ranges(), move(urls), move(on_load));
auto& loader_ref = *loader; auto& loader_ref = *loader;
auto maybe_font_loaders_list = m_loaded_fonts.get(key); auto maybe_font_loaders_list = m_loaded_fonts.get(key);
if (maybe_font_loaders_list.has_value()) { if (maybe_font_loaders_list.has_value()) {

View file

@ -315,7 +315,7 @@ private:
class FontLoader : public Weakable<FontLoader> { class FontLoader : public Weakable<FontLoader> {
public: public:
FontLoader(StyleComputer& style_computer, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<::URL::URL> urls, ESCAPING Function<void(RefPtr<Gfx::Typeface const>)> on_load = {}); FontLoader(StyleComputer& style_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 = {});
virtual ~FontLoader(); virtual ~FontLoader();
@ -333,10 +333,11 @@ private:
void font_did_load_or_fail(RefPtr<Gfx::Typeface const>); void font_did_load_or_fail(RefPtr<Gfx::Typeface const>);
StyleComputer& m_style_computer; StyleComputer& m_style_computer;
GC::Ptr<CSSStyleSheet> m_parent_style_sheet;
FlyString m_family_name; FlyString m_family_name;
Vector<Gfx::UnicodeRange> m_unicode_ranges; Vector<Gfx::UnicodeRange> m_unicode_ranges;
RefPtr<Gfx::Typeface const> m_vector_font; RefPtr<Gfx::Typeface const> m_vector_font;
Vector<::URL::URL> m_urls; Vector<URL> m_urls;
GC::Root<Fetch::Infrastructure::FetchController> m_fetch_controller; GC::Root<Fetch::Infrastructure::FetchController> m_fetch_controller;
Function<void(RefPtr<Gfx::Typeface const>)> m_on_load; Function<void(RefPtr<Gfx::Typeface const>)> m_on_load;
}; };

View file

@ -34,11 +34,11 @@ String FontSourceStyleValue::to_string(SerializationMode) const
builder.append(')'); builder.append(')');
return builder.to_string_without_validation(); return builder.to_string_without_validation();
}, },
[this](::URL::URL const& url) { [this](URL const& url) {
// <url> [ format(<font-format>)]? [ tech( <font-tech>#)]? // <url> [ format(<font-format>)]? [ tech( <font-tech>#)]?
// FIXME: tech() // FIXME: tech()
StringBuilder builder; StringBuilder builder;
serialize_a_url(builder, url.to_string()); builder.append(url.to_string());
if (m_format.has_value()) { if (m_format.has_value()) {
builder.append(" format("sv); builder.append(" format("sv);
@ -59,8 +59,8 @@ bool FontSourceStyleValue::properties_equal(FontSourceStyleValue const& other) c
} }
return false; return false;
}, },
[&other](::URL::URL const& url) { [&other](URL const& url) {
if (auto* other_url = other.m_source.get_pointer<::URL::URL>()) { if (auto* other_url = other.m_source.get_pointer<URL>()) {
return url == *other_url; return url == *other_url;
} }
return false; return false;

View file

@ -8,6 +8,7 @@
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <LibWeb/CSS/CSSStyleValue.h> #include <LibWeb/CSS/CSSStyleValue.h>
#include <LibWeb/CSS/URL.h>
namespace Web::CSS { namespace Web::CSS {
@ -16,7 +17,7 @@ public:
struct Local { struct Local {
NonnullRefPtr<CSSStyleValue const> name; NonnullRefPtr<CSSStyleValue const> name;
}; };
using Source = Variant<Local, ::URL::URL>; using Source = Variant<Local, URL>;
static ValueComparingNonnullRefPtr<FontSourceStyleValue const> create(Source source, Optional<FlyString> format) static ValueComparingNonnullRefPtr<FontSourceStyleValue const> create(Source source, Optional<FlyString> format)
{ {

View file

@ -728,12 +728,15 @@ void dump_font_face_rule(StringBuilder& builder, CSS::CSSFontFaceRule const& rul
indent(builder, indent_levels + 1); indent(builder, indent_levels + 1);
builder.append("sources:\n"sv); builder.append("sources:\n"sv);
for (auto const& source : font_face.sources()) { for (auto const& [local_or_url, format] : font_face.sources()) {
indent(builder, indent_levels + 2); indent(builder, indent_levels + 2);
if (source.local_or_url.has<URL::URL>()) local_or_url.visit(
builder.appendff("url={}, format={}\n", source.local_or_url.get<URL::URL>(), source.format.value_or("???"_string)); [&builder, &format](CSS::URL const& url) {
else builder.appendff("url={}, format={}\n", url, format.value_or("???"_string));
builder.appendff("local={}\n", source.local_or_url.get<FlyString>()); },
[&builder](FlyString const& local) {
builder.appendff("local={}\n", local);
});
} }
indent(builder, indent_levels + 1); indent(builder, indent_levels + 1);