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}