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