script/dom/bindings/
root.rs1use std::cell::{OnceCell, UnsafeCell};
28use std::default::Default;
29use std::hash::{Hash, Hasher};
30use std::{mem, ptr};
31
32use js::jsapi::{Heap, JSObject, JSTracer, Value};
33use js::rust::HandleValue;
34use layout_api::TrustedNodeAddress;
35use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
36pub(crate) use script_bindings::root::*;
37use style::thread_state;
38
39use crate::dom::bindings::conversions::DerivedFrom;
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::reflector::DomObject;
42use crate::dom::bindings::trace::JSTraceable;
43use crate::dom::node::Node;
44
45pub(crate) trait ToLayout<T> {
46 unsafe fn to_layout(&self) -> LayoutDom<'_, T>;
52}
53
54impl<T: DomObject> ToLayout<T> for Dom<T> {
55 unsafe fn to_layout(&self) -> LayoutDom<'_, T> {
56 assert_in_layout();
57 LayoutDom {
58 value: unsafe { self.as_ptr().as_ref().unwrap() },
59 }
60 }
61}
62
63#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
66#[repr(transparent)]
67pub(crate) struct LayoutDom<'dom, T> {
68 value: &'dom T,
69}
70
71impl<'dom, T> LayoutDom<'dom, T>
72where
73 T: Castable,
74{
75 pub(crate) fn upcast<U>(&self) -> LayoutDom<'dom, U>
77 where
78 U: Castable,
79 T: DerivedFrom<U>,
80 {
81 assert_in_layout();
82 LayoutDom {
83 value: self.value.upcast::<U>(),
84 }
85 }
86
87 pub(crate) fn downcast<U>(&self) -> Option<LayoutDom<'dom, U>>
89 where
90 U: DerivedFrom<T>,
91 {
92 assert_in_layout();
93 self.value.downcast::<U>().map(|value| LayoutDom { value })
94 }
95
96 pub(crate) fn is<U>(&self) -> bool
98 where
99 U: DerivedFrom<T>,
100 {
101 assert_in_layout();
102 self.value.is::<U>()
103 }
104}
105
106impl<T> LayoutDom<'_, T>
107where
108 T: DomObject,
109{
110 pub(crate) unsafe fn get_jsobject(&self) -> *mut JSObject {
112 assert_in_layout();
113 self.value.reflector().get_jsobject().get()
114 }
115}
116
117impl<T> Copy for LayoutDom<'_, T> {}
118
119impl<T> PartialEq for LayoutDom<'_, T> {
120 fn eq(&self, other: &Self) -> bool {
121 std::ptr::eq(self.value, other.value)
122 }
123}
124
125impl<T> Eq for LayoutDom<'_, T> {}
126
127impl<T> Hash for LayoutDom<'_, T> {
128 fn hash<H: Hasher>(&self, state: &mut H) {
129 (self.value as *const T).hash(state)
130 }
131}
132
133impl<T> Clone for LayoutDom<'_, T> {
134 #[inline]
135 #[allow(clippy::non_canonical_clone_impl)]
136 fn clone(&self) -> Self {
137 assert_in_layout();
138 *self
139 }
140}
141
142impl LayoutDom<'_, Node> {
143 pub(crate) unsafe fn from_trusted_node_address(inner: TrustedNodeAddress) -> Self {
146 assert_in_layout();
147 let TrustedNodeAddress(addr) = inner;
148 LayoutDom {
149 value: unsafe { &*(addr as *const Node) },
150 }
151 }
152}
153
154#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
160#[derive(JSTraceable)]
161pub(crate) struct MutDom<T: DomObject> {
162 val: UnsafeCell<Dom<T>>,
163}
164
165impl<T: DomObject> MutDom<T> {
166 pub(crate) fn new(initial: &T) -> MutDom<T> {
168 assert_in_script();
169 MutDom {
170 val: UnsafeCell::new(Dom::from_ref(initial)),
171 }
172 }
173
174 pub(crate) fn set(&self, val: &T) {
176 assert_in_script();
177 unsafe {
178 *self.val.get() = Dom::from_ref(val);
179 }
180 }
181
182 pub(crate) fn get(&self) -> DomRoot<T> {
184 assert_in_script();
185 unsafe { DomRoot::from_ref(&*ptr::read(self.val.get())) }
186 }
187}
188
189impl<T: DomObject> MallocSizeOf for MutDom<T> {
190 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
191 0
193 }
194}
195
196impl<T: DomObject> PartialEq for MutDom<T> {
197 fn eq(&self, other: &Self) -> bool {
198 unsafe { *self.val.get() == *other.val.get() }
199 }
200}
201
202impl<T: DomObject + PartialEq> PartialEq<T> for MutDom<T> {
203 fn eq(&self, other: &T) -> bool {
204 unsafe { **self.val.get() == *other }
205 }
206}
207
208pub(crate) fn assert_in_layout() {
209 debug_assert!(thread_state::get().is_layout());
210}
211
212#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
219#[derive(JSTraceable)]
220pub(crate) struct MutNullableDom<T: DomObject> {
221 ptr: UnsafeCell<Option<Dom<T>>>,
222}
223
224impl<T: DomObject> MutNullableDom<T> {
225 pub(crate) fn new(initial: Option<&T>) -> MutNullableDom<T> {
227 assert_in_script();
228 MutNullableDom {
229 ptr: UnsafeCell::new(initial.map(Dom::from_ref)),
230 }
231 }
232
233 pub(crate) fn or_init<F>(&self, cb: F) -> DomRoot<T>
236 where
237 F: FnOnce() -> DomRoot<T>,
238 {
239 assert_in_script();
240 match self.get() {
241 Some(inner) => inner,
242 None => {
243 let inner = cb();
244 self.set(Some(&inner));
245 inner
246 },
247 }
248 }
249
250 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
253 pub(crate) unsafe fn get_inner_as_layout(&self) -> Option<LayoutDom<'_, T>> {
254 assert_in_layout();
255 unsafe { (*self.ptr.get()).as_ref().map(|js| js.to_layout()) }
256 }
257
258 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
260 pub(crate) fn get(&self) -> Option<DomRoot<T>> {
261 assert_in_script();
262 unsafe { ptr::read(self.ptr.get()).map(|o| DomRoot::from_ref(&*o)) }
263 }
264
265 pub(crate) fn set(&self, val: Option<&T>) {
267 assert_in_script();
268 unsafe {
269 *self.ptr.get() = val.map(|p| Dom::from_ref(p));
270 }
271 }
272
273 pub(crate) fn take(&self) -> Option<DomRoot<T>> {
275 let value = self.get();
276 self.set(None);
277 value
278 }
279
280 pub(crate) fn if_is_some<F, R>(&self, cb: F) -> Option<&R>
282 where
283 F: FnOnce(&T) -> &R,
284 {
285 unsafe {
286 if let Some(ref value) = *self.ptr.get() {
287 Some(cb(value))
288 } else {
289 None
290 }
291 }
292 }
293}
294
295impl<T: DomObject> PartialEq for MutNullableDom<T> {
296 fn eq(&self, other: &Self) -> bool {
297 unsafe { *self.ptr.get() == *other.ptr.get() }
298 }
299}
300
301impl<T: DomObject> PartialEq<Option<&T>> for MutNullableDom<T> {
302 fn eq(&self, other: &Option<&T>) -> bool {
303 unsafe { *self.ptr.get() == other.map(Dom::from_ref) }
304 }
305}
306
307impl<T: DomObject> Default for MutNullableDom<T> {
308 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
309 fn default() -> MutNullableDom<T> {
310 assert_in_script();
311 MutNullableDom {
312 ptr: UnsafeCell::new(None),
313 }
314 }
315}
316
317impl<T: DomObject> MallocSizeOf for MutNullableDom<T> {
318 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
319 0
321 }
322}
323
324#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
331pub(crate) struct DomOnceCell<T: DomObject> {
332 ptr: OnceCell<Dom<T>>,
333}
334
335impl<T> DomOnceCell<T>
336where
337 T: DomObject,
338{
339 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
342 pub(crate) fn init_once<F>(&self, cb: F) -> &T
343 where
344 F: FnOnce() -> DomRoot<T>,
345 {
346 assert_in_script();
347 self.ptr.get_or_init(|| Dom::from_ref(&cb()))
348 }
349}
350
351impl<T: DomObject> Default for DomOnceCell<T> {
352 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
353 fn default() -> DomOnceCell<T> {
354 assert_in_script();
355 DomOnceCell {
356 ptr: OnceCell::new(),
357 }
358 }
359}
360
361impl<T: DomObject> MallocSizeOf for DomOnceCell<T> {
362 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
363 0
365 }
366}
367
368#[cfg_attr(crown, allow(crown::unrooted_must_root))]
369unsafe impl<T: DomObject> JSTraceable for DomOnceCell<T> {
370 unsafe fn trace(&self, trc: *mut JSTracer) {
371 if let Some(ptr) = self.ptr.get() {
372 unsafe { ptr.trace(trc) };
373 }
374 }
375}
376
377impl<'dom, T> LayoutDom<'dom, T>
378where
379 T: 'dom + DomObject,
380{
381 pub(crate) fn unsafe_get(self) -> &'dom T {
384 assert_in_layout();
385 self.value
386 }
387
388 pub(crate) unsafe fn to_layout_slice(slice: &'dom [Dom<T>]) -> &'dom [LayoutDom<'dom, T>] {
391 let _ = mem::transmute::<Dom<T>, LayoutDom<T>>;
394 unsafe { &*(slice as *const [Dom<T>] as *const [LayoutDom<T>]) }
395 }
396}
397
398pub trait AsHandleValue<'a> {
404 fn as_handle_value(&'a self) -> HandleValue<'a>;
405}
406
407impl<'a> AsHandleValue<'a> for Heap<Value> {
408 #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
409 fn as_handle_value(&'a self) -> HandleValue<'a> {
410 unsafe { HandleValue::from_marked_location(self.ptr.get() as *const _) }
413 }
414}