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::context::JSContext;
12use js::rust::HandleObject;
13use script_bindings::cell::DomRefCell;
14use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
15use style::error_reporting::ParseErrorReporter;
16use style::font_face::SourceList;
17use style::properties::font_face::Descriptors;
18use style::stylesheets::{CssRuleType, FontFaceRule, UrlExtraData};
19use style_traits::{ParsingMode, ToCss};
20
21use crate::css::parser_context_for_document_with_reporter;
22use crate::dom::bindings::codegen::Bindings::FontFaceBinding::{
23 FontFaceDescriptors, FontFaceLoadStatus, FontFaceMethods,
24};
25use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
26use crate::dom::bindings::codegen::UnionTypes;
27use crate::dom::bindings::codegen::UnionTypes::StringOrArrayBufferViewOrArrayBuffer;
28use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
29use crate::dom::bindings::refcounted::Trusted;
30use crate::dom::bindings::reflector::DomGlobal;
31use crate::dom::bindings::root::{DomRoot, MutNullableDom};
32use crate::dom::bindings::str::DOMString;
33use crate::dom::css::fontfaceset::FontFaceSet;
34use crate::dom::globalscope::GlobalScope;
35use crate::dom::node::NodeTraits;
36use crate::dom::promise::Promise;
37use crate::dom::window::Window;
38use crate::script_runtime::CanGc;
39
40#[dom_struct]
42pub struct FontFace {
43 reflector: Reflector,
44 status: Cell<FontFaceLoadStatus>,
45 family_name: DomRefCell<DOMString>,
46 descriptors: DomRefCell<FontFaceDescriptors>,
47
48 font_face_set: MutNullableDom<FontFaceSet>,
53
54 #[no_trace = "Does not contain managed objects"]
60 template: RefCell<Option<(LowercaseFontFamilyName, FontTemplate)>>,
61
62 #[no_trace = "Does not contain managed objects"]
63 urls: DomRefCell<Option<SourceList>>,
65
66 #[conditional_malloc_size_of]
68 font_status_promise: Rc<Promise>,
69}
70
71fn parse_font_face_descriptors(
77 global: &GlobalScope,
78 family_name: &DOMString,
79 sources: Option<&DOMString>,
80 input_descriptors: &FontFaceDescriptors,
81) -> Fallible<FontFaceRule> {
82 let window = global.as_window(); let document = window.Document();
84 let url_data = UrlExtraData(document.owner_global().api_base_url().get_arc());
85 let error_reporter = FontFaceErrorReporter {
86 not_encountered_error: Cell::new(true),
87 };
88 let parser_context = parser_context_for_document_with_reporter(
89 &document,
90 CssRuleType::FontFace,
91 ParsingMode::DEFAULT,
92 &url_data,
93 &error_reporter,
94 );
95
96 let FontFaceDescriptors {
97 ascentOverride,
98 descentOverride,
99 display,
100 featureSettings,
101 lineGapOverride,
102 stretch,
103 style,
104 unicodeRange,
105 variationSettings,
106 weight,
107 } = input_descriptors;
108
109 let maybe_sources = sources.map_or_else(String::new, |sources| format!("src: {sources};"));
110 let font_face_rule = format!(
111 r"
112 ascent-override: {ascentOverride};
113 descent-override: {descentOverride};
114 font-display: {display};
115 font-family: {family_name};
116 font-feature-settings: {featureSettings};
117 font-stretch: {stretch};
118 font-style: {style};
119 font-variation-settings: {variationSettings};
120 font-weight: {weight};
121 line-gap-override: {lineGapOverride};
122 unicode-range: {unicodeRange};
123 {maybe_sources}
124 "
125 );
126
127 let location = cssparser::SourceLocation { line: 0, column: 0 };
129 let mut input = ParserInput::new(&font_face_rule);
130 let mut parser = Parser::new(&mut input);
131 let mut parsed_font_face_rule =
132 style::font_face::parse_font_face_block(&parser_context, &mut parser, location);
133
134 if let Some(ref mut sources) = parsed_font_face_rule.descriptors.src {
135 let supported_sources: Vec<_> = sources
136 .0
137 .iter()
138 .rev()
139 .filter(FontContext::is_supported_web_font_source)
140 .cloned()
141 .collect();
142 if supported_sources.is_empty() {
143 error_reporter.not_encountered_error.set(false);
144 } else {
145 sources.0 = supported_sources;
146 }
147 }
148
149 if error_reporter.not_encountered_error.get() {
150 Ok(parsed_font_face_rule)
151 } else {
152 Err(Error::Syntax(None))
153 }
154}
155
156fn serialize_parsed_descriptors(descriptors: &Descriptors) -> FontFaceDescriptors {
157 FontFaceDescriptors {
158 ascentOverride: descriptors.ascent_override.to_css_string().into(),
159 descentOverride: descriptors.descent_override.to_css_string().into(),
160 display: descriptors.font_display.to_css_string().into(),
161 featureSettings: descriptors.font_feature_settings.to_css_string().into(),
162 lineGapOverride: descriptors.line_gap_override.to_css_string().into(),
163 stretch: descriptors.font_stretch.to_css_string().into(),
164 style: descriptors.font_style.to_css_string().into(),
165 unicodeRange: descriptors.unicode_range.to_css_string().into(),
166 variationSettings: descriptors.font_variation_settings.to_css_string().into(),
167 weight: descriptors.font_weight.to_css_string().into(),
168 }
169}
170
171struct FontFaceErrorReporter {
172 not_encountered_error: Cell<bool>,
173}
174
175impl ParseErrorReporter for FontFaceErrorReporter {
176 fn report_error(
177 &self,
178 _url: &UrlExtraData,
179 _location: cssparser::SourceLocation,
180 _error: style::error_reporting::ContextualParseError,
181 ) {
182 self.not_encountered_error.set(false);
183 }
184}
185
186impl FontFace {
187 fn new_failed_font_face(cx: &mut JSContext, global: &GlobalScope) -> Self {
190 let font_status_promise = Promise::new2(cx, global);
191 font_status_promise.reject_error_with_cx(cx, Error::Syntax(None));
194
195 Self {
198 reflector: Reflector::new(),
199 font_face_set: MutNullableDom::default(),
200 font_status_promise,
201 family_name: DomRefCell::default(),
202 urls: Default::default(),
203 descriptors: DomRefCell::new(FontFaceDescriptors {
204 ascentOverride: DOMString::new(),
205 descentOverride: DOMString::new(),
206 display: DOMString::new(),
207 featureSettings: DOMString::new(),
208 lineGapOverride: DOMString::new(),
209 stretch: DOMString::new(),
210 style: DOMString::new(),
211 unicodeRange: DOMString::new(),
212 variationSettings: DOMString::new(),
213 weight: DOMString::new(),
214 }),
215 status: Cell::new(FontFaceLoadStatus::Error),
216 template: RefCell::default(),
217 }
218 }
219
220 fn new_inherited(
225 cx: &mut JSContext,
226 global: &GlobalScope,
227 family_name: DOMString,
228 source: Option<&DOMString>,
229 descriptors: &FontFaceDescriptors,
230 ) -> Self {
231 let parse_result = parse_font_face_descriptors(global, &family_name, source, descriptors);
236
237 let Ok(ref parsed_font_face_rule) = parse_result else {
238 return Self::new_failed_font_face(cx, global);
243 };
244
245 let font_status_promise = Promise::new2(cx, global);
247
248 let sources = parsed_font_face_rule.descriptors.src.clone();
249
250 Self {
252 reflector: Reflector::new(),
253
254 status: Cell::new(FontFaceLoadStatus::Unloaded),
256
257 descriptors: DomRefCell::new(serialize_parsed_descriptors(
259 &parsed_font_face_rule.descriptors,
260 )),
261
262 font_face_set: MutNullableDom::default(),
263 family_name: DomRefCell::new(family_name),
264 urls: DomRefCell::new(sources),
265 template: RefCell::default(),
266 font_status_promise,
267 }
268 }
269
270 pub(crate) fn new(
272 cx: &mut JSContext,
273 global: &GlobalScope,
274 proto: Option<HandleObject>,
275 font_family: DOMString,
276 source: StringOrArrayBufferViewOrArrayBuffer,
277 descriptors: &FontFaceDescriptors,
278 ) -> DomRoot<Self> {
279 let url_source = if let StringOrArrayBufferViewOrArrayBuffer::String(source) = &source {
280 Some(source)
281 } else {
282 None
283 };
284
285 let font_face_rule = reflect_dom_object_with_proto(
286 Box::new(Self::new_inherited(
287 cx,
288 global,
289 font_family,
290 url_source,
291 descriptors,
292 )),
293 global,
294 proto,
295 CanGc::from_cx(cx),
296 );
297
298 let font_face_bytes = match source {
303 StringOrArrayBufferViewOrArrayBuffer::String(_) => {
304 return font_face_rule;
305 },
306 StringOrArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
307 StringOrArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
308 };
309
310 let trusted_font_face_rule = Trusted::new(&*font_face_rule);
311 let trusted_global = Trusted::new(global);
312 global
313 .task_manager()
314 .font_loading_task_source()
315 .queue(task!(
316 load_font_from_arraybuffer: move |cx| {
317 let font_face_rule = trusted_font_face_rule.root();
318 let global = trusted_global.root();
319
320 font_face_rule.load_from_data(cx, &global, font_face_bytes);
321 }
322 ));
323
324 font_face_rule
325 }
326
327 fn load_from_data(&self, cx: &mut JSContext, global: &GlobalScope, data: Vec<u8>) {
329 let parsed_font_face_rule = self.font_face_rule(global);
330
331 self.status.set(FontFaceLoadStatus::Loading);
333
334 if let Some(font_face_set) = self.font_face_set.get() {
336 font_face_set.handle_font_face_status_changed(cx, self);
337 }
338
339 let result = global
343 .as_window()
344 .font_context()
345 .construct_web_font_from_data(&data, (&parsed_font_face_rule).into());
346
347 if let Some(template) = result {
348 self.font_status_promise
351 .resolve_native(&self, CanGc::deprecated_note());
352 self.status.set(FontFaceLoadStatus::Loaded);
353 *self.template.borrow_mut() = Some(template);
354
355 if let Some(font_face_set) = self.font_face_set.get() {
357 font_face_set.handle_font_face_status_changed(cx, self);
362 }
363 } else {
364 self.font_status_promise
367 .reject_error(Error::Syntax(None), CanGc::deprecated_note());
368 self.status.set(FontFaceLoadStatus::Error);
369
370 if let Some(font_face_set) = self.font_face_set.get() {
372 font_face_set.handle_font_face_status_changed(cx, self);
377 }
378 }
379 }
380
381 pub(super) fn set_associated_font_face_set(&self, font_face_set: &FontFaceSet) {
382 self.font_face_set.set(Some(font_face_set));
383 }
384
385 pub(super) fn template(&self) -> Option<(LowercaseFontFamilyName, FontTemplate)> {
386 self.template.borrow().clone()
387 }
388
389 fn validate_and_set_descriptors(&self, new_descriptors: FontFaceDescriptors) -> ErrorResult {
396 let global = self.global();
397 let parsed_font_face_rule = parse_font_face_descriptors(
398 &global,
399 &self.family_name.borrow(),
400 None,
401 &new_descriptors,
402 )?;
403
404 *self.descriptors.borrow_mut() =
405 serialize_parsed_descriptors(&parsed_font_face_rule.descriptors);
406 Ok(())
407 }
408
409 fn font_face_rule(&self, global: &GlobalScope) -> FontFaceRule {
410 parse_font_face_descriptors(
413 global,
414 &self.family_name.borrow(),
415 None,
416 &self.descriptors.borrow(),
417 )
418 .expect("Parsing shouldn't fail as descriptors are valid by construction")
419 }
420}
421
422impl FontFaceMethods<crate::DomTypeHolder> for FontFace {
423 fn Family(&self) -> DOMString {
425 self.family_name.borrow().clone()
426 }
427
428 fn SetFamily(&self, family_name: DOMString) -> ErrorResult {
430 let descriptors = self.descriptors.borrow();
431 let global = self.global();
432 let _ = parse_font_face_descriptors(&global, &family_name, None, &descriptors)?;
433 *self.family_name.borrow_mut() = family_name;
434 Ok(())
435 }
436
437 fn Style(&self) -> DOMString {
439 self.descriptors.borrow().style.clone()
440 }
441
442 fn SetStyle(&self, value: DOMString) -> ErrorResult {
444 let mut new_descriptors = self.descriptors.borrow().clone();
445 new_descriptors.style = value;
446 self.validate_and_set_descriptors(new_descriptors)
447 }
448
449 fn Weight(&self) -> DOMString {
451 self.descriptors.borrow().weight.clone()
452 }
453
454 fn SetWeight(&self, value: DOMString) -> ErrorResult {
456 let mut new_descriptors = self.descriptors.borrow().clone();
457 new_descriptors.weight = value;
458 self.validate_and_set_descriptors(new_descriptors)
459 }
460
461 fn Stretch(&self) -> DOMString {
463 self.descriptors.borrow().stretch.clone()
464 }
465
466 fn SetStretch(&self, value: DOMString) -> ErrorResult {
468 let mut new_descriptors = self.descriptors.borrow().clone();
469 new_descriptors.stretch = value;
470 self.validate_and_set_descriptors(new_descriptors)
471 }
472
473 fn UnicodeRange(&self) -> DOMString {
475 self.descriptors.borrow().unicodeRange.clone()
476 }
477
478 fn SetUnicodeRange(&self, value: DOMString) -> ErrorResult {
480 let mut new_descriptors = self.descriptors.borrow().clone();
481 new_descriptors.unicodeRange = value;
482 self.validate_and_set_descriptors(new_descriptors)
483 }
484
485 fn FeatureSettings(&self) -> DOMString {
487 self.descriptors.borrow().featureSettings.clone()
488 }
489
490 fn SetFeatureSettings(&self, value: DOMString) -> ErrorResult {
492 let mut new_descriptors = self.descriptors.borrow().clone();
493 new_descriptors.featureSettings = value;
494 self.validate_and_set_descriptors(new_descriptors)
495 }
496
497 fn VariationSettings(&self) -> DOMString {
499 self.descriptors.borrow().variationSettings.clone()
500 }
501
502 fn SetVariationSettings(&self, value: DOMString) -> ErrorResult {
504 let mut new_descriptors = self.descriptors.borrow().clone();
505 new_descriptors.variationSettings = value;
506 self.validate_and_set_descriptors(new_descriptors)
507 }
508
509 fn Display(&self) -> DOMString {
511 self.descriptors.borrow().display.clone()
512 }
513
514 fn SetDisplay(&self, value: DOMString) -> ErrorResult {
516 let mut new_descriptors = self.descriptors.borrow().clone();
517 new_descriptors.display = value;
518 self.validate_and_set_descriptors(new_descriptors)
519 }
520
521 fn AscentOverride(&self) -> DOMString {
523 self.descriptors.borrow().ascentOverride.clone()
524 }
525
526 fn SetAscentOverride(&self, value: DOMString) -> ErrorResult {
528 let mut new_descriptors = self.descriptors.borrow().clone();
529 new_descriptors.ascentOverride = value;
530 self.validate_and_set_descriptors(new_descriptors)
531 }
532
533 fn DescentOverride(&self) -> DOMString {
535 self.descriptors.borrow().descentOverride.clone()
536 }
537
538 fn SetDescentOverride(&self, value: DOMString) -> ErrorResult {
540 let mut new_descriptors = self.descriptors.borrow().clone();
541 new_descriptors.descentOverride = value;
542 self.validate_and_set_descriptors(new_descriptors)
543 }
544
545 fn LineGapOverride(&self) -> DOMString {
547 self.descriptors.borrow().lineGapOverride.clone()
548 }
549
550 fn SetLineGapOverride(&self, value: DOMString) -> ErrorResult {
552 let mut new_descriptors = self.descriptors.borrow().clone();
553 new_descriptors.lineGapOverride = value;
554 self.validate_and_set_descriptors(new_descriptors)
555 }
556
557 fn Status(&self) -> FontFaceLoadStatus {
559 self.status.get()
560 }
561
562 fn Load(&self, cx: &mut JSContext) -> Rc<Promise> {
567 let Some(sources) = self.urls.borrow_mut().take() else {
568 return self.font_status_promise.clone();
572 };
573
574 debug_assert_eq!(self.status.get(), FontFaceLoadStatus::Unloaded);
578
579 let global = self.global();
580 let trusted = Trusted::new(self);
581 let task_source = global
582 .task_manager()
583 .font_loading_task_source()
584 .to_sendable();
585
586 let finished_callback = Box::new(
587 move |family_name: LowercaseFontFamilyName, load_result: Option<_>| {
588 let trusted = trusted.clone();
589
590 task_source.queue(task!(resolve_font_face_load_task: move |cx| {
593 let font_face = trusted.root();
594
595 match load_result {
596 None => {
597 font_face.status.set(FontFaceLoadStatus::Error);
601 font_face.font_status_promise.reject_error_with_cx(cx, Error::Network(None));
602 }
603 Some(template) => {
604 font_face.status.set(FontFaceLoadStatus::Loaded);
608 let old_template = font_face.template.borrow_mut().replace((family_name, template));
609 debug_assert!(old_template.is_none(), "FontFace's template must be intialized only once");
610 font_face.font_status_promise.resolve_native_with_cx(cx, &font_face);
611 }
612 }
613
614 if let Some(font_face_set) = font_face.font_face_set.get() {
615 font_face_set.handle_font_face_status_changed(cx, &font_face);
621 }
622 }));
623 },
624 );
625
626 let parsed_font_face_rule = self.font_face_rule(&global);
629
630 let document_context = global.as_window().web_font_context();
632
633 global.as_window().font_context().load_web_font_for_script(
638 global.webview_id(),
639 sources,
640 (&parsed_font_face_rule).into(),
641 finished_callback,
642 &document_context,
643 );
644
645 self.status.set(FontFaceLoadStatus::Loading);
648
649 if let Some(font_face_set) = self.font_face_set.get() {
653 font_face_set.handle_font_face_status_changed(cx, self);
654 }
655
656 self.font_status_promise.clone()
657 }
658
659 fn Loaded(&self) -> Rc<Promise> {
661 self.font_status_promise.clone()
662 }
663
664 fn Constructor(
666 cx: &mut JSContext,
667 window: &Window,
668 proto: Option<HandleObject>,
669 family: DOMString,
670 source: UnionTypes::StringOrArrayBufferViewOrArrayBuffer,
671 descriptors: &FontFaceDescriptors,
672 ) -> DomRoot<FontFace> {
673 let global = window.as_global_scope();
674 FontFace::new(cx, global, proto, family, source, descriptors)
675 }
676}