js/xpconnect/wrappers/ChromeObjectWrapper.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
     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/. */
     7 #include "ChromeObjectWrapper.h"
     8 #include "jsapi.h"
    10 using namespace JS;
    12 namespace xpc {
    14 // When creating wrappers for chrome objects in content, we detect if the
    15 // prototype of the wrapped chrome object is a prototype for a standard class
    16 // (like Array.prototype). If it is, we use the corresponding standard prototype
    17 // from the wrapper's scope, rather than the wrapped standard prototype
    18 // from the wrappee's scope.
    19 //
    20 // One of the reasons for doing this is to allow standard operations like
    21 // chromeArray.forEach(..) to Just Work without explicitly listing them in
    22 // __exposedProps__. Since proxies don't automatically inherit behavior from
    23 // their prototype, we have to instrument the traps to do this manually.
    24 ChromeObjectWrapper ChromeObjectWrapper::singleton;
    26 using js::assertEnteredPolicy;
    28 static bool
    29 AllowedByBase(JSContext *cx, HandleObject wrapper, HandleId id,
    30               js::Wrapper::Action act)
    31 {
    32     MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) ==
    33                &ChromeObjectWrapper::singleton);
    34     bool bp;
    35     ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton;
    36     return handler->ChromeObjectWrapperBase::enter(cx, wrapper, id, act, &bp);
    37 }
    39 static bool
    40 PropIsFromStandardPrototype(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc)
    41 {
    42     MOZ_ASSERT(desc.object());
    43     RootedObject unwrapped(cx, js::UncheckedUnwrap(desc.object()));
    44     JSAutoCompartment ac(cx, unwrapped);
    45     return IdentifyStandardPrototype(unwrapped) != JSProto_Null;
    46 }
    48 // Note that we're past the policy enforcement stage, here, so we can query
    49 // ChromeObjectWrapperBase and get an unfiltered view of the underlying object.
    50 // This lets us determine whether the property we would have found (given a
    51 // transparent wrapper) would have come off a standard prototype.
    52 static bool
    53 PropIsFromStandardPrototype(JSContext *cx, HandleObject wrapper,
    54                             HandleId id)
    55 {
    56     MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) ==
    57                &ChromeObjectWrapper::singleton);
    58     Rooted<JSPropertyDescriptor> desc(cx);
    59     ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton;
    60     if (!handler->ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id,
    61                                                                  &desc) ||
    62         !desc.object())
    63     {
    64         return false;
    65     }
    66     return PropIsFromStandardPrototype(cx, &desc);
    67 }
    69 bool
    70 ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx,
    71                                            HandleObject wrapper,
    72                                            HandleId id,
    73                                            JS::MutableHandle<JSPropertyDescriptor> desc)
    74 {
    75     assertEnteredPolicy(cx, wrapper, id, GET | SET);
    76     // First, try a lookup on the base wrapper if permitted.
    77     desc.object().set(nullptr);
    78     if (AllowedByBase(cx, wrapper, id, Wrapper::GET) &&
    79         !ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id,
    80                                                         desc)) {
    81         return false;
    82     }
    84     // If the property is something that can be found on a standard prototype,
    85     // prefer the one we'll get via the prototype chain in the content
    86     // compartment.
    87     if (desc.object() && PropIsFromStandardPrototype(cx, desc))
    88         desc.object().set(nullptr);
    90     // If we found something or have no proto, we're done.
    91     RootedObject wrapperProto(cx);
    92     if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
    93       return false;
    94     if (desc.object() || !wrapperProto)
    95         return true;
    97     // If not, try doing the lookup on the prototype.
    98     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
    99     return JS_GetPropertyDescriptorById(cx, wrapperProto, id, desc);
   100 }
   102 bool
   103 ChromeObjectWrapper::has(JSContext *cx, HandleObject wrapper,
   104                          HandleId id, bool *bp)
   105 {
   106     assertEnteredPolicy(cx, wrapper, id, GET);
   107     // Try the lookup on the base wrapper if permitted.
   108     if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) &&
   109         !ChromeObjectWrapperBase::has(cx, wrapper, id, bp))
   110     {
   111         return false;
   112     }
   114     // If we found something or have no prototype, we're done.
   115     RootedObject wrapperProto(cx);
   116     if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
   117         return false;
   118     if (*bp || !wrapperProto)
   119         return true;
   121     // Try the prototype if that failed.
   122     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
   123     Rooted<JSPropertyDescriptor> desc(cx);
   124     if (!JS_GetPropertyDescriptorById(cx, wrapperProto, id, &desc))
   125         return false;
   126     *bp = !!desc.object();
   127     return true;
   128 }
   130 bool
   131 ChromeObjectWrapper::get(JSContext *cx, HandleObject wrapper,
   132                          HandleObject receiver, HandleId id,
   133                          MutableHandleValue vp)
   134 {
   135     assertEnteredPolicy(cx, wrapper, id, GET);
   136     vp.setUndefined();
   137     // Only call through to the get trap on the underlying object if we're
   138     // allowed to see the property, and if what we'll find is not on a standard
   139     // prototype.
   140     if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) &&
   141         !PropIsFromStandardPrototype(cx, wrapper, id))
   142     {
   143         // Call the get trap.
   144         if (!ChromeObjectWrapperBase::get(cx, wrapper, receiver, id, vp))
   145             return false;
   146         // If we found something, we're done.
   147         if (!vp.isUndefined())
   148             return true;
   149     }
   151     // If we have no proto, we're done.
   152     RootedObject wrapperProto(cx);
   153     if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
   154         return false;
   155     if (!wrapperProto)
   156         return true;
   158     // Try the prototype.
   159     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
   160     return js::GetGeneric(cx, wrapperProto, receiver, id, vp.address());
   161 }
   163 // SecurityWrapper categorically returns false for objectClassIs, but the
   164 // contacts API depends on Array.isArray returning true for COW-implemented
   165 // contacts. This isn't really ideal, but make it work for now.
   166 bool
   167 ChromeObjectWrapper::objectClassIs(HandleObject obj, js::ESClassValue classValue,
   168                                    JSContext *cx)
   169 {
   170   return CrossCompartmentWrapper::objectClassIs(obj, classValue, cx);
   171 }
   173 // This mechanism isn't ideal because we end up calling enter() on the base class
   174 // twice (once during enter() here and once during the trap itself), and policy
   175 // enforcement or COWs isn't cheap. But it results in the cleanest code, and this
   176 // whole proto remapping thing for COWs is going to be phased out anyway.
   177 bool
   178 ChromeObjectWrapper::enter(JSContext *cx, HandleObject wrapper,
   179                            HandleId id, js::Wrapper::Action act, bool *bp)
   180 {
   181     if (AllowedByBase(cx, wrapper, id, act))
   182         return true;
   183     // COWs fail silently for GETs, and that also happens to be the only case
   184     // where we might want to redirect the lookup to the home prototype chain.
   185     *bp = act == Wrapper::GET || act == Wrapper::ENUMERATE;
   186     if (!*bp || id == JSID_VOID)
   187         return false;
   189     // Note that PropIsFromStandardPrototype needs to invoke getPropertyDescriptor
   190     // before we've fully entered the policy. Waive our policy.
   191     js::AutoWaivePolicy policy(cx, wrapper, id, act);
   192     return PropIsFromStandardPrototype(cx, wrapper, id);
   193 }
   195 }

mercurial