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.

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

mercurial