1use std::default::Default;
8use std::ffi::CStr;
9use std::rc::Rc;
10
11use js::context::JSContext;
12use js::jsapi::{Heap, IsCallable, JSObject};
13use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue};
14use js::rust::wrappers2::{EnterRealm, JS_WrapObject, LeaveRealm};
15use js::rust::{HandleObject, MutableHandleValue};
16
17use crate::codegen::GenericBindings::WindowBinding::Window_Binding::WindowMethods;
18use crate::error::{Error, Fallible};
19use crate::interfaces::{DocumentHelpers, DomHelpers, GlobalScopeHelpers};
20use crate::js::JS_GetProperty;
21use crate::permanent_root::PermanentRoot;
22use crate::realms::enter_auto_realm;
23use crate::reflector::DomObject;
24use crate::root::Dom;
25use crate::settings_stack::{run_a_callback, run_a_script};
26use crate::{DomTypes, cformat};
27
28pub trait ThisReflector {
29 fn jsobject(&self) -> *mut JSObject;
30}
31
32pub trait OwnerWindow<D: DomTypes> {
38 fn owner_window(&self) -> Option<crate::root::DomRoot<D::Window>> {
39 None
40 }
41}
42
43impl<T: DomObject> ThisReflector for T {
44 fn jsobject(&self) -> *mut JSObject {
45 self.reflector().get_jsobject().get()
46 }
47}
48
49impl ThisReflector for HandleObject<'_> {
50 fn jsobject(&self) -> *mut JSObject {
51 self.get()
52 }
53}
54
55impl<D: DomTypes> OwnerWindow<D> for HandleObject<'_> {}
56
57#[derive(Clone, Copy, PartialEq)]
59pub enum ExceptionHandling {
60 Report,
62 Rethrow,
64}
65
66pub struct RootedCallback<T>(Rc<(T, PermanentRoot)>);
68
69impl<D: DomTypes, T: HasCallbackHolder<D = D>> RootedCallback<T> {
70 pub fn to_traced(&self) -> TracedCallback<T>
72 where
73 T: for<'a> From<&'a CallbackObject<D>>,
74 {
75 let mut duplicate = Rc::new(T::from(self.callback_holder()));
76 match Rc::get_mut(&mut duplicate) {
78 Some(ref mut callback) => unsafe {
79 callback
80 .callback_holder_mut()
81 .init_callback(self.callback())
82 },
83 None => unreachable!(),
84 };
85 TracedCallback(duplicate)
86 }
87}
88
89impl<T> Clone for RootedCallback<T> {
90 fn clone(&self) -> Self {
91 Self(self.0.clone())
92 }
93}
94
95impl<T> std::ops::Deref for RootedCallback<T> {
96 type Target = T;
97 fn deref(&self) -> &Self::Target {
98 &self.0.0
99 }
100}
101
102impl<T: js::conversions::ToJSValConvertible> js::conversions::ToJSValConvertible
103 for RootedCallback<T>
104{
105 fn to_jsval(&self, cx: &mut JSContext, rval: MutableHandleValue<'_>) {
106 self.0.0.to_jsval(cx, rval)
107 }
108}
109
110#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
111#[derive(JSTraceable, MallocSizeOf, PartialEq)]
112pub struct TracedCallback<T>(#[conditional_malloc_size_of] Rc<T>);
115
116impl<T: crate::JSTraceable> js::gc::Rootable for TracedCallback<T> {}
117
118impl<T> Clone for TracedCallback<T> {
119 fn clone(&self) -> Self {
120 Self(self.0.clone())
121 }
122}
123
124impl<T> std::ops::Deref for TracedCallback<T> {
125 type Target = T;
126 fn deref(&self) -> &Self::Target {
127 &self.0
128 }
129}
130
131#[expect(unsafe_code)]
132pub(crate) unsafe fn create_callback_rooted<D: DomTypes, T: HasCallbackHolder<D = D>>(
133 cx: &JSContext,
134 obj: T,
135 callback: *mut JSObject,
136) -> RootedCallback<T> {
137 let mut ret = Rc::new((obj, PermanentRoot::default()));
138 let (callback2, permanent_root) = Rc::get_mut(&mut ret).unwrap();
139 unsafe {
140 callback2.callback_holder_mut().init_callback(callback);
141 permanent_root.init(cx, callback2.callback(), c"Callback::root");
142 };
143 RootedCallback(ret)
144}
145
146impl<T: CallbackContainer + HasCallbackHolder> TracedCallback<T> {
147 pub fn root(&self, cx: &JSContext) -> RootedCallback<T> {
148 unsafe { T::new(cx, self.callback()) }
150 }
151}
152
153#[derive(JSTraceable, MallocSizeOf)]
156#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
157pub struct CallbackObject<D: DomTypes> {
158 #[ignore_malloc_size_of = "measured by mozjs"]
160 callback: Heap<*mut JSObject>,
161 incumbent: Option<Dom<D::GlobalScope>>,
173}
174
175impl<D: DomTypes> CallbackObject<D> {
176 fn new_from_existing(other: &CallbackObject<D>) -> Self {
177 Self {
178 callback: Heap::default(),
179 incumbent: other.incumbent.clone(),
180 }
181 }
182
183 fn new_with_exterior_root() -> Self {
184 Self {
185 callback: Heap::default(),
186 incumbent: D::GlobalScope::incumbent().map(|i| Dom::from_ref(&*i)),
187 }
188 }
189
190 pub fn get(&self) -> *mut JSObject {
191 self.callback.get()
192 }
193
194 #[expect(unsafe_code)]
195 unsafe fn init_callback(&mut self, callback: *mut JSObject) {
196 self.callback.set(callback);
197 }
198}
199
200impl<D: DomTypes> PartialEq for CallbackObject<D> {
201 fn eq(&self, other: &CallbackObject<D>) -> bool {
202 self.callback.get() == other.callback.get()
203 }
204}
205
206pub trait HasCallbackHolder {
208 type D: DomTypes;
209
210 fn callback_holder(&self) -> &CallbackObject<Self::D>;
212 fn callback_holder_mut(&mut self) -> &mut CallbackObject<Self::D>;
214
215 fn callback(&self) -> *mut JSObject {
217 self.callback_holder().get()
218 }
219}
220
221pub trait CallbackContainer {
224 unsafe fn new(cx: &JSContext, callback: *mut JSObject) -> RootedCallback<Self>
229 where
230 Self: Sized;
231}
232
233#[derive(JSTraceable, MallocSizeOf, PartialEq)]
235#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
236pub struct CallbackFunction<D: DomTypes> {
237 object: CallbackObject<D>,
238}
239
240impl<'a, D: DomTypes> From<&'a CallbackObject<D>> for CallbackFunction<D> {
241 fn from(object: &'a CallbackObject<D>) -> Self {
242 Self {
243 object: CallbackObject::new_from_existing(object),
244 }
245 }
246}
247
248impl<D: DomTypes> CallbackFunction<D> {
249 pub(crate) fn new_with_exterior_root() -> Self {
252 Self {
253 object: CallbackObject::new_with_exterior_root(),
254 }
255 }
256}
257
258impl<D: DomTypes> HasCallbackHolder for CallbackFunction<D> {
259 type D = D;
260 fn callback_holder(&self) -> &CallbackObject<D> {
262 &self.object
263 }
264
265 fn callback_holder_mut(&mut self) -> &mut CallbackObject<D> {
266 &mut self.object
267 }
268}
269
270#[derive(JSTraceable, MallocSizeOf, PartialEq)]
272#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
273pub struct CallbackInterface<D: DomTypes> {
274 object: CallbackObject<D>,
275}
276
277impl<'a, D: DomTypes> From<&'a CallbackObject<D>> for CallbackInterface<D> {
278 fn from(object: &'a CallbackObject<D>) -> Self {
279 Self {
280 object: CallbackObject::new_from_existing(object),
281 }
282 }
283}
284
285impl<D: DomTypes> HasCallbackHolder for CallbackInterface<D> {
286 type D = D;
287 fn callback_holder(&self) -> &CallbackObject<D> {
289 &self.object
290 }
291
292 fn callback_holder_mut(&mut self) -> &mut CallbackObject<D> {
293 &mut self.object
294 }
295}
296
297impl<D: DomTypes> CallbackInterface<D> {
298 pub(crate) fn new_with_exterior_root() -> Self {
300 Self {
301 object: CallbackObject::new_with_exterior_root(),
302 }
303 }
304
305 pub fn get_callable_property(&self, cx: &mut JSContext, name: &CStr) -> Fallible<JSVal> {
308 rooted!(&in(cx) let mut callable = UndefinedValue());
309 rooted!(&in(cx) let obj = self.callback_holder().get());
310 unsafe {
311 if !JS_GetProperty(cx, obj.handle(), name, callable.handle_mut()) {
312 return Err(Error::JSFailed);
313 }
314
315 if !callable.is_object() || !IsCallable(callable.to_object()) {
316 return Err(Error::Type(cformat!(
317 "The value of the {} property is not callable",
318 name.to_string_lossy()
319 )));
320 }
321 }
322 Ok(callable.get())
323 }
324}
325
326pub(crate) fn wrap_call_this_value<T: ThisReflector>(
328 cx: &mut JSContext,
329 p: &T,
330 mut rval: MutableHandleValue,
331) -> bool {
332 rooted!(&in(cx) let mut obj = p.jsobject());
333
334 if obj.is_null() {
335 rval.set(NullValue());
336 return true;
337 }
338
339 unsafe {
340 if !JS_WrapObject(cx, obj.handle_mut()) {
341 return false;
342 }
343 }
344
345 rval.set(ObjectValue(*obj));
346 true
347}
348
349pub(crate) fn call_setup<D: DomTypes, T: HasCallbackHolder<D = D>, R>(
353 cx: &mut JSContext,
354 callback: &T,
355 owner_window: Option<&D::Window>,
356 handling: ExceptionHandling,
357 f: impl FnOnce(&mut JSContext) -> R,
358) -> R {
359 if let Some(window) = owner_window {
360 window.Document().ensure_safe_to_run_script_or_layout();
361 }
362
363 let global = unsafe { D::GlobalScope::from_object(callback.callback()) };
366 let global = &global;
367
368 run_a_script::<D, R, _>(cx, global, move |cx| {
370 let actual_callback = || {
371 let old_realm = unsafe { EnterRealm(cx, callback.callback()) };
372 let result = f(cx);
373 unsafe {
374 LeaveRealm(cx, old_realm);
375 }
376 if handling == ExceptionHandling::Report {
377 let mut realm = enter_auto_realm::<D>(cx, &**global);
378 let cx = &mut realm.current_realm();
379 <D as DomHelpers<D>>::report_pending_exception(cx);
380 }
381 result
382 };
383 if let Some(incumbent_global) = callback.callback_holder().incumbent.as_deref() {
384 run_a_callback::<D, R>(incumbent_global, actual_callback)
386 } else {
387 actual_callback()
388 }
389 }) }