michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "XrayWrapper.h" michael@0: #include "AccessCheck.h" michael@0: #include "WrapperFactory.h" michael@0: michael@0: #include "nsIContent.h" michael@0: #include "nsIControllers.h" michael@0: #include "nsContentUtils.h" michael@0: michael@0: #include "XPCWrapper.h" michael@0: #include "xpcprivate.h" michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsprf.h" michael@0: #include "nsJSUtils.h" michael@0: michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/dom/WindowBinding.h" michael@0: #include "nsGlobalWindow.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: using namespace JS; michael@0: using namespace mozilla; michael@0: michael@0: using js::Wrapper; michael@0: using js::BaseProxyHandler; michael@0: using js::IsCrossCompartmentWrapper; michael@0: using js::UncheckedUnwrap; michael@0: using js::CheckedUnwrap; michael@0: michael@0: namespace xpc { michael@0: michael@0: using namespace XrayUtils; michael@0: michael@0: // Whitelist for the standard ES classes we can Xray to. michael@0: static bool michael@0: IsJSXraySupported(JSProtoKey key) michael@0: { michael@0: switch (key) { michael@0: case JSProto_Date: michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: XrayType michael@0: GetXrayType(JSObject *obj) michael@0: { michael@0: obj = js::UncheckedUnwrap(obj, /* stopAtOuter = */ false); michael@0: if (mozilla::dom::UseDOMXray(obj)) michael@0: return XrayForDOMObject; michael@0: michael@0: const js::Class* clasp = js::GetObjectClass(obj); michael@0: if (IS_WN_CLASS(clasp) || clasp->ext.innerObject) michael@0: return XrayForWrappedNative; michael@0: michael@0: JSProtoKey standardProto = IdentifyStandardInstanceOrPrototype(obj); michael@0: if (IsJSXraySupported(standardProto)) michael@0: return XrayForJSObject; michael@0: michael@0: return NotXray; michael@0: } michael@0: michael@0: JSObject * michael@0: XrayAwareCalleeGlobal(JSObject *fun) michael@0: { michael@0: MOZ_ASSERT(js::IsFunctionObject(fun)); michael@0: JSObject *scope = js::GetObjectParent(fun); michael@0: if (IsXrayWrapper(scope)) michael@0: scope = js::UncheckedUnwrap(scope); michael@0: return js::GetGlobalForObjectCrossCompartment(scope); michael@0: } michael@0: michael@0: const uint32_t JSSLOT_RESOLVING = 0; michael@0: ResolvingId::ResolvingId(JSContext *cx, HandleObject wrapper, HandleId id) michael@0: : mId(id), michael@0: mHolder(cx, getHolderObject(wrapper)), michael@0: mPrev(getResolvingId(mHolder)), michael@0: mXrayShadowing(false) michael@0: { michael@0: js::SetReservedSlot(mHolder, JSSLOT_RESOLVING, js::PrivateValue(this)); michael@0: } michael@0: michael@0: ResolvingId::~ResolvingId() michael@0: { michael@0: MOZ_ASSERT(getResolvingId(mHolder) == this, "unbalanced ResolvingIds"); michael@0: js::SetReservedSlot(mHolder, JSSLOT_RESOLVING, js::PrivateValue(mPrev)); michael@0: } michael@0: michael@0: bool michael@0: ResolvingId::isXrayShadowing(jsid id) michael@0: { michael@0: if (!mXrayShadowing) michael@0: return false; michael@0: michael@0: return mId == id; michael@0: } michael@0: michael@0: bool michael@0: ResolvingId::isResolving(jsid id) michael@0: { michael@0: for (ResolvingId *cur = this; cur; cur = cur->mPrev) { michael@0: if (cur->mId == id) michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: ResolvingId * michael@0: ResolvingId::getResolvingId(JSObject *holder) michael@0: { michael@0: MOZ_ASSERT(strcmp(JS_GetClass(holder)->name, "NativePropertyHolder") == 0); michael@0: return (ResolvingId *)js::GetReservedSlot(holder, JSSLOT_RESOLVING).toPrivate(); michael@0: } michael@0: michael@0: JSObject * michael@0: ResolvingId::getHolderObject(JSObject *wrapper) michael@0: { michael@0: return &js::GetProxyExtra(wrapper, 0).toObject(); michael@0: } michael@0: michael@0: ResolvingId * michael@0: ResolvingId::getResolvingIdFromWrapper(JSObject *wrapper) michael@0: { michael@0: return getResolvingId(getHolderObject(wrapper)); michael@0: } michael@0: michael@0: class MOZ_STACK_CLASS ResolvingIdDummy michael@0: { michael@0: public: michael@0: ResolvingIdDummy(JSContext *cx, HandleObject wrapper, HandleId id) michael@0: { michael@0: } michael@0: }; michael@0: michael@0: class XrayTraits michael@0: { michael@0: public: michael@0: static JSObject* getTargetObject(JSObject *wrapper) { michael@0: return js::UncheckedUnwrap(wrapper, /* stopAtOuter = */ false); michael@0: } michael@0: michael@0: virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) = 0; michael@0: // NB: resolveOwnProperty may decide whether or not to cache what it finds michael@0: // on the holder. If the result is not cached, the lookup will happen afresh michael@0: // for each access, which is the right thing for things like dynamic NodeList michael@0: // properties. michael@0: virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, michael@0: HandleObject wrapper, HandleObject holder, michael@0: HandleId id, MutableHandle desc); michael@0: michael@0: virtual void preserveWrapper(JSObject *target) = 0; michael@0: michael@0: static bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, michael@0: bool strict, MutableHandleValue vp); michael@0: michael@0: JSObject* getExpandoObject(JSContext *cx, HandleObject target, michael@0: HandleObject consumer); michael@0: JSObject* ensureExpandoObject(JSContext *cx, HandleObject wrapper, michael@0: HandleObject target); michael@0: michael@0: JSObject* getHolder(JSObject *wrapper); michael@0: JSObject* ensureHolder(JSContext *cx, HandleObject wrapper); michael@0: virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) = 0; michael@0: michael@0: JSObject* getExpandoChain(HandleObject obj) { michael@0: return GetObjectScope(obj)->GetExpandoChain(obj); michael@0: } michael@0: michael@0: bool setExpandoChain(JSContext *cx, HandleObject obj, HandleObject chain) { michael@0: return GetObjectScope(obj)->SetExpandoChain(cx, obj, chain); michael@0: } michael@0: bool cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src); michael@0: michael@0: private: michael@0: bool expandoObjectMatchesConsumer(JSContext *cx, HandleObject expandoObject, michael@0: nsIPrincipal *consumerOrigin, michael@0: HandleObject exclusiveGlobal); michael@0: JSObject* getExpandoObjectInternal(JSContext *cx, HandleObject target, michael@0: nsIPrincipal *origin, michael@0: JSObject *exclusiveGlobal); michael@0: JSObject* attachExpandoObject(JSContext *cx, HandleObject target, michael@0: nsIPrincipal *origin, michael@0: HandleObject exclusiveGlobal); michael@0: }; michael@0: michael@0: class XPCWrappedNativeXrayTraits : public XrayTraits michael@0: { michael@0: public: michael@0: enum { michael@0: HasPrototype = 0 michael@0: }; michael@0: michael@0: static const XrayType Type = XrayForWrappedNative; michael@0: michael@0: virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc, michael@0: Handle existingDesc, bool *defined); michael@0: virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, michael@0: AutoIdVector &props); michael@0: static bool call(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, js::Wrapper& baseInstance); michael@0: static bool construct(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, js::Wrapper& baseInstance); michael@0: michael@0: static bool isResolving(JSContext *cx, JSObject *holder, jsid id); michael@0: michael@0: static bool resolveDOMCollectionProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc); michael@0: michael@0: static XPCWrappedNative* getWN(JSObject *wrapper) { michael@0: return XPCWrappedNative::Get(getTargetObject(wrapper)); michael@0: } michael@0: michael@0: virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE; michael@0: michael@0: typedef ResolvingId ResolvingIdImpl; michael@0: michael@0: virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; michael@0: michael@0: static const JSClass HolderClass; michael@0: static XPCWrappedNativeXrayTraits singleton; michael@0: }; michael@0: michael@0: const JSClass XPCWrappedNativeXrayTraits::HolderClass = { michael@0: "NativePropertyHolder", JSCLASS_HAS_RESERVED_SLOTS(2), michael@0: JS_PropertyStub, JS_DeletePropertyStub, holder_get, holder_set, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub michael@0: }; michael@0: michael@0: class DOMXrayTraits : public XrayTraits michael@0: { michael@0: public: michael@0: enum { michael@0: HasPrototype = 0 michael@0: }; michael@0: michael@0: static const XrayType Type = XrayForDOMObject; michael@0: michael@0: virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc, michael@0: Handle existingDesc, bool *defined); michael@0: static bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, michael@0: bool strict, MutableHandleValue vp); michael@0: virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, michael@0: AutoIdVector &props); michael@0: static bool call(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, js::Wrapper& baseInstance); michael@0: static bool construct(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, js::Wrapper& baseInstance); michael@0: michael@0: static bool isResolving(JSContext *cx, JSObject *holder, jsid id) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: typedef ResolvingIdDummy ResolvingIdImpl; michael@0: michael@0: virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE; michael@0: michael@0: virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; michael@0: michael@0: static DOMXrayTraits singleton; michael@0: }; michael@0: michael@0: class JSXrayTraits : public XrayTraits michael@0: { michael@0: public: michael@0: enum { michael@0: HasPrototype = 1 michael@0: }; michael@0: static const XrayType Type = XrayForJSObject; michael@0: michael@0: virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("resolveNativeProperty hook should never be called with HasPrototype = 1"); michael@0: } michael@0: michael@0: virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: michael@0: static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc, michael@0: Handle existingDesc, bool *defined) michael@0: { michael@0: // There's no useful per-trait work to do here. Punt back up to the common code. michael@0: *defined = false; michael@0: return true; michael@0: } michael@0: michael@0: virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, michael@0: AutoIdVector &props); michael@0: michael@0: static bool call(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, js::Wrapper& baseInstance) michael@0: { michael@0: // We'll handle this when we start supporting Functions. michael@0: RootedValue v(cx, ObjectValue(*wrapper)); michael@0: js_ReportIsNotFunction(cx, v); michael@0: return false; michael@0: } michael@0: michael@0: static bool construct(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, js::Wrapper& baseInstance) michael@0: { michael@0: // We'll handle this when we start supporting Functions. michael@0: RootedValue v(cx, ObjectValue(*wrapper)); michael@0: js_ReportIsNotFunction(cx, v); michael@0: return false; michael@0: } michael@0: michael@0: static bool isResolving(JSContext *cx, JSObject *holder, jsid id) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: typedef ResolvingIdDummy ResolvingIdImpl; michael@0: michael@0: bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, michael@0: JS::HandleObject target, michael@0: JS::MutableHandleObject protop) michael@0: { michael@0: RootedObject holder(cx, ensureHolder(cx, wrapper)); michael@0: JSProtoKey key = isPrototype(holder) ? JSProto_Object michael@0: : getProtoKey(holder); michael@0: { michael@0: JSAutoCompartment ac(cx, target); michael@0: if (!JS_GetClassPrototype(cx, key, protop)) michael@0: return nullptr; michael@0: } michael@0: return JS_WrapObject(cx, protop); michael@0: } michael@0: michael@0: virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE { michael@0: // In the case of pure JS objects, there is no underlying object, and michael@0: // the target is the canonical representation of state. If it gets michael@0: // collected, then expandos and such should be collected too. So there's michael@0: // nothing to do here. michael@0: } michael@0: michael@0: enum { michael@0: SLOT_PROTOKEY = 0, michael@0: SLOT_ISPROTOTYPE, michael@0: SLOT_COUNT michael@0: }; michael@0: virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; michael@0: michael@0: static JSProtoKey getProtoKey(JSObject *holder) { michael@0: int32_t key = js::GetReservedSlot(holder, SLOT_PROTOKEY).toInt32(); michael@0: return static_cast(key); michael@0: } michael@0: michael@0: static bool isPrototype(JSObject *holder) { michael@0: return js::GetReservedSlot(holder, SLOT_ISPROTOTYPE).toBoolean(); michael@0: } michael@0: michael@0: static const JSClass HolderClass; michael@0: static JSXrayTraits singleton; michael@0: }; michael@0: michael@0: const JSClass JSXrayTraits::HolderClass = { michael@0: "JSXrayHolder", JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), michael@0: JS_PropertyStub, JS_DeletePropertyStub, michael@0: JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub michael@0: }; michael@0: michael@0: bool michael@0: JSXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, michael@0: HandleObject wrapper, HandleObject holder, michael@0: HandleId id, michael@0: MutableHandle desc) michael@0: { michael@0: // Call the common code. michael@0: bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, michael@0: id, desc); michael@0: if (!ok || desc.object()) michael@0: return ok; michael@0: michael@0: // Non-prototypes don't have anything on them yet. michael@0: if (!isPrototype(holder)) michael@0: return true; michael@0: michael@0: // The non-HasPrototypes semantics implemented by traditional Xrays are kind michael@0: // of broken with respect to |own|-ness and the holder. The common code michael@0: // muddles through by only checking the holder for non-|own| lookups, but michael@0: // that doesn't work for us. So we do an explicit holder check here, and hope michael@0: // that this mess gets fixed up soon. michael@0: if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) michael@0: return false; michael@0: if (desc.object()) { michael@0: desc.object().set(wrapper); michael@0: return true; michael@0: } michael@0: michael@0: // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. michael@0: RootedObject target(cx, getTargetObject(wrapper)); michael@0: const js::Class *clasp = js::GetObjectClass(target); michael@0: JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); michael@0: MOZ_ASSERT(protoKey == getProtoKey(holder)); michael@0: MOZ_ASSERT(clasp->spec.defined()); michael@0: michael@0: // Handle the 'constructor' property. michael@0: if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) { michael@0: RootedObject constructor(cx); michael@0: { michael@0: JSAutoCompartment ac(cx, target); michael@0: if (!JS_GetClassObject(cx, protoKey, &constructor)) michael@0: return false; michael@0: } michael@0: if (!JS_WrapObject(cx, &constructor)) michael@0: return false; michael@0: desc.object().set(wrapper); michael@0: desc.setAttributes(0); michael@0: desc.setGetter(nullptr); michael@0: desc.setSetter(nullptr); michael@0: desc.value().setObject(*constructor); michael@0: return true; michael@0: } michael@0: michael@0: // Find the properties available, if any. michael@0: const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; michael@0: if (!fs) michael@0: return true; michael@0: michael@0: // Compute the property name we're looking for. We'll handle indexed michael@0: // properties when we start supporting arrays. michael@0: if (!JSID_IS_STRING(id)) michael@0: return true; michael@0: Rooted str(cx, JSID_TO_FLAT_STRING(id)); michael@0: michael@0: // Scan through the properties. If we don't find anything, we're done. michael@0: for (; fs->name; ++fs) { michael@0: // We don't support self-hosted functions yet. See bug 972987. michael@0: if (fs->selfHostedName) michael@0: continue; michael@0: if (JS_FlatStringEqualsAscii(str, fs->name)) michael@0: break; michael@0: } michael@0: if (!fs->name) michael@0: return true; michael@0: michael@0: // Generate an Xrayed version of the method. michael@0: Rooted fun(cx, JS_NewFunctionById(cx, fs->call.op, fs->nargs, michael@0: 0, wrapper, id)); michael@0: if (!fun) michael@0: return false; michael@0: michael@0: // The generic Xray machinery only defines non-own properties on the holder. michael@0: // This is broken, and will be fixed at some point, but for now we need to michael@0: // cache the value explicitly. See the corresponding call to michael@0: // JS_GetPropertyById at the top of this function. michael@0: return JS_DefinePropertyById(cx, holder, id, michael@0: ObjectValue(*JS_GetFunctionObject(fun)), michael@0: nullptr, nullptr, 0) && michael@0: JS_GetPropertyDescriptorById(cx, holder, id, desc); michael@0: } michael@0: michael@0: bool michael@0: JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, michael@0: AutoIdVector &props) michael@0: { michael@0: RootedObject holder(cx, ensureHolder(cx, wrapper)); michael@0: if (!holder) michael@0: return false; michael@0: michael@0: // Non-prototypes don't have anything on them yet. michael@0: if (!isPrototype(holder)) michael@0: return true; michael@0: michael@0: // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. michael@0: RootedObject target(cx, getTargetObject(wrapper)); michael@0: const js::Class *clasp = js::GetObjectClass(target); michael@0: MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == getProtoKey(holder)); michael@0: MOZ_ASSERT(clasp->spec.defined()); michael@0: michael@0: // Find the properties available, if any. michael@0: const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; michael@0: if (!fs) michael@0: return true; michael@0: michael@0: // Intern all the strings, and pass theme to the caller. michael@0: for (; fs->name; ++fs) { michael@0: // We don't support self-hosted functions yet. See bug 972987. michael@0: if (fs->selfHostedName) michael@0: continue; michael@0: RootedString str(cx, JS_InternString(cx, fs->name)); michael@0: if (!str) michael@0: return false; michael@0: if (!props.append(INTERNED_STRING_TO_JSID(cx, str))) michael@0: return false; michael@0: } michael@0: michael@0: // Add the 'constructor' property. michael@0: return props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)); michael@0: } michael@0: michael@0: JSObject* michael@0: JSXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) michael@0: { michael@0: RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); michael@0: RootedObject target(cx, getTargetObject(wrapper)); michael@0: RootedObject holder(cx, JS_NewObjectWithGivenProto(cx, &HolderClass, michael@0: JS::NullPtr(), global)); michael@0: if (!holder) michael@0: return nullptr; michael@0: michael@0: // Compute information about the target. michael@0: bool isPrototype = false; michael@0: JSProtoKey key = IdentifyStandardInstance(target); michael@0: if (key == JSProto_Null) { michael@0: isPrototype = true; michael@0: key = IdentifyStandardPrototype(target); michael@0: } michael@0: MOZ_ASSERT(key != JSProto_Null); michael@0: michael@0: // Store it on the holder. michael@0: RootedValue v(cx); michael@0: v.setNumber(static_cast(key)); michael@0: js::SetReservedSlot(holder, SLOT_PROTOKEY, v); michael@0: v.setBoolean(isPrototype); michael@0: js::SetReservedSlot(holder, SLOT_ISPROTOTYPE, v); michael@0: michael@0: return holder; michael@0: } michael@0: michael@0: XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton; michael@0: DOMXrayTraits DOMXrayTraits::singleton; michael@0: JSXrayTraits JSXrayTraits::singleton; michael@0: michael@0: XrayTraits* michael@0: GetXrayTraits(JSObject *obj) michael@0: { michael@0: switch (GetXrayType(obj)) { michael@0: case XrayForDOMObject: michael@0: return &DOMXrayTraits::singleton; michael@0: case XrayForWrappedNative: michael@0: return &XPCWrappedNativeXrayTraits::singleton; michael@0: case XrayForJSObject: michael@0: return &JSXrayTraits::singleton; michael@0: default: michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Xray expando handling. michael@0: * michael@0: * We hang expandos for Xray wrappers off a reserved slot on the target object michael@0: * so that same-origin compartments can share expandos for a given object. We michael@0: * have a linked list of expando objects, one per origin. The properties on these michael@0: * objects are generally wrappers pointing back to the compartment that applied michael@0: * them. michael@0: * michael@0: * The expando objects should _never_ be exposed to script. The fact that they michael@0: * live in the target compartment is a detail of the implementation, and does michael@0: * not imply that code in the target compartment should be allowed to inspect michael@0: * them. They are private to the origin that placed them. michael@0: */ michael@0: michael@0: enum ExpandoSlots { michael@0: JSSLOT_EXPANDO_NEXT = 0, michael@0: JSSLOT_EXPANDO_ORIGIN, michael@0: JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, michael@0: JSSLOT_EXPANDO_PROTOTYPE, michael@0: JSSLOT_EXPANDO_COUNT michael@0: }; michael@0: michael@0: static nsIPrincipal* michael@0: ObjectPrincipal(JSObject *obj) michael@0: { michael@0: return GetCompartmentPrincipal(js::GetObjectCompartment(obj)); michael@0: } michael@0: michael@0: static nsIPrincipal* michael@0: GetExpandoObjectPrincipal(JSObject *expandoObject) michael@0: { michael@0: Value v = JS_GetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN); michael@0: return static_cast(v.toPrivate()); michael@0: } michael@0: michael@0: static void michael@0: ExpandoObjectFinalize(JSFreeOp *fop, JSObject *obj) michael@0: { michael@0: // Release the principal. michael@0: nsIPrincipal *principal = GetExpandoObjectPrincipal(obj); michael@0: NS_RELEASE(principal); michael@0: } michael@0: michael@0: const JSClass ExpandoObjectClass = { michael@0: "XrayExpandoObject", michael@0: JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT), michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ExpandoObjectFinalize michael@0: }; michael@0: michael@0: bool michael@0: XrayTraits::expandoObjectMatchesConsumer(JSContext *cx, michael@0: HandleObject expandoObject, michael@0: nsIPrincipal *consumerOrigin, michael@0: HandleObject exclusiveGlobal) michael@0: { michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx)); michael@0: michael@0: // First, compare the principals. michael@0: nsIPrincipal *o = GetExpandoObjectPrincipal(expandoObject); michael@0: // Note that it's very important here to ignore document.domain. We michael@0: // pull the principal for the expando object off of the first consumer michael@0: // for a given origin, and freely share the expandos amongst multiple michael@0: // same-origin consumers afterwards. However, this means that we have michael@0: // no way to know whether _all_ consumers have opted in to collaboration michael@0: // by explicitly setting document.domain. So we just mandate that expando michael@0: // sharing is unaffected by it. michael@0: if (!consumerOrigin->Equals(o)) michael@0: return false; michael@0: michael@0: // Sandboxes want exclusive expando objects. michael@0: JSObject *owner = JS_GetReservedSlot(expandoObject, michael@0: JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL) michael@0: .toObjectOrNull(); michael@0: if (!owner && !exclusiveGlobal) michael@0: return true; michael@0: michael@0: // The exclusive global should always be wrapped in the target's compartment. michael@0: MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx)); michael@0: MOZ_ASSERT(!owner || js::IsObjectInContextCompartment(owner, cx)); michael@0: return owner == exclusiveGlobal; michael@0: } michael@0: michael@0: JSObject * michael@0: XrayTraits::getExpandoObjectInternal(JSContext *cx, HandleObject target, michael@0: nsIPrincipal *origin, michael@0: JSObject *exclusiveGlobalArg) michael@0: { michael@0: // The expando object lives in the compartment of the target, so all our michael@0: // work needs to happen there. michael@0: RootedObject exclusiveGlobal(cx, exclusiveGlobalArg); michael@0: JSAutoCompartment ac(cx, target); michael@0: if (!JS_WrapObject(cx, &exclusiveGlobal)) michael@0: return nullptr; michael@0: michael@0: // Iterate through the chain, looking for a same-origin object. michael@0: RootedObject head(cx, getExpandoChain(target)); michael@0: while (head) { michael@0: if (expandoObjectMatchesConsumer(cx, head, origin, exclusiveGlobal)) michael@0: return head; michael@0: head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); michael@0: } michael@0: michael@0: // Not found. michael@0: return nullptr; michael@0: } michael@0: michael@0: JSObject * michael@0: XrayTraits::getExpandoObject(JSContext *cx, HandleObject target, HandleObject consumer) michael@0: { michael@0: JSObject *consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer); michael@0: bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox"); michael@0: return getExpandoObjectInternal(cx, target, ObjectPrincipal(consumer), michael@0: isSandbox ? consumerGlobal : nullptr); michael@0: } michael@0: michael@0: JSObject * michael@0: XrayTraits::attachExpandoObject(JSContext *cx, HandleObject target, michael@0: nsIPrincipal *origin, HandleObject exclusiveGlobal) michael@0: { michael@0: // Make sure the compartments are sane. michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx)); michael@0: MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx)); michael@0: michael@0: // No duplicates allowed. michael@0: MOZ_ASSERT(!getExpandoObjectInternal(cx, target, origin, exclusiveGlobal)); michael@0: michael@0: // Create the expando object. We parent it directly to the target object. michael@0: RootedObject expandoObject(cx, JS_NewObjectWithGivenProto(cx, &ExpandoObjectClass, michael@0: JS::NullPtr(), target)); michael@0: if (!expandoObject) michael@0: return nullptr; michael@0: michael@0: // AddRef and store the principal. michael@0: NS_ADDREF(origin); michael@0: JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, PRIVATE_TO_JSVAL(origin)); michael@0: michael@0: // Note the exclusive global, if any. michael@0: JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, michael@0: OBJECT_TO_JSVAL(exclusiveGlobal)); michael@0: michael@0: // If this is our first expando object, take the opportunity to preserve michael@0: // the wrapper. This keeps our expandos alive even if the Xray wrapper gets michael@0: // collected. michael@0: RootedObject chain(cx, getExpandoChain(target)); michael@0: if (!chain) michael@0: preserveWrapper(target); michael@0: michael@0: // Insert it at the front of the chain. michael@0: JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, OBJECT_TO_JSVAL(chain)); michael@0: setExpandoChain(cx, target, expandoObject); michael@0: michael@0: return expandoObject; michael@0: } michael@0: michael@0: JSObject * michael@0: XrayTraits::ensureExpandoObject(JSContext *cx, HandleObject wrapper, michael@0: HandleObject target) michael@0: { michael@0: // Expando objects live in the target compartment. michael@0: JSAutoCompartment ac(cx, target); michael@0: JSObject *expandoObject = getExpandoObject(cx, target, wrapper); michael@0: if (!expandoObject) { michael@0: // If the object is a sandbox, we don't want it to share expandos with michael@0: // anyone else, so we tag it with the sandbox global. michael@0: // michael@0: // NB: We first need to check the class, _then_ wrap for the target's michael@0: // compartment. michael@0: RootedObject consumerGlobal(cx, js::GetGlobalForObjectCrossCompartment(wrapper)); michael@0: bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox"); michael@0: if (!JS_WrapObject(cx, &consumerGlobal)) michael@0: return nullptr; michael@0: expandoObject = attachExpandoObject(cx, target, ObjectPrincipal(wrapper), michael@0: isSandbox ? (HandleObject)consumerGlobal : NullPtr()); michael@0: } michael@0: return expandoObject; michael@0: } michael@0: michael@0: bool michael@0: XrayTraits::cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src) michael@0: { michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(dst, cx)); michael@0: MOZ_ASSERT(getExpandoChain(dst) == nullptr); michael@0: michael@0: RootedObject oldHead(cx, getExpandoChain(src)); michael@0: while (oldHead) { michael@0: RootedObject exclusive(cx, JS_GetReservedSlot(oldHead, michael@0: JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL) michael@0: .toObjectOrNull()); michael@0: if (!JS_WrapObject(cx, &exclusive)) michael@0: return false; michael@0: RootedObject newHead(cx, attachExpandoObject(cx, dst, GetExpandoObjectPrincipal(oldHead), michael@0: exclusive)); michael@0: if (!JS_CopyPropertiesFrom(cx, newHead, oldHead)) michael@0: return false; michael@0: oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: namespace XrayUtils { michael@0: bool CloneExpandoChain(JSContext *cx, JSObject *dstArg, JSObject *srcArg) michael@0: { michael@0: RootedObject dst(cx, dstArg); michael@0: RootedObject src(cx, srcArg); michael@0: return GetXrayTraits(src)->cloneExpandoChain(cx, dst, src); michael@0: } michael@0: } michael@0: michael@0: static JSObject * michael@0: GetHolder(JSObject *obj) michael@0: { michael@0: return &js::GetProxyExtra(obj, 0).toObject(); michael@0: } michael@0: michael@0: JSObject* michael@0: XrayTraits::getHolder(JSObject *wrapper) michael@0: { michael@0: MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); michael@0: js::Value v = js::GetProxyExtra(wrapper, 0); michael@0: return v.isObject() ? &v.toObject() : nullptr; michael@0: } michael@0: michael@0: JSObject* michael@0: XrayTraits::ensureHolder(JSContext *cx, HandleObject wrapper) michael@0: { michael@0: RootedObject holder(cx, getHolder(wrapper)); michael@0: if (holder) michael@0: return holder; michael@0: holder = createHolder(cx, wrapper); // virtual trap. michael@0: if (holder) michael@0: js::SetProxyExtra(wrapper, 0, ObjectValue(*holder)); michael@0: return holder; michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeXrayTraits::isResolving(JSContext *cx, JSObject *holder, michael@0: jsid id) michael@0: { michael@0: ResolvingId *cur = ResolvingId::getResolvingId(holder); michael@0: if (!cur) michael@0: return false; michael@0: return cur->isResolving(id); michael@0: } michael@0: michael@0: namespace XrayUtils { michael@0: michael@0: bool michael@0: IsXPCWNHolderClass(const JSClass *clasp) michael@0: { michael@0: return clasp == &XPCWrappedNativeXrayTraits::HolderClass; michael@0: } michael@0: michael@0: } michael@0: michael@0: michael@0: // Some DOM objects have shared properties that don't have an explicit michael@0: // getter/setter and rely on the class getter/setter. We install a michael@0: // class getter/setter on the holder object to trigger them. michael@0: bool michael@0: holder_get(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandleValue vp) michael@0: { michael@0: // JSClass::getProperty is wacky enough that it's hard to be sure someone michael@0: // can't inherit this getter by prototyping a random object to an michael@0: // XrayWrapper. Be safe. michael@0: NS_ENSURE_TRUE(WrapperFactory::IsXrayWrapper(wrapper), true); michael@0: JSObject *holder = GetHolder(wrapper); michael@0: michael@0: XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); michael@0: if (NATIVE_HAS_FLAG(wn, WantGetProperty)) { michael@0: JSAutoCompartment ac(cx, holder); michael@0: bool retval = true; michael@0: nsresult rv = wn->GetScriptableCallback()->GetProperty(wn, cx, wrapper, michael@0: id, vp.address(), &retval); michael@0: if (NS_FAILED(rv) || !retval) { michael@0: if (retval) michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: holder_set(JSContext *cx, HandleObject wrapper, HandleId id, bool strict, MutableHandleValue vp) michael@0: { michael@0: // JSClass::setProperty is wacky enough that it's hard to be sure someone michael@0: // can't inherit this getter by prototyping a random object to an michael@0: // XrayWrapper. Be safe. michael@0: NS_ENSURE_TRUE(WrapperFactory::IsXrayWrapper(wrapper), true); michael@0: JSObject *holder = GetHolder(wrapper); michael@0: if (XPCWrappedNativeXrayTraits::isResolving(cx, holder, id)) { michael@0: return true; michael@0: } michael@0: michael@0: XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); michael@0: if (NATIVE_HAS_FLAG(wn, WantSetProperty)) { michael@0: JSAutoCompartment ac(cx, holder); michael@0: bool retval = true; michael@0: nsresult rv = wn->GetScriptableCallback()->SetProperty(wn, cx, wrapper, michael@0: id, vp.address(), &retval); michael@0: if (NS_FAILED(rv) || !retval) { michael@0: if (retval) michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: class AutoSetWrapperNotShadowing michael@0: { michael@0: public: michael@0: AutoSetWrapperNotShadowing(ResolvingId *resolvingId MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: MOZ_ASSERT(resolvingId); michael@0: mResolvingId = resolvingId; michael@0: mResolvingId->mXrayShadowing = true; michael@0: } michael@0: michael@0: ~AutoSetWrapperNotShadowing() michael@0: { michael@0: mResolvingId->mXrayShadowing = false; michael@0: } michael@0: michael@0: private: michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: ResolvingId *mResolvingId; michael@0: }; michael@0: michael@0: // This is called after the resolveNativeProperty could not find any property michael@0: // with the given id. At this point we can check for DOM specific collections michael@0: // like document["formName"] because we already know that it is not shadowing michael@0: // any native property. michael@0: bool michael@0: XPCWrappedNativeXrayTraits::resolveDOMCollectionProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) michael@0: { michael@0: // If we are not currently resolving this id and resolveNative is called michael@0: // we don't do anything. (see defineProperty in case of shadowing is forbidden). michael@0: ResolvingId *rid = ResolvingId::getResolvingId(holder); michael@0: if (!rid || rid->mId != id) michael@0: return true; michael@0: michael@0: XPCWrappedNative *wn = getWN(wrapper); michael@0: if (!wn) { michael@0: // This should NEVER happen, but let's be extra careful here michael@0: // because of the reported crashes (Bug 832091). michael@0: XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); michael@0: return false; michael@0: } michael@0: if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) michael@0: return true; michael@0: michael@0: ResolvingId *resolvingId = ResolvingId::getResolvingIdFromWrapper(wrapper); michael@0: if (!resolvingId) { michael@0: // This should NEVER happen, but let's be extra careful here michael@0: // becaue of the reported crashes (Bug 832091). michael@0: XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); michael@0: return false; michael@0: } michael@0: michael@0: // Setting the current ResolvingId in non-shadowing mode. So for this id michael@0: // Xray won't ignore DOM specific collection properties temporarily. michael@0: AutoSetWrapperNotShadowing asw(resolvingId); michael@0: michael@0: bool retval = true; michael@0: RootedObject pobj(cx); michael@0: nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, wrapper, id, michael@0: pobj.address(), &retval); michael@0: if (NS_FAILED(rv)) { michael@0: if (retval) michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: if (pobj && !JS_GetPropertyDescriptorById(cx, holder, id, desc)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static nsGlobalWindow* michael@0: AsWindow(JSContext *cx, JSObject *wrapper) michael@0: { michael@0: nsGlobalWindow* win; michael@0: // We want to use our target object here, since we don't want to be michael@0: // doing a security check while unwrapping. michael@0: JSObject* target = XrayTraits::getTargetObject(wrapper); michael@0: nsresult rv = UNWRAP_OBJECT(Window, target, win); michael@0: if (NS_SUCCEEDED(rv)) michael@0: return win; michael@0: michael@0: nsCOMPtr piWin = do_QueryInterface( michael@0: nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, target)); michael@0: return static_cast(piWin.get()); michael@0: } michael@0: michael@0: static bool michael@0: IsWindow(JSContext *cx, JSObject *wrapper) michael@0: { michael@0: return !!AsWindow(cx, wrapper); michael@0: } michael@0: michael@0: static nsQueryInterface michael@0: do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper); michael@0: michael@0: void michael@0: XPCWrappedNativeXrayTraits::preserveWrapper(JSObject *target) michael@0: { michael@0: XPCWrappedNative *wn = XPCWrappedNative::Get(target); michael@0: nsRefPtr ci; michael@0: CallQueryInterface(wn->Native(), getter_AddRefs(ci)); michael@0: if (ci) michael@0: ci->PreserveWrapper(wn->Native()); michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) michael@0: { michael@0: MOZ_ASSERT(js::GetObjectJSClass(holder) == &HolderClass); michael@0: michael@0: desc.object().set(nullptr); michael@0: michael@0: // This will do verification and the method lookup for us. michael@0: RootedObject target(cx, getTargetObject(wrapper)); michael@0: XPCCallContext ccx(JS_CALLER, cx, target, NullPtr(), id); michael@0: michael@0: // There are no native numeric properties, so we can shortcut here. We will michael@0: // not find the property. However we want to support non shadowing dom michael@0: // specific collection properties like window.frames, so we still have to michael@0: // check for those. michael@0: if (!JSID_IS_STRING(id)) { michael@0: /* Not found */ michael@0: return resolveDOMCollectionProperty(cx, wrapper, holder, id, desc); michael@0: } michael@0: michael@0: michael@0: // The |controllers| property is accessible as a [ChromeOnly] property on michael@0: // Window.WebIDL, and [noscript] in XPIDL. Chrome needs to see this over michael@0: // Xray, so we need to special-case it until we move |Window| to WebIDL. michael@0: nsGlobalWindow *win = nullptr; michael@0: if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONTROLLERS) && michael@0: AccessCheck::isChrome(wrapper) && michael@0: (win = AsWindow(cx, wrapper))) michael@0: { michael@0: nsCOMPtr c; michael@0: nsresult rv = win->GetControllers(getter_AddRefs(c)); michael@0: if (NS_SUCCEEDED(rv) && c) { michael@0: rv = nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, CurrentGlobalOrNull(cx), michael@0: c, nullptr, nullptr, true, michael@0: desc.value()); michael@0: } michael@0: michael@0: if (NS_FAILED(rv) || !c) { michael@0: JS_ReportError(cx, "Failed to invoke GetControllers via Xrays"); michael@0: return false; michael@0: } michael@0: michael@0: desc.object().set(wrapper); michael@0: return true; michael@0: } michael@0: michael@0: XPCNativeInterface *iface; michael@0: XPCNativeMember *member; michael@0: XPCWrappedNative *wn = getWN(wrapper); michael@0: michael@0: if (ccx.GetWrapper() != wn || !wn->IsValid()) { michael@0: // Something is wrong. If the wrapper is not even valid let's not risk michael@0: // calling resolveDOMCollectionProperty. michael@0: return true; michael@0: } else if (!(iface = ccx.GetInterface()) || michael@0: !(member = ccx.GetMember())) { michael@0: /* Not found */ michael@0: return resolveDOMCollectionProperty(cx, wrapper, holder, id, desc); michael@0: } michael@0: michael@0: desc.object().set(holder); michael@0: desc.setAttributes(JSPROP_ENUMERATE); michael@0: desc.setGetter(nullptr); michael@0: desc.setSetter(nullptr); michael@0: desc.value().set(JSVAL_VOID); michael@0: michael@0: RootedValue fval(cx, JSVAL_VOID); michael@0: if (member->IsConstant()) { michael@0: if (!member->GetConstantValue(ccx, iface, desc.value().address())) { michael@0: JS_ReportError(cx, "Failed to convert constant native property to JS value"); michael@0: return false; michael@0: } michael@0: } else if (member->IsAttribute()) { michael@0: // This is a getter/setter. Clone a function for it. michael@0: if (!member->NewFunctionObject(ccx, iface, wrapper, fval.address())) { michael@0: JS_ReportError(cx, "Failed to clone function object for native getter/setter"); michael@0: return false; michael@0: } michael@0: michael@0: unsigned attrs = desc.attributes(); michael@0: attrs |= JSPROP_GETTER; michael@0: if (member->IsWritableAttribute()) michael@0: attrs |= JSPROP_SETTER; michael@0: michael@0: // Make the property shared on the holder so no slot is allocated michael@0: // for it. This avoids keeping garbage alive through that slot. michael@0: attrs |= JSPROP_SHARED; michael@0: desc.setAttributes(attrs); michael@0: } else { michael@0: // This is a method. Clone a function for it. michael@0: if (!member->NewFunctionObject(ccx, iface, wrapper, desc.value().address())) { michael@0: JS_ReportError(cx, "Failed to clone function object for native function"); michael@0: return false; michael@0: } michael@0: michael@0: // Without a wrapper the function would live on the prototype. Since we michael@0: // don't have one, we have to avoid calling the scriptable helper's michael@0: // GetProperty method for this property, so stub out the getter and michael@0: // setter here explicitly. michael@0: desc.setGetter(JS_PropertyStub); michael@0: desc.setSetter(JS_StrictPropertyStub); michael@0: } michael@0: michael@0: if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval)) michael@0: return false; michael@0: michael@0: if (desc.hasGetterObject()) michael@0: desc.setGetterObject(&fval.toObject()); michael@0: if (desc.hasSetterObject()) michael@0: desc.setSetterObject(&fval.toObject()); michael@0: michael@0: // Define the property. michael@0: return JS_DefinePropertyById(cx, holder, id, desc.value(), michael@0: desc.getter(), desc.setter(), desc.attributes()); michael@0: } michael@0: michael@0: static bool michael@0: wrappedJSObject_getter(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandleValue vp) michael@0: { michael@0: if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) { michael@0: JS_ReportError(cx, "Unexpected object"); michael@0: return false; michael@0: } michael@0: michael@0: vp.set(OBJECT_TO_JSVAL(wrapper)); michael@0: michael@0: return WrapperFactory::WaiveXrayAndWrap(cx, vp); michael@0: } michael@0: michael@0: bool michael@0: XrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, michael@0: HandleObject wrapper, HandleObject holder, HandleId id, michael@0: MutableHandle desc) michael@0: { michael@0: desc.object().set(nullptr); michael@0: RootedObject target(cx, getTargetObject(wrapper)); michael@0: RootedObject expando(cx, getExpandoObject(cx, target, wrapper)); michael@0: michael@0: // Check for expando properties first. Note that the expando object lives michael@0: // in the target compartment. michael@0: bool found = false; michael@0: if (expando) { michael@0: JSAutoCompartment ac(cx, expando); michael@0: if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) michael@0: return false; michael@0: found = !!desc.object(); michael@0: } michael@0: michael@0: // Next, check for ES builtins. michael@0: if (!found && JS_IsGlobalObject(target)) { michael@0: JSProtoKey key = JS_IdToProtoKey(cx, id); michael@0: JSAutoCompartment ac(cx, target); michael@0: if (key != JSProto_Null) { michael@0: MOZ_ASSERT(key < JSProto_LIMIT); michael@0: RootedObject constructor(cx); michael@0: if (!JS_GetClassObject(cx, key, &constructor)) michael@0: return false; michael@0: MOZ_ASSERT(constructor); michael@0: desc.value().set(ObjectValue(*constructor)); michael@0: found = true; michael@0: } else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EVAL)) { michael@0: RootedObject eval(cx); michael@0: if (!js::GetOriginalEval(cx, target, &eval)) michael@0: return false; michael@0: desc.value().set(ObjectValue(*eval)); michael@0: found = true; michael@0: } michael@0: } michael@0: michael@0: if (found) { michael@0: if (!JS_WrapPropertyDescriptor(cx, desc)) michael@0: return false; michael@0: // Pretend the property lives on the wrapper. michael@0: desc.object().set(wrapper); michael@0: return true; michael@0: } michael@0: michael@0: // Handle .wrappedJSObject for subsuming callers. This should move once we michael@0: // sort out own-ness for the holder. michael@0: if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) && michael@0: AccessCheck::wrapperSubsumes(wrapper)) michael@0: { michael@0: if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) michael@0: return false; michael@0: if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedValue(), michael@0: wrappedJSObject_getter, nullptr, michael@0: JSPROP_ENUMERATE | JSPROP_SHARED)) { michael@0: return false; michael@0: } michael@0: if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) michael@0: return false; michael@0: desc.object().set(wrapper); michael@0: return true; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: XrayTraits::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, michael@0: bool strict, MutableHandleValue vp) michael@0: { michael@0: // Skip our Base if it isn't already BaseProxyHandler. michael@0: js::BaseProxyHandler *handler = js::GetProxyHandler(wrapper); michael@0: return handler->js::BaseProxyHandler::set(cx, wrapper, receiver, id, strict, vp); michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, michael@0: HandleObject wrapper, HandleObject holder, michael@0: HandleId id, michael@0: MutableHandle desc) michael@0: { michael@0: // Call the common code. michael@0: bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, michael@0: id, desc); michael@0: if (!ok || desc.object()) michael@0: return ok; michael@0: michael@0: // Check for indexed access on a window. michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: nsGlobalWindow* win = AsWindow(cx, wrapper); michael@0: // Note: As() unwraps outer windows to get to the inner window. michael@0: if (win) { michael@0: bool unused; michael@0: nsCOMPtr subframe = win->IndexedGetter(index, unused); michael@0: if (subframe) { michael@0: nsGlobalWindow* global = static_cast(subframe.get()); michael@0: global->EnsureInnerWindow(); michael@0: JSObject* obj = global->FastGetGlobalJSObject(); michael@0: if (MOZ_UNLIKELY(!obj)) { michael@0: // It's gone? michael@0: return xpc::Throw(cx, NS_ERROR_FAILURE); michael@0: } michael@0: desc.value().setObject(*obj); michael@0: FillPropertyDescriptor(desc, wrapper, true); michael@0: return JS_WrapPropertyDescriptor(cx, desc); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Xray wrappers don't use the regular wrapper hierarchy, so we should be michael@0: // in the wrapper's compartment here, not the wrappee. michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); michael@0: michael@0: bool hasProp; michael@0: if (!JS_HasPropertyById(cx, holder, id, &hasProp)) { michael@0: return false; michael@0: } michael@0: if (!hasProp) { michael@0: XPCWrappedNative *wn = getWN(wrapper); michael@0: michael@0: // Run the resolve hook of the wrapped native. michael@0: if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) { michael@0: return true; michael@0: } michael@0: michael@0: bool retval = true; michael@0: RootedObject pobj(cx); michael@0: nsIXPCScriptable *callback = wn->GetScriptableInfo()->GetCallback(); michael@0: nsresult rv = callback->NewResolve(wn, cx, wrapper, id, pobj.address(), michael@0: &retval); michael@0: if (NS_FAILED(rv)) { michael@0: if (retval) michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: MOZ_ASSERT(!pobj || (JS_HasPropertyById(cx, holder, id, &hasProp) && michael@0: hasProp), "id got defined somewhere else?"); michael@0: } michael@0: michael@0: // resolveOwnProperty must return a non-empty |desc| if and only if an |own| michael@0: // property was found on the object. However, given how the NewResolve setup michael@0: // works, we can't run the resolve hook if the holder already has a property michael@0: // of the same name. So if there was a pre-existing property on the holder, michael@0: // we have to use it. But we have no way of knowing if it corresponded to an michael@0: // |own| or non-|own| property, since both get cached on the holder and the michael@0: // |own|-ness information is lost. michael@0: // michael@0: // So we just over-zealously call things |own| here. This can cause us to michael@0: // return non-|own| properties from Object.getOwnPropertyDescriptor if michael@0: // lookups are performed in a certain order, but we can probably live with michael@0: // that until XPCWN Xrays go away with the new DOM bindings. michael@0: return JS_GetPropertyDescriptorById(cx, holder, id, desc); michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc, michael@0: Handle existingDesc, bool *defined) michael@0: { michael@0: *defined = false; michael@0: JSObject *holder = singleton.ensureHolder(cx, wrapper); michael@0: if (isResolving(cx, holder, id)) { michael@0: if (!desc.hasAttributes(JSPROP_GETTER | JSPROP_SETTER)) { michael@0: if (!desc.getter()) michael@0: desc.setGetter(holder_get); michael@0: if (!desc.setter()) michael@0: desc.setSetter(holder_set); michael@0: } michael@0: michael@0: *defined = true; michael@0: return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.getter(), desc.setter(), michael@0: desc.attributes()); michael@0: } michael@0: michael@0: // Check for an indexed property on a Window. If that's happening, do michael@0: // nothing but claim we defined it so it won't get added as an expando. michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index) && IsWindow(cx, wrapper)) { michael@0: *defined = true; michael@0: return true; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, michael@0: AutoIdVector &props) michael@0: { michael@0: // Force all native properties to be materialized onto the wrapped native. michael@0: AutoIdVector wnProps(cx); michael@0: { michael@0: RootedObject target(cx, singleton.getTargetObject(wrapper)); michael@0: JSAutoCompartment ac(cx, target); michael@0: if (!js::GetPropertyNames(cx, target, flags, &wnProps)) michael@0: return false; michael@0: } michael@0: if (!JS_WrapAutoIdVector(cx, wnProps)) michael@0: return false; michael@0: michael@0: // Go through the properties we got and enumerate all native ones. michael@0: for (size_t n = 0; n < wnProps.length(); ++n) { michael@0: RootedId id(cx, wnProps[n]); michael@0: bool hasProp; michael@0: if (!JS_HasPropertyById(cx, wrapper, id, &hasProp)) michael@0: return false; michael@0: if (hasProp) michael@0: props.append(id); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: JSObject * michael@0: XPCWrappedNativeXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) michael@0: { michael@0: RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); michael@0: JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, JS::NullPtr(), michael@0: global); michael@0: if (!holder) michael@0: return nullptr; michael@0: michael@0: js::SetReservedSlot(holder, JSSLOT_RESOLVING, PrivateValue(nullptr)); michael@0: return holder; michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeXrayTraits::call(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, michael@0: js::Wrapper& baseInstance) michael@0: { michael@0: // Run the resolve hook of the wrapped native. michael@0: XPCWrappedNative *wn = getWN(wrapper); michael@0: if (NATIVE_HAS_FLAG(wn, WantCall)) { michael@0: XPCCallContext ccx(JS_CALLER, cx, wrapper, NullPtr(), JSID_VOIDHANDLE, args.length(), michael@0: args.array(), args.rval().address()); michael@0: if (!ccx.IsValid()) michael@0: return false; michael@0: bool ok = true; michael@0: nsresult rv = wn->GetScriptableInfo()->GetCallback()->Call( michael@0: wn, cx, wrapper, args, &ok); michael@0: if (NS_FAILED(rv)) { michael@0: if (ok) michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: michael@0: } michael@0: michael@0: bool michael@0: XPCWrappedNativeXrayTraits::construct(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, michael@0: js::Wrapper& baseInstance) michael@0: { michael@0: // Run the resolve hook of the wrapped native. michael@0: XPCWrappedNative *wn = getWN(wrapper); michael@0: if (NATIVE_HAS_FLAG(wn, WantConstruct)) { michael@0: XPCCallContext ccx(JS_CALLER, cx, wrapper, NullPtr(), JSID_VOIDHANDLE, args.length(), michael@0: args.array(), args.rval().address()); michael@0: if (!ccx.IsValid()) michael@0: return false; michael@0: bool ok = true; michael@0: nsresult rv = wn->GetScriptableInfo()->GetCallback()->Construct( michael@0: wn, cx, wrapper, args, &ok); michael@0: if (NS_FAILED(rv)) { michael@0: if (ok) michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: michael@0: } michael@0: michael@0: bool michael@0: DOMXrayTraits::resolveNativeProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) michael@0: { michael@0: RootedObject obj(cx, getTargetObject(wrapper)); michael@0: if (!XrayResolveNativeProperty(cx, wrapper, obj, id, desc)) michael@0: return false; michael@0: michael@0: MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?"); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DOMXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, michael@0: HandleObject holder, HandleId id, michael@0: MutableHandle desc) michael@0: { michael@0: // Call the common code. michael@0: bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc); michael@0: if (!ok || desc.object()) michael@0: return ok; michael@0: michael@0: // Check for indexed access on a window. michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: nsGlobalWindow* win = AsWindow(cx, wrapper); michael@0: // Note: As() unwraps outer windows to get to the inner window. michael@0: if (win) { michael@0: bool unused; michael@0: nsCOMPtr subframe = win->IndexedGetter(index, unused); michael@0: if (subframe) { michael@0: nsGlobalWindow* global = static_cast(subframe.get()); michael@0: global->EnsureInnerWindow(); michael@0: JSObject* obj = global->FastGetGlobalJSObject(); michael@0: if (MOZ_UNLIKELY(!obj)) { michael@0: // It's gone? michael@0: return xpc::Throw(cx, NS_ERROR_FAILURE); michael@0: } michael@0: desc.value().setObject(*obj); michael@0: FillPropertyDescriptor(desc, wrapper, true); michael@0: return JS_WrapPropertyDescriptor(cx, desc); michael@0: } michael@0: } michael@0: } michael@0: michael@0: RootedObject obj(cx, getTargetObject(wrapper)); michael@0: if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc)) michael@0: return false; michael@0: michael@0: MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?"); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DOMXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc, michael@0: Handle existingDesc, bool *defined) michael@0: { michael@0: // Check for an indexed property on a Window. If that's happening, do michael@0: // nothing but claim we defined it so it won't get added as an expando. michael@0: if (IsWindow(cx, wrapper)) { michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: *defined = true; michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: if (!existingDesc.object()) michael@0: return true; michael@0: michael@0: JS::Rooted obj(cx, getTargetObject(wrapper)); michael@0: return XrayDefineProperty(cx, wrapper, obj, id, desc, defined); michael@0: } michael@0: michael@0: bool michael@0: DOMXrayTraits::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, michael@0: bool strict, MutableHandleValue vp) michael@0: { michael@0: MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(wrapper)); michael@0: RootedObject obj(cx, getTargetObject(wrapper)); michael@0: if (IsDOMProxy(obj)) { michael@0: DOMProxyHandler* handler = GetDOMProxyHandler(obj); michael@0: michael@0: bool done; michael@0: if (!handler->setCustom(cx, obj, id, vp, &done)) michael@0: return false; michael@0: if (done) michael@0: return true; michael@0: } michael@0: return XrayTraits::set(cx, wrapper, receiver, id, strict, vp); michael@0: } michael@0: michael@0: bool michael@0: DOMXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, michael@0: AutoIdVector &props) michael@0: { michael@0: JS::Rooted obj(cx, getTargetObject(wrapper)); michael@0: return XrayEnumerateProperties(cx, wrapper, obj, flags, props); michael@0: } michael@0: michael@0: bool michael@0: DOMXrayTraits::call(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, js::Wrapper& baseInstance) michael@0: { michael@0: RootedObject obj(cx, getTargetObject(wrapper)); michael@0: const js::Class* clasp = js::GetObjectClass(obj); michael@0: // What we have is either a WebIDL interface object, a WebIDL prototype michael@0: // object, or a WebIDL instance object. WebIDL prototype objects never have michael@0: // a clasp->call. WebIDL interface objects we want to invoke on the xray michael@0: // compartment. WebIDL instance objects either don't have a clasp->call or michael@0: // are using "legacycaller", which basically means plug-ins. We want to michael@0: // call those on the content compartment. michael@0: if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) { michael@0: if (!clasp->call) { michael@0: RootedValue v(cx, ObjectValue(*wrapper)); michael@0: js_ReportIsNotFunction(cx, v); michael@0: return false; michael@0: } michael@0: // call it on the Xray compartment michael@0: if (!clasp->call(cx, args.length(), args.base())) michael@0: return false; michael@0: } else { michael@0: // This is only reached for WebIDL instance objects, and in practice michael@0: // only for plugins. Just call them on the content compartment. michael@0: if (!baseInstance.call(cx, wrapper, args)) michael@0: return false; michael@0: } michael@0: return JS_WrapValue(cx, args.rval()); michael@0: } michael@0: michael@0: bool michael@0: DOMXrayTraits::construct(JSContext *cx, HandleObject wrapper, michael@0: const JS::CallArgs &args, js::Wrapper& baseInstance) michael@0: { michael@0: RootedObject obj(cx, getTargetObject(wrapper)); michael@0: MOZ_ASSERT(mozilla::dom::HasConstructor(obj)); michael@0: const js::Class* clasp = js::GetObjectClass(obj); michael@0: // See comments in DOMXrayTraits::call() explaining what's going on here. michael@0: if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) { michael@0: if (!clasp->construct) { michael@0: RootedValue v(cx, ObjectValue(*wrapper)); michael@0: js_ReportIsNotFunction(cx, v); michael@0: return false; michael@0: } michael@0: if (!clasp->construct(cx, args.length(), args.base())) michael@0: return false; michael@0: } else { michael@0: if (!baseInstance.construct(cx, wrapper, args)) michael@0: return false; michael@0: } michael@0: if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval())) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: DOMXrayTraits::preserveWrapper(JSObject *target) michael@0: { michael@0: nsISupports *identity = mozilla::dom::UnwrapDOMObjectToISupports(target); michael@0: if (!identity) michael@0: return; michael@0: nsWrapperCache* cache = nullptr; michael@0: CallQueryInterface(identity, &cache); michael@0: if (cache) michael@0: cache->PreserveWrapper(identity); michael@0: } michael@0: michael@0: JSObject* michael@0: DOMXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) michael@0: { michael@0: RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); michael@0: return JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), global); michael@0: } michael@0: michael@0: template michael@0: XrayWrapper::XrayWrapper(unsigned flags) michael@0: : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG) michael@0: { michael@0: Base::setHasPrototype(Traits::HasPrototype); michael@0: } michael@0: michael@0: template michael@0: XrayWrapper::~XrayWrapper() michael@0: { michael@0: } michael@0: michael@0: namespace XrayUtils { michael@0: michael@0: JSObject * michael@0: GetNativePropertiesObject(JSContext *cx, JSObject *wrapper) michael@0: { michael@0: MOZ_ASSERT(js::IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper), michael@0: "bad object passed in"); michael@0: michael@0: JSObject *holder = GetHolder(wrapper); michael@0: MOZ_ASSERT(holder, "uninitialized wrapper being used?"); michael@0: return holder; michael@0: } michael@0: michael@0: bool michael@0: IsXrayResolving(JSContext *cx, HandleObject wrapper, HandleId id) michael@0: { michael@0: if (!WrapperFactory::IsXrayWrapper(wrapper) || michael@0: GetXrayType(wrapper) != XrayForWrappedNative) michael@0: { michael@0: return false; michael@0: } michael@0: JSObject *holder = michael@0: XPCWrappedNativeXrayTraits::singleton.ensureHolder(cx, wrapper); michael@0: return XPCWrappedNativeXrayTraits::isResolving(cx, holder, id); michael@0: } michael@0: michael@0: bool michael@0: HasNativeProperty(JSContext *cx, HandleObject wrapper, HandleId id, bool *hasProp) michael@0: { michael@0: MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); michael@0: XrayTraits *traits = GetXrayTraits(wrapper); michael@0: MOZ_ASSERT(traits); michael@0: RootedObject holder(cx, traits->ensureHolder(cx, wrapper)); michael@0: NS_ENSURE_TRUE(holder, false); michael@0: *hasProp = false; michael@0: Rooted desc(cx); michael@0: Wrapper *handler = Wrapper::wrapperHandler(wrapper); michael@0: michael@0: // Try resolveOwnProperty. michael@0: Maybe resolvingId; michael@0: if (traits == &XPCWrappedNativeXrayTraits::singleton) michael@0: resolvingId.construct(cx, wrapper, id); michael@0: if (!traits->resolveOwnProperty(cx, *handler, wrapper, holder, id, &desc)) michael@0: return false; michael@0: if (desc.object()) { michael@0: *hasProp = true; michael@0: return true; michael@0: } michael@0: michael@0: // Try the holder. michael@0: bool found = false; michael@0: if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) michael@0: return false; michael@0: if (found) { michael@0: *hasProp = true; michael@0: return true; michael@0: } michael@0: michael@0: // Try resolveNativeProperty. michael@0: if (!traits->resolveNativeProperty(cx, wrapper, holder, id, &desc)) michael@0: return false; michael@0: *hasProp = !!desc.object(); michael@0: return true; michael@0: } michael@0: michael@0: } // namespace XrayUtils michael@0: michael@0: static bool michael@0: XrayToString(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (!args.thisv().isObject()) { michael@0: JS_ReportError(cx, "XrayToString called on an incompatible object"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject wrapper(cx, &args.thisv().toObject()); michael@0: if (!wrapper) michael@0: return false; michael@0: if (IsWrapper(wrapper) && michael@0: GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) { michael@0: wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper); michael@0: } michael@0: if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) { michael@0: JS_ReportError(cx, "XrayToString called on an incompatible object"); michael@0: return false; michael@0: } michael@0: michael@0: static const char start[] = "[object XrayWrapper "; michael@0: static const char end[] = "]"; michael@0: michael@0: RootedObject obj(cx, XrayTraits::getTargetObject(wrapper)); michael@0: XrayType type = GetXrayType(obj); michael@0: if (type == XrayForDOMObject) michael@0: return NativeToString(cx, wrapper, obj, start, end, args.rval()); michael@0: michael@0: if (type != XrayForWrappedNative) { michael@0: JS_ReportError(cx, "XrayToString called on an incompatible object"); michael@0: return false; michael@0: } michael@0: michael@0: nsAutoString result; michael@0: result.AppendASCII(start); michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); michael@0: char *wrapperStr = wn->ToString(); michael@0: if (!wrapperStr) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: result.AppendASCII(wrapperStr); michael@0: JS_smprintf_free(wrapperStr); michael@0: michael@0: result.AppendASCII(end); michael@0: michael@0: JSString *str = JS_NewUCStringCopyN(cx, result.get(), result.Length()); michael@0: if (!str) michael@0: return false; michael@0: michael@0: args.rval().setString(str); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: static void michael@0: DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj) michael@0: { michael@0: // In general, we shouldn't have cross-compartment wrappers here, because michael@0: // we should be running in an XBL scope, and the content prototype should michael@0: // contain wrappers to functions defined in the XBL scope. But if the node michael@0: // has been adopted into another compartment, those prototypes will now point michael@0: // to a different XBL scope (which is ok). michael@0: MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj), michael@0: xpc::IsXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj)))); michael@0: MOZ_ASSERT(JS_ObjectIsCallable(cx, obj)); michael@0: } michael@0: michael@0: static void michael@0: DEBUG_CheckXBLLookup(JSContext *cx, JSPropertyDescriptor *desc) michael@0: { michael@0: if (!desc->obj) michael@0: return; michael@0: if (!desc->value.isUndefined()) { michael@0: MOZ_ASSERT(desc->value.isObject()); michael@0: DEBUG_CheckXBLCallable(cx, &desc->value.toObject()); michael@0: } michael@0: if (desc->getter) { michael@0: MOZ_ASSERT(desc->attrs & JSPROP_GETTER); michael@0: DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->getter)); michael@0: } michael@0: if (desc->setter) { michael@0: MOZ_ASSERT(desc->attrs & JSPROP_SETTER); michael@0: DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->setter)); michael@0: } michael@0: } michael@0: #else michael@0: #define DEBUG_CheckXBLLookup(a, b) {} michael@0: #endif michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::isExtensible(JSContext *cx, JS::Handle wrapper, bool *extensible) michael@0: { michael@0: // Xray wrappers are supposed to provide a clean view of the target michael@0: // reflector, hiding any modifications by script in the target scope. So michael@0: // even if that script freezes the reflector, we don't want to make that michael@0: // visible to the caller. DOM reflectors are always extensible by default, michael@0: // so we can just return true here. michael@0: *extensible = true; michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::preventExtensions(JSContext *cx, HandleObject wrapper) michael@0: { michael@0: // See above. michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); michael@0: return false; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: JS::MutableHandle desc) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); michael@0: RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); michael@0: if (Traits::isResolving(cx, holder, id)) { michael@0: desc.object().set(nullptr); michael@0: return true; michael@0: } michael@0: michael@0: typename Traits::ResolvingIdImpl resolving(cx, wrapper, id); michael@0: michael@0: if (!holder) michael@0: return false; michael@0: michael@0: // Ordering is important here. michael@0: // michael@0: // We first need to call resolveOwnProperty, even before checking the holder, michael@0: // because there might be a new dynamic |own| property that appears and michael@0: // shadows a previously-resolved non-own property that we cached on the michael@0: // holder. This can happen with indexed properties on NodeLists, for example, michael@0: // which are |own| value props. michael@0: // michael@0: // resolveOwnProperty may or may not cache what it finds on the holder, michael@0: // depending on how ephemeral it decides the property is. XPCWN |own| michael@0: // properties generally end up on the holder via NewResolve, whereas michael@0: // NodeList |own| properties don't get defined on the holder, since they're michael@0: // supposed to be dynamic. This means that we have to first check the result michael@0: // of resolveOwnProperty, and _then_, if that comes up blank, check the michael@0: // holder for any cached native properties. michael@0: // michael@0: // Finally, we call resolveNativeProperty, which checks non-own properties, michael@0: // and unconditionally caches what it finds on the holder. michael@0: michael@0: // Check resolveOwnProperty. michael@0: if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc)) michael@0: return false; michael@0: michael@0: // Check the holder. michael@0: if (!desc.object() && !JS_GetPropertyDescriptorById(cx, holder, id, desc)) michael@0: return false; michael@0: if (desc.object()) { michael@0: desc.object().set(wrapper); michael@0: return true; michael@0: } michael@0: michael@0: // Nothing in the cache. Call through, and cache the result. michael@0: if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc)) michael@0: return false; michael@0: michael@0: // We need to handle named access on the Window somewhere other than michael@0: // Traits::resolveOwnProperty, because per spec it happens on the Global michael@0: // Scope Polluter and thus the resulting properties are non-|own|. However, michael@0: // we're set up (above) to cache (on the holder) anything that comes out of michael@0: // resolveNativeProperty, which we don't want for something dynamic like michael@0: // named access. So we just handle it separately here. michael@0: nsGlobalWindow *win = nullptr; michael@0: if (!desc.object() && michael@0: JSID_IS_STRING(id) && michael@0: (win = AsWindow(cx, wrapper))) michael@0: { michael@0: nsDependentJSString name(id); michael@0: nsCOMPtr childDOMWin = win->GetChildWindow(name); michael@0: if (childDOMWin) { michael@0: nsGlobalWindow *cwin = static_cast(childDOMWin.get()); michael@0: JSObject *childObj = cwin->FastGetGlobalJSObject(); michael@0: if (MOZ_UNLIKELY(!childObj)) michael@0: return xpc::Throw(cx, NS_ERROR_FAILURE); michael@0: FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj), michael@0: /* readOnly = */ true); michael@0: return JS_WrapPropertyDescriptor(cx, desc); michael@0: } michael@0: } michael@0: michael@0: if (!desc.object() && michael@0: id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING)) michael@0: { michael@0: michael@0: JSFunction *toString = JS_NewFunction(cx, XrayToString, 0, 0, wrapper, "toString"); michael@0: if (!toString) michael@0: return false; michael@0: michael@0: desc.object().set(wrapper); michael@0: desc.setAttributes(0); michael@0: desc.setGetter(nullptr); michael@0: desc.setSetter(nullptr); michael@0: desc.value().setObject(*JS_GetFunctionObject(toString)); michael@0: } michael@0: michael@0: // If we're a special scope for in-content XBL, our script expects to see michael@0: // the bound XBL methods and attributes when accessing content. However, michael@0: // these members are implemented in content via custom-spliced prototypes, michael@0: // and thus aren't visible through Xray wrappers unless we handle them michael@0: // explicitly. So we check if we're running in such a scope, and if so, michael@0: // whether the wrappee is a bound element. If it is, we do a lookup via michael@0: // specialized XBL machinery. michael@0: // michael@0: // While we have to do some sketchy walking through content land, we should michael@0: // be protected by read-only/non-configurable properties, and any functions michael@0: // we end up with should _always_ be living in an XBL scope (usually ours, michael@0: // but could be another if the node has been adopted). michael@0: // michael@0: // Make sure to assert this. michael@0: nsCOMPtr content; michael@0: if (!desc.object() && michael@0: EnsureCompartmentPrivate(wrapper)->scope->IsXBLScope() && michael@0: (content = do_QueryInterfaceNative(cx, wrapper))) michael@0: { michael@0: if (!nsContentUtils::LookupBindingMember(cx, content, id, desc)) michael@0: return false; michael@0: DEBUG_CheckXBLLookup(cx, desc.address()); michael@0: } michael@0: michael@0: // If we still have nothing, we're done. michael@0: if (!desc.object()) michael@0: return true; michael@0: michael@0: if (!JS_DefinePropertyById(cx, holder, id, desc.value(), desc.getter(), michael@0: desc.setter(), desc.attributes()) || michael@0: !JS_GetPropertyDescriptorById(cx, holder, id, desc)) michael@0: { michael@0: return false; michael@0: } michael@0: MOZ_ASSERT(desc.object()); michael@0: desc.object().set(wrapper); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: JS::MutableHandle desc) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); michael@0: RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); michael@0: if (Traits::isResolving(cx, holder, id)) { michael@0: desc.object().set(nullptr); michael@0: return true; michael@0: } michael@0: michael@0: typename Traits::ResolvingIdImpl resolving(cx, wrapper, id); michael@0: michael@0: // NB: Nothing we do here acts on the wrapped native itself, so we don't michael@0: // enter our policy. michael@0: michael@0: if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc)) michael@0: return false; michael@0: if (desc.object()) michael@0: desc.object().set(wrapper); michael@0: return true; michael@0: } michael@0: michael@0: // Consider what happens when chrome does |xray.expando = xray.wrappedJSObject|. michael@0: // michael@0: // Since the expando comes from the target compartment, wrapping it back into michael@0: // the target compartment to define it on the expando object ends up stripping michael@0: // off the Xray waiver that gives |xray| and |xray.wrappedJSObject| different michael@0: // identities. This is generally the right thing to do when wrapping across michael@0: // compartments, but is incorrect in the special case of the Xray expando michael@0: // object. Manually re-apply Xrays if necessary. michael@0: // michael@0: // NB: In order to satisfy the invariants of WaiveXray, we need to pass michael@0: // in an object sans security wrapper, which means we need to strip off any michael@0: // potential same-compartment security wrapper that may have been applied michael@0: // to the content object. This is ok, because the the expando object is only michael@0: // ever accessed by code across the compartment boundary. michael@0: static bool michael@0: RecreateLostWaivers(JSContext *cx, JSPropertyDescriptor *orig, michael@0: MutableHandle wrapped) michael@0: { michael@0: // Compute whether the original objects were waived, and implicitly, whether michael@0: // they were objects at all. michael@0: bool valueWasWaived = michael@0: orig->value.isObject() && michael@0: WrapperFactory::HasWaiveXrayFlag(&orig->value.toObject()); michael@0: bool getterWasWaived = michael@0: (orig->attrs & JSPROP_GETTER) && michael@0: WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->getter)); michael@0: bool setterWasWaived = michael@0: (orig->attrs & JSPROP_SETTER) && michael@0: WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->setter)); michael@0: michael@0: // Recreate waivers. Note that for value, we need an extra UncheckedUnwrap michael@0: // to handle same-compartment security wrappers (see above). This should michael@0: // never happen for getters/setters. michael@0: michael@0: RootedObject rewaived(cx); michael@0: if (valueWasWaived && !IsCrossCompartmentWrapper(&wrapped.value().toObject())) { michael@0: rewaived = &wrapped.value().toObject(); michael@0: rewaived = WrapperFactory::WaiveXray(cx, UncheckedUnwrap(rewaived)); michael@0: NS_ENSURE_TRUE(rewaived, false); michael@0: wrapped.value().set(ObjectValue(*rewaived)); michael@0: } michael@0: if (getterWasWaived && !IsCrossCompartmentWrapper(wrapped.getterObject())) { michael@0: MOZ_ASSERT(CheckedUnwrap(wrapped.getterObject())); michael@0: rewaived = WrapperFactory::WaiveXray(cx, wrapped.getterObject()); michael@0: NS_ENSURE_TRUE(rewaived, false); michael@0: wrapped.setGetterObject(rewaived); michael@0: } michael@0: if (setterWasWaived && !IsCrossCompartmentWrapper(wrapped.setterObject())) { michael@0: MOZ_ASSERT(CheckedUnwrap(wrapped.setterObject())); michael@0: rewaived = WrapperFactory::WaiveXray(cx, wrapped.setterObject()); michael@0: NS_ENSURE_TRUE(rewaived, false); michael@0: wrapped.setSetterObject(rewaived); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::defineProperty(JSContext *cx, HandleObject wrapper, michael@0: HandleId id, MutableHandle desc) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); michael@0: michael@0: Rooted existing_desc(cx); michael@0: if (!getOwnPropertyDescriptor(cx, wrapper, id, &existing_desc)) michael@0: return false; michael@0: michael@0: if (existing_desc.object() && existing_desc.isPermanent()) michael@0: return true; // silently ignore attempt to overwrite native property michael@0: michael@0: bool defined = false; michael@0: if (!Traits::defineProperty(cx, wrapper, id, desc, existing_desc, &defined)) michael@0: return false; michael@0: if (defined) michael@0: return true; michael@0: michael@0: // We're placing an expando. The expando objects live in the target michael@0: // compartment, so we need to enter it. michael@0: RootedObject target(cx, Traits::singleton.getTargetObject(wrapper)); michael@0: JSAutoCompartment ac(cx, target); michael@0: michael@0: // Grab the relevant expando object. michael@0: RootedObject expandoObject(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, michael@0: target)); michael@0: if (!expandoObject) michael@0: return false; michael@0: michael@0: // Wrap the property descriptor for the target compartment. michael@0: Rooted wrappedDesc(cx, desc); michael@0: if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc)) michael@0: return false; michael@0: michael@0: // Fix up Xray waivers. michael@0: if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc)) michael@0: return false; michael@0: michael@0: return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc.value(), michael@0: wrappedDesc.getter(), wrappedDesc.setter(), michael@0: wrappedDesc.get().attrs); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, michael@0: AutoIdVector &props) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); michael@0: return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::delete_(JSContext *cx, HandleObject wrapper, michael@0: HandleId id, bool *bp) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); michael@0: michael@0: // Check the expando object. michael@0: RootedObject target(cx, Traits::getTargetObject(wrapper)); michael@0: RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); michael@0: if (expando) { michael@0: JSAutoCompartment ac(cx, expando); michael@0: return JS_DeletePropertyById2(cx, expando, id, bp); michael@0: } michael@0: *bp = true; michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::enumerate(JSContext *cx, HandleObject wrapper, unsigned flags, michael@0: AutoIdVector &props) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); michael@0: if (!AccessCheck::wrapperSubsumes(wrapper)) { michael@0: JS_ReportError(cx, "Not allowed to enumerate cross origin objects"); michael@0: return false; michael@0: } michael@0: michael@0: // Enumerate expando properties first. Note that the expando object lives michael@0: // in the target compartment. michael@0: RootedObject target(cx, Traits::singleton.getTargetObject(wrapper)); michael@0: RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); michael@0: if (expando) { michael@0: JSAutoCompartment ac(cx, expando); michael@0: if (!js::GetPropertyNames(cx, expando, flags, &props)) michael@0: return false; michael@0: } michael@0: if (!JS_WrapAutoIdVector(cx, props)) michael@0: return false; michael@0: michael@0: return Traits::singleton.enumerateNames(cx, wrapper, flags, props); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::enumerate(JSContext *cx, HandleObject wrapper, michael@0: AutoIdVector &props) michael@0: { michael@0: return enumerate(cx, wrapper, 0, props); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::get(JSContext *cx, HandleObject wrapper, michael@0: HandleObject receiver, HandleId id, michael@0: MutableHandleValue vp) michael@0: { michael@0: // Skip our Base if it isn't already ProxyHandler. michael@0: // NB: None of the functions we call are prepared for the receiver not michael@0: // being the wrapper, so ignore the receiver here. michael@0: return js::BaseProxyHandler::get(cx, wrapper, Traits::HasPrototype ? receiver : wrapper, id, vp); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::set(JSContext *cx, HandleObject wrapper, michael@0: HandleObject receiver, HandleId id, michael@0: bool strict, MutableHandleValue vp) michael@0: { michael@0: // Delegate to Traits. michael@0: // NB: None of the functions we call are prepared for the receiver not michael@0: // being the wrapper, so ignore the receiver here. michael@0: return Traits::set(cx, wrapper, Traits::HasPrototype ? receiver : wrapper, id, strict, vp); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::has(JSContext *cx, HandleObject wrapper, michael@0: HandleId id, bool *bp) michael@0: { michael@0: // Skip our Base if it isn't already ProxyHandler. michael@0: return js::BaseProxyHandler::has(cx, wrapper, id, bp); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::hasOwn(JSContext *cx, HandleObject wrapper, michael@0: HandleId id, bool *bp) michael@0: { michael@0: // Skip our Base if it isn't already ProxyHandler. michael@0: return js::BaseProxyHandler::hasOwn(cx, wrapper, id, bp); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::keys(JSContext *cx, HandleObject wrapper, michael@0: AutoIdVector &props) michael@0: { michael@0: // Skip our Base if it isn't already ProxyHandler. michael@0: return js::BaseProxyHandler::keys(cx, wrapper, props); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::iterate(JSContext *cx, HandleObject wrapper, michael@0: unsigned flags, MutableHandleValue vp) michael@0: { michael@0: // Skip our Base if it isn't already ProxyHandler. michael@0: return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL); michael@0: return Traits::call(cx, wrapper, args, Base::singleton); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::construct(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL); michael@0: return Traits::construct(cx, wrapper, args, Base::singleton); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::defaultValue(JSContext *cx, HandleObject wrapper, michael@0: JSType hint, MutableHandleValue vp) michael@0: { michael@0: // Even if this isn't a security wrapper, Xray semantics dictate that we michael@0: // run the DefaultValue algorithm directly on the Xray wrapper. michael@0: // michael@0: // NB: We don't have to worry about things with special [[DefaultValue]] michael@0: // behavior like Date because we'll never have an XrayWrapper to them. michael@0: return js::DefaultValue(cx, wrapper, hint, vp); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, michael@0: JS::MutableHandleObject protop) michael@0: { michael@0: // We really only want this override for non-SecurityWrapper-inheriting michael@0: // |Base|. But doing that statically with templates requires partial method michael@0: // specializations (and therefore a helper class), which is all more trouble michael@0: // than it's worth. Do a dynamic check. michael@0: if (Base::hasSecurityPolicy()) michael@0: return Base::getPrototypeOf(cx, wrapper, protop); michael@0: michael@0: RootedObject target(cx, Traits::getTargetObject(wrapper)); michael@0: RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); michael@0: michael@0: // We want to keep the Xray's prototype distinct from that of content, but michael@0: // only if there's been a set. If there's not an expando, or the expando michael@0: // slot is |undefined|, hand back the default proto, appropriately wrapped. michael@0: michael@0: RootedValue v(cx); michael@0: if (expando) { michael@0: JSAutoCompartment ac(cx, expando); michael@0: v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE); michael@0: } michael@0: if (v.isUndefined()) michael@0: return getPrototypeOfHelper(cx, wrapper, target, protop); michael@0: michael@0: protop.set(v.toObjectOrNull()); michael@0: return JS_WrapObject(cx, protop); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XrayWrapper::setPrototypeOf(JSContext *cx, JS::HandleObject wrapper, michael@0: JS::HandleObject proto, bool *bp) michael@0: { michael@0: // Do this only for non-SecurityWrapper-inheriting |Base|. See the comment michael@0: // in getPrototypeOf(). michael@0: if (Base::hasSecurityPolicy()) michael@0: return Base::setPrototypeOf(cx, wrapper, proto, bp); michael@0: michael@0: RootedObject target(cx, Traits::getTargetObject(wrapper)); michael@0: RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target)); michael@0: michael@0: // The expando lives in the target's compartment, so do our installation there. michael@0: JSAutoCompartment ac(cx, target); michael@0: michael@0: RootedValue v(cx, ObjectOrNullValue(proto)); michael@0: if (!JS_WrapValue(cx, &v)) michael@0: return false; michael@0: JS_SetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE, v); michael@0: *bp = true; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * The Permissive / Security variants should be used depending on whether the michael@0: * compartment of the wrapper is guranteed to subsume the compartment of the michael@0: * wrapped object (i.e. - whether it is safe from a security perspective to michael@0: * unwrap the wrapper). michael@0: */ michael@0: michael@0: template<> michael@0: PermissiveXrayXPCWN PermissiveXrayXPCWN::singleton(0); michael@0: template class PermissiveXrayXPCWN; michael@0: michael@0: template<> michael@0: SecurityXrayXPCWN SecurityXrayXPCWN::singleton(0); michael@0: template class SecurityXrayXPCWN; michael@0: michael@0: template<> michael@0: PermissiveXrayDOM PermissiveXrayDOM::singleton(0); michael@0: template class PermissiveXrayDOM; michael@0: michael@0: template<> michael@0: SecurityXrayDOM SecurityXrayDOM::singleton(0); michael@0: template class SecurityXrayDOM; michael@0: michael@0: template<> michael@0: PermissiveXrayJS PermissiveXrayJS::singleton(0); michael@0: template class PermissiveXrayJS; michael@0: michael@0: template<> michael@0: SCSecurityXrayXPCWN SCSecurityXrayXPCWN::singleton(0); michael@0: template class SCSecurityXrayXPCWN; michael@0: michael@0: static nsQueryInterface michael@0: do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper) michael@0: { michael@0: nsISupports* nativeSupports = nullptr; michael@0: if (IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper)) { michael@0: RootedObject target(cx, XrayTraits::getTargetObject(wrapper)); michael@0: XrayType type = GetXrayType(target); michael@0: if (type == XrayForDOMObject) { michael@0: nativeSupports = UnwrapDOMObjectToISupports(target); michael@0: } else if (type == XrayForWrappedNative) { michael@0: XPCWrappedNative *wn = XPCWrappedNative::Get(target); michael@0: nativeSupports = wn->Native(); michael@0: } michael@0: } else { michael@0: nsIXPConnect *xpc = nsXPConnect::XPConnect(); michael@0: nativeSupports = xpc->GetNativeOfWrapper(cx, wrapper); michael@0: } michael@0: michael@0: return nsQueryInterface(nativeSupports); michael@0: } michael@0: michael@0: }