1#![deny(unsafe_op_in_unsafe_fn)]
9
10use std::ffi::{CStr, CString};
11use std::ops::{Deref, DerefMut};
12use std::os::raw::c_char;
13use std::ptr;
14use std::ptr::NonNull;
15
16use js::context::{JSContext, RawJSContext};
17use js::conversions::{ToJSValConvertible, jsstr_to_string};
18use js::glue::{GetProxyHandler, GetProxyHandlerFamily, GetProxyPrivate, SetProxyPrivate};
19use js::jsapi::{
20 DOMProxyShadowsResult, GetObjectRealmOrNull, GetRealmPrincipals, GetStaticPrototype,
21 Handle as RawHandle, HandleId as RawHandleId, HandleObject as RawHandleObject,
22 HandleValue as RawHandleValue, HandleValueArray, IsWindowProxy, JSErrNum, JSFunctionSpec,
23 JSITER_HIDDEN, JSITER_OWNONLY, JSITER_SYMBOLS, JSObject, JSPROP_READONLY, JSPropertySpec,
24 JSString, MutableHandleIdVector as RawMutableHandleIdVector,
25 MutableHandleObject as RawMutableHandleObject, ObjectOpResult, PropertyDescriptor,
26 SetDOMProxyInformation, SymbolCode, jsid,
27};
28use js::jsid::SymbolId;
29use js::jsval::{ObjectValue, UndefinedValue};
30use js::realm::{AutoRealm, CurrentRealm};
31use js::rust::wrappers2::{
32 AppendToIdVector, Call, GetObjectProto, GetPropertyKeys, GetWellKnownSymbol,
33 InvokeGetOwnPropertyDescriptor, JS_AlreadyHasOwnPropertyById, JS_AtomizeAndPinString,
34 JS_DefineFunctions, JS_DefineProperties, JS_DefinePropertyById, JS_DeletePropertyById,
35 JS_GetOwnPropertyDescriptorById, JS_IdToValue, JS_IsExceptionPending,
36 JS_NewObjectWithGivenProto, JS_ValueToSource, RUST_INTERNED_STRING_TO_JSID, RUST_JSID_IS_VOID,
37 SetDataPropertyDescriptor, SetPropertyIgnoringNamedGetter, int_to_jsid,
38};
39use js::rust::{
40 Handle, HandleId, HandleObject, HandleValue, IntoHandle, MutableHandle, MutableHandleObject,
41 MutableHandleValue,
42};
43
44use crate::DomTypes;
45use crate::conversions::{is_dom_proxy, jsid_to_string, native_from_object};
46use crate::error::Error;
47use crate::interfaces::{DomHelpers, GlobalScopeHelpers};
48use crate::principals::ServoJSPrincipalsRef;
49use crate::reflector::DomObject;
50use crate::str::DOMString;
51
52pub(crate) unsafe extern "C" fn shadow_check_callback(
57 cx: *mut RawJSContext,
58 object: RawHandleObject,
59 id: RawHandleId,
60) -> DOMProxyShadowsResult {
61 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
65 let cx = &mut cx;
66
67 let object = unsafe { HandleObject::from_raw(object) };
68 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
69 get_expando_object(object, expando.handle_mut());
70 if !expando.get().is_null() {
71 let mut has_own = false;
72 let raw_id = unsafe { Handle::from_raw(id) };
73
74 if !unsafe { JS_AlreadyHasOwnPropertyById(cx, expando.handle(), raw_id, &mut has_own) } {
75 return DOMProxyShadowsResult::ShadowCheckFailed;
76 }
77
78 if has_own {
79 return DOMProxyShadowsResult::ShadowsViaDirectExpando;
80 }
81 }
82
83 DOMProxyShadowsResult::DoesntShadow
85}
86
87pub fn init() {
89 unsafe {
90 SetDOMProxyInformation(
91 GetProxyHandlerFamily(),
92 Some(shadow_check_callback),
93 ptr::null(),
94 );
95 }
96}
97
98pub(crate) unsafe extern "C" fn define_property(
104 cx: *mut RawJSContext,
105 proxy: RawHandleObject,
106 id: RawHandleId,
107 desc: RawHandle<PropertyDescriptor>,
108 result: *mut ObjectOpResult,
109) -> bool {
110 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
112 let cx = &mut cx;
113
114 let proxy = unsafe { Handle::from_raw(proxy) };
115 let id = unsafe { Handle::from_raw(id) };
116 let desc = unsafe { Handle::from_raw(desc) };
117
118 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
119 ensure_expando_object(cx, proxy, expando.handle_mut());
120
121 unsafe { JS_DefinePropertyById(cx, expando.handle(), id, desc, result) }
122}
123
124pub(crate) unsafe extern "C" fn delete(
130 cx: *mut RawJSContext,
131 proxy: RawHandleObject,
132 id: RawHandleId,
133 bp: *mut ObjectOpResult,
134) -> bool {
135 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
137 let cx = &mut cx;
138
139 let proxy = unsafe { Handle::from_raw(proxy) };
140 let id = unsafe { Handle::from_raw(id) };
141
142 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
143 get_expando_object(proxy, expando.handle_mut());
144
145 if expando.is_null() {
146 unsafe {
147 (*bp).code_ = 0 };
149 return true;
150 }
151
152 unsafe { JS_DeletePropertyById(cx, expando.handle(), id, bp) }
153}
154
155pub unsafe extern "C" fn prevent_extensions(
163 _cx: *mut RawJSContext,
164 _proxy: RawHandleObject,
165 result: *mut ObjectOpResult,
166) -> bool {
167 unsafe { (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t };
168 true
169}
170
171pub unsafe extern "C" fn is_extensible(
179 _cx: *mut RawJSContext,
180 _proxy: RawHandleObject,
181 succeeded: *mut bool,
182) -> bool {
183 unsafe { *succeeded = true };
184 true
185}
186
187pub(crate) unsafe extern "C" fn get_prototype_if_ordinary(
200 _: *mut RawJSContext,
201 proxy: RawHandleObject,
202 is_ordinary: *mut bool,
203 proto: RawMutableHandleObject,
204) -> bool {
205 unsafe { *is_ordinary = true };
206 proto.set(unsafe { GetStaticPrototype(proxy.get()) });
207 true
208}
209
210pub(crate) fn get_expando_object(obj: HandleObject, mut expando: MutableHandleObject) {
212 unsafe {
213 assert!(is_dom_proxy(obj.get()));
214 let val = &mut UndefinedValue();
215 GetProxyPrivate(obj.get(), val);
216 expando.set(if val.is_undefined() {
217 ptr::null_mut()
218 } else {
219 val.to_object()
220 });
221 }
222}
223
224pub(crate) fn ensure_expando_object(
227 cx: &mut JSContext,
228 obj: HandleObject,
229 mut expando: MutableHandleObject,
230) {
231 unsafe {
232 assert!(is_dom_proxy(obj.get()));
233 get_expando_object(obj, expando.reborrow());
234 if expando.is_null() {
235 expando.set(JS_NewObjectWithGivenProto(
236 cx,
237 ptr::null_mut(),
238 HandleObject::null(),
239 ));
240 assert!(!expando.is_null());
241
242 SetProxyPrivate(obj.get(), &ObjectValue(expando.get()));
243 }
244 }
245}
246
247pub fn set_property_descriptor(
250 desc: MutableHandle<PropertyDescriptor>,
251 value: HandleValue,
252 attrs: u32,
253 is_none: &mut bool,
254) {
255 unsafe { SetDataPropertyDescriptor(desc, value, attrs) };
256 *is_none = false;
257}
258
259fn id_to_source(cx: &mut JSContext, id: HandleId) -> Option<DOMString> {
260 unsafe {
261 if RUST_JSID_IS_VOID(id) {
262 return None;
263 }
264 rooted!(&in(cx) let mut value = UndefinedValue());
265 rooted!(&in(cx) let mut jsstr = ptr::null_mut::<JSString>());
266 JS_IdToValue(cx, id.get(), value.handle_mut())
267 .then(|| {
268 jsstr.set(JS_ValueToSource(cx, value.handle()));
269 jsstr.get()
270 })
271 .and_then(NonNull::new)
272 .map(|jsstr| jsstr_to_string(cx, jsstr).into())
273 }
274}
275
276pub(crate) struct CrossOriginProperties {
281 pub(crate) attributes: &'static [JSPropertySpec],
282 pub(crate) methods: &'static [JSFunctionSpec],
283}
284
285impl CrossOriginProperties {
286 fn keys(&self) -> impl Iterator<Item = *const c_char> + '_ {
288 self.attributes
290 .iter()
291 .map(|spec| unsafe { spec.name.string_ })
292 .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ }))
293 .filter(|ptr| !ptr.is_null())
294 }
295}
296
297fn cross_origin_own_property_keys(
301 cx: &mut JSContext,
302 _proxy: HandleObject,
303 cross_origin_properties: &'static CrossOriginProperties,
304 props: RawMutableHandleIdVector,
305) -> bool {
306 for key in cross_origin_properties.keys() {
309 unsafe {
310 rooted!(&in(cx) let rooted = JS_AtomizeAndPinString(cx, key));
311 rooted!(&in(cx) let mut rooted_jsid: jsid);
312 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), rooted_jsid.handle_mut());
313 AppendToIdVector(props, rooted_jsid.handle());
314 }
315 }
316
317 append_cross_origin_allowlisted_prop_keys(cx, props);
320
321 true
322}
323
324pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
327 _: *mut RawJSContext,
328 _proxy: RawHandleObject,
329 is_ordinary: *mut bool,
330 _proto: RawMutableHandleObject,
331) -> bool {
332 unsafe { *is_ordinary = false };
334 true
335}
336
337pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
345 cx: *mut RawJSContext,
346 proxy: RawHandleObject,
347 proto: RawHandleObject,
348 result: *mut ObjectOpResult,
349) -> bool {
350 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
352 let cx = &mut cx;
353 rooted!(&in(cx) let mut current = ptr::null_mut::<JSObject>());
361 if !unsafe { GetObjectProto(cx, Handle::from_raw(proxy), current.handle_mut()) } {
362 return false;
363 }
364
365 if proto.get() == current.get() {
367 unsafe {
368 (*result).code_ = 0 };
370 return true;
371 }
372
373 unsafe { (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize };
375 true
376}
377
378fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
379 if d.hasGetter_() {
380 out.set(d.getter_);
381 }
382}
383
384fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
385 if d.hasSetter_() {
386 out.set(d.setter_);
387 }
388}
389
390fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
392 d.hasSetter_() || d.hasGetter_()
393}
394
395fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
397 d.hasWritable_() || d.hasValue_()
398}
399
400pub(crate) unsafe fn cross_origin_has_own(
409 cx: &mut CurrentRealm,
410 _proxy: HandleObject,
411 cross_origin_properties: &'static CrossOriginProperties,
412 id: HandleId,
413 bp: *mut bool,
414) -> bool {
415 unsafe {
419 *bp = jsid_to_string(cx, id).is_some_and(|key| {
420 cross_origin_properties.keys().any(|defined_key| {
421 let defined_key = CStr::from_ptr(defined_key);
422 defined_key.to_bytes() == key.str().as_bytes()
423 })
424 })
425 };
426
427 true
428}
429
430pub(crate) fn cross_origin_get_own_property_helper(
437 cx: &mut CurrentRealm,
438 proxy: HandleObject,
439 cross_origin_properties: &'static CrossOriginProperties,
440 id: HandleId,
441 desc: MutableHandle<PropertyDescriptor>,
442 is_none: &mut bool,
443) -> bool {
444 rooted!(&in(cx) let mut holder = ptr::null_mut::<JSObject>());
445 ensure_cross_origin_property_holder(cx, proxy, cross_origin_properties, holder.handle_mut());
446
447 unsafe { JS_GetOwnPropertyDescriptorById(cx, holder.handle(), id, desc, is_none) }
448}
449
450const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[
451 SymbolCode::toStringTag,
452 SymbolCode::hasInstance,
453 SymbolCode::isConcatSpreadable,
454];
455
456fn is_cross_origin_allowlisted_prop(cx: &mut JSContext, id: HandleId) -> bool {
457 unsafe {
458 if jsid_to_string(cx, id).is_some_and(|st| st == "then") {
459 return true;
460 }
461
462 rooted!(&in(cx) let mut allowed_id: jsid);
463 ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| {
464 allowed_id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
465 allowed_id.get().asBits_ == id.asBits_
468 })
469 }
470}
471
472fn append_cross_origin_allowlisted_prop_keys(cx: &mut JSContext, props: RawMutableHandleIdVector) {
477 unsafe {
478 rooted!(&in(cx) let mut id: jsid);
479
480 let jsstring = JS_AtomizeAndPinString(cx, c"then".as_ptr());
481 rooted!(&in(cx) let rooted = jsstring);
482 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), id.handle_mut());
483 AppendToIdVector(props, id.handle());
484
485 for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() {
486 id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
487 AppendToIdVector(props, id.handle());
488 }
489 }
490}
491
492fn ensure_cross_origin_property_holder(
505 cx: &mut CurrentRealm,
506 _proxy: HandleObject,
507 cross_origin_properties: &'static CrossOriginProperties,
508 mut out_holder: MutableHandleObject,
509) -> bool {
510 unsafe {
517 out_holder.set(JS_NewObjectWithGivenProto(
518 cx,
519 ptr::null_mut(),
520 HandleObject::null(),
521 ));
522
523 if out_holder.get().is_null() ||
524 !JS_DefineProperties(
525 cx,
526 out_holder.handle(),
527 cross_origin_properties.attributes.as_ptr(),
528 ) ||
529 !JS_DefineFunctions(
530 cx,
531 out_holder.handle(),
532 cross_origin_properties.methods.as_ptr(),
533 )
534 {
535 return false;
536 }
537 }
538
539 true
542}
543
544pub(crate) fn is_cross_origin_object<D: DomTypes>(cx: &mut JSContext, obj: HandleObject) -> bool {
550 unsafe {
551 IsWindowProxy(*obj) ||
552 native_from_object::<D::Location>(cx, *obj).is_ok() ||
553 native_from_object::<D::DissimilarOriginLocation>(cx, *obj).is_ok()
554 }
555}
556
557pub(crate) fn report_cross_origin_denial<D: DomTypes>(
564 cx: &mut CurrentRealm,
565 id: HandleId,
566 access: &str,
567) -> bool {
568 if let Some(id) = id_to_source(cx, id) {
569 debug!(
570 "permission denied to {} property {} on cross-origin object",
571 access,
572 &*id.str(),
573 );
574 } else {
575 debug!("permission denied to {} on cross-origin object", access);
576 }
577 unsafe {
578 if !JS_IsExceptionPending(cx) {
579 let global = D::GlobalScope::from_current_realm(cx);
580 <D as DomHelpers<D>>::throw_dom_exception(cx, &global, Error::Security(None));
582 }
583 }
584 false
585}
586
587pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx<D: DomTypes>(
591 cx: *mut RawJSContext,
592 proxy: RawHandleObject,
593 id: RawHandleId,
594 v: RawHandleValue,
595 receiver: RawHandleValue,
596 result: *mut ObjectOpResult,
597) -> bool {
598 unsafe {
599 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
601 let mut realm = CurrentRealm::assert(&mut cx);
602 let proxy = HandleObject::from_raw(proxy);
603 let id = Handle::from_raw(id);
604 let v = Handle::from_raw(v);
605 let receiver = Handle::from_raw(receiver);
606
607 if !is_platform_object_same_origin(&realm, proxy) {
608 return cross_origin_set::<D>(&mut realm, proxy, id, v.into_handle(), receiver, result);
609 }
610
611 let mut realm = AutoRealm::new_from_handle(&mut realm, proxy);
613
614 rooted!(&in(&mut realm) let mut own_desc = PropertyDescriptor::default());
617 let mut is_none = false;
618 if !InvokeGetOwnPropertyDescriptor(
619 GetProxyHandler(*proxy),
620 &mut realm,
621 proxy,
622 id,
623 own_desc.handle_mut(),
624 &mut is_none,
625 ) {
626 return false;
627 }
628
629 SetPropertyIgnoringNamedGetter(
630 &mut realm,
631 proxy,
632 id,
633 v,
634 receiver,
635 if is_none {
636 None
637 } else {
638 Some(own_desc.handle())
639 },
640 result,
641 )
642 }
643}
644
645pub fn maybe_cross_origin_get_prototype<D: DomTypes>(
650 cx: &mut CurrentRealm,
651 proxy: HandleObject,
652 get_proto_object: fn(cx: &mut JSContext, global: HandleObject, rval: MutableHandleObject),
653 mut proto: MutableHandleObject,
654) -> bool {
655 if is_platform_object_same_origin(cx, proxy) {
657 let mut realm = AutoRealm::new_from_handle(cx, proxy);
658 let mut realm = realm.current_realm();
659 let global = D::GlobalScope::from_current_realm(&mut realm);
660 get_proto_object(
661 &mut realm,
662 global.reflector().get_jsobject(),
663 proto.reborrow(),
664 );
665 return !proto.is_null();
666 }
667
668 proto.set(ptr::null_mut());
670 true
671}
672
673pub(crate) fn cross_origin_get<D: DomTypes>(
680 cx: &mut CurrentRealm,
681 proxy: HandleObject,
682 receiver: HandleValue,
683 id: HandleId,
684 mut vp: MutableHandleValue,
685) -> bool {
686 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
688 let mut is_none = false;
689 if !unsafe {
690 InvokeGetOwnPropertyDescriptor(
691 GetProxyHandler(*proxy),
692 cx,
693 proxy,
694 id,
695 descriptor.handle_mut(),
696 &mut is_none,
697 )
698 } {
699 return false;
700 }
701
702 assert!(
704 !is_none,
705 "Callees should throw in all cases when they are not finding \
706 a property decriptor"
707 );
708
709 if is_data_descriptor(&descriptor) {
711 vp.set(descriptor.value_);
712 return true;
713 }
714
715 assert!(is_accessor_descriptor(&descriptor));
717
718 rooted!(&in(cx) let mut getter = ptr::null_mut::<JSObject>());
723 get_getter_object(&descriptor, getter.handle_mut().into());
724 if getter.get().is_null() {
725 return report_cross_origin_denial::<D>(cx, id, "get");
726 }
727
728 rooted!(&in(cx) let mut getter_jsval = UndefinedValue());
729 getter.get().to_jsval(cx, getter_jsval.handle_mut());
730
731 unsafe {
733 Call(
734 cx,
735 receiver,
736 getter_jsval.handle(),
737 &HandleValueArray::empty(),
738 vp,
739 )
740 }
741}
742
743unsafe fn cross_origin_set<D: DomTypes>(
750 cx: &mut CurrentRealm,
751 proxy: HandleObject,
752 id: HandleId,
753 v: RawHandleValue,
754 receiver: HandleValue,
755 result: *mut ObjectOpResult,
756) -> bool {
757 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
759 let mut is_none = false;
760 if !unsafe {
761 InvokeGetOwnPropertyDescriptor(
762 GetProxyHandler(*proxy),
763 cx,
764 proxy,
765 id,
766 descriptor.handle_mut(),
767 &mut is_none,
768 )
769 } {
770 return false;
771 }
772
773 assert!(
775 !is_none,
776 "Callees should throw in all cases when they are not finding \
777 a property decriptor"
778 );
779
780 rooted!(&in(cx) let mut setter = ptr::null_mut::<JSObject>());
783 get_setter_object(&descriptor, setter.handle_mut().into());
784 if setter.get().is_null() {
785 return report_cross_origin_denial::<D>(cx, id, "set");
787 }
788
789 rooted!(&in(cx) let mut setter_jsval = UndefinedValue());
790 setter.get().to_jsval(cx, setter_jsval.handle_mut());
791
792 rooted!(&in(cx) let mut ignored = UndefinedValue());
796 if !unsafe {
797 Call(
798 cx,
799 receiver,
800 setter_jsval.handle(),
801 &HandleValueArray {
804 length_: 1,
805 elements_: v.ptr,
806 },
807 ignored.handle_mut(),
808 )
809 } {
810 return false;
811 }
812
813 unsafe {
814 (*result).code_ = 0 };
816 true
817}
818
819pub(crate) fn cross_origin_property_fallback<D: DomTypes>(
826 cx: &mut CurrentRealm,
827 _proxy: HandleObject,
828 id: HandleId,
829 desc: MutableHandle<PropertyDescriptor>,
830 is_none: &mut bool,
831) -> bool {
832 assert!(*is_none, "why are we being called?");
833
834 if is_cross_origin_allowlisted_prop(cx, id) {
839 set_property_descriptor(
840 desc,
841 HandleValue::undefined(),
842 JSPROP_READONLY as u32,
843 is_none,
844 );
845 return true;
846 }
847
848 report_cross_origin_denial::<D>(cx, id, "access")
850}
851
852#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
854pub(crate) struct JSProxyHandlerOwnPropertyKeysConfig<T: DomObject> {
855 pub(crate) indexed_getter_and_length: Option<fn(&T, &mut JSContext) -> u32>,
856 pub(crate) cross_origin: Option<&'static CrossOriginProperties>,
857 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
858 pub(crate) supported_named_properties: Option<fn(*const T, &mut JSContext) -> Vec<DOMString>>,
859}
860
861enum Realm<'a> {
863 AutoRealm(AutoRealm<'a>),
864 CurrentRealm(&'a mut CurrentRealm<'a>),
865}
866
867impl<'cx> Deref for Realm<'cx> {
868 type Target = JSContext;
869
870 fn deref(&'_ self) -> &'_ Self::Target {
871 match self {
872 Realm::AutoRealm(auto_realm) => auto_realm,
873 Realm::CurrentRealm(current_realm) => current_realm,
874 }
875 }
876}
877
878impl<'cx> DerefMut for Realm<'cx> {
879 fn deref_mut(&'_ mut self) -> &'_ mut Self::Target {
880 match self {
881 Realm::AutoRealm(auto_realm) => auto_realm,
882 Realm::CurrentRealm(current_realm) => current_realm,
883 }
884 }
885}
886
887#[expect(non_snake_case)]
888pub(crate) unsafe fn JSProxyHandlerOwnPropertyKeys<T>(
890 config: JSProxyHandlerOwnPropertyKeysConfig<T>,
891 cx: *mut RawJSContext,
892 proxy: RawHandleObject,
893 props: RawMutableHandleIdVector,
894) -> bool
895where
896 T: DomObject,
897{
898 unsafe {
899 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
901 let mut cx = CurrentRealm::assert(&mut cx);
902 let current_realm = &mut cx;
903 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
904
905 let proxy = Handle::from_raw(proxy);
906
907 let mut cx = if let Some(cross_origin_properties) = config.cross_origin {
908 if !is_platform_object_same_origin(current_realm, proxy) {
909 return cross_origin_own_property_keys(
910 current_realm,
911 proxy,
912 cross_origin_properties,
913 props,
914 );
915 }
916
917 let cx = AutoRealm::new_from_handle(current_realm, proxy);
919 Realm::AutoRealm(cx)
920 } else {
921 Realm::CurrentRealm(current_realm)
922 };
923
924 if let Some(length_fn) = config.indexed_getter_and_length {
925 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
926 rooted!(&in(cx) let mut rooted_jsid: jsid);
927 for i in 0..length {
928 int_to_jsid(i as i32, rooted_jsid.handle_mut());
929 AppendToIdVector(props, rooted_jsid.handle());
930 }
931 }
932
933 if let Some(properties) = config.supported_named_properties {
934 for name in properties(unwrapped_proxy, &mut cx) {
935 let cstring = CString::new(name).unwrap();
936 let jsstring = JS_AtomizeAndPinString(&cx, cstring.as_ptr());
937 rooted!(&in(cx) let rooted = jsstring);
938 rooted!(&in(cx) let mut rooted_jsid: jsid);
939 RUST_INTERNED_STRING_TO_JSID(
940 &mut cx,
941 rooted.handle().get(),
942 rooted_jsid.handle_mut(),
943 );
944 AppendToIdVector(props, rooted_jsid.handle());
945 }
946 }
947
948 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
949 get_expando_object(proxy, expando.handle_mut());
950
951 if !expando.is_null() &&
952 !GetPropertyKeys(
953 &mut cx,
954 expando.handle(),
955 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
956 props,
957 )
958 {
959 return false;
960 }
961 }
962 true
963}
964
965#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
967pub(crate) struct JSProxyHandlerOwnEnumerablePropertyKeysConfig<T: DomObject> {
968 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
969 #[expect(clippy::type_complexity)]
970 pub(crate) indexed_getter_and_length: Option<Box<dyn Fn(&T, &mut JSContext) -> u32>>,
971 pub(crate) cross_origin: bool,
972}
973
974#[expect(non_snake_case)]
975pub(crate) fn JSProxyHandlerGetOwnEnumerablePropertyKeys<T>(
976 config: JSProxyHandlerOwnEnumerablePropertyKeysConfig<T>,
977 cx: *mut RawJSContext,
978 proxy: RawHandleObject,
979 props: RawMutableHandleIdVector,
980) -> bool
981where
982 T: DomObject,
983{
984 unsafe {
985 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
987 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
988 let mut cx = CurrentRealm::assert(&mut cx);
989 let current_realm = &mut cx;
990
991 let proxy = Handle::from_raw(proxy);
992
993 let mut cx = if config.cross_origin {
994 if !is_platform_object_same_origin(current_realm, proxy) {
995 return true;
997 }
998
999 let cx = AutoRealm::new_from_handle(current_realm, proxy);
1001 Realm::AutoRealm(cx)
1002 } else {
1003 Realm::CurrentRealm(current_realm)
1004 };
1005 if let Some(length_fn) = config.indexed_getter_and_length {
1006 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
1007 rooted!(&in(cx) let mut rooted_jsid: jsid);
1008 for i in 0..length {
1009 int_to_jsid(i as i32, rooted_jsid.handle_mut());
1010 AppendToIdVector(props, rooted_jsid.handle());
1011 }
1012 }
1013
1014 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
1015 get_expando_object(proxy, expando.handle_mut());
1016 if !expando.is_null() &&
1017 !GetPropertyKeys(
1018 &mut cx,
1019 expando.handle(),
1020 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1021 props,
1022 )
1023 {
1024 return false;
1025 }
1026 }
1027
1028 true
1029}
1030
1031pub(crate) fn is_platform_object_same_origin(realm: &CurrentRealm, obj: HandleObject) -> bool {
1033 let subject_realm = realm.realm().as_ptr();
1034 let object_realm = unsafe { GetObjectRealmOrNull(*obj) };
1035 assert!(!object_realm.is_null());
1036
1037 if subject_realm == object_realm {
1038 return true;
1039 }
1040
1041 let subject_principals =
1042 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(subject_realm)) };
1043 let object_principals =
1044 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(object_realm)) };
1045
1046 let subject_origin = subject_principals.origin();
1047 let object_origin = object_principals.origin();
1048
1049 let result = subject_origin.same_origin_domain(&object_origin);
1050 log::trace!(
1051 "object {:p} (realm = {:p}, principalls = {:p}, origin = {:?}) is {} \
1052 with reference to the current Realm (realm = {:p}, principals = {:p}, \
1053 origin = {:?})",
1054 obj.get(),
1055 object_realm,
1056 object_principals.as_raw(),
1057 object_origin.immutable(),
1058 ["NOT same domain-origin", "same domain-origin"][result as usize],
1059 subject_realm,
1060 subject_principals.as_raw(),
1061 subject_origin.immutable()
1062 );
1063
1064 result
1065}