зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1363208 part 2. Add a helper class for implementing the HTML requirements for cross-origin-accessible objects. r=jandem,peterv
Differential Revision: https://phabricator.services.mozilla.com/D15425 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
67be15b8fc
Коммит
79d353e3bc
|
@ -0,0 +1,433 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/MaybeCrossOriginObject.h"
|
||||
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/RemoteObjectProxy.h"
|
||||
#include "js/Proxy.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Wrapper.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "AccessCheck.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool IsLocation(JSObject* obj) {
|
||||
return strcmp(js::GetObjectClass(obj)->name, "Location") == 0;
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/* static */
|
||||
bool MaybeCrossOriginObjectMixins::IsPlatformObjectSameOrigin(
|
||||
JSContext* cx, JS::Handle<JSObject*> obj) {
|
||||
MOZ_ASSERT(!js::IsCrossCompartmentWrapper(obj));
|
||||
// WindowProxy and Window must always be same-Realm, so we can do
|
||||
// our IsPlatformObjectSameOrigin check against either one. But verify that
|
||||
// in case we have a WindowProxy the right things happen.
|
||||
MOZ_ASSERT(js::GetNonCCWObjectRealm(obj) ==
|
||||
// "true" for second arg means to unwrap WindowProxy to
|
||||
// get at the Window.
|
||||
js::GetNonCCWObjectRealm(js::UncheckedUnwrap(obj, true)),
|
||||
"WindowProxy not same-Realm as Window?");
|
||||
|
||||
BasePrincipal* subjectPrincipal =
|
||||
BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(cx));
|
||||
nsIPrincipal* objectPrincipal = nsContentUtils::ObjectPrincipal(obj);
|
||||
|
||||
// The spec effectively has an EqualsConsideringDomain check here,
|
||||
// because the spec has no concept of asymmetric security
|
||||
// relationships. But we shouldn't ever end up here in the
|
||||
// asymmetric case anyway: That case should end up with Xrays, which
|
||||
// don't call into this code.
|
||||
//
|
||||
// Let's assert that EqualsConsideringDomain and
|
||||
// SubsumesConsideringDomain give the same results and use
|
||||
// EqualsConsideringDomain for the check we actually do, since it's
|
||||
// stricter and more closely matches the spec.
|
||||
MOZ_ASSERT(
|
||||
subjectPrincipal->FastEqualsConsideringDomain(objectPrincipal) ==
|
||||
subjectPrincipal->FastSubsumesConsideringDomain(objectPrincipal),
|
||||
"Why are we in an asymmetric case here?");
|
||||
return subjectPrincipal->FastEqualsConsideringDomain(objectPrincipal);
|
||||
}
|
||||
|
||||
bool MaybeCrossOriginObjectMixins::CrossOriginGetOwnPropertyHelper(
|
||||
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const {
|
||||
MOZ_ASSERT(!IsPlatformObjectSameOrigin(cx, obj) || IsRemoteObjectProxy(obj),
|
||||
"Why did we get called?");
|
||||
// First check for an IDL-defined cross-origin property with the given name.
|
||||
// This corresponds to
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)
|
||||
// step 2.
|
||||
JS::Rooted<JSObject*> holder(cx);
|
||||
if (!EnsureHolder(cx, obj, &holder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desc.object()) {
|
||||
desc.object().set(obj);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool MaybeCrossOriginObjectMixins::CrossOriginPropertyFallback(
|
||||
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) {
|
||||
MOZ_ASSERT(!desc.object(), "Why are we being called?");
|
||||
|
||||
// Step 1.
|
||||
if (xpc::IsCrossOriginWhitelistedProp(cx, id)) {
|
||||
// Spec says to return PropertyDescriptor {
|
||||
// [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false,
|
||||
// [[Configurable]]: true
|
||||
// }.
|
||||
desc.setDataDescriptor(JS::UndefinedHandleValue, JSPROP_READONLY);
|
||||
desc.object().set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Step 2.
|
||||
return ReportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("access"));
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool MaybeCrossOriginObjectMixins::CrossOriginGet(
|
||||
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<JS::Value> receiver,
|
||||
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) {
|
||||
// This is fairly similar to BaseProxyHandler::get, but there are some
|
||||
// differences. Most importantly, we want to throw if we have a descriptor
|
||||
// with no getter, while BaseProxyHandler::get returns undefined. The other
|
||||
// big difference is that we don't have to worry about prototypes (ours is
|
||||
// always null).
|
||||
|
||||
// We want to invoke [[GetOwnProperty]] on "obj", but _without_ entering its
|
||||
// compartment, because for the proxies we have here [[GetOwnProperty]] will
|
||||
// do security checks based on the current Realm. Unfortunately,
|
||||
// JS_GetPropertyDescriptorById asserts that compartments match. Luckily, we
|
||||
// know that "obj" is a proxy here, so we can directly call its
|
||||
// getOwnPropertyDescriptor() hook.
|
||||
//
|
||||
// It looks like Proxy::getOwnPropertyDescriptor is not public, so just grab
|
||||
// the handler and call its getOwnPropertyDescriptor hook directly.
|
||||
MOZ_ASSERT(js::IsProxy(obj), "How did we get a bogus object here?");
|
||||
MOZ_ASSERT(
|
||||
js::IsWindowProxy(obj) || IsLocation(obj) || IsRemoteObjectProxy(obj),
|
||||
"Unexpected proxy");
|
||||
MOZ_ASSERT(!IsPlatformObjectSameOrigin(cx, obj) || IsRemoteObjectProxy(obj),
|
||||
"Why did we get called?");
|
||||
js::AssertSameCompartment(cx, receiver);
|
||||
|
||||
// Step 1.
|
||||
JS::Rooted<JS::PropertyDescriptor> desc(cx);
|
||||
if (!js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, obj, id, &desc)) {
|
||||
return false;
|
||||
}
|
||||
desc.assertCompleteIfFound();
|
||||
|
||||
// Step 2.
|
||||
MOZ_ASSERT(desc.object(),
|
||||
"Callees should throw in all cases when they are not finding a "
|
||||
"property decriptor");
|
||||
|
||||
// Step 3.
|
||||
if (desc.isDataDescriptor()) {
|
||||
vp.set(desc.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
MOZ_ASSERT(desc.isAccessorDescriptor());
|
||||
|
||||
// Step 5.
|
||||
JS::Rooted<JSObject*> getter(cx);
|
||||
if (!desc.hasGetterObject() || !(getter = desc.getterObject())) {
|
||||
// Step 6.
|
||||
return ReportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("get"));
|
||||
}
|
||||
|
||||
// Step 7.
|
||||
return JS::Call(cx, receiver, getter, JS::HandleValueArray::empty(), vp);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool MaybeCrossOriginObjectMixins::CrossOriginSet(
|
||||
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
|
||||
JS::ObjectOpResult& result) {
|
||||
// We want to invoke [[GetOwnProperty]] on "obj", but _without_ entering its
|
||||
// compartment, because for the proxies we have here [[GetOwnProperty]] will
|
||||
// do security checks based on the current Realm. Unfortunately,
|
||||
// JS_GetPropertyDescriptorById asserts that compartments match. Luckily, we
|
||||
// know that "obj" is a proxy here, so we can directly call its
|
||||
// getOwnPropertyDescriptor() hook.
|
||||
//
|
||||
// It looks like Proxy::getOwnPropertyDescriptor is not public, so just grab
|
||||
// the handler and call its getOwnPropertyDescriptor hook directly.
|
||||
MOZ_ASSERT(js::IsProxy(obj), "How did we get a bogus object here?");
|
||||
MOZ_ASSERT(
|
||||
js::IsWindowProxy(obj) || IsLocation(obj) || IsRemoteObjectProxy(obj),
|
||||
"Unexpected proxy");
|
||||
MOZ_ASSERT(!IsPlatformObjectSameOrigin(cx, obj) || IsRemoteObjectProxy(obj),
|
||||
"Why did we get called?");
|
||||
js::AssertSameCompartment(cx, receiver);
|
||||
js::AssertSameCompartment(cx, v);
|
||||
|
||||
// Step 1.
|
||||
JS::Rooted<JS::PropertyDescriptor> desc(cx);
|
||||
if (!js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, obj, id, &desc)) {
|
||||
return false;
|
||||
}
|
||||
desc.assertCompleteIfFound();
|
||||
|
||||
// Step 2.
|
||||
MOZ_ASSERT(desc.object(),
|
||||
"Callees should throw in all cases when they are not finding a "
|
||||
"property decriptor");
|
||||
|
||||
// Step 3.
|
||||
JS::Rooted<JSObject*> setter(cx);
|
||||
if (desc.hasSetterObject() && (setter = desc.setterObject())) {
|
||||
JS::Rooted<JS::Value> ignored(cx);
|
||||
// Step 3.1.
|
||||
if (!JS::Call(cx, receiver, setter, JS::HandleValueArray(v), &ignored)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3.2.
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
return ReportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("set"));
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool MaybeCrossOriginObjectMixins::EnsureHolder(
|
||||
JSContext* cx, JS::Handle<JSObject*> obj, size_t slot,
|
||||
JSPropertySpec* attributes, JSFunctionSpec* methods,
|
||||
JS::MutableHandle<JSObject*> holder) {
|
||||
MOZ_ASSERT(!IsPlatformObjectSameOrigin(cx, obj) || IsRemoteObjectProxy(obj),
|
||||
"Why are we calling this at all in same-origin cases?");
|
||||
// We store the holders in a weakmap stored in obj's slot. Our object is
|
||||
// always a proxy, so we can just go ahead and use GetProxyReservedSlot here.
|
||||
JS::Rooted<JS::Value> weakMapVal(cx, js::GetProxyReservedSlot(obj, slot));
|
||||
if (weakMapVal.isUndefined()) {
|
||||
// Enter the Realm of "obj" when we allocate the WeakMap, since we are going
|
||||
// to store it in a slot on "obj" and in general we may not be
|
||||
// same-compartment with "obj" here.
|
||||
JSAutoRealm ar(cx, obj);
|
||||
JSObject* newMap = JS::NewWeakMapObject(cx);
|
||||
if (!newMap) {
|
||||
return false;
|
||||
}
|
||||
weakMapVal.setObject(*newMap);
|
||||
js::SetProxyReservedSlot(obj, slot, weakMapVal);
|
||||
}
|
||||
MOZ_ASSERT(weakMapVal.isObject(),
|
||||
"How did a non-object else end up in this slot?");
|
||||
|
||||
JS::Rooted<JSObject*> map(cx, &weakMapVal.toObject());
|
||||
MOZ_ASSERT(JS::IsWeakMapObject(map),
|
||||
"How did something else end up in this slot?");
|
||||
|
||||
// We need to be in "map"'s compartment to work with it. Per spec, the key
|
||||
// for this map is supposed to be the pair (current settings, relevant
|
||||
// settings). The current settings corresponds to the current Realm of cx.
|
||||
// The relevant settings corresponds to the Realm of "obj", but since all of
|
||||
// our objects are per-Realm singletons, we are basically using "obj" itself
|
||||
// as part of the key.
|
||||
//
|
||||
// To represent the current settings, we use the current-Realm
|
||||
// Object.prototype. We can't use the current global, because we can't get a
|
||||
// useful cross-compartment wrapper for it; such wrappers would always go
|
||||
// through a WindowProxy and would not be guarantee to keep pointing to a
|
||||
// single Realm when unwrapped. We want to grab this key before we start
|
||||
// changing Realms.
|
||||
JS::Rooted<JSObject*> key(cx, JS::GetRealmObjectPrototype(cx));
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> holderVal(cx);
|
||||
{ // Scope for working with the map
|
||||
JSAutoRealm ar(cx, map);
|
||||
if (!MaybeWrapObject(cx, &key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS::GetWeakMapEntry(cx, map, key, &holderVal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (holderVal.isObject()) {
|
||||
// We want to do an unchecked unwrap, because the holder (and the current
|
||||
// caller) may actually be more privileged than our map.
|
||||
holder.set(js::UncheckedUnwrap(&holderVal.toObject()));
|
||||
|
||||
// holder might be a dead object proxy if things got nuked.
|
||||
if (!JS_IsDeadWrapper(holder)) {
|
||||
MOZ_ASSERT(js::GetContextRealm(cx) == js::GetNonCCWObjectRealm(holder),
|
||||
"How did we end up with a key/value mismatch?");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find a usable holder. Go ahead and allocate one. At this point
|
||||
// we have two options: we could allocate the holder in the current Realm and
|
||||
// store a cross-compartment wrapper for it in the map as needed, or we could
|
||||
// allocate the holder in the Realm of the map and have it hold
|
||||
// cross-compartment references to all the methods it holds, since those
|
||||
// methods need to be in our current Realm. It seems better to allocate the
|
||||
// holder in our current Realm.
|
||||
holder.set(JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
|
||||
if (!holder || !JS_DefineProperties(cx, holder, attributes) ||
|
||||
!JS_DefineFunctions(cx, holder, methods)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
holderVal.setObject(*holder);
|
||||
{ // Scope for working with the map
|
||||
JSAutoRealm ar(cx, map);
|
||||
|
||||
// Key is already in the right Realm, but we need to wrap the value.
|
||||
if (!MaybeWrapValue(cx, &holderVal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS::SetWeakMapEntry(cx, map, key, holderVal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool MaybeCrossOriginObjectMixins::ReportCrossOriginDenial(
|
||||
JSContext* aCx, JS::Handle<jsid> aId, const nsACString& aAccessType) {
|
||||
xpc::AccessCheck::reportCrossOriginDenial(aCx, aId, aAccessType);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
bool MaybeCrossOriginObject<Base>::getPrototype(
|
||||
JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::MutableHandle<JSObject*> protop) const {
|
||||
if (!IsPlatformObjectSameOrigin(cx, proxy)) {
|
||||
protop.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
{ // Scope for JSAutoRealm
|
||||
JSAutoRealm ar(cx, proxy);
|
||||
protop.set(getSameOriginPrototype(cx));
|
||||
if (!protop) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return MaybeWrapObject(cx, protop);
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
bool MaybeCrossOriginObject<Base>::setPrototype(
|
||||
JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<JSObject*> proto,
|
||||
JS::ObjectOpResult& result) const {
|
||||
// Inlined version of
|
||||
// https://tc39.github.io/ecma262/#sec-set-immutable-prototype
|
||||
js::AssertSameCompartment(cx, proto);
|
||||
|
||||
// We have to be careful how we get the prototype. In particular, we do _NOT_
|
||||
// want to enter the Realm of "proxy" to do that, in case we're not
|
||||
// same-origin with it here.
|
||||
JS::Rooted<JSObject*> wrappedProxy(cx, proxy);
|
||||
if (!MaybeWrapObject(cx, &wrappedProxy)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> currentProto(cx);
|
||||
if (!js::GetObjectProto(cx, wrappedProxy, ¤tProto)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentProto != proto) {
|
||||
return result.failCantSetProto();
|
||||
}
|
||||
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
bool MaybeCrossOriginObject<Base>::getPrototypeIfOrdinary(
|
||||
JSContext* cx, JS::Handle<JSObject*> proxy, bool* isOrdinary,
|
||||
JS::MutableHandle<JSObject*> protop) const {
|
||||
// We have a custom [[GetPrototypeOf]]
|
||||
*isOrdinary = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
bool MaybeCrossOriginObject<Base>::isExtensible(JSContext* cx,
|
||||
JS::Handle<JSObject*> proxy,
|
||||
bool* extensible) const {
|
||||
// We never allow [[PreventExtensions]] to succeed.
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
bool MaybeCrossOriginObject<Base>::preventExtensions(
|
||||
JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::ObjectOpResult& result) const {
|
||||
return result.failCantPreventExtensions();
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
bool MaybeCrossOriginObject<Base>::defineProperty(
|
||||
JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result) const {
|
||||
if (!IsPlatformObjectSameOrigin(cx, proxy)) {
|
||||
return ReportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("define"));
|
||||
}
|
||||
|
||||
// Enter the Realm of proxy and do the remaining work in there.
|
||||
JSAutoRealm ar(cx, proxy);
|
||||
JS::Rooted<JS::PropertyDescriptor> descCopy(cx, desc);
|
||||
if (!JS_WrapPropertyDescriptor(cx, &descCopy)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_MarkCrossZoneId(cx, id);
|
||||
|
||||
return definePropertySameOrigin(cx, proxy, id, descCopy, result);
|
||||
}
|
||||
|
||||
template <typename Base>
|
||||
JSObject* MaybeCrossOriginObject<Base>::enumerate(
|
||||
JSContext* cx, JS::Handle<JSObject*> proxy) const {
|
||||
// We want to avoid any possible magic here and just do the BaseProxyHandler
|
||||
// thing of using our property keys to enumerate.
|
||||
//
|
||||
// Note that we do not need to enter the Realm of "proxy" here, nor do we want
|
||||
// to: if this is a cross-origin access we want to handle it appropriately.
|
||||
return js::BaseProxyHandler::enumerate(cx, proxy);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,325 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_MaybeCrossOriginObject_h
|
||||
#define mozilla_dom_MaybeCrossOriginObject_h
|
||||
|
||||
/**
|
||||
* Shared infrastructure for WindowProxy and Location objects. These
|
||||
* are the objects that can be accessed cross-origin in the HTML
|
||||
* specification.
|
||||
*
|
||||
* This class can be inherited from by the relevant proxy handlers to
|
||||
* help implement spec algorithms.
|
||||
*
|
||||
* The algorithms this class implements come from
|
||||
* <https://html.spec.whatwg.org/multipage/browsers.html#shared-abstract-operations>,
|
||||
* <https://html.spec.whatwg.org/multipage/window-object.html#the-windowproxy-exotic-object>,
|
||||
* and
|
||||
* <https://html.spec.whatwg.org/multipage/history.html#the-location-interface>.
|
||||
*
|
||||
* The class is templated on its base so we can directly implement the things
|
||||
* that should have identical implementations for WindowProxy and Location. The
|
||||
* templating is needed because WindowProxy needs to be a wrapper and Location
|
||||
* shouldn't be one.
|
||||
*/
|
||||
|
||||
#include "js/Class.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Methods that MaybeCrossOriginObject wants that do not depend on the "Base"
|
||||
// template parameter. We can avoid having multiple instantiations of them by
|
||||
// pulling them out into this helper class.
|
||||
class MaybeCrossOriginObjectMixins {
|
||||
protected:
|
||||
/**
|
||||
* Implementation of
|
||||
* <https://html.spec.whatwg.org/multipage/browsers.html#isplatformobjectsameorigin-(-o-)>.
|
||||
* "cx" and "obj" may or may not be same-compartment and even when
|
||||
* same-compartment may not be same-Realm. "obj" can be a WindowProxy, a
|
||||
* Window, or a Location.
|
||||
*/
|
||||
static bool IsPlatformObjectSameOrigin(JSContext* cx,
|
||||
JS::Handle<JSObject*> obj);
|
||||
|
||||
/**
|
||||
* Implementation of
|
||||
* <https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)>.
|
||||
*
|
||||
* "cx" and "obj" are expected to be different-Realm here, and may be
|
||||
* different-compartment. "obj" can be a "WindowProxy" or a "Location" or a
|
||||
* cross-process proxy for one of those.
|
||||
*/
|
||||
bool CrossOriginGetOwnPropertyHelper(
|
||||
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const;
|
||||
|
||||
/**
|
||||
* Implementation of
|
||||
* <https://html.spec.whatwg.org/multipage/browsers.html#crossoriginpropertyfallback-(-p-)>.
|
||||
*
|
||||
* This should be called at the end of getOwnPropertyDescriptor
|
||||
* methods in the cross-origin case.
|
||||
*
|
||||
* "cx" and "obj" are expected to be different-Realm here, and may
|
||||
* be different-compartment. "obj" can be a "WindowProxy" or a
|
||||
* "Location" or a cross-process proxy for one of those.
|
||||
*/
|
||||
static bool CrossOriginPropertyFallback(
|
||||
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc);
|
||||
|
||||
/**
|
||||
* Implementation of
|
||||
* <https://html.spec.whatwg.org/multipage/browsers.html#crossoriginget-(-o,-p,-receiver-)>.
|
||||
*
|
||||
* "cx" and "obj" are expected to be different-Realm here and may be
|
||||
* different-compartment. "obj" can be a "WindowProxy" or a
|
||||
* "Location" or a cross-process proxy for one of those.
|
||||
*
|
||||
* "receiver" will be in the compartment of "cx". The return value will
|
||||
* be in the compartment of "cx".
|
||||
*/
|
||||
static bool CrossOriginGet(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
JS::Handle<JS::Value> receiver,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::Value> vp);
|
||||
|
||||
/**
|
||||
* Implementation of
|
||||
* <https://html.spec.whatwg.org/multipage/browsers.html#crossoriginset-(-o,-p,-v,-receiver-)>.
|
||||
*
|
||||
* "cx" and "obj" are expected to be different-Realm here and may be
|
||||
* different-compartment. "obj" can be a "WindowProxy" or a
|
||||
* "Location" or a cross-process proxy for one of those.
|
||||
*
|
||||
* "receiver" and "v" will be in the compartment of "cx".
|
||||
*/
|
||||
static bool CrossOriginSet(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
JS::Handle<jsid> id, JS::Handle<JS::Value> v,
|
||||
JS::Handle<JS::Value> receiver,
|
||||
JS::ObjectOpResult& result);
|
||||
|
||||
/**
|
||||
* Utility method to ensure a holder for cross-origin properties for the
|
||||
* current global of the JSContext.
|
||||
*
|
||||
* When this is called, "cx" and "obj" are _always_ different-Realm, because
|
||||
* this is only used in cross-origin situations. The "holder" return value is
|
||||
* always in the Realm of "cx".
|
||||
*
|
||||
* "obj" is the object which has space to store the collection of holders in
|
||||
* the given slot.
|
||||
*
|
||||
* "attributes" and "methods" are the cross-origin attributes and methods we
|
||||
* care about, which should get defined on holders.
|
||||
*/
|
||||
static bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
size_t slot, JSPropertySpec* attributes,
|
||||
JSFunctionSpec* methods,
|
||||
JS::MutableHandle<JSObject*> holder);
|
||||
|
||||
/**
|
||||
* Ensures we have a holder object for the current Realm. When this is
|
||||
* called, "obj" is guaranteed to not be same-Realm with "cx", because this
|
||||
* is only used for cross-origin cases.
|
||||
*
|
||||
* Subclasses are expected to implement this by calling our static
|
||||
* EnsureHolder with the appropriate arguments.
|
||||
*/
|
||||
virtual bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::MutableHandle<JSObject*> holder) const = 0;
|
||||
|
||||
/**
|
||||
* Report a cross-origin denial for a property named by aId. Always
|
||||
* returns false, so it can be used as "return
|
||||
* ReportCrossOriginDenial(...);".
|
||||
*/
|
||||
static bool ReportCrossOriginDenial(JSContext* aCx, JS::Handle<jsid> aId,
|
||||
const nsACString& aAccessType);
|
||||
};
|
||||
|
||||
// A proxy handler for objects that may be cross-origin objects. Whether they
|
||||
// actually _are_ cross-origin objects can change dynamically if document.domain
|
||||
// is set.
|
||||
template <typename Base>
|
||||
class MaybeCrossOriginObject : public Base,
|
||||
public MaybeCrossOriginObjectMixins {
|
||||
protected:
|
||||
template <typename... Args>
|
||||
constexpr MaybeCrossOriginObject(Args&&... aArgs)
|
||||
: Base(std::forward<Args>(aArgs)...) {}
|
||||
|
||||
/**
|
||||
* Implementation of [[GetPrototypeOf]] as defined in
|
||||
* <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getprototypeof>
|
||||
* and
|
||||
* <https://html.spec.whatwg.org/multipage/history.html#location-getprototypeof>.
|
||||
*
|
||||
* Our prototype-storage model looks quite different from the spec's, so we
|
||||
* need to implement some hooks that don't directly map to the spec.
|
||||
*
|
||||
* "proxy" is the WindowProxy or Location involved. It may or may not be
|
||||
* same-compartment with cx.
|
||||
*
|
||||
* "protop" is the prototype value (possibly null). It is guaranteed to be
|
||||
* same-compartment with cx after this function returns successfully.
|
||||
*/
|
||||
bool getPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::MutableHandle<JSObject*> protop) const final;
|
||||
|
||||
/**
|
||||
* Hook for doing the OrdinaryGetPrototypeOf bits that [[GetPrototypeOf]] does
|
||||
* in the spec. Location and WindowProxy store that information somewhat
|
||||
* differently.
|
||||
*
|
||||
* The prototype should come from the Realm of "cx".
|
||||
*/
|
||||
virtual JSObject* getSameOriginPrototype(JSContext* cx) const = 0;
|
||||
|
||||
/**
|
||||
* Implementation of [[SetPrototypeOf]] as defined in
|
||||
* <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-setprototypeof>
|
||||
* and
|
||||
* <https://html.spec.whatwg.org/multipage/history.html#location-setprototypeof>.
|
||||
*
|
||||
* "proxy" is the WindowProxy or Location object involved. It may or may not
|
||||
* be same-compartment with "cx".
|
||||
*
|
||||
* "proto" is the new prototype object (possibly null). It must be
|
||||
* same-compartment with "cx".
|
||||
*/
|
||||
bool setPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<JSObject*> proto,
|
||||
JS::ObjectOpResult& result) const final;
|
||||
|
||||
/**
|
||||
* Our non-standard getPrototypeIfOrdinary hook. We don't need to implement
|
||||
* setImmutablePrototype, because the default behavior of not allowing it is
|
||||
* fine for us.
|
||||
*/
|
||||
bool getPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
bool* isOrdinary,
|
||||
JS::MutableHandle<JSObject*> protop) const final;
|
||||
|
||||
/**
|
||||
* Implementation of [[IsExtensible]] as defined in
|
||||
* <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-isextensible>
|
||||
* and
|
||||
* <https://html.spec.whatwg.org/multipage/history.html#location-isextensible>.
|
||||
*/
|
||||
bool isExtensible(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
bool* extensible) const final;
|
||||
|
||||
/**
|
||||
* Implementation of [[PreventExtensions]] as defined in
|
||||
* <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-preventextensions>
|
||||
* and
|
||||
* <https://html.spec.whatwg.org/multipage/history.html#location-preventextensions>.
|
||||
*/
|
||||
bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::ObjectOpResult& result) const final;
|
||||
|
||||
/**
|
||||
* Implementation of [[GetOwnProperty]] is completely delegated to subclasses.
|
||||
*
|
||||
* "proxy" is the WindowProxy or Location object involved. It may or may not
|
||||
* be same-compartment with cx.
|
||||
*/
|
||||
bool getOwnPropertyDescriptor(
|
||||
JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override = 0;
|
||||
|
||||
/**
|
||||
* Implementation of [[DefineOwnProperty]] as defined in
|
||||
* <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-defineownproperty>
|
||||
* and
|
||||
* <https://html.spec.whatwg.org/multipage/history.html#location-defineownproperty>.
|
||||
* "proxy" is the WindowProxy or Location object involved. It may or may not
|
||||
* be same-compartment with cx.
|
||||
*
|
||||
*/
|
||||
bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<jsid> id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::ObjectOpResult& result) const final;
|
||||
|
||||
/**
|
||||
* Hook for handling the same-origin case in defineProperty.
|
||||
*
|
||||
* "proxy" is the WindowProxy or Location object involved. It will be
|
||||
* same-compartment with cx.
|
||||
*
|
||||
* "desc" is a the descriptor being defined. It will be same-compartment with
|
||||
* cx.
|
||||
*/
|
||||
virtual bool definePropertySameOrigin(JSContext* cx,
|
||||
JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<jsid> id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::ObjectOpResult& result) const = 0;
|
||||
|
||||
/**
|
||||
* Implementation of [[Get]] is completely delegated to subclasses.
|
||||
*
|
||||
* "proxy" is the WindowProxy or Location object involved. It may or may not
|
||||
* be same-compartment with "cx".
|
||||
*
|
||||
* "receiver" is the receiver ("this") for the get. It will be
|
||||
* same-compartment with "cx"
|
||||
*
|
||||
* "vp" is the return value. It will be same-compartment with "cx".
|
||||
*/
|
||||
bool get(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::Value> vp) const override = 0;
|
||||
|
||||
/**
|
||||
* Implementation of [[Set]] is completely delegated to subclasses.
|
||||
*
|
||||
* "proxy" is the WindowProxy or Location object involved. It may or may not
|
||||
* be same-compartment with "cx".
|
||||
*
|
||||
* "v" is the value being set. It will be same-compartment with "cx".
|
||||
*
|
||||
* "receiver" is the receiver ("this") for the set. It will be
|
||||
* same-compartment with "cx".
|
||||
*/
|
||||
bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
||||
JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
|
||||
JS::ObjectOpResult& result) const override = 0;
|
||||
|
||||
/**
|
||||
* Implementation of [[Delete]] is completely delegated to subclasses.
|
||||
*
|
||||
* "proxy" is the WindowProxy or Location object involved. It may or may not
|
||||
* be same-compartment with "cx".
|
||||
*/
|
||||
bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
||||
JS::ObjectOpResult& result) const override = 0;
|
||||
|
||||
/**
|
||||
* Spidermonkey-internal hook for enumerating objects.
|
||||
*/
|
||||
JSObject* enumerate(JSContext* cx, JS::Handle<JSObject*> proxy) const final;
|
||||
|
||||
/**
|
||||
* Spidermonkey-internal hook used by Object.prototype.toString. Subclasses
|
||||
* need to implement this, because we don't know what className they want.
|
||||
* Except in the cross-origin case, when we could maybe handle it...
|
||||
*/
|
||||
const char* className(JSContext* cx,
|
||||
JS::Handle<JSObject*> proxy) const override = 0;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_MaybeCrossOriginObject_h */
|
|
@ -121,8 +121,7 @@ bool RemoteOuterWindowProxy::getOwnPropertyDescriptor(
|
|||
return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("access"));
|
||||
}
|
||||
|
||||
bool ok = RemoteObjectProxy::getOwnPropertyDescriptorInternal(aCx, aProxy,
|
||||
aId, aDesc);
|
||||
bool ok = CrossOriginGetOwnPropertyHelper(aCx, aProxy, aId, aDesc);
|
||||
if (!ok || aDesc.object()) {
|
||||
return ok;
|
||||
}
|
||||
|
@ -140,7 +139,7 @@ bool RemoteOuterWindowProxy::getOwnPropertyDescriptor(
|
|||
}
|
||||
}
|
||||
|
||||
return getOwnPropertyDescriptorTail(aCx, aProxy, aId, aDesc);
|
||||
return CrossOriginPropertyFallback(aCx, aProxy, aId, aDesc);
|
||||
}
|
||||
|
||||
bool AppendIndexedPropertyNames(JSContext* aCx, BrowsingContext* aContext,
|
||||
|
|
|
@ -193,6 +193,7 @@ EXPORTS.mozilla.dom += [
|
|||
'IntlUtils.h',
|
||||
'Link.h',
|
||||
'Location.h',
|
||||
'MaybeCrossOriginObject.h',
|
||||
'MessageBroadcaster.h',
|
||||
'MessageListenerManager.h',
|
||||
'MessageManagerGlobal.h',
|
||||
|
@ -298,6 +299,7 @@ UNIFIED_SOURCES += [
|
|||
'IntlUtils.cpp',
|
||||
'Link.cpp',
|
||||
'Location.cpp',
|
||||
'MaybeCrossOriginObject.cpp',
|
||||
'MessageBroadcaster.cpp',
|
||||
'MessageListenerManager.cpp',
|
||||
'MessageManagerGlobal.cpp',
|
||||
|
|
|
@ -20,12 +20,12 @@ const js::Class RemoteObjectProxyClass =
|
|||
bool RemoteObjectProxyBase::getOwnPropertyDescriptor(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> aDesc) const {
|
||||
bool ok = getOwnPropertyDescriptorInternal(aCx, aProxy, aId, aDesc);
|
||||
bool ok = CrossOriginGetOwnPropertyHelper(aCx, aProxy, aId, aDesc);
|
||||
if (!ok || aDesc.object()) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
return getOwnPropertyDescriptorTail(aCx, aProxy, aId, aDesc);
|
||||
return CrossOriginPropertyFallback(aCx, aProxy, aId, aDesc);
|
||||
}
|
||||
|
||||
bool RemoteObjectProxyBase::defineProperty(
|
||||
|
@ -137,24 +137,7 @@ bool RemoteObjectProxyBase::get(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
|||
JS::Handle<JS::Value> aReceiver,
|
||||
JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JS::Value> aVp) const {
|
||||
Rooted<PropertyDescriptor> desc(aCx);
|
||||
if (!getOwnPropertyDescriptor(aCx, aProxy, aId, &desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(desc.object());
|
||||
|
||||
if (desc.isDataDescriptor()) {
|
||||
aVp.set(desc.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> getter(aCx);
|
||||
if (!desc.hasGetterObject() || !(getter = desc.getterObject())) {
|
||||
return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("get"));
|
||||
}
|
||||
|
||||
return JS::Call(aCx, aReceiver, getter, JS::HandleValueArray::empty(), aVp);
|
||||
return CrossOriginGet(aCx, aProxy, aReceiver, aId, aVp);
|
||||
}
|
||||
|
||||
bool RemoteObjectProxyBase::set(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
||||
|
@ -162,21 +145,7 @@ bool RemoteObjectProxyBase::set(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
|||
JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aReceiver,
|
||||
JS::ObjectOpResult& aResult) const {
|
||||
Rooted<PropertyDescriptor> desc(aCx);
|
||||
if (!getOwnPropertyDescriptor(aCx, aProxy, aId, &desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(desc.object());
|
||||
|
||||
JS::Rooted<JSObject*> setter(aCx);
|
||||
if (!desc.hasSetterObject() || !(setter = desc.setterObject())) {
|
||||
return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("set"));
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> rv(aCx);
|
||||
return JS::Call(aCx, aReceiver, setter, JS::HandleValueArray(aValue), &rv) &&
|
||||
aResult.succeed();
|
||||
return CrossOriginSet(aCx, aProxy, aId, aValue, aReceiver, aResult);
|
||||
}
|
||||
|
||||
bool RemoteObjectProxyBase::hasOwn(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
||||
|
@ -208,32 +177,6 @@ JSObject* RemoteObjectProxyBase::CreateProxyObject(
|
|||
return js::NewProxyObject(aCx, this, native, nullptr, options);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool RemoteObjectProxyBase::getOwnPropertyDescriptorTail(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
|
||||
if (xpc::IsCrossOriginWhitelistedProp(aCx, aId)) {
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)
|
||||
// step 3 says to return PropertyDescriptor {
|
||||
// [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false,
|
||||
// [[Configurable]]: true
|
||||
// }.
|
||||
//
|
||||
aDesc.setDataDescriptor(JS::UndefinedHandleValue, JSPROP_READONLY);
|
||||
aDesc.object().set(aProxy);
|
||||
return true;
|
||||
}
|
||||
|
||||
return ReportCrossOriginDenial(aCx, aId, NS_LITERAL_CSTRING("access"));
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool RemoteObjectProxyBase::ReportCrossOriginDenial(
|
||||
JSContext* aCx, JS::Handle<jsid> aId, const nsACString& aAccessType) {
|
||||
xpc::AccessCheck::reportCrossOriginDenial(aCx, aId, aAccessType);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char RemoteObjectProxyBase::sCrossOriginProxyFamily = 0;
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define mozilla_dom_RemoteObjectProxy_h
|
||||
|
||||
#include "js/Proxy.h"
|
||||
#include "mozilla/dom/MaybeCrossOriginObject.h"
|
||||
#include "mozilla/dom/PrototypeList.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
|
@ -19,7 +20,8 @@ namespace dom {
|
|||
* don't depend on properties/methods of the specific WebIDL interface that this
|
||||
* proxy implements.
|
||||
*/
|
||||
class RemoteObjectProxyBase : public js::BaseProxyHandler {
|
||||
class RemoteObjectProxyBase : public js::BaseProxyHandler,
|
||||
public MaybeCrossOriginObjectMixins {
|
||||
protected:
|
||||
explicit constexpr RemoteObjectProxyBase(prototypes::ID aPrototypeID)
|
||||
: BaseProxyHandler(&sCrossOriginProxyFamily, false),
|
||||
|
@ -77,8 +79,9 @@ class RemoteObjectProxyBase : public js::BaseProxyHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if aProxy represents an object implementing the WebIDL
|
||||
* interface for aProtoID. aProxy should be a proxy object.
|
||||
* Returns true if aProxy is a cross-process proxy that represents
|
||||
* an object implementing the WebIDL interface for aProtoID. aProxy
|
||||
* should be a proxy object.
|
||||
*/
|
||||
static inline bool IsRemoteObjectProxy(JSObject* aProxy,
|
||||
prototypes::ID aProtoID) {
|
||||
|
@ -88,62 +91,19 @@ class RemoteObjectProxyBase : public js::BaseProxyHandler {
|
|||
aProtoID;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool getOwnPropertyDescriptorInternal(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> aDesc) const {
|
||||
JS::Rooted<JSObject*> holder(aCx);
|
||||
if (!EnsureHolder(aCx, aProxy, &holder) ||
|
||||
!JS_GetOwnPropertyDescriptorById(aCx, holder, aId, aDesc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aDesc.object()) {
|
||||
aDesc.object().set(aProxy);
|
||||
}
|
||||
|
||||
return true;
|
||||
/**
|
||||
* Returns true if aProxy is a cross-process proxy, no matter which
|
||||
* interface it represents. aProxy should be a proxy object.
|
||||
*/
|
||||
static inline bool IsRemoteObjectProxy(JSObject* aProxy) {
|
||||
const js::BaseProxyHandler* handler = js::GetProxyHandler(aProxy);
|
||||
return handler->family() == &sCrossOriginProxyFamily;
|
||||
}
|
||||
|
||||
protected:
|
||||
JSObject* CreateProxyObject(JSContext* aCx, void* aNative,
|
||||
const js::Class* aClasp) const;
|
||||
|
||||
/**
|
||||
* Implements the tail of getOwnPropertyDescriptor, dealing in particular with
|
||||
* properties that are whitelisted by xpc::IsCrossOriginWhitelistedProp.
|
||||
*/
|
||||
static bool getOwnPropertyDescriptorTail(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> aDesc);
|
||||
static bool ReportCrossOriginDenial(JSContext* aCx, JS::Handle<jsid> aId,
|
||||
const nsACString& aAccessType);
|
||||
|
||||
/**
|
||||
* This gets a cached, or creates and caches, a holder object that contains
|
||||
* the WebIDL properties for this proxy.
|
||||
*/
|
||||
bool EnsureHolder(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
||||
JS::MutableHandle<JSObject*> aHolder) const {
|
||||
// FIXME Need to have a holder per realm, should store a weakmap in the
|
||||
// reserved slot.
|
||||
JS::Value v = js::GetProxyReservedSlot(aProxy, 0);
|
||||
if (v.isObject()) {
|
||||
aHolder.set(&v.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
aHolder.set(JS_NewObjectWithGivenProto(aCx, nullptr, nullptr));
|
||||
if (!aHolder || !DefinePropertiesAndFunctions(aCx, aHolder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
js::SetProxyReservedSlot(aProxy, 0, JS::ObjectValue(*aHolder));
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool DefinePropertiesAndFunctions(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aHolder) const = 0;
|
||||
|
||||
const prototypes::ID mPrototypeID;
|
||||
|
||||
static const char sCrossOriginProxyFamily;
|
||||
|
@ -175,16 +135,17 @@ class RemoteObjectProxy : public RemoteObjectProxyBase {
|
|||
using RemoteObjectProxyBase::RemoteObjectProxyBase;
|
||||
|
||||
private:
|
||||
bool DefinePropertiesAndFunctions(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aHolder) const final {
|
||||
return JS_DefineProperties(aCx, aHolder, P) &&
|
||||
JS_DefineFunctions(aCx, aHolder, F);
|
||||
bool EnsureHolder(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
||||
JS::MutableHandle<JSObject*> aHolder) const final {
|
||||
return MaybeCrossOriginObjectMixins::EnsureHolder(
|
||||
aCx, aProxy, /* slot = */ 0, P, F, aHolder);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if aObj is a proxy object that represents an object implementing
|
||||
* the WebIDL interface for aProtoID.
|
||||
* Returns true if aObj is a cross-process proxy object that
|
||||
* represents an object implementing the WebIDL interface for
|
||||
* aProtoID.
|
||||
*/
|
||||
static inline bool IsRemoteObjectProxy(JSObject* aObj,
|
||||
prototypes::ID aProtoID) {
|
||||
|
@ -194,6 +155,17 @@ static inline bool IsRemoteObjectProxy(JSObject* aObj,
|
|||
return RemoteObjectProxyBase::IsRemoteObjectProxy(aObj, aProtoID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if aObj is a cross-process proxy object, no matter
|
||||
* which WebIDL interface it corresponds to.
|
||||
*/
|
||||
static inline bool IsRemoteObjectProxy(JSObject* aObj) {
|
||||
if (!js::IsProxy(aObj)) {
|
||||
return false;
|
||||
}
|
||||
return RemoteObjectProxyBase::IsRemoteObjectProxy(aObj);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче