script_bindings/
reflector.rs1use 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::interfaces::GlobalScopeHelpers;
12use crate::iterable::{Iterable, IterableIterator};
13use crate::realms::InRealm;
14use crate::root::{Dom, DomRoot, Root};
15use crate::{DomTypes, JSTraceable};
16
17pub trait AssociatedMemorySize: Default {
18 fn size(&self) -> usize;
19}
20
21impl AssociatedMemorySize for () {
22 fn size(&self) -> usize {
23 0
24 }
25}
26
27#[derive(Default, MallocSizeOf)]
28pub struct AssociatedMemory(Cell<usize>);
29
30impl AssociatedMemorySize for AssociatedMemory {
31 fn size(&self) -> usize {
32 self.0.get()
33 }
34}
35
36#[derive(MallocSizeOf)]
38#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
39pub struct Reflector<T = ()> {
41 #[ignore_malloc_size_of = "defined and measured in rust-mozjs"]
42 object: Heap<*mut JSObject>,
43 size: T,
45 proto_id: Cell<u16>,
47}
48
49unsafe impl<T> js::gc::Traceable for Reflector<T> {
50 unsafe fn trace(&self, _: *mut js::jsapi::JSTracer) {}
51}
52
53impl<T> PartialEq for Reflector<T> {
54 fn eq(&self, other: &Reflector<T>) -> bool {
55 self.object.get() == other.object.get()
56 }
57}
58
59impl<T> Reflector<T> {
60 #[inline]
62 pub fn get_jsobject(&self) -> HandleObject<'_> {
63 unsafe { HandleObject::from_raw(self.object.handle()) }
65 }
66
67 #[inline]
69 pub fn proto_id(&self) -> u16 {
70 self.proto_id.get()
71 }
72
73 #[inline]
75 pub fn set_proto_id(&self, id: u16) {
76 self.proto_id.set(id);
77 }
78
79 unsafe fn set_jsobject(&self, object: *mut JSObject) {
85 assert!(self.object.get().is_null());
86 assert!(!object.is_null());
87 self.object.set(object);
88 }
89
90 pub fn rootable(&self) -> &Heap<*mut JSObject> {
94 &self.object
95 }
96}
97
98impl<T: AssociatedMemorySize> Reflector<T> {
99 #[expect(clippy::new_without_default)]
102 pub fn new() -> Reflector<T> {
103 Reflector {
104 object: Heap::default(),
105 proto_id: Cell::new(u16::MAX),
106 size: T::default(),
107 }
108 }
109
110 pub fn rust_size<D>(&self, _: &D) -> usize {
111 size_of::<D>() + size_of::<Box<D>>() + self.size.size()
112 }
113
114 pub fn drop_memory<D>(&self, d: &D) {
116 unsafe {
117 RemoveAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
118 }
119 }
120}
121
122impl Reflector<AssociatedMemory> {
123 pub fn update_memory_size<D>(&self, d: &D, new_size: usize) {
125 if self.size.size() == new_size {
126 return;
127 }
128 unsafe {
129 RemoveAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
130 self.size.0.set(new_size);
131 AddAssociatedMemory(self.object.get(), self.rust_size(d), MemoryUse::DOMBinding);
132 }
133 }
134}
135
136pub trait DomObject: js::gc::Traceable + 'static {
138 type ReflectorType: AssociatedMemorySize;
139 fn reflector(&self) -> &Reflector<Self::ReflectorType>;
141}
142
143impl DomObject for Reflector<()> {
144 type ReflectorType = ();
145
146 fn reflector(&self) -> &Reflector<Self::ReflectorType> {
147 self
148 }
149}
150
151impl DomObject for Reflector<AssociatedMemory> {
152 type ReflectorType = AssociatedMemory;
153
154 fn reflector(&self) -> &Reflector<Self::ReflectorType> {
155 self
156 }
157}
158
159pub trait MutDomObject: DomObject {
161 unsafe fn init_reflector<D>(&self, obj: *mut JSObject);
168
169 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject);
175}
176
177impl MutDomObject for Reflector<()> {
178 unsafe fn init_reflector<D>(&self, obj: *mut JSObject) {
179 unsafe {
180 js::jsapi::AddAssociatedMemory(
181 obj,
182 size_of::<D>() + size_of::<Box<D>>(),
183 MemoryUse::DOMBinding,
184 );
185 self.init_reflector_without_associated_memory(obj);
186 }
187 }
188
189 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject) {
190 unsafe {
191 self.set_jsobject(obj);
192 }
193 }
194}
195
196impl MutDomObject for Reflector<AssociatedMemory> {
197 unsafe fn init_reflector<D>(&self, obj: *mut JSObject) {
198 unsafe {
199 js::jsapi::AddAssociatedMemory(
200 obj,
201 size_of::<D>() + size_of::<Box<D>>(),
202 MemoryUse::DOMBinding,
203 );
204 self.init_reflector_without_associated_memory(obj);
205 }
206 }
207
208 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut JSObject) {
209 unsafe {
210 self.set_jsobject(obj);
211 }
212 }
213}
214
215pub trait DomGlobalGeneric<D: DomTypes>: DomObject {
216 fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope>
220 where
221 Self: Sized,
222 {
223 D::GlobalScope::from_reflector(self, realm)
224 }
225}
226
227impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {}
228
229pub trait DomObjectWrap<D: DomTypes>: Sized + DomObject + DomGlobalGeneric<D> {
231 #[expect(clippy::type_complexity)]
233 const WRAP: unsafe fn(
234 &mut js::context::JSContext,
235 &D::GlobalScope,
236 Option<HandleObject>,
237 Box<Self>,
238 ) -> Root<Dom<Self>>;
239}
240
241pub trait DomObjectIteratorWrap<D: DomTypes>: DomObjectWrap<D> + JSTraceable + Iterable {
244 #[expect(clippy::type_complexity)]
246 const ITER_WRAP: unsafe fn(
247 &mut js::context::JSContext,
248 &D::GlobalScope,
249 Option<HandleObject>,
250 Box<IterableIterator<D, Self>>,
251 ) -> Root<Dom<IterableIterator<D, Self>>>;
252}