1use std::cell::UnsafeCell;
5use std::ops::Deref;
6use std::{mem, ptr};
7
8use js::context::NoGC;
9use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
10
11use crate::DomObject;
12use crate::assert::{assert_in_layout, assert_in_script};
13use crate::conversions::DerivedFrom;
14use crate::inheritance::Castable;
15use crate::root::{Dom, DomRoot};
16
17pub trait ToLayout<'dom, T: DomObject, L: LayoutFromRaw<'dom, T>> {
18 unsafe fn to_layout(&self) -> L;
24}
25
26impl<'dom, T: DomObject, L: LayoutFromRaw<'dom, T>> ToLayout<'dom, T, L> for Dom<T> {
27 unsafe fn to_layout(&self) -> L {
28 assert_in_layout();
29 L::from_raw(unsafe { self.as_ptr().as_ref().unwrap() })
30 }
31}
32
33#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
39#[derive(JSTraceable)]
40pub struct MutDom<T: DomObject> {
41 val: UnsafeCell<Dom<T>>,
42}
43
44impl<T: DomObject> MutDom<T> {
45 pub fn new(initial: &T) -> MutDom<T> {
47 assert_in_script();
48 MutDom {
49 val: UnsafeCell::new(Dom::from_ref(initial)),
50 }
51 }
52
53 pub fn set(&self, val: &T) {
55 assert_in_script();
56 unsafe {
57 *self.val.get() = Dom::from_ref(val);
58 }
59 }
60
61 pub fn get(&self) -> DomRoot<T> {
63 assert_in_script();
64 unsafe { DomRoot::from_ref(&*ptr::read(self.val.get())) }
65 }
66}
67
68impl<T: DomObject> MallocSizeOf for MutDom<T> {
69 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
70 0
72 }
73}
74
75impl<T: DomObject> PartialEq for MutDom<T> {
76 fn eq(&self, other: &Self) -> bool {
77 unsafe { *self.val.get() == *other.val.get() }
78 }
79}
80
81impl<T: DomObject + PartialEq> PartialEq<T> for MutDom<T> {
82 fn eq(&self, other: &T) -> bool {
83 unsafe { **self.val.get() == *other }
84 }
85}
86
87#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
90pub struct UnrootedDom<'a, T: DomObject> {
91 inner: Dom<T>,
92 no_gc: &'a NoGC,
93}
94
95impl<'a, T: DomObject> UnrootedDom<'a, T> {
96 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
98 pub fn from_dom(object: Dom<T>, no_gc: &'a NoGC) -> UnrootedDom<'a, T> {
99 UnrootedDom {
100 inner: object,
101 no_gc,
102 }
103 }
104}
105
106impl<'a, T: DomObject> Deref for UnrootedDom<'a, T> {
107 type Target = Dom<T>;
108
109 fn deref(&self) -> &Self::Target {
110 &self.inner
111 }
112}
113
114impl<'a, T: Castable> UnrootedDom<'a, T> {
118 pub fn upcast<U>(dom: UnrootedDom<'a, T>) -> UnrootedDom<'a, U>
120 where
121 U: Castable,
122 T: DerivedFrom<U>,
123 {
124 UnrootedDom {
125 inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
126 no_gc: dom.no_gc,
127 }
128 }
129
130 pub fn downcast<U>(dom: UnrootedDom<'a, T>) -> Option<UnrootedDom<'a, U>>
132 where
133 U: DerivedFrom<T>,
134 {
135 if dom.is::<U>() {
136 Some(UnrootedDom {
137 inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
138 no_gc: dom.no_gc,
139 })
140 } else {
141 None
142 }
143 }
144}
145
146impl<'a, T: DomObject> PartialEq<T> for UnrootedDom<'a, T> {
147 fn eq(&self, other: &T) -> bool {
148 self.inner == other
149 }
150}
151
152impl<'a, T: DomObject> PartialEq<UnrootedDom<'a, T>> for UnrootedDom<'a, T> {
153 fn eq(&self, other: &UnrootedDom<'a, T>) -> bool {
154 self.inner == other.inner
155 }
156}
157
158pub unsafe trait LayoutFromRaw<'dom, T: DomObject> {
164 fn from_raw(d: &'dom T) -> Self;
165}
166
167#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
174#[derive(JSTraceable)]
175pub struct MutNullableDom<T: DomObject> {
176 ptr: UnsafeCell<Option<Dom<T>>>,
177}
178
179impl<T: DomObject> MutNullableDom<T> {
180 pub fn new(initial: Option<&T>) -> MutNullableDom<T> {
182 assert_in_script();
183 MutNullableDom {
184 ptr: UnsafeCell::new(initial.map(Dom::from_ref)),
185 }
186 }
187
188 pub fn or_init<F>(&self, cb: F) -> DomRoot<T>
191 where
192 F: FnOnce() -> DomRoot<T>,
193 {
194 assert_in_script();
195 match self.get() {
196 Some(inner) => inner,
197 None => {
198 let inner = cb();
199 self.set(Some(&inner));
200 inner
201 },
202 }
203 }
204
205 pub unsafe fn get_inner_as_layout<'dom, L: LayoutFromRaw<'dom, T>>(&'dom self) -> Option<L> {
210 assert_in_layout();
211 unsafe { (*self.ptr.get()).as_ref().map(|js| js.to_layout()) }
212 }
213
214 pub fn get(&self) -> Option<DomRoot<T>> {
216 assert_in_script();
217 unsafe { ptr::read(self.ptr.get()).map(|o| DomRoot::from_ref(&*o)) }
218 }
219
220 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
224 pub fn get_unrooted<'a>(&self, no_gc: &'a NoGC) -> Option<UnrootedDom<'a, T>> {
225 assert_in_script();
226 let ptr = unsafe { ptr::read(self.ptr.get()) };
227 ptr.map(|o| Dom::from_ref(&*o))
228 .map(|dom| UnrootedDom { inner: dom, no_gc })
229 }
230
231 pub fn set(&self, val: Option<&T>) {
233 assert_in_script();
234 unsafe {
235 *self.ptr.get() = val.map(|p| Dom::from_ref(p));
236 }
237 }
238
239 pub fn take(&self) -> Option<DomRoot<T>> {
241 let value = self.get();
242 self.set(None);
243 value
244 }
245
246 pub fn clear(&self) {
248 self.set(None)
249 }
250
251 pub fn if_is_some<F, R>(&self, cb: F) -> Option<&R>
253 where
254 F: FnOnce(&T) -> &R,
255 {
256 unsafe {
257 if let Some(ref value) = *self.ptr.get() {
258 Some(cb(value))
259 } else {
260 None
261 }
262 }
263 }
264}
265
266impl<T: DomObject> PartialEq for MutNullableDom<T> {
267 fn eq(&self, other: &Self) -> bool {
268 unsafe { *self.ptr.get() == *other.ptr.get() }
269 }
270}
271
272impl<T: DomObject> PartialEq<Option<&T>> for MutNullableDom<T> {
273 fn eq(&self, other: &Option<&T>) -> bool {
274 unsafe { *self.ptr.get() == other.map(Dom::from_ref) }
275 }
276}
277
278impl<T: DomObject> Default for MutNullableDom<T> {
279 fn default() -> MutNullableDom<T> {
280 assert_in_script();
281 MutNullableDom {
282 ptr: UnsafeCell::new(None),
283 }
284 }
285}
286
287impl<T: DomObject> MallocSizeOf for MutNullableDom<T> {
288 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
289 0
291 }
292}