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