1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/wrappers/XrayWrapper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2292 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ 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 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "XrayWrapper.h" 1.11 +#include "AccessCheck.h" 1.12 +#include "WrapperFactory.h" 1.13 + 1.14 +#include "nsIContent.h" 1.15 +#include "nsIControllers.h" 1.16 +#include "nsContentUtils.h" 1.17 + 1.18 +#include "XPCWrapper.h" 1.19 +#include "xpcprivate.h" 1.20 + 1.21 +#include "jsapi.h" 1.22 +#include "jsprf.h" 1.23 +#include "nsJSUtils.h" 1.24 + 1.25 +#include "mozilla/dom/BindingUtils.h" 1.26 +#include "mozilla/dom/WindowBinding.h" 1.27 +#include "nsGlobalWindow.h" 1.28 + 1.29 +using namespace mozilla::dom; 1.30 +using namespace JS; 1.31 +using namespace mozilla; 1.32 + 1.33 +using js::Wrapper; 1.34 +using js::BaseProxyHandler; 1.35 +using js::IsCrossCompartmentWrapper; 1.36 +using js::UncheckedUnwrap; 1.37 +using js::CheckedUnwrap; 1.38 + 1.39 +namespace xpc { 1.40 + 1.41 +using namespace XrayUtils; 1.42 + 1.43 +// Whitelist for the standard ES classes we can Xray to. 1.44 +static bool 1.45 +IsJSXraySupported(JSProtoKey key) 1.46 +{ 1.47 + switch (key) { 1.48 + case JSProto_Date: 1.49 + return true; 1.50 + default: 1.51 + return false; 1.52 + } 1.53 +} 1.54 + 1.55 +XrayType 1.56 +GetXrayType(JSObject *obj) 1.57 +{ 1.58 + obj = js::UncheckedUnwrap(obj, /* stopAtOuter = */ false); 1.59 + if (mozilla::dom::UseDOMXray(obj)) 1.60 + return XrayForDOMObject; 1.61 + 1.62 + const js::Class* clasp = js::GetObjectClass(obj); 1.63 + if (IS_WN_CLASS(clasp) || clasp->ext.innerObject) 1.64 + return XrayForWrappedNative; 1.65 + 1.66 + JSProtoKey standardProto = IdentifyStandardInstanceOrPrototype(obj); 1.67 + if (IsJSXraySupported(standardProto)) 1.68 + return XrayForJSObject; 1.69 + 1.70 + return NotXray; 1.71 +} 1.72 + 1.73 +JSObject * 1.74 +XrayAwareCalleeGlobal(JSObject *fun) 1.75 +{ 1.76 + MOZ_ASSERT(js::IsFunctionObject(fun)); 1.77 + JSObject *scope = js::GetObjectParent(fun); 1.78 + if (IsXrayWrapper(scope)) 1.79 + scope = js::UncheckedUnwrap(scope); 1.80 + return js::GetGlobalForObjectCrossCompartment(scope); 1.81 +} 1.82 + 1.83 +const uint32_t JSSLOT_RESOLVING = 0; 1.84 +ResolvingId::ResolvingId(JSContext *cx, HandleObject wrapper, HandleId id) 1.85 + : mId(id), 1.86 + mHolder(cx, getHolderObject(wrapper)), 1.87 + mPrev(getResolvingId(mHolder)), 1.88 + mXrayShadowing(false) 1.89 +{ 1.90 + js::SetReservedSlot(mHolder, JSSLOT_RESOLVING, js::PrivateValue(this)); 1.91 +} 1.92 + 1.93 +ResolvingId::~ResolvingId() 1.94 +{ 1.95 + MOZ_ASSERT(getResolvingId(mHolder) == this, "unbalanced ResolvingIds"); 1.96 + js::SetReservedSlot(mHolder, JSSLOT_RESOLVING, js::PrivateValue(mPrev)); 1.97 +} 1.98 + 1.99 +bool 1.100 +ResolvingId::isXrayShadowing(jsid id) 1.101 +{ 1.102 + if (!mXrayShadowing) 1.103 + return false; 1.104 + 1.105 + return mId == id; 1.106 +} 1.107 + 1.108 +bool 1.109 +ResolvingId::isResolving(jsid id) 1.110 +{ 1.111 + for (ResolvingId *cur = this; cur; cur = cur->mPrev) { 1.112 + if (cur->mId == id) 1.113 + return true; 1.114 + } 1.115 + 1.116 + return false; 1.117 +} 1.118 + 1.119 +ResolvingId * 1.120 +ResolvingId::getResolvingId(JSObject *holder) 1.121 +{ 1.122 + MOZ_ASSERT(strcmp(JS_GetClass(holder)->name, "NativePropertyHolder") == 0); 1.123 + return (ResolvingId *)js::GetReservedSlot(holder, JSSLOT_RESOLVING).toPrivate(); 1.124 +} 1.125 + 1.126 +JSObject * 1.127 +ResolvingId::getHolderObject(JSObject *wrapper) 1.128 +{ 1.129 + return &js::GetProxyExtra(wrapper, 0).toObject(); 1.130 +} 1.131 + 1.132 +ResolvingId * 1.133 +ResolvingId::getResolvingIdFromWrapper(JSObject *wrapper) 1.134 +{ 1.135 + return getResolvingId(getHolderObject(wrapper)); 1.136 +} 1.137 + 1.138 +class MOZ_STACK_CLASS ResolvingIdDummy 1.139 +{ 1.140 +public: 1.141 + ResolvingIdDummy(JSContext *cx, HandleObject wrapper, HandleId id) 1.142 + { 1.143 + } 1.144 +}; 1.145 + 1.146 +class XrayTraits 1.147 +{ 1.148 +public: 1.149 + static JSObject* getTargetObject(JSObject *wrapper) { 1.150 + return js::UncheckedUnwrap(wrapper, /* stopAtOuter = */ false); 1.151 + } 1.152 + 1.153 + virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, 1.154 + HandleObject holder, HandleId id, 1.155 + MutableHandle<JSPropertyDescriptor> desc) = 0; 1.156 + // NB: resolveOwnProperty may decide whether or not to cache what it finds 1.157 + // on the holder. If the result is not cached, the lookup will happen afresh 1.158 + // for each access, which is the right thing for things like dynamic NodeList 1.159 + // properties. 1.160 + virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, 1.161 + HandleObject wrapper, HandleObject holder, 1.162 + HandleId id, MutableHandle<JSPropertyDescriptor> desc); 1.163 + 1.164 + virtual void preserveWrapper(JSObject *target) = 0; 1.165 + 1.166 + static bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, 1.167 + bool strict, MutableHandleValue vp); 1.168 + 1.169 + JSObject* getExpandoObject(JSContext *cx, HandleObject target, 1.170 + HandleObject consumer); 1.171 + JSObject* ensureExpandoObject(JSContext *cx, HandleObject wrapper, 1.172 + HandleObject target); 1.173 + 1.174 + JSObject* getHolder(JSObject *wrapper); 1.175 + JSObject* ensureHolder(JSContext *cx, HandleObject wrapper); 1.176 + virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) = 0; 1.177 + 1.178 + JSObject* getExpandoChain(HandleObject obj) { 1.179 + return GetObjectScope(obj)->GetExpandoChain(obj); 1.180 + } 1.181 + 1.182 + bool setExpandoChain(JSContext *cx, HandleObject obj, HandleObject chain) { 1.183 + return GetObjectScope(obj)->SetExpandoChain(cx, obj, chain); 1.184 + } 1.185 + bool cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src); 1.186 + 1.187 +private: 1.188 + bool expandoObjectMatchesConsumer(JSContext *cx, HandleObject expandoObject, 1.189 + nsIPrincipal *consumerOrigin, 1.190 + HandleObject exclusiveGlobal); 1.191 + JSObject* getExpandoObjectInternal(JSContext *cx, HandleObject target, 1.192 + nsIPrincipal *origin, 1.193 + JSObject *exclusiveGlobal); 1.194 + JSObject* attachExpandoObject(JSContext *cx, HandleObject target, 1.195 + nsIPrincipal *origin, 1.196 + HandleObject exclusiveGlobal); 1.197 +}; 1.198 + 1.199 +class XPCWrappedNativeXrayTraits : public XrayTraits 1.200 +{ 1.201 +public: 1.202 + enum { 1.203 + HasPrototype = 0 1.204 + }; 1.205 + 1.206 + static const XrayType Type = XrayForWrappedNative; 1.207 + 1.208 + virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, 1.209 + HandleObject holder, HandleId id, 1.210 + MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; 1.211 + virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, 1.212 + HandleObject holder, HandleId id, 1.213 + MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; 1.214 + static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, 1.215 + MutableHandle<JSPropertyDescriptor> desc, 1.216 + Handle<JSPropertyDescriptor> existingDesc, bool *defined); 1.217 + virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, 1.218 + AutoIdVector &props); 1.219 + static bool call(JSContext *cx, HandleObject wrapper, 1.220 + const JS::CallArgs &args, js::Wrapper& baseInstance); 1.221 + static bool construct(JSContext *cx, HandleObject wrapper, 1.222 + const JS::CallArgs &args, js::Wrapper& baseInstance); 1.223 + 1.224 + static bool isResolving(JSContext *cx, JSObject *holder, jsid id); 1.225 + 1.226 + static bool resolveDOMCollectionProperty(JSContext *cx, HandleObject wrapper, 1.227 + HandleObject holder, HandleId id, 1.228 + MutableHandle<JSPropertyDescriptor> desc); 1.229 + 1.230 + static XPCWrappedNative* getWN(JSObject *wrapper) { 1.231 + return XPCWrappedNative::Get(getTargetObject(wrapper)); 1.232 + } 1.233 + 1.234 + virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE; 1.235 + 1.236 + typedef ResolvingId ResolvingIdImpl; 1.237 + 1.238 + virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; 1.239 + 1.240 + static const JSClass HolderClass; 1.241 + static XPCWrappedNativeXrayTraits singleton; 1.242 +}; 1.243 + 1.244 +const JSClass XPCWrappedNativeXrayTraits::HolderClass = { 1.245 + "NativePropertyHolder", JSCLASS_HAS_RESERVED_SLOTS(2), 1.246 + JS_PropertyStub, JS_DeletePropertyStub, holder_get, holder_set, 1.247 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.248 +}; 1.249 + 1.250 +class DOMXrayTraits : public XrayTraits 1.251 +{ 1.252 +public: 1.253 + enum { 1.254 + HasPrototype = 0 1.255 + }; 1.256 + 1.257 + static const XrayType Type = XrayForDOMObject; 1.258 + 1.259 + virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, 1.260 + HandleObject holder, HandleId id, 1.261 + MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; 1.262 + virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, 1.263 + HandleObject holder, HandleId id, 1.264 + MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; 1.265 + static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, 1.266 + MutableHandle<JSPropertyDescriptor> desc, 1.267 + Handle<JSPropertyDescriptor> existingDesc, bool *defined); 1.268 + static bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, 1.269 + bool strict, MutableHandleValue vp); 1.270 + virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, 1.271 + AutoIdVector &props); 1.272 + static bool call(JSContext *cx, HandleObject wrapper, 1.273 + const JS::CallArgs &args, js::Wrapper& baseInstance); 1.274 + static bool construct(JSContext *cx, HandleObject wrapper, 1.275 + const JS::CallArgs &args, js::Wrapper& baseInstance); 1.276 + 1.277 + static bool isResolving(JSContext *cx, JSObject *holder, jsid id) 1.278 + { 1.279 + return false; 1.280 + } 1.281 + 1.282 + typedef ResolvingIdDummy ResolvingIdImpl; 1.283 + 1.284 + virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE; 1.285 + 1.286 + virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; 1.287 + 1.288 + static DOMXrayTraits singleton; 1.289 +}; 1.290 + 1.291 +class JSXrayTraits : public XrayTraits 1.292 +{ 1.293 +public: 1.294 + enum { 1.295 + HasPrototype = 1 1.296 + }; 1.297 + static const XrayType Type = XrayForJSObject; 1.298 + 1.299 + virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, 1.300 + HandleObject holder, HandleId id, 1.301 + MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE 1.302 + { 1.303 + MOZ_ASSUME_UNREACHABLE("resolveNativeProperty hook should never be called with HasPrototype = 1"); 1.304 + } 1.305 + 1.306 + virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, 1.307 + HandleObject holder, HandleId id, 1.308 + MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; 1.309 + 1.310 + static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, 1.311 + MutableHandle<JSPropertyDescriptor> desc, 1.312 + Handle<JSPropertyDescriptor> existingDesc, bool *defined) 1.313 + { 1.314 + // There's no useful per-trait work to do here. Punt back up to the common code. 1.315 + *defined = false; 1.316 + return true; 1.317 + } 1.318 + 1.319 + virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, 1.320 + AutoIdVector &props); 1.321 + 1.322 + static bool call(JSContext *cx, HandleObject wrapper, 1.323 + const JS::CallArgs &args, js::Wrapper& baseInstance) 1.324 + { 1.325 + // We'll handle this when we start supporting Functions. 1.326 + RootedValue v(cx, ObjectValue(*wrapper)); 1.327 + js_ReportIsNotFunction(cx, v); 1.328 + return false; 1.329 + } 1.330 + 1.331 + static bool construct(JSContext *cx, HandleObject wrapper, 1.332 + const JS::CallArgs &args, js::Wrapper& baseInstance) 1.333 + { 1.334 + // We'll handle this when we start supporting Functions. 1.335 + RootedValue v(cx, ObjectValue(*wrapper)); 1.336 + js_ReportIsNotFunction(cx, v); 1.337 + return false; 1.338 + } 1.339 + 1.340 + static bool isResolving(JSContext *cx, JSObject *holder, jsid id) 1.341 + { 1.342 + return false; 1.343 + } 1.344 + 1.345 + typedef ResolvingIdDummy ResolvingIdImpl; 1.346 + 1.347 + bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, 1.348 + JS::HandleObject target, 1.349 + JS::MutableHandleObject protop) 1.350 + { 1.351 + RootedObject holder(cx, ensureHolder(cx, wrapper)); 1.352 + JSProtoKey key = isPrototype(holder) ? JSProto_Object 1.353 + : getProtoKey(holder); 1.354 + { 1.355 + JSAutoCompartment ac(cx, target); 1.356 + if (!JS_GetClassPrototype(cx, key, protop)) 1.357 + return nullptr; 1.358 + } 1.359 + return JS_WrapObject(cx, protop); 1.360 + } 1.361 + 1.362 + virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE { 1.363 + // In the case of pure JS objects, there is no underlying object, and 1.364 + // the target is the canonical representation of state. If it gets 1.365 + // collected, then expandos and such should be collected too. So there's 1.366 + // nothing to do here. 1.367 + } 1.368 + 1.369 + enum { 1.370 + SLOT_PROTOKEY = 0, 1.371 + SLOT_ISPROTOTYPE, 1.372 + SLOT_COUNT 1.373 + }; 1.374 + virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; 1.375 + 1.376 + static JSProtoKey getProtoKey(JSObject *holder) { 1.377 + int32_t key = js::GetReservedSlot(holder, SLOT_PROTOKEY).toInt32(); 1.378 + return static_cast<JSProtoKey>(key); 1.379 + } 1.380 + 1.381 + static bool isPrototype(JSObject *holder) { 1.382 + return js::GetReservedSlot(holder, SLOT_ISPROTOTYPE).toBoolean(); 1.383 + } 1.384 + 1.385 + static const JSClass HolderClass; 1.386 + static JSXrayTraits singleton; 1.387 +}; 1.388 + 1.389 +const JSClass JSXrayTraits::HolderClass = { 1.390 + "JSXrayHolder", JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), 1.391 + JS_PropertyStub, JS_DeletePropertyStub, 1.392 + JS_PropertyStub, JS_StrictPropertyStub, 1.393 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.394 +}; 1.395 + 1.396 +bool 1.397 +JSXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, 1.398 + HandleObject wrapper, HandleObject holder, 1.399 + HandleId id, 1.400 + MutableHandle<JSPropertyDescriptor> desc) 1.401 +{ 1.402 + // Call the common code. 1.403 + bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, 1.404 + id, desc); 1.405 + if (!ok || desc.object()) 1.406 + return ok; 1.407 + 1.408 + // Non-prototypes don't have anything on them yet. 1.409 + if (!isPrototype(holder)) 1.410 + return true; 1.411 + 1.412 + // The non-HasPrototypes semantics implemented by traditional Xrays are kind 1.413 + // of broken with respect to |own|-ness and the holder. The common code 1.414 + // muddles through by only checking the holder for non-|own| lookups, but 1.415 + // that doesn't work for us. So we do an explicit holder check here, and hope 1.416 + // that this mess gets fixed up soon. 1.417 + if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) 1.418 + return false; 1.419 + if (desc.object()) { 1.420 + desc.object().set(wrapper); 1.421 + return true; 1.422 + } 1.423 + 1.424 + // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. 1.425 + RootedObject target(cx, getTargetObject(wrapper)); 1.426 + const js::Class *clasp = js::GetObjectClass(target); 1.427 + JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); 1.428 + MOZ_ASSERT(protoKey == getProtoKey(holder)); 1.429 + MOZ_ASSERT(clasp->spec.defined()); 1.430 + 1.431 + // Handle the 'constructor' property. 1.432 + if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) { 1.433 + RootedObject constructor(cx); 1.434 + { 1.435 + JSAutoCompartment ac(cx, target); 1.436 + if (!JS_GetClassObject(cx, protoKey, &constructor)) 1.437 + return false; 1.438 + } 1.439 + if (!JS_WrapObject(cx, &constructor)) 1.440 + return false; 1.441 + desc.object().set(wrapper); 1.442 + desc.setAttributes(0); 1.443 + desc.setGetter(nullptr); 1.444 + desc.setSetter(nullptr); 1.445 + desc.value().setObject(*constructor); 1.446 + return true; 1.447 + } 1.448 + 1.449 + // Find the properties available, if any. 1.450 + const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; 1.451 + if (!fs) 1.452 + return true; 1.453 + 1.454 + // Compute the property name we're looking for. We'll handle indexed 1.455 + // properties when we start supporting arrays. 1.456 + if (!JSID_IS_STRING(id)) 1.457 + return true; 1.458 + Rooted<JSFlatString*> str(cx, JSID_TO_FLAT_STRING(id)); 1.459 + 1.460 + // Scan through the properties. If we don't find anything, we're done. 1.461 + for (; fs->name; ++fs) { 1.462 + // We don't support self-hosted functions yet. See bug 972987. 1.463 + if (fs->selfHostedName) 1.464 + continue; 1.465 + if (JS_FlatStringEqualsAscii(str, fs->name)) 1.466 + break; 1.467 + } 1.468 + if (!fs->name) 1.469 + return true; 1.470 + 1.471 + // Generate an Xrayed version of the method. 1.472 + Rooted<JSFunction*> fun(cx, JS_NewFunctionById(cx, fs->call.op, fs->nargs, 1.473 + 0, wrapper, id)); 1.474 + if (!fun) 1.475 + return false; 1.476 + 1.477 + // The generic Xray machinery only defines non-own properties on the holder. 1.478 + // This is broken, and will be fixed at some point, but for now we need to 1.479 + // cache the value explicitly. See the corresponding call to 1.480 + // JS_GetPropertyById at the top of this function. 1.481 + return JS_DefinePropertyById(cx, holder, id, 1.482 + ObjectValue(*JS_GetFunctionObject(fun)), 1.483 + nullptr, nullptr, 0) && 1.484 + JS_GetPropertyDescriptorById(cx, holder, id, desc); 1.485 +} 1.486 + 1.487 +bool 1.488 +JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, 1.489 + AutoIdVector &props) 1.490 +{ 1.491 + RootedObject holder(cx, ensureHolder(cx, wrapper)); 1.492 + if (!holder) 1.493 + return false; 1.494 + 1.495 + // Non-prototypes don't have anything on them yet. 1.496 + if (!isPrototype(holder)) 1.497 + return true; 1.498 + 1.499 + // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. 1.500 + RootedObject target(cx, getTargetObject(wrapper)); 1.501 + const js::Class *clasp = js::GetObjectClass(target); 1.502 + MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == getProtoKey(holder)); 1.503 + MOZ_ASSERT(clasp->spec.defined()); 1.504 + 1.505 + // Find the properties available, if any. 1.506 + const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; 1.507 + if (!fs) 1.508 + return true; 1.509 + 1.510 + // Intern all the strings, and pass theme to the caller. 1.511 + for (; fs->name; ++fs) { 1.512 + // We don't support self-hosted functions yet. See bug 972987. 1.513 + if (fs->selfHostedName) 1.514 + continue; 1.515 + RootedString str(cx, JS_InternString(cx, fs->name)); 1.516 + if (!str) 1.517 + return false; 1.518 + if (!props.append(INTERNED_STRING_TO_JSID(cx, str))) 1.519 + return false; 1.520 + } 1.521 + 1.522 + // Add the 'constructor' property. 1.523 + return props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)); 1.524 +} 1.525 + 1.526 +JSObject* 1.527 +JSXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) 1.528 +{ 1.529 + RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); 1.530 + RootedObject target(cx, getTargetObject(wrapper)); 1.531 + RootedObject holder(cx, JS_NewObjectWithGivenProto(cx, &HolderClass, 1.532 + JS::NullPtr(), global)); 1.533 + if (!holder) 1.534 + return nullptr; 1.535 + 1.536 + // Compute information about the target. 1.537 + bool isPrototype = false; 1.538 + JSProtoKey key = IdentifyStandardInstance(target); 1.539 + if (key == JSProto_Null) { 1.540 + isPrototype = true; 1.541 + key = IdentifyStandardPrototype(target); 1.542 + } 1.543 + MOZ_ASSERT(key != JSProto_Null); 1.544 + 1.545 + // Store it on the holder. 1.546 + RootedValue v(cx); 1.547 + v.setNumber(static_cast<uint32_t>(key)); 1.548 + js::SetReservedSlot(holder, SLOT_PROTOKEY, v); 1.549 + v.setBoolean(isPrototype); 1.550 + js::SetReservedSlot(holder, SLOT_ISPROTOTYPE, v); 1.551 + 1.552 + return holder; 1.553 +} 1.554 + 1.555 +XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton; 1.556 +DOMXrayTraits DOMXrayTraits::singleton; 1.557 +JSXrayTraits JSXrayTraits::singleton; 1.558 + 1.559 +XrayTraits* 1.560 +GetXrayTraits(JSObject *obj) 1.561 +{ 1.562 + switch (GetXrayType(obj)) { 1.563 + case XrayForDOMObject: 1.564 + return &DOMXrayTraits::singleton; 1.565 + case XrayForWrappedNative: 1.566 + return &XPCWrappedNativeXrayTraits::singleton; 1.567 + case XrayForJSObject: 1.568 + return &JSXrayTraits::singleton; 1.569 + default: 1.570 + return nullptr; 1.571 + } 1.572 +} 1.573 + 1.574 +/* 1.575 + * Xray expando handling. 1.576 + * 1.577 + * We hang expandos for Xray wrappers off a reserved slot on the target object 1.578 + * so that same-origin compartments can share expandos for a given object. We 1.579 + * have a linked list of expando objects, one per origin. The properties on these 1.580 + * objects are generally wrappers pointing back to the compartment that applied 1.581 + * them. 1.582 + * 1.583 + * The expando objects should _never_ be exposed to script. The fact that they 1.584 + * live in the target compartment is a detail of the implementation, and does 1.585 + * not imply that code in the target compartment should be allowed to inspect 1.586 + * them. They are private to the origin that placed them. 1.587 + */ 1.588 + 1.589 +enum ExpandoSlots { 1.590 + JSSLOT_EXPANDO_NEXT = 0, 1.591 + JSSLOT_EXPANDO_ORIGIN, 1.592 + JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, 1.593 + JSSLOT_EXPANDO_PROTOTYPE, 1.594 + JSSLOT_EXPANDO_COUNT 1.595 +}; 1.596 + 1.597 +static nsIPrincipal* 1.598 +ObjectPrincipal(JSObject *obj) 1.599 +{ 1.600 + return GetCompartmentPrincipal(js::GetObjectCompartment(obj)); 1.601 +} 1.602 + 1.603 +static nsIPrincipal* 1.604 +GetExpandoObjectPrincipal(JSObject *expandoObject) 1.605 +{ 1.606 + Value v = JS_GetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN); 1.607 + return static_cast<nsIPrincipal*>(v.toPrivate()); 1.608 +} 1.609 + 1.610 +static void 1.611 +ExpandoObjectFinalize(JSFreeOp *fop, JSObject *obj) 1.612 +{ 1.613 + // Release the principal. 1.614 + nsIPrincipal *principal = GetExpandoObjectPrincipal(obj); 1.615 + NS_RELEASE(principal); 1.616 +} 1.617 + 1.618 +const JSClass ExpandoObjectClass = { 1.619 + "XrayExpandoObject", 1.620 + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT), 1.621 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.622 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ExpandoObjectFinalize 1.623 +}; 1.624 + 1.625 +bool 1.626 +XrayTraits::expandoObjectMatchesConsumer(JSContext *cx, 1.627 + HandleObject expandoObject, 1.628 + nsIPrincipal *consumerOrigin, 1.629 + HandleObject exclusiveGlobal) 1.630 +{ 1.631 + MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx)); 1.632 + 1.633 + // First, compare the principals. 1.634 + nsIPrincipal *o = GetExpandoObjectPrincipal(expandoObject); 1.635 + // Note that it's very important here to ignore document.domain. We 1.636 + // pull the principal for the expando object off of the first consumer 1.637 + // for a given origin, and freely share the expandos amongst multiple 1.638 + // same-origin consumers afterwards. However, this means that we have 1.639 + // no way to know whether _all_ consumers have opted in to collaboration 1.640 + // by explicitly setting document.domain. So we just mandate that expando 1.641 + // sharing is unaffected by it. 1.642 + if (!consumerOrigin->Equals(o)) 1.643 + return false; 1.644 + 1.645 + // Sandboxes want exclusive expando objects. 1.646 + JSObject *owner = JS_GetReservedSlot(expandoObject, 1.647 + JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL) 1.648 + .toObjectOrNull(); 1.649 + if (!owner && !exclusiveGlobal) 1.650 + return true; 1.651 + 1.652 + // The exclusive global should always be wrapped in the target's compartment. 1.653 + MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx)); 1.654 + MOZ_ASSERT(!owner || js::IsObjectInContextCompartment(owner, cx)); 1.655 + return owner == exclusiveGlobal; 1.656 +} 1.657 + 1.658 +JSObject * 1.659 +XrayTraits::getExpandoObjectInternal(JSContext *cx, HandleObject target, 1.660 + nsIPrincipal *origin, 1.661 + JSObject *exclusiveGlobalArg) 1.662 +{ 1.663 + // The expando object lives in the compartment of the target, so all our 1.664 + // work needs to happen there. 1.665 + RootedObject exclusiveGlobal(cx, exclusiveGlobalArg); 1.666 + JSAutoCompartment ac(cx, target); 1.667 + if (!JS_WrapObject(cx, &exclusiveGlobal)) 1.668 + return nullptr; 1.669 + 1.670 + // Iterate through the chain, looking for a same-origin object. 1.671 + RootedObject head(cx, getExpandoChain(target)); 1.672 + while (head) { 1.673 + if (expandoObjectMatchesConsumer(cx, head, origin, exclusiveGlobal)) 1.674 + return head; 1.675 + head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); 1.676 + } 1.677 + 1.678 + // Not found. 1.679 + return nullptr; 1.680 +} 1.681 + 1.682 +JSObject * 1.683 +XrayTraits::getExpandoObject(JSContext *cx, HandleObject target, HandleObject consumer) 1.684 +{ 1.685 + JSObject *consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer); 1.686 + bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox"); 1.687 + return getExpandoObjectInternal(cx, target, ObjectPrincipal(consumer), 1.688 + isSandbox ? consumerGlobal : nullptr); 1.689 +} 1.690 + 1.691 +JSObject * 1.692 +XrayTraits::attachExpandoObject(JSContext *cx, HandleObject target, 1.693 + nsIPrincipal *origin, HandleObject exclusiveGlobal) 1.694 +{ 1.695 + // Make sure the compartments are sane. 1.696 + MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx)); 1.697 + MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx)); 1.698 + 1.699 + // No duplicates allowed. 1.700 + MOZ_ASSERT(!getExpandoObjectInternal(cx, target, origin, exclusiveGlobal)); 1.701 + 1.702 + // Create the expando object. We parent it directly to the target object. 1.703 + RootedObject expandoObject(cx, JS_NewObjectWithGivenProto(cx, &ExpandoObjectClass, 1.704 + JS::NullPtr(), target)); 1.705 + if (!expandoObject) 1.706 + return nullptr; 1.707 + 1.708 + // AddRef and store the principal. 1.709 + NS_ADDREF(origin); 1.710 + JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, PRIVATE_TO_JSVAL(origin)); 1.711 + 1.712 + // Note the exclusive global, if any. 1.713 + JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, 1.714 + OBJECT_TO_JSVAL(exclusiveGlobal)); 1.715 + 1.716 + // If this is our first expando object, take the opportunity to preserve 1.717 + // the wrapper. This keeps our expandos alive even if the Xray wrapper gets 1.718 + // collected. 1.719 + RootedObject chain(cx, getExpandoChain(target)); 1.720 + if (!chain) 1.721 + preserveWrapper(target); 1.722 + 1.723 + // Insert it at the front of the chain. 1.724 + JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, OBJECT_TO_JSVAL(chain)); 1.725 + setExpandoChain(cx, target, expandoObject); 1.726 + 1.727 + return expandoObject; 1.728 +} 1.729 + 1.730 +JSObject * 1.731 +XrayTraits::ensureExpandoObject(JSContext *cx, HandleObject wrapper, 1.732 + HandleObject target) 1.733 +{ 1.734 + // Expando objects live in the target compartment. 1.735 + JSAutoCompartment ac(cx, target); 1.736 + JSObject *expandoObject = getExpandoObject(cx, target, wrapper); 1.737 + if (!expandoObject) { 1.738 + // If the object is a sandbox, we don't want it to share expandos with 1.739 + // anyone else, so we tag it with the sandbox global. 1.740 + // 1.741 + // NB: We first need to check the class, _then_ wrap for the target's 1.742 + // compartment. 1.743 + RootedObject consumerGlobal(cx, js::GetGlobalForObjectCrossCompartment(wrapper)); 1.744 + bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox"); 1.745 + if (!JS_WrapObject(cx, &consumerGlobal)) 1.746 + return nullptr; 1.747 + expandoObject = attachExpandoObject(cx, target, ObjectPrincipal(wrapper), 1.748 + isSandbox ? (HandleObject)consumerGlobal : NullPtr()); 1.749 + } 1.750 + return expandoObject; 1.751 +} 1.752 + 1.753 +bool 1.754 +XrayTraits::cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src) 1.755 +{ 1.756 + MOZ_ASSERT(js::IsObjectInContextCompartment(dst, cx)); 1.757 + MOZ_ASSERT(getExpandoChain(dst) == nullptr); 1.758 + 1.759 + RootedObject oldHead(cx, getExpandoChain(src)); 1.760 + while (oldHead) { 1.761 + RootedObject exclusive(cx, JS_GetReservedSlot(oldHead, 1.762 + JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL) 1.763 + .toObjectOrNull()); 1.764 + if (!JS_WrapObject(cx, &exclusive)) 1.765 + return false; 1.766 + RootedObject newHead(cx, attachExpandoObject(cx, dst, GetExpandoObjectPrincipal(oldHead), 1.767 + exclusive)); 1.768 + if (!JS_CopyPropertiesFrom(cx, newHead, oldHead)) 1.769 + return false; 1.770 + oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); 1.771 + } 1.772 + return true; 1.773 +} 1.774 + 1.775 +namespace XrayUtils { 1.776 +bool CloneExpandoChain(JSContext *cx, JSObject *dstArg, JSObject *srcArg) 1.777 +{ 1.778 + RootedObject dst(cx, dstArg); 1.779 + RootedObject src(cx, srcArg); 1.780 + return GetXrayTraits(src)->cloneExpandoChain(cx, dst, src); 1.781 +} 1.782 +} 1.783 + 1.784 +static JSObject * 1.785 +GetHolder(JSObject *obj) 1.786 +{ 1.787 + return &js::GetProxyExtra(obj, 0).toObject(); 1.788 +} 1.789 + 1.790 +JSObject* 1.791 +XrayTraits::getHolder(JSObject *wrapper) 1.792 +{ 1.793 + MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); 1.794 + js::Value v = js::GetProxyExtra(wrapper, 0); 1.795 + return v.isObject() ? &v.toObject() : nullptr; 1.796 +} 1.797 + 1.798 +JSObject* 1.799 +XrayTraits::ensureHolder(JSContext *cx, HandleObject wrapper) 1.800 +{ 1.801 + RootedObject holder(cx, getHolder(wrapper)); 1.802 + if (holder) 1.803 + return holder; 1.804 + holder = createHolder(cx, wrapper); // virtual trap. 1.805 + if (holder) 1.806 + js::SetProxyExtra(wrapper, 0, ObjectValue(*holder)); 1.807 + return holder; 1.808 +} 1.809 + 1.810 +bool 1.811 +XPCWrappedNativeXrayTraits::isResolving(JSContext *cx, JSObject *holder, 1.812 + jsid id) 1.813 +{ 1.814 + ResolvingId *cur = ResolvingId::getResolvingId(holder); 1.815 + if (!cur) 1.816 + return false; 1.817 + return cur->isResolving(id); 1.818 +} 1.819 + 1.820 +namespace XrayUtils { 1.821 + 1.822 +bool 1.823 +IsXPCWNHolderClass(const JSClass *clasp) 1.824 +{ 1.825 + return clasp == &XPCWrappedNativeXrayTraits::HolderClass; 1.826 +} 1.827 + 1.828 +} 1.829 + 1.830 + 1.831 +// Some DOM objects have shared properties that don't have an explicit 1.832 +// getter/setter and rely on the class getter/setter. We install a 1.833 +// class getter/setter on the holder object to trigger them. 1.834 +bool 1.835 +holder_get(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandleValue vp) 1.836 +{ 1.837 + // JSClass::getProperty is wacky enough that it's hard to be sure someone 1.838 + // can't inherit this getter by prototyping a random object to an 1.839 + // XrayWrapper. Be safe. 1.840 + NS_ENSURE_TRUE(WrapperFactory::IsXrayWrapper(wrapper), true); 1.841 + JSObject *holder = GetHolder(wrapper); 1.842 + 1.843 + XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); 1.844 + if (NATIVE_HAS_FLAG(wn, WantGetProperty)) { 1.845 + JSAutoCompartment ac(cx, holder); 1.846 + bool retval = true; 1.847 + nsresult rv = wn->GetScriptableCallback()->GetProperty(wn, cx, wrapper, 1.848 + id, vp.address(), &retval); 1.849 + if (NS_FAILED(rv) || !retval) { 1.850 + if (retval) 1.851 + XPCThrower::Throw(rv, cx); 1.852 + return false; 1.853 + } 1.854 + } 1.855 + return true; 1.856 +} 1.857 + 1.858 +bool 1.859 +holder_set(JSContext *cx, HandleObject wrapper, HandleId id, bool strict, MutableHandleValue vp) 1.860 +{ 1.861 + // JSClass::setProperty is wacky enough that it's hard to be sure someone 1.862 + // can't inherit this getter by prototyping a random object to an 1.863 + // XrayWrapper. Be safe. 1.864 + NS_ENSURE_TRUE(WrapperFactory::IsXrayWrapper(wrapper), true); 1.865 + JSObject *holder = GetHolder(wrapper); 1.866 + if (XPCWrappedNativeXrayTraits::isResolving(cx, holder, id)) { 1.867 + return true; 1.868 + } 1.869 + 1.870 + XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); 1.871 + if (NATIVE_HAS_FLAG(wn, WantSetProperty)) { 1.872 + JSAutoCompartment ac(cx, holder); 1.873 + bool retval = true; 1.874 + nsresult rv = wn->GetScriptableCallback()->SetProperty(wn, cx, wrapper, 1.875 + id, vp.address(), &retval); 1.876 + if (NS_FAILED(rv) || !retval) { 1.877 + if (retval) 1.878 + XPCThrower::Throw(rv, cx); 1.879 + return false; 1.880 + } 1.881 + } 1.882 + return true; 1.883 +} 1.884 + 1.885 +class AutoSetWrapperNotShadowing 1.886 +{ 1.887 +public: 1.888 + AutoSetWrapperNotShadowing(ResolvingId *resolvingId MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.889 + { 1.890 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.891 + MOZ_ASSERT(resolvingId); 1.892 + mResolvingId = resolvingId; 1.893 + mResolvingId->mXrayShadowing = true; 1.894 + } 1.895 + 1.896 + ~AutoSetWrapperNotShadowing() 1.897 + { 1.898 + mResolvingId->mXrayShadowing = false; 1.899 + } 1.900 + 1.901 +private: 1.902 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.903 + ResolvingId *mResolvingId; 1.904 +}; 1.905 + 1.906 +// This is called after the resolveNativeProperty could not find any property 1.907 +// with the given id. At this point we can check for DOM specific collections 1.908 +// like document["formName"] because we already know that it is not shadowing 1.909 +// any native property. 1.910 +bool 1.911 +XPCWrappedNativeXrayTraits::resolveDOMCollectionProperty(JSContext *cx, HandleObject wrapper, 1.912 + HandleObject holder, HandleId id, 1.913 + MutableHandle<JSPropertyDescriptor> desc) 1.914 +{ 1.915 + // If we are not currently resolving this id and resolveNative is called 1.916 + // we don't do anything. (see defineProperty in case of shadowing is forbidden). 1.917 + ResolvingId *rid = ResolvingId::getResolvingId(holder); 1.918 + if (!rid || rid->mId != id) 1.919 + return true; 1.920 + 1.921 + XPCWrappedNative *wn = getWN(wrapper); 1.922 + if (!wn) { 1.923 + // This should NEVER happen, but let's be extra careful here 1.924 + // because of the reported crashes (Bug 832091). 1.925 + XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); 1.926 + return false; 1.927 + } 1.928 + if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) 1.929 + return true; 1.930 + 1.931 + ResolvingId *resolvingId = ResolvingId::getResolvingIdFromWrapper(wrapper); 1.932 + if (!resolvingId) { 1.933 + // This should NEVER happen, but let's be extra careful here 1.934 + // becaue of the reported crashes (Bug 832091). 1.935 + XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); 1.936 + return false; 1.937 + } 1.938 + 1.939 + // Setting the current ResolvingId in non-shadowing mode. So for this id 1.940 + // Xray won't ignore DOM specific collection properties temporarily. 1.941 + AutoSetWrapperNotShadowing asw(resolvingId); 1.942 + 1.943 + bool retval = true; 1.944 + RootedObject pobj(cx); 1.945 + nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, wrapper, id, 1.946 + pobj.address(), &retval); 1.947 + if (NS_FAILED(rv)) { 1.948 + if (retval) 1.949 + XPCThrower::Throw(rv, cx); 1.950 + return false; 1.951 + } 1.952 + 1.953 + if (pobj && !JS_GetPropertyDescriptorById(cx, holder, id, desc)) 1.954 + return false; 1.955 + 1.956 + return true; 1.957 +} 1.958 + 1.959 +static nsGlobalWindow* 1.960 +AsWindow(JSContext *cx, JSObject *wrapper) 1.961 +{ 1.962 + nsGlobalWindow* win; 1.963 + // We want to use our target object here, since we don't want to be 1.964 + // doing a security check while unwrapping. 1.965 + JSObject* target = XrayTraits::getTargetObject(wrapper); 1.966 + nsresult rv = UNWRAP_OBJECT(Window, target, win); 1.967 + if (NS_SUCCEEDED(rv)) 1.968 + return win; 1.969 + 1.970 + nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface( 1.971 + nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, target)); 1.972 + return static_cast<nsGlobalWindow*>(piWin.get()); 1.973 +} 1.974 + 1.975 +static bool 1.976 +IsWindow(JSContext *cx, JSObject *wrapper) 1.977 +{ 1.978 + return !!AsWindow(cx, wrapper); 1.979 +} 1.980 + 1.981 +static nsQueryInterface 1.982 +do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper); 1.983 + 1.984 +void 1.985 +XPCWrappedNativeXrayTraits::preserveWrapper(JSObject *target) 1.986 +{ 1.987 + XPCWrappedNative *wn = XPCWrappedNative::Get(target); 1.988 + nsRefPtr<nsXPCClassInfo> ci; 1.989 + CallQueryInterface(wn->Native(), getter_AddRefs(ci)); 1.990 + if (ci) 1.991 + ci->PreserveWrapper(wn->Native()); 1.992 +} 1.993 + 1.994 +bool 1.995 +XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext *cx, HandleObject wrapper, 1.996 + HandleObject holder, HandleId id, 1.997 + MutableHandle<JSPropertyDescriptor> desc) 1.998 +{ 1.999 + MOZ_ASSERT(js::GetObjectJSClass(holder) == &HolderClass); 1.1000 + 1.1001 + desc.object().set(nullptr); 1.1002 + 1.1003 + // This will do verification and the method lookup for us. 1.1004 + RootedObject target(cx, getTargetObject(wrapper)); 1.1005 + XPCCallContext ccx(JS_CALLER, cx, target, NullPtr(), id); 1.1006 + 1.1007 + // There are no native numeric properties, so we can shortcut here. We will 1.1008 + // not find the property. However we want to support non shadowing dom 1.1009 + // specific collection properties like window.frames, so we still have to 1.1010 + // check for those. 1.1011 + if (!JSID_IS_STRING(id)) { 1.1012 + /* Not found */ 1.1013 + return resolveDOMCollectionProperty(cx, wrapper, holder, id, desc); 1.1014 + } 1.1015 + 1.1016 + 1.1017 + // The |controllers| property is accessible as a [ChromeOnly] property on 1.1018 + // Window.WebIDL, and [noscript] in XPIDL. Chrome needs to see this over 1.1019 + // Xray, so we need to special-case it until we move |Window| to WebIDL. 1.1020 + nsGlobalWindow *win = nullptr; 1.1021 + if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONTROLLERS) && 1.1022 + AccessCheck::isChrome(wrapper) && 1.1023 + (win = AsWindow(cx, wrapper))) 1.1024 + { 1.1025 + nsCOMPtr<nsIControllers> c; 1.1026 + nsresult rv = win->GetControllers(getter_AddRefs(c)); 1.1027 + if (NS_SUCCEEDED(rv) && c) { 1.1028 + rv = nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, CurrentGlobalOrNull(cx), 1.1029 + c, nullptr, nullptr, true, 1.1030 + desc.value()); 1.1031 + } 1.1032 + 1.1033 + if (NS_FAILED(rv) || !c) { 1.1034 + JS_ReportError(cx, "Failed to invoke GetControllers via Xrays"); 1.1035 + return false; 1.1036 + } 1.1037 + 1.1038 + desc.object().set(wrapper); 1.1039 + return true; 1.1040 + } 1.1041 + 1.1042 + XPCNativeInterface *iface; 1.1043 + XPCNativeMember *member; 1.1044 + XPCWrappedNative *wn = getWN(wrapper); 1.1045 + 1.1046 + if (ccx.GetWrapper() != wn || !wn->IsValid()) { 1.1047 + // Something is wrong. If the wrapper is not even valid let's not risk 1.1048 + // calling resolveDOMCollectionProperty. 1.1049 + return true; 1.1050 + } else if (!(iface = ccx.GetInterface()) || 1.1051 + !(member = ccx.GetMember())) { 1.1052 + /* Not found */ 1.1053 + return resolveDOMCollectionProperty(cx, wrapper, holder, id, desc); 1.1054 + } 1.1055 + 1.1056 + desc.object().set(holder); 1.1057 + desc.setAttributes(JSPROP_ENUMERATE); 1.1058 + desc.setGetter(nullptr); 1.1059 + desc.setSetter(nullptr); 1.1060 + desc.value().set(JSVAL_VOID); 1.1061 + 1.1062 + RootedValue fval(cx, JSVAL_VOID); 1.1063 + if (member->IsConstant()) { 1.1064 + if (!member->GetConstantValue(ccx, iface, desc.value().address())) { 1.1065 + JS_ReportError(cx, "Failed to convert constant native property to JS value"); 1.1066 + return false; 1.1067 + } 1.1068 + } else if (member->IsAttribute()) { 1.1069 + // This is a getter/setter. Clone a function for it. 1.1070 + if (!member->NewFunctionObject(ccx, iface, wrapper, fval.address())) { 1.1071 + JS_ReportError(cx, "Failed to clone function object for native getter/setter"); 1.1072 + return false; 1.1073 + } 1.1074 + 1.1075 + unsigned attrs = desc.attributes(); 1.1076 + attrs |= JSPROP_GETTER; 1.1077 + if (member->IsWritableAttribute()) 1.1078 + attrs |= JSPROP_SETTER; 1.1079 + 1.1080 + // Make the property shared on the holder so no slot is allocated 1.1081 + // for it. This avoids keeping garbage alive through that slot. 1.1082 + attrs |= JSPROP_SHARED; 1.1083 + desc.setAttributes(attrs); 1.1084 + } else { 1.1085 + // This is a method. Clone a function for it. 1.1086 + if (!member->NewFunctionObject(ccx, iface, wrapper, desc.value().address())) { 1.1087 + JS_ReportError(cx, "Failed to clone function object for native function"); 1.1088 + return false; 1.1089 + } 1.1090 + 1.1091 + // Without a wrapper the function would live on the prototype. Since we 1.1092 + // don't have one, we have to avoid calling the scriptable helper's 1.1093 + // GetProperty method for this property, so stub out the getter and 1.1094 + // setter here explicitly. 1.1095 + desc.setGetter(JS_PropertyStub); 1.1096 + desc.setSetter(JS_StrictPropertyStub); 1.1097 + } 1.1098 + 1.1099 + if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval)) 1.1100 + return false; 1.1101 + 1.1102 + if (desc.hasGetterObject()) 1.1103 + desc.setGetterObject(&fval.toObject()); 1.1104 + if (desc.hasSetterObject()) 1.1105 + desc.setSetterObject(&fval.toObject()); 1.1106 + 1.1107 + // Define the property. 1.1108 + return JS_DefinePropertyById(cx, holder, id, desc.value(), 1.1109 + desc.getter(), desc.setter(), desc.attributes()); 1.1110 +} 1.1111 + 1.1112 +static bool 1.1113 +wrappedJSObject_getter(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandleValue vp) 1.1114 +{ 1.1115 + if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) { 1.1116 + JS_ReportError(cx, "Unexpected object"); 1.1117 + return false; 1.1118 + } 1.1119 + 1.1120 + vp.set(OBJECT_TO_JSVAL(wrapper)); 1.1121 + 1.1122 + return WrapperFactory::WaiveXrayAndWrap(cx, vp); 1.1123 +} 1.1124 + 1.1125 +bool 1.1126 +XrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, 1.1127 + HandleObject wrapper, HandleObject holder, HandleId id, 1.1128 + MutableHandle<JSPropertyDescriptor> desc) 1.1129 +{ 1.1130 + desc.object().set(nullptr); 1.1131 + RootedObject target(cx, getTargetObject(wrapper)); 1.1132 + RootedObject expando(cx, getExpandoObject(cx, target, wrapper)); 1.1133 + 1.1134 + // Check for expando properties first. Note that the expando object lives 1.1135 + // in the target compartment. 1.1136 + bool found = false; 1.1137 + if (expando) { 1.1138 + JSAutoCompartment ac(cx, expando); 1.1139 + if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) 1.1140 + return false; 1.1141 + found = !!desc.object(); 1.1142 + } 1.1143 + 1.1144 + // Next, check for ES builtins. 1.1145 + if (!found && JS_IsGlobalObject(target)) { 1.1146 + JSProtoKey key = JS_IdToProtoKey(cx, id); 1.1147 + JSAutoCompartment ac(cx, target); 1.1148 + if (key != JSProto_Null) { 1.1149 + MOZ_ASSERT(key < JSProto_LIMIT); 1.1150 + RootedObject constructor(cx); 1.1151 + if (!JS_GetClassObject(cx, key, &constructor)) 1.1152 + return false; 1.1153 + MOZ_ASSERT(constructor); 1.1154 + desc.value().set(ObjectValue(*constructor)); 1.1155 + found = true; 1.1156 + } else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EVAL)) { 1.1157 + RootedObject eval(cx); 1.1158 + if (!js::GetOriginalEval(cx, target, &eval)) 1.1159 + return false; 1.1160 + desc.value().set(ObjectValue(*eval)); 1.1161 + found = true; 1.1162 + } 1.1163 + } 1.1164 + 1.1165 + if (found) { 1.1166 + if (!JS_WrapPropertyDescriptor(cx, desc)) 1.1167 + return false; 1.1168 + // Pretend the property lives on the wrapper. 1.1169 + desc.object().set(wrapper); 1.1170 + return true; 1.1171 + } 1.1172 + 1.1173 + // Handle .wrappedJSObject for subsuming callers. This should move once we 1.1174 + // sort out own-ness for the holder. 1.1175 + if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) && 1.1176 + AccessCheck::wrapperSubsumes(wrapper)) 1.1177 + { 1.1178 + if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) 1.1179 + return false; 1.1180 + if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedValue(), 1.1181 + wrappedJSObject_getter, nullptr, 1.1182 + JSPROP_ENUMERATE | JSPROP_SHARED)) { 1.1183 + return false; 1.1184 + } 1.1185 + if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) 1.1186 + return false; 1.1187 + desc.object().set(wrapper); 1.1188 + return true; 1.1189 + } 1.1190 + 1.1191 + return true; 1.1192 +} 1.1193 + 1.1194 +bool 1.1195 +XrayTraits::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, 1.1196 + bool strict, MutableHandleValue vp) 1.1197 +{ 1.1198 + // Skip our Base if it isn't already BaseProxyHandler. 1.1199 + js::BaseProxyHandler *handler = js::GetProxyHandler(wrapper); 1.1200 + return handler->js::BaseProxyHandler::set(cx, wrapper, receiver, id, strict, vp); 1.1201 +} 1.1202 + 1.1203 +bool 1.1204 +XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, 1.1205 + HandleObject wrapper, HandleObject holder, 1.1206 + HandleId id, 1.1207 + MutableHandle<JSPropertyDescriptor> desc) 1.1208 +{ 1.1209 + // Call the common code. 1.1210 + bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, 1.1211 + id, desc); 1.1212 + if (!ok || desc.object()) 1.1213 + return ok; 1.1214 + 1.1215 + // Check for indexed access on a window. 1.1216 + int32_t index = GetArrayIndexFromId(cx, id); 1.1217 + if (IsArrayIndex(index)) { 1.1218 + nsGlobalWindow* win = AsWindow(cx, wrapper); 1.1219 + // Note: As() unwraps outer windows to get to the inner window. 1.1220 + if (win) { 1.1221 + bool unused; 1.1222 + nsCOMPtr<nsIDOMWindow> subframe = win->IndexedGetter(index, unused); 1.1223 + if (subframe) { 1.1224 + nsGlobalWindow* global = static_cast<nsGlobalWindow*>(subframe.get()); 1.1225 + global->EnsureInnerWindow(); 1.1226 + JSObject* obj = global->FastGetGlobalJSObject(); 1.1227 + if (MOZ_UNLIKELY(!obj)) { 1.1228 + // It's gone? 1.1229 + return xpc::Throw(cx, NS_ERROR_FAILURE); 1.1230 + } 1.1231 + desc.value().setObject(*obj); 1.1232 + FillPropertyDescriptor(desc, wrapper, true); 1.1233 + return JS_WrapPropertyDescriptor(cx, desc); 1.1234 + } 1.1235 + } 1.1236 + } 1.1237 + 1.1238 + // Xray wrappers don't use the regular wrapper hierarchy, so we should be 1.1239 + // in the wrapper's compartment here, not the wrappee. 1.1240 + MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); 1.1241 + 1.1242 + bool hasProp; 1.1243 + if (!JS_HasPropertyById(cx, holder, id, &hasProp)) { 1.1244 + return false; 1.1245 + } 1.1246 + if (!hasProp) { 1.1247 + XPCWrappedNative *wn = getWN(wrapper); 1.1248 + 1.1249 + // Run the resolve hook of the wrapped native. 1.1250 + if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) { 1.1251 + return true; 1.1252 + } 1.1253 + 1.1254 + bool retval = true; 1.1255 + RootedObject pobj(cx); 1.1256 + nsIXPCScriptable *callback = wn->GetScriptableInfo()->GetCallback(); 1.1257 + nsresult rv = callback->NewResolve(wn, cx, wrapper, id, pobj.address(), 1.1258 + &retval); 1.1259 + if (NS_FAILED(rv)) { 1.1260 + if (retval) 1.1261 + XPCThrower::Throw(rv, cx); 1.1262 + return false; 1.1263 + } 1.1264 + 1.1265 + MOZ_ASSERT(!pobj || (JS_HasPropertyById(cx, holder, id, &hasProp) && 1.1266 + hasProp), "id got defined somewhere else?"); 1.1267 + } 1.1268 + 1.1269 + // resolveOwnProperty must return a non-empty |desc| if and only if an |own| 1.1270 + // property was found on the object. However, given how the NewResolve setup 1.1271 + // works, we can't run the resolve hook if the holder already has a property 1.1272 + // of the same name. So if there was a pre-existing property on the holder, 1.1273 + // we have to use it. But we have no way of knowing if it corresponded to an 1.1274 + // |own| or non-|own| property, since both get cached on the holder and the 1.1275 + // |own|-ness information is lost. 1.1276 + // 1.1277 + // So we just over-zealously call things |own| here. This can cause us to 1.1278 + // return non-|own| properties from Object.getOwnPropertyDescriptor if 1.1279 + // lookups are performed in a certain order, but we can probably live with 1.1280 + // that until XPCWN Xrays go away with the new DOM bindings. 1.1281 + return JS_GetPropertyDescriptorById(cx, holder, id, desc); 1.1282 +} 1.1283 + 1.1284 +bool 1.1285 +XPCWrappedNativeXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, 1.1286 + MutableHandle<JSPropertyDescriptor> desc, 1.1287 + Handle<JSPropertyDescriptor> existingDesc, bool *defined) 1.1288 +{ 1.1289 + *defined = false; 1.1290 + JSObject *holder = singleton.ensureHolder(cx, wrapper); 1.1291 + if (isResolving(cx, holder, id)) { 1.1292 + if (!desc.hasAttributes(JSPROP_GETTER | JSPROP_SETTER)) { 1.1293 + if (!desc.getter()) 1.1294 + desc.setGetter(holder_get); 1.1295 + if (!desc.setter()) 1.1296 + desc.setSetter(holder_set); 1.1297 + } 1.1298 + 1.1299 + *defined = true; 1.1300 + return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.getter(), desc.setter(), 1.1301 + desc.attributes()); 1.1302 + } 1.1303 + 1.1304 + // Check for an indexed property on a Window. If that's happening, do 1.1305 + // nothing but claim we defined it so it won't get added as an expando. 1.1306 + int32_t index = GetArrayIndexFromId(cx, id); 1.1307 + if (IsArrayIndex(index) && IsWindow(cx, wrapper)) { 1.1308 + *defined = true; 1.1309 + return true; 1.1310 + } 1.1311 + 1.1312 + return true; 1.1313 +} 1.1314 + 1.1315 +bool 1.1316 +XPCWrappedNativeXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, 1.1317 + AutoIdVector &props) 1.1318 +{ 1.1319 + // Force all native properties to be materialized onto the wrapped native. 1.1320 + AutoIdVector wnProps(cx); 1.1321 + { 1.1322 + RootedObject target(cx, singleton.getTargetObject(wrapper)); 1.1323 + JSAutoCompartment ac(cx, target); 1.1324 + if (!js::GetPropertyNames(cx, target, flags, &wnProps)) 1.1325 + return false; 1.1326 + } 1.1327 + if (!JS_WrapAutoIdVector(cx, wnProps)) 1.1328 + return false; 1.1329 + 1.1330 + // Go through the properties we got and enumerate all native ones. 1.1331 + for (size_t n = 0; n < wnProps.length(); ++n) { 1.1332 + RootedId id(cx, wnProps[n]); 1.1333 + bool hasProp; 1.1334 + if (!JS_HasPropertyById(cx, wrapper, id, &hasProp)) 1.1335 + return false; 1.1336 + if (hasProp) 1.1337 + props.append(id); 1.1338 + } 1.1339 + return true; 1.1340 +} 1.1341 + 1.1342 +JSObject * 1.1343 +XPCWrappedNativeXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) 1.1344 +{ 1.1345 + RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); 1.1346 + JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, JS::NullPtr(), 1.1347 + global); 1.1348 + if (!holder) 1.1349 + return nullptr; 1.1350 + 1.1351 + js::SetReservedSlot(holder, JSSLOT_RESOLVING, PrivateValue(nullptr)); 1.1352 + return holder; 1.1353 +} 1.1354 + 1.1355 +bool 1.1356 +XPCWrappedNativeXrayTraits::call(JSContext *cx, HandleObject wrapper, 1.1357 + const JS::CallArgs &args, 1.1358 + js::Wrapper& baseInstance) 1.1359 +{ 1.1360 + // Run the resolve hook of the wrapped native. 1.1361 + XPCWrappedNative *wn = getWN(wrapper); 1.1362 + if (NATIVE_HAS_FLAG(wn, WantCall)) { 1.1363 + XPCCallContext ccx(JS_CALLER, cx, wrapper, NullPtr(), JSID_VOIDHANDLE, args.length(), 1.1364 + args.array(), args.rval().address()); 1.1365 + if (!ccx.IsValid()) 1.1366 + return false; 1.1367 + bool ok = true; 1.1368 + nsresult rv = wn->GetScriptableInfo()->GetCallback()->Call( 1.1369 + wn, cx, wrapper, args, &ok); 1.1370 + if (NS_FAILED(rv)) { 1.1371 + if (ok) 1.1372 + XPCThrower::Throw(rv, cx); 1.1373 + return false; 1.1374 + } 1.1375 + } 1.1376 + 1.1377 + return true; 1.1378 + 1.1379 +} 1.1380 + 1.1381 +bool 1.1382 +XPCWrappedNativeXrayTraits::construct(JSContext *cx, HandleObject wrapper, 1.1383 + const JS::CallArgs &args, 1.1384 + js::Wrapper& baseInstance) 1.1385 +{ 1.1386 + // Run the resolve hook of the wrapped native. 1.1387 + XPCWrappedNative *wn = getWN(wrapper); 1.1388 + if (NATIVE_HAS_FLAG(wn, WantConstruct)) { 1.1389 + XPCCallContext ccx(JS_CALLER, cx, wrapper, NullPtr(), JSID_VOIDHANDLE, args.length(), 1.1390 + args.array(), args.rval().address()); 1.1391 + if (!ccx.IsValid()) 1.1392 + return false; 1.1393 + bool ok = true; 1.1394 + nsresult rv = wn->GetScriptableInfo()->GetCallback()->Construct( 1.1395 + wn, cx, wrapper, args, &ok); 1.1396 + if (NS_FAILED(rv)) { 1.1397 + if (ok) 1.1398 + XPCThrower::Throw(rv, cx); 1.1399 + return false; 1.1400 + } 1.1401 + } 1.1402 + 1.1403 + return true; 1.1404 + 1.1405 +} 1.1406 + 1.1407 +bool 1.1408 +DOMXrayTraits::resolveNativeProperty(JSContext *cx, HandleObject wrapper, 1.1409 + HandleObject holder, HandleId id, 1.1410 + MutableHandle<JSPropertyDescriptor> desc) 1.1411 +{ 1.1412 + RootedObject obj(cx, getTargetObject(wrapper)); 1.1413 + if (!XrayResolveNativeProperty(cx, wrapper, obj, id, desc)) 1.1414 + return false; 1.1415 + 1.1416 + MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?"); 1.1417 + 1.1418 + return true; 1.1419 +} 1.1420 + 1.1421 +bool 1.1422 +DOMXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, 1.1423 + HandleObject holder, HandleId id, 1.1424 + MutableHandle<JSPropertyDescriptor> desc) 1.1425 +{ 1.1426 + // Call the common code. 1.1427 + bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc); 1.1428 + if (!ok || desc.object()) 1.1429 + return ok; 1.1430 + 1.1431 + // Check for indexed access on a window. 1.1432 + int32_t index = GetArrayIndexFromId(cx, id); 1.1433 + if (IsArrayIndex(index)) { 1.1434 + nsGlobalWindow* win = AsWindow(cx, wrapper); 1.1435 + // Note: As() unwraps outer windows to get to the inner window. 1.1436 + if (win) { 1.1437 + bool unused; 1.1438 + nsCOMPtr<nsIDOMWindow> subframe = win->IndexedGetter(index, unused); 1.1439 + if (subframe) { 1.1440 + nsGlobalWindow* global = static_cast<nsGlobalWindow*>(subframe.get()); 1.1441 + global->EnsureInnerWindow(); 1.1442 + JSObject* obj = global->FastGetGlobalJSObject(); 1.1443 + if (MOZ_UNLIKELY(!obj)) { 1.1444 + // It's gone? 1.1445 + return xpc::Throw(cx, NS_ERROR_FAILURE); 1.1446 + } 1.1447 + desc.value().setObject(*obj); 1.1448 + FillPropertyDescriptor(desc, wrapper, true); 1.1449 + return JS_WrapPropertyDescriptor(cx, desc); 1.1450 + } 1.1451 + } 1.1452 + } 1.1453 + 1.1454 + RootedObject obj(cx, getTargetObject(wrapper)); 1.1455 + if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc)) 1.1456 + return false; 1.1457 + 1.1458 + MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?"); 1.1459 + 1.1460 + return true; 1.1461 +} 1.1462 + 1.1463 +bool 1.1464 +DOMXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, 1.1465 + MutableHandle<JSPropertyDescriptor> desc, 1.1466 + Handle<JSPropertyDescriptor> existingDesc, bool *defined) 1.1467 +{ 1.1468 + // Check for an indexed property on a Window. If that's happening, do 1.1469 + // nothing but claim we defined it so it won't get added as an expando. 1.1470 + if (IsWindow(cx, wrapper)) { 1.1471 + int32_t index = GetArrayIndexFromId(cx, id); 1.1472 + if (IsArrayIndex(index)) { 1.1473 + *defined = true; 1.1474 + return true; 1.1475 + } 1.1476 + } 1.1477 + 1.1478 + if (!existingDesc.object()) 1.1479 + return true; 1.1480 + 1.1481 + JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper)); 1.1482 + return XrayDefineProperty(cx, wrapper, obj, id, desc, defined); 1.1483 +} 1.1484 + 1.1485 +bool 1.1486 +DOMXrayTraits::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, 1.1487 + bool strict, MutableHandleValue vp) 1.1488 +{ 1.1489 + MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(wrapper)); 1.1490 + RootedObject obj(cx, getTargetObject(wrapper)); 1.1491 + if (IsDOMProxy(obj)) { 1.1492 + DOMProxyHandler* handler = GetDOMProxyHandler(obj); 1.1493 + 1.1494 + bool done; 1.1495 + if (!handler->setCustom(cx, obj, id, vp, &done)) 1.1496 + return false; 1.1497 + if (done) 1.1498 + return true; 1.1499 + } 1.1500 + return XrayTraits::set(cx, wrapper, receiver, id, strict, vp); 1.1501 +} 1.1502 + 1.1503 +bool 1.1504 +DOMXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, 1.1505 + AutoIdVector &props) 1.1506 +{ 1.1507 + JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper)); 1.1508 + return XrayEnumerateProperties(cx, wrapper, obj, flags, props); 1.1509 +} 1.1510 + 1.1511 +bool 1.1512 +DOMXrayTraits::call(JSContext *cx, HandleObject wrapper, 1.1513 + const JS::CallArgs &args, js::Wrapper& baseInstance) 1.1514 +{ 1.1515 + RootedObject obj(cx, getTargetObject(wrapper)); 1.1516 + const js::Class* clasp = js::GetObjectClass(obj); 1.1517 + // What we have is either a WebIDL interface object, a WebIDL prototype 1.1518 + // object, or a WebIDL instance object. WebIDL prototype objects never have 1.1519 + // a clasp->call. WebIDL interface objects we want to invoke on the xray 1.1520 + // compartment. WebIDL instance objects either don't have a clasp->call or 1.1521 + // are using "legacycaller", which basically means plug-ins. We want to 1.1522 + // call those on the content compartment. 1.1523 + if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) { 1.1524 + if (!clasp->call) { 1.1525 + RootedValue v(cx, ObjectValue(*wrapper)); 1.1526 + js_ReportIsNotFunction(cx, v); 1.1527 + return false; 1.1528 + } 1.1529 + // call it on the Xray compartment 1.1530 + if (!clasp->call(cx, args.length(), args.base())) 1.1531 + return false; 1.1532 + } else { 1.1533 + // This is only reached for WebIDL instance objects, and in practice 1.1534 + // only for plugins. Just call them on the content compartment. 1.1535 + if (!baseInstance.call(cx, wrapper, args)) 1.1536 + return false; 1.1537 + } 1.1538 + return JS_WrapValue(cx, args.rval()); 1.1539 +} 1.1540 + 1.1541 +bool 1.1542 +DOMXrayTraits::construct(JSContext *cx, HandleObject wrapper, 1.1543 + const JS::CallArgs &args, js::Wrapper& baseInstance) 1.1544 +{ 1.1545 + RootedObject obj(cx, getTargetObject(wrapper)); 1.1546 + MOZ_ASSERT(mozilla::dom::HasConstructor(obj)); 1.1547 + const js::Class* clasp = js::GetObjectClass(obj); 1.1548 + // See comments in DOMXrayTraits::call() explaining what's going on here. 1.1549 + if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) { 1.1550 + if (!clasp->construct) { 1.1551 + RootedValue v(cx, ObjectValue(*wrapper)); 1.1552 + js_ReportIsNotFunction(cx, v); 1.1553 + return false; 1.1554 + } 1.1555 + if (!clasp->construct(cx, args.length(), args.base())) 1.1556 + return false; 1.1557 + } else { 1.1558 + if (!baseInstance.construct(cx, wrapper, args)) 1.1559 + return false; 1.1560 + } 1.1561 + if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval())) 1.1562 + return false; 1.1563 + return true; 1.1564 +} 1.1565 + 1.1566 +void 1.1567 +DOMXrayTraits::preserveWrapper(JSObject *target) 1.1568 +{ 1.1569 + nsISupports *identity = mozilla::dom::UnwrapDOMObjectToISupports(target); 1.1570 + if (!identity) 1.1571 + return; 1.1572 + nsWrapperCache* cache = nullptr; 1.1573 + CallQueryInterface(identity, &cache); 1.1574 + if (cache) 1.1575 + cache->PreserveWrapper(identity); 1.1576 +} 1.1577 + 1.1578 +JSObject* 1.1579 +DOMXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) 1.1580 +{ 1.1581 + RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); 1.1582 + return JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), global); 1.1583 +} 1.1584 + 1.1585 +template <typename Base, typename Traits> 1.1586 +XrayWrapper<Base, Traits>::XrayWrapper(unsigned flags) 1.1587 + : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG) 1.1588 +{ 1.1589 + Base::setHasPrototype(Traits::HasPrototype); 1.1590 +} 1.1591 + 1.1592 +template <typename Base, typename Traits> 1.1593 +XrayWrapper<Base, Traits>::~XrayWrapper() 1.1594 +{ 1.1595 +} 1.1596 + 1.1597 +namespace XrayUtils { 1.1598 + 1.1599 +JSObject * 1.1600 +GetNativePropertiesObject(JSContext *cx, JSObject *wrapper) 1.1601 +{ 1.1602 + MOZ_ASSERT(js::IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper), 1.1603 + "bad object passed in"); 1.1604 + 1.1605 + JSObject *holder = GetHolder(wrapper); 1.1606 + MOZ_ASSERT(holder, "uninitialized wrapper being used?"); 1.1607 + return holder; 1.1608 +} 1.1609 + 1.1610 +bool 1.1611 +IsXrayResolving(JSContext *cx, HandleObject wrapper, HandleId id) 1.1612 +{ 1.1613 + if (!WrapperFactory::IsXrayWrapper(wrapper) || 1.1614 + GetXrayType(wrapper) != XrayForWrappedNative) 1.1615 + { 1.1616 + return false; 1.1617 + } 1.1618 + JSObject *holder = 1.1619 + XPCWrappedNativeXrayTraits::singleton.ensureHolder(cx, wrapper); 1.1620 + return XPCWrappedNativeXrayTraits::isResolving(cx, holder, id); 1.1621 +} 1.1622 + 1.1623 +bool 1.1624 +HasNativeProperty(JSContext *cx, HandleObject wrapper, HandleId id, bool *hasProp) 1.1625 +{ 1.1626 + MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); 1.1627 + XrayTraits *traits = GetXrayTraits(wrapper); 1.1628 + MOZ_ASSERT(traits); 1.1629 + RootedObject holder(cx, traits->ensureHolder(cx, wrapper)); 1.1630 + NS_ENSURE_TRUE(holder, false); 1.1631 + *hasProp = false; 1.1632 + Rooted<JSPropertyDescriptor> desc(cx); 1.1633 + Wrapper *handler = Wrapper::wrapperHandler(wrapper); 1.1634 + 1.1635 + // Try resolveOwnProperty. 1.1636 + Maybe<ResolvingId> resolvingId; 1.1637 + if (traits == &XPCWrappedNativeXrayTraits::singleton) 1.1638 + resolvingId.construct(cx, wrapper, id); 1.1639 + if (!traits->resolveOwnProperty(cx, *handler, wrapper, holder, id, &desc)) 1.1640 + return false; 1.1641 + if (desc.object()) { 1.1642 + *hasProp = true; 1.1643 + return true; 1.1644 + } 1.1645 + 1.1646 + // Try the holder. 1.1647 + bool found = false; 1.1648 + if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) 1.1649 + return false; 1.1650 + if (found) { 1.1651 + *hasProp = true; 1.1652 + return true; 1.1653 + } 1.1654 + 1.1655 + // Try resolveNativeProperty. 1.1656 + if (!traits->resolveNativeProperty(cx, wrapper, holder, id, &desc)) 1.1657 + return false; 1.1658 + *hasProp = !!desc.object(); 1.1659 + return true; 1.1660 +} 1.1661 + 1.1662 +} // namespace XrayUtils 1.1663 + 1.1664 +static bool 1.1665 +XrayToString(JSContext *cx, unsigned argc, Value *vp) 1.1666 +{ 1.1667 + CallArgs args = CallArgsFromVp(argc, vp); 1.1668 + 1.1669 + if (!args.thisv().isObject()) { 1.1670 + JS_ReportError(cx, "XrayToString called on an incompatible object"); 1.1671 + return false; 1.1672 + } 1.1673 + 1.1674 + RootedObject wrapper(cx, &args.thisv().toObject()); 1.1675 + if (!wrapper) 1.1676 + return false; 1.1677 + if (IsWrapper(wrapper) && 1.1678 + GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) { 1.1679 + wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper); 1.1680 + } 1.1681 + if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) { 1.1682 + JS_ReportError(cx, "XrayToString called on an incompatible object"); 1.1683 + return false; 1.1684 + } 1.1685 + 1.1686 + static const char start[] = "[object XrayWrapper "; 1.1687 + static const char end[] = "]"; 1.1688 + 1.1689 + RootedObject obj(cx, XrayTraits::getTargetObject(wrapper)); 1.1690 + XrayType type = GetXrayType(obj); 1.1691 + if (type == XrayForDOMObject) 1.1692 + return NativeToString(cx, wrapper, obj, start, end, args.rval()); 1.1693 + 1.1694 + if (type != XrayForWrappedNative) { 1.1695 + JS_ReportError(cx, "XrayToString called on an incompatible object"); 1.1696 + return false; 1.1697 + } 1.1698 + 1.1699 + nsAutoString result; 1.1700 + result.AppendASCII(start); 1.1701 + 1.1702 + XPCCallContext ccx(JS_CALLER, cx, obj); 1.1703 + XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); 1.1704 + char *wrapperStr = wn->ToString(); 1.1705 + if (!wrapperStr) { 1.1706 + JS_ReportOutOfMemory(cx); 1.1707 + return false; 1.1708 + } 1.1709 + result.AppendASCII(wrapperStr); 1.1710 + JS_smprintf_free(wrapperStr); 1.1711 + 1.1712 + result.AppendASCII(end); 1.1713 + 1.1714 + JSString *str = JS_NewUCStringCopyN(cx, result.get(), result.Length()); 1.1715 + if (!str) 1.1716 + return false; 1.1717 + 1.1718 + args.rval().setString(str); 1.1719 + return true; 1.1720 +} 1.1721 + 1.1722 +#ifdef DEBUG 1.1723 + 1.1724 +static void 1.1725 +DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj) 1.1726 +{ 1.1727 + // In general, we shouldn't have cross-compartment wrappers here, because 1.1728 + // we should be running in an XBL scope, and the content prototype should 1.1729 + // contain wrappers to functions defined in the XBL scope. But if the node 1.1730 + // has been adopted into another compartment, those prototypes will now point 1.1731 + // to a different XBL scope (which is ok). 1.1732 + MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj), 1.1733 + xpc::IsXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj)))); 1.1734 + MOZ_ASSERT(JS_ObjectIsCallable(cx, obj)); 1.1735 +} 1.1736 + 1.1737 +static void 1.1738 +DEBUG_CheckXBLLookup(JSContext *cx, JSPropertyDescriptor *desc) 1.1739 +{ 1.1740 + if (!desc->obj) 1.1741 + return; 1.1742 + if (!desc->value.isUndefined()) { 1.1743 + MOZ_ASSERT(desc->value.isObject()); 1.1744 + DEBUG_CheckXBLCallable(cx, &desc->value.toObject()); 1.1745 + } 1.1746 + if (desc->getter) { 1.1747 + MOZ_ASSERT(desc->attrs & JSPROP_GETTER); 1.1748 + DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->getter)); 1.1749 + } 1.1750 + if (desc->setter) { 1.1751 + MOZ_ASSERT(desc->attrs & JSPROP_SETTER); 1.1752 + DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->setter)); 1.1753 + } 1.1754 +} 1.1755 +#else 1.1756 +#define DEBUG_CheckXBLLookup(a, b) {} 1.1757 +#endif 1.1758 + 1.1759 +template <typename Base, typename Traits> 1.1760 +bool 1.1761 +XrayWrapper<Base, Traits>::isExtensible(JSContext *cx, JS::Handle<JSObject*> wrapper, bool *extensible) 1.1762 +{ 1.1763 + // Xray wrappers are supposed to provide a clean view of the target 1.1764 + // reflector, hiding any modifications by script in the target scope. So 1.1765 + // even if that script freezes the reflector, we don't want to make that 1.1766 + // visible to the caller. DOM reflectors are always extensible by default, 1.1767 + // so we can just return true here. 1.1768 + *extensible = true; 1.1769 + return true; 1.1770 +} 1.1771 + 1.1772 +template <typename Base, typename Traits> 1.1773 +bool 1.1774 +XrayWrapper<Base, Traits>::preventExtensions(JSContext *cx, HandleObject wrapper) 1.1775 +{ 1.1776 + // See above. 1.1777 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); 1.1778 + return false; 1.1779 +} 1.1780 + 1.1781 +template <typename Base, typename Traits> 1.1782 +bool 1.1783 +XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, 1.1784 + JS::MutableHandle<JSPropertyDescriptor> desc) 1.1785 +{ 1.1786 + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); 1.1787 + RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); 1.1788 + if (Traits::isResolving(cx, holder, id)) { 1.1789 + desc.object().set(nullptr); 1.1790 + return true; 1.1791 + } 1.1792 + 1.1793 + typename Traits::ResolvingIdImpl resolving(cx, wrapper, id); 1.1794 + 1.1795 + if (!holder) 1.1796 + return false; 1.1797 + 1.1798 + // Ordering is important here. 1.1799 + // 1.1800 + // We first need to call resolveOwnProperty, even before checking the holder, 1.1801 + // because there might be a new dynamic |own| property that appears and 1.1802 + // shadows a previously-resolved non-own property that we cached on the 1.1803 + // holder. This can happen with indexed properties on NodeLists, for example, 1.1804 + // which are |own| value props. 1.1805 + // 1.1806 + // resolveOwnProperty may or may not cache what it finds on the holder, 1.1807 + // depending on how ephemeral it decides the property is. XPCWN |own| 1.1808 + // properties generally end up on the holder via NewResolve, whereas 1.1809 + // NodeList |own| properties don't get defined on the holder, since they're 1.1810 + // supposed to be dynamic. This means that we have to first check the result 1.1811 + // of resolveOwnProperty, and _then_, if that comes up blank, check the 1.1812 + // holder for any cached native properties. 1.1813 + // 1.1814 + // Finally, we call resolveNativeProperty, which checks non-own properties, 1.1815 + // and unconditionally caches what it finds on the holder. 1.1816 + 1.1817 + // Check resolveOwnProperty. 1.1818 + if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc)) 1.1819 + return false; 1.1820 + 1.1821 + // Check the holder. 1.1822 + if (!desc.object() && !JS_GetPropertyDescriptorById(cx, holder, id, desc)) 1.1823 + return false; 1.1824 + if (desc.object()) { 1.1825 + desc.object().set(wrapper); 1.1826 + return true; 1.1827 + } 1.1828 + 1.1829 + // Nothing in the cache. Call through, and cache the result. 1.1830 + if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc)) 1.1831 + return false; 1.1832 + 1.1833 + // We need to handle named access on the Window somewhere other than 1.1834 + // Traits::resolveOwnProperty, because per spec it happens on the Global 1.1835 + // Scope Polluter and thus the resulting properties are non-|own|. However, 1.1836 + // we're set up (above) to cache (on the holder) anything that comes out of 1.1837 + // resolveNativeProperty, which we don't want for something dynamic like 1.1838 + // named access. So we just handle it separately here. 1.1839 + nsGlobalWindow *win = nullptr; 1.1840 + if (!desc.object() && 1.1841 + JSID_IS_STRING(id) && 1.1842 + (win = AsWindow(cx, wrapper))) 1.1843 + { 1.1844 + nsDependentJSString name(id); 1.1845 + nsCOMPtr<nsIDOMWindow> childDOMWin = win->GetChildWindow(name); 1.1846 + if (childDOMWin) { 1.1847 + nsGlobalWindow *cwin = static_cast<nsGlobalWindow*>(childDOMWin.get()); 1.1848 + JSObject *childObj = cwin->FastGetGlobalJSObject(); 1.1849 + if (MOZ_UNLIKELY(!childObj)) 1.1850 + return xpc::Throw(cx, NS_ERROR_FAILURE); 1.1851 + FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj), 1.1852 + /* readOnly = */ true); 1.1853 + return JS_WrapPropertyDescriptor(cx, desc); 1.1854 + } 1.1855 + } 1.1856 + 1.1857 + if (!desc.object() && 1.1858 + id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING)) 1.1859 + { 1.1860 + 1.1861 + JSFunction *toString = JS_NewFunction(cx, XrayToString, 0, 0, wrapper, "toString"); 1.1862 + if (!toString) 1.1863 + return false; 1.1864 + 1.1865 + desc.object().set(wrapper); 1.1866 + desc.setAttributes(0); 1.1867 + desc.setGetter(nullptr); 1.1868 + desc.setSetter(nullptr); 1.1869 + desc.value().setObject(*JS_GetFunctionObject(toString)); 1.1870 + } 1.1871 + 1.1872 + // If we're a special scope for in-content XBL, our script expects to see 1.1873 + // the bound XBL methods and attributes when accessing content. However, 1.1874 + // these members are implemented in content via custom-spliced prototypes, 1.1875 + // and thus aren't visible through Xray wrappers unless we handle them 1.1876 + // explicitly. So we check if we're running in such a scope, and if so, 1.1877 + // whether the wrappee is a bound element. If it is, we do a lookup via 1.1878 + // specialized XBL machinery. 1.1879 + // 1.1880 + // While we have to do some sketchy walking through content land, we should 1.1881 + // be protected by read-only/non-configurable properties, and any functions 1.1882 + // we end up with should _always_ be living in an XBL scope (usually ours, 1.1883 + // but could be another if the node has been adopted). 1.1884 + // 1.1885 + // Make sure to assert this. 1.1886 + nsCOMPtr<nsIContent> content; 1.1887 + if (!desc.object() && 1.1888 + EnsureCompartmentPrivate(wrapper)->scope->IsXBLScope() && 1.1889 + (content = do_QueryInterfaceNative(cx, wrapper))) 1.1890 + { 1.1891 + if (!nsContentUtils::LookupBindingMember(cx, content, id, desc)) 1.1892 + return false; 1.1893 + DEBUG_CheckXBLLookup(cx, desc.address()); 1.1894 + } 1.1895 + 1.1896 + // If we still have nothing, we're done. 1.1897 + if (!desc.object()) 1.1898 + return true; 1.1899 + 1.1900 + if (!JS_DefinePropertyById(cx, holder, id, desc.value(), desc.getter(), 1.1901 + desc.setter(), desc.attributes()) || 1.1902 + !JS_GetPropertyDescriptorById(cx, holder, id, desc)) 1.1903 + { 1.1904 + return false; 1.1905 + } 1.1906 + MOZ_ASSERT(desc.object()); 1.1907 + desc.object().set(wrapper); 1.1908 + return true; 1.1909 +} 1.1910 + 1.1911 +template <typename Base, typename Traits> 1.1912 +bool 1.1913 +XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, 1.1914 + JS::MutableHandle<JSPropertyDescriptor> desc) 1.1915 +{ 1.1916 + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); 1.1917 + RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); 1.1918 + if (Traits::isResolving(cx, holder, id)) { 1.1919 + desc.object().set(nullptr); 1.1920 + return true; 1.1921 + } 1.1922 + 1.1923 + typename Traits::ResolvingIdImpl resolving(cx, wrapper, id); 1.1924 + 1.1925 + // NB: Nothing we do here acts on the wrapped native itself, so we don't 1.1926 + // enter our policy. 1.1927 + 1.1928 + if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc)) 1.1929 + return false; 1.1930 + if (desc.object()) 1.1931 + desc.object().set(wrapper); 1.1932 + return true; 1.1933 +} 1.1934 + 1.1935 +// Consider what happens when chrome does |xray.expando = xray.wrappedJSObject|. 1.1936 +// 1.1937 +// Since the expando comes from the target compartment, wrapping it back into 1.1938 +// the target compartment to define it on the expando object ends up stripping 1.1939 +// off the Xray waiver that gives |xray| and |xray.wrappedJSObject| different 1.1940 +// identities. This is generally the right thing to do when wrapping across 1.1941 +// compartments, but is incorrect in the special case of the Xray expando 1.1942 +// object. Manually re-apply Xrays if necessary. 1.1943 +// 1.1944 +// NB: In order to satisfy the invariants of WaiveXray, we need to pass 1.1945 +// in an object sans security wrapper, which means we need to strip off any 1.1946 +// potential same-compartment security wrapper that may have been applied 1.1947 +// to the content object. This is ok, because the the expando object is only 1.1948 +// ever accessed by code across the compartment boundary. 1.1949 +static bool 1.1950 +RecreateLostWaivers(JSContext *cx, JSPropertyDescriptor *orig, 1.1951 + MutableHandle<JSPropertyDescriptor> wrapped) 1.1952 +{ 1.1953 + // Compute whether the original objects were waived, and implicitly, whether 1.1954 + // they were objects at all. 1.1955 + bool valueWasWaived = 1.1956 + orig->value.isObject() && 1.1957 + WrapperFactory::HasWaiveXrayFlag(&orig->value.toObject()); 1.1958 + bool getterWasWaived = 1.1959 + (orig->attrs & JSPROP_GETTER) && 1.1960 + WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->getter)); 1.1961 + bool setterWasWaived = 1.1962 + (orig->attrs & JSPROP_SETTER) && 1.1963 + WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->setter)); 1.1964 + 1.1965 + // Recreate waivers. Note that for value, we need an extra UncheckedUnwrap 1.1966 + // to handle same-compartment security wrappers (see above). This should 1.1967 + // never happen for getters/setters. 1.1968 + 1.1969 + RootedObject rewaived(cx); 1.1970 + if (valueWasWaived && !IsCrossCompartmentWrapper(&wrapped.value().toObject())) { 1.1971 + rewaived = &wrapped.value().toObject(); 1.1972 + rewaived = WrapperFactory::WaiveXray(cx, UncheckedUnwrap(rewaived)); 1.1973 + NS_ENSURE_TRUE(rewaived, false); 1.1974 + wrapped.value().set(ObjectValue(*rewaived)); 1.1975 + } 1.1976 + if (getterWasWaived && !IsCrossCompartmentWrapper(wrapped.getterObject())) { 1.1977 + MOZ_ASSERT(CheckedUnwrap(wrapped.getterObject())); 1.1978 + rewaived = WrapperFactory::WaiveXray(cx, wrapped.getterObject()); 1.1979 + NS_ENSURE_TRUE(rewaived, false); 1.1980 + wrapped.setGetterObject(rewaived); 1.1981 + } 1.1982 + if (setterWasWaived && !IsCrossCompartmentWrapper(wrapped.setterObject())) { 1.1983 + MOZ_ASSERT(CheckedUnwrap(wrapped.setterObject())); 1.1984 + rewaived = WrapperFactory::WaiveXray(cx, wrapped.setterObject()); 1.1985 + NS_ENSURE_TRUE(rewaived, false); 1.1986 + wrapped.setSetterObject(rewaived); 1.1987 + } 1.1988 + 1.1989 + return true; 1.1990 +} 1.1991 + 1.1992 +template <typename Base, typename Traits> 1.1993 +bool 1.1994 +XrayWrapper<Base, Traits>::defineProperty(JSContext *cx, HandleObject wrapper, 1.1995 + HandleId id, MutableHandle<JSPropertyDescriptor> desc) 1.1996 +{ 1.1997 + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); 1.1998 + 1.1999 + Rooted<JSPropertyDescriptor> existing_desc(cx); 1.2000 + if (!getOwnPropertyDescriptor(cx, wrapper, id, &existing_desc)) 1.2001 + return false; 1.2002 + 1.2003 + if (existing_desc.object() && existing_desc.isPermanent()) 1.2004 + return true; // silently ignore attempt to overwrite native property 1.2005 + 1.2006 + bool defined = false; 1.2007 + if (!Traits::defineProperty(cx, wrapper, id, desc, existing_desc, &defined)) 1.2008 + return false; 1.2009 + if (defined) 1.2010 + return true; 1.2011 + 1.2012 + // We're placing an expando. The expando objects live in the target 1.2013 + // compartment, so we need to enter it. 1.2014 + RootedObject target(cx, Traits::singleton.getTargetObject(wrapper)); 1.2015 + JSAutoCompartment ac(cx, target); 1.2016 + 1.2017 + // Grab the relevant expando object. 1.2018 + RootedObject expandoObject(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, 1.2019 + target)); 1.2020 + if (!expandoObject) 1.2021 + return false; 1.2022 + 1.2023 + // Wrap the property descriptor for the target compartment. 1.2024 + Rooted<JSPropertyDescriptor> wrappedDesc(cx, desc); 1.2025 + if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc)) 1.2026 + return false; 1.2027 + 1.2028 + // Fix up Xray waivers. 1.2029 + if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc)) 1.2030 + return false; 1.2031 + 1.2032 + return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc.value(), 1.2033 + wrappedDesc.getter(), wrappedDesc.setter(), 1.2034 + wrappedDesc.get().attrs); 1.2035 +} 1.2036 + 1.2037 +template <typename Base, typename Traits> 1.2038 +bool 1.2039 +XrayWrapper<Base, Traits>::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, 1.2040 + AutoIdVector &props) 1.2041 +{ 1.2042 + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); 1.2043 + return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props); 1.2044 +} 1.2045 + 1.2046 +template <typename Base, typename Traits> 1.2047 +bool 1.2048 +XrayWrapper<Base, Traits>::delete_(JSContext *cx, HandleObject wrapper, 1.2049 + HandleId id, bool *bp) 1.2050 +{ 1.2051 + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); 1.2052 + 1.2053 + // Check the expando object. 1.2054 + RootedObject target(cx, Traits::getTargetObject(wrapper)); 1.2055 + RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); 1.2056 + if (expando) { 1.2057 + JSAutoCompartment ac(cx, expando); 1.2058 + return JS_DeletePropertyById2(cx, expando, id, bp); 1.2059 + } 1.2060 + *bp = true; 1.2061 + return true; 1.2062 +} 1.2063 + 1.2064 +template <typename Base, typename Traits> 1.2065 +bool 1.2066 +XrayWrapper<Base, Traits>::enumerate(JSContext *cx, HandleObject wrapper, unsigned flags, 1.2067 + AutoIdVector &props) 1.2068 +{ 1.2069 + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); 1.2070 + if (!AccessCheck::wrapperSubsumes(wrapper)) { 1.2071 + JS_ReportError(cx, "Not allowed to enumerate cross origin objects"); 1.2072 + return false; 1.2073 + } 1.2074 + 1.2075 + // Enumerate expando properties first. Note that the expando object lives 1.2076 + // in the target compartment. 1.2077 + RootedObject target(cx, Traits::singleton.getTargetObject(wrapper)); 1.2078 + RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); 1.2079 + if (expando) { 1.2080 + JSAutoCompartment ac(cx, expando); 1.2081 + if (!js::GetPropertyNames(cx, expando, flags, &props)) 1.2082 + return false; 1.2083 + } 1.2084 + if (!JS_WrapAutoIdVector(cx, props)) 1.2085 + return false; 1.2086 + 1.2087 + return Traits::singleton.enumerateNames(cx, wrapper, flags, props); 1.2088 +} 1.2089 + 1.2090 +template <typename Base, typename Traits> 1.2091 +bool 1.2092 +XrayWrapper<Base, Traits>::enumerate(JSContext *cx, HandleObject wrapper, 1.2093 + AutoIdVector &props) 1.2094 +{ 1.2095 + return enumerate(cx, wrapper, 0, props); 1.2096 +} 1.2097 + 1.2098 +template <typename Base, typename Traits> 1.2099 +bool 1.2100 +XrayWrapper<Base, Traits>::get(JSContext *cx, HandleObject wrapper, 1.2101 + HandleObject receiver, HandleId id, 1.2102 + MutableHandleValue vp) 1.2103 +{ 1.2104 + // Skip our Base if it isn't already ProxyHandler. 1.2105 + // NB: None of the functions we call are prepared for the receiver not 1.2106 + // being the wrapper, so ignore the receiver here. 1.2107 + return js::BaseProxyHandler::get(cx, wrapper, Traits::HasPrototype ? receiver : wrapper, id, vp); 1.2108 +} 1.2109 + 1.2110 +template <typename Base, typename Traits> 1.2111 +bool 1.2112 +XrayWrapper<Base, Traits>::set(JSContext *cx, HandleObject wrapper, 1.2113 + HandleObject receiver, HandleId id, 1.2114 + bool strict, MutableHandleValue vp) 1.2115 +{ 1.2116 + // Delegate to Traits. 1.2117 + // NB: None of the functions we call are prepared for the receiver not 1.2118 + // being the wrapper, so ignore the receiver here. 1.2119 + return Traits::set(cx, wrapper, Traits::HasPrototype ? receiver : wrapper, id, strict, vp); 1.2120 +} 1.2121 + 1.2122 +template <typename Base, typename Traits> 1.2123 +bool 1.2124 +XrayWrapper<Base, Traits>::has(JSContext *cx, HandleObject wrapper, 1.2125 + HandleId id, bool *bp) 1.2126 +{ 1.2127 + // Skip our Base if it isn't already ProxyHandler. 1.2128 + return js::BaseProxyHandler::has(cx, wrapper, id, bp); 1.2129 +} 1.2130 + 1.2131 +template <typename Base, typename Traits> 1.2132 +bool 1.2133 +XrayWrapper<Base, Traits>::hasOwn(JSContext *cx, HandleObject wrapper, 1.2134 + HandleId id, bool *bp) 1.2135 +{ 1.2136 + // Skip our Base if it isn't already ProxyHandler. 1.2137 + return js::BaseProxyHandler::hasOwn(cx, wrapper, id, bp); 1.2138 +} 1.2139 + 1.2140 +template <typename Base, typename Traits> 1.2141 +bool 1.2142 +XrayWrapper<Base, Traits>::keys(JSContext *cx, HandleObject wrapper, 1.2143 + AutoIdVector &props) 1.2144 +{ 1.2145 + // Skip our Base if it isn't already ProxyHandler. 1.2146 + return js::BaseProxyHandler::keys(cx, wrapper, props); 1.2147 +} 1.2148 + 1.2149 +template <typename Base, typename Traits> 1.2150 +bool 1.2151 +XrayWrapper<Base, Traits>::iterate(JSContext *cx, HandleObject wrapper, 1.2152 + unsigned flags, MutableHandleValue vp) 1.2153 +{ 1.2154 + // Skip our Base if it isn't already ProxyHandler. 1.2155 + return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); 1.2156 +} 1.2157 + 1.2158 +template <typename Base, typename Traits> 1.2159 +bool 1.2160 +XrayWrapper<Base, Traits>::call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) 1.2161 +{ 1.2162 + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL); 1.2163 + return Traits::call(cx, wrapper, args, Base::singleton); 1.2164 +} 1.2165 + 1.2166 +template <typename Base, typename Traits> 1.2167 +bool 1.2168 +XrayWrapper<Base, Traits>::construct(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) 1.2169 +{ 1.2170 + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL); 1.2171 + return Traits::construct(cx, wrapper, args, Base::singleton); 1.2172 +} 1.2173 + 1.2174 +template <typename Base, typename Traits> 1.2175 +bool 1.2176 +XrayWrapper<Base, Traits>::defaultValue(JSContext *cx, HandleObject wrapper, 1.2177 + JSType hint, MutableHandleValue vp) 1.2178 +{ 1.2179 + // Even if this isn't a security wrapper, Xray semantics dictate that we 1.2180 + // run the DefaultValue algorithm directly on the Xray wrapper. 1.2181 + // 1.2182 + // NB: We don't have to worry about things with special [[DefaultValue]] 1.2183 + // behavior like Date because we'll never have an XrayWrapper to them. 1.2184 + return js::DefaultValue(cx, wrapper, hint, vp); 1.2185 +} 1.2186 + 1.2187 +template <typename Base, typename Traits> 1.2188 +bool 1.2189 +XrayWrapper<Base, Traits>::getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, 1.2190 + JS::MutableHandleObject protop) 1.2191 +{ 1.2192 + // We really only want this override for non-SecurityWrapper-inheriting 1.2193 + // |Base|. But doing that statically with templates requires partial method 1.2194 + // specializations (and therefore a helper class), which is all more trouble 1.2195 + // than it's worth. Do a dynamic check. 1.2196 + if (Base::hasSecurityPolicy()) 1.2197 + return Base::getPrototypeOf(cx, wrapper, protop); 1.2198 + 1.2199 + RootedObject target(cx, Traits::getTargetObject(wrapper)); 1.2200 + RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); 1.2201 + 1.2202 + // We want to keep the Xray's prototype distinct from that of content, but 1.2203 + // only if there's been a set. If there's not an expando, or the expando 1.2204 + // slot is |undefined|, hand back the default proto, appropriately wrapped. 1.2205 + 1.2206 + RootedValue v(cx); 1.2207 + if (expando) { 1.2208 + JSAutoCompartment ac(cx, expando); 1.2209 + v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE); 1.2210 + } 1.2211 + if (v.isUndefined()) 1.2212 + return getPrototypeOfHelper(cx, wrapper, target, protop); 1.2213 + 1.2214 + protop.set(v.toObjectOrNull()); 1.2215 + return JS_WrapObject(cx, protop); 1.2216 +} 1.2217 + 1.2218 +template <typename Base, typename Traits> 1.2219 +bool 1.2220 +XrayWrapper<Base, Traits>::setPrototypeOf(JSContext *cx, JS::HandleObject wrapper, 1.2221 + JS::HandleObject proto, bool *bp) 1.2222 +{ 1.2223 + // Do this only for non-SecurityWrapper-inheriting |Base|. See the comment 1.2224 + // in getPrototypeOf(). 1.2225 + if (Base::hasSecurityPolicy()) 1.2226 + return Base::setPrototypeOf(cx, wrapper, proto, bp); 1.2227 + 1.2228 + RootedObject target(cx, Traits::getTargetObject(wrapper)); 1.2229 + RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target)); 1.2230 + 1.2231 + // The expando lives in the target's compartment, so do our installation there. 1.2232 + JSAutoCompartment ac(cx, target); 1.2233 + 1.2234 + RootedValue v(cx, ObjectOrNullValue(proto)); 1.2235 + if (!JS_WrapValue(cx, &v)) 1.2236 + return false; 1.2237 + JS_SetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE, v); 1.2238 + *bp = true; 1.2239 + return true; 1.2240 +} 1.2241 + 1.2242 + 1.2243 +/* 1.2244 + * The Permissive / Security variants should be used depending on whether the 1.2245 + * compartment of the wrapper is guranteed to subsume the compartment of the 1.2246 + * wrapped object (i.e. - whether it is safe from a security perspective to 1.2247 + * unwrap the wrapper). 1.2248 + */ 1.2249 + 1.2250 +template<> 1.2251 +PermissiveXrayXPCWN PermissiveXrayXPCWN::singleton(0); 1.2252 +template class PermissiveXrayXPCWN; 1.2253 + 1.2254 +template<> 1.2255 +SecurityXrayXPCWN SecurityXrayXPCWN::singleton(0); 1.2256 +template class SecurityXrayXPCWN; 1.2257 + 1.2258 +template<> 1.2259 +PermissiveXrayDOM PermissiveXrayDOM::singleton(0); 1.2260 +template class PermissiveXrayDOM; 1.2261 + 1.2262 +template<> 1.2263 +SecurityXrayDOM SecurityXrayDOM::singleton(0); 1.2264 +template class SecurityXrayDOM; 1.2265 + 1.2266 +template<> 1.2267 +PermissiveXrayJS PermissiveXrayJS::singleton(0); 1.2268 +template class PermissiveXrayJS; 1.2269 + 1.2270 +template<> 1.2271 +SCSecurityXrayXPCWN SCSecurityXrayXPCWN::singleton(0); 1.2272 +template class SCSecurityXrayXPCWN; 1.2273 + 1.2274 +static nsQueryInterface 1.2275 +do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper) 1.2276 +{ 1.2277 + nsISupports* nativeSupports = nullptr; 1.2278 + if (IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper)) { 1.2279 + RootedObject target(cx, XrayTraits::getTargetObject(wrapper)); 1.2280 + XrayType type = GetXrayType(target); 1.2281 + if (type == XrayForDOMObject) { 1.2282 + nativeSupports = UnwrapDOMObjectToISupports(target); 1.2283 + } else if (type == XrayForWrappedNative) { 1.2284 + XPCWrappedNative *wn = XPCWrappedNative::Get(target); 1.2285 + nativeSupports = wn->Native(); 1.2286 + } 1.2287 + } else { 1.2288 + nsIXPConnect *xpc = nsXPConnect::XPConnect(); 1.2289 + nativeSupports = xpc->GetNativeOfWrapper(cx, wrapper); 1.2290 + } 1.2291 + 1.2292 + return nsQueryInterface(nativeSupports); 1.2293 +} 1.2294 + 1.2295 +}