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(Reflector);
78 unsafe impl JSTraceable for ReflectorStackRoot {
79 unsafe fn trace(&self, tracer: *mut JSTracer) {
80 unsafe { trace_reflector(tracer, "on stack", &self.0) };
81 }
82 }
83 unsafe { &*(self.reflector() as *const Reflector as *const ReflectorStackRoot) }
84 }
85}
86
87unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T>
88where
89 T: DomObject,
90{
91 fn stable_trace_object(&self) -> *const dyn JSTraceable {
92 struct MaybeUnreflectedStackRoot<T>(T);
96 unsafe impl<T> JSTraceable for MaybeUnreflectedStackRoot<T>
97 where
98 T: DomObject,
99 {
100 unsafe fn trace(&self, tracer: *mut JSTracer) {
101 if self.0.reflector().get_jsobject().is_null() {
102 unsafe { self.0.trace(tracer) };
103 } else {
104 unsafe { trace_reflector(tracer, "on stack", self.0.reflector()) };
105 }
106 }
107 }
108 unsafe { &*(self.ptr.as_ptr() as *const T as *const MaybeUnreflectedStackRoot<T>) }
109 }
110}
111
112impl<T> Deref for Root<T>
113where
114 T: Deref + StableTraceObject,
115{
116 type Target = <T as Deref>::Target;
117
118 fn deref(&self) -> &Self::Target {
119 assert_in_script();
120 &self.value
121 }
122}
123
124impl<T> Drop for Root<T>
125where
126 T: StableTraceObject,
127{
128 fn drop(&mut self) {
129 unsafe {
130 (*self.root_list).unroot(self.value.stable_trace_object());
131 }
132 }
133}
134
135impl<T: fmt::Debug + StableTraceObject> fmt::Debug for Root<T> {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137 self.value.fmt(f)
138 }
139}
140
141impl<T: fmt::Debug + DomObject> fmt::Debug for Dom<T> {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 (**self).fmt(f)
144 }
145}
146
147#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
155#[repr(transparent)]
156pub struct Dom<T> {
157 ptr: ptr::NonNull<T>,
158}
159
160impl<T> MallocSizeOf for Dom<T> {
163 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
164 0
165 }
166}
167
168impl<T> PartialEq for Dom<T> {
169 fn eq(&self, other: &Dom<T>) -> bool {
170 self.ptr.as_ptr() == other.ptr.as_ptr()
171 }
172}
173
174impl<'a, T: DomObject> PartialEq<&'a T> for Dom<T> {
175 fn eq(&self, other: &&'a T) -> bool {
176 *self == Dom::from_ref(*other)
177 }
178}
179
180impl<T> Eq for Dom<T> {}
181
182impl<T> Hash for Dom<T> {
183 fn hash<H: Hasher>(&self, state: &mut H) {
184 self.ptr.as_ptr().hash(state)
185 }
186}
187
188impl<T> Clone for Dom<T> {
189 #[inline]
190 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
191 fn clone(&self) -> Self {
192 assert_in_script();
193 Dom { ptr: self.ptr }
194 }
195}
196
197impl<T: DomObject> Dom<T> {
198 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
200 pub fn from_ref(obj: &T) -> Dom<T> {
201 assert_in_script();
202 Dom {
203 ptr: ptr::NonNull::from(obj),
204 }
205 }
206
207 pub fn as_rooted(&self) -> DomRoot<T> {
209 DomRoot::from_ref(self)
210 }
211
212 pub fn as_ptr(&self) -> *const T {
213 self.ptr.as_ptr()
214 }
215}
216
217impl<T: DomObject> Deref for Dom<T> {
218 type Target = T;
219
220 fn deref(&self) -> &T {
221 assert_in_script();
222 unsafe { &*self.ptr.as_ptr() }
225 }
226}
227
228unsafe impl<T: DomObject> JSTraceable for Dom<T> {
229 unsafe fn trace(&self, tracer: *mut JSTracer) {
230 let trace_info = if cfg!(debug_assertions) {
231 std::any::type_name::<T>()
232 } else {
233 "DOM object on heap"
234 };
235 unsafe {
236 trace_reflector(tracer, trace_info, (*self.ptr.as_ptr()).reflector());
237 }
238 }
239}
240
241#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
243pub struct MaybeUnreflectedDom<T> {
244 ptr: ptr::NonNull<T>,
245}
246
247impl<T> MaybeUnreflectedDom<T>
248where
249 T: DomObject,
250{
251 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
256 pub unsafe fn from_box(value: Box<T>) -> Self {
257 Self {
258 ptr: Box::leak(value).into(),
259 }
260 }
261}
262
263impl<T> Root<MaybeUnreflectedDom<T>>
264where
265 T: DomObject,
266{
267 pub fn as_ptr(&self) -> *const T {
268 self.value.ptr.as_ptr()
269 }
270}
271
272impl<T> Root<MaybeUnreflectedDom<T>>
273where
274 T: MutDomObject,
275{
276 pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
281 let ptr = self.as_ptr();
282 drop(self);
283 let root = DomRoot::from_ref(unsafe { &*ptr });
284 unsafe { root.init_reflector::<T>(obj) };
285 root
286 }
287}
288
289pub type DomRoot<T> = Root<Dom<T>>;
291
292impl<T: Castable> DomRoot<T> {
293 pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
295 where
296 U: Castable,
297 T: DerivedFrom<U>,
298 {
299 unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
300 }
301
302 pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
304 where
305 U: DerivedFrom<T>,
306 {
307 if root.is::<U>() {
308 Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
309 } else {
310 None
311 }
312 }
313}
314
315impl<T: DomObject> DomRoot<T> {
316 pub fn from_ref(unrooted: &T) -> DomRoot<T> {
318 unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
319 }
320
321 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
328 pub fn as_traced(&self) -> Dom<T> {
329 Dom::from_ref(self)
330 }
331}
332
333impl<T> MallocSizeOf for DomRoot<T>
334where
335 T: DomObject + MallocSizeOf,
336{
337 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
338 0
339 }
340}
341
342impl<T> PartialEq for DomRoot<T>
343where
344 T: DomObject,
345{
346 fn eq(&self, other: &Self) -> bool {
347 self.value == other.value
348 }
349}
350
351impl<T: DomObject> Eq for DomRoot<T> {}
352
353impl<T: DomObject> Hash for DomRoot<T> {
354 fn hash<H: Hasher>(&self, state: &mut H) {
355 self.value.hash(state);
356 }
357}
358
359impl<T> Clone for DomRoot<T>
360where
361 T: DomObject,
362{
363 fn clone(&self) -> DomRoot<T> {
364 DomRoot::from_ref(self)
365 }
366}
367
368unsafe impl<T> JSTraceable for DomRoot<T>
369where
370 T: DomObject,
371{
372 unsafe fn trace(&self, _: *mut JSTracer) {
373 }
375}
376
377pub struct RootCollection {
384 roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
385}
386
387impl RootCollection {
388 #[expect(clippy::new_without_default)]
390 pub const fn new() -> RootCollection {
391 RootCollection {
392 roots: UnsafeCell::new(vec![]),
393 }
394 }
395
396 unsafe fn root(&self, object: *const dyn JSTraceable) {
398 assert_in_script();
399 unsafe { (*self.roots.get()).push(object) };
400 }
401
402 unsafe fn unroot(&self, object: *const dyn JSTraceable) {
404 assert_in_script();
405 let roots = unsafe { &mut *self.roots.get() };
406 match roots
407 .iter()
408 .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
409 {
410 Some(idx) => {
411 unsafe {
415 let len = roots.len() - 1;
416 if len != idx {
417 let base_ptr = roots.as_mut_ptr();
418 ptr::copy_nonoverlapping(base_ptr.add(len), base_ptr.add(idx), 1);
419 }
420 roots.set_len(len);
421 }
422 },
423 None => panic!("Can't remove a root that was never rooted!"),
424 }
425 }
426}
427
428thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
429
430pub unsafe fn trace_roots(tracer: *mut JSTracer) {
435 trace!("tracing stack roots");
436 STACK_ROOTS.with(|collection| {
437 let collection = unsafe { &*collection.roots.get() };
438 for root in collection {
439 unsafe {
440 (**root).trace(tracer);
441 }
442 }
443 });
444}
445
446pub fn assert_in_script() {
447 debug_assert!(thread_state::get().is_script());
448}
449
450pub trait DomSlice<T>
452where
453 T: JSTraceable + DomObject,
454{
455 fn r(&self) -> &[&T];
457}
458
459impl<T> DomSlice<T> for [Dom<T>]
460where
461 T: JSTraceable + DomObject,
462{
463 #[inline]
464 fn r(&self) -> &[&T] {
465 let _ = mem::transmute::<Dom<T>, &T>;
466 unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
467 }
468}