Skip to main content

script_bindings/
dom.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/. */
4use std::cell::UnsafeCell;
5use std::hash::{Hash, Hasher};
6use std::marker::PhantomData;
7use std::ops::Deref;
8use std::{mem, ptr};
9
10use js::context::NoGC;
11use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
12
13use crate::DomObject;
14use crate::assert::assert_in_script;
15use crate::conversions::DerivedFrom;
16use crate::inheritance::Castable;
17use crate::root::{Dom, DomRoot};
18
19/// A holder that provides interior mutability for GC-managed values such as
20/// `Dom<T>`.  Essentially a `Cell<Dom<T>>`, but safer.
21///
22/// This should only be used as a field in other DOM objects; see warning
23/// on `Dom<T>`.
24#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
25#[derive(JSTraceable)]
26pub struct MutDom<T: DomObject> {
27    val: UnsafeCell<Dom<T>>,
28}
29
30impl<T: DomObject> MutDom<T> {
31    /// Create a new `MutDom`.
32    pub fn new(initial: &T) -> MutDom<T> {
33        assert_in_script();
34        MutDom {
35            val: UnsafeCell::new(Dom::from_ref(initial)),
36        }
37    }
38
39    /// Set this `MutDom` to the given value.
40    pub fn set(&self, val: &T) {
41        assert_in_script();
42        unsafe {
43            *self.val.get() = Dom::from_ref(val);
44        }
45    }
46
47    /// Get the value in this `MutDom`.
48    pub fn get(&self) -> DomRoot<T> {
49        assert_in_script();
50        unsafe { DomRoot::from_ref(&*ptr::read(self.val.get())) }
51    }
52
53    /// Get the [`DomObject`] without rooting it as an [`UnrootedDom`]. This is safe as
54    /// the return value shares the lifetime of the provided [`NoGC`] This implies that
55    /// while the [`UnrootedDom`] is alive, garbage collection will not happen.
56    pub fn get_unrooted<'a>(&self, _: &'a NoGC) -> UnrootedDom<'a, T> {
57        assert_in_script();
58        UnrootedDom {
59            inner: unsafe { ptr::read(self.val.get()) },
60            _phantom: PhantomData,
61        }
62    }
63
64    /// Get a reference to the traced inner value of this [`MutDom`].
65    ///
66    /// # Safety
67    ///
68    /// - The caller *must not* modify the value of the [`MutDom`] while the
69    ///   reference is alive.
70    /// - The caller *must ensure* that no garbage collection happens while the
71    ///   reference is alive.
72    pub unsafe fn as_ref_unsafe(&self) -> &Dom<T> {
73        unsafe { &*self.val.get() }
74    }
75}
76
77impl<T: DomObject> MallocSizeOf for MutDom<T> {
78    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
79        // See comment on MallocSizeOf for Dom<T>.
80        0
81    }
82}
83
84impl<T: DomObject> PartialEq for MutDom<T> {
85    fn eq(&self, other: &Self) -> bool {
86        unsafe { *self.val.get() == *other.val.get() }
87    }
88}
89
90impl<T: DomObject + PartialEq> PartialEq<T> for MutDom<T> {
91    fn eq(&self, other: &T) -> bool {
92        unsafe { **self.val.get() == *other }
93    }
94}
95
96/// A reference to a [`DomObject`] that can live on the stack unrooted by having it
97/// inherit the lifetime of a [`NoGC`], which is a token that ensures that garbage
98/// collection will not happen.
99#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
100pub struct UnrootedDom<'a, T: DomObject> {
101    inner: Dom<T>,
102    _phantom: PhantomData<&'a ()>,
103}
104
105impl<'a, T: DomObject + std::fmt::Debug> std::fmt::Debug for UnrootedDom<'a, T> {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        self.inner.fmt(f)
108    }
109}
110
111impl<'a, T: DomObject> Clone for UnrootedDom<'a, T> {
112    fn clone(&self) -> Self {
113        Self {
114            inner: self.inner.clone(),
115            _phantom: PhantomData,
116        }
117    }
118}
119
120impl<'a, T: DomObject> UnrootedDom<'a, T> {
121    /// Construct an [`UnrootedDom`] with the lifetime of the given [`NoGC`] token. It is
122    /// safe to keep the returned value on the stack as it cannot outlive the lifetime of
123    /// the token and the token should ensure that no garbage collection will take place
124    /// as long as it is alive.
125    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
126    pub fn from_dom(object: Dom<T>, _no_gc: &'a NoGC) -> UnrootedDom<'a, T> {
127        UnrootedDom {
128            inner: object,
129            _phantom: PhantomData,
130        }
131    }
132}
133
134impl<'a, T: DomObject> Deref for UnrootedDom<'a, T> {
135    type Target = Dom<T>;
136
137    fn deref(&self) -> &Self::Target {
138        &self.inner
139    }
140}
141
142impl<'a, T: Castable> UnrootedDom<'a, T> {
143    /// Cast a DOM object root upwards to one of the interfaces it derives from.
144    pub fn upcast<U>(dom: UnrootedDom<'a, T>) -> UnrootedDom<'a, U>
145    where
146        U: Castable,
147        T: DerivedFrom<U>,
148    {
149        UnrootedDom {
150            inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
151            _phantom: PhantomData,
152        }
153    }
154
155    /// Cast a DOM object root downwards to one of the interfaces it might implement.
156    pub fn downcast<U>(dom: UnrootedDom<'a, T>) -> Option<UnrootedDom<'a, U>>
157    where
158        U: DerivedFrom<T>,
159    {
160        if dom.is::<U>() {
161            Some(UnrootedDom {
162                inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
163                _phantom: PhantomData,
164            })
165        } else {
166            None
167        }
168    }
169}
170
171impl<'a, T: DomObject> PartialEq<T> for UnrootedDom<'a, T> {
172    fn eq(&self, other: &T) -> bool {
173        self.inner == other
174    }
175}
176
177/// Forwards to `impl PartialEq for Dom<T>` which compares by pointer address
178impl<'a, 'b, T: DomObject> PartialEq<UnrootedDom<'a, T>> for UnrootedDom<'b, T> {
179    fn eq(&self, other: &UnrootedDom<'a, T>) -> bool {
180        self.inner == other.inner
181    }
182}
183
184impl<'a, T: DomObject> Eq for UnrootedDom<'a, T> {}
185
186/// Forwards to `impl Hash for Dom<T>` which hashes the pointer address
187impl<'a, T: DomObject> Hash for UnrootedDom<'a, T> {
188    fn hash<H: Hasher>(&self, state: &mut H) {
189        self.inner.hash(state);
190    }
191}
192
193/// A holder that provides interior mutability for GC-managed values such as
194/// `Dom<T>`, with nullability represented by an enclosing Option wrapper.
195/// Essentially a `Cell<Option<Dom<T>>>`, but safer.
196///
197/// This should only be used as a field in other DOM objects; see warning
198/// on `Dom<T>`.
199#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
200#[derive(JSTraceable)]
201pub struct MutNullableDom<T: DomObject> {
202    ptr: UnsafeCell<Option<Dom<T>>>,
203}
204
205impl<T: DomObject> MutNullableDom<T> {
206    /// Create a new `MutNullableDom`.
207    pub fn new(initial: Option<&T>) -> MutNullableDom<T> {
208        assert_in_script();
209        MutNullableDom {
210            ptr: UnsafeCell::new(initial.map(Dom::from_ref)),
211        }
212    }
213
214    /// Retrieve a copy of the current inner value. If it is `None`, it is
215    /// initialized with the result of `cb` first.
216    pub fn or_init<F>(&self, cb: F) -> DomRoot<T>
217    where
218        F: FnOnce() -> DomRoot<T>,
219    {
220        assert_in_script();
221        match self.get() {
222            Some(inner) => inner,
223            None => {
224                let inner = cb();
225                self.set(Some(&inner));
226                inner
227            },
228        }
229    }
230
231    /// Get a rooted ([`DomRoot`]) reference to the value contained in this
232    /// [`MutNullableDom`].
233    pub fn get(&self) -> Option<DomRoot<T>> {
234        assert_in_script();
235        unsafe { ptr::read(self.ptr.get()).map(|o| DomRoot::from_ref(&*o)) }
236    }
237
238    /// Get a reference to the traced inner value of this [`MutNullableDom`].
239    ///
240    /// # Safety
241    ///
242    /// - The caller *must not* modify the value of the [`MutNullableDom`] while the
243    ///   reference is alive.
244    /// - The caller *must ensure* that no garbage collection happens while the
245    ///   reference is alive.
246    pub unsafe fn as_ref_unsafe(&self) -> Option<&Dom<T>> {
247        unsafe { (*self.ptr.get()).as_ref() }
248    }
249
250    /// Get the `DomObject` without rooting it. Constructing an UnrootedDom. This is safe
251    /// as we take a reference to NoGC and bound the lifetime by NoGC bound. This implies that
252    /// while the `UnrootedDom` is alive we do not have a GC run.
253    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
254    pub fn get_unrooted<'a>(&self, _: &'a NoGC) -> Option<UnrootedDom<'a, T>> {
255        assert_in_script();
256        let ptr = unsafe { ptr::read(self.ptr.get()) };
257        ptr.map(|traced_value| Dom::from_ref(&*traced_value))
258            .map(|dom| UnrootedDom {
259                inner: dom,
260                _phantom: PhantomData,
261            })
262    }
263
264    /// Set this `MutNullableDom` to the given value.
265    pub fn set(&self, val: Option<&T>) {
266        assert_in_script();
267        unsafe {
268            *self.ptr.get() = val.map(|p| Dom::from_ref(p));
269        }
270    }
271
272    /// Gets the current value out of this object and sets it to `None`.
273    pub fn take(&self) -> Option<DomRoot<T>> {
274        let value = self.get();
275        self.set(None);
276        value
277    }
278
279    /// Sets the current value of this [`MutNullableDom`] to `None`.
280    pub fn clear(&self) {
281        self.set(None)
282    }
283
284    /// Runs the given callback on the object if it's not null.
285    pub fn if_is_some<F, R>(&self, cb: F) -> Option<&R>
286    where
287        F: FnOnce(&T) -> &R,
288    {
289        unsafe {
290            if let Some(ref value) = *self.ptr.get() {
291                Some(cb(value))
292            } else {
293                None
294            }
295        }
296    }
297}
298
299impl<T: DomObject> PartialEq for MutNullableDom<T> {
300    fn eq(&self, other: &Self) -> bool {
301        unsafe { *self.ptr.get() == *other.ptr.get() }
302    }
303}
304
305impl<T: DomObject> PartialEq<Option<&T>> for MutNullableDom<T> {
306    fn eq(&self, other: &Option<&T>) -> bool {
307        unsafe { *self.ptr.get() == other.map(Dom::from_ref) }
308    }
309}
310
311impl<T: DomObject> Default for MutNullableDom<T> {
312    fn default() -> MutNullableDom<T> {
313        assert_in_script();
314        MutNullableDom {
315            ptr: UnsafeCell::new(None),
316        }
317    }
318}
319
320impl<T: DomObject> MallocSizeOf for MutNullableDom<T> {
321    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
322        // See comment on MallocSizeOf for Dom<T>.
323        0
324    }
325}