1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bindings/DOMJSProxyHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,371 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: set ts=2 sw=2 et tw=99 ft=cpp: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/dom/DOMJSProxyHandler.h" 1.11 +#include "xpcpublic.h" 1.12 +#include "xpcprivate.h" 1.13 +#include "XPCQuickStubs.h" 1.14 +#include "XPCWrapper.h" 1.15 +#include "WrapperFactory.h" 1.16 +#include "nsDOMClassInfo.h" 1.17 +#include "nsWrapperCacheInlines.h" 1.18 +#include "mozilla/dom/BindingUtils.h" 1.19 + 1.20 +#include "jsapi.h" 1.21 + 1.22 +using namespace JS; 1.23 + 1.24 +namespace mozilla { 1.25 +namespace dom { 1.26 + 1.27 +jsid s_length_id = JSID_VOID; 1.28 + 1.29 +bool 1.30 +DefineStaticJSVals(JSContext* cx) 1.31 +{ 1.32 + return InternJSString(cx, s_length_id, "length"); 1.33 +} 1.34 + 1.35 + 1.36 +const char HandlerFamily = 0; 1.37 + 1.38 +js::DOMProxyShadowsResult 1.39 +DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) 1.40 +{ 1.41 + JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO); 1.42 + if (v.isObject()) { 1.43 + bool hasOwn; 1.44 + Rooted<JSObject*> object(cx, &v.toObject()); 1.45 + if (!JS_AlreadyHasOwnPropertyById(cx, object, id, &hasOwn)) 1.46 + return js::ShadowCheckFailed; 1.47 + 1.48 + return hasOwn ? js::Shadows : js::DoesntShadow; 1.49 + } 1.50 + 1.51 + if (v.isUndefined()) { 1.52 + return js::DoesntShadow; 1.53 + } 1.54 + 1.55 + bool hasOwn; 1.56 + if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn)) 1.57 + return js::ShadowCheckFailed; 1.58 + 1.59 + return hasOwn ? js::Shadows : js::DoesntShadowUnique; 1.60 +} 1.61 + 1.62 +// Store the information for the specialized ICs. 1.63 +struct SetDOMProxyInformation 1.64 +{ 1.65 + SetDOMProxyInformation() { 1.66 + js::SetDOMProxyInformation((const void*) &HandlerFamily, 1.67 + js::PROXY_EXTRA_SLOT + JSPROXYSLOT_EXPANDO, DOMProxyShadows); 1.68 + } 1.69 +}; 1.70 + 1.71 +SetDOMProxyInformation gSetDOMProxyInformation; 1.72 + 1.73 +// static 1.74 +JSObject* 1.75 +DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj) 1.76 +{ 1.77 + MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object"); 1.78 + JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO); 1.79 + if (v.isUndefined()) { 1.80 + return nullptr; 1.81 + } 1.82 + 1.83 + if (v.isObject()) { 1.84 + js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue()); 1.85 + XPCWrappedNativeScope* scope = xpc::MaybeGetObjectScope(obj); 1.86 + if (scope) { 1.87 + scope->RemoveDOMExpandoObject(obj); 1.88 + } 1.89 + } else { 1.90 + js::ExpandoAndGeneration* expandoAndGeneration = 1.91 + static_cast<js::ExpandoAndGeneration*>(v.toPrivate()); 1.92 + v = expandoAndGeneration->expando; 1.93 + if (v.isUndefined()) { 1.94 + return nullptr; 1.95 + } 1.96 + expandoAndGeneration->expando = UndefinedValue(); 1.97 + } 1.98 + 1.99 + 1.100 + return &v.toObject(); 1.101 +} 1.102 + 1.103 +// static 1.104 +JSObject* 1.105 +DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj) 1.106 +{ 1.107 + NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object"); 1.108 + JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO); 1.109 + if (v.isObject()) { 1.110 + return &v.toObject(); 1.111 + } 1.112 + 1.113 + js::ExpandoAndGeneration* expandoAndGeneration; 1.114 + if (!v.isUndefined()) { 1.115 + expandoAndGeneration = static_cast<js::ExpandoAndGeneration*>(v.toPrivate()); 1.116 + if (expandoAndGeneration->expando.isObject()) { 1.117 + return &expandoAndGeneration->expando.toObject(); 1.118 + } 1.119 + } else { 1.120 + expandoAndGeneration = nullptr; 1.121 + } 1.122 + 1.123 + JS::Rooted<JSObject*> parent(cx, js::GetObjectParent(obj)); 1.124 + JS::Rooted<JSObject*> expando(cx, 1.125 + JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), parent)); 1.126 + if (!expando) { 1.127 + return nullptr; 1.128 + } 1.129 + 1.130 + nsISupports* native = UnwrapDOMObject<nsISupports>(obj); 1.131 + nsWrapperCache* cache; 1.132 + CallQueryInterface(native, &cache); 1.133 + if (expandoAndGeneration) { 1.134 + cache->PreserveWrapper(native); 1.135 + expandoAndGeneration->expando.setObject(*expando); 1.136 + 1.137 + return expando; 1.138 + } 1.139 + 1.140 + XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj); 1.141 + if (!scope->RegisterDOMExpandoObject(obj)) { 1.142 + return nullptr; 1.143 + } 1.144 + 1.145 + cache->SetPreservingWrapper(true); 1.146 + js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando)); 1.147 + 1.148 + return expando; 1.149 +} 1.150 + 1.151 +bool 1.152 +DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) 1.153 +{ 1.154 + // always extensible per WebIDL 1.155 + *extensible = true; 1.156 + return true; 1.157 +} 1.158 + 1.159 +bool 1.160 +DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) 1.161 +{ 1.162 + // Throw a TypeError, per WebIDL. 1.163 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.164 + JSMSG_CANT_CHANGE_EXTENSIBILITY); 1.165 + return false; 1.166 +} 1.167 + 1.168 +bool 1.169 +BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx, 1.170 + JS::Handle<JSObject*> proxy, 1.171 + JS::Handle<jsid> id, 1.172 + MutableHandle<JSPropertyDescriptor> desc) 1.173 +{ 1.174 + if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) { 1.175 + return false; 1.176 + } 1.177 + if (desc.object()) { 1.178 + return true; 1.179 + } 1.180 + 1.181 + JS::Rooted<JSObject*> proto(cx); 1.182 + if (!js::GetObjectProto(cx, proxy, &proto)) { 1.183 + return false; 1.184 + } 1.185 + if (!proto) { 1.186 + desc.object().set(nullptr); 1.187 + return true; 1.188 + } 1.189 + 1.190 + return JS_GetPropertyDescriptorById(cx, proto, id, desc); 1.191 +} 1.192 + 1.193 +bool 1.194 +BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx, 1.195 + JS::Handle<JSObject*> proxy, 1.196 + JS::Handle<jsid> id, 1.197 + MutableHandle<JSPropertyDescriptor> desc) 1.198 +{ 1.199 + return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false, 1.200 + desc); 1.201 +} 1.202 + 1.203 +bool 1.204 +DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 1.205 + MutableHandle<JSPropertyDescriptor> desc, bool* defined) 1.206 +{ 1.207 + if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) { 1.208 + return JS_ReportErrorFlagsAndNumber(cx, 1.209 + JSREPORT_WARNING | JSREPORT_STRICT | 1.210 + JSREPORT_STRICT_MODE_ERROR, 1.211 + js_GetErrorMessage, nullptr, 1.212 + JSMSG_GETTER_ONLY); 1.213 + } 1.214 + 1.215 + if (xpc::WrapperFactory::IsXrayWrapper(proxy)) { 1.216 + return true; 1.217 + } 1.218 + 1.219 + JSObject* expando = EnsureExpandoObject(cx, proxy); 1.220 + if (!expando) { 1.221 + return false; 1.222 + } 1.223 + 1.224 + bool dummy; 1.225 + return js_DefineOwnProperty(cx, expando, id, desc, &dummy); 1.226 +} 1.227 + 1.228 +bool 1.229 +DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> receiver, 1.230 + Handle<jsid> id, bool strict, MutableHandle<JS::Value> vp) 1.231 +{ 1.232 + MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), 1.233 + "Should not have a XrayWrapper here"); 1.234 + bool done; 1.235 + if (!setCustom(cx, proxy, id, vp, &done)) { 1.236 + return false; 1.237 + } 1.238 + if (done) { 1.239 + return true; 1.240 + } 1.241 + 1.242 + // Make sure to ignore our named properties when checking for own 1.243 + // property descriptors for a set. 1.244 + JS::Rooted<JSPropertyDescriptor> desc(cx); 1.245 + if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true, 1.246 + &desc)) { 1.247 + return false; 1.248 + } 1.249 + bool descIsOwn = desc.object() != nullptr; 1.250 + if (!desc.object()) { 1.251 + // Don't just use getPropertyDescriptor, unlike BaseProxyHandler::set, 1.252 + // because that would call getOwnPropertyDescriptor on ourselves. Instead, 1.253 + // directly delegate to the proto, if any. 1.254 + JS::Rooted<JSObject*> proto(cx); 1.255 + if (!js::GetObjectProto(cx, proxy, &proto)) { 1.256 + return false; 1.257 + } 1.258 + if (proto && !JS_GetPropertyDescriptorById(cx, proto, id, &desc)) { 1.259 + return false; 1.260 + } 1.261 + } 1.262 + 1.263 + return js::SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, 1.264 + &desc, descIsOwn, strict, vp); 1.265 +} 1.266 + 1.267 +bool 1.268 +DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy, 1.269 + JS::Handle<jsid> id, bool* bp) 1.270 +{ 1.271 + JS::Rooted<JSObject*> expando(cx); 1.272 + if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) { 1.273 + return JS_DeletePropertyById2(cx, expando, id, bp); 1.274 + } 1.275 + 1.276 + *bp = true; 1.277 + return true; 1.278 +} 1.279 + 1.280 +bool 1.281 +BaseDOMProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, 1.282 + AutoIdVector& props) 1.283 +{ 1.284 + JS::Rooted<JSObject*> proto(cx); 1.285 + if (!JS_GetPrototype(cx, proxy, &proto)) { 1.286 + return false; 1.287 + } 1.288 + return keys(cx, proxy, props) && 1.289 + (!proto || js::GetPropertyNames(cx, proto, 0, &props)); 1.290 +} 1.291 + 1.292 +bool 1.293 +BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 1.294 + JS::Handle<JSObject*> callable) 1.295 +{ 1.296 + return js::WatchGuts(cx, proxy, id, callable); 1.297 +} 1.298 + 1.299 +bool 1.300 +BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) 1.301 +{ 1.302 + return js::UnwatchGuts(cx, proxy, id); 1.303 +} 1.304 + 1.305 +bool 1.306 +BaseDOMProxyHandler::getOwnPropertyNames(JSContext* cx, 1.307 + JS::Handle<JSObject*> proxy, 1.308 + JS::AutoIdVector& props) 1.309 +{ 1.310 + return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props); 1.311 +} 1.312 + 1.313 +bool 1.314 +BaseDOMProxyHandler::keys(JSContext* cx, 1.315 + JS::Handle<JSObject*> proxy, 1.316 + JS::AutoIdVector& props) 1.317 +{ 1.318 + return ownPropNames(cx, proxy, JSITER_OWNONLY, props); 1.319 +} 1.320 + 1.321 +bool 1.322 +DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) 1.323 +{ 1.324 + if (!hasOwn(cx, proxy, id, bp)) { 1.325 + return false; 1.326 + } 1.327 + 1.328 + if (*bp) { 1.329 + // We have the property ourselves; no need to worry about our prototype 1.330 + // chain. 1.331 + return true; 1.332 + } 1.333 + 1.334 + // OK, now we have to look at the proto 1.335 + JS::Rooted<JSObject*> proto(cx); 1.336 + if (!js::GetObjectProto(cx, proxy, &proto)) { 1.337 + return false; 1.338 + } 1.339 + if (!proto) { 1.340 + return true; 1.341 + } 1.342 + bool protoHasProp; 1.343 + bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp); 1.344 + if (ok) { 1.345 + *bp = protoHasProp; 1.346 + } 1.347 + return ok; 1.348 +} 1.349 + 1.350 +int32_t 1.351 +IdToInt32(JSContext* cx, JS::Handle<jsid> id) 1.352 +{ 1.353 + JS::Rooted<JS::Value> idval(cx); 1.354 + double array_index; 1.355 + int32_t i; 1.356 + if (!::JS_IdToValue(cx, id, &idval) || 1.357 + !JS::ToNumber(cx, idval, &array_index) || 1.358 + !::JS_DoubleIsInt32(array_index, &i)) { 1.359 + return -1; 1.360 + } 1.361 + 1.362 + return i; 1.363 +} 1.364 + 1.365 +bool 1.366 +DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 1.367 + JS::MutableHandle<JS::Value> vp, bool *done) 1.368 +{ 1.369 + *done = false; 1.370 + return true; 1.371 +} 1.372 + 1.373 +} // namespace dom 1.374 +} // namespace mozilla