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_weak_referenceable_dom_object<D, T, U>(
317 cx: &mut JSContext,
318 obj: Rc<T>,
319 global: &U,
320) -> DomRoot<T>
321where
322 D: DomTypes,
323 T: DomObject + WeakReferenceableDomObjectWrap<D>,
324 U: DerivedFrom<D::GlobalScope>,
325{
326 let global_scope = global.upcast();
327 unsafe { T::WRAP(cx, global_scope, None, obj) }
328}
329
330pub fn reflect_weak_referenceable_dom_object_with_proto<D, T, U>(
331 cx: &mut JSContext,
332 obj: Rc<T>,
333 global: &U,
334 proto: Option<HandleObject>,
335) -> DomRoot<T>
336where
337 D: DomTypes,
338 T: DomObject + WeakReferenceableDomObjectWrap<D>,
339 U: DerivedFrom<D::GlobalScope>,
340{
341 let global_scope = global.upcast();
342 unsafe { T::WRAP(cx, global_scope, proto, obj) }
343}
344
345type WrapFn<D, AbstractType> = unsafe fn(
346 &mut js::context::JSContext,
347 &<D as DomTypes>::GlobalScope,
348 Option<HandleObject>,
349 Box<AbstractType>,
350) -> DomRoot<AbstractType>;
351
352pub fn reflect_dom_object_with_proto_and_wrap<D, AbstractType, GlobalType>(
355 obj: Box<AbstractType>,
356 global: &GlobalType,
357 proto: Option<HandleObject>,
358 cx: &mut js::context::JSContext,
359 wrap: WrapFn<D, AbstractType>,
360) -> DomRoot<AbstractType>
361where
362 D: DomTypes,
363 AbstractType: DomObject,
364 GlobalType: DerivedFrom<D::GlobalScope>,
365 Box<AbstractType>: From<Box<AbstractType>>,
366 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
367{
368 let global_scope = global.upcast();
369 unsafe { wrap(cx, global_scope, proto, obj) }
370}
371
372pub fn reflect_dom_object_with_wrap<D, AbstractType, GlobalType>(
375 obj: Box<AbstractType>,
376 global: &GlobalType,
377 cx: &mut js::context::JSContext,
378 wrap: WrapFn<D, AbstractType>,
379) -> DomRoot<AbstractType>
380where
381 D: DomTypes,
382 AbstractType: DomObject,
383 GlobalType: DerivedFrom<D::GlobalScope>,
384 Box<AbstractType>: From<Box<AbstractType>>,
385 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
386{
387 let global_scope = global.upcast();
388 unsafe { wrap(cx, global_scope, None, obj) }
389}
390
391type WrapFnRc<D, AbstractType> = unsafe fn(
392 &mut js::context::JSContext,
393 &<D as DomTypes>::GlobalScope,
394 Option<HandleObject>,
395 Rc<AbstractType>,
396) -> DomRoot<AbstractType>;
397
398pub fn reflect_weak_referenceable_dom_object_with_cx_and_wrap<D, AbstractType, GlobalType>(
401 cx: &mut JSContext,
402 obj: Rc<AbstractType>,
403 global: &GlobalType,
404 wrap: WrapFnRc<D, AbstractType>,
405) -> DomRoot<AbstractType>
406where
407 D: DomTypes,
408 AbstractType: DomObject,
409 GlobalType: DerivedFrom<D::GlobalScope>,
410 Rc<AbstractType>: From<Rc<AbstractType>>,
411 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
412{
413 let global_scope = global.upcast();
414 unsafe { wrap(cx, global_scope, None, obj) }
415}