JSBool
WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp)
{
  // Slim wrappers don't expect to be wrapped, so morph them to fat wrappers
  // if we're about to wrap one.
  JSObject *innerObj = JSVAL_TO_OBJECT(v);
  if (IS_SLIM_WRAPPER(innerObj) && !MorphSlimWrapper(cx, innerObj)) {
    return ThrowException(NS_ERROR_FAILURE, cx);
  }

  JSObject *wrapperObj =
    JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SOWClass), NULL, parent);
  if (!wrapperObj) {
    return JS_FALSE;
  }

  *vp = OBJECT_TO_JSVAL(wrapperObj);
  js::AutoObjectRooter tvr(cx, wrapperObj);

  if (!JS_SetReservedSlot(cx, wrapperObj, sWrappedObjSlot, v) ||
      !JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, JSVAL_ZERO)) {
    return JS_FALSE;
  }

  return JS_TRUE;
}
Beispiel #2
0
static inline QITableEntry *
GetOffsetsFromSlimWrapper(JSObject *obj)
{
    NS_ASSERTION(IS_SLIM_WRAPPER(obj), "What kind of object is this?");
    return GetOffsets(static_cast<nsISupports*>(xpc_GetJSPrivate(obj)),
                      GetSlimWrapperProto(obj));
}
Beispiel #3
0
/**
 * Get the interface name and member name (for error messages).
 *
 * We could instead have each quick stub pass its name to the error-handling
 * functions, as that name is statically known.  But that would be redundant;
 * the information is handy at runtime anyway.  Also, this code often produces
 * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]"
 * rather than "[nsIDOMNode.appendChild]".
 */
static void
GetMemberInfo(JSObject *obj,
              jsval memberId,
              const char **ifaceName,
              const char **memberName)
{
    // Get the interface name.  From DefinePropertyIfFound (in
    // xpcwrappednativejsops.cpp) and XPCThrower::Verbosify.
    //
    // We could instead make the quick stub could pass in its interface name,
    // but this code often produces a more specific error message, e.g.
    *ifaceName = "Unknown";

    NS_ASSERTION(IS_WRAPPER_CLASS(STOBJ_GET_CLASS(obj)) ||
                 STOBJ_GET_CLASS(obj) == &XPC_WN_Tearoff_JSClass ||
                 IS_SLIM_WRAPPER(obj),
                 "obj must be a wrapper");
    XPCWrappedNativeProto *proto;
    if(IS_SLIM_WRAPPER(obj))
    {
        proto = GetSlimWrapperProto(obj);
    }
    else
    {
        XPCWrappedNative *wrapper = (XPCWrappedNative *) obj->getPrivate();
        proto = wrapper->GetProto();
    }
    if(proto)
    {
        XPCNativeSet *set = proto->GetSet();
        if(set)
        {
            XPCNativeMember *member;
            XPCNativeInterface *iface;

            if(set->FindMember(memberId, &member, &iface))
                *ifaceName = iface->GetNameString();
        }
    }

    *memberName = (JSVAL_IS_STRING(memberId)
                   ? JS_GetStringBytes(JSVAL_TO_STRING(memberId))
                   : "unknown");
}
Beispiel #4
0
void
LogSlimWrapperWillMorph(JSContext *cx, JSObject *obj, const char *propname,
                        const char *functionName)
{
    if (obj && IS_SLIM_WRAPPER(obj)) {
        XPCNativeScriptableInfo *si =
            GetSlimWrapperProto(obj)->GetScriptableInfo();
        printf("***** morphing %s from %s", si->GetJSClass()->name,
               functionName);
        if (propname)
            printf(" for %s", propname);
        printf(" (%p, %p)\n", obj,
               static_cast<nsISupports*>(xpc_GetJSPrivate(obj)));
        xpc_DumpJSStack(cx, false, false, false);
    }
}
JSObject *
WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj, unsigned flags)
{
    // Don't unwrap an outer window, just double wrap it if needed.
    if (js::GetObjectClass(obj)->ext.innerObject)
        return DoubleWrap(cx, obj, flags);

    // Here are the rules for wrapping:
    // We should never get a proxy here (the JS engine unwraps those for us).
    JS_ASSERT(!IsWrapper(obj));

    // As soon as an object is wrapped in a security wrapper, it morphs to be
    // a fat wrapper. (see also: bug XXX).
    if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj))
        return nsnull;

    // We only hand out outer objects to script.
    obj = GetCurrentOuter(cx, obj);
    if (js::GetObjectClass(obj)->ext.innerObject)
        return DoubleWrap(cx, obj, flags);

    // Now, our object is ready to be wrapped, but several objects (notably
    // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
    // those objects in a security wrapper, then we need to hand back the
    // wrapper for the new scope instead. Also, global objects don't move
    // between scopes so for those we also want to return the wrapper. So...
    if (!IS_WN_WRAPPER(obj) || !js::GetObjectParent(obj))
        return DoubleWrap(cx, obj, flags);

    XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));

    JSAutoEnterCompartment ac;
    if (!ac.enter(cx, obj))
        return nsnull;
    XPCCallContext ccx(JS_CALLER, cx, obj);

    {
        if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) {
            // We have a precreate hook. This object might enforce that we only
            // ever create JS object for it.
            JSObject *originalScope = scope;
            nsresult rv = wn->GetScriptableInfo()->GetCallback()->
                PreCreate(wn->Native(), cx, scope, &scope);
            NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags));

            // If the handed back scope differs from the passed-in scope and is in
            // a separate compartment, then this object is explicitly requesting
            // that we don't create a second JS object for it: create a security
            // wrapper.
            if (js::GetObjectCompartment(originalScope) != js::GetObjectCompartment(scope))
                return DoubleWrap(cx, obj, flags);

            // Note: this penalizes objects that only have one wrapper, but are
            // being accessed across compartments. We would really prefer to
            // replace the above code with a test that says "do you only have one
            // wrapper?"
        }
    }

    // NB: Passing a holder here inhibits slim wrappers under
    // WrapNativeToJSVal.
    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;

    // This public WrapNativeToJSVal API enters the compartment of 'scope'
    // so we don't have to.
    jsval v;
    nsresult rv =
        nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull,
                                                           &NS_GET_IID(nsISupports), false,
                                                           &v, getter_AddRefs(holder));
    if (NS_SUCCEEDED(rv)) {
        obj = JSVAL_TO_OBJECT(v);
        NS_ASSERTION(IS_WN_WRAPPER(obj), "bad object");

        // Because the underlying native didn't have a PreCreate hook, we had
        // to a new (or possibly pre-existing) XPCWN in our compartment.
        // This could be a problem for chrome code that passes XPCOM objects
        // across compartments, because the effects of QI would disappear across
        // compartments.
        //
        // So whenever we pull an XPCWN across compartments in this manner, we
        // give the destination object the union of the two native sets. We try
        // to do this cleverly in the common case to avoid too much overhead.
        XPCWrappedNative *newwn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
        XPCNativeSet *unionSet = XPCNativeSet::GetNewOrUsed(ccx, newwn->GetSet(),
                                                            wn->GetSet(), false);
        if (!unionSet)
            return nsnull;
        newwn->SetSet(unionSet);
    }

    return DoubleWrap(cx, obj, flags);
}
void
XPCCallContext::Init(XPCContext::LangType callerLanguage,
                     JSBool callBeginRequest,
                     JSObject* obj,
                     JSObject* funobj,
                     WrapperInitOptions wrapperInitOptions,
                     jsid name,
                     uintN argc,
                     jsval *argv,
                     jsval *rval)
{
    if (!mXPC)
        return;

    mThreadData = XPCPerThreadData::GetData(mJSContext);

    if (!mThreadData)
        return;

    XPCJSContextStack* stack = mThreadData->GetJSContextStack();
    JSContext* topJSContext;

    if (!stack || NS_FAILED(stack->Peek(&topJSContext))) {
        // If we don't have a stack we're probably in shutdown.
        NS_ASSERTION(!stack, "Bad, Peek failed!");
        mJSContext = nsnull;
        return;
    }

    if (!mJSContext) {
        // This is slightly questionable. If called without an explicit
        // JSContext (generally a call to a wrappedJS) we will use the JSContext
        // on the top of the JSContext stack - if there is one - *before*
        // falling back on the safe JSContext.
        // This is good AND bad because it makes calls from JS -> native -> JS
        // have JS stack 'continuity' for purposes of stack traces etc.
        // Note: this *is* what the pre-XPCCallContext xpconnect did too.

        if (topJSContext)
            mJSContext = topJSContext;
        else if (NS_FAILED(stack->GetSafeJSContext(&mJSContext)) || !mJSContext)
            return;
    }

    if (topJSContext != mJSContext) {
        if (NS_FAILED(stack->Push(mJSContext))) {
            NS_ERROR("bad!");
            return;
        }
        mContextPopRequired = true;
    }

    // Get into the request as early as we can to avoid problems with scanning
    // callcontexts on other threads from within the gc callbacks.

    NS_ASSERTION(!callBeginRequest || mCallerLanguage == NATIVE_CALLER,
                 "Don't call JS_BeginRequest unless the caller is native.");
    if (callBeginRequest)
        JS_BeginRequest(mJSContext);

    mXPCContext = XPCContext::GetXPCContext(mJSContext);
    mPrevCallerLanguage = mXPCContext->SetCallingLangType(mCallerLanguage);

    // hook into call context chain for our thread
    mPrevCallContext = mThreadData->SetCallContext(this);

    // We only need to addref xpconnect once so only do it if this is the first
    // context in the chain.
    if (!mPrevCallContext)
        NS_ADDREF(mXPC);

    mState = HAVE_CONTEXT;

    if (!obj)
        return;

    mScopeForNewJSObjects = obj;

    mState = HAVE_SCOPE;

    mMethodIndex = 0xDEAD;

    mState = HAVE_OBJECT;

    mTearOff = nsnull;
    if (wrapperInitOptions == INIT_SHOULD_LOOKUP_WRAPPER) {
        mWrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(mJSContext, obj,
                                                                funobj,
                                                                &mFlattenedJSObject,
                                                                &mTearOff);
        if (mWrapper) {
            DEBUG_CheckWrapperThreadSafety(mWrapper);

            mFlattenedJSObject = mWrapper->GetFlatJSObject();

            if (mTearOff)
                mScriptableInfo = nsnull;
            else
                mScriptableInfo = mWrapper->GetScriptableInfo();
        } else {
            NS_ABORT_IF_FALSE(!mFlattenedJSObject || IS_SLIM_WRAPPER(mFlattenedJSObject),
                              "should have a slim wrapper");
        }
    }

    if (!JSID_IS_VOID(name))
        SetName(name);

    if (argc != NO_ARGS)
        SetArgsAndResultPtr(argc, argv, rval);

    CHECK_STATE(HAVE_OBJECT);
}
Beispiel #7
0
/* PRBool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out PRBool bp); */
NS_IMETHODIMP
nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper,
                     JSContext * cx, JSObject * obj,
                     const jsval &val, PRBool *bp, PRBool *_retval)
{
    *bp = JS_FALSE;
    nsresult rv = NS_OK;

    if(!JSVAL_IS_PRIMITIVE(val))
    {
        // we have a JSObject
        JSObject* obj = JSVAL_TO_OBJECT(val);

        NS_ASSERTION(obj, "when is an object not an object?");

        // is this really a native xpcom object with a wrapper?
        const nsIID* iid;
        mInfo->GetIIDShared(&iid);

        if(IS_SLIM_WRAPPER(obj))
        {
            XPCWrappedNativeProto* proto = GetSlimWrapperProto(obj);
            if(proto->GetSet()->HasInterfaceWithAncestor(iid))
            {
                *bp = JS_TRUE;
                return NS_OK;
            }

#ifdef DEBUG_slimwrappers
            char foo[NSID_LENGTH];
            iid->ToProvidedString(foo);
            SLIM_LOG_WILL_MORPH_FOR_PROP(cx, obj, foo);
#endif
            if(!MorphSlimWrapper(cx, obj))
                return NS_ERROR_FAILURE;
        }

        XPCWrappedNative* other_wrapper =
           XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);

        if(!other_wrapper)
            return NS_OK;

        // We'll trust the interface set of the wrapper if this is known
        // to be an interface that the objects *expects* to be able to
        // handle.
        if(other_wrapper->HasInterfaceNoQI(*iid))
        {
            *bp = JS_TRUE;
            return NS_OK;
        }

        // Otherwise, we'll end up Querying the native object to be sure.
        XPCCallContext ccx(JS_CALLER, cx);

        AutoMarkingNativeInterfacePtr iface(ccx);
        iface = XPCNativeInterface::GetNewOrUsed(ccx, iid);

        if(iface && other_wrapper->FindTearOff(ccx, iface))
            *bp = JS_TRUE;
    }
    return rv;
}
Beispiel #8
0
JSObject *
WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope,
                                   HandleObject objArg, unsigned flags)
{
    RootedObject obj(cx, objArg);
    // Outerize any raw inner objects at the entry point here, so that we don't
    // have to worry about them for the rest of the wrapping code.
    if (js::IsInnerObject(obj)) {
        JSAutoCompartment ac(cx, obj);
        obj = JS_ObjectToOuterObject(cx, obj);
        NS_ENSURE_TRUE(obj, nullptr);
        // The outerization hook wraps, which means that we can end up with a
        // CCW here if |obj| was a navigated-away-from inner. Strip any CCWs.
        obj = js::UncheckedUnwrap(obj);
        MOZ_ASSERT(js::IsOuterObject(obj));
    }

    // If we've got an outer window, there's nothing special that needs to be
    // done here, and we can move on to the next phase of wrapping. We handle
    // this case first to allow us to assert against wrappers below.
    if (js::IsOuterObject(obj))
        return DoubleWrap(cx, obj, flags);

    // Here are the rules for wrapping:
    // We should never get a proxy here (the JS engine unwraps those for us).
    MOZ_ASSERT(!IsWrapper(obj));

    // As soon as an object is wrapped in a security wrapper, it morphs to be
    // a fat wrapper. (see also: bug XXX).
    if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj))
        return nullptr;

    // Now, our object is ready to be wrapped, but several objects (notably
    // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
    // those objects in a security wrapper, then we need to hand back the
    // wrapper for the new scope instead. Also, global objects don't move
    // between scopes so for those we also want to return the wrapper. So...
    if (!IS_WN_WRAPPER(obj) || !js::GetObjectParent(obj))
        return DoubleWrap(cx, obj, flags);

    XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));

    JSAutoCompartment ac(cx, obj);
    XPCCallContext ccx(JS_CALLER, cx, obj);
    RootedObject wrapScope(cx, scope);

    {
        if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) {
            // We have a precreate hook. This object might enforce that we only
            // ever create JS object for it.

            // Note: this penalizes objects that only have one wrapper, but are
            // being accessed across compartments. We would really prefer to
            // replace the above code with a test that says "do you only have one
            // wrapper?"
            nsresult rv = wn->GetScriptableInfo()->GetCallback()->
                PreCreate(wn->Native(), cx, scope, wrapScope.address());
            NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags));

            // If the handed back scope differs from the passed-in scope and is in
            // a separate compartment, then this object is explicitly requesting
            // that we don't create a second JS object for it: create a security
            // wrapper.
            if (js::GetObjectCompartment(scope) != js::GetObjectCompartment(wrapScope))
                return DoubleWrap(cx, obj, flags);

            RootedObject currentScope(cx, JS_GetGlobalForObject(cx, obj));
            if (MOZ_UNLIKELY(wrapScope != currentScope)) {
                // The wrapper claims it wants to be in the new scope, but
                // currently has a reflection that lives in the old scope. This
                // can mean one of two things, both of which are rare:
                //
                // 1 - The object has a PreCreate hook (we checked for it above),
                // but is deciding to request one-wrapper-per-scope (rather than
                // one-wrapper-per-native) for some reason. Usually, a PreCreate
                // hook indicates one-wrapper-per-native. In this case we want to
                // make a new wrapper in the new scope.
                //
                // 2 - We're midway through wrapper reparenting. The document has
                // moved to a new scope, but |wn| hasn't been moved yet, and
                // we ended up calling JS_WrapObject() on its JS object. In this
                // case, we want to return the existing wrapper.
                //
                // So we do a trick: call PreCreate _again_, but say that we're
                // wrapping for the old scope, rather than the new one. If (1) is
                // the case, then PreCreate will return the scope we pass to it
                // (the old scope). If (2) is the case, PreCreate will return the
                // scope of the document (the new scope).
                RootedObject probe(cx);
                rv = wn->GetScriptableInfo()->GetCallback()->
                    PreCreate(wn->Native(), cx, currentScope, probe.address());

                // Check for case (2).
                if (probe != currentScope) {
                    MOZ_ASSERT(probe == wrapScope);
                    return DoubleWrap(cx, obj, flags);
                }

                // Ok, must be case (1). Fall through and create a new wrapper.
            }

            // Nasty hack for late-breaking bug 781476. This will confuse identity checks,
            // but it's probably better than any of our alternatives.
            //
            // Note: We have to ignore domain here. The JS engine assumes that, given a
            // compartment c, if c->wrap(x) returns a cross-compartment wrapper at time t0,
            // it will also return a cross-compartment wrapper for any time t1 > t0 unless
            // an explicit transplant is performed. In particular, wrapper recomputation
            // assumes that recomputing a wrapper will always result in a wrapper.
            //
            // This doesn't actually pose a security issue, because we'll still compute
            // the correct (opaque) wrapper for the object below given the security
            // characteristics of the two compartments.
            if (!AccessCheck::isChrome(js::GetObjectCompartment(wrapScope)) &&
                 AccessCheck::subsumesIgnoringDomain(js::GetObjectCompartment(wrapScope),
                                                     js::GetObjectCompartment(obj)))
            {
                return DoubleWrap(cx, obj, flags);
            }
        }
    }

    // NB: Passing a holder here inhibits slim wrappers under
    // WrapNativeToJSVal.
    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;

    // This public WrapNativeToJSVal API enters the compartment of 'wrapScope'
    // so we don't have to.
    RootedValue v(cx);
    nsresult rv =
        nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, wrapScope, wn->Native(), nullptr,
                                                           &NS_GET_IID(nsISupports), false,
                                                           v.address(), getter_AddRefs(holder));
    if (NS_SUCCEEDED(rv)) {
        obj = JSVAL_TO_OBJECT(v);
        NS_ASSERTION(IS_WN_WRAPPER(obj), "bad object");

        // Because the underlying native didn't have a PreCreate hook, we had
        // to a new (or possibly pre-existing) XPCWN in our compartment.
        // This could be a problem for chrome code that passes XPCOM objects
        // across compartments, because the effects of QI would disappear across
        // compartments.
        //
        // So whenever we pull an XPCWN across compartments in this manner, we
        // give the destination object the union of the two native sets. We try
        // to do this cleverly in the common case to avoid too much overhead.
        XPCWrappedNative *newwn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
        XPCNativeSet *unionSet = XPCNativeSet::GetNewOrUsed(ccx, newwn->GetSet(),
                                                            wn->GetSet(), false);
        if (!unionSet)
            return nullptr;
        newwn->SetSet(unionSet);
    }

    return DoubleWrap(cx, obj, flags);
}
/* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */
NS_IMETHODIMP
nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper,
                     JSContext * cx, JSObject * obj,
                     const jsval &val, bool *bp, bool *_retval)
{
    *bp = false;
    nsresult rv = NS_OK;

    if (!JSVAL_IS_PRIMITIVE(val)) {
        // we have a JSObject
        JSObject* obj = JSVAL_TO_OBJECT(val);

        NS_ASSERTION(obj, "when is an object not an object?");

        // is this really a native xpcom object with a wrapper?
        const nsIID* iid;
        mInfo->GetIIDShared(&iid);

        if (IS_SLIM_WRAPPER(obj)) {
            XPCWrappedNativeProto* proto = GetSlimWrapperProto(obj);
            if (proto->GetSet()->HasInterfaceWithAncestor(iid)) {
                *bp = true;
                return NS_OK;
            }

#ifdef DEBUG_slimwrappers
            char foo[NSID_LENGTH];
            iid->ToProvidedString(foo);
            SLIM_LOG_WILL_MORPH_FOR_PROP(cx, obj, foo);
#endif
            if (!MorphSlimWrapper(cx, obj))
                return NS_ERROR_FAILURE;
        }

        nsISupports *identity;
        if (mozilla::dom::binding::instanceIsProxy(obj)) {
            identity =
                static_cast<nsISupports*>(js::GetProxyPrivate(obj).toPrivate());
        } else if (mozilla::dom::bindings::IsDOMClass(js::GetObjectJSClass(obj))) {
            NS_ASSERTION(mozilla::dom::bindings::DOMJSClass::FromJSClass(
                              js::GetObjectJSClass(obj))->mDOMObjectIsISupports,
                         "This only works on nsISupports classes!");
            identity =
                mozilla::dom::bindings::UnwrapDOMObject<nsISupports>(obj,
                                                                     js::GetObjectJSClass(obj));
        } else {
            identity = nsnull;
        }

        if (identity) {
            nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(identity);

            XPCCallContext ccx(JS_CALLER, cx);

            AutoMarkingNativeSetPtr set(ccx);
            set = XPCNativeSet::GetNewOrUsed(ccx, ci);
            if (!set)
                return NS_ERROR_FAILURE;
            *bp = set->HasInterfaceWithAncestor(iid);
            return NS_OK;
        }

        XPCWrappedNative* other_wrapper =
           XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);

        if (!other_wrapper)
            return NS_OK;

        // We'll trust the interface set of the wrapper if this is known
        // to be an interface that the objects *expects* to be able to
        // handle.
        if (other_wrapper->HasInterfaceNoQI(*iid)) {
            *bp = true;
            return NS_OK;
        }

        // Otherwise, we'll end up Querying the native object to be sure.
        XPCCallContext ccx(JS_CALLER, cx);

        AutoMarkingNativeInterfacePtr iface(ccx);
        iface = XPCNativeInterface::GetNewOrUsed(ccx, iid);

        nsresult findResult = NS_OK;
        if (iface && other_wrapper->FindTearOff(ccx, iface, false, &findResult))
            *bp = true;
        if (NS_FAILED(findResult) && findResult != NS_ERROR_NO_INTERFACE)
            rv = findResult;
    }
    return rv;
}
JSObject *
WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags)
{
    // Don't unwrap an outer window, just double wrap it if needed.
    if (obj->getClass()->ext.innerObject)
        return DoubleWrap(cx, obj, flags);

    // Here are the rules for wrapping:
    // We should never get a proxy here (the JS engine unwraps those for us).
    JS_ASSERT(!obj->isWrapper());

    // As soon as an object is wrapped in a security wrapper, it morphs to be
    // a fat wrapper. (see also: bug XXX).
    if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj))
        return nsnull;

    // We only hand out outer objects to script.
    obj = GetCurrentOuter(cx, obj);
    if (obj->getClass()->ext.innerObject)
        return DoubleWrap(cx, obj, flags);

    // Now, our object is ready to be wrapped, but several objects (notably
    // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
    // those objects in a security wrapper, then we need to hand back the
    // wrapper for the new scope instead. Also, global objects don't move
    // between scopes so for those we also want to return the wrapper. So...
    if (!IS_WN_WRAPPER(obj) || !obj->getParent())
        return DoubleWrap(cx, obj, flags);

    XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));

    // If the object doesn't have classinfo we want to return the same
    // XPCWrappedNative so that we keep the same set of interfaces.
    if (!wn->GetClassInfo())
        return DoubleWrap(cx, obj, flags);

    JSAutoEnterCompartment ac;
    if (!ac.enter(cx, obj))
        return nsnull;
    XPCCallContext ccx(JS_CALLER, cx, obj);

    {
        if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) {
            // We have a precreate hook. This object might enforce that we only
            // ever create JS object for it.
            JSObject *originalScope = scope;
            nsresult rv = wn->GetScriptableInfo()->GetCallback()->
                PreCreate(wn->Native(), cx, scope, &scope);
            NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags));

            // If the handed back scope differs from the passed-in scope and is in
            // a separate compartment, then this object is explicitly requesting
            // that we don't create a second JS object for it: create a security
            // wrapper.
            if (originalScope->compartment() != scope->getCompartment())
                return DoubleWrap(cx, obj, flags);

            // Note: this penalizes objects that only have one wrapper, but are
            // being accessed across compartments. We would really prefer to
            // replace the above code with a test that says "do you only have one
            // wrapper?"
        }
    }

    // NB: Passing a holder here inhibits slim wrappers under
    // WrapNativeToJSVal.
    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;

    // This public WrapNativeToJSVal API enters the compartment of 'scope'
    // so we don't have to.
    jsval v;
    nsresult rv =
        nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull,
                                                           &NS_GET_IID(nsISupports), PR_FALSE,
                                                           &v, getter_AddRefs(holder));
    if (NS_SUCCEEDED(rv)) {
        obj = JSVAL_TO_OBJECT(v);
        NS_ASSERTION(IS_WN_WRAPPER(obj), "bad object");

        XPCWrappedNative *newwn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
        if (newwn->GetSet()->GetInterfaceCount() < wn->GetSet()->GetInterfaceCount())
            newwn->SetSet(wn->GetSet());
    }

    return DoubleWrap(cx, obj, flags);
}
JSBool
WrapObject(JSContext *cx, JSObject *scope, jsval v, jsval *vp)
{
  // This might be redundant if called from XPC_SJOW_Construct, but it should
  // be cheap in that case.
  JSObject *objToWrap = UnsafeUnwrapSecurityWrapper(cx, JSVAL_TO_OBJECT(v));
  if (!objToWrap ||
      JS_TypeOfValue(cx, OBJECT_TO_JSVAL(objToWrap)) == JSTYPE_XML) {
    return ThrowException(NS_ERROR_INVALID_ARG, cx);
  }

  // Prevent script created Script objects from ever being wrapped
  // with XPCSafeJSObjectWrapper, and never let the eval function
  // object be directly wrapped.

  if (objToWrap->getClass() == &js_ScriptClass ||
      (JS_ObjectIsFunction(cx, objToWrap) &&
       JS_GetFunctionFastNative(cx, JS_ValueToFunction(cx, v)) ==
       XPCWrapper::sEvalNative)) {
    return ThrowException(NS_ERROR_INVALID_ARG, cx);
  }

  XPCWrappedNativeScope *xpcscope =
    XPCWrappedNativeScope::FindInJSObjectScope(cx, scope);
  NS_ASSERTION(xpcscope, "what crazy scope are we in?");

  XPCWrappedNative *wrappedNative;
  WrapperType type = xpcscope->GetWrapperFor(cx, objToWrap, SJOW,
                                             &wrappedNative);

  // NB: We allow XOW here because we're as restrictive as it is (and we know
  // we're same origin here).
  if (type != NONE && type != XOW && !(type & SJOW)) {
    return ThrowException(NS_ERROR_INVALID_ARG, cx);
  }

  SLIM_LOG_WILL_MORPH(cx, objToWrap);
  if (IS_SLIM_WRAPPER(objToWrap) && !MorphSlimWrapper(cx, objToWrap)) {
    return ThrowException(NS_ERROR_FAILURE, cx);
  }

  XPCWrappedNative *wn =
    XPCWrappedNative::GetWrappedNativeOfJSObject(cx, objToWrap);
  if (wn) {
    CheckWindow(wn);
  }

  JSObject *wrapperObj =
    JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SJOWClass), nsnull, scope);

  if (!wrapperObj) {
    // JS_NewObjectWithGivenProto already threw.
    return JS_FALSE;
  }

  *vp = OBJECT_TO_JSVAL(wrapperObj);
  if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot,
                          OBJECT_TO_JSVAL(objToWrap)) ||
      !JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO)) {
    return JS_FALSE;
  }

  return JS_TRUE;
}
Beispiel #12
0
nsresult
xpc_qsUnwrapArgImpl(JSContext *cx,
                    jsval v,
                    const nsIID &iid,
                    void **ppArg,
                    nsISupports **ppArgRef,
                    jsval *vp)
{
    // From XPCConvert::JSData2Native
    if(JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))
    {
        *ppArg = nsnull;
        *ppArgRef = nsnull;
        return NS_OK;
    }

    if(!JSVAL_IS_OBJECT(v))
    {
        *ppArgRef = nsnull;
        return ((JSVAL_IS_INT(v) && JSVAL_TO_INT(v) == 0)
                ? NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL
                : NS_ERROR_XPC_BAD_CONVERT_JS);
    }
    JSObject *src = JSVAL_TO_OBJECT(v);

    if(IS_SLIM_WRAPPER(src))
    {
        nsISupports *iface = static_cast<nsISupports*>(xpc_GetJSPrivate(src));
        if(NS_FAILED(getNative(iface, GetOffsetsFromSlimWrapper(src),
                               src, iid, ppArg, ppArgRef, vp)))
            return NS_ERROR_XPC_BAD_CONVERT_JS;
        return NS_OK;
    }

    // From XPCConvert::JSObject2NativeInterface
    XPCWrappedNative* wrappedNative =
        XPCWrappedNative::GetWrappedNativeOfJSObject(cx, src);
    nsISupports *iface;
    if(wrappedNative)
    {
        iface = wrappedNative->GetIdentityObject();
        if(NS_FAILED(getNativeFromWrapper(wrappedNative, iid, ppArg, ppArgRef,
                                          vp)))
            return NS_ERROR_XPC_BAD_CONVERT_JS;
        return NS_OK;
    }
    // else...
    // Slow path.

    // XXX E4X breaks the world. Don't try wrapping E4X objects!
    // This hack can be removed (or changed accordingly) when the
    // DOM <-> E4X bindings are complete, see bug 270553
    if(JS_TypeOfValue(cx, OBJECT_TO_JSVAL(src)) == JSTYPE_XML)
    {
        *ppArgRef = nsnull;
        return NS_ERROR_XPC_BAD_CONVERT_JS;
    }

    // Does the JSObject have 'nsISupportness'?
    // XXX hmm, I wonder if this matters anymore with no
    // oldstyle DOM objects around.
    if(XPCConvert::GetISupportsFromJSObject(src, &iface))
    {
        if(!iface || NS_FAILED(iface->QueryInterface(iid, ppArg)))
        {
            *ppArgRef = nsnull;
            return NS_ERROR_XPC_BAD_CONVERT_JS;
        }

        *ppArgRef = static_cast<nsISupports*>(*ppArg);
        return NS_OK;
    }

    // Create the ccx needed for quick stubs.
    XPCCallContext ccx(JS_CALLER, cx);
    if(!ccx.IsValid())
    {
        *ppArgRef = nsnull;
        return NS_ERROR_XPC_BAD_CONVERT_JS;
    }

    nsXPCWrappedJS *wrapper;
    nsresult rv =
        nsXPCWrappedJS::GetNewOrUsed(ccx, src, iid, nsnull, &wrapper);
    if(NS_FAILED(rv) || !wrapper)
    {
        *ppArgRef = nsnull;
        return rv;
    }

    // We need to go through the QueryInterface logic to make this return
    // the right thing for the various 'special' interfaces; e.g.
    // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
    // there is an outer to avoid nasty recursion.
    rv = wrapper->QueryInterface(iid, ppArg);
    if(NS_SUCCEEDED(rv))
    {
        *ppArgRef = static_cast<nsISupports*>(*ppArg);
        *vp = OBJECT_TO_JSVAL(wrapper->GetJSObject());
    }
    NS_RELEASE(wrapper);
    return rv;
}
JSBool
XPC_SJOW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                   jsval *rval)
{
  if (argc < 1) {
    return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx);
  }

  // |obj| almost always has the wrong proto and parent so we have to create
  // our own object anyway.  Set |obj| to null so we don't use it by accident.
  obj = nsnull;

  if (JSVAL_IS_PRIMITIVE(argv[0])) {
    JSStackFrame *fp = nsnull;
    if (JS_FrameIterator(cx, &fp) && JS_IsConstructorFrame(cx, fp)) {
      return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
    }

    *rval = argv[0];
    return JS_TRUE;
  }

  JSObject *objToWrap = JSVAL_TO_OBJECT(argv[0]);

  // Prevent script created Script objects from ever being wrapped
  // with XPCSafeJSObjectWrapper, and never let the eval function
  // object be directly wrapped.

  if (STOBJ_GET_CLASS(objToWrap) == &js_ScriptClass ||
      (::JS_ObjectIsFunction(cx, objToWrap) &&
       ::JS_GetFunctionNative(cx, ::JS_ValueToFunction(cx, argv[0])) ==
       XPCWrapper::sEvalNative)) {
    return ThrowException(NS_ERROR_INVALID_ARG, cx);
  }

  SLIM_LOG_WILL_MORPH(cx, objToWrap);
  if(IS_SLIM_WRAPPER(objToWrap) && !MorphSlimWrapper(cx, objToWrap)) {
    return ThrowException(NS_ERROR_FAILURE, cx);
  }

  // Check that the caller can access the unsafe object.
  if (!CanCallerAccess(cx, objToWrap)) {
    // CanCallerAccess() already threw for us.
    return JS_FALSE;
  }

  JSObject *unsafeObj = GetUnsafeObject(objToWrap);

  if (unsafeObj) {
    // We're asked to wrap an already wrapped object. Re-wrap the
    // object wrapped by the given wrapper.

    objToWrap = unsafeObj;
  }

  // Don't use the object the JS engine created for us, it is in most
  // cases incorectly parented and has a proto from the wrong scope.
  JSObject *wrapperObj =
    ::JS_NewObjectWithGivenProto(cx, &sXPC_SJOW_JSClass.base, nsnull,
                                 objToWrap);

  if (!wrapperObj) {
    // JS_NewObjectWithGivenProto already threw.
    return JS_FALSE;
  }

  if (!::JS_SetReservedSlot(cx, wrapperObj, XPC_SJOW_SLOT_IS_RESOLVING,
                            JSVAL_ZERO)) {
    return JS_FALSE;
  }

  *rval = OBJECT_TO_JSVAL(wrapperObj);

  return JS_TRUE;
}