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, GetRealmKeyObject, GetWeakMapEntry, GetWellKnownSymbol,
34 JS_AlreadyHasOwnPropertyById, JS_AtomizeAndPinString, JS_DefineFunctions, JS_DefineProperties,
35 JS_DefinePropertyById, JS_DeletePropertyById, JS_ValueToSource, JS_WrapObject, JS_WrapValue,
36 NewWeakMapObject, RUST_INTERNED_STRING_TO_JSID, RUST_JSID_IS_VOID, SetDataPropertyDescriptor,
37 SetPropertyIgnoringNamedGetter, SetWeakMapEntry, 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::js::{
49 GetPropertyKeys, JS_GetOwnPropertyDescriptorById, JS_IdToValue, JS_IsExceptionPending,
50 JS_NewObjectWithGivenProto,
51};
52use crate::principals::ServoJSPrincipalsRef;
53use crate::reflector::DomObject;
54use crate::str::DOMString;
55
56pub const CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT: u32 = 1;
61
62pub(crate) unsafe extern "C" fn shadow_check_callback(
67 cx: *mut RawJSContext,
68 object: RawHandleObject,
69 id: RawHandleId,
70) -> DOMProxyShadowsResult {
71 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
75 let cx = &mut cx;
76
77 let object = unsafe { HandleObject::from_raw(object) };
78 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
79 get_expando_object(object, expando.handle_mut());
80 if !expando.get().is_null() {
81 let mut has_own = false;
82 let raw_id = unsafe { Handle::from_raw(id) };
83
84 if !unsafe { JS_AlreadyHasOwnPropertyById(cx, expando.handle(), raw_id, &mut has_own) } {
85 return DOMProxyShadowsResult::ShadowCheckFailed;
86 }
87
88 if has_own {
89 return DOMProxyShadowsResult::ShadowsViaDirectExpando;
90 }
91 }
92
93 DOMProxyShadowsResult::DoesntShadow
95}
96
97pub fn init() {
99 unsafe {
100 SetDOMProxyInformation(
101 GetProxyHandlerFamily(),
102 Some(shadow_check_callback),
103 ptr::null(),
104 );
105 }
106}
107
108pub(crate) unsafe extern "C" fn define_property(
114 cx: *mut RawJSContext,
115 proxy: RawHandleObject,
116 id: RawHandleId,
117 desc: RawHandle<PropertyDescriptor>,
118 result: *mut ObjectOpResult,
119) -> bool {
120 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
122 let cx = &mut cx;
123
124 let proxy = unsafe { Handle::from_raw(proxy) };
125 let id = unsafe { Handle::from_raw(id) };
126 let desc = unsafe { Handle::from_raw(desc) };
127
128 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
129 ensure_expando_object(cx, proxy, expando.handle_mut());
130
131 unsafe { JS_DefinePropertyById(cx, expando.handle(), id, desc, result) }
132}
133
134pub(crate) unsafe extern "C" fn delete(
140 cx: *mut RawJSContext,
141 proxy: RawHandleObject,
142 id: RawHandleId,
143 bp: *mut ObjectOpResult,
144) -> bool {
145 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
147 let cx = &mut cx;
148
149 let proxy = unsafe { Handle::from_raw(proxy) };
150 let id = unsafe { Handle::from_raw(id) };
151
152 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
153 get_expando_object(proxy, expando.handle_mut());
154
155 if expando.is_null() {
156 unsafe {
157 (*bp).code_ = 0 };
159 return true;
160 }
161
162 unsafe { JS_DeletePropertyById(cx, expando.handle(), id, bp) }
163}
164
165pub unsafe extern "C" fn prevent_extensions(
173 _cx: *mut RawJSContext,
174 _proxy: RawHandleObject,
175 result: *mut ObjectOpResult,
176) -> bool {
177 unsafe { (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t };
178 true
179}
180
181pub unsafe extern "C" fn is_extensible(
189 _cx: *mut RawJSContext,
190 _proxy: RawHandleObject,
191 succeeded: *mut bool,
192) -> bool {
193 unsafe { *succeeded = true };
194 true
195}
196
197pub(crate) unsafe extern "C" fn get_prototype_if_ordinary(
210 _: *mut RawJSContext,
211 proxy: RawHandleObject,
212 is_ordinary: *mut bool,
213 proto: RawMutableHandleObject,
214) -> bool {
215 unsafe { *is_ordinary = true };
216 proto.set(unsafe { GetStaticPrototype(proxy.get()) });
217 true
218}
219
220pub(crate) fn get_expando_object(obj: HandleObject, mut expando: MutableHandleObject) {
222 unsafe {
223 assert!(is_dom_proxy(obj.get()));
224 let val = &mut UndefinedValue();
225 GetProxyPrivate(obj.get(), val);
226 expando.set(if val.is_undefined() {
227 ptr::null_mut()
228 } else {
229 val.to_object()
230 });
231 }
232}
233
234pub(crate) fn ensure_expando_object(
237 cx: &mut JSContext,
238 obj: HandleObject,
239 mut expando: MutableHandleObject,
240) {
241 unsafe {
242 assert!(is_dom_proxy(obj.get()));
243 get_expando_object(obj, expando.reborrow());
244 if expando.is_null() {
245 expando.set(JS_NewObjectWithGivenProto(cx, None, HandleObject::null()));
246 assert!(!expando.is_null());
247
248 SetProxyPrivate(obj.get(), &ObjectValue(expando.get()));
249 }
250 }
251}
252
253pub fn set_property_descriptor(
256 desc: MutableHandle<PropertyDescriptor>,
257 value: HandleValue,
258 attrs: u32,
259 is_none: &mut bool,
260) {
261 unsafe { SetDataPropertyDescriptor(desc, value, attrs) };
262 *is_none = false;
263}
264
265fn id_to_source(cx: &mut JSContext, id: HandleId) -> Option<DOMString> {
266 unsafe {
267 if RUST_JSID_IS_VOID(id) {
268 return None;
269 }
270 rooted!(&in(cx) let mut value = UndefinedValue());
271 rooted!(&in(cx) let mut jsstr = ptr::null_mut::<JSString>());
272 JS_IdToValue(cx, id.get(), value.handle_mut())
273 .then(|| {
274 jsstr.set(JS_ValueToSource(cx, value.handle()));
275 jsstr.get()
276 })
277 .and_then(NonNull::new)
278 .map(|jsstr| jsstr_to_string(cx, jsstr).into())
279 }
280}
281
282pub struct CrossOriginProperties {
287 pub(crate) attributes: &'static [JSPropertySpec],
288 pub(crate) methods: &'static [JSFunctionSpec],
289}
290
291impl CrossOriginProperties {
292 fn keys(&self) -> impl Iterator<Item = *const c_char> + '_ {
294 self.attributes
296 .iter()
297 .map(|spec| unsafe { spec.name.string_ })
298 .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ }))
299 .filter(|ptr| !ptr.is_null())
300 }
301}
302
303pub fn cross_origin_own_property_keys(
307 cx: &mut JSContext,
308 _proxy: HandleObject,
309 cross_origin_properties: &'static CrossOriginProperties,
310 props: RawMutableHandleIdVector,
311) -> bool {
312 for key in cross_origin_properties.keys() {
315 unsafe {
316 rooted!(&in(cx) let rooted = JS_AtomizeAndPinString(cx, key));
317 rooted!(&in(cx) let mut rooted_jsid: jsid);
318 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), rooted_jsid.handle_mut());
319 AppendToIdVector(props, rooted_jsid.handle());
320 }
321 }
322
323 append_cross_origin_allowlisted_prop_keys(cx, props);
326
327 true
328}
329
330pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
333 _: *mut RawJSContext,
334 _proxy: RawHandleObject,
335 is_ordinary: *mut bool,
336 _proto: RawMutableHandleObject,
337) -> bool {
338 unsafe { *is_ordinary = false };
340 true
341}
342
343pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
351 cx: *mut RawJSContext,
352 proxy: RawHandleObject,
353 proto: RawHandleObject,
354 result: *mut ObjectOpResult,
355) -> bool {
356 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
358 let cx = &mut cx;
359 rooted!(&in(cx) let mut current = ptr::null_mut::<JSObject>());
367 if !unsafe { GetObjectProto(cx, Handle::from_raw(proxy), current.handle_mut()) } {
368 return false;
369 }
370
371 if proto.get() == current.get() {
373 unsafe {
374 (*result).code_ = 0 };
376 return true;
377 }
378
379 unsafe { (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize };
381 true
382}
383
384fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
385 if d.hasGetter_() {
386 out.set(d.getter_);
387 }
388}
389
390fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
391 if d.hasSetter_() {
392 out.set(d.setter_);
393 }
394}
395
396pub(crate) fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
398 d.hasSetter_() || d.hasGetter_()
399}
400
401fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
403 d.hasWritable_() || d.hasValue_()
404}
405
406pub(crate) fn cross_origin_has_own<D: DomTypes>(
411 cx: &mut CurrentRealm,
412 proxy: HandleObject,
413 cross_origin_properties: &'static CrossOriginProperties,
414 id: HandleId,
415 exists: &mut bool,
416) -> bool {
417 rooted!(&in(cx) let mut property_descriptor = PropertyDescriptor::default());
418 let mut is_none = false;
419 if !cross_origin_get_own_property_helper(
420 cx,
421 proxy,
422 cross_origin_properties,
423 id,
424 property_descriptor.handle_mut(),
425 &mut is_none,
426 ) {
427 return false;
428 }
429
430 if is_none &&
431 !cross_origin_property_fallback::<D>(
432 cx,
433 proxy,
434 id,
435 property_descriptor.handle_mut(),
436 &mut is_none,
437 )
438 {
439 return false;
440 }
441
442 *exists = !is_none;
443 true
444}
445
446pub fn cross_origin_get_own_property_helper(
453 cx: &mut CurrentRealm,
454 proxy: HandleObject,
455 cross_origin_properties: &'static CrossOriginProperties,
456 id: HandleId,
457 desc: MutableHandle<PropertyDescriptor>,
458 is_none: &mut bool,
459) -> bool {
460 rooted!(&in(cx) let mut holder = ptr::null_mut::<JSObject>());
461 if !ensure_cross_origin_property_holder(cx, proxy, cross_origin_properties, holder.handle_mut())
462 {
463 return false;
464 }
465 JS_GetOwnPropertyDescriptorById(cx, holder.handle(), id, desc, is_none)
466}
467
468const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[
469 SymbolCode::toStringTag,
470 SymbolCode::hasInstance,
471 SymbolCode::isConcatSpreadable,
472];
473
474fn is_cross_origin_allowlisted_prop(cx: &mut JSContext, id: HandleId) -> bool {
475 unsafe {
476 if jsid_to_string(cx, id).is_some_and(|st| st == "then") {
477 return true;
478 }
479
480 rooted!(&in(cx) let mut allowed_id: jsid);
481 ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| {
482 allowed_id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
483 allowed_id.get().asBits_ == id.asBits_
486 })
487 }
488}
489
490fn append_cross_origin_allowlisted_prop_keys(cx: &mut JSContext, props: RawMutableHandleIdVector) {
495 unsafe {
496 rooted!(&in(cx) let mut id: jsid);
497
498 let jsstring = JS_AtomizeAndPinString(cx, c"then".as_ptr());
499 rooted!(&in(cx) let rooted = jsstring);
500 RUST_INTERNED_STRING_TO_JSID(cx, rooted.handle().get(), id.handle_mut());
501 AppendToIdVector(props, id.handle());
502
503 for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() {
504 id.set(SymbolId(GetWellKnownSymbol(cx, allowed_code)));
505 AppendToIdVector(props, id.handle());
506 }
507 }
508}
509
510fn ensure_cross_origin_property_holder(
523 cx: &mut CurrentRealm,
524 proxy: HandleObject,
525 cross_origin_properties: &'static CrossOriginProperties,
526 mut out_holder: MutableHandleObject,
527) -> bool {
528 rooted!(&in(cx) let mut weak_value_map = UndefinedValue());
529 unsafe {
530 GetProxyReservedSlot(
531 proxy.get(),
532 CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT,
533 weak_value_map.as_ptr(),
534 )
535 };
536 if weak_value_map.is_undefined() {
537 let mut realm = AutoRealm::new_from_handle(cx, proxy);
538 let new_map = unsafe { NewWeakMapObject(&mut realm) };
539 if new_map.is_null() {
540 return false;
541 }
542 weak_value_map.set(ObjectValue(new_map));
543 unsafe {
544 SetProxyReservedSlot(
545 proxy.get(),
546 CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT,
547 weak_value_map.as_ptr(),
548 )
549 };
550 }
551
552 rooted!(&in(cx) let map = weak_value_map.to_object());
553 debug_assert!(unsafe { IsWeakMapObject(map.get()) });
554
555 rooted!(&in(cx) let mut key = unsafe { GetRealmKeyObject(cx) });
582 if key.is_null() {
583 return false;
584 }
585
586 rooted!(&in(cx) let mut holder_value = UndefinedValue());
587 {
588 let mut realm = AutoRealm::new_from_handle(cx, map.handle());
589 let cx = &mut realm;
590
591 if !unsafe { JS_WrapObject(cx, key.handle_mut()) } {
592 return false;
593 }
594
595 rooted!(&in(cx) let key_value = ObjectValue(key.get()));
596 if !unsafe {
597 GetWeakMapEntry(
598 cx,
599 map.handle(),
600 key_value.handle(),
601 holder_value.handle_mut(),
602 )
603 } {
604 return false;
605 }
606 }
607
608 if holder_value.is_object() {
609 out_holder.set(unsafe { UncheckedUnwrapObject(holder_value.to_object(), true) });
612
613 if !unsafe { JS_IsDeadWrapper(out_holder.get()) } {
615 return true;
616 }
617 }
618
619 out_holder.set(JS_NewObjectWithGivenProto(cx, None, HandleObject::null()));
627
628 if out_holder.get().is_null() ||
629 !unsafe {
630 JS_DefineProperties(
631 cx,
632 out_holder.handle(),
633 cross_origin_properties.attributes.as_ptr(),
634 )
635 } ||
636 !unsafe {
637 JS_DefineFunctions(
638 cx,
639 out_holder.handle(),
640 cross_origin_properties.methods.as_ptr(),
641 )
642 }
643 {
644 return false;
645 }
646
647 holder_value.set(ObjectValue(out_holder.get()));
648
649 {
650 let mut realm = AutoRealm::new_from_handle(cx, map.handle());
651 let cx = &mut realm;
652
653 if !unsafe { JS_WrapValue(cx, holder_value.handle_mut()) } {
655 return false;
656 }
657
658 rooted!(&in(cx) let key_value = ObjectValue(key.get()));
659 if !unsafe { SetWeakMapEntry(cx, map.handle(), key_value.handle(), holder_value.handle()) }
660 {
661 return false;
662 }
663 }
664
665 true
666}
667
668pub(crate) fn is_cross_origin_object<D: DomTypes>(cx: &mut JSContext, obj: HandleObject) -> bool {
674 unsafe { IsWindowProxy(*obj) || native_from_object::<D::Location>(cx, *obj).is_ok() }
675}
676
677pub fn report_cross_origin_denial<D: DomTypes>(
684 cx: &mut CurrentRealm,
685 id: HandleId,
686 access: &str,
687) -> bool {
688 if let Some(id) = id_to_source(cx, id) {
689 debug!(
690 "permission denied to {} property {} on cross-origin object",
691 access,
692 &*id.str(),
693 );
694 } else {
695 debug!("permission denied to {} on cross-origin object", access);
696 }
697 if !JS_IsExceptionPending(cx) {
698 let global = D::GlobalScope::from_current_realm(cx);
699 <D as DomHelpers<D>>::throw_dom_exception(cx, &global, Error::Security(None));
701 }
702 false
703}
704
705pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx<D: DomTypes>(
709 cx: *mut RawJSContext,
710 proxy: RawHandleObject,
711 id: RawHandleId,
712 v: RawHandleValue,
713 receiver: RawHandleValue,
714 result: *mut ObjectOpResult,
715) -> bool {
716 unsafe {
717 let mut cx = JSContext::from_ptr(NonNull::new(cx).unwrap());
719 let mut realm = CurrentRealm::assert(&mut cx);
720 let proxy = HandleObject::from_raw(proxy);
721 let id = Handle::from_raw(id);
722 let v = Handle::from_raw(v);
723 let receiver = Handle::from_raw(receiver);
724
725 if !is_platform_object_same_origin(&realm, proxy) {
726 return cross_origin_set::<D>(&mut realm, proxy, id, v.into_handle(), receiver, result);
727 }
728
729 let mut realm = AutoRealm::new_from_handle(&mut realm, proxy);
731
732 rooted!(&in(&mut realm) let mut own_desc = PropertyDescriptor::default());
735 let mut is_none = false;
736 if !JS_GetOwnPropertyDescriptorById(
737 &mut realm,
738 proxy,
739 id,
740 own_desc.handle_mut(),
741 &mut is_none,
742 ) {
743 return false;
744 }
745
746 SetPropertyIgnoringNamedGetter(
747 &mut realm,
748 proxy,
749 id,
750 v,
751 receiver,
752 if is_none {
753 None
754 } else {
755 Some(own_desc.handle())
756 },
757 result,
758 )
759 }
760}
761
762pub fn maybe_cross_origin_get_prototype<D: DomTypes>(
767 cx: &mut CurrentRealm,
768 proxy: HandleObject,
769 get_proto_object: fn(cx: &mut JSContext, global: HandleObject, rval: MutableHandleObject),
770 mut proto: MutableHandleObject,
771) -> bool {
772 if is_platform_object_same_origin(cx, proxy) {
774 let mut realm = AutoRealm::new_from_handle(cx, proxy);
775 let mut realm = realm.current_realm();
776 let global = D::GlobalScope::from_current_realm(&mut realm);
777 get_proto_object(
778 &mut realm,
779 global.reflector().get_jsobject(),
780 proto.reborrow(),
781 );
782 return !proto.is_null();
783 }
784
785 proto.set(ptr::null_mut());
787 true
788}
789
790pub fn cross_origin_get<D: DomTypes>(
797 cx: &mut CurrentRealm,
798 proxy: HandleObject,
799 receiver: HandleValue,
800 id: HandleId,
801 mut vp: MutableHandleValue,
802) -> bool {
803 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
805 let mut is_none = false;
806 if !JS_GetOwnPropertyDescriptorById(cx, proxy, id, descriptor.handle_mut(), &mut is_none) {
807 return false;
808 }
809
810 assert!(
812 !is_none,
813 "Callees should throw in all cases when they are not finding \
814 a property decriptor"
815 );
816
817 if is_data_descriptor(&descriptor) {
819 vp.set(descriptor.value_);
820 return true;
821 }
822
823 assert!(is_accessor_descriptor(&descriptor));
825
826 rooted!(&in(cx) let mut getter = ptr::null_mut::<JSObject>());
831 get_getter_object(&descriptor, getter.handle_mut().into());
832 if getter.get().is_null() {
833 return report_cross_origin_denial::<D>(cx, id, "get");
834 }
835
836 rooted!(&in(cx) let mut getter_jsval = UndefinedValue());
837 getter.get().to_jsval(cx, getter_jsval.handle_mut());
838
839 unsafe {
841 Call(
842 cx,
843 receiver,
844 getter_jsval.handle(),
845 &HandleValueArray::empty(),
846 vp,
847 )
848 }
849}
850
851pub unsafe fn cross_origin_set<D: DomTypes>(
861 cx: &mut CurrentRealm,
862 proxy: HandleObject,
863 id: HandleId,
864 v: RawHandleValue,
865 receiver: HandleValue,
866 result: *mut ObjectOpResult,
867) -> bool {
868 rooted!(&in(cx) let mut descriptor = PropertyDescriptor::default());
870 let mut is_none = false;
871 if !JS_GetOwnPropertyDescriptorById(cx, proxy, id, descriptor.handle_mut(), &mut is_none) {
872 return false;
873 }
874
875 assert!(
877 !is_none,
878 "Callees should throw in all cases when they are not finding \
879 a property decriptor"
880 );
881
882 rooted!(&in(cx) let mut setter = ptr::null_mut::<JSObject>());
885 get_setter_object(&descriptor, setter.handle_mut().into());
886 if setter.get().is_null() {
887 return report_cross_origin_denial::<D>(cx, id, "set");
889 }
890
891 rooted!(&in(cx) let mut setter_jsval = UndefinedValue());
892 setter.get().to_jsval(cx, setter_jsval.handle_mut());
893
894 rooted!(&in(cx) let mut ignored = UndefinedValue());
898 if !unsafe {
899 Call(
900 cx,
901 receiver,
902 setter_jsval.handle(),
903 &HandleValueArray {
906 length_: 1,
907 elements_: v.ptr,
908 },
909 ignored.handle_mut(),
910 )
911 } {
912 return false;
913 }
914
915 unsafe {
916 (*result).code_ = 0 };
918 true
919}
920
921pub fn cross_origin_property_fallback<D: DomTypes>(
928 cx: &mut CurrentRealm,
929 _proxy: HandleObject,
930 id: HandleId,
931 desc: MutableHandle<PropertyDescriptor>,
932 is_none: &mut bool,
933) -> bool {
934 assert!(*is_none, "why are we being called?");
935
936 if is_cross_origin_allowlisted_prop(cx, id) {
941 set_property_descriptor(
942 desc,
943 HandleValue::undefined(),
944 JSPROP_READONLY as u32,
945 is_none,
946 );
947 return true;
948 }
949
950 report_cross_origin_denial::<D>(cx, id, "access")
952}
953
954#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
956pub(crate) struct JSProxyHandlerOwnPropertyKeysConfig<T: DomObject> {
957 pub(crate) indexed_getter_and_length: Option<fn(&T, &mut JSContext) -> u32>,
958 pub(crate) cross_origin: Option<&'static CrossOriginProperties>,
959 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
960 pub(crate) supported_named_properties: Option<fn(*const T, &mut JSContext) -> Vec<DOMString>>,
961}
962
963enum Realm<'a> {
965 AutoRealm(AutoRealm<'a>),
966 CurrentRealm(&'a mut CurrentRealm<'a>),
967}
968
969impl<'cx> Deref for Realm<'cx> {
970 type Target = JSContext;
971
972 fn deref(&'_ self) -> &'_ Self::Target {
973 match self {
974 Realm::AutoRealm(auto_realm) => auto_realm,
975 Realm::CurrentRealm(current_realm) => current_realm,
976 }
977 }
978}
979
980impl<'cx> DerefMut for Realm<'cx> {
981 fn deref_mut(&'_ mut self) -> &'_ mut Self::Target {
982 match self {
983 Realm::AutoRealm(auto_realm) => auto_realm,
984 Realm::CurrentRealm(current_realm) => current_realm,
985 }
986 }
987}
988
989#[expect(non_snake_case)]
990pub(crate) unsafe fn JSProxyHandlerOwnPropertyKeys<T>(
992 config: JSProxyHandlerOwnPropertyKeysConfig<T>,
993 cx: *mut RawJSContext,
994 proxy: RawHandleObject,
995 props: RawMutableHandleIdVector,
996) -> bool
997where
998 T: DomObject,
999{
1000 unsafe {
1001 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
1003 let mut cx = CurrentRealm::assert(&mut cx);
1004 let current_realm = &mut cx;
1005 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
1006
1007 let proxy = Handle::from_raw(proxy);
1008
1009 let mut cx = if let Some(cross_origin_properties) = config.cross_origin {
1010 if !is_platform_object_same_origin(current_realm, proxy) {
1011 return cross_origin_own_property_keys(
1012 current_realm,
1013 proxy,
1014 cross_origin_properties,
1015 props,
1016 );
1017 }
1018
1019 let cx = AutoRealm::new_from_handle(current_realm, proxy);
1021 Realm::AutoRealm(cx)
1022 } else {
1023 Realm::CurrentRealm(current_realm)
1024 };
1025
1026 if let Some(length_fn) = config.indexed_getter_and_length {
1027 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
1028 rooted!(&in(cx) let mut rooted_jsid: jsid);
1029 for i in 0..length {
1030 int_to_jsid(i as i32, rooted_jsid.handle_mut());
1031 AppendToIdVector(props, rooted_jsid.handle());
1032 }
1033 }
1034
1035 if let Some(properties) = config.supported_named_properties {
1036 for name in properties(unwrapped_proxy, &mut cx) {
1037 let cstring = CString::new(name).unwrap();
1038 let jsstring = JS_AtomizeAndPinString(&cx, cstring.as_ptr());
1039 rooted!(&in(cx) let rooted = jsstring);
1040 rooted!(&in(cx) let mut rooted_jsid: jsid);
1041 RUST_INTERNED_STRING_TO_JSID(
1042 &mut cx,
1043 rooted.handle().get(),
1044 rooted_jsid.handle_mut(),
1045 );
1046 AppendToIdVector(props, rooted_jsid.handle());
1047 }
1048 }
1049
1050 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
1051 get_expando_object(proxy, expando.handle_mut());
1052
1053 if !expando.is_null() &&
1054 !GetPropertyKeys(
1055 &mut cx,
1056 expando.handle(),
1057 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1058 props,
1059 )
1060 {
1061 return false;
1062 }
1063 }
1064 true
1065}
1066
1067#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
1069pub(crate) struct JSProxyHandlerOwnEnumerablePropertyKeysConfig<T: DomObject> {
1070 pub(crate) unwrapped_proxy: unsafe fn(RawHandleObject) -> *const T,
1071 #[expect(clippy::type_complexity)]
1072 pub(crate) indexed_getter_and_length: Option<Box<dyn Fn(&T, &mut JSContext) -> u32>>,
1073 pub(crate) cross_origin: bool,
1074}
1075
1076#[expect(non_snake_case)]
1077pub(crate) fn JSProxyHandlerGetOwnEnumerablePropertyKeys<T>(
1078 config: JSProxyHandlerOwnEnumerablePropertyKeysConfig<T>,
1079 cx: *mut RawJSContext,
1080 proxy: RawHandleObject,
1081 props: RawMutableHandleIdVector,
1082) -> bool
1083where
1084 T: DomObject,
1085{
1086 unsafe {
1087 let mut cx = JSContext::from_ptr(ptr::NonNull::new(cx).unwrap());
1089 let unwrapped_proxy = (config.unwrapped_proxy)(proxy);
1090 let mut cx = CurrentRealm::assert(&mut cx);
1091 let current_realm = &mut cx;
1092
1093 let proxy = Handle::from_raw(proxy);
1094
1095 let mut cx = if config.cross_origin {
1096 if !is_platform_object_same_origin(current_realm, proxy) {
1097 return true;
1099 }
1100
1101 let cx = AutoRealm::new_from_handle(current_realm, proxy);
1103 Realm::AutoRealm(cx)
1104 } else {
1105 Realm::CurrentRealm(current_realm)
1106 };
1107 if let Some(length_fn) = config.indexed_getter_and_length {
1108 let length = (length_fn)(&*unwrapped_proxy, &mut cx);
1109 rooted!(&in(cx) let mut rooted_jsid: jsid);
1110 for i in 0..length {
1111 int_to_jsid(i as i32, rooted_jsid.handle_mut());
1112 AppendToIdVector(props, rooted_jsid.handle());
1113 }
1114 }
1115
1116 rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
1117 get_expando_object(proxy, expando.handle_mut());
1118 if !expando.is_null() &&
1119 !GetPropertyKeys(
1120 &mut cx,
1121 expando.handle(),
1122 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1123 props,
1124 )
1125 {
1126 return false;
1127 }
1128 }
1129
1130 true
1131}
1132
1133pub fn is_platform_object_same_origin(realm: &CurrentRealm, obj: HandleObject) -> bool {
1135 let subject_realm = realm.realm().as_ptr();
1136 let object_realm = unsafe { GetObjectRealmOrNull(*obj) };
1137 assert!(!object_realm.is_null());
1138
1139 if subject_realm == object_realm {
1140 return true;
1141 }
1142
1143 let subject_principals =
1144 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(subject_realm)) };
1145 let object_principals =
1146 unsafe { ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(object_realm)) };
1147
1148 let subject_origin = subject_principals.origin();
1149 let object_origin = object_principals.origin();
1150
1151 let result = subject_origin.same_origin_domain(&object_origin);
1152 log::trace!(
1153 "object {:p} (realm = {:p}, principalls = {:p}, origin = {:?}) is {} \
1154 with reference to the current Realm (realm = {:p}, principals = {:p}, \
1155 origin = {:?})",
1156 obj.get(),
1157 object_realm,
1158 object_principals.as_raw(),
1159 object_origin.immutable(),
1160 ["NOT same domain-origin", "same domain-origin"][result as usize],
1161 subject_realm,
1162 subject_principals.as_raw(),
1163 subject_origin.immutable()
1164 );
1165
1166 result
1167}