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