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; }
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)); }
/** * 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"); }
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); }
/* 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; }
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; }
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; }