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