dom/bindings/DOMJSProxyHandler.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial