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::{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 JS_AlreadyHasOwnPropertyById, JS_AtomizeAndPinString, JS_DefineFunctions, JS_DefineProperties,
31 JS_DefinePropertyById, JS_DeletePropertyById, JS_GetOwnPropertyDescriptorById, JS_IdToValue,
32 JS_IsExceptionPending, JS_NewObjectWithGivenProto, JS_ValueToSource,
33 RUST_INTERNED_STRING_TO_JSID, RUST_JSID_IS_VOID, SetDataPropertyDescriptor,
34 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 = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
62 let cx = &mut cx;
63
64 let object = unsafe { 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 = unsafe { Handle::from_raw(id) };
70
71 if !unsafe { 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 = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
109 let cx = &mut cx;
110
111 let proxy = unsafe { Handle::from_raw(proxy) };
112 let id = unsafe { Handle::from_raw(id) };
113 let desc = unsafe { 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 unsafe { 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 = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
134 let cx = &mut cx;
135
136 let proxy = unsafe { Handle::from_raw(proxy) };
137 let id = unsafe { 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 unsafe {
144 (*bp).code_ = 0 };
146 return true;
147 }
148
149 unsafe { JS_DeletePropertyById(cx, expando.handle(), id, bp) }
150}
151
152pub unsafe extern "C" fn prevent_extensions(
160 _cx: *mut RawJSContext,
161 _proxy: RawHandleObject,
162 result: *mut ObjectOpResult,
163) -> bool {
164 unsafe { (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t };
165 true
166}
167
168pub unsafe extern "C" fn is_extensible(
176 _cx: *mut RawJSContext,
177 _proxy: RawHandleObject,
178 succeeded: *mut bool,
179) -> bool {
180 unsafe { *succeeded = true };
181 true
182}
183
184pub(crate) unsafe extern "C" fn get_prototype_if_ordinary(
197 _: *mut RawJSContext,
198 proxy: RawHandleObject,
199 is_ordinary: *mut bool,
200 proto: RawMutableHandleObject,
201) -> bool {
202 unsafe { *is_ordinary = true };
203 proto.set(unsafe { GetStaticPrototype(proxy.get()) });
204 true
205}
206
207pub(crate) fn get_expando_object(obj: HandleObject, mut expando: MutableHandleObject) {
209 unsafe {
210 assert!(is_dom_proxy(obj.get()));
211 let val = &mut UndefinedValue();
212 GetProxyPrivate(obj.get(), val);
213 expando.set(if val.is_undefined() {
214 ptr::null_mut()
215 } else {
216 val.to_object()
217 });
218 }
219}
220
221pub(crate) fn ensure_expando_object(
224 cx: &mut JSContext,
225 obj: HandleObject,
226 mut expando: MutableHandleObject,
227) {
228 unsafe {
229 assert!(is_dom_proxy(obj.get()));
230 get_expando_object(obj, expando.reborrow());
231 if expando.is_null() {
232 expando.set(JS_NewObjectWithGivenProto(
233 cx,
234 ptr::null_mut(),
235 HandleObject::null(),
236 ));
237 assert!(!expando.is_null());
238
239 SetProxyPrivate(obj.get(), &ObjectValue(expando.get()));
240 }
241 }
242}
243
244pub fn set_property_descriptor(
247 desc: MutableHandle<PropertyDescriptor>,
248 value: HandleValue,
249 attrs: u32,
250 is_none: &mut bool,
251) {
252 unsafe { SetDataPropertyDescriptor(desc, value, attrs) };
253 *is_none = false;
254}
255
256fn id_to_source(cx: &mut JSContext, id: HandleId) -> Option<DOMString> {
257 unsafe {
258 if RUST_JSID_IS_VOID(id) {
259 return None;
260 }
261 rooted!(&in(cx) let mut value = UndefinedValue());
262 rooted!(&in(cx) let mut jsstr = ptr::null_mut::<JSString>());
263 JS_IdToValue(cx, id.get(), value.handle_mut())
264 .then(|| {
265 jsstr.set(JS_ValueToSource(cx, value.handle()));
266 jsstr.get()
267 })
268 .and_then(NonNull::new)
269 .map(|jsstr| jsstr_to_string(cx, jsstr).into())
270 }
271}
272
273pub struct CrossOriginProperties {
278 pub(crate) attributes: &'static [JSPropertySpec],
279 pub(crate) methods: &'static [JSFunctionSpec],
280}
281
282impl CrossOriginProperties {
283 fn keys(&self) -> impl Iterator<Item = *const c_char> + '_ {
285 self.attributes
287 .iter()
288 .map(|spec| unsafe { spec.name.string_ })
289 .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ }))
290 .filter(|ptr| !ptr.is_null())
291 }
292}
293
294pub fn cross_origin_own_property_keys(
298 cx: &mut JSContext,
299 _proxy: HandleObject,
300 cross_origin_properties: &'static CrossOriginProperties,
301 props: RawMutableHandleIdVector,
302) -> bool {
303 for key in cross_origin_properties.keys() {
306 unsafe {
307 rooted!(&in(cx) let rooted = JS_AtomizeAndPinString(cx, key));
308 rooted!(&in(cx) let mut rooted_jsid: jsid);
309 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), rooted_jsid.handle_mut());
310 AppendToIdVector(props, rooted_jsid.handle());
311 }
312 }
313
314 append_cross_origin_allowlisted_prop_keys(cx, props);
317
318 true
319}
320
321pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
324 _: *mut RawJSContext,
325 _proxy: RawHandleObject,
326 is_ordinary: *mut bool,
327 _proto: RawMutableHandleObject,
328) -> bool {
329 unsafe { *is_ordinary = false };
331 true
332}
333
334pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
342 cx: *mut RawJSContext,
343 proxy: RawHandleObject,
344 proto: RawHandleObject,
345 result: *mut ObjectOpResult,
346) -> bool {
347 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
349 let cx = &mut cx;
350 rooted!(&in(cx) let mut current = ptr::null_mut::<JSObject>());
358 if !unsafe { GetObjectProto(cx, Handle::from_raw(proxy), current.handle_mut()) } {
359 return false;
360 }
361
362 if proto.get() == current.get() {
364 unsafe {
365 (*result).code_ = 0 };
367 return true;
368 }
369
370 unsafe { (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize };
372 true
373}
374
375fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
376 if d.hasGetter_() {
377 out.set(d.getter_);
378 }
379}
380
381fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
382 if d.hasSetter_() {
383 out.set(d.setter_);
384 }
385}
386
387fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
389 d.hasSetter_() || d.hasGetter_()
390}
391
392fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
394 d.hasWritable_() || d.hasValue_()
395}
396
397pub(crate) unsafe fn cross_origin_has_own(
406 cx: &mut CurrentRealm,
407 _proxy: HandleObject,
408 cross_origin_properties: &'static CrossOriginProperties,
409 id: HandleId,
410 bp: *mut bool,
411) -> bool {
412 unsafe {
416 *bp = jsid_to_string(cx, id).is_some_and(|key| {
417 cross_origin_properties.keys().any(|defined_key| {
418 let defined_key = CStr::from_ptr(defined_key);
419 defined_key.to_bytes() == key.str().as_bytes()
420 })
421 })
422 };
423
424 true
425}
426
427pub fn cross_origin_get_own_property_helper(
434 cx: &mut CurrentRealm,
435 proxy: HandleObject,
436 cross_origin_properties: &'static CrossOriginProperties,
437 id: HandleId,
438 desc: MutableHandle<PropertyDescriptor>,
439 is_none: &mut bool,
440) -> bool {
441 rooted!(&in(cx) let mut holder = ptr::null_mut::<JSObject>());
442 ensure_cross_origin_property_holder(cx, proxy, cross_origin_properties, holder.handle_mut());
443
444 unsafe { JS_GetOwnPropertyDescriptorById(cx, holder.handle(), id, desc, is_none) }
445}
446
447const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[
448 SymbolCode::toStringTag,
449 SymbolCode::hasInstance,
450 SymbolCode::isConcatSpreadable,
451];
452
453fn is_cross_origin_allowlisted_prop(cx: &mut JSContext, id: HandleId) -> bool {
454 unsafe {
455 if jsid_to_string(cx, id).is_some_and(|st| st == "then") {
456 return true;
457 }
458
459 rooted!(&in(cx) let mut allowed_id: jsid);
460 ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| {
461 allowed_id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
462 allowed_id.get().asBits_ == id.asBits_
465 })
466 }
467}
468
469fn append_cross_origin_allowlisted_prop_keys(cx: &mut JSContext, props: RawMutableHandleIdVector) {
474 unsafe {
475 rooted!(&in(cx) let mut id: jsid);
476
477 let jsstring = JS_AtomizeAndPinString(cx, c"then".as_ptr());
478 rooted!(&in(cx) let rooted = jsstring);
479 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), id.handle_mut());
480 AppendToIdVector(props, id.handle());
481
482 for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() {
483 id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
484 AppendToIdVector(props, id.handle());
485 }
486 }
487}
488
489fn ensure_cross_origin_property_holder(
502 cx: &mut CurrentRealm,
503 _proxy: HandleObject,
504 cross_origin_properties: &'static CrossOriginProperties,
505 mut out_holder: MutableHandleObject,
506) -> bool {
507 unsafe {
514 out_holder.set(JS_NewObjectWithGivenProto(
515 cx,
516 ptr::null_mut(),
517 HandleObject::null(),
518 ));
519
520 if out_holder.get().is_null() ||
521 !JS_DefineProperties(
522 cx,
523 out_holder.handle(),
524 cross_origin_properties.attributes.as_ptr(),
525 ) ||
526 !JS_DefineFunctions(
527 cx,
528 out_holder.handle(),
529 cross_origin_properties.methods.as_ptr(),
530 )
531 {
532 return false;
533 }
534 }
535
536 true
539}
540
541pub(crate) fn is_cross_origin_object<D: DomTypes>(cx: &mut JSContext, obj: HandleObject) -> bool {
547 unsafe {
548 IsWindowProxy(*obj) ||
549 native_from_object::<D::Location>(cx, *obj).is_ok() ||
550 native_from_object::<D::DissimilarOriginLocation>(cx, *obj).is_ok()
551 }
552}
553
554pub fn report_cross_origin_denial<D: DomTypes>(
561 cx: &mut CurrentRealm,
562 id: HandleId,
563 access: &str,
564) -> bool {
565 if let Some(id) = id_to_source(cx, id) {
566 debug!(
567 "permission denied to {} property {} on cross-origin object",
568 access,
569 &*id.str(),
570 );
571 } else {
572 debug!("permission denied to {} on cross-origin object", access);
573 }
574 unsafe {
575 if !JS_IsExceptionPending(cx) {
576 let global = D::GlobalScope::from_current_realm(cx);
577 <D as DomHelpers<D>>::throw_dom_exception(cx, &global, Error::Security(None));
579 }
580 }
581 false
582}
583
584pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx<D: DomTypes>(
588 cx: *mut RawJSContext,
589 proxy: RawHandleObject,
590 id: RawHandleId,
591 v: RawHandleValue,
592 receiver: RawHandleValue,
593 result: *mut ObjectOpResult,
594) -> bool {
595 unsafe {
596 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
598 let mut realm = CurrentRealm::assert(&mut cx);
599 let proxy = HandleObject::from_raw(proxy);
600 let id = Handle::from_raw(id);
601 let v = Handle::from_raw(v);
602 let receiver = Handle::from_raw(receiver);
603
604 if !is_platform_object_same_origin(&realm, proxy) {
605 return cross_origin_set::<D>(&mut realm, proxy, id, v.into_handle(), receiver, result);
606 }
607
608 let mut realm = AutoRealm::new_from_handle(&mut realm, proxy);
610
611 rooted!(&in(&mut realm) let mut own_desc = PropertyDescriptor::default());
614 let mut is_none = false;
615 if !JS_GetOwnPropertyDescriptorById(
616 &mut realm,
617 proxy,
618 id,
619 own_desc.handle_mut(),
620 &mut is_none,
621 ) {
622 return false;
623 }
624
625 SetPropertyIgnoringNamedGetter(
626 &mut realm,
627 proxy,
628 id,
629 v,
630 receiver,
631 if is_none {
632 None
633 } else {
634 Some(own_desc.handle())
635 },
636 result,
637 )
638 }
639}
640
641pub fn maybe_cross_origin_get_prototype<D: DomTypes>(
646 cx: &mut CurrentRealm,
647 proxy: HandleObject,
648 get_proto_object: fn(cx: &mut JSContext, global: HandleObject, rval: MutableHandleObject),
649 mut proto: MutableHandleObject,
650) -> bool {
651 if is_platform_object_same_origin(cx, proxy) {
653 let mut realm = AutoRealm::new_from_handle(cx, proxy);
654 let mut realm = realm.current_realm();
655 let global = D::GlobalScope::from_current_realm(&mut realm);
656 get_proto_object(
657 &mut realm,
658 global.reflector().get_jsobject(),
659 proto.reborrow(),
660 );
661 return !proto.is_null();
662 }
663
664 proto.set(ptr::null_mut());
666 true
667}
668
669pub fn cross_origin_get<D: DomTypes>(
676 cx: &mut CurrentRealm,
677 proxy: HandleObject,
678 receiver: HandleValue,
679 id: HandleId,
680 mut vp: MutableHandleValue,
681) -> bool {
682 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
684 let mut is_none = false;
685 if !unsafe {
686 JS_GetOwnPropertyDescriptorById(cx, proxy, id, descriptor.handle_mut(), &mut is_none)
687 } {
688 return false;
689 }
690
691 assert!(
693 !is_none,
694 "Callees should throw in all cases when they are not finding \
695 a property decriptor"
696 );
697
698 if is_data_descriptor(&descriptor) {
700 vp.set(descriptor.value_);
701 return true;
702 }
703
704 assert!(is_accessor_descriptor(&descriptor));
706
707 rooted!(&in(cx) let mut getter = ptr::null_mut::<JSObject>());
712 get_getter_object(&descriptor, getter.handle_mut().into());
713 if getter.get().is_null() {
714 return report_cross_origin_denial::<D>(cx, id, "get");
715 }
716
717 rooted!(&in(cx) let mut getter_jsval = UndefinedValue());
718 getter.get().to_jsval(cx, getter_jsval.handle_mut());
719
720 unsafe {
722 Call(
723 cx,
724 receiver,
725 getter_jsval.handle(),
726 &HandleValueArray::empty(),
727 vp,
728 )
729 }
730}
731
732pub unsafe fn cross_origin_set<D: DomTypes>(
742 cx: &mut CurrentRealm,
743 proxy: HandleObject,
744 id: HandleId,
745 v: RawHandleValue,
746 receiver: HandleValue,
747 result: *mut ObjectOpResult,
748) -> bool {
749 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
751 let mut is_none = false;
752 if !unsafe {
753 JS_GetOwnPropertyDescriptorById(cx, proxy, id, descriptor.handle_mut(), &mut is_none)
754 } {
755 return false;
756 }
757
758 assert!(
760 !is_none,
761 "Callees should throw in all cases when they are not finding \
762 a property decriptor"
763 );
764
765 rooted!(&in(cx) let mut setter = ptr::null_mut::<JSObject>());
768 get_setter_object(&descriptor, setter.handle_mut().into());
769 if setter.get().is_null() {
770 return report_cross_origin_denial::<D>(cx, id, "set");
772 }
773
774 rooted!(&in(cx) let mut setter_jsval = UndefinedValue());
775 setter.get().to_jsval(cx, setter_jsval.handle_mut());
776
777 rooted!(&in(cx) let mut ignored = UndefinedValue());
781 if !unsafe {
782 Call(
783 cx,
784 receiver,
785 setter_jsval.handle(),
786 &HandleValueArray {
789 length_: 1,
790 elements_: v.ptr,
791 },
792 ignored.handle_mut(),
793 )
794 } {
795 return false;
796 }
797
798 unsafe {
799 (*result).code_ = 0 };
801 true
802}
803
804pub fn cross_origin_property_fallback<D: DomTypes>(
811 cx: &mut CurrentRealm,
812 _proxy: HandleObject,
813 id: HandleId,
814 desc: MutableHandle<PropertyDescriptor>,
815 is_none: &mut bool,
816) -> bool {
817 assert!(*is_none, "why are we being called?");
818
819 if is_cross_origin_allowlisted_prop(cx, id) {
824 set_property_descriptor(
825 desc,
826 HandleValue::undefined(),
827 JSPROP_READONLY as u32,
828 is_none,
829 );
830 return true;
831 }
832
833 report_cross_origin_denial::<D>(cx, id, "access")
835}
836
837#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
839pub(crate) struct JSProxyHandlerOwnPropertyKeysConfig<T: DomObject> {
840 pub(crate) indexed_getter_and_length: Option<fn(&T, &mut JSContext) -> u32>,
841 pub(crate) cross_origin: Option<&'static CrossOriginProperties>,
842 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
843 pub(crate) supported_named_properties: Option<fn(*const T, &mut JSContext) -> Vec<DOMString>>,
844}
845
846enum Realm<'a> {
848 AutoRealm(AutoRealm<'a>),
849 CurrentRealm(&'a mut CurrentRealm<'a>),
850}
851
852impl<'cx> Deref for Realm<'cx> {
853 type Target = JSContext;
854
855 fn deref(&'_ self) -> &'_ Self::Target {
856 match self {
857 Realm::AutoRealm(auto_realm) => auto_realm,
858 Realm::CurrentRealm(current_realm) => current_realm,
859 }
860 }
861}
862
863impl<'cx> DerefMut for Realm<'cx> {
864 fn deref_mut(&'_ mut self) -> &'_ mut Self::Target {
865 match self {
866 Realm::AutoRealm(auto_realm) => auto_realm,
867 Realm::CurrentRealm(current_realm) => current_realm,
868 }
869 }
870}
871
872#[expect(non_snake_case)]
873pub(crate) unsafe fn JSProxyHandlerOwnPropertyKeys<T>(
875 config: JSProxyHandlerOwnPropertyKeysConfig<T>,
876 cx: *mut RawJSContext,
877 proxy: RawHandleObject,
878 props: RawMutableHandleIdVector,
879) -> bool
880where
881 T: DomObject,
882{
883 unsafe {
884 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
886 let mut cx = CurrentRealm::assert(&mut cx);
887 let current_realm = &mut cx;
888 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
889
890 let proxy = Handle::from_raw(proxy);
891
892 let mut cx = if let Some(cross_origin_properties) = config.cross_origin {
893 if !is_platform_object_same_origin(current_realm, proxy) {
894 return cross_origin_own_property_keys(
895 current_realm,
896 proxy,
897 cross_origin_properties,
898 props,
899 );
900 }
901
902 let cx = AutoRealm::new_from_handle(current_realm, proxy);
904 Realm::AutoRealm(cx)
905 } else {
906 Realm::CurrentRealm(current_realm)
907 };
908
909 if let Some(length_fn) = config.indexed_getter_and_length {
910 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
911 rooted!(&in(cx) let mut rooted_jsid: jsid);
912 for i in 0..length {
913 int_to_jsid(i as i32, rooted_jsid.handle_mut());
914 AppendToIdVector(props, rooted_jsid.handle());
915 }
916 }
917
918 if let Some(properties) = config.supported_named_properties {
919 for name in properties(unwrapped_proxy, &mut cx) {
920 let cstring = CString::new(name).unwrap();
921 let jsstring = JS_AtomizeAndPinString(&cx, cstring.as_ptr());
922 rooted!(&in(cx) let rooted = jsstring);
923 rooted!(&in(cx) let mut rooted_jsid: jsid);
924 RUST_INTERNED_STRING_TO_JSID(
925 &mut cx,
926 rooted.handle().get(),
927 rooted_jsid.handle_mut(),
928 );
929 AppendToIdVector(props, rooted_jsid.handle());
930 }
931 }
932
933 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
934 get_expando_object(proxy, expando.handle_mut());
935
936 if !expando.is_null() &&
937 !GetPropertyKeys(
938 &mut cx,
939 expando.handle(),
940 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
941 props,
942 )
943 {
944 return false;
945 }
946 }
947 true
948}
949
950#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
952pub(crate) struct JSProxyHandlerOwnEnumerablePropertyKeysConfig<T: DomObject> {
953 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
954 #[expect(clippy::type_complexity)]
955 pub(crate) indexed_getter_and_length: Option<Box<dyn Fn(&T, &mut JSContext) -> u32>>,
956 pub(crate) cross_origin: bool,
957}
958
959#[expect(non_snake_case)]
960pub(crate) fn JSProxyHandlerGetOwnEnumerablePropertyKeys<T>(
961 config: JSProxyHandlerOwnEnumerablePropertyKeysConfig<T>,
962 cx: *mut RawJSContext,
963 proxy: RawHandleObject,
964 props: RawMutableHandleIdVector,
965) -> bool
966where
967 T: DomObject,
968{
969 unsafe {
970 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
972 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
973 let mut cx = CurrentRealm::assert(&mut cx);
974 let current_realm = &mut cx;
975
976 let proxy = Handle::from_raw(proxy);
977
978 let mut cx = if config.cross_origin {
979 if !is_platform_object_same_origin(current_realm, proxy) {
980 return true;
982 }
983
984 let cx = AutoRealm::new_from_handle(current_realm, proxy);
986 Realm::AutoRealm(cx)
987 } else {
988 Realm::CurrentRealm(current_realm)
989 };
990 if let Some(length_fn) = config.indexed_getter_and_length {
991 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
992 rooted!(&in(cx) let mut rooted_jsid: jsid);
993 for i in 0..length {
994 int_to_jsid(i as i32, rooted_jsid.handle_mut());
995 AppendToIdVector(props, rooted_jsid.handle());
996 }
997 }
998
999 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
1000 get_expando_object(proxy, expando.handle_mut());
1001 if !expando.is_null() &&
1002 !GetPropertyKeys(
1003 &mut cx,
1004 expando.handle(),
1005 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1006 props,
1007 )
1008 {
1009 return false;
1010 }
1011 }
1012
1013 true
1014}
1015
1016pub fn is_platform_object_same_origin(realm: &CurrentRealm, obj: HandleObject) -> bool {
1018 let subject_realm = realm.realm().as_ptr();
1019 let object_realm = unsafe { GetObjectRealmOrNull(*obj) };
1020 assert!(!object_realm.is_null());
1021
1022 if subject_realm == object_realm {
1023 return true;
1024 }
1025
1026 let subject_principals =
1027 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(subject_realm)) };
1028 let object_principals =
1029 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(object_realm)) };
1030
1031 let subject_origin = subject_principals.origin();
1032 let object_origin = object_principals.origin();
1033
1034 let result = subject_origin.same_origin_domain(&object_origin);
1035 log::trace!(
1036 "object {:p} (realm = {:p}, principalls = {:p}, origin = {:?}) is {} \
1037 with reference to the current Realm (realm = {:p}, principals = {:p}, \
1038 origin = {:?})",
1039 obj.get(),
1040 object_realm,
1041 object_principals.as_raw(),
1042 object_origin.immutable(),
1043 ["NOT same domain-origin", "same domain-origin"][result as usize],
1044 subject_realm,
1045 subject_principals.as_raw(),
1046 subject_origin.immutable()
1047 );
1048
1049 result
1050}