/* -*- 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 "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 aProxy, JS::Handle aId, JS::MutableHandle> 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 aProxy, JS::Handle aId, JS::Handle aDesc, JS::ObjectOpResult& aResult) const { // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-defineownproperty // step 3 and // https://html.spec.whatwg.org/multipage/browsers.html#location-defineownproperty // step 2 return ReportCrossOriginDenial(aCx, aId, "define"_ns); } bool RemoteObjectProxyBase::ownPropertyKeys( JSContext* aCx, JS::Handle aProxy, JS::MutableHandleVector aProps) const { // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginownpropertykeys-(-o-) // step 2 and // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginproperties-(-o-) JS::Rooted holder(aCx); if (!EnsureHolder(aCx, aProxy, &holder) || !js::GetPropertyKeys(aCx, holder, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, aProps)) { return false; } // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginownpropertykeys-(-o-) // step 3 and 4 return xpc::AppendCrossOriginWhitelistedPropNames(aCx, aProps); } bool RemoteObjectProxyBase::delete_(JSContext* aCx, JS::Handle aProxy, JS::Handle aId, JS::ObjectOpResult& aResult) const { // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-delete // step 3 and // https://html.spec.whatwg.org/multipage/browsers.html#location-delete step 2 return ReportCrossOriginDenial(aCx, aId, "delete"_ns); } bool RemoteObjectProxyBase::getPrototypeIfOrdinary( JSContext* aCx, JS::Handle aProxy, bool* aIsOrdinary, JS::MutableHandle aProtop) const { // WindowProxy's and Location's [[GetPrototypeOf]] traps aren't the ordinary // definition: // // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof // https://html.spec.whatwg.org/multipage/browsers.html#location-getprototypeof // // 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 aProxy, JS::ObjectOpResult& aResult) const { // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-preventextensions // and // https://html.spec.whatwg.org/multipage/browsers.html#location-preventextensions return aResult.failCantPreventExtensions(); } bool RemoteObjectProxyBase::isExtensible(JSContext* aCx, JS::Handle aProxy, bool* aExtensible) const { // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-isextensible // and // https://html.spec.whatwg.org/multipage/browsers.html#location-isextensible *aExtensible = true; return true; } bool RemoteObjectProxyBase::get(JSContext* aCx, JS::Handle aProxy, JS::Handle aReceiver, JS::Handle aId, JS::MutableHandle aVp) const { return CrossOriginGet(aCx, aProxy, aReceiver, aId, aVp); } bool RemoteObjectProxyBase::set(JSContext* aCx, JS::Handle aProxy, JS::Handle aId, JS::Handle aValue, JS::Handle aReceiver, JS::ObjectOpResult& aResult) const { return CrossOriginSet(aCx, aProxy, aId, aValue, aReceiver, aResult); } bool RemoteObjectProxyBase::getOwnEnumerablePropertyKeys( JSContext* aCx, JS::Handle aProxy, JS::MutableHandleVector aProps) const { return true; } const char* RemoteObjectProxyBase::className( JSContext* aCx, JS::Handle aProxy) const { MOZ_ASSERT(js::IsProxy(aProxy)); return "Object"; } void RemoteObjectProxyBase::GetOrCreateProxyObject( JSContext* aCx, void* aNative, const JSClass* aClasp, JS::Handle aTransplantTo, JS::MutableHandle 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_ASSERT(!aTransplantTo, "No existing value allowed if we're doing a transplant"); 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 native(aCx, JS::PrivateValue(aNative)); JS::Rooted obj( aCx, js::NewProxyObject(aCx, this, native, nullptr, options)); if (!obj) { return; } bool success; if (!JS_SetImmutablePrototype(aCx, obj, &success)) { 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_ASSERT_IF(aTransplantTo, JS::GetClass(aTransplantTo) != aClasp); if (!map.put(aNative, aTransplantTo ? aTransplantTo : obj)) { return; } aProxy.set(obj); } const char RemoteObjectProxyBase::sCrossOriginProxyFamily = 0; } // namespace mozilla::dom