1use std::ffi::{CStr, CString};
8use std::ops::{Deref, DerefMut};
9use std::os::raw::c_char;
10use std::ptr;
11use std::ptr::NonNull;
12
13use js::context::{JSContext, RawJSContext};
14use js::conversions::{ToJSValConvertible, jsstr_to_string};
15use js::glue::{GetProxyHandler, GetProxyHandlerFamily, GetProxyPrivate, SetProxyPrivate};
16use js::jsapi::{
17 DOMProxyShadowsResult, GetObjectRealmOrNull, GetRealmPrincipals, GetStaticPrototype,
18 Handle as RawHandle, HandleId as RawHandleId, HandleObject as RawHandleObject,
19 HandleValue as RawHandleValue, HandleValueArray, IsWindowProxy, JSErrNum, JSFunctionSpec,
20 JSITER_HIDDEN, JSITER_OWNONLY, JSITER_SYMBOLS, JSObject, JSPROP_READONLY, JSPropertySpec,
21 JSString, MutableHandleIdVector as RawMutableHandleIdVector,
22 MutableHandleObject as RawMutableHandleObject, ObjectOpResult, PropertyDescriptor,
23 SetDOMProxyInformation, SymbolCode, jsid,
24};
25use js::jsid::SymbolId;
26use js::jsval::{ObjectValue, UndefinedValue};
27use js::realm::{AutoRealm, CurrentRealm};
28use js::rust::wrappers2::{
29 AppendToIdVector, Call, GetObjectProto, GetPropertyKeys, GetWellKnownSymbol,
30 InvokeGetOwnPropertyDescriptor, JS_AlreadyHasOwnPropertyById, JS_AtomizeAndPinString,
31 JS_DefineFunctions, JS_DefineProperties, JS_DefinePropertyById, JS_DeletePropertyById,
32 JS_GetOwnPropertyDescriptorById, JS_IdToValue, JS_IsExceptionPending,
33 JS_NewObjectWithGivenProto, JS_ValueToSource, RUST_INTERNED_STRING_TO_JSID, RUST_JSID_IS_VOID,
34 SetDataPropertyDescriptor, SetPropertyIgnoringNamedGetter, int_to_jsid,
35};
36use js::rust::{
37 Handle, HandleId, HandleObject, HandleValue, IntoHandle, MutableHandle, MutableHandleObject,
38 MutableHandleValue,
39};
40
41use crate::DomTypes;
42use crate::conversions::{is_dom_proxy, jsid_to_string, native_from_object};
43use crate::error::Error;
44use crate::interfaces::{DomHelpers, GlobalScopeHelpers};
45use crate::principals::ServoJSPrincipalsRef;
46use crate::reflector::DomObject;
47use crate::str::DOMString;
48
49pub(crate) unsafe extern "C" fn shadow_check_callback(
54 cx: *mut RawJSContext,
55 object: RawHandleObject,
56 id: RawHandleId,
57) -> DOMProxyShadowsResult {
58 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
62 let cx = &mut cx;
63
64 let object = HandleObject::from_raw(object);
65 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
66 get_expando_object(object, expando.handle_mut());
67 if !expando.get().is_null() {
68 let mut has_own = false;
69 let raw_id = Handle::from_raw(id);
70
71 if !JS_AlreadyHasOwnPropertyById(cx, expando.handle(), raw_id, &mut has_own) {
72 return DOMProxyShadowsResult::ShadowCheckFailed;
73 }
74
75 if has_own {
76 return DOMProxyShadowsResult::ShadowsViaDirectExpando;
77 }
78 }
79
80 DOMProxyShadowsResult::DoesntShadow
82}
83
84pub fn init() {
86 unsafe {
87 SetDOMProxyInformation(
88 GetProxyHandlerFamily(),
89 Some(shadow_check_callback),
90 ptr::null(),
91 );
92 }
93}
94
95pub(crate) unsafe extern "C" fn define_property(
101 cx: *mut RawJSContext,
102 proxy: RawHandleObject,
103 id: RawHandleId,
104 desc: RawHandle<PropertyDescriptor>,
105 result: *mut ObjectOpResult,
106) -> bool {
107 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
109 let cx = &mut cx;
110
111 let proxy = Handle::from_raw(proxy);
112 let id = Handle::from_raw(id);
113 let desc = Handle::from_raw(desc);
114
115 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
116 ensure_expando_object(cx, proxy, expando.handle_mut());
117
118 JS_DefinePropertyById(cx, expando.handle(), id, desc, result)
119}
120
121pub(crate) unsafe extern "C" fn delete(
127 cx: *mut RawJSContext,
128 proxy: RawHandleObject,
129 id: RawHandleId,
130 bp: *mut ObjectOpResult,
131) -> bool {
132 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
134 let cx = &mut cx;
135
136 let proxy = Handle::from_raw(proxy);
137 let id = Handle::from_raw(id);
138
139 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
140 get_expando_object(proxy, expando.handle_mut());
141
142 if expando.is_null() {
143 (*bp).code_ = 0 ;
144 return true;
145 }
146
147 JS_DeletePropertyById(cx, expando.handle(), id, bp)
148}
149
150pub(crate) unsafe extern "C" fn prevent_extensions(
155 _cx: *mut RawJSContext,
156 _proxy: RawHandleObject,
157 result: *mut ObjectOpResult,
158) -> bool {
159 (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t;
160 true
161}
162
163pub(crate) unsafe extern "C" fn is_extensible(
168 _cx: *mut RawJSContext,
169 _proxy: RawHandleObject,
170 succeeded: *mut bool,
171) -> bool {
172 *succeeded = true;
173 true
174}
175
176pub(crate) unsafe extern "C" fn get_prototype_if_ordinary(
189 _: *mut RawJSContext,
190 proxy: RawHandleObject,
191 is_ordinary: *mut bool,
192 proto: RawMutableHandleObject,
193) -> bool {
194 *is_ordinary = true;
195 proto.set(GetStaticPrototype(proxy.get()));
196 true
197}
198
199pub(crate) fn get_expando_object(obj: HandleObject, mut expando: MutableHandleObject) {
201 unsafe {
202 assert!(is_dom_proxy(obj.get()));
203 let val = &mut UndefinedValue();
204 GetProxyPrivate(obj.get(), val);
205 expando.set(if val.is_undefined() {
206 ptr::null_mut()
207 } else {
208 val.to_object()
209 });
210 }
211}
212
213pub(crate) fn ensure_expando_object(
216 cx: &mut JSContext,
217 obj: HandleObject,
218 mut expando: MutableHandleObject,
219) {
220 unsafe {
221 assert!(is_dom_proxy(obj.get()));
222 get_expando_object(obj, expando.reborrow());
223 if expando.is_null() {
224 expando.set(JS_NewObjectWithGivenProto(
225 cx,
226 ptr::null_mut(),
227 HandleObject::null(),
228 ));
229 assert!(!expando.is_null());
230
231 SetProxyPrivate(obj.get(), &ObjectValue(expando.get()));
232 }
233 }
234}
235
236pub fn set_property_descriptor(
239 desc: MutableHandle<PropertyDescriptor>,
240 value: HandleValue,
241 attrs: u32,
242 is_none: &mut bool,
243) {
244 unsafe { SetDataPropertyDescriptor(desc, value, attrs) };
245 *is_none = false;
246}
247
248fn id_to_source(cx: &mut JSContext, id: HandleId) -> Option<DOMString> {
249 unsafe {
250 if RUST_JSID_IS_VOID(id) {
251 return None;
252 }
253 rooted!(&in(cx) let mut value = UndefinedValue());
254 rooted!(&in(cx) let mut jsstr = ptr::null_mut::<JSString>());
255 JS_IdToValue(cx, id.get(), value.handle_mut())
256 .then(|| {
257 jsstr.set(JS_ValueToSource(cx, value.handle()));
258 jsstr.get()
259 })
260 .and_then(NonNull::new)
261 .map(|jsstr| jsstr_to_string(cx, jsstr).into())
262 }
263}
264
265pub(crate) struct CrossOriginProperties {
270 pub(crate) attributes: &'static [JSPropertySpec],
271 pub(crate) methods: &'static [JSFunctionSpec],
272}
273
274impl CrossOriginProperties {
275 fn keys(&self) -> impl Iterator<Item = *const c_char> + '_ {
277 self.attributes
279 .iter()
280 .map(|spec| unsafe { spec.name.string_ })
281 .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ }))
282 .filter(|ptr| !ptr.is_null())
283 }
284}
285
286fn cross_origin_own_property_keys(
290 cx: &mut JSContext,
291 _proxy: HandleObject,
292 cross_origin_properties: &'static CrossOriginProperties,
293 props: RawMutableHandleIdVector,
294) -> bool {
295 for key in cross_origin_properties.keys() {
298 unsafe {
299 rooted!(&in(cx) let rooted = JS_AtomizeAndPinString(cx, key));
300 rooted!(&in(cx) let mut rooted_jsid: jsid);
301 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), rooted_jsid.handle_mut());
302 AppendToIdVector(props, rooted_jsid.handle());
303 }
304 }
305
306 append_cross_origin_allowlisted_prop_keys(cx, props);
309
310 true
311}
312
313pub(crate) unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
316 _: *mut RawJSContext,
317 _proxy: RawHandleObject,
318 is_ordinary: *mut bool,
319 _proto: RawMutableHandleObject,
320) -> bool {
321 *is_ordinary = false;
323 true
324}
325
326pub(crate) unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
334 cx: *mut RawJSContext,
335 proxy: RawHandleObject,
336 proto: RawHandleObject,
337 result: *mut ObjectOpResult,
338) -> bool {
339 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
341 let cx = &mut cx;
342 rooted!(&in(cx) let mut current = ptr::null_mut::<JSObject>());
350 if !GetObjectProto(cx, Handle::from_raw(proxy), current.handle_mut()) {
351 return false;
352 }
353
354 if proto.get() == current.get() {
356 (*result).code_ = 0 ;
357 return true;
358 }
359
360 (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize;
362 true
363}
364
365fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
366 if d.hasGetter_() {
367 out.set(d.getter_);
368 }
369}
370
371fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
372 if d.hasSetter_() {
373 out.set(d.setter_);
374 }
375}
376
377fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
379 d.hasSetter_() || d.hasGetter_()
380}
381
382fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
384 d.hasWritable_() || d.hasValue_()
385}
386
387pub(crate) unsafe fn cross_origin_has_own(
396 cx: &mut CurrentRealm,
397 _proxy: HandleObject,
398 cross_origin_properties: &'static CrossOriginProperties,
399 id: HandleId,
400 bp: *mut bool,
401) -> bool {
402 *bp = jsid_to_string(cx, id).is_some_and(|key| {
406 cross_origin_properties.keys().any(|defined_key| {
407 let defined_key = CStr::from_ptr(defined_key);
408 defined_key.to_bytes() == key.str().as_bytes()
409 })
410 });
411
412 true
413}
414
415pub(crate) fn cross_origin_get_own_property_helper(
422 cx: &mut CurrentRealm,
423 proxy: HandleObject,
424 cross_origin_properties: &'static CrossOriginProperties,
425 id: HandleId,
426 desc: MutableHandle<PropertyDescriptor>,
427 is_none: &mut bool,
428) -> bool {
429 rooted!(&in(cx) let mut holder = ptr::null_mut::<JSObject>());
430 ensure_cross_origin_property_holder(cx, proxy, cross_origin_properties, holder.handle_mut());
431
432 unsafe { JS_GetOwnPropertyDescriptorById(cx, holder.handle(), id, desc, is_none) }
433}
434
435const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[
436 SymbolCode::toStringTag,
437 SymbolCode::hasInstance,
438 SymbolCode::isConcatSpreadable,
439];
440
441fn is_cross_origin_allowlisted_prop(cx: &mut JSContext, id: HandleId) -> bool {
442 unsafe {
443 if jsid_to_string(cx, id).is_some_and(|st| st == "then") {
444 return true;
445 }
446
447 rooted!(&in(cx) let mut allowed_id: jsid);
448 ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| {
449 allowed_id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
450 allowed_id.get().asBits_ == id.asBits_
453 })
454 }
455}
456
457fn append_cross_origin_allowlisted_prop_keys(cx: &mut JSContext, props: RawMutableHandleIdVector) {
462 unsafe {
463 rooted!(&in(cx) let mut id: jsid);
464
465 let jsstring = JS_AtomizeAndPinString(cx, c"then".as_ptr());
466 rooted!(&in(cx) let rooted = jsstring);
467 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), id.handle_mut());
468 AppendToIdVector(props, id.handle());
469
470 for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() {
471 id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
472 AppendToIdVector(props, id.handle());
473 }
474 }
475}
476
477fn ensure_cross_origin_property_holder(
490 cx: &mut CurrentRealm,
491 _proxy: HandleObject,
492 cross_origin_properties: &'static CrossOriginProperties,
493 mut out_holder: MutableHandleObject,
494) -> bool {
495 unsafe {
502 out_holder.set(JS_NewObjectWithGivenProto(
503 cx,
504 ptr::null_mut(),
505 HandleObject::null(),
506 ));
507
508 if out_holder.get().is_null() ||
509 !JS_DefineProperties(
510 cx,
511 out_holder.handle(),
512 cross_origin_properties.attributes.as_ptr(),
513 ) ||
514 !JS_DefineFunctions(
515 cx,
516 out_holder.handle(),
517 cross_origin_properties.methods.as_ptr(),
518 )
519 {
520 return false;
521 }
522 }
523
524 true
527}
528
529pub(crate) fn is_cross_origin_object<D: DomTypes>(cx: &mut JSContext, obj: HandleObject) -> bool {
535 unsafe {
536 IsWindowProxy(*obj) ||
537 native_from_object::<D::Location>(*obj, cx.raw_cx()).is_ok() ||
538 native_from_object::<D::DissimilarOriginLocation>(*obj, cx.raw_cx()).is_ok()
539 }
540}
541
542pub(crate) fn report_cross_origin_denial<D: DomTypes>(
549 cx: &mut CurrentRealm,
550 id: HandleId,
551 access: &str,
552) -> bool {
553 if let Some(id) = id_to_source(cx, id) {
554 debug!(
555 "permission denied to {} property {} on cross-origin object",
556 access,
557 &*id.str(),
558 );
559 } else {
560 debug!("permission denied to {} on cross-origin object", access);
561 }
562 unsafe {
563 if !JS_IsExceptionPending(cx) {
564 let global = D::GlobalScope::from_current_realm(cx);
565 <D as DomHelpers<D>>::throw_dom_exception(cx, &global, Error::Security(None));
567 }
568 }
569 false
570}
571
572pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx<D: DomTypes>(
576 cx: *mut RawJSContext,
577 proxy: RawHandleObject,
578 id: RawHandleId,
579 v: RawHandleValue,
580 receiver: RawHandleValue,
581 result: *mut ObjectOpResult,
582) -> bool {
583 unsafe {
584 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
586 let mut realm = CurrentRealm::assert(&mut cx);
587 let proxy = HandleObject::from_raw(proxy);
588 let id = Handle::from_raw(id);
589 let v = Handle::from_raw(v);
590 let receiver = Handle::from_raw(receiver);
591
592 if !is_platform_object_same_origin(&realm, proxy) {
593 return cross_origin_set::<D>(&mut realm, proxy, id, v.into_handle(), receiver, result);
594 }
595
596 let mut realm = AutoRealm::new_from_handle(&mut realm, proxy);
598
599 rooted!(&in(&mut realm) let mut own_desc = PropertyDescriptor::default());
602 let mut is_none = false;
603 if !InvokeGetOwnPropertyDescriptor(
604 GetProxyHandler(*proxy),
605 &mut realm,
606 proxy,
607 id,
608 own_desc.handle_mut(),
609 &mut is_none,
610 ) {
611 return false;
612 }
613
614 SetPropertyIgnoringNamedGetter(
615 &mut realm,
616 proxy,
617 id,
618 v,
619 receiver,
620 if is_none {
621 None
622 } else {
623 Some(own_desc.handle())
624 },
625 result,
626 )
627 }
628}
629
630pub(crate) fn maybe_cross_origin_get_prototype<D: DomTypes>(
634 cx: &mut CurrentRealm,
635 proxy: HandleObject,
636 get_proto_object: fn(cx: &mut JSContext, global: HandleObject, rval: MutableHandleObject),
637 mut proto: MutableHandleObject,
638) -> bool {
639 if is_platform_object_same_origin(cx, proxy) {
641 let mut realm = AutoRealm::new_from_handle(cx, proxy);
642 let mut realm = realm.current_realm();
643 let global = D::GlobalScope::from_current_realm(&realm);
644 get_proto_object(
645 &mut realm,
646 global.reflector().get_jsobject(),
647 proto.reborrow(),
648 );
649 return !proto.is_null();
650 }
651
652 proto.set(ptr::null_mut());
654 true
655}
656
657pub(crate) fn cross_origin_get<D: DomTypes>(
664 cx: &mut CurrentRealm,
665 proxy: HandleObject,
666 receiver: HandleValue,
667 id: HandleId,
668 mut vp: MutableHandleValue,
669) -> bool {
670 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
672 let mut is_none = false;
673 if !unsafe {
674 InvokeGetOwnPropertyDescriptor(
675 GetProxyHandler(*proxy),
676 cx,
677 proxy,
678 id,
679 descriptor.handle_mut(),
680 &mut is_none,
681 )
682 } {
683 return false;
684 }
685
686 assert!(
688 !is_none,
689 "Callees should throw in all cases when they are not finding \
690 a property decriptor"
691 );
692
693 if is_data_descriptor(&descriptor) {
695 vp.set(descriptor.value_);
696 return true;
697 }
698
699 assert!(is_accessor_descriptor(&descriptor));
701
702 rooted!(&in(cx) let mut getter = ptr::null_mut::<JSObject>());
707 get_getter_object(&descriptor, getter.handle_mut().into());
708 if getter.get().is_null() {
709 return report_cross_origin_denial::<D>(cx, id, "get");
710 }
711
712 rooted!(&in(cx) let mut getter_jsval = UndefinedValue());
713 unsafe {
714 getter
715 .get()
716 .to_jsval(cx.raw_cx(), getter_jsval.handle_mut());
717 }
718
719 unsafe {
721 Call(
722 cx,
723 receiver,
724 getter_jsval.handle(),
725 &HandleValueArray::empty(),
726 vp,
727 )
728 }
729}
730
731unsafe fn cross_origin_set<D: DomTypes>(
738 cx: &mut CurrentRealm,
739 proxy: HandleObject,
740 id: HandleId,
741 v: RawHandleValue,
742 receiver: HandleValue,
743 result: *mut ObjectOpResult,
744) -> bool {
745 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
747 let mut is_none = false;
748 if !InvokeGetOwnPropertyDescriptor(
749 GetProxyHandler(*proxy),
750 cx,
751 proxy,
752 id,
753 descriptor.handle_mut(),
754 &mut is_none,
755 ) {
756 return false;
757 }
758
759 assert!(
761 !is_none,
762 "Callees should throw in all cases when they are not finding \
763 a property decriptor"
764 );
765
766 rooted!(&in(cx) let mut setter = ptr::null_mut::<JSObject>());
769 get_setter_object(&descriptor, setter.handle_mut().into());
770 if setter.get().is_null() {
771 return report_cross_origin_denial::<D>(cx, id, "set");
773 }
774
775 rooted!(&in(cx) let mut setter_jsval = UndefinedValue());
776 setter
777 .get()
778 .to_jsval(cx.raw_cx(), setter_jsval.handle_mut());
779
780 rooted!(&in(cx) let mut ignored = UndefinedValue());
784 if !Call(
785 cx,
786 receiver,
787 setter_jsval.handle(),
788 &HandleValueArray {
791 length_: 1,
792 elements_: v.ptr,
793 },
794 ignored.handle_mut(),
795 ) {
796 return false;
797 }
798
799 (*result).code_ = 0 ;
800 true
801}
802
803pub(crate) fn cross_origin_property_fallback<D: DomTypes>(
810 cx: &mut CurrentRealm,
811 _proxy: HandleObject,
812 id: HandleId,
813 desc: MutableHandle<PropertyDescriptor>,
814 is_none: &mut bool,
815) -> bool {
816 assert!(*is_none, "why are we being called?");
817
818 if is_cross_origin_allowlisted_prop(cx, id) {
823 set_property_descriptor(
824 desc,
825 HandleValue::undefined(),
826 JSPROP_READONLY as u32,
827 is_none,
828 );
829 return true;
830 }
831
832 report_cross_origin_denial::<D>(cx, id, "access")
834}
835
836#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
838pub(crate) struct JSProxyHandlerOwnPropertyKeysConfig<T: DomObject> {
839 pub(crate) indexed_getter_and_length: Option<fn(&T, &mut JSContext) -> u32>,
840 pub(crate) cross_origin: Option<&'static CrossOriginProperties>,
841 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
842 pub(crate) supported_named_properties: Option<fn(*const T, &mut JSContext) -> Vec<DOMString>>,
843}
844
845enum Realm<'a> {
847 AutoRealm(AutoRealm<'a>),
848 CurrentRealm(&'a mut CurrentRealm<'a>),
849}
850
851impl<'cx> Deref for Realm<'cx> {
852 type Target = JSContext;
853
854 fn deref(&'_ self) -> &'_ Self::Target {
855 match self {
856 Realm::AutoRealm(auto_realm) => auto_realm,
857 Realm::CurrentRealm(current_realm) => current_realm,
858 }
859 }
860}
861
862impl<'cx> DerefMut for Realm<'cx> {
863 fn deref_mut(&'_ mut self) -> &'_ mut Self::Target {
864 match self {
865 Realm::AutoRealm(auto_realm) => auto_realm,
866 Realm::CurrentRealm(current_realm) => current_realm,
867 }
868 }
869}
870
871#[expect(non_snake_case)]
872pub(crate) unsafe fn JSProxyHandlerOwnPropertyKeys<T>(
874 config: JSProxyHandlerOwnPropertyKeysConfig<T>,
875 cx: *mut RawJSContext,
876 proxy: RawHandleObject,
877 props: RawMutableHandleIdVector,
878) -> bool
879where
880 T: DomObject,
881{
882 unsafe {
883 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
885 let mut cx = CurrentRealm::assert(&mut cx);
886 let current_realm = &mut cx;
887 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
888
889 let proxy = Handle::from_raw(proxy);
890
891 let mut cx = if let Some(cross_origin_properties) = config.cross_origin {
892 if !is_platform_object_same_origin(current_realm, proxy) {
893 return cross_origin_own_property_keys(
894 current_realm,
895 proxy,
896 cross_origin_properties,
897 props,
898 );
899 }
900
901 let cx = AutoRealm::new_from_handle(current_realm, proxy);
903 Realm::AutoRealm(cx)
904 } else {
905 Realm::CurrentRealm(current_realm)
906 };
907
908 if let Some(length_fn) = config.indexed_getter_and_length {
909 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
910 rooted!(&in(cx) let mut rooted_jsid: jsid);
911 for i in 0..length {
912 int_to_jsid(i as i32, rooted_jsid.handle_mut());
913 AppendToIdVector(props, rooted_jsid.handle());
914 }
915 }
916
917 if let Some(properties) = config.supported_named_properties {
918 for name in properties(unwrapped_proxy, &mut cx) {
919 let cstring = CString::new(name).unwrap();
920 let jsstring = JS_AtomizeAndPinString(&cx, cstring.as_ptr());
921 rooted!(&in(cx) let rooted = jsstring);
922 rooted!(&in(cx) let mut rooted_jsid: jsid);
923 RUST_INTERNED_STRING_TO_JSID(
924 &mut cx,
925 rooted.handle().get(),
926 rooted_jsid.handle_mut(),
927 );
928 AppendToIdVector(props, rooted_jsid.handle());
929 }
930 }
931
932 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
933 get_expando_object(proxy, expando.handle_mut());
934
935 if !expando.is_null() &&
936 !GetPropertyKeys(
937 &mut cx,
938 expando.handle(),
939 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
940 props,
941 )
942 {
943 return false;
944 }
945 }
946 true
947}
948
949#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
951pub(crate) struct JSProxyHandlerOwnEnumerablePropertyKeysConfig<T: DomObject> {
952 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
953 #[expect(clippy::type_complexity)]
954 pub(crate) indexed_getter_and_length: Option<Box<dyn Fn(&T, &mut JSContext) -> u32>>,
955 pub(crate) cross_origin: bool,
956}
957
958#[expect(non_snake_case)]
959pub(crate) fn JSProxyHandlerGetOwnEnumerablePropertyKeys<T>(
960 config: JSProxyHandlerOwnEnumerablePropertyKeysConfig<T>,
961 cx: *mut RawJSContext,
962 proxy: RawHandleObject,
963 props: RawMutableHandleIdVector,
964) -> bool
965where
966 T: DomObject,
967{
968 unsafe {
969 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
971 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
972 let mut cx = CurrentRealm::assert(&mut cx);
973 let current_realm = &mut cx;
974
975 let proxy = Handle::from_raw(proxy);
976
977 let mut cx = if config.cross_origin {
978 if !is_platform_object_same_origin(current_realm, proxy) {
979 return true;
981 }
982
983 let cx = AutoRealm::new_from_handle(current_realm, proxy);
985 Realm::AutoRealm(cx)
986 } else {
987 Realm::CurrentRealm(current_realm)
988 };
989 if let Some(length_fn) = config.indexed_getter_and_length {
990 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
991 rooted!(&in(cx) let mut rooted_jsid: jsid);
992 for i in 0..length {
993 int_to_jsid(i as i32, rooted_jsid.handle_mut());
994 AppendToIdVector(props, rooted_jsid.handle());
995 }
996 }
997
998 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
999 get_expando_object(proxy, expando.handle_mut());
1000 if !expando.is_null() &&
1001 !GetPropertyKeys(
1002 &mut cx,
1003 expando.handle(),
1004 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1005 props,
1006 )
1007 {
1008 return false;
1009 }
1010 }
1011
1012 true
1013}
1014
1015pub(crate) fn is_platform_object_same_origin(realm: &CurrentRealm, obj: HandleObject) -> bool {
1017 let subject_realm = realm.realm().as_ptr();
1018 let obj_realm = unsafe { GetObjectRealmOrNull(*obj) };
1019 assert!(!obj_realm.is_null());
1020
1021 let subject_principals =
1022 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(subject_realm)) };
1023 let obj_principals =
1024 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(obj_realm)) };
1025
1026 let subject_origin = subject_principals.origin();
1027 let obj_origin = obj_principals.origin();
1028
1029 let result = subject_origin.same_origin_domain(&obj_origin);
1030 log::trace!(
1031 "object {:p} (realm = {:p}, principalls = {:p}, origin = {:?}) is {} \
1032 with reference to the current Realm (realm = {:p}, principals = {:p}, \
1033 origin = {:?})",
1034 obj.get(),
1035 obj_realm,
1036 obj_principals.as_raw(),
1037 obj_origin.immutable(),
1038 ["NOT same domain-origin", "same domain-origin"][result as usize],
1039 subject_realm,
1040 subject_principals.as_raw(),
1041 subject_origin.immutable()
1042 );
1043
1044 result
1045}