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, _: *mut js::jsapi::JSTracer) {}
54}
55
56impl<T> PartialEq for Reflector<T> {
57 fn eq(&self, other: &Reflector<T>) -> bool {
58 self.object.get() == other.object.get()
59 }
60}
61
62impl<T> Reflector<T> {
63 #[inline]
65 pub fn get_jsobject(&self) -> HandleObject<'_> {
66 unsafe { HandleObject::from_raw(self.object.handle()) }
68 }
69
70 #[inline]
72 pub fn proto_id(&self) -> u16 {
73 self.proto_id.get()
74 }
75
76 #[inline]
78 pub fn set_proto_id(&self, id: u16) {
79 self.proto_id.set(id);
80 }
81
82 unsafe fn set_jsobject(&self, object: *mut JSObject) {
88 assert!(self.object.get().is_null());
89 assert!(!object.is_null());
90 self.object.set(object);
91 }
92
93 pub fn rootable(&self) -> &Heap<*mut JSObject> {
97 &self.object
98 }
99}
100
101impl<T: AssociatedMemorySize> Reflector<T> {
102 #[expect(clippy::new_without_default)]
105 pub fn new() -> Reflector<T> {
106 Reflector {
107 object: Heap::default(),
108 proto_id: Cell::new(u16::MAX),
109 size: T::default(),
110 }
111 }
112
113 pub fn rust_size<D>(&self, _: &D) -> usize {
114 size_of::<D>() + size_of::<Box<D>>() + self.size.size()
115 }
116
117 pub fn drop_memory<D>(&self, d: &D) {
119 unsafe {
120 RemoveAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
121 }
122 }
123}
124
125impl Reflector<AssociatedMemory> {
126 pub fn update_memory_size<D>(&self, d: &D, new_size: usize) {
128 if self.size.size() == new_size {
129 return;
130 }
131 unsafe {
132 RemoveAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
133 self.size.0.set(new_size);
134 AddAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
135 }
136 }
137}
138
139pub trait DomObject: js::gc::Traceable + 'static {
141 type ReflectorType: AssociatedMemorySize;
142 fn reflector(&self) -> &Reflector<Self::ReflectorType>;
144}
145
146impl DomObject for Reflector<()> {
147 type ReflectorType = ();
148
149 fn reflector(&self) -> &Reflector<Self::ReflectorType> {
150 self
151 }
152}
153
154impl DomObject for Reflector<AssociatedMemory> {
155 type ReflectorType = AssociatedMemory;
156
157 fn reflector(&self) -> &Reflector<Self::ReflectorType> {
158 self
159 }
160}
161
162pub trait MutDomObject: DomObject {
164 unsafe fn init_reflector<D>(&self, obj: *mut JSObject);
171
172 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject);
178}
179
180impl MutDomObject for Reflector<()> {
181 unsafe fn init_reflector<D>(&self, obj: *mut JSObject) {
182 unsafe {
183 js::jsapi::AddAssociatedMemory(
184 obj,
185 size_of::<D>() + size_of::<Box<D>>(),
186 MemoryUse::DOMBinding,
187 );
188 self.init_reflector_without_associated_memory(obj);
189 }
190 }
191
192 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject) {
193 unsafe {
194 self.set_jsobject(obj);
195 }
196 }
197}
198
199impl MutDomObject for Reflector<AssociatedMemory> {
200 unsafe fn init_reflector<D>(&self, obj: *mut JSObject) {
201 unsafe {
202 js::jsapi::AddAssociatedMemory(
203 obj,
204 size_of::<D>() + size_of::<Box<D>>(),
205 MemoryUse::DOMBinding,
206 );
207 self.init_reflector_without_associated_memory(obj);
208 }
209 }
210
211 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject) {
212 unsafe {
213 self.set_jsobject(obj);
214 }
215 }
216}
217
218pub trait DomGlobalGeneric<D: DomTypes>: DomObject {
219 fn global_from_reflector(&self) -> DomRoot<D::GlobalScope>
223 where
224 Self: Sized,
225 {
226 let cx = unsafe { JSContext::get_from_thread() };
237 let cx = &mut cx.expect("JS runtime has shut down");
238 let _realm = enter_auto_realm::<D>(cx, self);
239 D::GlobalScope::from_reflector(self)
240 }
241}
242
243impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {}
244
245pub trait DomObjectWrap<D: DomTypes>: Sized + DomObject + DomGlobalGeneric<D> {
247 #[expect(clippy::type_complexity)]
249 const WRAP: unsafe fn(
250 &mut JSContext,
251 &D::GlobalScope,
252 Option<HandleObject>,
253 Box<Self>,
254 ) -> Root<Dom<Self>>;
255}
256
257pub trait WeakReferenceableDomObjectWrap<D: DomTypes>:
259 Sized + DomObject + DomGlobalGeneric<D>
260{
261 #[expect(clippy::type_complexity)]
263 const WRAP: unsafe fn(
264 &mut js::context::JSContext,
265 &D::GlobalScope,
266 Option<HandleObject>,
267 Rc<Self>,
268 ) -> Root<Dom<Self>>;
269}
270
271pub trait DomObjectIteratorWrap<D: DomTypes>: DomObjectWrap<D> + JSTraceable + Iterable {
274 #[expect(clippy::type_complexity)]
276 const ITER_WRAP: unsafe fn(
277 &mut JSContext,
278 &D::GlobalScope,
279 Option<HandleObject>,
280 Box<IterableIterator<D, Self>>,
281 ) -> Root<Dom<IterableIterator<D, Self>>>;
282}
283
284pub fn reflect_dom_object<D, T, U>(cx: &mut JSContext, obj: Box<T>, global: &U) -> DomRoot<T>
287where
288 D: DomTypes,
289 T: DomObject + DomObjectWrap<D>,
290 U: DerivedFrom<D::GlobalScope>,
291{
292 let global_scope = global.upcast();
293 unsafe { T::WRAP(cx, global_scope, None, obj) }
294}
295
296pub fn reflect_dom_object_with_proto<D, T, U>(
297 cx: &mut JSContext,
298 obj: Box<T>,
299 global: &U,
300 proto: Option<HandleObject>,
301) -> DomRoot<T>
302where
303 D: DomTypes,
304 T: DomObject + DomObjectWrap<D>,
305 U: DerivedFrom<D::GlobalScope>,
306{
307 let global_scope = global.upcast();
308 unsafe { T::WRAP(cx, global_scope, proto, obj) }
309}
310
311pub fn reflect_dom_object_with_cx<D, T, U>(
315 obj: Box<T>,
316 global: &U,
317 cx: &mut JSContext,
318) -> DomRoot<T>
319where
320 D: DomTypes,
321 T: DomObject + DomObjectWrap<D>,
322 U: DerivedFrom<D::GlobalScope>,
323{
324 let global_scope = global.upcast();
325 unsafe { T::WRAP(cx, global_scope, None, obj) }
326}
327
328pub fn reflect_weak_referenceable_dom_object<D, T, U>(
331 cx: &mut JSContext,
332 obj: Rc<T>,
333 global: &U,
334) -> DomRoot<T>
335where
336 D: DomTypes,
337 T: DomObject + WeakReferenceableDomObjectWrap<D>,
338 U: DerivedFrom<D::GlobalScope>,
339{
340 let global_scope = global.upcast();
341 unsafe { T::WRAP(cx, global_scope, None, obj) }
342}
343
344pub fn reflect_weak_referenceable_dom_object_with_proto<D, T, U>(
345 cx: &mut JSContext,
346 obj: Rc<T>,
347 global: &U,
348 proto: Option<HandleObject>,
349) -> DomRoot<T>
350where
351 D: DomTypes,
352 T: DomObject + WeakReferenceableDomObjectWrap<D>,
353 U: DerivedFrom<D::GlobalScope>,
354{
355 let global_scope = global.upcast();
356 unsafe { T::WRAP(cx, global_scope, proto, obj) }
357}
358
359type WrapFn<D, AbstractType> = unsafe fn(
360 &mut js::context::JSContext,
361 &<D as DomTypes>::GlobalScope,
362 Option<HandleObject>,
363 Box<AbstractType>,
364) -> DomRoot<AbstractType>;
365
366pub fn reflect_dom_object_with_proto_and_wrap<D, AbstractType, GlobalType>(
369 obj: Box<AbstractType>,
370 global: &GlobalType,
371 proto: Option<HandleObject>,
372 cx: &mut js::context::JSContext,
373 wrap: WrapFn<D, AbstractType>,
374) -> DomRoot<AbstractType>
375where
376 D: DomTypes,
377 AbstractType: DomObject,
378 GlobalType: DerivedFrom<D::GlobalScope>,
379 Box<AbstractType>: From<Box<AbstractType>>,
380 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
381{
382 let global_scope = global.upcast();
383 unsafe { wrap(cx, global_scope, proto, obj) }
384}
385
386pub fn reflect_dom_object_with_wrap<D, AbstractType, GlobalType>(
389 obj: Box<AbstractType>,
390 global: &GlobalType,
391 cx: &mut js::context::JSContext,
392 wrap: WrapFn<D, AbstractType>,
393) -> DomRoot<AbstractType>
394where
395 D: DomTypes,
396 AbstractType: DomObject,
397 GlobalType: DerivedFrom<D::GlobalScope>,
398 Box<AbstractType>: From<Box<AbstractType>>,
399 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
400{
401 let global_scope = global.upcast();
402 unsafe { wrap(cx, global_scope, None, obj) }
403}
404
405type WrapFnRc<D, AbstractType> = unsafe fn(
406 &mut js::context::JSContext,
407 &<D as DomTypes>::GlobalScope,
408 Option<HandleObject>,
409 Rc<AbstractType>,
410) -> DomRoot<AbstractType>;
411
412pub fn reflect_weak_referenceable_dom_object_with_cx_and_wrap<D, AbstractType, GlobalType>(
415 cx: &mut JSContext,
416 obj: Rc<AbstractType>,
417 global: &GlobalType,
418 wrap: WrapFnRc<D, AbstractType>,
419) -> DomRoot<AbstractType>
420where
421 D: DomTypes,
422 AbstractType: DomObject,
423 GlobalType: DerivedFrom<D::GlobalScope>,
424 Rc<AbstractType>: From<Rc<AbstractType>>,
425 DomRoot<AbstractType>: From<DomRoot<AbstractType>>,
426{
427 let global_scope = global.upcast();
428 unsafe { wrap(cx, global_scope, None, obj) }
429}