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