1use std::cell::{OnceCell, UnsafeCell};
28use std::default::Default;
29use std::hash::{Hash, Hasher};
30use std::ops::Deref;
31use std::{mem, ptr};
32
33use js::context::NoGC;
34use js::jsapi::{Heap, JSObject, JSTracer, Value};
35use js::rust::HandleValue;
36use layout_api::TrustedNodeAddress;
37use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
38pub(crate) use script_bindings::root::*;
39use style::thread_state;
40
41use crate::dom::bindings::conversions::DerivedFrom;
42use crate::dom::bindings::inheritance::Castable;
43use crate::dom::bindings::reflector::DomObject;
44use crate::dom::bindings::trace::JSTraceable;
45use crate::dom::node::Node;
46
47pub(crate) trait ToLayout<T> {
48 unsafe fn to_layout(&self) -> LayoutDom<'_, T>;
54}
55
56impl<T: DomObject> ToLayout<T> for Dom<T> {
57 unsafe fn to_layout(&self) -> LayoutDom<'_, T> {
58 assert_in_layout();
59 LayoutDom {
60 value: unsafe { self.as_ptr().as_ref().unwrap() },
61 }
62 }
63}
64
65#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
68#[repr(transparent)]
69pub(crate) struct LayoutDom<'dom, T> {
70 value: &'dom T,
71}
72
73impl<'dom, T> LayoutDom<'dom, T>
74where
75 T: Castable,
76{
77 pub(crate) fn upcast<U>(&self) -> LayoutDom<'dom, U>
79 where
80 U: Castable,
81 T: DerivedFrom<U>,
82 {
83 assert_in_layout();
84 LayoutDom {
85 value: self.value.upcast::<U>(),
86 }
87 }
88
89 pub(crate) fn downcast<U>(&self) -> Option<LayoutDom<'dom, U>>
91 where
92 U: DerivedFrom<T>,
93 {
94 assert_in_layout();
95 self.value.downcast::<U>().map(|value| LayoutDom { value })
96 }
97
98 pub(crate) fn is<U>(&self) -> bool
100 where
101 U: DerivedFrom<T>,
102 {
103 assert_in_layout();
104 self.value.is::<U>()
105 }
106
107 pub(crate) unsafe fn as_ref(self) -> &'dom T {
113 self.value
114 }
115}
116
117impl<T> LayoutDom<'_, T>
118where
119 T: DomObject,
120{
121 pub(crate) unsafe fn get_jsobject(&self) -> *mut JSObject {
123 assert_in_layout();
124 self.value.reflector().get_jsobject().get()
125 }
126}
127
128impl<T> Copy for LayoutDom<'_, T> {}
129
130impl<T> PartialEq for LayoutDom<'_, T> {
131 fn eq(&self, other: &Self) -> bool {
132 std::ptr::eq(self.value, other.value)
133 }
134}
135
136impl<T> Eq for LayoutDom<'_, T> {}
137
138impl<T> Hash for LayoutDom<'_, T> {
139 fn hash<H: Hasher>(&self, state: &mut H) {
140 (self.value as *const T).hash(state)
141 }
142}
143
144#[expect(clippy::non_canonical_clone_impl)]
145impl<T> Clone for LayoutDom<'_, T> {
146 #[inline]
147 fn clone(&self) -> Self {
148 assert_in_layout();
149 *self
150 }
151}
152
153impl LayoutDom<'_, Node> {
154 pub(crate) unsafe fn from_trusted_node_address(inner: TrustedNodeAddress) -> Self {
157 assert_in_layout();
158 let TrustedNodeAddress(addr) = inner;
159 LayoutDom {
160 value: unsafe { &*(addr as *const Node) },
161 }
162 }
163}
164
165#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
171#[derive(JSTraceable)]
172pub(crate) struct MutDom<T: DomObject> {
173 val: UnsafeCell<Dom<T>>,
174}
175
176impl<T: DomObject> MutDom<T> {
177 pub(crate) fn new(initial: &T) -> MutDom<T> {
179 assert_in_script();
180 MutDom {
181 val: UnsafeCell::new(Dom::from_ref(initial)),
182 }
183 }
184
185 pub(crate) fn set(&self, val: &T) {
187 assert_in_script();
188 unsafe {
189 *self.val.get() = Dom::from_ref(val);
190 }
191 }
192
193 pub(crate) fn get(&self) -> DomRoot<T> {
195 assert_in_script();
196 unsafe { DomRoot::from_ref(&*ptr::read(self.val.get())) }
197 }
198}
199
200impl<T: DomObject> MallocSizeOf for MutDom<T> {
201 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
202 0
204 }
205}
206
207impl<T: DomObject> PartialEq for MutDom<T> {
208 fn eq(&self, other: &Self) -> bool {
209 unsafe { *self.val.get() == *other.val.get() }
210 }
211}
212
213impl<T: DomObject + PartialEq> PartialEq<T> for MutDom<T> {
214 fn eq(&self, other: &T) -> bool {
215 unsafe { **self.val.get() == *other }
216 }
217}
218
219pub(crate) fn assert_in_layout() {
220 debug_assert!(thread_state::get().is_layout());
221}
222
223#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
226pub(crate) struct UnrootedDom<'a, T: DomObject> {
227 inner: Dom<T>,
228 no_gc: &'a NoGC,
229}
230
231impl<'a, T: DomObject> UnrootedDom<'a, T> {
232 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
234 pub(crate) fn from_dom(object: Dom<T>, no_gc: &'a NoGC) -> UnrootedDom<'a, T> {
235 UnrootedDom {
236 inner: object,
237 no_gc,
238 }
239 }
240}
241
242impl<'a, T: DomObject> Deref for UnrootedDom<'a, T> {
243 type Target = Dom<T>;
244
245 fn deref(&self) -> &Self::Target {
246 &self.inner
247 }
248}
249
250impl<'a, T: Castable> UnrootedDom<'a, T> {
254 pub fn upcast<U>(dom: UnrootedDom<'a, T>) -> UnrootedDom<'a, U>
256 where
257 U: Castable,
258 T: DerivedFrom<U>,
259 {
260 UnrootedDom {
261 inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
262 no_gc: dom.no_gc,
263 }
264 }
265
266 pub fn downcast<U>(dom: UnrootedDom<'a, T>) -> Option<UnrootedDom<'a, U>>
268 where
269 U: DerivedFrom<T>,
270 {
271 if dom.is::<U>() {
272 Some(UnrootedDom {
273 inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
274 no_gc: dom.no_gc,
275 })
276 } else {
277 None
278 }
279 }
280}
281
282impl<'a, T: DomObject> PartialEq<&T> for UnrootedDom<'a, T> {
283 fn eq(&self, other: &&T) -> bool {
284 self.inner == Dom::from_ref(*other)
285 }
286}
287
288#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
295#[derive(JSTraceable)]
296pub(crate) struct MutNullableDom<T: DomObject> {
297 ptr: UnsafeCell<Option<Dom<T>>>,
298}
299
300impl<T: DomObject> MutNullableDom<T> {
301 pub(crate) fn new(initial: Option<&T>) -> MutNullableDom<T> {
303 assert_in_script();
304 MutNullableDom {
305 ptr: UnsafeCell::new(initial.map(Dom::from_ref)),
306 }
307 }
308
309 pub(crate) fn or_init<F>(&self, cb: F) -> DomRoot<T>
312 where
313 F: FnOnce() -> DomRoot<T>,
314 {
315 assert_in_script();
316 match self.get() {
317 Some(inner) => inner,
318 None => {
319 let inner = cb();
320 self.set(Some(&inner));
321 inner
322 },
323 }
324 }
325
326 pub(crate) unsafe fn get_inner_as_layout(&self) -> Option<LayoutDom<'_, T>> {
329 assert_in_layout();
330 unsafe { (*self.ptr.get()).as_ref().map(|js| js.to_layout()) }
331 }
332
333 pub(crate) fn get(&self) -> Option<DomRoot<T>> {
335 assert_in_script();
336 unsafe { ptr::read(self.ptr.get()).map(|o| DomRoot::from_ref(&*o)) }
337 }
338
339 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
343 pub(crate) fn get_unrooted<'a>(&self, no_gc: &'a NoGC) -> Option<UnrootedDom<'a, T>> {
344 assert_in_script();
345 let ptr = unsafe { ptr::read(self.ptr.get()) };
346 ptr.map(|o| Dom::from_ref(&*o))
347 .map(|dom| UnrootedDom { inner: dom, no_gc })
348 }
349
350 pub(crate) fn set(&self, val: Option<&T>) {
352 assert_in_script();
353 unsafe {
354 *self.ptr.get() = val.map(|p| Dom::from_ref(p));
355 }
356 }
357
358 pub(crate) fn take(&self) -> Option<DomRoot<T>> {
360 let value = self.get();
361 self.set(None);
362 value
363 }
364
365 pub(crate) fn clear(&self) {
367 self.set(None)
368 }
369
370 pub(crate) fn if_is_some<F, R>(&self, cb: F) -> Option<&R>
372 where
373 F: FnOnce(&T) -> &R,
374 {
375 unsafe {
376 if let Some(ref value) = *self.ptr.get() {
377 Some(cb(value))
378 } else {
379 None
380 }
381 }
382 }
383}
384
385impl<T: DomObject> PartialEq for MutNullableDom<T> {
386 fn eq(&self, other: &Self) -> bool {
387 unsafe { *self.ptr.get() == *other.ptr.get() }
388 }
389}
390
391impl<T: DomObject> PartialEq<Option<&T>> for MutNullableDom<T> {
392 fn eq(&self, other: &Option<&T>) -> bool {
393 unsafe { *self.ptr.get() == other.map(Dom::from_ref) }
394 }
395}
396
397impl<T: DomObject> Default for MutNullableDom<T> {
398 fn default() -> MutNullableDom<T> {
399 assert_in_script();
400 MutNullableDom {
401 ptr: UnsafeCell::new(None),
402 }
403 }
404}
405
406impl<T: DomObject> MallocSizeOf for MutNullableDom<T> {
407 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
408 0
410 }
411}
412
413#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
420pub(crate) struct DomOnceCell<T: DomObject> {
421 ptr: OnceCell<Dom<T>>,
422}
423
424impl<T> DomOnceCell<T>
425where
426 T: DomObject,
427{
428 pub(crate) fn init_once<F>(&self, cb: F) -> &T
431 where
432 F: FnOnce() -> DomRoot<T>,
433 {
434 assert_in_script();
435 self.ptr.get_or_init(|| Dom::from_ref(&cb()))
436 }
437}
438
439impl<T: DomObject> Default for DomOnceCell<T> {
440 fn default() -> DomOnceCell<T> {
441 assert_in_script();
442 DomOnceCell {
443 ptr: OnceCell::new(),
444 }
445 }
446}
447
448impl<T: DomObject> MallocSizeOf for DomOnceCell<T> {
449 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
450 0
452 }
453}
454
455unsafe impl<T: DomObject> JSTraceable for DomOnceCell<T> {
456 unsafe fn trace(&self, trc: *mut JSTracer) {
457 if let Some(ptr) = self.ptr.get() {
458 unsafe { ptr.trace(trc) };
459 }
460 }
461}
462
463impl<'dom, T> LayoutDom<'dom, T>
464where
465 T: 'dom + DomObject,
466{
467 pub(crate) fn unsafe_get(self) -> &'dom T {
470 assert_in_layout();
471 self.value
472 }
473
474 pub(crate) unsafe fn to_layout_slice(slice: &'dom [Dom<T>]) -> &'dom [LayoutDom<'dom, T>] {
477 let _ = mem::transmute::<Dom<T>, LayoutDom<T>>;
480 unsafe { &*(slice as *const [Dom<T>] as *const [LayoutDom<T>]) }
481 }
482}
483
484pub trait AsHandleValue<'a> {
490 fn as_handle_value(&'a self) -> HandleValue<'a>;
491}
492
493impl<'a> AsHandleValue<'a> for Heap<Value> {
494 #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
495 fn as_handle_value(&'a self) -> HandleValue<'a> {
496 unsafe { HandleValue::from_marked_location(self.ptr.get() as *const _) }
499 }
500}