/src/serenity/Userland/Libraries/LibWeb/CSS/FontFace.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibCore/Promise.h> |
8 | | #include <LibGfx/Font/OpenType/Font.h> |
9 | | #include <LibGfx/Font/VectorFont.h> |
10 | | #include <LibGfx/Font/WOFF/Font.h> |
11 | | #include <LibGfx/Font/WOFF2/Font.h> |
12 | | #include <LibJS/Heap/Heap.h> |
13 | | #include <LibJS/Runtime/ArrayBuffer.h> |
14 | | #include <LibJS/Runtime/Realm.h> |
15 | | #include <LibWeb/Bindings/FontFacePrototype.h> |
16 | | #include <LibWeb/Bindings/Intrinsics.h> |
17 | | #include <LibWeb/CSS/FontFace.h> |
18 | | #include <LibWeb/CSS/Parser/Parser.h> |
19 | | #include <LibWeb/CSS/StyleComputer.h> |
20 | | #include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h> |
21 | | #include <LibWeb/HTML/Window.h> |
22 | | #include <LibWeb/Platform/EventLoopPlugin.h> |
23 | | #include <LibWeb/WebIDL/AbstractOperations.h> |
24 | | #include <LibWeb/WebIDL/Buffers.h> |
25 | | #include <LibWeb/WebIDL/Promise.h> |
26 | | |
27 | | namespace Web::CSS { |
28 | | |
29 | | static NonnullRefPtr<Core::Promise<NonnullRefPtr<Gfx::VectorFont>>> load_vector_font(ByteBuffer const& data) |
30 | 0 | { |
31 | 0 | auto promise = Core::Promise<NonnullRefPtr<Gfx::VectorFont>>::construct(); |
32 | | |
33 | | // FIXME: 'Asynchronously' shouldn't mean 'later on the main thread'. |
34 | | // Can we defer this to a background thread? |
35 | 0 | Platform::EventLoopPlugin::the().deferred_invoke([&data, promise] { |
36 | | // FIXME: This should be de-duplicated with StyleComputer::FontLoader::try_load_font |
37 | | // We don't have the luxury of knowing the MIME type, so we have to try all formats. |
38 | 0 | auto ttf = OpenType::Font::try_load_from_externally_owned_memory(data); |
39 | 0 | if (!ttf.is_error()) { |
40 | 0 | promise->resolve(ttf.release_value()); |
41 | 0 | return; |
42 | 0 | } |
43 | 0 | auto woff = WOFF::Font::try_load_from_externally_owned_memory(data); |
44 | 0 | if (!woff.is_error()) { |
45 | 0 | promise->resolve(woff.release_value()); |
46 | 0 | return; |
47 | 0 | } |
48 | 0 | auto woff2 = WOFF2::Font::try_load_from_externally_owned_memory(data); |
49 | 0 | if (!woff2.is_error()) { |
50 | 0 | promise->resolve(woff2.release_value()); |
51 | 0 | return; |
52 | 0 | } |
53 | 0 | promise->reject(Error::from_string_literal("Automatic format detection failed")); |
54 | 0 | }); |
55 | |
|
56 | 0 | return promise; |
57 | 0 | } |
58 | | |
59 | | JS_DEFINE_ALLOCATOR(FontFace); |
60 | | |
61 | | template<CSS::PropertyID PropertyID> |
62 | | RefPtr<CSSStyleValue const> parse_property_string(JS::Realm& realm, StringView value) |
63 | 0 | { |
64 | 0 | auto parser = CSS::Parser::Parser::create(CSS::Parser::ParsingContext(realm), value); |
65 | 0 | return parser.parse_as_css_value(PropertyID); |
66 | 0 | } Unexecuted instantiation: AK::RefPtr<Web::CSS::CSSStyleValue const> Web::CSS::parse_property_string<(Web::CSS::PropertyID)54>(JS::Realm&, AK::StringView) Unexecuted instantiation: AK::RefPtr<Web::CSS::CSSStyleValue const> Web::CSS::parse_property_string<(Web::CSS::PropertyID)58>(JS::Realm&, AK::StringView) Unexecuted instantiation: AK::RefPtr<Web::CSS::CSSStyleValue const> Web::CSS::parse_property_string<(Web::CSS::PropertyID)61>(JS::Realm&, AK::StringView) Unexecuted instantiation: AK::RefPtr<Web::CSS::CSSStyleValue const> Web::CSS::parse_property_string<(Web::CSS::PropertyID)62>(JS::Realm&, AK::StringView) |
67 | | |
68 | | // https://drafts.csswg.org/css-font-loading/#font-face-constructor |
69 | | JS::NonnullGCPtr<FontFace> FontFace::construct_impl(JS::Realm& realm, String family, FontFaceSource source, FontFaceDescriptors const& descriptors) |
70 | 0 | { |
71 | 0 | auto& vm = realm.vm(); |
72 | 0 | auto base_url = HTML::relevant_settings_object(realm.global_object()).api_base_url(); |
73 | | |
74 | | // 1. Let font face be a fresh FontFace object. Set font face’s status attribute to "unloaded", |
75 | | // Set its internal [[FontStatusPromise]] slot to a fresh pending Promise object. |
76 | 0 | auto promise = WebIDL::create_promise(realm); |
77 | | |
78 | | // FIXME: Parse the family argument, and the members of the descriptors argument, |
79 | | // according to the grammars of the corresponding descriptors of the CSS @font-face rule. |
80 | | // If the source argument is a CSSOMString, parse it according to the grammar of the CSS src descriptor of the @font-face rule. |
81 | | // If any of them fail to parse correctly, reject font face’s [[FontStatusPromise]] with a DOMException named "SyntaxError", |
82 | | // set font face’s corresponding attributes to the empty string, and set font face’s status attribute to "error". |
83 | | // Otherwise, set font face’s corresponding attributes to the serialization of the parsed values. |
84 | | |
85 | | // 2. (Out of order) If the source argument was a CSSOMString, set font face’s internal [[Urls]] |
86 | | // slot to the string. |
87 | | // If the source argument was a BinaryData, set font face’s internal [[Data]] slot |
88 | | // to the passed argument. |
89 | 0 | Vector<CSS::ParsedFontFace::Source> sources; |
90 | 0 | ByteBuffer buffer; |
91 | 0 | if (auto* string = source.get_pointer<String>()) { |
92 | 0 | auto parser = CSS::Parser::Parser::create(CSS::Parser::ParsingContext(realm, base_url), *string); |
93 | 0 | sources = parser.parse_as_font_face_src(); |
94 | 0 | if (sources.is_empty()) |
95 | 0 | WebIDL::reject_promise(realm, promise, WebIDL::SyntaxError::create(realm, "FontFace constructor: Invalid source string"_string)); |
96 | 0 | } else { |
97 | 0 | auto buffer_source = source.get<JS::Handle<WebIDL::BufferSource>>(); |
98 | 0 | auto maybe_buffer = WebIDL::get_buffer_source_copy(buffer_source->raw_object()); |
99 | 0 | if (maybe_buffer.is_error()) { |
100 | 0 | VERIFY(maybe_buffer.error().code() == ENOMEM); |
101 | 0 | auto throw_completion = vm.throw_completion<JS::InternalError>(vm.error_message(JS::VM::ErrorMessage::OutOfMemory)); |
102 | 0 | WebIDL::reject_promise(realm, promise, *throw_completion.value()); |
103 | 0 | } else { |
104 | 0 | buffer = maybe_buffer.release_value(); |
105 | 0 | } |
106 | 0 | } |
107 | | |
108 | 0 | if (buffer.is_empty() && sources.is_empty()) |
109 | 0 | WebIDL::reject_promise(realm, promise, WebIDL::SyntaxError::create(realm, "FontFace constructor: Invalid font source"_string)); |
110 | |
|
111 | 0 | auto font = realm.heap().allocate<FontFace>(realm, realm, promise, move(sources), move(buffer), move(family), descriptors); |
112 | | |
113 | | // 1. (continued) Return font face. If font face’s status is "error", terminate this algorithm; |
114 | | // otherwise, complete the rest of these steps asynchronously. |
115 | 0 | if (font->status() == Bindings::FontFaceLoadStatus::Error) |
116 | 0 | return font; |
117 | | |
118 | | // 3. If font face’s [[Data]] slot is not null, queue a task to run the following steps synchronously: |
119 | 0 | if (font->m_binary_data.is_empty()) |
120 | 0 | return font; |
121 | | |
122 | 0 | HTML::queue_global_task(HTML::Task::Source::FontLoading, HTML::relevant_global_object(*font), JS::create_heap_function(vm.heap(), [font] { |
123 | | // 1. Set font face’s status attribute to "loading". |
124 | 0 | font->m_status = Bindings::FontFaceLoadStatus::Loading; |
125 | | |
126 | | // 2. FIXME: For each FontFaceSet font face is in: |
127 | | |
128 | | // 3. Asynchronously, attempt to parse the data in it as a font. |
129 | | // When this is completed, successfully or not, queue a task to run the following steps synchronously: |
130 | 0 | font->m_font_load_promise = load_vector_font(font->m_binary_data); |
131 | |
|
132 | 0 | font->m_font_load_promise->when_resolved([font = JS::make_handle(font)](auto const& vector_font) -> ErrorOr<void> { |
133 | 0 | HTML::queue_global_task(HTML::Task::Source::FontLoading, HTML::relevant_global_object(*font), JS::create_heap_function(font->heap(), [font = JS::NonnullGCPtr(*font), vector_font] { |
134 | 0 | HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*font), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes); |
135 | | // 1. If the load was successful, font face now represents the parsed font; |
136 | | // fulfill font face’s [[FontStatusPromise]] with font face, and set its status attribute to "loaded". |
137 | | |
138 | | // FIXME: Are we supposed to set the properties of the FontFace based on the loaded vector font? |
139 | 0 | font->m_parsed_font = vector_font; |
140 | 0 | font->m_status = Bindings::FontFaceLoadStatus::Loaded; |
141 | 0 | WebIDL::resolve_promise(font->realm(), font->m_font_status_promise, font); |
142 | | |
143 | | // FIXME: For each FontFaceSet font face is in: |
144 | |
|
145 | 0 | font->m_font_load_promise = nullptr; |
146 | 0 | })); |
147 | 0 | return {}; |
148 | 0 | }); |
149 | 0 | font->m_font_load_promise->when_rejected([font = JS::make_handle(font)](auto const& error) { |
150 | 0 | HTML::queue_global_task(HTML::Task::Source::FontLoading, HTML::relevant_global_object(*font), JS::create_heap_function(font->heap(), [font = JS::NonnullGCPtr(*font), error = Error::copy(error)] { |
151 | 0 | HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*font), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes); |
152 | | // 2. Otherwise, reject font face’s [[FontStatusPromise]] with a DOMException named "SyntaxError" |
153 | | // and set font face’s status attribute to "error". |
154 | 0 | font->m_status = Bindings::FontFaceLoadStatus::Error; |
155 | 0 | WebIDL::reject_promise(font->realm(), font->m_font_status_promise, WebIDL::SyntaxError::create(font->realm(), MUST(String::formatted("Failed to load font: {}", error)))); |
156 | | |
157 | | // FIXME: For each FontFaceSet font face is in: |
158 | | |
159 | 0 | font->m_font_load_promise = nullptr; |
160 | 0 | })); |
161 | 0 | }); |
162 | 0 | })); |
163 | |
|
164 | 0 | return font; |
165 | 0 | } |
166 | | |
167 | | FontFace::FontFace(JS::Realm& realm, JS::NonnullGCPtr<WebIDL::Promise> font_status_promise, Vector<ParsedFontFace::Source> urls, ByteBuffer data, String font_family, FontFaceDescriptors const& descriptors) |
168 | 0 | : Bindings::PlatformObject(realm) |
169 | 0 | , m_font_status_promise(font_status_promise) |
170 | 0 | , m_urls(move(urls)) |
171 | 0 | , m_binary_data(move(data)) |
172 | 0 | { |
173 | 0 | m_family = move(font_family); |
174 | 0 | m_style = descriptors.style; |
175 | 0 | m_weight = descriptors.weight; |
176 | 0 | m_stretch = descriptors.stretch; |
177 | 0 | m_unicode_range = descriptors.unicode_range; |
178 | 0 | m_feature_settings = descriptors.feature_settings; |
179 | 0 | m_variation_settings = descriptors.variation_settings; |
180 | 0 | m_display = descriptors.display; |
181 | 0 | m_ascent_override = descriptors.ascent_override; |
182 | 0 | m_descent_override = descriptors.descent_override; |
183 | 0 | m_line_gap_override = descriptors.line_gap_override; |
184 | | |
185 | | // FIXME: Parse from descriptor |
186 | | // FIXME: Have gettter reflect this member instead of the string |
187 | 0 | m_unicode_ranges.empend(0x0u, 0x10FFFFu); |
188 | |
|
189 | 0 | if (verify_cast<JS::Promise>(*m_font_status_promise->promise()).state() == JS::Promise::State::Rejected) |
190 | 0 | m_status = Bindings::FontFaceLoadStatus::Error; |
191 | 0 | } |
192 | | |
193 | 0 | FontFace::~FontFace() = default; |
194 | | |
195 | | void FontFace::initialize(JS::Realm& realm) |
196 | 0 | { |
197 | 0 | Base::initialize(realm); |
198 | |
|
199 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(FontFace); |
200 | 0 | } |
201 | | |
202 | | void FontFace::visit_edges(JS::Cell::Visitor& visitor) |
203 | 0 | { |
204 | 0 | Base::visit_edges(visitor); |
205 | |
|
206 | 0 | visitor.visit(m_font_status_promise); |
207 | 0 | } |
208 | | |
209 | | JS::NonnullGCPtr<JS::Promise> FontFace::loaded() const |
210 | 0 | { |
211 | 0 | return verify_cast<JS::Promise>(*m_font_status_promise->promise()); |
212 | 0 | } |
213 | | |
214 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-family |
215 | | WebIDL::ExceptionOr<void> FontFace::set_family(String const& string) |
216 | 0 | { |
217 | 0 | auto property = parse_property_string<CSS::PropertyID::FontFamily>(realm(), string); |
218 | 0 | if (!property) |
219 | 0 | return WebIDL::SyntaxError::create(realm(), "FontFace.family setter: Invalid font descriptor"_string); |
220 | | |
221 | 0 | if (m_is_css_connected) { |
222 | | // FIXME: Propagate to the CSSFontFaceRule and update the font-family property |
223 | 0 | } |
224 | |
|
225 | 0 | m_family = property->to_string(); |
226 | |
|
227 | 0 | return {}; |
228 | 0 | } |
229 | | |
230 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-style |
231 | | WebIDL::ExceptionOr<void> FontFace::set_style(String const& string) |
232 | 0 | { |
233 | 0 | auto property = parse_property_string<CSS::PropertyID::FontStyle>(realm(), string); |
234 | 0 | if (!property) |
235 | 0 | return WebIDL::SyntaxError::create(realm(), "FontFace.style setter: Invalid font descriptor"_string); |
236 | | |
237 | 0 | if (m_is_css_connected) { |
238 | | // FIXME: Propagate to the CSSFontFaceRule and update the font-style property |
239 | 0 | } |
240 | |
|
241 | 0 | m_style = property->to_string(); |
242 | |
|
243 | 0 | return {}; |
244 | 0 | } |
245 | | |
246 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-weight |
247 | | WebIDL::ExceptionOr<void> FontFace::set_weight(String const& string) |
248 | 0 | { |
249 | 0 | auto property = parse_property_string<CSS::PropertyID::FontWeight>(realm(), string); |
250 | 0 | if (!property) |
251 | 0 | return WebIDL::SyntaxError::create(realm(), "FontFace.weight setter: Invalid font descriptor"_string); |
252 | | |
253 | 0 | if (m_is_css_connected) { |
254 | | // FIXME: Propagate to the CSSFontFaceRule and update the font-weight property |
255 | 0 | } |
256 | |
|
257 | 0 | m_weight = property->to_string(); |
258 | |
|
259 | 0 | return {}; |
260 | 0 | } |
261 | | |
262 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-stretch |
263 | | WebIDL::ExceptionOr<void> FontFace::set_stretch(String const& string) |
264 | 0 | { |
265 | | // NOTE: font-stretch is now an alias for font-width |
266 | 0 | auto property = parse_property_string<CSS::PropertyID::FontWidth>(realm(), string); |
267 | 0 | if (!property) |
268 | 0 | return WebIDL::SyntaxError::create(realm(), "FontFace.stretch setter: Invalid font descriptor"_string); |
269 | | |
270 | 0 | if (m_is_css_connected) { |
271 | | // FIXME: Propagate to the CSSFontFaceRule and update the font-width property |
272 | 0 | } |
273 | |
|
274 | 0 | m_stretch = property->to_string(); |
275 | |
|
276 | 0 | return {}; |
277 | 0 | } |
278 | | |
279 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-unicoderange |
280 | | WebIDL::ExceptionOr<void> FontFace::set_unicode_range(String const&) |
281 | 0 | { |
282 | | // FIXME: This *should* work, but the <urange> production is hard to parse |
283 | | // from just a value string in our implementation |
284 | 0 | return WebIDL::NotSupportedError::create(realm(), "unicode range is not yet implemented"_string); |
285 | 0 | } |
286 | | |
287 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-featuresettings |
288 | | WebIDL::ExceptionOr<void> FontFace::set_feature_settings(String const&) |
289 | 0 | { |
290 | 0 | return WebIDL::NotSupportedError::create(realm(), "feature settings is not yet implemented"_string); |
291 | 0 | } |
292 | | |
293 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-variationsettings |
294 | | WebIDL::ExceptionOr<void> FontFace::set_variation_settings(String const&) |
295 | 0 | { |
296 | 0 | return WebIDL::NotSupportedError::create(realm(), "variation settings is not yet implemented"_string); |
297 | 0 | } |
298 | | |
299 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-display |
300 | | WebIDL::ExceptionOr<void> FontFace::set_display(String const&) |
301 | 0 | { |
302 | 0 | return WebIDL::NotSupportedError::create(realm(), "display is not yet implemented"_string); |
303 | 0 | } |
304 | | |
305 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-ascentoverride |
306 | | WebIDL::ExceptionOr<void> FontFace::set_ascent_override(String const&) |
307 | 0 | { |
308 | 0 | return WebIDL::NotSupportedError::create(realm(), "ascent override is not yet implemented"_string); |
309 | 0 | } |
310 | | |
311 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-descentoverride |
312 | | WebIDL::ExceptionOr<void> FontFace::set_descent_override(String const&) |
313 | 0 | { |
314 | 0 | return WebIDL::NotSupportedError::create(realm(), "descent override is not yet implemented"_string); |
315 | 0 | } |
316 | | |
317 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-linegapoverride |
318 | | WebIDL::ExceptionOr<void> FontFace::set_line_gap_override(String const&) |
319 | 0 | { |
320 | 0 | return WebIDL::NotSupportedError::create(realm(), "line gap override is not yet implemented"_string); |
321 | 0 | } |
322 | | |
323 | | // https://drafts.csswg.org/css-font-loading/#dom-fontface-load |
324 | | JS::NonnullGCPtr<JS::Promise> FontFace::load() |
325 | 0 | { |
326 | | // 1. Let font face be the FontFace object on which this method was called. |
327 | 0 | auto& font_face = *this; |
328 | | |
329 | | // 2. If font face’s [[Urls]] slot is null, or its status attribute is anything other than "unloaded", |
330 | | // return font face’s [[FontStatusPromise]] and abort these steps. |
331 | 0 | if (font_face.m_urls.is_empty() || font_face.m_status != Bindings::FontFaceLoadStatus::Unloaded) |
332 | 0 | return font_face.loaded(); |
333 | | |
334 | 0 | load_font_source(); |
335 | |
|
336 | 0 | return font_face.loaded(); |
337 | 0 | } |
338 | | |
339 | | void FontFace::load_font_source() |
340 | 0 | { |
341 | 0 | VERIFY(!m_urls.is_empty() && m_status == Bindings::FontFaceLoadStatus::Unloaded); |
342 | | // NOTE: These steps are from the load() method, but can also be called by the user agent when the font |
343 | | // is needed to render something on the page. |
344 | | |
345 | | // User agents can initiate font loads on their own, whenever they determine that a given font face is necessary |
346 | | // to render something on the page. When this happens, they must act as if they had called the corresponding |
347 | | // FontFace’s load() method described here. |
348 | | |
349 | | // 3. Otherwise, set font face’s status attribute to "loading", return font face’s [[FontStatusPromise]], |
350 | | // and continue executing the rest of this algorithm asynchronously. |
351 | 0 | m_status = Bindings::FontFaceLoadStatus::Loading; |
352 | |
|
353 | 0 | Web::Platform::EventLoopPlugin::the().deferred_invoke([font = JS::make_handle(this)] { |
354 | | // 4. Using the value of font face’s [[Urls]] slot, attempt to load a font as defined in [CSS-FONTS-3], |
355 | | // as if it was the value of a @font-face rule’s src descriptor. |
356 | | |
357 | | // 5. When the load operation completes, successfully or not, queue a task to run the following steps synchronously: |
358 | 0 | auto on_error = [font] { |
359 | 0 | HTML::queue_global_task(HTML::Task::Source::FontLoading, HTML::relevant_global_object(*font), JS::create_heap_function(font->heap(), [font = JS::NonnullGCPtr(*font)] { |
360 | 0 | HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*font), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes); |
361 | | |
362 | | // 1. If the attempt to load fails, reject font face’s [[FontStatusPromise]] with a DOMException whose name |
363 | | // is "NetworkError" and set font face’s status attribute to "error". |
364 | 0 | font->m_status = Bindings::FontFaceLoadStatus::Error; |
365 | 0 | WebIDL::reject_promise(font->realm(), font->m_font_status_promise, WebIDL::NetworkError::create(font->realm(), "Failed to load font"_string)); |
366 | | |
367 | | // FIXME: For each FontFaceSet font face is in: |
368 | 0 | })); |
369 | 0 | }; |
370 | |
|
371 | 0 | auto on_load = [font](FontLoader const& loader) { |
372 | | // FIXME: We are assuming that the font loader will live as long as the document! This is an unsafe capture |
373 | 0 | HTML::queue_global_task(HTML::Task::Source::FontLoading, HTML::relevant_global_object(*font), JS::create_heap_function(font->heap(), [font = JS::NonnullGCPtr(*font), &loader] { |
374 | 0 | HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*font), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes); |
375 | | |
376 | | // 2. Otherwise, font face now represents the loaded font; fulfill font face’s [[FontStatusPromise]] with font face |
377 | | // and set font face’s status attribute to "loaded". |
378 | 0 | font->m_parsed_font = loader.vector_font(); |
379 | 0 | font->m_status = Bindings::FontFaceLoadStatus::Loaded; |
380 | 0 | WebIDL::resolve_promise(font->realm(), font->m_font_status_promise, font); |
381 | | |
382 | | // FIXME: For each FontFaceSet font face is in: |
383 | 0 | })); |
384 | 0 | }; |
385 | | |
386 | | // FIXME: We should probably put the 'font cache' on the WindowOrWorkerGlobalScope instead of tying it to the document's style computer |
387 | 0 | auto& global = HTML::relevant_global_object(*font); |
388 | 0 | if (is<HTML::Window>(global)) { |
389 | 0 | auto& window = static_cast<HTML::Window&>(global); |
390 | 0 | auto& style_computer = const_cast<StyleComputer&>(window.document()->style_computer()); |
391 | | |
392 | | // FIXME: The ParsedFontFace is kind of expensive to create. We should be using a shared sub-object for the data |
393 | 0 | ParsedFontFace parsed_font_face { |
394 | 0 | font->m_family, |
395 | 0 | font->m_weight.to_number<int>(), |
396 | 0 | 0, // FIXME: slope |
397 | 0 | Gfx::FontWidth::Normal, // FIXME: width |
398 | 0 | font->m_urls, |
399 | 0 | font->m_unicode_ranges, |
400 | 0 | {}, // FIXME: ascent_override |
401 | 0 | {}, // FIXME: descent_override |
402 | 0 | {}, // FIXME: line_gap_override |
403 | 0 | FontDisplay::Auto, // FIXME: font_display |
404 | 0 | {}, // font-named-instance doesn't exist in FontFace |
405 | 0 | {}, // font-language-override doesn't exist in FontFace |
406 | 0 | {}, // FIXME: feature_settings |
407 | 0 | {}, // FIXME: variation_settings |
408 | 0 | }; |
409 | 0 | if (auto loader = style_computer.load_font_face(parsed_font_face, move(on_load), move(on_error)); loader.has_value()) |
410 | 0 | loader->start_loading_next_url(); |
411 | 0 | } else { |
412 | | // FIXME: Don't know how to load fonts in workers! They don't have a StyleComputer |
413 | 0 | dbgln("FIXME: Worker font loading not implemented"); |
414 | 0 | } |
415 | 0 | }); |
416 | 0 | } |
417 | | |
418 | | } |