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