script/dom/
fontfaceset.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use fonts::FontContextWebFontMethods;
9use js::rust::HandleObject;
10
11use super::bindings::reflector::DomGlobal;
12use super::types::Window;
13use crate::dom::bindings::codegen::Bindings::FontFaceSetBinding::FontFaceSetMethods;
14use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
15use crate::dom::bindings::refcounted::TrustedPromise;
16use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
17use crate::dom::bindings::root::DomRoot;
18use crate::dom::bindings::str::DOMString;
19use crate::dom::eventtarget::EventTarget;
20use crate::dom::fontface::FontFace;
21use crate::dom::globalscope::GlobalScope;
22use crate::dom::promise::Promise;
23use crate::script_runtime::CanGc;
24
25/// <https://drafts.csswg.org/css-font-loading/#FontFaceSet-interface>
26#[dom_struct]
27pub(crate) struct FontFaceSet {
28    target: EventTarget,
29
30    /// <https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-readypromise-slot>
31    #[ignore_malloc_size_of = "Rc"]
32    promise: Rc<Promise>,
33}
34
35impl FontFaceSet {
36    fn new_inherited(global: &GlobalScope, can_gc: CanGc) -> Self {
37        FontFaceSet {
38            target: EventTarget::new_inherited(),
39            promise: Promise::new(global, can_gc),
40        }
41    }
42
43    pub(crate) fn new(
44        global: &GlobalScope,
45        proto: Option<HandleObject>,
46        can_gc: CanGc,
47    ) -> DomRoot<Self> {
48        reflect_dom_object_with_proto(
49            Box::new(FontFaceSet::new_inherited(global, can_gc)),
50            global,
51            proto,
52            can_gc,
53        )
54    }
55
56    pub(super) fn handle_font_face_status_changed(&self, font_face: &FontFace) {
57        if font_face.loaded() {
58            let Some(window) = DomRoot::downcast::<Window>(self.global()) else {
59                return;
60            };
61
62            let (family_name, template) = font_face
63                .template()
64                .expect("A loaded web font should have a template");
65            window
66                .font_context()
67                .add_template_to_font_context(family_name, template);
68            window.Document().dirty_all_nodes();
69        }
70    }
71
72    /// Fulfill the font ready promise, returning true if it was not already fulfilled beforehand.
73    pub(crate) fn fulfill_ready_promise_if_needed(&self, can_gc: CanGc) -> bool {
74        if self.promise.is_fulfilled() {
75            return false;
76        }
77        self.promise.resolve_native(self, can_gc);
78        true
79    }
80
81    pub(crate) fn waiting_to_fullfill_promise(&self) -> bool {
82        !self.promise.is_fulfilled()
83    }
84}
85
86impl FontFaceSetMethods<crate::DomTypeHolder> for FontFaceSet {
87    /// <https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-ready>
88    fn Ready(&self) -> Rc<Promise> {
89        self.promise.clone()
90    }
91
92    /// <https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-add>
93    fn Add(&self, font_face: &FontFace) -> DomRoot<FontFaceSet> {
94        font_face.set_associated_font_face_set(self);
95        self.handle_font_face_status_changed(font_face);
96        DomRoot::from_ref(self)
97    }
98
99    /// <https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-load>
100    fn Load(&self, _font: DOMString, _text: DOMString, can_gc: CanGc) -> Rc<Promise> {
101        // Step 1. Let font face set be the FontFaceSet object this method was called on. Let
102        // promise be a newly-created promise object.
103        let promise = Promise::new(&self.global(), can_gc);
104
105        // TODO: Step 3. Find the matching font faces from font face set using the font and text
106        // arguments passed to the function, and let font face list be the return value (ignoring
107        // the found faces flag). If a syntax error was returned, reject promise with a SyntaxError
108        // exception and terminate these steps.
109
110        let trusted = TrustedPromise::new(promise.clone());
111        // Step 4. Queue a task to run the following steps synchronously:
112        self.global()
113            .task_manager()
114            .font_loading_task_source()
115            .queue(task!(resolve_font_face_set_load_task: move || {
116                let promise = trusted.root();
117
118                // TODO: Step 4.1. For all of the font faces in the font face list, call their load()
119                // method.
120
121                // TODO: Step 4.2. Resolve promise with the result of waiting for all of the
122                // [[FontStatusPromise]]s of each font face in the font face list, in order.
123                let matched_fonts = Vec::<&FontFace>::new();
124                promise.resolve_native(&matched_fonts, CanGc::note());
125            }));
126
127        // Step 2. Return promise. Complete the rest of these steps asynchronously.
128        promise
129    }
130}