diff options
Diffstat (limited to 'components/script/dom/bindings/proxyhandler.rs')
-rw-r--r-- | components/script/dom/bindings/proxyhandler.rs | 605 |
1 files changed, 600 insertions, 5 deletions
diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs index 11913c03642..3650df6186b 100644 --- a/components/script/dom/bindings/proxyhandler.rs +++ b/components/script/dom/bindings/proxyhandler.rs @@ -6,25 +6,47 @@ #![deny(missing_docs)] -use crate::dom::bindings::conversions::is_dom_proxy; +use crate::dom::bindings::conversions::{is_dom_proxy, jsid_to_string, jsstring_to_str}; +use crate::dom::bindings::error::{throw_dom_exception, Error}; +use crate::dom::bindings::principals::ServoJSPrincipalsRef; +use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::str::DOMString; use crate::dom::bindings::utils::delete_property_by_id; -use js::glue::GetProxyHandlerFamily; +use crate::dom::globalscope::GlobalScope; +use crate::realms::{AlreadyInRealm, InRealm}; +use crate::script_runtime::JSContext as SafeJSContext; +use js::conversions::ToJSValConvertible; +use js::glue::{ + GetProxyHandler, GetProxyHandlerFamily, InvokeGetOwnPropertyDescriptor, RUST_SYMBOL_TO_JSID, +}; use js::glue::{GetProxyPrivate, SetProxyPrivate}; +use js::jsapi; use js::jsapi::GetStaticPrototype; use js::jsapi::Handle as RawHandle; use js::jsapi::HandleId as RawHandleId; use js::jsapi::HandleObject as RawHandleObject; +use js::jsapi::HandleValue as RawHandleValue; +use js::jsapi::JSAutoRealm; +use js::jsapi::JS_AtomizeAndPinString; use js::jsapi::JS_DefinePropertyById; +use js::jsapi::JS_GetOwnPropertyDescriptorById; +use js::jsapi::JS_IsExceptionPending; +use js::jsapi::MutableHandle as RawMutableHandle; +use js::jsapi::MutableHandleIdVector as RawMutableHandleIdVector; use js::jsapi::MutableHandleObject as RawMutableHandleObject; +use js::jsapi::MutableHandleValue as RawMutableHandleValue; use js::jsapi::ObjectOpResult; +use js::jsapi::{jsid, GetObjectRealmOrNull, GetRealmPrincipals, JSFunctionSpec, JSPropertySpec}; use js::jsapi::{DOMProxyShadowsResult, JSContext, JSObject, PropertyDescriptor}; +use js::jsapi::{GetWellKnownSymbol, SymbolCode}; use js::jsapi::{JSErrNum, SetDOMProxyInformation}; use js::jsval::ObjectValue; use js::jsval::UndefinedValue; use js::rust::wrappers::JS_AlreadyHasOwnPropertyById; use js::rust::wrappers::JS_NewObjectWithGivenProto; -use js::rust::{Handle, HandleObject, MutableHandle, MutableHandleObject}; -use std::ptr; +use js::rust::wrappers::{AppendToIdVector, RUST_INTERNED_STRING_TO_JSID}; +use js::rust::{get_context_realm, Handle, HandleObject, MutableHandle, MutableHandleObject}; +use std::{ffi::CStr, os::raw::c_char, ptr}; /// Determine if this id shadows any existing properties for this proxy. pub unsafe extern "C" fn shadow_check_callback( @@ -120,7 +142,7 @@ pub unsafe extern "C" fn is_extensible( /// /// This implementation always handles the case of the ordinary /// `[[GetPrototypeOf]]` behavior. An alternative implementation will be -/// necessary for the Location object. +/// necessary for maybe-cross-origin objects. pub unsafe extern "C" fn get_prototype_if_ordinary( _: *mut JSContext, proxy: RawHandleObject, @@ -177,3 +199,576 @@ pub fn fill_property_descriptor( desc.getter = None; desc.setter = None; } + +/// <https://html.spec.whatwg.org/multipage/#isplatformobjectsameorigin-(-o-)> +pub unsafe fn is_platform_object_same_origin(cx: SafeJSContext, obj: RawHandleObject) -> bool { + let subject_realm = get_context_realm(*cx); + let obj_realm = GetObjectRealmOrNull(*obj); + assert!(!obj_realm.is_null()); + + let subject_principals = + ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(subject_realm)); + let obj_principals = ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(obj_realm)); + + let subject_origin = subject_principals.origin(); + let obj_origin = obj_principals.origin(); + + let result = subject_origin.same_origin_domain(&obj_origin); + log::trace!( + "object {:p} (realm = {:p}, principalls = {:p}, origin = {:?}) is {} \ + with reference to the current Realm (realm = {:p}, principals = {:p}, \ + origin = {:?})", + obj.get(), + obj_realm, + obj_principals.as_raw(), + obj_origin.immutable(), + ["NOT same domain-origin", "same domain-origin"][result as usize], + subject_realm, + subject_principals.as_raw(), + subject_origin.immutable() + ); + + result +} + +/// Report a cross-origin denial for a property, Always returns `false`, so it +/// can be used as `return report_cross_origin_denial(...);`. +/// +/// What this function does corresponds to the operations in +/// <https://html.spec.whatwg.org/multipage/#the-location-interface> denoted as +/// "Throw a `SecurityError` DOMException". +pub unsafe fn report_cross_origin_denial(cx: SafeJSContext, id: RawHandleId, access: &str) -> bool { + debug!( + "permission denied to {} property {} on cross-origin object", + access, + id_to_source(cx, id).as_deref().unwrap_or("< error >"), + ); + let in_realm_proof = AlreadyInRealm::assert_for_cx(cx); + if !JS_IsExceptionPending(*cx) { + let global = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); + // TODO: include `id` and `access` in the exception message + throw_dom_exception(cx, &*global, Error::Security); + } + false +} + +unsafe fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> { + rooted!(in(*cx) let mut value = UndefinedValue()); + rooted!(in(*cx) let mut jsstr = ptr::null_mut::<jsapi::JSString>()); + jsapi::JS_IdToValue(*cx, id.get(), value.handle_mut().into()) + .then(|| { + jsstr.set(jsapi::JS_ValueToSource(*cx, value.handle().into())); + jsstr.get() + }) + .filter(|jsstr| !jsstr.is_null()) + .map(|jsstr| jsstring_to_str(*cx, jsstr)) +} + +/// Property and method specs that correspond to the elements of +/// [`CrossOriginProperties(O)`]. +/// +/// [`CrossOriginProperties(O)`]: https://html.spec.whatwg.org/multipage/#crossoriginproperties-(-o-) +pub struct CrossOriginProperties { + pub attributes: &'static [JSPropertySpec], + pub methods: &'static [JSFunctionSpec], +} + +impl CrossOriginProperties { + /// Enumerate the property keys defined by `self`. + fn keys(&self) -> impl Iterator<Item = *const c_char> + '_ { + // Safety: All cross-origin property keys are strings, not symbols + self.attributes + .iter() + .map(|spec| unsafe { spec.name.string_ }) + .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ })) + .filter(|ptr| !ptr.is_null()) + } +} + +/// Implementation of [`CrossOriginOwnPropertyKeys`]. +/// +/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-) +pub unsafe fn cross_origin_own_property_keys( + cx: SafeJSContext, + _proxy: RawHandleObject, + cross_origin_properties: &'static CrossOriginProperties, + props: RawMutableHandleIdVector, +) -> bool { + // > 2. For each `e` of `! CrossOriginProperties(O)`, append + // > `e.[[Property]]` to `keys`. + for key in cross_origin_properties.keys() { + rooted!(in(*cx) let rooted = JS_AtomizeAndPinString(*cx, key)); + rooted!(in(*cx) let mut rooted_jsid: jsid); + RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), rooted_jsid.handle_mut()); + AppendToIdVector(props, rooted_jsid.handle()); + } + + // > 3. Return the concatenation of `keys` and `« "then", @@toStringTag, + // > @@hasInstance, @@isConcatSpreadable »`. + append_cross_origin_allowlisted_prop_keys(cx, props); + + true +} + +/// Implementation of `[[Set]]` for [`Location`]. +/// +/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-set +pub unsafe extern "C" fn maybe_cross_origin_set_rawcx( + cx: *mut JSContext, + proxy: RawHandleObject, + id: RawHandleId, + v: RawHandleValue, + receiver: RawHandleValue, + result: *mut ObjectOpResult, +) -> bool { + let cx = SafeJSContext::from_ptr(cx); + + if !is_platform_object_same_origin(cx, proxy) { + return cross_origin_set(cx, proxy, id, v, receiver, result); + } + + // Safe to enter the Realm of proxy now. + let _ac = JSAutoRealm::new(*cx, proxy.get()); + + // OrdinarySet + // <https://tc39.es/ecma262/#sec-ordinaryset> + rooted!(in(*cx) let mut own_desc = PropertyDescriptor::default()); + if !InvokeGetOwnPropertyDescriptor( + GetProxyHandler(*proxy), + *cx, + proxy, + id, + own_desc.handle_mut().into(), + ) { + return false; + } + + js::jsapi::SetPropertyIgnoringNamedGetter( + *cx, + proxy, + id, + v, + receiver, + own_desc.handle().into(), + result, + ) +} + +pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx( + _: *mut JSContext, + _proxy: RawHandleObject, + is_ordinary: *mut bool, + _proto: RawMutableHandleObject, +) -> bool { + // We have a custom `[[GetPrototypeOf]]`, so return `false` + *is_ordinary = false; + true +} + +/// Implementation of `[[GetPrototypeOf]]` for [`Location`]. +/// +/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-getprototypeof +pub unsafe fn maybe_cross_origin_get_prototype( + cx: SafeJSContext, + proxy: RawHandleObject, + get_proto_object: unsafe fn(cx: SafeJSContext, global: HandleObject, rval: MutableHandleObject), + proto: RawMutableHandleObject, +) -> bool { + // > 1. If ! IsPlatformObjectSameOrigin(this) is true, then return ! OrdinaryGetPrototypeOf(this). + if is_platform_object_same_origin(cx, proxy) { + let ac = JSAutoRealm::new(*cx, proxy.get()); + let global = GlobalScope::from_context(*cx, InRealm::Entered(&ac)); + get_proto_object( + cx, + global.reflector().get_jsobject(), + MutableHandleObject::from_raw(proto), + ); + return !proto.is_null(); + } + + // > 2. Return null. + proto.set(ptr::null_mut()); + true +} + +/// Implementation of `[[SetPrototypeOf]]` for [`Location`] and [`WindowProxy`]. +/// +/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-setprototypeof +/// [`WindowProxy`]: https://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof +pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx( + cx: *mut JSContext, + proxy: RawHandleObject, + proto: RawHandleObject, + result: *mut ObjectOpResult, +) -> bool { + // > 1. Return `! SetImmutablePrototype(this, V)`. + // + // <https://tc39.es/ecma262/#sec-set-immutable-prototype>: + // + // > 1. Assert: Either `Type(V)` is Object or `Type(V)` is Null. + // + // > 2. Let current be `? O.[[GetPrototypeOf]]()`. + rooted!(in(cx) let mut current = ptr::null_mut::<JSObject>()); + if !jsapi::GetObjectProto(cx, proxy, current.handle_mut().into()) { + return false; + } + + // > 3. If `SameValue(V, current)` is true, return true. + if proto.get() == current.get() { + (*result).code_ = 0 /* OkCode */; + return true; + } + + // > 4. Return false. + (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize; + true +} + +/// Implementation of [`CrossOriginGet`]. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. +/// +/// [`CrossOriginGet`]: https://html.spec.whatwg.org/multipage/#crossoriginget-(-o,-p,-receiver-) +pub unsafe fn cross_origin_get( + cx: SafeJSContext, + proxy: RawHandleObject, + receiver: RawHandleValue, + id: RawHandleId, + vp: RawMutableHandleValue, +) -> bool { + // > 1. Let `desc` be `? O.[[GetOwnProperty]](P)`. + rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default()); + if !InvokeGetOwnPropertyDescriptor( + GetProxyHandler(*proxy), + *cx, + proxy, + id, + descriptor.handle_mut().into(), + ) { + return false; + } + + // > 2. Assert: `desc` is not undefined. + assert!( + !descriptor.obj.is_null(), + "Callees should throw in all cases when they are not finding \ + a property decriptor" + ); + + // > 3. If `! IsDataDescriptor(desc)` is true, then return `desc.[[Value]]`. + if is_data_descriptor(&descriptor) { + vp.set(descriptor.value); + return true; + } + + // > 4. Assert: `IsAccessorDescriptor(desc)` is `true`. + assert!(is_accessor_descriptor(&descriptor)); + + // > 5. Let `getter` be `desc.[[Get]]`. + // > + // > 6. If `getter` is `undefined`, then throw a `SecurityError` + // > `DOMException`. + rooted!(in(*cx) let mut getter = ptr::null_mut::<JSObject>()); + get_getter_object(&descriptor, getter.handle_mut().into()); + if getter.get().is_null() { + return report_cross_origin_denial(cx, id, "get"); + } + + rooted!(in(*cx) let mut getter_jsval = UndefinedValue()); + getter.get().to_jsval(*cx, getter_jsval.handle_mut()); + + // > 7. Return `? Call(getter, Receiver)`. + jsapi::Call( + *cx, + receiver, + getter_jsval.handle().into(), + &jsapi::HandleValueArray::new(), + vp, + ) +} + +/// Implementation of [`CrossOriginSet`]. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. +/// +/// [`CrossOriginSet`]: https://html.spec.whatwg.org/multipage/#crossoriginset-(-o,-p,-v,-receiver-) +pub unsafe fn cross_origin_set( + cx: SafeJSContext, + proxy: RawHandleObject, + id: RawHandleId, + v: RawHandleValue, + receiver: RawHandleValue, + result: *mut ObjectOpResult, +) -> bool { + // > 1. Let desc be ? O.[[GetOwnProperty]](P). + rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default()); + if !InvokeGetOwnPropertyDescriptor( + GetProxyHandler(*proxy), + *cx, + proxy, + id, + descriptor.handle_mut().into(), + ) { + return false; + } + + // > 2. Assert: desc is not undefined. + assert!( + !descriptor.obj.is_null(), + "Callees should throw in all cases when they are not finding \ + a property decriptor" + ); + + // > 3. If desc.[[Set]] is present and its value is not undefined, + // > then: [...] + rooted!(in(*cx) let mut setter = ptr::null_mut::<JSObject>()); + get_setter_object(&descriptor, setter.handle_mut().into()); + if setter.get().is_null() { + // > 4. Throw a "SecurityError" DOMException. + return report_cross_origin_denial(cx, id, "set"); + } + + rooted!(in(*cx) let mut setter_jsval = UndefinedValue()); + setter.get().to_jsval(*cx, setter_jsval.handle_mut()); + + // > 3.1. Perform ? Call(setter, Receiver, «V»). + // > + // > 3.2. Return true. + rooted!(in(*cx) let mut ignored = UndefinedValue()); + if !jsapi::Call( + *cx, + receiver, + setter_jsval.handle().into(), + // FIXME: Our binding lacks `HandleValueArray(Handle<Value>)` + // <https://searchfox.org/mozilla-central/rev/072710086ddfe25aa2962c8399fefb2304e8193b/js/public/ValueArray.h#54-55> + &jsapi::HandleValueArray { + length_: 1, + elements_: v.ptr, + }, + ignored.handle_mut().into(), + ) { + return false; + } + + (*result).code_ = 0 /* OkCode */; + true +} + +unsafe fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { + if (d.attrs & jsapi::JSPROP_GETTER as u32) != 0 { + out.set(std::mem::transmute(d.getter)); + } +} + +unsafe fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { + if (d.attrs & jsapi::JSPROP_SETTER as u32) != 0 { + out.set(std::mem::transmute(d.setter)); + } +} + +/// <https://tc39.es/ecma262/#sec-isaccessordescriptor> +fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool { + d.attrs & (jsapi::JSPROP_GETTER as u32 | jsapi::JSPROP_SETTER as u32) != 0 +} + +/// <https://tc39.es/ecma262/#sec-isdatadescriptor> +fn is_data_descriptor(d: &PropertyDescriptor) -> bool { + let is_accessor = is_accessor_descriptor(d); + let is_generic = d.attrs & + (jsapi::JSPROP_GETTER as u32 | + jsapi::JSPROP_SETTER as u32 | + jsapi::JSPROP_IGNORE_READONLY | + jsapi::JSPROP_IGNORE_VALUE) == + jsapi::JSPROP_IGNORE_READONLY | jsapi::JSPROP_IGNORE_VALUE; + !is_accessor && !is_generic +} + +/// Evaluate `CrossOriginGetOwnPropertyHelper(proxy, id) != null`. +/// SpiderMonkey-specific. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. +pub unsafe fn cross_origin_has_own( + cx: SafeJSContext, + _proxy: RawHandleObject, + cross_origin_properties: &'static CrossOriginProperties, + id: RawHandleId, + bp: *mut bool, +) -> bool { + // TODO: Once we have the slot for the holder, it'd be more efficient to + // use `ensure_cross_origin_property_holder`. We'll need `_proxy` to + // do that. + *bp = jsid_to_string(*cx, Handle::from_raw(id)).map_or(false, |key| { + cross_origin_properties.keys().any(|defined_key| { + let defined_key = CStr::from_ptr(defined_key); + defined_key.to_bytes() == key.as_bytes() + }) + }); + + true +} + +/// Implementation of [`CrossOriginGetOwnPropertyHelper`]. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. +/// +/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-) +pub unsafe fn cross_origin_get_own_property_helper( + cx: SafeJSContext, + proxy: RawHandleObject, + cross_origin_properties: &'static CrossOriginProperties, + id: RawHandleId, + mut desc: RawMutableHandle<PropertyDescriptor>, +) -> bool { + rooted!(in(*cx) let mut holder = ptr::null_mut::<JSObject>()); + + ensure_cross_origin_property_holder( + cx, + proxy, + cross_origin_properties, + holder.handle_mut().into(), + ); + + if !JS_GetOwnPropertyDescriptorById(*cx, holder.handle().into(), id, desc) { + return false; + } + + if !desc.obj.is_null() { + desc.obj = proxy.get(); + } + + true +} + +/// Implementation of [`CrossOriginPropertyFallback`]. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. +/// +/// [`CrossOriginPropertyFallback`]: https://html.spec.whatwg.org/multipage/#crossoriginpropertyfallback-(-p-) +pub unsafe fn cross_origin_property_fallback( + cx: SafeJSContext, + proxy: RawHandleObject, + id: RawHandleId, + mut desc: RawMutableHandle<PropertyDescriptor>, +) -> bool { + assert!(desc.obj.is_null(), "why are we being called?"); + + // > 1. If P is `then`, `@@toStringTag`, `@@hasInstance`, or + // > `@@isConcatSpreadable`, then return `PropertyDescriptor{ [[Value]]: + // > undefined, [[Writable]]: false, [[Enumerable]]: false, + // > [[Configurable]]: true }`. + if is_cross_origin_allowlisted_prop(cx, id) { + *desc = PropertyDescriptor { + getter: None, + setter: None, + value: UndefinedValue(), + attrs: jsapi::JSPROP_READONLY as u32, + obj: proxy.get(), + }; + return true; + } + + // > 2. Throw a `SecurityError` `DOMException`. + report_cross_origin_denial(cx, id, "access") +} + +const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[ + SymbolCode::toStringTag, + SymbolCode::hasInstance, + SymbolCode::isConcatSpreadable, +]; + +unsafe fn is_cross_origin_allowlisted_prop(cx: SafeJSContext, id: RawHandleId) -> bool { + if jsid_to_string(*cx, Handle::from_raw(id)).map_or(false, |st| st == "then") { + return true; + } + + rooted!(in(*cx) let mut allowed_id: jsid); + ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| { + RUST_SYMBOL_TO_JSID( + GetWellKnownSymbol(*cx, allowed_code), + allowed_id.handle_mut().into(), + ); + // `jsid`s containing `JS::Symbol *` can be compared by + // referential equality + allowed_id.get().asBits == id.asBits + }) +} + +/// Append `« "then", @@toStringTag, @@hasInstance, @@isConcatSpreadable »` to +/// `props`. This is used to implement [`CrossOriginOwnPropertyKeys`]. +/// +/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-) +unsafe fn append_cross_origin_allowlisted_prop_keys( + cx: SafeJSContext, + props: RawMutableHandleIdVector, +) { + rooted!(in(*cx) let mut id: jsid); + + let jsstring = JS_AtomizeAndPinString(*cx, b"then\0".as_ptr() as *const c_char); + rooted!(in(*cx) let rooted = jsstring); + RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), id.handle_mut()); + AppendToIdVector(props, id.handle()); + + for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() { + RUST_SYMBOL_TO_JSID( + GetWellKnownSymbol(*cx, allowed_code), + id.handle_mut().into(), + ); + AppendToIdVector(props, id.handle()); + } +} + +/// Get the holder for cross-origin properties for the current global of the +/// `JSContext`, creating one and storing it in a slot of the proxy object if it +/// doesn't exist yet. +/// +/// This essentially creates a cache of [`CrossOriginGetOwnPropertyHelper`]'s +/// results for all property keys. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. The `out_holder` return value will always +/// be in the Realm of `cx`. +/// +/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-) +unsafe fn ensure_cross_origin_property_holder( + cx: SafeJSContext, + _proxy: RawHandleObject, + cross_origin_properties: &'static CrossOriginProperties, + out_holder: RawMutableHandleObject, +) -> bool { + // TODO: We don't have the slot to store the holder yet. For now, + // the holder is constructed every time this function is called, + // which is not only inefficient but also deviates from the + // specification in a subtle yet observable way. + + // Create a holder for the current Realm + out_holder.set(jsapi::JS_NewObjectWithGivenProto( + *cx, + ptr::null_mut(), + RawHandleObject::null(), + )); + + if out_holder.get().is_null() || + !jsapi::JS_DefineProperties( + *cx, + out_holder.handle(), + cross_origin_properties.attributes.as_ptr(), + ) || + !jsapi::JS_DefineFunctions( + *cx, + out_holder.handle(), + cross_origin_properties.methods.as_ptr(), + ) + { + return false; + } + + // TODO: Store the holder in the slot that we don't have yet. + + true +} |