use std::ffi::c_void;
use std::ptr;
use js::JSCLASS_IS_GLOBAL;
use js::context::JSContext;
use js::gc::{HandleObject, MutableHandleObject};
use js::glue::SetProxyReservedSlot;
use js::jsapi::{JS_SetReservedSlot, JSAutoRealm, JSClass, JSObject};
use js::jsval::{PrivateValue, UndefinedValue};
use js::rust::wrappers2::{
JS_InitializePropertiesFromCompatibleNativeObject, JS_NewObjectWithGivenProto, JS_WrapObject,
NewProxyObject,
};
use js::rust::{Handle, get_context_realm, get_object_class, get_object_realm};
use crate::codegen::PrototypeList;
use crate::conversions::DOM_OBJECT_SLOT;
use crate::import::module::JS_GetReservedSlot;
use crate::proxyhandler::ensure_expando_object;
use crate::root::{DomRoot, MaybeUnreflectedDom, Root};
use crate::utils::DOM_PROTO_UNFORGEABLE_HOLDER_SLOT;
use crate::{DomObject, DomTypes, MutDomObject};
type ProtoObjectFn = fn(&mut js::context::JSContext, HandleObject, MutableHandleObject);
pub(crate) struct WrapConfig {
pub(crate) is_maybe_cross_origin_object: bool,
pub(crate) is_proxy: bool,
pub(crate) proxy_handler: Option<*const c_void>,
pub(crate) prototype_id: PrototypeList::ID,
pub(crate) class: Option<&'static JSClass>,
pub(crate) proto_object_fn: ProtoObjectFn,
pub(crate) has_legacy_unforgeable_members: bool,
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) unsafe fn wrap<T: MutDomObject, D: DomTypes>(
cx: &mut JSContext,
scope: &D::GlobalScope,
given_proto: Option<js::rust::Handle<*mut JSObject>>,
raw: Root<MaybeUnreflectedDom<T>>,
config: WrapConfig,
) -> DomRoot<T> {
unsafe {
rooted!(&in(cx) let mut obj = ptr::null_mut::<JSObject>());
wrap_object(
cx,
&scope.reflector().get_jsobject(),
given_proto,
raw.as_ptr() as *const c_void,
&config,
obj.handle_mut(),
);
let root = raw.reflect_with(obj.get());
root.reflector().set_proto_id(config.prototype_id as u16);
root
}
}
unsafe fn wrap_object(
cx: &mut JSContext,
scope: &HandleObject,
given_proto: Option<js::rust::Handle<*mut JSObject>>,
raw: *const c_void,
config: &WrapConfig,
mut rval: MutableHandleObject,
) {
unsafe {
assert!(!scope.get().is_null());
assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0);
let _ac = JSAutoRealm::new(cx.raw_cx(), scope.get());
rooted!(&in(cx) let mut canonical_proto = ptr::null_mut::<JSObject>());
(config.proto_object_fn)(cx, *scope, canonical_proto.handle_mut());
assert!(!canonical_proto.is_null());
if config.is_proxy {
let handler: *const libc::c_void = config.proxy_handler.unwrap();
if config.is_maybe_cross_origin_object {
rval.set(NewProxyObject(
cx,
handler,
Handle::undefined(),
ptr::null_mut(),
ptr::null(),
true,
));
} else {
rval.set(NewProxyObject(
cx,
handler,
Handle::undefined(),
canonical_proto.get(),
ptr::null(),
false,
));
};
assert!(!rval.is_null());
SetProxyReservedSlot(rval.get(), 0, &PrivateValue(raw));
} else {
rooted!(&in(cx) let mut proto = ptr::null_mut::<JSObject>());
if let Some(given) = given_proto {
proto.set(*given);
if get_context_realm(cx.raw_cx()) != get_object_realm(*given) {
assert!(JS_WrapObject(cx, proto.handle_mut()));
}
} else {
proto.set(*canonical_proto);
}
rval.set(JS_NewObjectWithGivenProto(
cx,
config.class.unwrap(),
proto.handle(),
));
assert!(!rval.is_null());
JS_SetReservedSlot(rval.get(), DOM_OBJECT_SLOT, &PrivateValue(raw));
};
if config.has_legacy_unforgeable_members {
rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
if config.is_proxy {
ensure_expando_object(cx, rval.handle(), expando.handle_mut());
}
let copy_fn = JS_InitializePropertiesFromCompatibleNativeObject;
let mut slot = UndefinedValue();
JS_GetReservedSlot(
canonical_proto.get(),
DOM_PROTO_UNFORGEABLE_HOLDER_SLOT,
&mut slot,
);
rooted!(&in(cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>());
unforgeable_holder.handle_mut().set(slot.to_object());
if config.is_proxy {
assert!(copy_fn(cx, expando.handle(), unforgeable_holder.handle()));
} else {
assert!(copy_fn(cx, rval.handle(), unforgeable_holder.handle()));
}
}
}
}