1use std::ffi::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::{
16 GetProxyHandlerFamily, GetProxyPrivate, GetProxyReservedSlot, SetProxyPrivate,
17 SetProxyReservedSlot, UncheckedUnwrapObject,
18};
19use js::jsapi::{
20 DOMProxyShadowsResult, GetObjectRealmOrNull, GetRealmPrincipals, GetStaticPrototype,
21 Handle as RawHandle, HandleId as RawHandleId, HandleObject as RawHandleObject,
22 HandleValue as RawHandleValue, HandleValueArray, IsWeakMapObject, IsWindowProxy,
23 JS_IsDeadWrapper, JSErrNum, JSFunctionSpec, JSITER_HIDDEN, JSITER_OWNONLY, JSITER_SYMBOLS,
24 JSObject, JSPROP_READONLY, JSPropertySpec, JSString,
25 MutableHandleIdVector as RawMutableHandleIdVector,
26 MutableHandleObject as RawMutableHandleObject, ObjectOpResult, PropertyDescriptor,
27 SetDOMProxyInformation, SymbolCode, jsid,
28};
29use js::jsid::SymbolId;
30use js::jsval::{ObjectValue, UndefinedValue};
31use js::realm::{AutoRealm, CurrentRealm};
32use js::rust::wrappers2::{
33 AppendToIdVector, Call, GetObjectProto, GetPropertyKeys, GetRealmKeyObject, GetWeakMapEntry,
34 GetWellKnownSymbol, JS_AlreadyHasOwnPropertyById, JS_AtomizeAndPinString, JS_DefineFunctions,
35 JS_DefineProperties, JS_DefinePropertyById, JS_DeletePropertyById,
36 JS_GetOwnPropertyDescriptorById, JS_IdToValue, JS_IsExceptionPending,
37 JS_NewObjectWithGivenProto, JS_ValueToSource, JS_WrapObject, JS_WrapValue, NewWeakMapObject,
38 RUST_INTERNED_STRING_TO_JSID, RUST_JSID_IS_VOID, SetDataPropertyDescriptor,
39 SetPropertyIgnoringNamedGetter, SetWeakMapEntry, int_to_jsid,
40};
41use js::rust::{
42 Handle, HandleId, HandleObject, HandleValue, IntoHandle, MutableHandle, MutableHandleObject,
43 MutableHandleValue,
44};
45
46use crate::DomTypes;
47use crate::conversions::{is_dom_proxy, jsid_to_string, native_from_object};
48use crate::error::Error;
49use crate::interfaces::{DomHelpers, GlobalScopeHelpers};
50use crate::principals::ServoJSPrincipalsRef;
51use crate::reflector::DomObject;
52use crate::str::DOMString;
53
54pub const CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT: u32 = 1;
59
60pub(crate) unsafe extern "C" fn shadow_check_callback(
65 cx: *mut RawJSContext,
66 object: RawHandleObject,
67 id: RawHandleId,
68) -> DOMProxyShadowsResult {
69 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
73 let cx = &mut cx;
74
75 let object = unsafe { HandleObject::from_raw(object) };
76 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
77 get_expando_object(object, expando.handle_mut());
78 if !expando.get().is_null() {
79 let mut has_own = false;
80 let raw_id = unsafe { Handle::from_raw(id) };
81
82 if !unsafe { JS_AlreadyHasOwnPropertyById(cx, expando.handle(), raw_id, &mut has_own) } {
83 return DOMProxyShadowsResult::ShadowCheckFailed;
84 }
85
86 if has_own {
87 return DOMProxyShadowsResult::ShadowsViaDirectExpando;
88 }
89 }
90
91 DOMProxyShadowsResult::DoesntShadow
93}
94
95pub fn init() {
97 unsafe {
98 SetDOMProxyInformation(
99 GetProxyHandlerFamily(),
100 Some(shadow_check_callback),
101 ptr::null(),
102 );
103 }
104}
105
106pub(crate) unsafe extern "C" fn define_property(
112 cx: *mut RawJSContext,
113 proxy: RawHandleObject,
114 id: RawHandleId,
115 desc: RawHandle<PropertyDescriptor>,
116 result: *mut ObjectOpResult,
117) -> bool {
118 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
120 let cx = &mut cx;
121
122 let proxy = unsafe { Handle::from_raw(proxy) };
123 let id = unsafe { Handle::from_raw(id) };
124 let desc = unsafe { Handle::from_raw(desc) };
125
126 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
127 ensure_expando_object(cx, proxy, expando.handle_mut());
128
129 unsafe { JS_DefinePropertyById(cx, expando.handle(), id, desc, result) }
130}
131
132pub(crate) unsafe extern "C" fn delete(
138 cx: *mut RawJSContext,
139 proxy: RawHandleObject,
140 id: RawHandleId,
141 bp: *mut ObjectOpResult,
142) -> bool {
143 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
145 let cx = &mut cx;
146
147 let proxy = unsafe { Handle::from_raw(proxy) };
148 let id = unsafe { Handle::from_raw(id) };
149
150 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
151 get_expando_object(proxy, expando.handle_mut());
152
153 if expando.is_null() {
154 unsafe {
155 (*bp).code_ = 0 };
157 return true;
158 }
159
160 unsafe { JS_DeletePropertyById(cx, expando.handle(), id, bp) }
161}
162
163pub unsafe extern "C" fn prevent_extensions(
171 _cx: *mut RawJSContext,
172 _proxy: RawHandleObject,
173 result: *mut ObjectOpResult,
174) -> bool {
175 unsafe { (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t };
176 true
177}
178
179pub unsafe extern "C" fn is_extensible(
187 _cx: *mut RawJSContext,
188 _proxy: RawHandleObject,
189 succeeded: *mut bool,
190) -> bool {
191 unsafe { *succeeded = true };
192 true
193}
194
195pub(crate) unsafe extern "C" fn get_prototype_if_ordinary(
208 _: *mut RawJSContext,
209 proxy: RawHandleObject,
210 is_ordinary: *mut bool,
211 proto: RawMutableHandleObject,
212) -> bool {
213 unsafe { *is_ordinary = true };
214 proto.set(unsafe { GetStaticPrototype(proxy.get()) });
215 true
216}
217
218pub(crate) fn get_expando_object(obj: HandleObject, mut expando: MutableHandleObject) {
220 unsafe {
221 assert!(is_dom_proxy(obj.get()));
222 let val = &mut UndefinedValue();
223 GetProxyPrivate(obj.get(), val);
224 expando.set(if val.is_undefined() {
225 ptr::null_mut()
226 } else {
227 val.to_object()
228 });
229 }
230}
231
232pub(crate) fn ensure_expando_object(
235 cx: &mut JSContext,
236 obj: HandleObject,
237 mut expando: MutableHandleObject,
238) {
239 unsafe {
240 assert!(is_dom_proxy(obj.get()));
241 get_expando_object(obj, expando.reborrow());
242 if expando.is_null() {
243 expando.set(JS_NewObjectWithGivenProto(
244 cx,
245 ptr::null_mut(),
246 HandleObject::null(),
247 ));
248 assert!(!expando.is_null());
249
250 SetProxyPrivate(obj.get(), &ObjectValue(expando.get()));
251 }
252 }
253}
254
255pub fn set_property_descriptor(
258 desc: MutableHandle<PropertyDescriptor>,
259 value: HandleValue,
260 attrs: u32,
261 is_none: &mut bool,
262) {
263 unsafe { SetDataPropertyDescriptor(desc, value, attrs) };
264 *is_none = false;
265}
266
267fn id_to_source(cx: &mut JSContext, id: HandleId) -> Option<DOMString> {
268 unsafe {
269 if RUST_JSID_IS_VOID(id) {
270 return None;
271 }
272 rooted!(&in(cx) let mut value = UndefinedValue());
273 rooted!(&in(cx) let mut jsstr = ptr::null_mut::<JSString>());
274 JS_IdToValue(cx, id.get(), value.handle_mut())
275 .then(|| {
276 jsstr.set(JS_ValueToSource(cx, value.handle()));
277 jsstr.get()
278 })
279 .and_then(NonNull::new)
280 .map(|jsstr| jsstr_to_string(cx, jsstr).into())
281 }
282}
283
284pub struct CrossOriginProperties {
289 pub(crate) attributes: &'static [JSPropertySpec],
290 pub(crate) methods: &'static [JSFunctionSpec],
291}
292
293impl CrossOriginProperties {
294 fn keys(&self) -> impl Iterator<Item = *const c_char> + '_ {
296 self.attributes
298 .iter()
299 .map(|spec| unsafe { spec.name.string_ })
300 .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ }))
301 .filter(|ptr| !ptr.is_null())
302 }
303}
304
305pub fn cross_origin_own_property_keys(
309 cx: &mut JSContext,
310 _proxy: HandleObject,
311 cross_origin_properties: &'static CrossOriginProperties,
312 props: RawMutableHandleIdVector,
313) -> bool {
314 for key in cross_origin_properties.keys() {
317 unsafe {
318 rooted!(&in(cx) let rooted = JS_AtomizeAndPinString(cx, key));
319 rooted!(&in(cx) let mut rooted_jsid: jsid);
320 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), rooted_jsid.handle_mut());
321 AppendToIdVector(props, rooted_jsid.handle());
322 }
323 }
324
325 append_cross_origin_allowlisted_prop_keys(cx, props);
328
329 true
330}
331
332pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
335 _: *mut RawJSContext,
336 _proxy: RawHandleObject,
337 is_ordinary: *mut bool,
338 _proto: RawMutableHandleObject,
339) -> bool {
340 unsafe { *is_ordinary = false };
342 true
343}
344
345pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
353 cx: *mut RawJSContext,
354 proxy: RawHandleObject,
355 proto: RawHandleObject,
356 result: *mut ObjectOpResult,
357) -> bool {
358 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
360 let cx = &mut cx;
361 rooted!(&in(cx) let mut current = ptr::null_mut::<JSObject>());
369 if !unsafe { GetObjectProto(cx, Handle::from_raw(proxy), current.handle_mut()) } {
370 return false;
371 }
372
373 if proto.get() == current.get() {
375 unsafe {
376 (*result).code_ = 0 };
378 return true;
379 }
380
381 unsafe { (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize };
383 true
384}
385
386fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
387 if d.hasGetter_() {
388 out.set(d.getter_);
389 }
390}
391
392fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
393 if d.hasSetter_() {
394 out.set(d.setter_);
395 }
396}
397
398fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
400 d.hasSetter_() || d.hasGetter_()
401}
402
403fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
405 d.hasWritable_() || d.hasValue_()
406}
407
408pub(crate) fn cross_origin_has_own<D: DomTypes>(
413 cx: &mut CurrentRealm,
414 proxy: HandleObject,
415 cross_origin_properties: &'static CrossOriginProperties,
416 id: HandleId,
417 exists: &mut bool,
418) -> bool {
419 rooted!(&in(cx) let mut property_descriptor = PropertyDescriptor::default());
420 let mut is_none = false;
421 if !cross_origin_get_own_property_helper(
422 cx,
423 proxy,
424 cross_origin_properties,
425 id,
426 property_descriptor.handle_mut(),
427 &mut is_none,
428 ) {
429 return false;
430 }
431
432 if is_none &&
433 !cross_origin_property_fallback::<D>(
434 cx,
435 proxy,
436 id,
437 property_descriptor.handle_mut(),
438 &mut is_none,
439 )
440 {
441 return false;
442 }
443
444 *exists = !is_none;
445 true
446}
447
448pub fn cross_origin_get_own_property_helper(
455 cx: &mut CurrentRealm,
456 proxy: HandleObject,
457 cross_origin_properties: &'static CrossOriginProperties,
458 id: HandleId,
459 desc: MutableHandle<PropertyDescriptor>,
460 is_none: &mut bool,
461) -> bool {
462 rooted!(&in(cx) let mut holder = ptr::null_mut::<JSObject>());
463 if !ensure_cross_origin_property_holder(cx, proxy, cross_origin_properties, holder.handle_mut())
464 {
465 return false;
466 }
467 unsafe { JS_GetOwnPropertyDescriptorById(cx, holder.handle(), id, desc, is_none) }
468}
469
470const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[
471 SymbolCode::toStringTag,
472 SymbolCode::hasInstance,
473 SymbolCode::isConcatSpreadable,
474];
475
476fn is_cross_origin_allowlisted_prop(cx: &mut JSContext, id: HandleId) -> bool {
477 unsafe {
478 if jsid_to_string(cx, id).is_some_and(|st| st == "then") {
479 return true;
480 }
481
482 rooted!(&in(cx) let mut allowed_id: jsid);
483 ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| {
484 allowed_id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
485 allowed_id.get().asBits_ == id.asBits_
488 })
489 }
490}
491
492fn append_cross_origin_allowlisted_prop_keys(cx: &mut JSContext, props: RawMutableHandleIdVector) {
497 unsafe {
498 rooted!(&in(cx) let mut id: jsid);
499
500 let jsstring = JS_AtomizeAndPinString(cx, c"then".as_ptr());
501 rooted!(&in(cx) let rooted = jsstring);
502 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), id.handle_mut());
503 AppendToIdVector(props, id.handle());
504
505 for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() {
506 id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
507 AppendToIdVector(props, id.handle());
508 }
509 }
510}
511
512fn ensure_cross_origin_property_holder(
525 cx: &mut CurrentRealm,
526 proxy: HandleObject,
527 cross_origin_properties: &'static CrossOriginProperties,
528 mut out_holder: MutableHandleObject,
529) -> bool {
530 rooted!(&in(cx) let mut weak_value_map = UndefinedValue());
531 unsafe {
532 GetProxyReservedSlot(
533 proxy.get(),
534 CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT,
535 weak_value_map.as_ptr(),
536 )
537 };
538 if weak_value_map.is_undefined() {
539 let mut realm = AutoRealm::new_from_handle(cx, proxy);
540 let new_map = unsafe { NewWeakMapObject(&mut realm) };
541 if new_map.is_null() {
542 return false;
543 }
544 weak_value_map.set(ObjectValue(new_map));
545 unsafe {
546 SetProxyReservedSlot(
547 proxy.get(),
548 CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT,
549 weak_value_map.as_ptr(),
550 )
551 };
552 }
553
554 rooted!(&in(cx) let map = weak_value_map.to_object());
555 debug_assert!(unsafe { IsWeakMapObject(map.get()) });
556
557 rooted!(&in(cx) let mut key = unsafe { GetRealmKeyObject(cx) });
584 if key.is_null() {
585 return false;
586 }
587
588 rooted!(&in(cx) let mut holder_value = UndefinedValue());
589 {
590 let mut realm = AutoRealm::new_from_handle(cx, map.handle());
591 let cx = &mut realm;
592
593 if !unsafe { JS_WrapObject(cx, key.handle_mut()) } {
594 return false;
595 }
596
597 rooted!(&in(cx) let key_value = ObjectValue(key.get()));
598 if !unsafe {
599 GetWeakMapEntry(
600 cx,
601 map.handle(),
602 key_value.handle(),
603 holder_value.handle_mut(),
604 )
605 } {
606 return false;
607 }
608 }
609
610 if holder_value.is_object() {
611 out_holder.set(unsafe { UncheckedUnwrapObject(holder_value.to_object(), true) });
614
615 if !unsafe { JS_IsDeadWrapper(out_holder.get()) } {
617 return true;
618 }
619 }
620
621 out_holder
629 .set(unsafe { JS_NewObjectWithGivenProto(cx, ptr::null_mut(), HandleObject::null()) });
630
631 if out_holder.get().is_null() ||
632 !unsafe {
633 JS_DefineProperties(
634 cx,
635 out_holder.handle(),
636 cross_origin_properties.attributes.as_ptr(),
637 )
638 } ||
639 !unsafe {
640 JS_DefineFunctions(
641 cx,
642 out_holder.handle(),
643 cross_origin_properties.methods.as_ptr(),
644 )
645 }
646 {
647 return false;
648 }
649
650 holder_value.set(ObjectValue(out_holder.get()));
651
652 {
653 let mut realm = AutoRealm::new_from_handle(cx, map.handle());
654 let cx = &mut realm;
655
656 if !unsafe { JS_WrapValue(cx, holder_value.handle_mut()) } {
658 return false;
659 }
660
661 rooted!(&in(cx) let key_value = ObjectValue(key.get()));
662 if !unsafe { SetWeakMapEntry(cx, map.handle(), key_value.handle(), holder_value.handle()) }
663 {
664 return false;
665 }
666 }
667
668 true
669}
670
671pub(crate) fn is_cross_origin_object<D: DomTypes>(cx: &mut JSContext, obj: HandleObject) -> bool {
677 unsafe { IsWindowProxy(*obj) || native_from_object::<D::Location>(cx, *obj).is_ok() }
678}
679
680pub fn report_cross_origin_denial<D: DomTypes>(
687 cx: &mut CurrentRealm,
688 id: HandleId,
689 access: &str,
690) -> bool {
691 if let Some(id) = id_to_source(cx, id) {
692 debug!(
693 "permission denied to {} property {} on cross-origin object",
694 access,
695 &*id.str(),
696 );
697 } else {
698 debug!("permission denied to {} on cross-origin object", access);
699 }
700 unsafe {
701 if !JS_IsExceptionPending(cx) {
702 let global = D::GlobalScope::from_current_realm(cx);
703 <D as DomHelpers<D>>::throw_dom_exception(cx, &global, Error::Security(None));
705 }
706 }
707 false
708}
709
710pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx<D: DomTypes>(
714 cx: *mut RawJSContext,
715 proxy: RawHandleObject,
716 id: RawHandleId,
717 v: RawHandleValue,
718 receiver: RawHandleValue,
719 result: *mut ObjectOpResult,
720) -> bool {
721 unsafe {
722 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
724 let mut realm = CurrentRealm::assert(&mut cx);
725 let proxy = HandleObject::from_raw(proxy);
726 let id = Handle::from_raw(id);
727 let v = Handle::from_raw(v);
728 let receiver = Handle::from_raw(receiver);
729
730 if !is_platform_object_same_origin(&realm, proxy) {
731 return cross_origin_set::<D>(&mut realm, proxy, id, v.into_handle(), receiver, result);
732 }
733
734 let mut realm = AutoRealm::new_from_handle(&mut realm, proxy);
736
737 rooted!(&in(&mut realm) let mut own_desc = PropertyDescriptor::default());
740 let mut is_none = false;
741 if !JS_GetOwnPropertyDescriptorById(
742 &mut realm,
743 proxy,
744 id,
745 own_desc.handle_mut(),
746 &mut is_none,
747 ) {
748 return false;
749 }
750
751 SetPropertyIgnoringNamedGetter(
752 &mut realm,
753 proxy,
754 id,
755 v,
756 receiver,
757 if is_none {
758 None
759 } else {
760 Some(own_desc.handle())
761 },
762 result,
763 )
764 }
765}
766
767pub fn maybe_cross_origin_get_prototype<D: DomTypes>(
772 cx: &mut CurrentRealm,
773 proxy: HandleObject,
774 get_proto_object: fn(cx: &mut JSContext, global: HandleObject, rval: MutableHandleObject),
775 mut proto: MutableHandleObject,
776) -> bool {
777 if is_platform_object_same_origin(cx, proxy) {
779 let mut realm = AutoRealm::new_from_handle(cx, proxy);
780 let mut realm = realm.current_realm();
781 let global = D::GlobalScope::from_current_realm(&mut realm);
782 get_proto_object(
783 &mut realm,
784 global.reflector().get_jsobject(),
785 proto.reborrow(),
786 );
787 return !proto.is_null();
788 }
789
790 proto.set(ptr::null_mut());
792 true
793}
794
795pub fn cross_origin_get<D: DomTypes>(
802 cx: &mut CurrentRealm,
803 proxy: HandleObject,
804 receiver: HandleValue,
805 id: HandleId,
806 mut vp: MutableHandleValue,
807) -> bool {
808 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
810 let mut is_none = false;
811 if !unsafe {
812 JS_GetOwnPropertyDescriptorById(cx, proxy, id, descriptor.handle_mut(), &mut is_none)
813 } {
814 return false;
815 }
816
817 assert!(
819 !is_none,
820 "Callees should throw in all cases when they are not finding \
821 a property decriptor"
822 );
823
824 if is_data_descriptor(&descriptor) {
826 vp.set(descriptor.value_);
827 return true;
828 }
829
830 assert!(is_accessor_descriptor(&descriptor));
832
833 rooted!(&in(cx) let mut getter = ptr::null_mut::<JSObject>());
838 get_getter_object(&descriptor, getter.handle_mut().into());
839 if getter.get().is_null() {
840 return report_cross_origin_denial::<D>(cx, id, "get");
841 }
842
843 rooted!(&in(cx) let mut getter_jsval = UndefinedValue());
844 getter.get().to_jsval(cx, getter_jsval.handle_mut());
845
846 unsafe {
848 Call(
849 cx,
850 receiver,
851 getter_jsval.handle(),
852 &HandleValueArray::empty(),
853 vp,
854 )
855 }
856}
857
858pub unsafe fn cross_origin_set<D: DomTypes>(
868 cx: &mut CurrentRealm,
869 proxy: HandleObject,
870 id: HandleId,
871 v: RawHandleValue,
872 receiver: HandleValue,
873 result: *mut ObjectOpResult,
874) -> bool {
875 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
877 let mut is_none = false;
878 if !unsafe {
879 JS_GetOwnPropertyDescriptorById(cx, proxy, id, descriptor.handle_mut(), &mut is_none)
880 } {
881 return false;
882 }
883
884 assert!(
886 !is_none,
887 "Callees should throw in all cases when they are not finding \
888 a property decriptor"
889 );
890
891 rooted!(&in(cx) let mut setter = ptr::null_mut::<JSObject>());
894 get_setter_object(&descriptor, setter.handle_mut().into());
895 if setter.get().is_null() {
896 return report_cross_origin_denial::<D>(cx, id, "set");
898 }
899
900 rooted!(&in(cx) let mut setter_jsval = UndefinedValue());
901 setter.get().to_jsval(cx, setter_jsval.handle_mut());
902
903 rooted!(&in(cx) let mut ignored = UndefinedValue());
907 if !unsafe {
908 Call(
909 cx,
910 receiver,
911 setter_jsval.handle(),
912 &HandleValueArray {
915 length_: 1,
916 elements_: v.ptr,
917 },
918 ignored.handle_mut(),
919 )
920 } {
921 return false;
922 }
923
924 unsafe {
925 (*result).code_ = 0 };
927 true
928}
929
930pub fn cross_origin_property_fallback<D: DomTypes>(
937 cx: &mut CurrentRealm,
938 _proxy: HandleObject,
939 id: HandleId,
940 desc: MutableHandle<PropertyDescriptor>,
941 is_none: &mut bool,
942) -> bool {
943 assert!(*is_none, "why are we being called?");
944
945 if is_cross_origin_allowlisted_prop(cx, id) {
950 set_property_descriptor(
951 desc,
952 HandleValue::undefined(),
953 JSPROP_READONLY as u32,
954 is_none,
955 );
956 return true;
957 }
958
959 report_cross_origin_denial::<D>(cx, id, "access")
961}
962
963#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
965pub(crate) struct JSProxyHandlerOwnPropertyKeysConfig<T: DomObject> {
966 pub(crate) indexed_getter_and_length: Option<fn(&T, &mut JSContext) -> u32>,
967 pub(crate) cross_origin: Option<&'static CrossOriginProperties>,
968 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
969 pub(crate) supported_named_properties: Option<fn(*const T, &mut JSContext) -> Vec<DOMString>>,
970}
971
972enum Realm<'a> {
974 AutoRealm(AutoRealm<'a>),
975 CurrentRealm(&'a mut CurrentRealm<'a>),
976}
977
978impl<'cx> Deref for Realm<'cx> {
979 type Target = JSContext;
980
981 fn deref(&'_ self) -> &'_ Self::Target {
982 match self {
983 Realm::AutoRealm(auto_realm) => auto_realm,
984 Realm::CurrentRealm(current_realm) => current_realm,
985 }
986 }
987}
988
989impl<'cx> DerefMut for Realm<'cx> {
990 fn deref_mut(&'_ mut self) -> &'_ mut Self::Target {
991 match self {
992 Realm::AutoRealm(auto_realm) => auto_realm,
993 Realm::CurrentRealm(current_realm) => current_realm,
994 }
995 }
996}
997
998#[expect(non_snake_case)]
999pub(crate) unsafe fn JSProxyHandlerOwnPropertyKeys<T>(
1001 config: JSProxyHandlerOwnPropertyKeysConfig<T>,
1002 cx: *mut RawJSContext,
1003 proxy: RawHandleObject,
1004 props: RawMutableHandleIdVector,
1005) -> bool
1006where
1007 T: DomObject,
1008{
1009 unsafe {
1010 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
1012 let mut cx = CurrentRealm::assert(&mut cx);
1013 let current_realm = &mut cx;
1014 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
1015
1016 let proxy = Handle::from_raw(proxy);
1017
1018 let mut cx = if let Some(cross_origin_properties) = config.cross_origin {
1019 if !is_platform_object_same_origin(current_realm, proxy) {
1020 return cross_origin_own_property_keys(
1021 current_realm,
1022 proxy,
1023 cross_origin_properties,
1024 props,
1025 );
1026 }
1027
1028 let cx = AutoRealm::new_from_handle(current_realm, proxy);
1030 Realm::AutoRealm(cx)
1031 } else {
1032 Realm::CurrentRealm(current_realm)
1033 };
1034
1035 if let Some(length_fn) = config.indexed_getter_and_length {
1036 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
1037 rooted!(&in(cx) let mut rooted_jsid: jsid);
1038 for i in 0..length {
1039 int_to_jsid(i as i32, rooted_jsid.handle_mut());
1040 AppendToIdVector(props, rooted_jsid.handle());
1041 }
1042 }
1043
1044 if let Some(properties) = config.supported_named_properties {
1045 for name in properties(unwrapped_proxy, &mut cx) {
1046 let cstring = CString::new(name).unwrap();
1047 let jsstring = JS_AtomizeAndPinString(&cx, cstring.as_ptr());
1048 rooted!(&in(cx) let rooted = jsstring);
1049 rooted!(&in(cx) let mut rooted_jsid: jsid);
1050 RUST_INTERNED_STRING_TO_JSID(
1051 &mut cx,
1052 rooted.handle().get(),
1053 rooted_jsid.handle_mut(),
1054 );
1055 AppendToIdVector(props, rooted_jsid.handle());
1056 }
1057 }
1058
1059 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
1060 get_expando_object(proxy, expando.handle_mut());
1061
1062 if !expando.is_null() &&
1063 !GetPropertyKeys(
1064 &mut cx,
1065 expando.handle(),
1066 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1067 props,
1068 )
1069 {
1070 return false;
1071 }
1072 }
1073 true
1074}
1075
1076#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
1078pub(crate) struct JSProxyHandlerOwnEnumerablePropertyKeysConfig<T: DomObject> {
1079 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
1080 #[expect(clippy::type_complexity)]
1081 pub(crate) indexed_getter_and_length: Option<Box<dyn Fn(&T, &mut JSContext) -> u32>>,
1082 pub(crate) cross_origin: bool,
1083}
1084
1085#[expect(non_snake_case)]
1086pub(crate) fn JSProxyHandlerGetOwnEnumerablePropertyKeys<T>(
1087 config: JSProxyHandlerOwnEnumerablePropertyKeysConfig<T>,
1088 cx: *mut RawJSContext,
1089 proxy: RawHandleObject,
1090 props: RawMutableHandleIdVector,
1091) -> bool
1092where
1093 T: DomObject,
1094{
1095 unsafe {
1096 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
1098 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
1099 let mut cx = CurrentRealm::assert(&mut cx);
1100 let current_realm = &mut cx;
1101
1102 let proxy = Handle::from_raw(proxy);
1103
1104 let mut cx = if config.cross_origin {
1105 if !is_platform_object_same_origin(current_realm, proxy) {
1106 return true;
1108 }
1109
1110 let cx = AutoRealm::new_from_handle(current_realm, proxy);
1112 Realm::AutoRealm(cx)
1113 } else {
1114 Realm::CurrentRealm(current_realm)
1115 };
1116 if let Some(length_fn) = config.indexed_getter_and_length {
1117 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
1118 rooted!(&in(cx) let mut rooted_jsid: jsid);
1119 for i in 0..length {
1120 int_to_jsid(i as i32, rooted_jsid.handle_mut());
1121 AppendToIdVector(props, rooted_jsid.handle());
1122 }
1123 }
1124
1125 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
1126 get_expando_object(proxy, expando.handle_mut());
1127 if !expando.is_null() &&
1128 !GetPropertyKeys(
1129 &mut cx,
1130 expando.handle(),
1131 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1132 props,
1133 )
1134 {
1135 return false;
1136 }
1137 }
1138
1139 true
1140}
1141
1142pub fn is_platform_object_same_origin(realm: &CurrentRealm, obj: HandleObject) -> bool {
1144 let subject_realm = realm.realm().as_ptr();
1145 let object_realm = unsafe { GetObjectRealmOrNull(*obj) };
1146 assert!(!object_realm.is_null());
1147
1148 if subject_realm == object_realm {
1149 return true;
1150 }
1151
1152 let subject_principals =
1153 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(subject_realm)) };
1154 let object_principals =
1155 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(object_realm)) };
1156
1157 let subject_origin = subject_principals.origin();
1158 let object_origin = object_principals.origin();
1159
1160 let result = subject_origin.same_origin_domain(&object_origin);
1161 log::trace!(
1162 "object {:p} (realm = {:p}, principalls = {:p}, origin = {:?}) is {} \
1163 with reference to the current Realm (realm = {:p}, principals = {:p}, \
1164 origin = {:?})",
1165 obj.get(),
1166 object_realm,
1167 object_principals.as_raw(),
1168 object_origin.immutable(),
1169 ["NOT same domain-origin", "same domain-origin"][result as usize],
1170 subject_realm,
1171 subject_principals.as_raw(),
1172 subject_origin.immutable()
1173 );
1174
1175 result
1176}