1use std::default::Default;
8use std::ffi::CString;
9use std::mem::drop;
10use std::rc::Rc;
11
12use js::jsapi::{
13 AddRawValueRoot, EnterRealm, Heap, IsCallable, JSObject, LeaveRealm, Realm, RemoveRawValueRoot,
14};
15use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue};
16use js::rust::wrappers::{JS_GetProperty, JS_WrapObject};
17use js::rust::{HandleObject, MutableHandleValue, Runtime};
18
19use crate::DomTypes;
20use crate::codegen::GenericBindings::WindowBinding::Window_Binding::WindowMethods;
21use crate::error::{Error, Fallible};
22use crate::inheritance::Castable;
23use crate::interfaces::{DocumentHelpers, DomHelpers, GlobalScopeHelpers};
24use crate::realms::{InRealm, enter_realm};
25use crate::reflector::DomObject;
26use crate::root::{Dom, DomRoot};
27use crate::script_runtime::{CanGc, JSContext};
28use crate::settings_stack::{GenericAutoEntryScript, GenericAutoIncumbentScript};
29use crate::utils::AsCCharPtrPtr;
30
31pub trait ThisReflector {
32 fn jsobject(&self) -> *mut JSObject;
33}
34
35impl<T: DomObject> ThisReflector for T {
36 fn jsobject(&self) -> *mut JSObject {
37 self.reflector().get_jsobject().get()
38 }
39}
40
41impl ThisReflector for HandleObject<'_> {
42 fn jsobject(&self) -> *mut JSObject {
43 self.get()
44 }
45}
46
47#[derive(Clone, Copy, PartialEq)]
49pub enum ExceptionHandling {
50 Report,
52 Rethrow,
54}
55
56#[derive(JSTraceable, MallocSizeOf)]
59#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
60pub struct CallbackObject<D: DomTypes> {
61 #[ignore_malloc_size_of = "measured by mozjs"]
63 callback: Heap<*mut JSObject>,
64 #[ignore_malloc_size_of = "measured by mozjs"]
65 permanent_js_root: Heap<JSVal>,
66
67 incumbent: Option<Dom<D::GlobalScope>>,
79}
80
81impl<D: DomTypes> CallbackObject<D> {
82 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
83 #[allow(clippy::new_without_default)]
85 fn new() -> Self {
86 Self {
87 callback: Heap::default(),
88 permanent_js_root: Heap::default(),
89 incumbent: D::GlobalScope::incumbent().map(|i| Dom::from_ref(&*i)),
90 }
91 }
92
93 pub fn get(&self) -> *mut JSObject {
94 self.callback.get()
95 }
96
97 #[allow(unsafe_code)]
98 unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) {
99 self.callback.set(callback);
100 self.permanent_js_root.set(ObjectValue(callback));
101 unsafe {
102 assert!(AddRawValueRoot(
103 *cx,
104 self.permanent_js_root.get_unsafe(),
105 b"CallbackObject::root\n".as_c_char_ptr()
106 ));
107 }
108 }
109}
110
111impl<D: DomTypes> Drop for CallbackObject<D> {
112 #[allow(unsafe_code)]
113 fn drop(&mut self) {
114 unsafe {
115 if let Some(cx) = Runtime::get() {
116 RemoveRawValueRoot(cx.as_ptr(), self.permanent_js_root.get_unsafe());
117 }
118 }
119 }
120}
121
122impl<D: DomTypes> PartialEq for CallbackObject<D> {
123 fn eq(&self, other: &CallbackObject<D>) -> bool {
124 self.callback.get() == other.callback.get()
125 }
126}
127
128pub trait CallbackContainer<D: DomTypes> {
131 unsafe fn new(cx: JSContext, callback: *mut JSObject) -> Rc<Self>;
136 fn callback_holder(&self) -> &CallbackObject<D>;
138 fn callback(&self) -> *mut JSObject {
140 self.callback_holder().get()
141 }
142 fn incumbent(&self) -> Option<&D::GlobalScope> {
147 self.callback_holder().incumbent.as_deref()
148 }
149}
150
151#[derive(JSTraceable, MallocSizeOf, PartialEq)]
153#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
154pub struct CallbackFunction<D: DomTypes> {
155 object: CallbackObject<D>,
156}
157
158impl<D: DomTypes> CallbackFunction<D> {
159 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
161 #[allow(clippy::new_without_default)]
163 pub fn new() -> Self {
164 Self {
165 object: CallbackObject::new(),
166 }
167 }
168
169 pub fn callback_holder(&self) -> &CallbackObject<D> {
171 &self.object
172 }
173
174 pub unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) {
180 unsafe { self.object.init(cx, callback) };
181 }
182}
183
184#[derive(JSTraceable, MallocSizeOf, PartialEq)]
186#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
187pub struct CallbackInterface<D: DomTypes> {
188 object: CallbackObject<D>,
189}
190
191impl<D: DomTypes> CallbackInterface<D> {
192 #[allow(clippy::new_without_default)]
195 pub fn new() -> Self {
196 Self {
197 object: CallbackObject::new(),
198 }
199 }
200
201 pub fn callback_holder(&self) -> &CallbackObject<D> {
203 &self.object
204 }
205
206 pub unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) {
212 unsafe { self.object.init(cx, callback) };
213 }
214
215 pub fn get_callable_property(&self, cx: JSContext, name: &str) -> Fallible<JSVal> {
218 rooted!(in(*cx) let mut callable = UndefinedValue());
219 rooted!(in(*cx) let obj = self.callback_holder().get());
220 unsafe {
221 let c_name = CString::new(name).unwrap();
222 if !JS_GetProperty(*cx, obj.handle(), c_name.as_ptr(), callable.handle_mut()) {
223 return Err(Error::JSFailed);
224 }
225
226 if !callable.is_object() || !IsCallable(callable.to_object()) {
227 return Err(Error::Type(format!(
228 "The value of the {} property is not callable",
229 name
230 )));
231 }
232 }
233 Ok(callable.get())
234 }
235}
236
237pub(crate) fn wrap_call_this_value<T: ThisReflector>(
239 cx: JSContext,
240 p: &T,
241 mut rval: MutableHandleValue,
242) -> bool {
243 rooted!(in(*cx) let mut obj = p.jsobject());
244
245 if obj.is_null() {
246 rval.set(NullValue());
247 return true;
248 }
249
250 unsafe {
251 if !JS_WrapObject(*cx, obj.handle_mut()) {
252 return false;
253 }
254 }
255
256 rval.set(ObjectValue(*obj));
257 true
258}
259
260pub struct CallSetup<D: DomTypes> {
263 exception_global: DomRoot<D::GlobalScope>,
266 cx: JSContext,
268 old_realm: *mut Realm,
270 handling: ExceptionHandling,
272 entry_script: Option<GenericAutoEntryScript<D>>,
275 incumbent_script: Option<GenericAutoIncumbentScript<D>>,
278}
279
280impl<D: DomTypes> CallSetup<D> {
281 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
283 pub fn new<T: CallbackContainer<D>>(callback: &T, handling: ExceptionHandling) -> Self {
284 let global = unsafe { D::GlobalScope::from_object(callback.callback()) };
285 if let Some(window) = global.downcast::<D::Window>() {
286 window.Document().ensure_safe_to_run_script_or_layout();
287 }
288 let cx = D::GlobalScope::get_cx();
289
290 let aes = GenericAutoEntryScript::<D>::new(&global);
291 let ais = callback.incumbent().map(GenericAutoIncumbentScript::new);
292 CallSetup {
293 exception_global: global,
294 cx,
295 old_realm: unsafe { EnterRealm(*cx, callback.callback()) },
296 handling,
297 entry_script: Some(aes),
298 incumbent_script: ais,
299 }
300 }
301
302 pub fn get_context(&self) -> JSContext {
304 self.cx
305 }
306}
307
308impl<D: DomTypes> Drop for CallSetup<D> {
309 fn drop(&mut self) {
310 unsafe {
311 LeaveRealm(*self.cx, self.old_realm);
312 }
313 if self.handling == ExceptionHandling::Report {
314 let ar = enter_realm::<D>(&*self.exception_global);
315 <D as DomHelpers<D>>::report_pending_exception(
316 self.cx,
317 true,
318 InRealm::Entered(&ar),
319 CanGc::note(),
320 );
321 }
322 drop(self.incumbent_script.take());
323 drop(self.entry_script.take().unwrap());
324 }
325}