dom/bindings/DOMJSProxyHandler.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * vim: set ts=2 sw=2 et tw=99 ft=cpp: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozilla/dom/DOMJSProxyHandler.h"
michael@0 8 #include "xpcpublic.h"
michael@0 9 #include "xpcprivate.h"
michael@0 10 #include "XPCQuickStubs.h"
michael@0 11 #include "XPCWrapper.h"
michael@0 12 #include "WrapperFactory.h"
michael@0 13 #include "nsDOMClassInfo.h"
michael@0 14 #include "nsWrapperCacheInlines.h"
michael@0 15 #include "mozilla/dom/BindingUtils.h"
michael@0 16
michael@0 17 #include "jsapi.h"
michael@0 18
michael@0 19 using namespace JS;
michael@0 20
michael@0 21 namespace mozilla {
michael@0 22 namespace dom {
michael@0 23
michael@0 24 jsid s_length_id = JSID_VOID;
michael@0 25
michael@0 26 bool
michael@0 27 DefineStaticJSVals(JSContext* cx)
michael@0 28 {
michael@0 29 return InternJSString(cx, s_length_id, "length");
michael@0 30 }
michael@0 31
michael@0 32
michael@0 33 const char HandlerFamily = 0;
michael@0 34
michael@0 35 js::DOMProxyShadowsResult
michael@0 36 DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
michael@0 37 {
michael@0 38 JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
michael@0 39 if (v.isObject()) {
michael@0 40 bool hasOwn;
michael@0 41 Rooted<JSObject*> object(cx, &v.toObject());
michael@0 42 if (!JS_AlreadyHasOwnPropertyById(cx, object, id, &hasOwn))
michael@0 43 return js::ShadowCheckFailed;
michael@0 44
michael@0 45 return hasOwn ? js::Shadows : js::DoesntShadow;
michael@0 46 }
michael@0 47
michael@0 48 if (v.isUndefined()) {
michael@0 49 return js::DoesntShadow;
michael@0 50 }
michael@0 51
michael@0 52 bool hasOwn;
michael@0 53 if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn))
michael@0 54 return js::ShadowCheckFailed;
michael@0 55
michael@0 56 return hasOwn ? js::Shadows : js::DoesntShadowUnique;
michael@0 57 }
michael@0 58
michael@0 59 // Store the information for the specialized ICs.
michael@0 60 struct SetDOMProxyInformation
michael@0 61 {
michael@0 62 SetDOMProxyInformation() {
michael@0 63 js::SetDOMProxyInformation((const void*) &HandlerFamily,
michael@0 64 js::PROXY_EXTRA_SLOT + JSPROXYSLOT_EXPANDO, DOMProxyShadows);
michael@0 65 }
michael@0 66 };
michael@0 67
michael@0 68 SetDOMProxyInformation gSetDOMProxyInformation;
michael@0 69
michael@0 70 // static
michael@0 71 JSObject*
michael@0 72 DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
michael@0 73 {
michael@0 74 MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
michael@0 75 JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
michael@0 76 if (v.isUndefined()) {
michael@0 77 return nullptr;
michael@0 78 }
michael@0 79
michael@0 80 if (v.isObject()) {
michael@0 81 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
michael@0 82 XPCWrappedNativeScope* scope = xpc::MaybeGetObjectScope(obj);
michael@0 83 if (scope) {
michael@0 84 scope->RemoveDOMExpandoObject(obj);
michael@0 85 }
michael@0 86 } else {
michael@0 87 js::ExpandoAndGeneration* expandoAndGeneration =
michael@0 88 static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
michael@0 89 v = expandoAndGeneration->expando;
michael@0 90 if (v.isUndefined()) {
michael@0 91 return nullptr;
michael@0 92 }
michael@0 93 expandoAndGeneration->expando = UndefinedValue();
michael@0 94 }
michael@0 95
michael@0 96
michael@0 97 return &v.toObject();
michael@0 98 }
michael@0 99
michael@0 100 // static
michael@0 101 JSObject*
michael@0 102 DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
michael@0 103 {
michael@0 104 NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
michael@0 105 JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
michael@0 106 if (v.isObject()) {
michael@0 107 return &v.toObject();
michael@0 108 }
michael@0 109
michael@0 110 js::ExpandoAndGeneration* expandoAndGeneration;
michael@0 111 if (!v.isUndefined()) {
michael@0 112 expandoAndGeneration = static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
michael@0 113 if (expandoAndGeneration->expando.isObject()) {
michael@0 114 return &expandoAndGeneration->expando.toObject();
michael@0 115 }
michael@0 116 } else {
michael@0 117 expandoAndGeneration = nullptr;
michael@0 118 }
michael@0 119
michael@0 120 JS::Rooted<JSObject*> parent(cx, js::GetObjectParent(obj));
michael@0 121 JS::Rooted<JSObject*> expando(cx,
michael@0 122 JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), parent));
michael@0 123 if (!expando) {
michael@0 124 return nullptr;
michael@0 125 }
michael@0 126
michael@0 127 nsISupports* native = UnwrapDOMObject<nsISupports>(obj);
michael@0 128 nsWrapperCache* cache;
michael@0 129 CallQueryInterface(native, &cache);
michael@0 130 if (expandoAndGeneration) {
michael@0 131 cache->PreserveWrapper(native);
michael@0 132 expandoAndGeneration->expando.setObject(*expando);
michael@0 133
michael@0 134 return expando;
michael@0 135 }
michael@0 136
michael@0 137 XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
michael@0 138 if (!scope->RegisterDOMExpandoObject(obj)) {
michael@0 139 return nullptr;
michael@0 140 }
michael@0 141
michael@0 142 cache->SetPreservingWrapper(true);
michael@0 143 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
michael@0 144
michael@0 145 return expando;
michael@0 146 }
michael@0 147
michael@0 148 bool
michael@0 149 DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
michael@0 150 {
michael@0 151 // always extensible per WebIDL
michael@0 152 *extensible = true;
michael@0 153 return true;
michael@0 154 }
michael@0 155
michael@0 156 bool
michael@0 157 DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy)
michael@0 158 {
michael@0 159 // Throw a TypeError, per WebIDL.
michael@0 160 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
michael@0 161 JSMSG_CANT_CHANGE_EXTENSIBILITY);
michael@0 162 return false;
michael@0 163 }
michael@0 164
michael@0 165 bool
michael@0 166 BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx,
michael@0 167 JS::Handle<JSObject*> proxy,
michael@0 168 JS::Handle<jsid> id,
michael@0 169 MutableHandle<JSPropertyDescriptor> desc)
michael@0 170 {
michael@0 171 if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) {
michael@0 172 return false;
michael@0 173 }
michael@0 174 if (desc.object()) {
michael@0 175 return true;
michael@0 176 }
michael@0 177
michael@0 178 JS::Rooted<JSObject*> proto(cx);
michael@0 179 if (!js::GetObjectProto(cx, proxy, &proto)) {
michael@0 180 return false;
michael@0 181 }
michael@0 182 if (!proto) {
michael@0 183 desc.object().set(nullptr);
michael@0 184 return true;
michael@0 185 }
michael@0 186
michael@0 187 return JS_GetPropertyDescriptorById(cx, proto, id, desc);
michael@0 188 }
michael@0 189
michael@0 190 bool
michael@0 191 BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
michael@0 192 JS::Handle<JSObject*> proxy,
michael@0 193 JS::Handle<jsid> id,
michael@0 194 MutableHandle<JSPropertyDescriptor> desc)
michael@0 195 {
michael@0 196 return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false,
michael@0 197 desc);
michael@0 198 }
michael@0 199
michael@0 200 bool
michael@0 201 DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
michael@0 202 MutableHandle<JSPropertyDescriptor> desc, bool* defined)
michael@0 203 {
michael@0 204 if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) {
michael@0 205 return JS_ReportErrorFlagsAndNumber(cx,
michael@0 206 JSREPORT_WARNING | JSREPORT_STRICT |
michael@0 207 JSREPORT_STRICT_MODE_ERROR,
michael@0 208 js_GetErrorMessage, nullptr,
michael@0 209 JSMSG_GETTER_ONLY);
michael@0 210 }
michael@0 211
michael@0 212 if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
michael@0 213 return true;
michael@0 214 }
michael@0 215
michael@0 216 JSObject* expando = EnsureExpandoObject(cx, proxy);
michael@0 217 if (!expando) {
michael@0 218 return false;
michael@0 219 }
michael@0 220
michael@0 221 bool dummy;
michael@0 222 return js_DefineOwnProperty(cx, expando, id, desc, &dummy);
michael@0 223 }
michael@0 224
michael@0 225 bool
michael@0 226 DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<JSObject*> receiver,
michael@0 227 Handle<jsid> id, bool strict, MutableHandle<JS::Value> vp)
michael@0 228 {
michael@0 229 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
michael@0 230 "Should not have a XrayWrapper here");
michael@0 231 bool done;
michael@0 232 if (!setCustom(cx, proxy, id, vp, &done)) {
michael@0 233 return false;
michael@0 234 }
michael@0 235 if (done) {
michael@0 236 return true;
michael@0 237 }
michael@0 238
michael@0 239 // Make sure to ignore our named properties when checking for own
michael@0 240 // property descriptors for a set.
michael@0 241 JS::Rooted<JSPropertyDescriptor> desc(cx);
michael@0 242 if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true,
michael@0 243 &desc)) {
michael@0 244 return false;
michael@0 245 }
michael@0 246 bool descIsOwn = desc.object() != nullptr;
michael@0 247 if (!desc.object()) {
michael@0 248 // Don't just use getPropertyDescriptor, unlike BaseProxyHandler::set,
michael@0 249 // because that would call getOwnPropertyDescriptor on ourselves. Instead,
michael@0 250 // directly delegate to the proto, if any.
michael@0 251 JS::Rooted<JSObject*> proto(cx);
michael@0 252 if (!js::GetObjectProto(cx, proxy, &proto)) {
michael@0 253 return false;
michael@0 254 }
michael@0 255 if (proto && !JS_GetPropertyDescriptorById(cx, proto, id, &desc)) {
michael@0 256 return false;
michael@0 257 }
michael@0 258 }
michael@0 259
michael@0 260 return js::SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id,
michael@0 261 &desc, descIsOwn, strict, vp);
michael@0 262 }
michael@0 263
michael@0 264 bool
michael@0 265 DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
michael@0 266 JS::Handle<jsid> id, bool* bp)
michael@0 267 {
michael@0 268 JS::Rooted<JSObject*> expando(cx);
michael@0 269 if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
michael@0 270 return JS_DeletePropertyById2(cx, expando, id, bp);
michael@0 271 }
michael@0 272
michael@0 273 *bp = true;
michael@0 274 return true;
michael@0 275 }
michael@0 276
michael@0 277 bool
michael@0 278 BaseDOMProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
michael@0 279 AutoIdVector& props)
michael@0 280 {
michael@0 281 JS::Rooted<JSObject*> proto(cx);
michael@0 282 if (!JS_GetPrototype(cx, proxy, &proto)) {
michael@0 283 return false;
michael@0 284 }
michael@0 285 return keys(cx, proxy, props) &&
michael@0 286 (!proto || js::GetPropertyNames(cx, proto, 0, &props));
michael@0 287 }
michael@0 288
michael@0 289 bool
michael@0 290 BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
michael@0 291 JS::Handle<JSObject*> callable)
michael@0 292 {
michael@0 293 return js::WatchGuts(cx, proxy, id, callable);
michael@0 294 }
michael@0 295
michael@0 296 bool
michael@0 297 BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
michael@0 298 {
michael@0 299 return js::UnwatchGuts(cx, proxy, id);
michael@0 300 }
michael@0 301
michael@0 302 bool
michael@0 303 BaseDOMProxyHandler::getOwnPropertyNames(JSContext* cx,
michael@0 304 JS::Handle<JSObject*> proxy,
michael@0 305 JS::AutoIdVector& props)
michael@0 306 {
michael@0 307 return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props);
michael@0 308 }
michael@0 309
michael@0 310 bool
michael@0 311 BaseDOMProxyHandler::keys(JSContext* cx,
michael@0 312 JS::Handle<JSObject*> proxy,
michael@0 313 JS::AutoIdVector& props)
michael@0 314 {
michael@0 315 return ownPropNames(cx, proxy, JSITER_OWNONLY, props);
michael@0 316 }
michael@0 317
michael@0 318 bool
michael@0 319 DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp)
michael@0 320 {
michael@0 321 if (!hasOwn(cx, proxy, id, bp)) {
michael@0 322 return false;
michael@0 323 }
michael@0 324
michael@0 325 if (*bp) {
michael@0 326 // We have the property ourselves; no need to worry about our prototype
michael@0 327 // chain.
michael@0 328 return true;
michael@0 329 }
michael@0 330
michael@0 331 // OK, now we have to look at the proto
michael@0 332 JS::Rooted<JSObject*> proto(cx);
michael@0 333 if (!js::GetObjectProto(cx, proxy, &proto)) {
michael@0 334 return false;
michael@0 335 }
michael@0 336 if (!proto) {
michael@0 337 return true;
michael@0 338 }
michael@0 339 bool protoHasProp;
michael@0 340 bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
michael@0 341 if (ok) {
michael@0 342 *bp = protoHasProp;
michael@0 343 }
michael@0 344 return ok;
michael@0 345 }
michael@0 346
michael@0 347 int32_t
michael@0 348 IdToInt32(JSContext* cx, JS::Handle<jsid> id)
michael@0 349 {
michael@0 350 JS::Rooted<JS::Value> idval(cx);
michael@0 351 double array_index;
michael@0 352 int32_t i;
michael@0 353 if (!::JS_IdToValue(cx, id, &idval) ||
michael@0 354 !JS::ToNumber(cx, idval, &array_index) ||
michael@0 355 !::JS_DoubleIsInt32(array_index, &i)) {
michael@0 356 return -1;
michael@0 357 }
michael@0 358
michael@0 359 return i;
michael@0 360 }
michael@0 361
michael@0 362 bool
michael@0 363 DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
michael@0 364 JS::MutableHandle<JS::Value> vp, bool *done)
michael@0 365 {
michael@0 366 *done = false;
michael@0 367 return true;
michael@0 368 }
michael@0 369
michael@0 370 } // namespace dom
michael@0 371 } // namespace mozilla

mercurial