1use std::cell::Cell;
6
7use js::jsapi::{AddAssociatedMemory, Heap, JSObject, MemoryUse, RemoveAssociatedMemory};
8use js::rust::HandleObject;
9use malloc_size_of_derive::MallocSizeOf;
10
11use crate::conversions::DerivedFrom;
12use crate::interfaces::GlobalScopeHelpers;
13use crate::iterable::{Iterable, IterableIterator};
14use crate::realms::InRealm;
15use crate::root::{Dom, DomRoot, Root};
16use crate::script_runtime::{CanGc, temp_cx};
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_(&self, realm: InRealm) -> DomRoot<D::GlobalScope>
222 where
223 Self: Sized,
224 {
225 D::GlobalScope::from_reflector(self, realm)
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 js::context::JSContext,
237 &D::GlobalScope,
238 Option<HandleObject>,
239 Box<Self>,
240 ) -> Root<Dom<Self>>;
241}
242
243pub trait DomObjectIteratorWrap<D: DomTypes>: DomObjectWrap<D> + JSTraceable + Iterable {
246 #[expect(clippy::type_complexity)]
248 const ITER_WRAP: unsafe fn(
249 &mut js::context::JSContext,
250 &D::GlobalScope,
251 Option<HandleObject>,
252 Box<IterableIterator<D, Self>>,
253 ) -> Root<Dom<IterableIterator<D, Self>>>;
254}
255
256pub fn reflect_dom_object<D, T, U>(obj: Box<T>, global: &U, _can_gc: CanGc) -> DomRoot<T>
259where
260 D: DomTypes,
261 T: DomObject + DomObjectWrap<D>,
262 U: DerivedFrom<D::GlobalScope>,
263{
264 let global_scope = global.upcast();
265 let mut cx = unsafe { temp_cx() };
266 unsafe { T::WRAP(&mut cx, global_scope, None, obj) }
267}
268
269pub fn reflect_dom_object_with_proto<D, T, U>(
270 obj: Box<T>,
271 global: &U,
272 proto: Option<HandleObject>,
273 _can_gc: CanGc,
274) -> DomRoot<T>
275where
276 D: DomTypes,
277 T: DomObject + DomObjectWrap<D>,
278 U: DerivedFrom<D::GlobalScope>,
279{
280 let global_scope = global.upcast();
281 let mut cx = unsafe { temp_cx() };
282 unsafe { T::WRAP(&mut cx, global_scope, proto, obj) }
283}
284
285pub fn reflect_dom_object_with_cx<D, T, U>(
288 obj: Box<T>,
289 global: &U,
290 cx: &mut js::context::JSContext,
291) -> DomRoot<T>
292where
293 D: DomTypes,
294 T: DomObject + DomObjectWrap<D>,
295 U: DerivedFrom<D::GlobalScope>,
296{
297 let global_scope = global.upcast();
298 unsafe { T::WRAP(cx, global_scope, None, obj) }
299}
300
301pub fn reflect_dom_object_with_proto_and_cx<D, T, U>(
304 obj: Box<T>,
305 global: &U,
306 proto: Option<HandleObject>,
307 cx: &mut js::context::JSContext,
308) -> DomRoot<T>
309where
310 D: DomTypes,
311 T: DomObject + DomObjectWrap<D>,
312 U: DerivedFrom<D::GlobalScope>,
313{
314 let global_scope = global.upcast();
315 unsafe { T::WRAP(cx, global_scope, proto, obj) }
316}