Skip to main content

script_bindings/
cell.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//! A shareable mutable container for the DOM.
6
7use std::cell::{BorrowError, BorrowMutError};
8pub use std::cell::{Ref, RefCell, RefMut};
9
10use js::jsapi::JSTracer;
11use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOfOps};
12
13use crate::CustomTraceable;
14use crate::assert::{assert_in_layout, assert_in_script};
15
16/// A mutable field in the DOM.
17///
18/// This extends the API of `std::cell::RefCell` to allow unsafe access in
19/// certain situations, with dynamic checking in debug builds.
20#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
21pub struct DomRefCell<T> {
22    value: RefCell<T>,
23}
24
25impl<T: MallocConditionalSizeOf> MallocConditionalSizeOf for DomRefCell<T> {
26    fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
27        self.value.borrow().conditional_size_of(ops)
28    }
29}
30
31// Functionality specific to Servo's `DomRefCell` type
32// ===================================================
33
34impl<T> DomRefCell<T> {
35    /// Return a reference to the contents.  For use in layout only.
36    ///
37    /// # Safety
38    ///
39    /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving
40    /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by
41    /// this method is alive is undefined behaviour.
42    ///
43    /// # Panics
44    ///
45    /// Panics if this is called from anywhere other than the layout thread
46    ///
47    /// Panics if the value is currently mutably borrowed.
48    #[expect(unsafe_code)]
49    pub unsafe fn borrow_for_layout(&self) -> &T {
50        assert_in_layout();
51        unsafe {
52            self.value
53                .try_borrow_unguarded()
54                .expect("cell is mutably borrowed")
55        }
56    }
57
58    /// Borrow the contents for the purpose of script deallocation.
59    ///
60    /// # Safety
61    ///
62    /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving
63    /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by
64    /// this method is alive is undefined behaviour.
65    ///
66    /// # Panics
67    ///
68    /// Panics if this is called from anywhere other than the script thread.
69    #[expect(unsafe_code)]
70    #[allow(clippy::mut_from_ref)]
71    pub unsafe fn borrow_for_script_deallocation(&self) -> &mut T {
72        assert_in_script();
73        unsafe { &mut *self.value.as_ptr() }
74    }
75
76    /// Mutably borrow a cell for layout. Ideally this would use
77    /// `RefCell::try_borrow_mut_unguarded` but that doesn't exist yet.
78    ///
79    /// # Safety
80    ///
81    /// Unlike RefCell::borrow, this method is unsafe because it does not return a Ref, thus leaving
82    /// the borrow flag untouched. Mutably borrowing the RefCell while the reference returned by
83    /// this method is alive is undefined behaviour.
84    ///
85    /// # Panics
86    ///
87    /// Panics if this is called from anywhere other than the layout thread.
88    #[expect(unsafe_code)]
89    #[allow(clippy::mut_from_ref)]
90    pub unsafe fn borrow_mut_for_layout(&self) -> &mut T {
91        assert_in_layout();
92        unsafe { &mut *self.value.as_ptr() }
93    }
94}
95
96// Functionality duplicated with `std::cell::RefCell`
97// ===================================================
98impl<T> DomRefCell<T> {
99    /// Create a new `DomRefCell` containing `value`.
100    pub fn new(value: T) -> DomRefCell<T> {
101        DomRefCell {
102            value: RefCell::new(value),
103        }
104    }
105
106    /// Immutably borrows the wrapped value.
107    ///
108    /// The borrow lasts until the returned `Ref` exits scope. Multiple
109    /// immutable borrows can be taken out at the same time.
110    ///
111    /// # Panics
112    ///
113    /// Panics if the value is currently mutably borrowed.
114    /// Panics if this is called from anywhere other than the script thread.
115    /// Use borrow_for_layout if the borrowed data might used during layout.
116    #[track_caller]
117    pub fn borrow(&self) -> Ref<'_, T> {
118        assert_in_script();
119        self.value.borrow()
120    }
121
122    /// Mutably borrows the wrapped value.
123    ///
124    /// The borrow lasts until the returned `RefMut` exits scope. The value
125    /// cannot be borrowed while this borrow is active.
126    ///
127    /// # Panics
128    ///
129    /// Panics if the value is currently borrowed.
130    /// Panics if this is called from anywhere other than the script thread.
131    #[track_caller]
132    pub fn borrow_mut(&self) -> RefMut<'_, T> {
133        assert_in_script();
134        self.value.borrow_mut()
135    }
136
137    /// Attempts to immutably borrow the wrapped value.
138    ///
139    /// The borrow lasts until the returned `Ref` exits scope. Multiple
140    /// immutable borrows can be taken out at the same time.
141    ///
142    /// Returns `None` if the value is currently mutably borrowed.
143    ///
144    /// # Panics
145    ///
146    /// Panics if this is called off the script thread.
147    pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
148        assert_in_script();
149        self.value.try_borrow()
150    }
151
152    /// Mutably borrows the wrapped value.
153    ///
154    /// The borrow lasts until the returned `RefMut` exits scope. The value
155    /// cannot be borrowed while this borrow is active.
156    ///
157    /// Returns `None` if the value is currently borrowed.
158    ///
159    /// # Panics
160    ///
161    /// Panics if this is called off the script thread.
162    pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
163        assert_in_script();
164        self.value.try_borrow_mut()
165    }
166}
167
168impl<T: Default> DomRefCell<T> {
169    /// Takes the wrapped value, leaving `Default::default()` in its place.
170    ///
171    /// # Panics
172    ///
173    /// Panics if the value is currently borrowed.
174    pub fn take(&self) -> T {
175        self.value.take()
176    }
177}
178
179unsafe impl<T: CustomTraceable> CustomTraceable for DomRefCell<T> {
180    unsafe fn trace(&self, trc: *mut JSTracer) {
181        unsafe { (*self).borrow().trace(trc) }
182    }
183}
184
185unsafe impl<T: js::gc::Traceable> js::gc::Traceable for DomRefCell<T> {
186    unsafe fn trace(&self, trc: *mut JSTracer) {
187        unsafe { (*self).borrow().trace(trc) };
188    }
189}