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::new(cx, global);
191 font_status_promise.reject_error(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::new(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 = reflect_dom_object_with_proto(
303 Box::new(Self::new_inherited(
304 cx,
305 global,
306 font_family,
307 url_source,
308 descriptors,
309 )),
310 global,
311 proto,
312 CanGc::from_cx(cx),
313 );
314
315 if font_face.Status() == FontFaceLoadStatus::Error {
316 return font_face;
317 }
318
319 let font_face_bytes = match source {
324 StringOrArrayBufferViewOrArrayBuffer::String(_) => {
325 return font_face;
326 },
327 StringOrArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
328 StringOrArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
329 };
330
331 let trusted_font_face = Trusted::new(&*font_face);
332 let trusted_global = Trusted::new(global);
333 global
334 .task_manager()
335 .font_loading_task_source()
336 .queue(task!(
337 load_font_from_arraybuffer: move |cx| {
338 let font_face = trusted_font_face.root();
339 let global = trusted_global.root();
340
341 font_face.load_from_data(cx, &global, font_face_bytes);
342 }
343 ));
344
345 font_face
346 }
347
348 fn load_from_data(&self, cx: &mut JSContext, global: &GlobalScope, data: Vec<u8>) {
350 self.status.set(FontFaceLoadStatus::Loading);
352
353 if let Some(font_face_set) = self.font_face_set.get() {
355 font_face_set.handle_font_face_status_changed(cx, self);
356 }
357
358 let parsed_font_face_rule = self.font_face_rule(global);
362 let result = parsed_font_face_rule
363 .ok()
364 .and_then(|parsed_font_face_rule| {
365 global
366 .as_window()
367 .font_context()
368 .construct_web_font_from_data(&data, (&parsed_font_face_rule).into())
369 });
370
371 if let Some(template) = result {
372 self.font_status_promise.resolve_native(cx, &self);
375 self.status.set(FontFaceLoadStatus::Loaded);
376 *self.template.borrow_mut() = Some(template);
377
378 if let Some(font_face_set) = self.font_face_set.get() {
380 font_face_set.handle_font_face_status_changed(cx, self);
385 }
386 } else {
387 self.font_status_promise
390 .reject_error(cx, Error::Syntax(None));
391 self.status.set(FontFaceLoadStatus::Error);
392
393 if let Some(font_face_set) = self.font_face_set.get() {
395 font_face_set.handle_font_face_status_changed(cx, self);
400 }
401 }
402 }
403
404 pub(super) fn set_associated_font_face_set(&self, font_face_set: &FontFaceSet) {
405 self.font_face_set.set(Some(font_face_set));
406 }
407
408 pub(super) fn template(&self) -> Option<(LowercaseFontFamilyName, FontTemplate)> {
409 self.template.borrow().clone()
410 }
411
412 fn validate_and_set_descriptors(&self, new_descriptors: FontFaceDescriptors) -> ErrorResult {
419 let global = self.global();
420 let parsed_font_face_rule = parse_font_face_descriptors(
421 &global,
422 &self.family_name.borrow(),
423 None,
424 &new_descriptors,
425 )?;
426
427 *self.descriptors.borrow_mut() =
428 serialize_parsed_descriptors(&parsed_font_face_rule.descriptors);
429 Ok(())
430 }
431
432 fn font_face_rule(&self, global: &GlobalScope) -> Fallible<FontFaceRule> {
433 parse_font_face_descriptors(
436 global,
437 &self.family_name.borrow(),
438 None,
439 &self.descriptors.borrow(),
440 )
441 }
442}
443
444impl FontFaceMethods<crate::DomTypeHolder> for FontFace {
445 fn Family(&self) -> DOMString {
447 self.family_name.borrow().clone()
448 }
449
450 fn SetFamily(&self, family_name: DOMString) -> ErrorResult {
452 let descriptors = self.descriptors.borrow();
453 let global = self.global();
454 let _ = parse_font_face_descriptors(&global, &family_name, None, &descriptors)?;
455 *self.family_name.borrow_mut() = family_name;
456 Ok(())
457 }
458
459 fn Style(&self) -> DOMString {
461 self.descriptors.borrow().style.clone()
462 }
463
464 fn SetStyle(&self, value: DOMString) -> ErrorResult {
466 let mut new_descriptors = self.descriptors.borrow().clone();
467 new_descriptors.style = value;
468 self.validate_and_set_descriptors(new_descriptors)
469 }
470
471 fn Weight(&self) -> DOMString {
473 self.descriptors.borrow().weight.clone()
474 }
475
476 fn SetWeight(&self, value: DOMString) -> ErrorResult {
478 let mut new_descriptors = self.descriptors.borrow().clone();
479 new_descriptors.weight = value;
480 self.validate_and_set_descriptors(new_descriptors)
481 }
482
483 fn Stretch(&self) -> DOMString {
485 self.descriptors.borrow().stretch.clone()
486 }
487
488 fn SetStretch(&self, value: DOMString) -> ErrorResult {
490 let mut new_descriptors = self.descriptors.borrow().clone();
491 new_descriptors.stretch = value;
492 self.validate_and_set_descriptors(new_descriptors)
493 }
494
495 fn UnicodeRange(&self) -> DOMString {
497 self.descriptors.borrow().unicodeRange.clone()
498 }
499
500 fn SetUnicodeRange(&self, value: DOMString) -> ErrorResult {
502 let mut new_descriptors = self.descriptors.borrow().clone();
503 new_descriptors.unicodeRange = value;
504 self.validate_and_set_descriptors(new_descriptors)
505 }
506
507 fn FeatureSettings(&self) -> DOMString {
509 self.descriptors.borrow().featureSettings.clone()
510 }
511
512 fn SetFeatureSettings(&self, value: DOMString) -> ErrorResult {
514 let mut new_descriptors = self.descriptors.borrow().clone();
515 new_descriptors.featureSettings = value;
516 self.validate_and_set_descriptors(new_descriptors)
517 }
518
519 fn VariationSettings(&self) -> DOMString {
521 self.descriptors.borrow().variationSettings.clone()
522 }
523
524 fn SetVariationSettings(&self, value: DOMString) -> ErrorResult {
526 let mut new_descriptors = self.descriptors.borrow().clone();
527 new_descriptors.variationSettings = value;
528 self.validate_and_set_descriptors(new_descriptors)
529 }
530
531 fn Display(&self) -> DOMString {
533 self.descriptors.borrow().display.clone()
534 }
535
536 fn SetDisplay(&self, value: DOMString) -> ErrorResult {
538 let mut new_descriptors = self.descriptors.borrow().clone();
539 new_descriptors.display = value;
540 self.validate_and_set_descriptors(new_descriptors)
541 }
542
543 fn AscentOverride(&self) -> DOMString {
545 self.descriptors.borrow().ascentOverride.clone()
546 }
547
548 fn SetAscentOverride(&self, value: DOMString) -> ErrorResult {
550 let mut new_descriptors = self.descriptors.borrow().clone();
551 new_descriptors.ascentOverride = value;
552 self.validate_and_set_descriptors(new_descriptors)
553 }
554
555 fn DescentOverride(&self) -> DOMString {
557 self.descriptors.borrow().descentOverride.clone()
558 }
559
560 fn SetDescentOverride(&self, value: DOMString) -> ErrorResult {
562 let mut new_descriptors = self.descriptors.borrow().clone();
563 new_descriptors.descentOverride = value;
564 self.validate_and_set_descriptors(new_descriptors)
565 }
566
567 fn LineGapOverride(&self) -> DOMString {
569 self.descriptors.borrow().lineGapOverride.clone()
570 }
571
572 fn SetLineGapOverride(&self, value: DOMString) -> ErrorResult {
574 let mut new_descriptors = self.descriptors.borrow().clone();
575 new_descriptors.lineGapOverride = value;
576 self.validate_and_set_descriptors(new_descriptors)
577 }
578
579 fn Status(&self) -> FontFaceLoadStatus {
581 self.status.get()
582 }
583
584 fn Load(&self, cx: &mut JSContext) -> Rc<Promise> {
589 let Some(sources) = self.urls.borrow_mut().take() else {
590 return self.font_status_promise.clone();
594 };
595
596 debug_assert_eq!(self.status.get(), FontFaceLoadStatus::Unloaded);
600
601 let global = self.global();
602 let trusted = Trusted::new(self);
603 let task_source = global
604 .task_manager()
605 .font_loading_task_source()
606 .to_sendable();
607
608 let finished_callback = Box::new(
609 move |family_name: LowercaseFontFamilyName, load_result: Option<_>| {
610 let trusted = trusted.clone();
611
612 task_source.queue(task!(resolve_font_face_load_task: move |cx| {
615 let font_face = trusted.root();
616
617 match load_result {
618 None => {
619 font_face.status.set(FontFaceLoadStatus::Error);
623 font_face.font_status_promise.reject_error(cx, Error::Network(None));
624 }
625 Some(template) => {
626 font_face.status.set(FontFaceLoadStatus::Loaded);
630 let old_template = font_face.template.borrow_mut().replace((family_name, template));
631 debug_assert!(old_template.is_none(), "FontFace's template must be intialized only once");
632 font_face.font_status_promise.resolve_native(cx, &font_face);
633 }
634 }
635
636 if let Some(font_face_set) = font_face.font_face_set.get() {
637 font_face_set.handle_font_face_status_changed(cx, &font_face);
643 }
644 }));
645 },
646 );
647
648 let parsed_font_face_rule = self
651 .font_face_rule(&global)
652 .expect("Parsing shouldn't fail as descriptors are valid by construction");
653
654 let document_context = global.as_window().web_font_context();
656
657 global.as_window().font_context().load_web_font_for_script(
662 global.webview_id(),
663 sources,
664 (&parsed_font_face_rule).into(),
665 finished_callback,
666 &document_context,
667 );
668
669 self.status.set(FontFaceLoadStatus::Loading);
672
673 if let Some(font_face_set) = self.font_face_set.get() {
677 font_face_set.handle_font_face_status_changed(cx, self);
678 }
679
680 self.font_status_promise.clone()
681 }
682
683 fn Loaded(&self) -> Rc<Promise> {
685 self.font_status_promise.clone()
686 }
687
688 fn Constructor(
690 cx: &mut JSContext,
691 window: &Window,
692 proto: Option<HandleObject>,
693 family: DOMString,
694 source: UnionTypes::StringOrArrayBufferViewOrArrayBuffer,
695 descriptors: &FontFaceDescriptors,
696 ) -> DomRoot<FontFace> {
697 let global = window.as_global_scope();
698 FontFace::new(cx, global, proto, family, source, descriptors)
699 }
700}