RemoteObjectProxy.cpp (7239B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "RemoteObjectProxy.h" 8 9 #include "AccessCheck.h" 10 #include "js/Object.h" // JS::GetClass 11 #include "jsfriendapi.h" 12 #include "xpcprivate.h" 13 14 namespace mozilla::dom { 15 16 bool RemoteObjectProxyBase::getOwnPropertyDescriptor( 17 JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId, 18 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> aDesc) const { 19 bool ok = CrossOriginGetOwnPropertyHelper(aCx, aProxy, aId, aDesc); 20 if (!ok || aDesc.isSome()) { 21 return ok; 22 } 23 24 return CrossOriginPropertyFallback(aCx, aProxy, aId, aDesc); 25 } 26 27 bool RemoteObjectProxyBase::defineProperty( 28 JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId, 29 JS::Handle<JS::PropertyDescriptor> aDesc, 30 JS::ObjectOpResult& aResult) const { 31 // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-defineownproperty 32 // step 3 and 33 // https://html.spec.whatwg.org/multipage/browsers.html#location-defineownproperty 34 // step 2 35 return ReportCrossOriginDenial(aCx, aId, "define"_ns); 36 } 37 38 bool RemoteObjectProxyBase::ownPropertyKeys( 39 JSContext* aCx, JS::Handle<JSObject*> aProxy, 40 JS::MutableHandleVector<jsid> aProps) const { 41 // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginownpropertykeys-(-o-) 42 // step 2 and 43 // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginproperties-(-o-) 44 JS::Rooted<JSObject*> holder(aCx); 45 if (!EnsureHolder(aCx, aProxy, &holder) || 46 !js::GetPropertyKeys(aCx, holder, 47 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, 48 aProps)) { 49 return false; 50 } 51 52 // https://html.spec.whatwg.org/multipage/browsers.html#crossoriginownpropertykeys-(-o-) 53 // step 3 and 4 54 return xpc::AppendCrossOriginWhitelistedPropNames(aCx, aProps); 55 } 56 57 bool RemoteObjectProxyBase::delete_(JSContext* aCx, 58 JS::Handle<JSObject*> aProxy, 59 JS::Handle<jsid> aId, 60 JS::ObjectOpResult& aResult) const { 61 // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-delete 62 // step 3 and 63 // https://html.spec.whatwg.org/multipage/browsers.html#location-delete step 2 64 return ReportCrossOriginDenial(aCx, aId, "delete"_ns); 65 } 66 67 bool RemoteObjectProxyBase::getPrototypeIfOrdinary( 68 JSContext* aCx, JS::Handle<JSObject*> aProxy, bool* aIsOrdinary, 69 JS::MutableHandle<JSObject*> aProtop) const { 70 // WindowProxy's and Location's [[GetPrototypeOf]] traps aren't the ordinary 71 // definition: 72 // 73 // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof 74 // https://html.spec.whatwg.org/multipage/browsers.html#location-getprototypeof 75 // 76 // We nonetheless can implement it with a static [[Prototype]], because the 77 // [[GetPrototypeOf]] trap should always return null. 78 *aIsOrdinary = true; 79 aProtop.set(nullptr); 80 return true; 81 } 82 83 bool RemoteObjectProxyBase::preventExtensions( 84 JSContext* aCx, JS::Handle<JSObject*> aProxy, 85 JS::ObjectOpResult& aResult) const { 86 // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-preventextensions 87 // and 88 // https://html.spec.whatwg.org/multipage/browsers.html#location-preventextensions 89 return aResult.failCantPreventExtensions(); 90 } 91 92 bool RemoteObjectProxyBase::isExtensible(JSContext* aCx, 93 JS::Handle<JSObject*> aProxy, 94 bool* aExtensible) const { 95 // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-isextensible 96 // and 97 // https://html.spec.whatwg.org/multipage/browsers.html#location-isextensible 98 *aExtensible = true; 99 return true; 100 } 101 102 bool RemoteObjectProxyBase::get(JSContext* aCx, JS::Handle<JSObject*> aProxy, 103 JS::Handle<JS::Value> aReceiver, 104 JS::Handle<jsid> aId, 105 JS::MutableHandle<JS::Value> aVp) const { 106 return CrossOriginGet(aCx, aProxy, aReceiver, aId, aVp); 107 } 108 109 bool RemoteObjectProxyBase::set(JSContext* aCx, JS::Handle<JSObject*> aProxy, 110 JS::Handle<jsid> aId, 111 JS::Handle<JS::Value> aValue, 112 JS::Handle<JS::Value> aReceiver, 113 JS::ObjectOpResult& aResult) const { 114 return CrossOriginSet(aCx, aProxy, aId, aValue, aReceiver, aResult); 115 } 116 117 bool RemoteObjectProxyBase::getOwnEnumerablePropertyKeys( 118 JSContext* aCx, JS::Handle<JSObject*> aProxy, 119 JS::MutableHandleVector<jsid> aProps) const { 120 return true; 121 } 122 123 const char* RemoteObjectProxyBase::className( 124 JSContext* aCx, JS::Handle<JSObject*> aProxy) const { 125 MOZ_ASSERT(js::IsProxy(aProxy)); 126 127 return NamesOfInterfacesWithProtos(mPrototypeID); 128 } 129 130 void RemoteObjectProxyBase::GetOrCreateProxyObject( 131 JSContext* aCx, void* aNative, const JSClass* aClasp, 132 JS::Handle<JSObject*> aTransplantTo, JS::MutableHandle<JSObject*> aProxy, 133 bool& aNewObjectCreated) const { 134 xpc::CompartmentPrivate* priv = 135 xpc::CompartmentPrivate::Get(JS::CurrentGlobalOrNull(aCx)); 136 xpc::CompartmentPrivate::RemoteProxyMap& map = priv->GetRemoteProxyMap(); 137 if (auto result = map.lookup(aNative)) { 138 MOZ_RELEASE_ASSERT(!aTransplantTo, 139 "GOCPO failed by finding an existing value"); 140 141 aProxy.set(result->value()); 142 143 // During a transplant, we put an object that is temporarily not a 144 // proxy object into the map. Make sure that we don't return one of 145 // these objects in the middle of a transplant. 146 MOZ_RELEASE_ASSERT(JS::GetClass(aProxy) == aClasp); 147 148 return; 149 } 150 151 js::ProxyOptions options; 152 options.setClass(aClasp); 153 JS::Rooted<JS::Value> native(aCx, JS::PrivateValue(aNative)); 154 JS::Rooted<JSObject*> obj( 155 aCx, js::NewProxyObject(aCx, this, native, nullptr, options)); 156 if (!obj) { 157 MOZ_RELEASE_ASSERT(!aTransplantTo, "GOCPO failed at NewProxyObject"); 158 return; 159 } 160 161 bool success; 162 if (!JS_SetImmutablePrototype(aCx, obj, &success)) { 163 MOZ_RELEASE_ASSERT(!aTransplantTo, 164 "GOCPO failed at JS_SetImmutablePrototype"); 165 return; 166 } 167 MOZ_ASSERT(success); 168 169 aNewObjectCreated = true; 170 171 // If we're transplanting onto an object, we want to make sure that it does 172 // not have the same class as aClasp to ensure that the release assert earlier 173 // in this function will actually fire if we try to return a proxy object in 174 // the middle of a transplant. 175 MOZ_RELEASE_ASSERT(!aTransplantTo || (JS::GetClass(aTransplantTo) != aClasp), 176 "GOCPO failed by not changing the class"); 177 178 if (!map.put(aNative, aTransplantTo ? aTransplantTo : obj)) { 179 MOZ_RELEASE_ASSERT(!aTransplantTo, "GOCPO failed at map.put"); 180 return; 181 } 182 183 aProxy.set(obj); 184 } 185 186 const char RemoteObjectProxyBase::sCrossOriginProxyFamily = 0; 187 188 } // namespace mozilla::dom