script/dom/css/
fontfaceset.rs1use std::cell::RefCell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::gc::Handle;
11use js::jsapi::Value;
12use js::realm::CurrentRealm;
13use js::rust::HandleObject;
14use script_bindings::cell::DomRefCell;
15use script_bindings::codegen::GenericBindings::FontFaceBinding::{
16 FontFaceLoadStatus, FontFaceMethods,
17};
18use script_bindings::like::Setlike;
19use script_bindings::reflector::reflect_dom_object_with_proto_and_cx;
20
21use crate::dom::bindings::codegen::Bindings::FontFaceSetBinding::FontFaceSetMethods;
22use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
23use crate::dom::bindings::refcounted::TrustedPromise;
24use crate::dom::bindings::reflector::DomGlobal;
25use crate::dom::bindings::root::{Dom, DomRoot};
26use crate::dom::bindings::str::DOMString;
27use crate::dom::eventtarget::EventTarget;
28use crate::dom::fontface::FontFace;
29use crate::dom::globalscope::GlobalScope;
30use crate::dom::promise::Promise;
31use crate::dom::promisenativehandler::Callback;
32use crate::dom::types::PromiseNativeHandler;
33use crate::dom::window::Window;
34use crate::realms::enter_auto_realm;
35
36#[dom_struct]
38pub(crate) struct FontFaceSet {
39 target: EventTarget,
40
41 #[conditional_malloc_size_of]
43 promise: RefCell<Rc<Promise>>,
44
45 set_entries: DomRefCell<Vec<Dom<FontFace>>>,
46}
47
48impl FontFaceSet {
49 fn new_inherited(cx: &mut JSContext, global: &GlobalScope) -> Self {
50 FontFaceSet {
51 target: EventTarget::new_inherited(),
52 promise: Promise::new(cx, global).into(),
53 set_entries: Default::default(),
54 }
55 }
56
57 pub(crate) fn new(
58 cx: &mut JSContext,
59 global: &GlobalScope,
60 proto: Option<HandleObject>,
61 ) -> DomRoot<Self> {
62 reflect_dom_object_with_proto_and_cx(
63 Box::new(FontFaceSet::new_inherited(cx, global)),
64 global,
65 proto,
66 cx,
67 )
68 }
69
70 pub(super) fn handle_font_face_status_changed(&self, cx: &mut JSContext, font_face: &FontFace) {
71 match font_face.Status() {
72 FontFaceLoadStatus::Loading => {
73 self.switch_to_loading(cx);
74 },
75 FontFaceLoadStatus::Loaded => {
76 let Some(window) = DomRoot::downcast::<Window>(self.global()) else {
77 return;
78 };
79
80 let (family_name, template) = font_face
81 .template()
82 .expect("A loaded web font should have a template");
83 window
84 .font_context()
85 .add_template_to_font_context(family_name, template);
86 window.Document().dirty_all_nodes();
87 },
88 _ => {},
89 }
90 }
91
92 pub(crate) fn fulfill_ready_promise_if_needed(&self, cx: &mut JSContext) -> bool {
94 let promise = self.promise.borrow().clone();
95 if promise.is_fulfilled() {
96 return false;
97 }
98 promise.resolve_native(cx, self);
99 true
100 }
101
102 pub(crate) fn waiting_to_fullfill_promise(&self) -> bool {
103 !self.promise.borrow().is_fulfilled()
104 }
105
106 fn contains_face(&self, target: &FontFace) -> bool {
107 self.set_entries
108 .borrow()
109 .iter()
110 .any(|face| &**face == target)
111 }
112
113 fn delete_face(&self, target: &FontFace) -> bool {
115 let mut set_entries = self.set_entries.borrow_mut();
116 let Some(index) = set_entries.iter().position(|face| &**face == target) else {
117 return false;
118 };
119 set_entries.remove(index);
120 true
121 }
122
123 pub(crate) fn switch_to_loading(&self, cx: &mut JSContext) {
125 if self.promise.borrow().is_fulfilled() {
134 *self.promise.borrow_mut() = Promise::new(cx, &self.global());
135 }
136
137 }
140}
141
142impl FontFaceSetMethods<crate::DomTypeHolder> for FontFaceSet {
143 fn Ready(&self) -> Rc<Promise> {
145 self.promise.borrow().clone()
146 }
147
148 fn Add(&self, cx: &mut JSContext, font_face: &FontFace) -> DomRoot<FontFaceSet> {
150 if self.contains_face(font_face) {
153 return DomRoot::from_ref(self);
154 }
155
156 self.set_entries.borrow_mut().push(Dom::from_ref(font_face));
161 font_face.set_associated_font_face_set(self);
162
163 self.handle_font_face_status_changed(cx, font_face);
167
168 DomRoot::from_ref(self)
170 }
171
172 fn Delete(&self, to_delete: &FontFace) -> bool {
174 self.delete_face(to_delete)
182 }
183
184 fn Clear(&self) {
186 self.set_entries.borrow_mut().clear();
189
190 }
193
194 fn Load(&self, cx: &mut JSContext, _font: DOMString, _text: DOMString) -> Rc<Promise> {
196 let load_promise = Promise::new(cx, &self.global());
199
200 #[derive(MallocSizeOf, JSTraceable)]
208 struct LoadPromiseFulfillmentHandler {
209 #[conditional_malloc_size_of]
210 load_promise: Rc<Promise>,
211 }
212 impl Callback for LoadPromiseFulfillmentHandler {
213 fn callback(&self, cx: &mut CurrentRealm, _: Handle<Value>) {
214 self.load_promise
215 .resolve_native(cx, &Vec::<&FontFace>::new());
216 }
217 }
218
219 let trusted_ready_promise = TrustedPromise::new(self.promise.borrow().clone());
221 let trusted_load_promise = TrustedPromise::new(load_promise.clone());
222 self.global()
223 .task_manager()
224 .font_loading_task_source()
225 .queue(task!(resolve_font_face_set_load_task: move |cx| {
226 let ready_promise = trusted_ready_promise.root();
227 let load_promise = trusted_load_promise.root();
228
229 let global = ready_promise.global();
241 let handler = PromiseNativeHandler::new(
242 cx,
243 &global,
244 Some(Box::new(LoadPromiseFulfillmentHandler {
245 load_promise,
246 })),
247 None,
248 );
249
250 let mut realm = enter_auto_realm(cx, &*global);
251 ready_promise.append_native_handler(&mut realm.current_realm(), &handler);
252 }));
253
254 load_promise
256 }
257
258 fn Size(&self) -> u32 {
260 self.set_entries.borrow().len() as u32
261 }
262}
263
264impl Setlike for FontFaceSet {
265 type Key = DomRoot<FontFace>;
266
267 #[inline(always)]
268 fn get_index(&self, index: u32) -> Option<Self::Key> {
269 self.set_entries
270 .borrow()
271 .get(index as usize)
272 .map(|face| face.as_rooted())
273 }
274
275 #[inline(always)]
276 fn size(&self) -> u32 {
277 self.set_entries.borrow().len() as u32
278 }
279
280 #[inline(always)]
281 fn add(&self, face: Self::Key) {
282 self.set_entries.borrow_mut().push(face.as_traced());
283 }
284
285 #[inline(always)]
286 fn has(&self, target: Self::Key) -> bool {
287 self.contains_face(&target)
288 }
289
290 #[inline(always)]
291 fn clear(&self) {
292 self.set_entries.borrow_mut().clear();
293 }
294
295 #[inline(always)]
296 fn delete(&self, to_delete: Self::Key) -> bool {
297 self.delete_face(&to_delete)
298 }
299}