1use std::cell::UnsafeCell;
6use std::hash::{Hash, Hasher};
7use std::ops::Deref;
8use std::{fmt, mem, ptr};
9
10use js::gc::Traceable as JSTraceable;
11use js::jsapi::{JSObject, JSTracer};
12use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
13use style::thread_state;
14
15use crate::conversions::DerivedFrom;
16use crate::inheritance::Castable;
17use crate::reflector::{DomObject, MutDomObject, Reflector};
18use crate::trace::trace_reflector;
19
20#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
22pub struct Root<T: StableTraceObject> {
23 value: T,
25 root_list: *const RootCollection,
27}
28
29impl<T> Root<T>
30where
31 T: StableTraceObject + 'static,
32{
33 pub unsafe fn new(value: T) -> Self {
39 unsafe fn add_to_root_list(object: *const dyn JSTraceable) -> *const RootCollection {
40 assert_in_script();
41 STACK_ROOTS.with(|root_list| {
42 unsafe { root_list.root(object) };
43 root_list as *const _
44 })
45 }
46
47 let root_list = unsafe { add_to_root_list(value.stable_trace_object()) };
48 Root { value, root_list }
49 }
50}
51
52pub unsafe trait StableTraceObject {
63 fn stable_trace_object(&self) -> *const dyn JSTraceable;
66}
67
68unsafe impl<T> StableTraceObject for Dom<T>
69where
70 T: DomObject,
71{
72 fn stable_trace_object(&self) -> *const dyn JSTraceable {
73 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
77 struct ReflectorStackRoot<T>(Reflector<T>);
78 unsafe impl<T> JSTraceable for ReflectorStackRoot<T> {
79 unsafe fn trace(&self, tracer: *mut JSTracer) {
80 unsafe { trace_reflector(tracer, "on stack", &self.0) };
81 }
82 }
83 unsafe {
84 &*(self.reflector() as *const Reflector<T::ReflectorType>
85 as *const ReflectorStackRoot<T::ReflectorType>)
86 }
87 }
88}
89
90unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T>
91where
92 T: DomObject,
93{
94 fn stable_trace_object(&self) -> *const dyn JSTraceable {
95 struct MaybeUnreflectedStackRoot<T>(T);
99 unsafe impl<T> JSTraceable for MaybeUnreflectedStackRoot<T>
100 where
101 T: DomObject,
102 {
103 unsafe fn trace(&self, tracer: *mut JSTracer) {
104 if self.0.reflector().get_jsobject().is_null() {
105 unsafe { self.0.trace(tracer) };
106 } else {
107 unsafe { trace_reflector(tracer, "on stack", self.0.reflector()) };
108 }
109 }
110 }
111 unsafe { &*(self.ptr.as_ptr() as *const T as *const MaybeUnreflectedStackRoot<T>) }
112 }
113}
114
115impl<T> Deref for Root<T>
116where
117 T: Deref + StableTraceObject,
118{
119 type Target = <T as Deref>::Target;
120
121 fn deref(&self) -> &Self::Target {
122 assert_in_script();
123 &self.value
124 }
125}
126
127impl<T> Drop for Root<T>
128where
129 T: StableTraceObject,
130{
131 fn drop(&mut self) {
132 unsafe {
133 (*self.root_list).unroot(self.value.stable_trace_object());
134 }
135 }
136}
137
138impl<T: fmt::Debug + StableTraceObject> fmt::Debug for Root<T> {
139 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140 self.value.fmt(f)
141 }
142}
143
144impl<T: fmt::Debug + DomObject> fmt::Debug for Dom<T> {
145 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146 (**self).fmt(f)
147 }
148}
149
150#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
158#[repr(transparent)]
159pub struct Dom<T> {
160 ptr: ptr::NonNull<T>,
161}
162
163impl<T> MallocSizeOf for Dom<T> {
166 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
167 0
168 }
169}
170
171impl<T> PartialEq for Dom<T> {
172 fn eq(&self, other: &Dom<T>) -> bool {
173 self.ptr.as_ptr() == other.ptr.as_ptr()
174 }
175}
176
177impl<'a, T: DomObject> PartialEq<&'a T> for Dom<T> {
178 fn eq(&self, other: &&'a T) -> bool {
179 *self == Dom::from_ref(*other)
180 }
181}
182
183impl<T> Eq for Dom<T> {}
184
185impl<T> Hash for Dom<T> {
186 fn hash<H: Hasher>(&self, state: &mut H) {
187 self.ptr.as_ptr().hash(state)
188 }
189}
190
191impl<T> Clone for Dom<T> {
192 #[inline]
193 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
194 fn clone(&self) -> Self {
195 assert_in_script();
196 Dom { ptr: self.ptr }
197 }
198}
199
200impl<T: DomObject> Dom<T> {
201 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
203 pub fn from_ref(obj: &T) -> Dom<T> {
204 assert_in_script();
205 Dom {
206 ptr: ptr::NonNull::from(obj),
207 }
208 }
209
210 pub fn as_rooted(&self) -> DomRoot<T> {
212 DomRoot::from_ref(self)
213 }
214
215 pub fn as_ptr(&self) -> *const T {
216 self.ptr.as_ptr()
217 }
218}
219
220impl<T: DomObject> Deref for Dom<T> {
221 type Target = T;
222
223 fn deref(&self) -> &T {
224 assert_in_script();
225 unsafe { &*self.ptr.as_ptr() }
228 }
229}
230
231unsafe impl<T: DomObject> JSTraceable for Dom<T> {
232 unsafe fn trace(&self, tracer: *mut JSTracer) {
233 let trace_info = if cfg!(debug_assertions) {
234 std::any::type_name::<T>()
235 } else {
236 "DOM object on heap"
237 };
238 unsafe {
239 trace_reflector(tracer, trace_info, (*self.ptr.as_ptr()).reflector());
240 }
241 }
242}
243
244#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
246pub struct MaybeUnreflectedDom<T> {
247 ptr: ptr::NonNull<T>,
248}
249
250impl<T> MaybeUnreflectedDom<T>
251where
252 T: DomObject,
253{
254 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
259 pub unsafe fn from_box(value: Box<T>) -> Self {
260 Self {
261 ptr: Box::leak(value).into(),
262 }
263 }
264}
265
266impl<T> Root<MaybeUnreflectedDom<T>>
267where
268 T: DomObject,
269{
270 pub fn as_ptr(&self) -> *const T {
271 self.value.ptr.as_ptr()
272 }
273}
274
275impl<T> Root<MaybeUnreflectedDom<T>>
276where
277 T: MutDomObject,
278{
279 pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
284 let ptr = self.as_ptr();
285 drop(self);
286 let root = DomRoot::from_ref(unsafe { &*ptr });
287 unsafe { root.init_reflector::<T>(obj) };
288 root
289 }
290}
291
292pub type DomRoot<T> = Root<Dom<T>>;
294
295impl<T: Castable> DomRoot<T> {
296 pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
298 where
299 U: Castable,
300 T: DerivedFrom<U>,
301 {
302 unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
303 }
304
305 pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
307 where
308 U: DerivedFrom<T>,
309 {
310 if root.is::<U>() {
311 Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
312 } else {
313 None
314 }
315 }
316}
317
318impl<T: DomObject> DomRoot<T> {
319 pub fn from_ref(unrooted: &T) -> DomRoot<T> {
321 unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
322 }
323
324 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
331 pub fn as_traced(&self) -> Dom<T> {
332 Dom::from_ref(self)
333 }
334}
335
336impl<T> MallocSizeOf for DomRoot<T>
337where
338 T: DomObject + MallocSizeOf,
339{
340 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
341 0
342 }
343}
344
345impl<T> PartialEq for DomRoot<T>
346where
347 T: DomObject,
348{
349 fn eq(&self, other: &Self) -> bool {
350 self.value == other.value
351 }
352}
353
354impl<T: DomObject> Eq for DomRoot<T> {}
355
356impl<T: DomObject> Hash for DomRoot<T> {
357 fn hash<H: Hasher>(&self, state: &mut H) {
358 self.value.hash(state);
359 }
360}
361
362impl<T> Clone for DomRoot<T>
363where
364 T: DomObject,
365{
366 fn clone(&self) -> DomRoot<T> {
367 DomRoot::from_ref(self)
368 }
369}
370
371unsafe impl<T> JSTraceable for DomRoot<T>
372where
373 T: DomObject,
374{
375 unsafe fn trace(&self, _: *mut JSTracer) {
376 }
378}
379
380pub struct RootCollection {
387 roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
388}
389
390impl RootCollection {
391 #[expect(clippy::new_without_default)]
393 pub const fn new() -> RootCollection {
394 RootCollection {
395 roots: UnsafeCell::new(vec![]),
396 }
397 }
398
399 unsafe fn root(&self, object: *const dyn JSTraceable) {
401 assert_in_script();
402 unsafe { (*self.roots.get()).push(object) };
403 }
404
405 unsafe fn unroot(&self, object: *const dyn JSTraceable) {
407 assert_in_script();
408 let roots = unsafe { &mut *self.roots.get() };
409 match roots
410 .iter()
411 .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
412 {
413 Some(idx) => {
414 unsafe {
418 let len = roots.len() - 1;
419 if len != idx {
420 let base_ptr = roots.as_mut_ptr();
421 ptr::copy_nonoverlapping(base_ptr.add(len), base_ptr.add(idx), 1);
422 }
423 roots.set_len(len);
424 }
425 },
426 None => panic!("Can't remove a root that was never rooted!"),
427 }
428 }
429}
430
431thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
432
433pub unsafe fn trace_roots(tracer: *mut JSTracer) {
438 trace!("tracing stack roots");
439 STACK_ROOTS.with(|collection| {
440 let collection = unsafe { &*collection.roots.get() };
441 for root in collection {
442 unsafe {
443 (**root).trace(tracer);
444 }
445 }
446 });
447}
448
449pub fn assert_in_script() {
450 debug_assert!(thread_state::get().is_script());
451}
452
453pub trait DomSlice<T>
455where
456 T: JSTraceable + DomObject,
457{
458 fn r(&self) -> &[&T];
460}
461
462impl<T> DomSlice<T> for [Dom<T>]
463where
464 T: JSTraceable + DomObject,
465{
466 #[inline]
467 fn r(&self) -> &[&T] {
468 let _ = mem::transmute::<Dom<T>, &T>;
469 unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
470 }
471}