use std::cell::UnsafeCell;
use std::ops::Deref;
use std::{mem, ptr};
use js::context::NoGC;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use crate::DomObject;
use crate::assert::{assert_in_layout, assert_in_script};
use crate::conversions::DerivedFrom;
use crate::inheritance::Castable;
use crate::root::{Dom, DomRoot};
pub trait ToLayout<'dom, T: DomObject, L: LayoutFromRaw<'dom, T>> {
unsafe fn to_layout(&self) -> L;
}
impl<'dom, T: DomObject, L: LayoutFromRaw<'dom, T>> ToLayout<'dom, T, L> for Dom<T> {
unsafe fn to_layout(&self) -> L {
assert_in_layout();
L::from_raw(unsafe { self.as_ptr().as_ref().unwrap() })
}
}
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(JSTraceable)]
pub struct MutDom<T: DomObject> {
val: UnsafeCell<Dom<T>>,
}
impl<T: DomObject> MutDom<T> {
pub fn new(initial: &T) -> MutDom<T> {
assert_in_script();
MutDom {
val: UnsafeCell::new(Dom::from_ref(initial)),
}
}
pub fn set(&self, val: &T) {
assert_in_script();
unsafe {
*self.val.get() = Dom::from_ref(val);
}
}
pub fn get(&self) -> DomRoot<T> {
assert_in_script();
unsafe { DomRoot::from_ref(&*ptr::read(self.val.get())) }
}
}
impl<T: DomObject> MallocSizeOf for MutDom<T> {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}
impl<T: DomObject> PartialEq for MutDom<T> {
fn eq(&self, other: &Self) -> bool {
unsafe { *self.val.get() == *other.val.get() }
}
}
impl<T: DomObject + PartialEq> PartialEq<T> for MutDom<T> {
fn eq(&self, other: &T) -> bool {
unsafe { **self.val.get() == *other }
}
}
#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
pub struct UnrootedDom<'a, T: DomObject> {
inner: Dom<T>,
no_gc: &'a NoGC,
}
impl<'a, T: DomObject> UnrootedDom<'a, T> {
#[cfg_attr(crown, expect(crown::unrooted_must_root))]
pub fn from_dom(object: Dom<T>, no_gc: &'a NoGC) -> UnrootedDom<'a, T> {
UnrootedDom {
inner: object,
no_gc,
}
}
}
impl<'a, T: DomObject> Deref for UnrootedDom<'a, T> {
type Target = Dom<T>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a, T: Castable> UnrootedDom<'a, T> {
pub fn upcast<U>(dom: UnrootedDom<'a, T>) -> UnrootedDom<'a, U>
where
U: Castable,
T: DerivedFrom<U>,
{
UnrootedDom {
inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
no_gc: dom.no_gc,
}
}
pub fn downcast<U>(dom: UnrootedDom<'a, T>) -> Option<UnrootedDom<'a, U>>
where
U: DerivedFrom<T>,
{
if dom.is::<U>() {
Some(UnrootedDom {
inner: unsafe { mem::transmute::<Dom<T>, Dom<U>>(dom.inner) },
no_gc: dom.no_gc,
})
} else {
None
}
}
}
impl<'a, T: DomObject> PartialEq<T> for UnrootedDom<'a, T> {
fn eq(&self, other: &T) -> bool {
self.inner == other
}
}
impl<'a, T: DomObject> PartialEq<UnrootedDom<'a, T>> for UnrootedDom<'a, T> {
fn eq(&self, other: &UnrootedDom<'a, T>) -> bool {
self.inner == other.inner
}
}
pub unsafe trait LayoutFromRaw<'dom, T: DomObject> {
fn from_raw(d: &'dom T) -> Self;
}
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(JSTraceable)]
pub struct MutNullableDom<T: DomObject> {
ptr: UnsafeCell<Option<Dom<T>>>,
}
impl<T: DomObject> MutNullableDom<T> {
pub fn new(initial: Option<&T>) -> MutNullableDom<T> {
assert_in_script();
MutNullableDom {
ptr: UnsafeCell::new(initial.map(Dom::from_ref)),
}
}
pub fn or_init<F>(&self, cb: F) -> DomRoot<T>
where
F: FnOnce() -> DomRoot<T>,
{
assert_in_script();
match self.get() {
Some(inner) => inner,
None => {
let inner = cb();
self.set(Some(&inner));
inner
},
}
}
pub unsafe fn get_inner_as_layout<'dom, L: LayoutFromRaw<'dom, T>>(&'dom self) -> Option<L> {
assert_in_layout();
unsafe { (*self.ptr.get()).as_ref().map(|js| js.to_layout()) }
}
pub fn get(&self) -> Option<DomRoot<T>> {
assert_in_script();
unsafe { ptr::read(self.ptr.get()).map(|o| DomRoot::from_ref(&*o)) }
}
#[cfg_attr(crown, expect(crown::unrooted_must_root))]
pub fn get_unrooted<'a>(&self, no_gc: &'a NoGC) -> Option<UnrootedDom<'a, T>> {
assert_in_script();
let ptr = unsafe { ptr::read(self.ptr.get()) };
ptr.map(|o| Dom::from_ref(&*o))
.map(|dom| UnrootedDom { inner: dom, no_gc })
}
pub fn set(&self, val: Option<&T>) {
assert_in_script();
unsafe {
*self.ptr.get() = val.map(|p| Dom::from_ref(p));
}
}
pub fn take(&self) -> Option<DomRoot<T>> {
let value = self.get();
self.set(None);
value
}
pub fn clear(&self) {
self.set(None)
}
pub fn if_is_some<F, R>(&self, cb: F) -> Option<&R>
where
F: FnOnce(&T) -> &R,
{
unsafe {
if let Some(ref value) = *self.ptr.get() {
Some(cb(value))
} else {
None
}
}
}
}
impl<T: DomObject> PartialEq for MutNullableDom<T> {
fn eq(&self, other: &Self) -> bool {
unsafe { *self.ptr.get() == *other.ptr.get() }
}
}
impl<T: DomObject> PartialEq<Option<&T>> for MutNullableDom<T> {
fn eq(&self, other: &Option<&T>) -> bool {
unsafe { *self.ptr.get() == other.map(Dom::from_ref) }
}
}
impl<T: DomObject> Default for MutNullableDom<T> {
fn default() -> MutNullableDom<T> {
assert_in_script();
MutNullableDom {
ptr: UnsafeCell::new(None),
}
}
}
impl<T: DomObject> MallocSizeOf for MutNullableDom<T> {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}