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
ParsedFontFace parsed_font_face {
nullptr,
font->m_family,
font->m_weight.to_number<int>(),
0, // FIXME: slope

View file

@ -6,6 +6,7 @@
*/
#include <LibWeb/CSS/CSSFontFaceDescriptors.h>
#include <LibWeb/CSS/CSSRule.h>
#include <LibWeb/CSS/ParsedFontFace.h>
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
@ -38,7 +39,7 @@ Vector<ParsedFontFace::Source> ParsedFontFace::sources_from_style_value(CSSStyle
[&](FontSourceStyleValue::Local const& local) {
sources.empend(extract_font_name(local.name), OptionalNone {});
},
[&](::URL::URL const& url) {
[&](URL const& url) {
// FIXME: tech()
sources.empend(url, font_source.format());
});
@ -171,6 +172,7 @@ ParsedFontFace ParsedFontFace::from_descriptors(CSSFontFaceDescriptors const& de
}
return ParsedFontFace {
descriptors.parent_rule()->parent_style_sheet(),
move(font_family),
move(weight),
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)
: m_font_family(move(font_family))
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_parent_style_sheet(parent_style_sheet)
, m_font_family(move(font_family))
, m_font_named_instance(move(font_named_instance))
, m_weight(weight)
, m_slope(slope)

View file

@ -10,16 +10,16 @@
#include <AK/FlyString.h>
#include <AK/HashMap.h>
#include <LibGfx/Font/UnicodeRange.h>
#include <LibURL/URL.h>
#include <LibWeb/CSS/Enums.h>
#include <LibWeb/CSS/Percentage.h>
#include <LibWeb/CSS/URL.h>
namespace Web::CSS {
class ParsedFontFace {
public:
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?
Optional<FlyString> format;
};
@ -27,9 +27,10 @@ public:
static Vector<Source> sources_from_style_value(CSSStyleValue 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;
GC::Ptr<CSSStyleSheet> parent_style_sheet() const { return m_parent_style_sheet; }
Optional<Percentage> ascent_override() const { return m_ascent_override; }
Optional<Percentage> descent_override() const { return m_descent_override; }
FontDisplay font_display() const { return m_font_display; }
@ -46,6 +47,7 @@ public:
Vector<Gfx::UnicodeRange> const& unicode_ranges() const { return m_unicode_ranges; }
private:
GC::Ptr<CSSStyleSheet> m_parent_style_sheet;
FlyString m_font_family;
Optional<FlyString> m_font_named_instance;
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);
if (!url.has_value())
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;
@ -3859,7 +3855,7 @@ RefPtr<FontSourceStyleValue const> Parser::parse_font_source_value(TokenStream<C
// FIXME: [ tech( <font-tech>#)]?
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)

View file

@ -187,8 +187,9 @@ StyleComputer::StyleComputer(DOM::Document& document)
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_parent_style_sheet(parent_style_sheet)
, m_family_name(move(family_name))
, m_unicode_ranges(move(unicode_ranges))
, 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
// 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:
// FIXME: Get the rule's parent style sheet from somewhere
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 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(), style_sheet_or_document, Fetch::Infrastructure::Request::Destination::Font, CorsMode::Cors,
[weak_loader = make_weak_ptr()](auto response, auto stream) {
// NB: If the FontLoader died before this fetch completed, nobody wants the data.
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.
Vector<::URL::URL> urls;
Vector<URL> urls;
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::URL>())
urls.append(*m_document->encoding_parse_url(source.local_or_url.get<::URL::URL>().to_string()));
if (source.local_or_url.has<URL>())
urls.append(source.local_or_url.get<URL>());
// FIXME: Handle local()
}
@ -3063,7 +3063,7 @@ Optional<FontLoader&> StyleComputer::load_font_face(ParsedFontFace const& font_f
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 maybe_font_loaders_list = m_loaded_fonts.get(key);
if (maybe_font_loaders_list.has_value()) {

View file

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

View file

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

View file

@ -8,6 +8,7 @@
#include <AK/FlyString.h>
#include <LibWeb/CSS/CSSStyleValue.h>
#include <LibWeb/CSS/URL.h>
namespace Web::CSS {
@ -16,7 +17,7 @@ public:
struct Local {
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)
{

View file

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