1use std::cell::{Cell, RefCell};
6use std::rc::Rc;
7
8use cssparser::{Parser, ParserInput};
9use dom_struct::dom_struct;
10use fonts::{FontContext, FontContextWebFontMethods, FontTemplate, LowercaseFontFamilyName};
11use js::rust::HandleObject;
12use style::error_reporting::ParseErrorReporter;
13use style::font_face::SourceList;
14use style::stylesheets::{CssRuleType, FontFaceRule, UrlExtraData};
15use style_traits::{ParsingMode, ToCss};
16
17use crate::css::parser_context_for_document_with_reporter;
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::FontFaceBinding::{
20 FontFaceDescriptors, FontFaceLoadStatus, FontFaceMethods,
21};
22use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
23use crate::dom::bindings::codegen::UnionTypes;
24use crate::dom::bindings::codegen::UnionTypes::StringOrArrayBufferViewOrArrayBuffer;
25use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
26use crate::dom::bindings::refcounted::Trusted;
27use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
28use crate::dom::bindings::root::{DomRoot, MutNullableDom};
29use crate::dom::bindings::str::DOMString;
30use crate::dom::css::fontfaceset::FontFaceSet;
31use crate::dom::globalscope::GlobalScope;
32use crate::dom::node::NodeTraits;
33use crate::dom::promise::Promise;
34use crate::dom::window::Window;
35use crate::script_runtime::CanGc;
36
37#[dom_struct]
39pub struct FontFace {
40 reflector: Reflector,
41 status: Cell<FontFaceLoadStatus>,
42 family_name: DomRefCell<DOMString>,
43 descriptors: DomRefCell<FontFaceDescriptors>,
44
45 font_face_set: MutNullableDom<FontFaceSet>,
50
51 #[no_trace = "Does not contain managed objects"]
57 template: RefCell<Option<(LowercaseFontFamilyName, FontTemplate)>>,
58
59 #[no_trace = "Does not contain managed objects"]
60 urls: DomRefCell<Option<SourceList>>,
62
63 #[conditional_malloc_size_of]
65 font_status_promise: Rc<Promise>,
66}
67
68fn parse_font_face_descriptors(
74 global: &GlobalScope,
75 family_name: &DOMString,
76 sources: Option<&DOMString>,
77 input_descriptors: &FontFaceDescriptors,
78) -> Fallible<FontFaceRule> {
79 let window = global.as_window(); let document = window.Document();
81 let url_data = UrlExtraData(document.owner_global().api_base_url().get_arc());
82 let error_reporter = FontFaceErrorReporter {
83 not_encountered_error: Cell::new(true),
84 };
85 let parser_context = parser_context_for_document_with_reporter(
86 &document,
87 CssRuleType::FontFace,
88 ParsingMode::DEFAULT,
89 &url_data,
90 &error_reporter,
91 );
92
93 let FontFaceDescriptors {
94 ascentOverride,
95 descentOverride,
96 display,
97 featureSettings,
98 lineGapOverride,
99 stretch,
100 style,
101 unicodeRange,
102 variationSettings,
103 weight,
104 } = input_descriptors;
105
106 let maybe_sources = sources.map_or_else(String::new, |sources| format!("src: {sources};"));
107 let font_face_rule = format!(
108 r"
109 ascent-override: {ascentOverride};
110 descent-override: {descentOverride};
111 font-display: {display};
112 font-family: {family_name};
113 font-feature-settings: {featureSettings};
114 font-stretch: {stretch};
115 font-style: {style};
116 font-variation-settings: {variationSettings};
117 font-weight: {weight};
118 line-gap-override: {lineGapOverride};
119 unicode-range: {unicodeRange};
120 {maybe_sources}
121 "
122 );
123
124 let location = cssparser::SourceLocation { line: 0, column: 0 };
126 let mut input = ParserInput::new(&font_face_rule);
127 let mut parser = Parser::new(&mut input);
128 let mut parsed_font_face_rule =
129 style::font_face::parse_font_face_block(&parser_context, &mut parser, location);
130
131 if let Some(ref mut sources) = parsed_font_face_rule.sources {
132 let supported_sources: Vec<_> = sources
133 .0
134 .iter()
135 .rev()
136 .filter(FontContext::is_supported_web_font_source)
137 .cloned()
138 .collect();
139 if supported_sources.is_empty() {
140 error_reporter.not_encountered_error.set(false);
141 } else {
142 sources.0 = supported_sources;
143 }
144 }
145
146 if error_reporter.not_encountered_error.get() {
147 Ok(parsed_font_face_rule)
148 } else {
149 Err(Error::Syntax(None))
150 }
151}
152
153fn serialize_parsed_descriptors(font_face_rule: &FontFaceRule) -> FontFaceDescriptors {
154 FontFaceDescriptors {
155 ascentOverride: font_face_rule.ascent_override.to_css_string().into(),
156 descentOverride: font_face_rule.descent_override.to_css_string().into(),
157 display: font_face_rule.display.to_css_string().into(),
158 featureSettings: font_face_rule.feature_settings.to_css_string().into(),
159 lineGapOverride: font_face_rule.line_gap_override.to_css_string().into(),
160 stretch: font_face_rule.stretch.to_css_string().into(),
161 style: font_face_rule.style.to_css_string().into(),
162 unicodeRange: font_face_rule.unicode_range.to_css_string().into(),
163 variationSettings: font_face_rule.variation_settings.to_css_string().into(),
164 weight: font_face_rule.weight.to_css_string().into(),
165 }
166}
167
168struct FontFaceErrorReporter {
169 not_encountered_error: Cell<bool>,
170}
171
172impl ParseErrorReporter for FontFaceErrorReporter {
173 fn report_error(
174 &self,
175 _url: &UrlExtraData,
176 _location: cssparser::SourceLocation,
177 _error: style::error_reporting::ContextualParseError,
178 ) {
179 self.not_encountered_error.set(false);
180 }
181}
182
183impl FontFace {
184 fn new_failed_font_face(global: &GlobalScope, can_gc: CanGc) -> Self {
187 let font_status_promise = Promise::new(global, can_gc);
188 font_status_promise.reject_error(Error::Syntax(None), can_gc);
191
192 Self {
195 reflector: Reflector::new(),
196 font_face_set: MutNullableDom::default(),
197 font_status_promise,
198 family_name: DomRefCell::default(),
199 urls: DomRefCell::default(),
200 descriptors: DomRefCell::new(FontFaceDescriptors {
201 ascentOverride: DOMString::new(),
202 descentOverride: DOMString::new(),
203 display: DOMString::new(),
204 featureSettings: DOMString::new(),
205 lineGapOverride: DOMString::new(),
206 stretch: DOMString::new(),
207 style: DOMString::new(),
208 unicodeRange: DOMString::new(),
209 variationSettings: DOMString::new(),
210 weight: DOMString::new(),
211 }),
212 status: Cell::new(FontFaceLoadStatus::Error),
213 template: RefCell::default(),
214 }
215 }
216
217 fn new_inherited(
219 global: &GlobalScope,
220 family_name: DOMString,
221 source: StringOrArrayBufferViewOrArrayBuffer,
222 descriptors: &FontFaceDescriptors,
223 can_gc: CanGc,
224 ) -> Self {
225 let StringOrArrayBufferViewOrArrayBuffer::String(ref source_string) = source else {
227 return Self::new_failed_font_face(global, can_gc);
228 };
229
230 let parse_result =
235 parse_font_face_descriptors(global, &family_name, Some(source_string), descriptors);
236
237 let Ok(ref parsed_font_face_rule) = parse_result else {
238 return Self::new_failed_font_face(global, can_gc);
243 };
244
245 let font_status_promise = Promise::new(global, can_gc);
247
248 let sources = parsed_font_face_rule
249 .sources
250 .clone()
251 .expect("Sources should be non-None after validation");
252
253 Self {
255 reflector: Reflector::new(),
256
257 status: Cell::new(FontFaceLoadStatus::Unloaded),
259
260 descriptors: DomRefCell::new(serialize_parsed_descriptors(parsed_font_face_rule)),
262
263 font_face_set: MutNullableDom::default(),
264 family_name: DomRefCell::new(family_name.clone()),
265 urls: DomRefCell::new(Some(sources)),
266 template: RefCell::default(),
267 font_status_promise,
268 }
269 }
270
271 pub(crate) fn new(
272 global: &GlobalScope,
273 proto: Option<HandleObject>,
274 font_family: DOMString,
275 source: StringOrArrayBufferViewOrArrayBuffer,
276 descriptors: &FontFaceDescriptors,
277 can_gc: CanGc,
278 ) -> DomRoot<Self> {
279 reflect_dom_object_with_proto(
280 Box::new(Self::new_inherited(
281 global,
282 font_family,
283 source,
284 descriptors,
285 can_gc,
286 )),
287 global,
288 proto,
289 can_gc,
290 )
291 }
292
293 pub(super) fn set_associated_font_face_set(&self, font_face_set: &FontFaceSet) {
294 self.font_face_set.set(Some(font_face_set));
295 }
296
297 pub(super) fn loaded(&self) -> bool {
298 self.status.get() == FontFaceLoadStatus::Loaded
299 }
300
301 pub(super) fn template(&self) -> Option<(LowercaseFontFamilyName, FontTemplate)> {
302 self.template.borrow().clone()
303 }
304
305 fn validate_and_set_descriptors(&self, new_descriptors: FontFaceDescriptors) -> ErrorResult {
312 let global = self.global();
313 let parsed_font_face_rule = parse_font_face_descriptors(
314 &global,
315 &self.family_name.borrow(),
316 None,
317 &new_descriptors,
318 )?;
319
320 *self.descriptors.borrow_mut() = serialize_parsed_descriptors(&parsed_font_face_rule);
321 Ok(())
322 }
323}
324
325impl FontFaceMethods<crate::DomTypeHolder> for FontFace {
326 fn Family(&self) -> DOMString {
328 self.family_name.borrow().clone()
329 }
330
331 fn SetFamily(&self, family_name: DOMString) -> ErrorResult {
333 let descriptors = self.descriptors.borrow();
334 let global = self.global();
335 let _ = parse_font_face_descriptors(&global, &family_name, None, &descriptors)?;
336 *self.family_name.borrow_mut() = family_name;
337 Ok(())
338 }
339
340 fn Style(&self) -> DOMString {
342 self.descriptors.borrow().style.clone()
343 }
344
345 fn SetStyle(&self, value: DOMString) -> ErrorResult {
347 let mut new_descriptors = self.descriptors.borrow().clone();
348 new_descriptors.style = value;
349 self.validate_and_set_descriptors(new_descriptors)
350 }
351
352 fn Weight(&self) -> DOMString {
354 self.descriptors.borrow().weight.clone()
355 }
356
357 fn SetWeight(&self, value: DOMString) -> ErrorResult {
359 let mut new_descriptors = self.descriptors.borrow().clone();
360 new_descriptors.weight = value;
361 self.validate_and_set_descriptors(new_descriptors)
362 }
363
364 fn Stretch(&self) -> DOMString {
366 self.descriptors.borrow().stretch.clone()
367 }
368
369 fn SetStretch(&self, value: DOMString) -> ErrorResult {
371 let mut new_descriptors = self.descriptors.borrow().clone();
372 new_descriptors.stretch = value;
373 self.validate_and_set_descriptors(new_descriptors)
374 }
375
376 fn UnicodeRange(&self) -> DOMString {
378 self.descriptors.borrow().unicodeRange.clone()
379 }
380
381 fn SetUnicodeRange(&self, value: DOMString) -> ErrorResult {
383 let mut new_descriptors = self.descriptors.borrow().clone();
384 new_descriptors.unicodeRange = value;
385 self.validate_and_set_descriptors(new_descriptors)
386 }
387
388 fn FeatureSettings(&self) -> DOMString {
390 self.descriptors.borrow().featureSettings.clone()
391 }
392
393 fn SetFeatureSettings(&self, value: DOMString) -> ErrorResult {
395 let mut new_descriptors = self.descriptors.borrow().clone();
396 new_descriptors.featureSettings = value;
397 self.validate_and_set_descriptors(new_descriptors)
398 }
399
400 fn VariationSettings(&self) -> DOMString {
402 self.descriptors.borrow().variationSettings.clone()
403 }
404
405 fn SetVariationSettings(&self, value: DOMString) -> ErrorResult {
407 let mut new_descriptors = self.descriptors.borrow().clone();
408 new_descriptors.variationSettings = value;
409 self.validate_and_set_descriptors(new_descriptors)
410 }
411
412 fn Display(&self) -> DOMString {
414 self.descriptors.borrow().display.clone()
415 }
416
417 fn SetDisplay(&self, value: DOMString) -> ErrorResult {
419 let mut new_descriptors = self.descriptors.borrow().clone();
420 new_descriptors.display = value;
421 self.validate_and_set_descriptors(new_descriptors)
422 }
423
424 fn AscentOverride(&self) -> DOMString {
426 self.descriptors.borrow().ascentOverride.clone()
427 }
428
429 fn SetAscentOverride(&self, value: DOMString) -> ErrorResult {
431 let mut new_descriptors = self.descriptors.borrow().clone();
432 new_descriptors.ascentOverride = value;
433 self.validate_and_set_descriptors(new_descriptors)
434 }
435
436 fn DescentOverride(&self) -> DOMString {
438 self.descriptors.borrow().descentOverride.clone()
439 }
440
441 fn SetDescentOverride(&self, value: DOMString) -> ErrorResult {
443 let mut new_descriptors = self.descriptors.borrow().clone();
444 new_descriptors.descentOverride = value;
445 self.validate_and_set_descriptors(new_descriptors)
446 }
447
448 fn LineGapOverride(&self) -> DOMString {
450 self.descriptors.borrow().lineGapOverride.clone()
451 }
452
453 fn SetLineGapOverride(&self, value: DOMString) -> ErrorResult {
455 let mut new_descriptors = self.descriptors.borrow().clone();
456 new_descriptors.lineGapOverride = value;
457 self.validate_and_set_descriptors(new_descriptors)
458 }
459
460 fn Status(&self) -> FontFaceLoadStatus {
462 self.status.get()
463 }
464
465 fn Load(&self) -> Rc<Promise> {
470 let Some(sources) = self.urls.borrow_mut().take() else {
471 return self.font_status_promise.clone();
475 };
476
477 debug_assert_eq!(self.status.get(), FontFaceLoadStatus::Unloaded);
481
482 let global = self.global();
483 let trusted = Trusted::new(self);
484 let task_source = global
485 .task_manager()
486 .font_loading_task_source()
487 .to_sendable();
488
489 let finished_callback = Box::new(
490 move |family_name: LowercaseFontFamilyName, load_result: Option<_>| {
491 let trusted = trusted.clone();
492
493 task_source.queue(task!(resolve_font_face_load_task: move || {
496 let font_face = trusted.root();
497
498 match load_result {
499 None => {
500 font_face.status.set(FontFaceLoadStatus::Error);
504 font_face.font_status_promise.reject_error(Error::Network(None), CanGc::note());
505 }
506 Some(template) => {
507 font_face.status.set(FontFaceLoadStatus::Loaded);
511 let old_template = font_face.template.borrow_mut().replace((family_name, template));
512 debug_assert!(old_template.is_none(), "FontFace's template must be intialized only once");
513 font_face.font_status_promise.resolve_native(&font_face, CanGc::note());
514 }
515 }
516
517 if let Some(font_face_set) = font_face.font_face_set.get() {
518 font_face_set.handle_font_face_status_changed(&font_face);
524 }
525 }));
526 },
527 );
528
529 let parsed_font_face_rule = parse_font_face_descriptors(
532 &global,
533 &self.family_name.borrow(),
534 None,
535 &self.descriptors.borrow(),
536 )
537 .expect("Parsing shouldn't fail as descriptors are valid by construction");
538
539 let document_context = global.as_window().web_font_context();
541
542 global.as_window().font_context().load_web_font_for_script(
547 global.webview_id(),
548 sources,
549 (&parsed_font_face_rule).into(),
550 finished_callback,
551 &document_context,
552 );
553
554 self.status.set(FontFaceLoadStatus::Loading);
557 self.font_status_promise.clone()
558 }
559
560 fn Loaded(&self) -> Rc<Promise> {
562 self.font_status_promise.clone()
563 }
564
565 fn Constructor(
567 window: &Window,
568 proto: Option<HandleObject>,
569 can_gc: CanGc,
570 family: DOMString,
571 source: UnionTypes::StringOrArrayBufferViewOrArrayBuffer,
572 descriptors: &FontFaceDescriptors,
573 ) -> DomRoot<FontFace> {
574 let global = window.as_global_scope();
575 FontFace::new(global, proto, family, source, descriptors, can_gc)
576 }
577}