1use std::cell::Cell;
6use std::rc::Rc;
7
8use js::context::JSContext;
9use js::jsapi::{AddAssociatedMemory, Heap, JSObject, MemoryUse, RemoveAssociatedMemory};
10use js::rust::HandleObject;
11use malloc_size_of_derive::MallocSizeOf;
12
13use crate::conversions::DerivedFrom;
14use crate::interfaces::GlobalScopeHelpers;
15use crate::iterable::{Iterable, IterableIterator};
16use crate::realms::enter_auto_realm;
17use crate::root::{Dom, DomRoot, Root};
18use crate::{DomTypes, JSTraceable};
19
20pub trait AssociatedMemorySize: Default {
21 fn size(&self) -> usize;
22}
23
24impl AssociatedMemorySize for () {
25 fn size(&self) -> usize {
26 0
27 }
28}
29
30#[derive(Default, MallocSizeOf)]
31pub struct AssociatedMemory(Cell<usize>);
32
33impl AssociatedMemorySize for AssociatedMemory {
34 fn size(&self) -> usize {
35 self.0.get()
36 }
37}
38
39#[derive(MallocSizeOf)]
41#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
42pub struct Reflector<T = ()> {
44 #[ignore_malloc_size_of = "defined and measured in rust-mozjs"]
45 object: Heap<*mut JSObject>,
46 size: T,
48 proto_id: Cell<u16>,
50}
51
52unsafe impl<T> js::gc::Traceable for Reflector<T> {
53 unsafe fn trace(&self, tracer: *mut js::jsapi::JSTracer) {
54 unsafe {
55 self.object.trace(tracer);
56 }
57 }
58}
59
60impl<T> PartialEq for Reflector<T> {
61 fn eq(&self, other: &Reflector<T>) -> bool {
62 self.object.get() == other.object.get()
63 }
64}
65
66impl<T> Reflector<T> {
67 #[inline]
69 pub fn get_jsobject(&self) -> HandleObject<'_> {
70 unsafe { HandleObject::from_raw(self.object.handle()) }
72 }
73
74 #[inline]
76 pub fn proto_id(&self) -> u16 {
77 self.proto_id.get()
78 }
79
80 #[inline]
82 pub fn set_proto_id(&self, id: u16) {
83 self.proto_id.set(id);
84 }
85
86 unsafe fn set_jsobject(&self, object: *mut JSObject) {
92 assert!(self.object.get().is_null());
93 assert!(!object.is_null());
94 self.object.set(object);
95 }
96
97 pub fn rootable(&self) -> &Heap<*mut JSObject> {
101 &self.object
102 }
103}
104
105impl<T: AssociatedMemorySize> Reflector<T> {
106 #[expect(clippy::new_without_default)]
109 pub fn new() -> Reflector<T> {
110 Reflector {
111 object: Heap::default(),
112 proto_id: Cell::new(u16::MAX),
113 size: T::default(),
114 }
115 }
116
117 pub fn rust_size<D>(&self, _: &D) -> usize {
118 size_of::<D>() + size_of::<Box<D>>() + self.size.size()
119 }
120
121 pub fn drop_memory<D>(&self, d: &D) {
123 unsafe {
124 RemoveAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
125 }
126 }
127}
128
129impl Reflector<AssociatedMemory> {
130 pub fn update_memory_size<D>(&self, d: &D, new_size: usize) {
132 if self.size.size() == new_size {
133 return;
134 }
135 unsafe {
136 RemoveAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
137 self.size.0.set(new_size);
138 AddAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
139 }
140 }
141}
142
143pub trait DomObject: js::gc::Traceable + 'static {
145 type ReflectorType: AssociatedMemorySize;
146 fn reflector(&self) -> &Reflector<Self::ReflectorType>;
148}
149
150impl DomObject for Reflector<()> {
151 type ReflectorType = ();
152
153 fn reflector(&self) -> &Reflector<Self::ReflectorType> {
154 self
155 }
156}
157
158impl DomObject for Reflector<AssociatedMemory> {
159 type ReflectorType = AssociatedMemory;
160
161 fn reflector(&self) -> &Reflector<Self::ReflectorType> {
162 self
163 }
164}
165
166pub trait MutDomObject: DomObject {
168 unsafe fn init_reflector<D>(&self, obj: *mut JSObject);
175
176 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject);
182}
183
184impl MutDomObject for Reflector<()> {
185 unsafe fn init_reflector<D>(&self, obj: *mut JSObject) {
186 unsafe {
187 js::jsapi::AddAssociatedMemory(
188 obj,
189 size_of::<D>() + size_of::<Box<D>>(),
190 MemoryUse::DOMBinding,
191 );
192 self.init_reflector_without_associated_memory(obj);
193 }
194 }
195
196 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject) {
197 unsafe {
198 self.set_jsobject(obj);
199 }
200 }
201}
202
203impl MutDomObject for Reflector<AssociatedMemory> {
204 unsafe fn init_reflector<D>(&self, obj: *mut JSObject) {
205 unsafe {
206 js::jsapi::AddAssociatedMemory(
207 obj,
208 size_of::<D>() + size_of::<Box<D>>(),
209 MemoryUse::DOMBinding,
210 );
211 self.init_reflector_without_associated_memory(obj);
212 }
213 }
214
215 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject) {
216 unsafe {
217 self.set_jsobject(obj);
218 }
219 }
220}
221
222pub trait DomGlobalGeneric<D: DomTypes>: DomObject {
223 fn global_from_reflector(&self) -> DomRoot<D::GlobalScope>
227 where
228 Self: Sized,
229 {
230 let cx = unsafe { JSContext::get_from_thread() };
241 let cx = &mut cx.expect("JS runtime has shut down");
242 let _realm = enter_auto_realm::<D>(cx, self);
243 D::GlobalScope::from_reflector(self)
244 }
245}
246
247impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {}
248
249pub trait DomObjectWrap<D: DomTypes>: Sized + DomObject + DomGlobalGeneric<D> {
251 #[expect(clippy::type_complexity)]
253 const WRAP: unsafe fn(
254 &mut JSContext,
255 &D::GlobalScope,
256 Option<HandleObject>,
257 Box<Self>,
258 ) -> Root<Dom<Self>>;
259}
260
261pub trait WeakReferenceableDomObjectWrap<D: DomTypes>:
263 Sized + DomObject + DomGlobalGeneric<D>
264{
265 #[expect(clippy::type_complexity)]
267 const WRAP: unsafe fn(
268 &mut js::context::JSContext,
269 &D::GlobalScope,
270 Option<HandleObject>,
271 Rc<Self>,
272 ) -> Root<Dom<Self>>;
273}
274
275pub trait DomObjectIteratorWrap<D: DomTypes>: DomObjectWrap<D> + JSTraceable + Iterable {
278 #[expect(clippy::type_complexity)]
280 const ITER_WRAP: unsafe fn(
281 &mut JSContext,
282 &D::GlobalScope,
283 Option<HandleObject>,
284 Box<IterableIterator<D, Self>>,
285 ) -> Root<Dom<IterableIterator<D, Self>>>;
286}
287
288pub fn reflect_dom_object<D, T, U>(cx: &mut JSContext, obj: Box<T>, global: &U) -> DomRoot<T>
291where
292 D: DomTypes,
293 T: DomObject + DomObjectWrap<D>,
294 U: DerivedFrom<D::GlobalScope>,
295{
296 let global_scope = global.upcast();
297 unsafe { T::WRAP(cx, global_scope, None, obj) }
298}
299
300pub fn reflect_dom_object_with_proto<D, T, U>(
301 cx: &mut JSContext,
302 obj: Box<T>,
303 global: &U,
304 proto: Option<HandleObject>,
305) -> DomRoot<T>
306where
307 D: DomTypes,
308 T: DomObject + DomObjectWrap<D>,
309 U: DerivedFrom<D::GlobalScope>,
310{
311 let global_scope = global.upcast();
312 unsafe { T::WRAP(cx, global_scope, proto, obj) }
313}
314
315pub fn reflect_dom_object_with_cx<D, T, U>(
319 obj: Box<T>,
320 global: &U,
321 cx: &mut JSContext,
322) -> DomRoot<T>
323where
324 D: DomTypes,
325 T: DomObject + DomObjectWrap<D>,
326 U: DerivedFrom<D::GlobalScope>,
327{
328 let global_scope = global.upcast();
329 unsafe { T::WRAP(cx, global_scope, None, obj) }
330}
331
332pub fn reflect_weak_referenceable_dom_object<D, T, U>(
335 cx: &mut JSContext,
336 obj: Rc<T>,
337 global: &U,
338) -> DomRoot<T>
339where
340 D: DomTypes,
341 T: DomObject + WeakReferenceableDomObjectWrap<D>,
342 U: DerivedFrom<D::GlobalScope>,
343{
344 let global_scope = global.upcast();
345 unsafe { T::WRAP(cx, global_scope, None, obj) }
346}
347
348pub fn reflect_weak_referenceable_dom_object_with_proto<D, T, U>(
349 cx: &mut JSContext,
350 obj: Rc<T>,
351 global: &U,
352 proto: Option<HandleObject>,
353) -> DomRoot<T>
354where
355 D: DomTypes,
356 T: DomObject + WeakReferenceableDomObjectWrap<D>,
357 U: DerivedFrom<D::GlobalScope>,
358{
359 let global_scope = global.upcast();
360 unsafe { T::WRAP(cx, global_scope, proto, obj) }
361}
362
363type WrapFn<D, AbstractType> = unsafe fn(
364 &mut js::context::JSContext,
365 &<D as DomTypes>::GlobalScope,
366 Option<HandleObject>,
367 Box<AbstractType>,
368) -> DomRoot<AbstractType>;
369
370pub fn reflect_dom_object_with_proto_and_wrap<D, AbstractType, GlobalType>(
373 obj: Box<AbstractType>,
374 global: &GlobalType,
375 proto: Option<HandleObject>,
376 cx: &mut js::context::JSContext,
377 wrap: WrapFn<D, AbstractType>,
378) -> DomRoot<AbstractType>
379where
380 D: DomTypes,
381 AbstractType: DomObject,
382 GlobalType: DerivedFrom<D::GlobalScope>,
383 Box<AbstractType>: From<Box<AbstractType>>,
384 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
385{
386 let global_scope = global.upcast();
387 unsafe { wrap(cx, global_scope, proto, obj) }
388}
389
390pub fn reflect_dom_object_with_wrap<D, AbstractType, GlobalType>(
393 obj: Box<AbstractType>,
394 global: &GlobalType,
395 cx: &mut js::context::JSContext,
396 wrap: WrapFn<D, AbstractType>,
397) -> DomRoot<AbstractType>
398where
399 D: DomTypes,
400 AbstractType: DomObject,
401 GlobalType: DerivedFrom<D::GlobalScope>,
402 Box<AbstractType>: From<Box<AbstractType>>,
403 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
404{
405 let global_scope = global.upcast();
406 unsafe { wrap(cx, global_scope, None, obj) }
407}
408
409type WrapFnRc<D, AbstractType> = unsafe fn(
410 &mut js::context::JSContext,
411 &<D as DomTypes>::GlobalScope,
412 Option<HandleObject>,
413 Rc<AbstractType>,
414) -> DomRoot<AbstractType>;
415
416pub fn reflect_weak_referenceable_dom_object_with_cx_and_wrap<D, AbstractType, GlobalType>(
419 cx: &mut JSContext,
420 obj: Rc<AbstractType>,
421 global: &GlobalType,
422 wrap: WrapFnRc<D, AbstractType>,
423) -> DomRoot<AbstractType>
424where
425 D: DomTypes,
426 AbstractType: DomObject,
427 GlobalType: DerivedFrom<D::GlobalScope>,
428 Rc<AbstractType>: From<Rc<AbstractType>>,
429 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
430{
431 let global_scope = global.upcast();
432 unsafe { wrap(cx, global_scope, None, obj) }
433}