script/dom/bindings/
root.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Smart pointers for the JS-managed DOM objects.
6//!
7//! The DOM is made up of DOM objects whose lifetime is entirely controlled by
8//! the whims of the SpiderMonkey garbage collector. The types in this module
9//! are designed to ensure that any interactions with said Rust types only
10//! occur on values that will remain alive the entire time.
11//!
12//! Here is a brief overview of the important types:
13//!
14//! - `Root<T>`: a stack-based rooted value.
15//! - `DomRoot<T>`: a stack-based reference to a rooted DOM object.
16//! - `Dom<T>`: a reference to a DOM object that can automatically be traced by
17//!   the GC when encountered as a field of a Rust structure.
18//!
19//! `Dom<T>` does not allow access to their inner value without explicitly
20//! creating a stack-based root via the `root` method. This returns a `DomRoot<T>`,
21//! which causes the JS-owned value to be uncollectable for the duration of the
22//! `Root` object's lifetime. A reference to the object can then be obtained
23//! from the `Root` object. These references are not allowed to outlive their
24//! originating `DomRoot<T>`.
25//!
26
27use std::cell::{OnceCell, UnsafeCell};
28use std::default::Default;
29use std::hash::{Hash, Hasher};
30use std::ops::Deref;
31use std::{mem, ptr};
32
33use js::context::NoGC;
34use js::jsapi::{Heap, JSObject, JSTracer, Value};
35use js::rust::HandleValue;
36use layout_api::TrustedNodeAddress;
37use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
38pub(crate) use script_bindings::root::*;
39use style::thread_state;
40
41use crate::dom::bindings::conversions::DerivedFrom;
42use crate::dom::bindings::inheritance::Castable;
43use crate::dom::bindings::reflector::DomObject;
44use crate::dom::bindings::trace::JSTraceable;
45use crate::dom::node::Node;
46
47pub(crate) trait ToLayout<T> {
48    /// Returns `LayoutDom<T>` containing the same pointer.
49    ///
50    /// # Safety
51    ///
52    /// The `self` parameter to this method must meet all the requirements of [`ptr::NonNull::as_ref`].
53    unsafe fn to_layout(&self) -> LayoutDom<'_, T>;
54}
55
56impl<T: DomObject> ToLayout<T> for Dom<T> {
57    unsafe fn to_layout(&self) -> LayoutDom<'_, T> {
58        assert_in_layout();
59        LayoutDom {
60            value: unsafe { self.as_ptr().as_ref().unwrap() },
61        }
62    }
63}
64
65/// An unrooted reference to a DOM object for use in layout. `Layout*Helpers`
66/// traits must be implemented on this.
67#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
68#[repr(transparent)]
69pub(crate) struct LayoutDom<'dom, T> {
70    value: &'dom T,
71}
72
73impl<'dom, T> LayoutDom<'dom, T>
74where
75    T: Castable,
76{
77    /// Cast a DOM object root upwards to one of the interfaces it derives from.
78    pub(crate) fn upcast<U>(&self) -> LayoutDom<'dom, U>
79    where
80        U: Castable,
81        T: DerivedFrom<U>,
82    {
83        assert_in_layout();
84        LayoutDom {
85            value: self.value.upcast::<U>(),
86        }
87    }
88
89    /// Cast a DOM object downwards to one of the interfaces it might implement.
90    pub(crate) fn downcast<U>(&self) -> Option<LayoutDom<'dom, U>>
91    where
92        U: DerivedFrom<T>,
93    {
94        assert_in_layout();
95        self.value.downcast::<U>().map(|value| LayoutDom { value })
96    }
97
98    /// Returns whether this inner object is a U.
99    pub(crate) fn is<U>(&self) -> bool
100    where
101        U: DerivedFrom<T>,
102    {
103        assert_in_layout();
104        self.value.is::<U>()
105    }
106
107    /// Get a reference to the internal value.
108    ///
109    /// ## SAFETY
110    /// This function effectively circumvents all the safety provided by `LayoutDom` as it allows
111    /// performing arbitrary (potentially mutating) operations on the value. Use with caution!
112    pub(crate) unsafe fn as_ref(self) -> &'dom T {
113        self.value
114    }
115}
116
117impl<T> LayoutDom<'_, T>
118where
119    T: DomObject,
120{
121    /// Get the reflector.
122    pub(crate) unsafe fn get_jsobject(&self) -> *mut JSObject {
123        assert_in_layout();
124        self.value.reflector().get_jsobject().get()
125    }
126}
127
128impl<T> Copy for LayoutDom<'_, T> {}
129
130impl<T> PartialEq for LayoutDom<'_, T> {
131    fn eq(&self, other: &Self) -> bool {
132        std::ptr::eq(self.value, other.value)
133    }
134}
135
136impl<T> Eq for LayoutDom<'_, T> {}
137
138impl<T> Hash for LayoutDom<'_, T> {
139    fn hash<H: Hasher>(&self, state: &mut H) {
140        (self.value as *const T).hash(state)
141    }
142}
143
144#[expect(clippy::non_canonical_clone_impl)]
145impl<T> Clone for LayoutDom<'_, T> {
146    #[inline]
147    fn clone(&self) -> Self {
148        assert_in_layout();
149        *self
150    }
151}
152
153impl LayoutDom<'_, Node> {
154    /// Create a new JS-owned value wrapped from an address known to be a
155    /// `Node` pointer.
156    pub(crate) unsafe fn from_trusted_node_address(inner: TrustedNodeAddress) -> Self {
157        assert_in_layout();
158        let TrustedNodeAddress(addr) = inner;
159        LayoutDom {
160            value: unsafe { &*(addr as *const Node) },
161        }
162    }
163}
164
165/// A holder that provides interior mutability for GC-managed values such as
166/// `Dom<T>`.  Essentially a `Cell<Dom<T>>`, but safer.
167///
168/// This should only be used as a field in other DOM objects; see warning
169/// on `Dom<T>`.
170#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
171#[derive(JSTraceable)]
172pub(crate) struct MutDom<T: DomObject> {
173    val: UnsafeCell<Dom<T>>,
174}
175
176impl<T: DomObject> MutDom<T> {
177    /// Create a new `MutDom`.
178    pub(crate) fn new(initial: &T) -> MutDom<T> {
179        assert_in_script();
180        MutDom {
181            val: UnsafeCell::new(Dom::from_ref(initial)),
182        }
183    }
184
185    /// Set this `MutDom` to the given value.
186    pub(crate) fn set(&self, val: &T) {
187        assert_in_script();
188        unsafe {
189            *self.val.get() = Dom::from_ref(val);
190        }
191    }
192
193    /// Get the value in this `MutDom`.
194    pub(crate) fn get(&self) -> DomRoot<T> {
195        assert_in_script();
196        unsafe { DomRoot::from_ref(&*ptr::read(self.val.get())) }
197    }
198}
199
200impl<T: DomObject> MallocSizeOf for MutDom<T> {
201    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
202        // See comment on MallocSizeOf for Dom<T>.
203        0
204    }
205}
206
207impl<T: DomObject> PartialEq for MutDom<T> {
208    fn eq(&self, other: &Self) -> bool {
209        unsafe { *self.val.get() == *other.val.get() }
210    }
211}
212
213impl<T: DomObject + PartialEq> PartialEq<T> for MutDom<T> {
214    fn eq(&self, other: &T) -> bool {
215        unsafe { **self.val.get() == *other }
216    }
217}
218
219pub(crate) fn assert_in_layout() {
220    debug_assert!(thread_state::get().is_layout());
221}
222
223/// A struct to make Unrooted Dom objects work. By taking a no_gc as reference, we ensure that the lifetime of this object
224/// is bounded by the lifetime of NoGC which enforces no gc happening.
225#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
226pub(crate) struct UnrootedDom<'a, T: DomObject> {
227    inner: Dom<T>,
228    no_gc: &'a NoGC,
229}
230
231impl<'a, T: DomObject> UnrootedDom<'a, T> {
232    /// Construct an `UnrootedDom` with the lifetime of `NoGC`. This is safe, as `NoGC` implies no garbage collection will happen
233    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
234    pub(crate) fn from_dom(object: Dom<T>, no_gc: &'a NoGC) -> UnrootedDom<'a, T> {
235        UnrootedDom {
236            inner: object,
237            no_gc,
238        }
239    }
240}
241
242impl<'a, T: DomObject> Deref for UnrootedDom<'a, T> {
243    type Target = Dom<T>;
244
245    fn deref(&self) -> &Self::Target {
246        &self.inner
247    }
248}
249
250/// Safety:
251/// We enforce the same lifetime as the given `UnrootedDom`, so the same
252/// guarantee about no GC happening in this lifetime.
253impl<'a, T: Castable> UnrootedDom<'a, T> {
254    /// Cast a DOM object root upwards to one of the interfaces it derives from.
255    pub fn upcast<U>(dom: UnrootedDom<'a, T>) -> UnrootedDom<'a, U>
256    where
257        U: Castable,
258        T: DerivedFrom<U>,
259    {
260        UnrootedDom {
261            inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
262            no_gc: dom.no_gc,
263        }
264    }
265
266    /// Cast a DOM object root downwards to one of the interfaces it might implement.
267    pub fn downcast<U>(dom: UnrootedDom<'a, T>) -> Option<UnrootedDom<'a, U>>
268    where
269        U: DerivedFrom<T>,
270    {
271        if dom.is::<U>() {
272            Some(UnrootedDom {
273                inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
274                no_gc: dom.no_gc,
275            })
276        } else {
277            None
278        }
279    }
280}
281
282impl<'a, T: DomObject> PartialEq<&T> for UnrootedDom<'a, T> {
283    fn eq(&self, other: &&T) -> bool {
284        self.inner == Dom::from_ref(*other)
285    }
286}
287
288/// A holder that provides interior mutability for GC-managed values such as
289/// `Dom<T>`, with nullability represented by an enclosing Option wrapper.
290/// Essentially a `Cell<Option<Dom<T>>>`, but safer.
291///
292/// This should only be used as a field in other DOM objects; see warning
293/// on `Dom<T>`.
294#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
295#[derive(JSTraceable)]
296pub(crate) struct MutNullableDom<T: DomObject> {
297    ptr: UnsafeCell<Option<Dom<T>>>,
298}
299
300impl<T: DomObject> MutNullableDom<T> {
301    /// Create a new `MutNullableDom`.
302    pub(crate) fn new(initial: Option<&T>) -> MutNullableDom<T> {
303        assert_in_script();
304        MutNullableDom {
305            ptr: UnsafeCell::new(initial.map(Dom::from_ref)),
306        }
307    }
308
309    /// Retrieve a copy of the current inner value. If it is `None`, it is
310    /// initialized with the result of `cb` first.
311    pub(crate) fn or_init<F>(&self, cb: F) -> DomRoot<T>
312    where
313        F: FnOnce() -> DomRoot<T>,
314    {
315        assert_in_script();
316        match self.get() {
317            Some(inner) => inner,
318            None => {
319                let inner = cb();
320                self.set(Some(&inner));
321                inner
322            },
323        }
324    }
325
326    /// Retrieve a copy of the inner optional `Dom<T>` as `LayoutDom<T>`.
327    /// For use by layout, which can't use safe types like Temporary.
328    pub(crate) unsafe fn get_inner_as_layout(&self) -> Option<LayoutDom<'_, T>> {
329        assert_in_layout();
330        unsafe { (*self.ptr.get()).as_ref().map(|js| js.to_layout()) }
331    }
332
333    /// Get a rooted value out of this object
334    pub(crate) fn get(&self) -> Option<DomRoot<T>> {
335        assert_in_script();
336        unsafe { ptr::read(self.ptr.get()).map(|o| DomRoot::from_ref(&*o)) }
337    }
338
339    /// Get the `DomObject` without rooting it. Constructing an UnrootedDom. This is safe
340    /// as we take a reference to NoGC and bound the lifetime by NoGC bound. This implies that
341    /// while the `UnrootedDom` is alive we do not have a GC run.
342    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
343    pub(crate) fn get_unrooted<'a>(&self, no_gc: &'a NoGC) -> Option<UnrootedDom<'a, T>> {
344        assert_in_script();
345        let ptr = unsafe { ptr::read(self.ptr.get()) };
346        ptr.map(|o| Dom::from_ref(&*o))
347            .map(|dom| UnrootedDom { inner: dom, no_gc })
348    }
349
350    /// Set this `MutNullableDom` to the given value.
351    pub(crate) fn set(&self, val: Option<&T>) {
352        assert_in_script();
353        unsafe {
354            *self.ptr.get() = val.map(|p| Dom::from_ref(p));
355        }
356    }
357
358    /// Gets the current value out of this object and sets it to `None`.
359    pub(crate) fn take(&self) -> Option<DomRoot<T>> {
360        let value = self.get();
361        self.set(None);
362        value
363    }
364
365    /// Sets the current value of this [`MutNullableDom`] to `None`.
366    pub(crate) fn clear(&self) {
367        self.set(None)
368    }
369
370    /// Runs the given callback on the object if it's not null.
371    pub(crate) fn if_is_some<F, R>(&self, cb: F) -> Option<&R>
372    where
373        F: FnOnce(&T) -> &R,
374    {
375        unsafe {
376            if let Some(ref value) = *self.ptr.get() {
377                Some(cb(value))
378            } else {
379                None
380            }
381        }
382    }
383}
384
385impl<T: DomObject> PartialEq for MutNullableDom<T> {
386    fn eq(&self, other: &Self) -> bool {
387        unsafe { *self.ptr.get() == *other.ptr.get() }
388    }
389}
390
391impl<T: DomObject> PartialEq<Option<&T>> for MutNullableDom<T> {
392    fn eq(&self, other: &Option<&T>) -> bool {
393        unsafe { *self.ptr.get() == other.map(Dom::from_ref) }
394    }
395}
396
397impl<T: DomObject> Default for MutNullableDom<T> {
398    fn default() -> MutNullableDom<T> {
399        assert_in_script();
400        MutNullableDom {
401            ptr: UnsafeCell::new(None),
402        }
403    }
404}
405
406impl<T: DomObject> MallocSizeOf for MutNullableDom<T> {
407    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
408        // See comment on MallocSizeOf for Dom<T>.
409        0
410    }
411}
412
413/// A holder that allows to lazily initialize the value only once
414/// `Dom<T>`, using OnceCell
415/// Essentially a `OnceCell<Dom<T>>`.
416///
417/// This should only be used as a field in other DOM objects; see warning
418/// on `Dom<T>`.
419#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
420pub(crate) struct DomOnceCell<T: DomObject> {
421    ptr: OnceCell<Dom<T>>,
422}
423
424impl<T> DomOnceCell<T>
425where
426    T: DomObject,
427{
428    /// Retrieve a copy of the current inner value. If it is `None`, it is
429    /// initialized with the result of `cb` first.
430    pub(crate) fn init_once<F>(&self, cb: F) -> &T
431    where
432        F: FnOnce() -> DomRoot<T>,
433    {
434        assert_in_script();
435        self.ptr.get_or_init(|| Dom::from_ref(&cb()))
436    }
437}
438
439impl<T: DomObject> Default for DomOnceCell<T> {
440    fn default() -> DomOnceCell<T> {
441        assert_in_script();
442        DomOnceCell {
443            ptr: OnceCell::new(),
444        }
445    }
446}
447
448impl<T: DomObject> MallocSizeOf for DomOnceCell<T> {
449    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
450        // See comment on MallocSizeOf for Dom<T>.
451        0
452    }
453}
454
455unsafe impl<T: DomObject> JSTraceable for DomOnceCell<T> {
456    unsafe fn trace(&self, trc: *mut JSTracer) {
457        if let Some(ptr) = self.ptr.get() {
458            unsafe { ptr.trace(trc) };
459        }
460    }
461}
462
463impl<'dom, T> LayoutDom<'dom, T>
464where
465    T: 'dom + DomObject,
466{
467    /// Returns a reference to the interior of this JS object. The fact
468    /// that this is unsafe is what necessitates the layout wrappers.
469    pub(crate) fn unsafe_get(self) -> &'dom T {
470        assert_in_layout();
471        self.value
472    }
473
474    /// Transforms a slice of `Dom<T>` into a slice of `LayoutDom<T>`.
475    // FIXME(nox): This should probably be done through a ToLayout trait.
476    pub(crate) unsafe fn to_layout_slice(slice: &'dom [Dom<T>]) -> &'dom [LayoutDom<'dom, T>] {
477        // This doesn't compile if Dom and LayoutDom don't have the same
478        // representation.
479        let _ = mem::transmute::<Dom<T>, LayoutDom<T>>;
480        unsafe { &*(slice as *const [Dom<T>] as *const [LayoutDom<T>]) }
481    }
482}
483
484/// Converts a rooted `Heap<Value>` into a `HandleValue`.
485///
486/// This is only safe if the `Heap` is rooted (e.g., held inside a `Dom`-managed struct),
487/// and the `#[must_root]` crown lint is active to enforce rooting at compile time.
488/// Avoids repeating unsafe `from_raw` calls at each usage site.
489pub trait AsHandleValue<'a> {
490    fn as_handle_value(&'a self) -> HandleValue<'a>;
491}
492
493impl<'a> AsHandleValue<'a> for Heap<Value> {
494    #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
495    fn as_handle_value(&'a self) -> HandleValue<'a> {
496        // SAFETY: `self` is assumed to be rooted, and `handle()` ties
497        // the lifetime to `&self`, which the compiler can enforce.
498        unsafe { HandleValue::from_marked_location(self.ptr.get() as *const _) }
499    }
500}