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::root::{Dom, DomRoot, Root};
17use crate::{DomTypes, JSTraceable};
18
19pub trait AssociatedMemorySize: Default {
20 fn size(&self) -> usize;
21}
22
23impl AssociatedMemorySize for () {
24 fn size(&self) -> usize {
25 0
26 }
27}
28
29#[derive(Default, MallocSizeOf)]
30pub struct AssociatedMemory(Cell<usize>);
31
32impl AssociatedMemorySize for AssociatedMemory {
33 fn size(&self) -> usize {
34 self.0.get()
35 }
36}
37
38#[derive(MallocSizeOf)]
40#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
41pub struct Reflector<T = ()> {
43 #[ignore_malloc_size_of = "defined and measured in rust-mozjs"]
44 object: Heap<*mut JSObject>,
45 size: T,
47 proto_id: Cell<u16>,
49}
50
51unsafe impl<T> js::gc::Traceable for Reflector<T> {
52 unsafe fn trace(&self, _: *mut js::jsapi::JSTracer) {}
53}
54
55impl<T> PartialEq for Reflector<T> {
56 fn eq(&self, other: &Reflector<T>) -> bool {
57 self.object.get() == other.object.get()
58 }
59}
60
61impl<T> Reflector<T> {
62 #[inline]
64 pub fn get_jsobject(&self) -> HandleObject<'_> {
65 unsafe { HandleObject::from_raw(self.object.handle()) }
67 }
68
69 #[inline]
71 pub fn proto_id(&self) -> u16 {
72 self.proto_id.get()
73 }
74
75 #[inline]
77 pub fn set_proto_id(&self, id: u16) {
78 self.proto_id.set(id);
79 }
80
81 unsafe fn set_jsobject(&self, object: *mut JSObject) {
87 assert!(self.object.get().is_null());
88 assert!(!object.is_null());
89 self.object.set(object);
90 }
91
92 pub fn rootable(&self) -> &Heap<*mut JSObject> {
96 &self.object
97 }
98}
99
100impl<T: AssociatedMemorySize> Reflector<T> {
101 #[expect(clippy::new_without_default)]
104 pub fn new() -> Reflector<T> {
105 Reflector {
106 object: Heap::default(),
107 proto_id: Cell::new(u16::MAX),
108 size: T::default(),
109 }
110 }
111
112 pub fn rust_size<D>(&self, _: &D) -> usize {
113 size_of::<D>() + size_of::<Box<D>>() + self.size.size()
114 }
115
116 pub fn drop_memory<D>(&self, d: &D) {
118 unsafe {
119 RemoveAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
120 }
121 }
122}
123
124impl Reflector<AssociatedMemory> {
125 pub fn update_memory_size<D>(&self, d: &D, new_size: usize) {
127 if self.size.size() == new_size {
128 return;
129 }
130 unsafe {
131 RemoveAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
132 self.size.0.set(new_size);
133 AddAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
134 }
135 }
136}
137
138pub trait DomObject: js::gc::Traceable + 'static {
140 type ReflectorType: AssociatedMemorySize;
141 fn reflector(&self) -> &Reflector<Self::ReflectorType>;
143}
144
145impl DomObject for Reflector<()> {
146 type ReflectorType = ();
147
148 fn reflector(&self) -> &Reflector<Self::ReflectorType> {
149 self
150 }
151}
152
153impl DomObject for Reflector<AssociatedMemory> {
154 type ReflectorType = AssociatedMemory;
155
156 fn reflector(&self) -> &Reflector<Self::ReflectorType> {
157 self
158 }
159}
160
161pub trait MutDomObject: DomObject {
163 unsafe fn init_reflector<D>(&self, obj: *mut JSObject);
170
171 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject);
177}
178
179impl MutDomObject for Reflector<()> {
180 unsafe fn init_reflector<D>(&self, obj: *mut JSObject) {
181 unsafe {
182 js::jsapi::AddAssociatedMemory(
183 obj,
184 size_of::<D>() + size_of::<Box<D>>(),
185 MemoryUse::DOMBinding,
186 );
187 self.init_reflector_without_associated_memory(obj);
188 }
189 }
190
191 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject) {
192 unsafe {
193 self.set_jsobject(obj);
194 }
195 }
196}
197
198impl MutDomObject for Reflector<AssociatedMemory> {
199 unsafe fn init_reflector<D>(&self, obj: *mut JSObject) {
200 unsafe {
201 js::jsapi::AddAssociatedMemory(
202 obj,
203 size_of::<D>() + size_of::<Box<D>>(),
204 MemoryUse::DOMBinding,
205 );
206 self.init_reflector_without_associated_memory(obj);
207 }
208 }
209
210 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject) {
211 unsafe {
212 self.set_jsobject(obj);
213 }
214 }
215}
216
217pub trait DomGlobalGeneric<D: DomTypes>: DomObject {
218 fn global_from_reflector(&self) -> DomRoot<D::GlobalScope>
222 where
223 Self: Sized,
224 {
225 D::GlobalScope::from_reflector(self)
226 }
227}
228
229impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {}
230
231pub trait DomObjectWrap<D: DomTypes>: Sized + DomObject + DomGlobalGeneric<D> {
233 #[expect(clippy::type_complexity)]
235 const WRAP: unsafe fn(
236 &mut JSContext,
237 &D::GlobalScope,
238 Option<HandleObject>,
239 Box<Self>,
240 ) -> Root<Dom<Self>>;
241}
242
243pub trait WeakReferenceableDomObjectWrap<D: DomTypes>:
245 Sized + DomObject + DomGlobalGeneric<D>
246{
247 #[expect(clippy::type_complexity)]
249 const WRAP: unsafe fn(
250 &mut js::context::JSContext,
251 &D::GlobalScope,
252 Option<HandleObject>,
253 Rc<Self>,
254 ) -> Root<Dom<Self>>;
255}
256
257pub trait DomObjectIteratorWrap<D: DomTypes>: DomObjectWrap<D> + JSTraceable + Iterable {
260 #[expect(clippy::type_complexity)]
262 const ITER_WRAP: unsafe fn(
263 &mut JSContext,
264 &D::GlobalScope,
265 Option<HandleObject>,
266 Box<IterableIterator<D, Self>>,
267 ) -> Root<Dom<IterableIterator<D, Self>>>;
268}
269
270pub fn reflect_dom_object<D, T, U>(cx: &mut JSContext, obj: Box<T>, global: &U) -> DomRoot<T>
273where
274 D: DomTypes,
275 T: DomObject + DomObjectWrap<D>,
276 U: DerivedFrom<D::GlobalScope>,
277{
278 let global_scope = global.upcast();
279 unsafe { T::WRAP(cx, global_scope, None, obj) }
280}
281
282pub fn reflect_dom_object_with_proto<D, T, U>(
283 cx: &mut JSContext,
284 obj: Box<T>,
285 global: &U,
286 proto: Option<HandleObject>,
287) -> DomRoot<T>
288where
289 D: DomTypes,
290 T: DomObject + DomObjectWrap<D>,
291 U: DerivedFrom<D::GlobalScope>,
292{
293 let global_scope = global.upcast();
294 unsafe { T::WRAP(cx, global_scope, proto, obj) }
295}
296
297pub fn reflect_dom_object_with_cx<D, T, U>(
301 obj: Box<T>,
302 global: &U,
303 cx: &mut JSContext,
304) -> DomRoot<T>
305where
306 D: DomTypes,
307 T: DomObject + DomObjectWrap<D>,
308 U: DerivedFrom<D::GlobalScope>,
309{
310 let global_scope = global.upcast();
311 unsafe { T::WRAP(cx, global_scope, None, obj) }
312}
313
314pub fn reflect_dom_object_with_proto_and_cx<D, T, U>(
318 obj: Box<T>,
319 global: &U,
320 proto: Option<HandleObject>,
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, proto, 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}