1use std::ffi::c_void;
5use std::ptr;
6
7use js::JSCLASS_IS_GLOBAL;
8use js::context::JSContext;
9use js::gc::{HandleObject, MutableHandleObject};
10use js::glue::SetProxyReservedSlot;
11use js::jsapi::{JS_SetReservedSlot, JSAutoRealm, JSClass, JSObject};
12use js::jsval::{PrivateValue, UndefinedValue};
13use js::rust::wrappers2::{
14 JS_CopyOwnPropertiesAndPrivateFields, JS_InitializePropertiesFromCompatibleNativeObject,
15 JS_NewObjectWithGivenProto, JS_WrapObject, NewProxyObject,
16};
17use js::rust::{Handle, get_context_realm, get_object_class, get_object_realm};
18
19use crate::codegen::PrototypeList;
20use crate::conversions::DOM_OBJECT_SLOT;
21use crate::import::module::JS_GetReservedSlot;
22use crate::proxyhandler::ensure_expando_object;
23use crate::root::{DomRoot, MaybeUnreflectedDom, Root};
24use crate::utils::DOM_PROTO_UNFORGEABLE_HOLDER_SLOT;
25use crate::{DomObject, DomTypes, MutDomObject};
26
27type ProtoObjectFn = fn(&mut js::context::JSContext, HandleObject, MutableHandleObject);
28
29pub(crate) struct WrapConfig {
31 pub(crate) is_maybe_cross_origin_object: bool,
32 pub(crate) is_proxy: bool,
33 pub(crate) proxy_handler: Option<*const c_void>,
34 pub(crate) prototype_id: PrototypeList::ID,
35 pub(crate) class: Option<&'static JSClass>,
36 pub(crate) proto_object_fn: ProtoObjectFn,
38 pub(crate) is_global: bool,
39 pub(crate) has_legacy_unforgeable_members: bool,
40}
41
42#[cfg_attr(crown, allow(crown::unrooted_must_root))]
43pub(crate) unsafe fn wrap<T: MutDomObject, D: DomTypes>(
44 cx: &mut JSContext,
45 scope: &D::GlobalScope,
46 given_proto: Option<js::rust::Handle<*mut JSObject>>,
47 raw: Root<MaybeUnreflectedDom<T>>,
48 config: WrapConfig,
49) -> DomRoot<T> {
50 unsafe {
51 let scope = scope.reflector().get_jsobject();
52 assert!(!scope.get().is_null());
53 assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0);
54 let _ac = JSAutoRealm::new(cx.raw_cx(), scope.get());
55
56 rooted!(&in(cx) let mut canonical_proto = ptr::null_mut::<JSObject>());
57 (config.proto_object_fn)(cx, scope, canonical_proto.handle_mut());
58 assert!(!canonical_proto.is_null());
59
60 rooted!(&in(cx) let mut obj = ptr::null_mut::<JSObject>());
61 if config.is_proxy {
62 let handler: *const libc::c_void = config.proxy_handler.unwrap();
63
64 if config.is_maybe_cross_origin_object {
65 obj.set(NewProxyObject(
66 cx,
67 handler,
68 Handle::undefined(),
69 ptr::null_mut(),
70 ptr::null(),
71 true,
72 ));
73 } else {
74 obj.set(NewProxyObject(
75 cx,
76 handler,
77 Handle::undefined(),
78 canonical_proto.get(),
79 ptr::null(),
80 false,
81 ));
82 };
83
84 assert!(!obj.is_null());
85 SetProxyReservedSlot(
86 obj.get(),
87 0,
88 &PrivateValue(raw.as_ptr() as *const libc::c_void),
89 );
90 } else {
91 rooted!(&in(cx) let mut proto = ptr::null_mut::<JSObject>());
92 if let Some(given) = given_proto {
93 proto.set(*given);
94 if get_context_realm(cx.raw_cx()) != get_object_realm(*given) {
95 assert!(JS_WrapObject(cx, proto.handle_mut()));
96 }
97 } else {
98 proto.set(*canonical_proto);
99 }
100 obj.set(JS_NewObjectWithGivenProto(
101 cx,
102 config.class.unwrap(),
103 proto.handle(),
104 ));
105 assert!(!obj.is_null());
106 JS_SetReservedSlot(
107 obj.get(),
108 DOM_OBJECT_SLOT,
109 &PrivateValue(raw.as_ptr() as *const libc::c_void),
110 );
111 };
112
113 let root = raw.reflect_with(obj.get());
114 root.reflector().set_proto_id(config.prototype_id as u16);
115
116 if config.has_legacy_unforgeable_members {
118 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
119 if config.is_proxy {
120 ensure_expando_object(cx, obj.handle(), expando.handle_mut());
121 }
122
123 let copy_fn = if config.is_global {
124 JS_CopyOwnPropertiesAndPrivateFields
125 } else {
126 JS_InitializePropertiesFromCompatibleNativeObject
127 };
128
129 let mut slot = UndefinedValue();
130 JS_GetReservedSlot(
131 canonical_proto.get(),
132 DOM_PROTO_UNFORGEABLE_HOLDER_SLOT,
133 &mut slot,
134 );
135 rooted!(&in(cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>());
136 unforgeable_holder.handle_mut().set(slot.to_object());
137 if config.is_proxy {
138 assert!(copy_fn(cx, expando.handle(), unforgeable_holder.handle()));
139 } else {
140 assert!(copy_fn(cx, obj.handle(), unforgeable_holder.handle()));
141 }
142 }
143
144 DomRoot::from_ref(&*root)
145 }
146}