Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#include "RemoteObjectProxy.h"
#include "AccessCheck.h"
#include "jsfriendapi.h"
#include "js/Object.h" // JS::GetClass
#include "xpcprivate.h"
namespace mozilla::dom {
bool RemoteObjectProxyBase::getOwnPropertyDescriptor(
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
JS::MutableHandle<Maybe<JS::PropertyDescriptor>> aDesc) const {
bool ok = CrossOriginGetOwnPropertyHelper(aCx, aProxy, aId, aDesc);
if (!ok || aDesc.isSome()) {
return ok;
}
return CrossOriginPropertyFallback(aCx, aProxy, aId, aDesc);
}
bool RemoteObjectProxyBase::defineProperty(
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
JS::Handle<JS::PropertyDescriptor> aDesc,
JS::ObjectOpResult& aResult) const {
// step 3 and
// step 2
return ReportCrossOriginDenial(aCx, aId, "define"_ns);
}
bool RemoteObjectProxyBase::ownPropertyKeys(
JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::MutableHandleVector<jsid> aProps) const {
// step 2 and
JS::Rooted<JSObject*> holder(aCx);
if (!EnsureHolder(aCx, aProxy, &holder) ||
!js::GetPropertyKeys(aCx, holder,
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
aProps)) {
return false;
}
// step 3 and 4
return xpc::AppendCrossOriginWhitelistedPropNames(aCx, aProps);
}
bool RemoteObjectProxyBase::delete_(JSContext* aCx,
JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId,
JS::ObjectOpResult& aResult) const {
// step 3 and
return ReportCrossOriginDenial(aCx, aId, "delete"_ns);
}
bool RemoteObjectProxyBase::getPrototypeIfOrdinary(
JSContext* aCx, JS::Handle<JSObject*> aProxy, bool* aIsOrdinary,
JS::MutableHandle<JSObject*> aProtop) const {
// WindowProxy's and Location's [[GetPrototypeOf]] traps aren't the ordinary
// definition:
//
//
// We nonetheless can implement it with a static [[Prototype]], because the
// [[GetPrototypeOf]] trap should always return null.
*aIsOrdinary = true;
aProtop.set(nullptr);
return true;
}
bool RemoteObjectProxyBase::preventExtensions(
JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::ObjectOpResult& aResult) const {
// and
return aResult.failCantPreventExtensions();
}
bool RemoteObjectProxyBase::isExtensible(JSContext* aCx,
JS::Handle<JSObject*> aProxy,
bool* aExtensible) const {
// and
*aExtensible = true;
return true;
}
bool RemoteObjectProxyBase::get(JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::Handle<JS::Value> aReceiver,
JS::Handle<jsid> aId,
JS::MutableHandle<JS::Value> aVp) const {
return CrossOriginGet(aCx, aProxy, aReceiver, aId, aVp);
}
bool RemoteObjectProxyBase::set(JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId,
JS::Handle<JS::Value> aValue,
JS::Handle<JS::Value> aReceiver,
JS::ObjectOpResult& aResult) const {
return CrossOriginSet(aCx, aProxy, aId, aValue, aReceiver, aResult);
}
bool RemoteObjectProxyBase::getOwnEnumerablePropertyKeys(
JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::MutableHandleVector<jsid> aProps) const {
return true;
}
const char* RemoteObjectProxyBase::className(
JSContext* aCx, JS::Handle<JSObject*> aProxy) const {
MOZ_ASSERT(js::IsProxy(aProxy));
return NamesOfInterfacesWithProtos(mPrototypeID);
}
void RemoteObjectProxyBase::GetOrCreateProxyObject(
JSContext* aCx, void* aNative, const JSClass* aClasp,
JS::Handle<JSObject*> aTransplantTo, JS::MutableHandle<JSObject*> aProxy,
bool& aNewObjectCreated) const {
xpc::CompartmentPrivate* priv =
xpc::CompartmentPrivate::Get(JS::CurrentGlobalOrNull(aCx));
xpc::CompartmentPrivate::RemoteProxyMap& map = priv->GetRemoteProxyMap();
if (auto result = map.lookup(aNative)) {
MOZ_RELEASE_ASSERT(!aTransplantTo,
"GOCPO failed by finding an existing value");
aProxy.set(result->value());
// During a transplant, we put an object that is temporarily not a
// proxy object into the map. Make sure that we don't return one of
// these objects in the middle of a transplant.
MOZ_RELEASE_ASSERT(JS::GetClass(aProxy) == aClasp);
return;
}
js::ProxyOptions options;
options.setClass(aClasp);
JS::Rooted<JS::Value> native(aCx, JS::PrivateValue(aNative));
JS::Rooted<JSObject*> obj(
aCx, js::NewProxyObject(aCx, this, native, nullptr, options));
if (!obj) {
MOZ_RELEASE_ASSERT(!aTransplantTo, "GOCPO failed at NewProxyObject");
return;
}
bool success;
if (!JS_SetImmutablePrototype(aCx, obj, &success)) {
MOZ_RELEASE_ASSERT(!aTransplantTo,
"GOCPO failed at JS_SetImmutablePrototype");
return;
}
MOZ_ASSERT(success);
aNewObjectCreated = true;
// If we're transplanting onto an object, we want to make sure that it does
// not have the same class as aClasp to ensure that the release assert earlier
// in this function will actually fire if we try to return a proxy object in
// the middle of a transplant.
MOZ_RELEASE_ASSERT(!aTransplantTo || (JS::GetClass(aTransplantTo) != aClasp),
"GOCPO failed by not changing the class");
if (!map.put(aNative, aTransplantTo ? aTransplantTo : obj)) {
MOZ_RELEASE_ASSERT(!aTransplantTo, "GOCPO failed at map.put");
return;
}
aProxy.set(obj);
}
const char RemoteObjectProxyBase::sCrossOriginProxyFamily = 0;
} // namespace mozilla::dom