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