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