1use std::ptr;
6use std::ptr::NonNull;
7use std::sync::LazyLock;
8
9use js::conversions::jsstr_to_string;
10use js::glue::{AppendToIdVector, CreateProxyHandler, NewProxyObject, ProxyTraps};
11use js::jsapi::{
12 GetWellKnownSymbol, Handle, HandleId, HandleObject, JS_SetImmutablePrototype,
13 JSCLASS_DELAY_METADATA_BUILDER, JSCLASS_IS_PROXY, JSCLASS_RESERVED_SLOTS_MASK,
14 JSCLASS_RESERVED_SLOTS_SHIFT, JSClass, JSClass_NON_NATIVE, JSContext, JSErrNum,
15 JSPROP_READONLY, MutableHandle, MutableHandleIdVector, MutableHandleObject, ObjectOpResult,
16 PropertyDescriptor, ProxyClassExtension, ProxyClassOps, ProxyObjectOps, SymbolCode,
17 UndefinedHandleValue,
18};
19use js::jsid::SymbolId;
20use js::jsval::UndefinedValue;
21use js::rust::{
22 Handle as RustHandle, HandleObject as RustHandleObject, IntoHandle,
23 MutableHandle as RustMutableHandle, MutableHandleObject as RustMutableHandleObject,
24};
25
26use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
27use crate::dom::bindings::proxyhandler::set_property_descriptor;
28use crate::dom::bindings::root::Root;
29use crate::dom::bindings::utils::has_property_on_prototype;
30use crate::dom::globalscope::GlobalScope;
31use crate::dom::window::Window;
32use crate::js::conversions::ToJSValConvertible;
33use crate::script_runtime::JSContext as SafeJSContext;
34
35struct SyncWrapper(*const libc::c_void);
36#[allow(unsafe_code)]
37unsafe impl Sync for SyncWrapper {}
38#[allow(unsafe_code)]
39unsafe impl Send for SyncWrapper {}
40
41static HANDLER: LazyLock<SyncWrapper> = LazyLock::new(|| {
42 let traps = ProxyTraps {
43 enter: None,
44 getOwnPropertyDescriptor: Some(get_own_property_descriptor),
45 defineProperty: Some(define_property),
46 ownPropertyKeys: Some(own_property_keys),
47 delete_: Some(delete),
48 enumerate: None,
49 getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
50 getPrototype: None,
51 setPrototype: None,
52 setImmutablePrototype: None,
53 preventExtensions: Some(prevent_extensions),
54 isExtensible: Some(is_extensible),
55 has: None,
56 get: None,
57 set: None,
58 call: None,
59 construct: None,
60 hasOwn: None,
61 getOwnEnumerablePropertyKeys: None,
62 nativeCall: None,
63 objectClassIs: None,
64 className: Some(class_name),
65 fun_toString: None,
66 boxedValue_unbox: None,
67 defaultValue: None,
68 trace: None,
69 finalize: None,
70 objectMoved: None,
71 isCallable: None,
72 isConstructor: None,
73 };
74
75 #[allow(unsafe_code)]
76 unsafe {
77 SyncWrapper(CreateProxyHandler(&traps, ptr::null()))
78 }
79});
80
81#[allow(unsafe_code)]
82unsafe extern "C" fn get_own_property_descriptor(
83 cx: *mut JSContext,
84 proxy: HandleObject,
85 id: HandleId,
86 desc: MutableHandle<PropertyDescriptor>,
87 is_none: *mut bool,
88) -> bool {
89 let cx = unsafe { SafeJSContext::from_ptr(cx) };
90
91 if id.is_symbol() {
92 if id.get().asBits_ ==
93 SymbolId(unsafe { GetWellKnownSymbol(*cx, SymbolCode::toStringTag) }).asBits_
94 {
95 rooted!(in(*cx) let mut rval = UndefinedValue());
96 unsafe { "WindowProperties".to_jsval(*cx, rval.handle_mut()) };
97 set_property_descriptor(
98 unsafe { RustMutableHandle::from_raw(desc) },
99 rval.handle(),
100 JSPROP_READONLY.into(),
101 unsafe { &mut *is_none },
102 );
103 }
104 return true;
105 }
106
107 let mut found = false;
108 let lookup_succeeded = unsafe {
109 has_property_on_prototype(
110 *cx,
111 RustHandle::from_raw(proxy),
112 RustHandle::from_raw(id),
113 &mut found,
114 )
115 };
116 if !lookup_succeeded {
117 return false;
118 }
119 if found {
120 return true;
121 }
122
123 let s = if id.is_string() {
124 unsafe { jsstr_to_string(*cx, NonNull::new(id.to_string()).unwrap()) }
125 } else if id.is_int() {
126 id.to_int().to_string()
130 } else if id.is_symbol() {
131 unreachable!()
133 } else {
134 unimplemented!()
135 };
136 if s.is_empty() {
137 return true;
138 }
139
140 let window = Root::downcast::<Window>(unsafe { GlobalScope::from_object(proxy.get()) })
141 .expect("global is not a window");
142 if let Some(obj) = window.NamedGetter(s.into()) {
143 rooted!(in(*cx) let mut rval = UndefinedValue());
144 unsafe {
145 obj.to_jsval(*cx, rval.handle_mut());
146 }
147 set_property_descriptor(
148 unsafe { RustMutableHandle::from_raw(desc) },
149 rval.handle(),
150 0,
151 unsafe { &mut *is_none },
152 );
153 }
154 true
155}
156
157#[allow(unsafe_code)]
158unsafe extern "C" fn own_property_keys(
159 cx: *mut JSContext,
160 _proxy: HandleObject,
161 props: MutableHandleIdVector,
162) -> bool {
163 unsafe {
167 rooted!(in(cx) let mut rooted = SymbolId(GetWellKnownSymbol(cx, SymbolCode::toStringTag)));
168 AppendToIdVector(props, rooted.handle().into());
169 }
170 true
171}
172
173#[allow(unsafe_code)]
174unsafe extern "C" fn define_property(
175 _cx: *mut JSContext,
176 _proxy: HandleObject,
177 _id: HandleId,
178 _desc: Handle<PropertyDescriptor>,
179 result: *mut ObjectOpResult,
180) -> bool {
181 unsafe {
182 (*result).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_NAMED_PROPERTY as usize;
183 }
184 true
185}
186
187#[allow(unsafe_code)]
188unsafe extern "C" fn delete(
189 _cx: *mut JSContext,
190 _proxy: HandleObject,
191 _id: HandleId,
192 result: *mut ObjectOpResult,
193) -> bool {
194 unsafe {
195 (*result).code_ = JSErrNum::JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY as usize;
196 }
197 true
198}
199
200#[allow(unsafe_code)]
201unsafe extern "C" fn get_prototype_if_ordinary(
202 _cx: *mut JSContext,
203 proxy: HandleObject,
204 is_ordinary: *mut bool,
205 proto: MutableHandleObject,
206) -> bool {
207 unsafe {
208 *is_ordinary = true;
209 proto.set(js::jsapi::GetStaticPrototype(proxy.get()));
210 }
211 true
212}
213
214#[allow(unsafe_code)]
215unsafe extern "C" fn prevent_extensions(
216 _cx: *mut JSContext,
217 _proxy: HandleObject,
218 result: *mut ObjectOpResult,
219) -> bool {
220 unsafe {
221 (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as usize;
222 }
223 true
224}
225
226#[allow(unsafe_code)]
227unsafe extern "C" fn is_extensible(
228 _cx: *mut JSContext,
229 _proxy: HandleObject,
230 extensible: *mut bool,
231) -> bool {
232 unsafe {
233 *extensible = true;
234 }
235 true
236}
237
238#[allow(unsafe_code)]
239unsafe extern "C" fn class_name(_cx: *mut JSContext, _proxy: HandleObject) -> *const libc::c_char {
240 c"WindowProperties".as_ptr()
241}
242
243#[allow(unsafe_code)]
245static CLASS: JSClass = JSClass {
246 name: c"WindowProperties".as_ptr(),
247 flags: JSClass_NON_NATIVE |
248 JSCLASS_IS_PROXY |
249 JSCLASS_DELAY_METADATA_BUILDER |
250 ((1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT), cOps: unsafe { &ProxyClassOps },
252 spec: ptr::null(),
253 ext: unsafe { &ProxyClassExtension },
254 oOps: unsafe { &ProxyObjectOps },
255};
256
257#[allow(unsafe_code)]
258pub(crate) fn create(
259 cx: SafeJSContext,
260 proto: RustHandleObject,
261 mut properties_obj: RustMutableHandleObject,
262) {
263 unsafe {
264 properties_obj.set(NewProxyObject(
265 *cx,
266 HANDLER.0,
267 UndefinedHandleValue,
268 proto.get(),
269 &CLASS,
270 false,
271 ));
272 assert!(!properties_obj.get().is_null());
273 let mut succeeded = false;
274 assert!(JS_SetImmutablePrototype(
275 *cx,
276 properties_obj.handle().into_handle(),
277 &mut succeeded
278 ));
279 assert!(succeeded);
280 }
281}