JSBool XPCDispInterface::Member::GetValue(XPCCallContext& ccx, XPCNativeInterface * iface, jsval * retval) const { // This is a method or attribute - we'll be needing a function object // We need to use the safe context for this thread because we don't want // to parent the new (and cached forever!) function object to the current // JSContext's global object. That would be bad! if((mType & RESOLVED) == 0) { JSContext* cx = ccx.GetSafeJSContext(); if(!cx) return JS_FALSE; intN argc; JSNative callback; // Is this a function or a parameterized getter/setter if(IsFunction() || IsParameterizedProperty()) { argc = GetParamCount(); callback = XPC_IDispatch_CallMethod; } else { argc = 0; callback = XPC_IDispatch_GetterSetter; } JSFunction *fun = JS_NewFunctionById(cx, callback, argc, 0, nsnull, mName); if(!fun) return JS_FALSE; JSObject* funobj = JS_GetFunctionObject(fun); if(!funobj) return JS_FALSE; // Store ourselves and our native interface within the JSObject if(!JS_SetReservedSlot(ccx, funobj, 0, PRIVATE_TO_JSVAL((void *) this))) return JS_FALSE; if(!JS_SetReservedSlot(ccx, funobj, 1, PRIVATE_TO_JSVAL(iface))) return JS_FALSE; { // scoped lock XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); const_cast<Member*>(this)->mVal = OBJECT_TO_JSVAL(funobj); const_cast<Member*>(this)->mType |= RESOLVED; } } *retval = mVal; return JS_TRUE; }
// static void XPCWrappedNativeScope::TraverseScopes(XPCCallContext& ccx) { // Hold the lock throughout. XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); for(XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) if(cur->mGlobalJSObject && cur->mScriptObjectPrincipal) { ccx.GetXPConnect()->RecordTraversal(cur->mGlobalJSObject, cur->mScriptObjectPrincipal); } }
XPCWrappedNativeScope::XPCWrappedNativeScope(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative) : mRuntime(ccx.GetRuntime()), mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)), mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)), mMainThreadWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)), mComponents(nsnull), mNext(nsnull), mGlobalJSObject(nsnull), mPrototypeJSObject(nsnull), mPrototypeNoHelper(nsnull), mScriptObjectPrincipal(nsnull), mNewDOMBindingsEnabled(ccx.GetRuntime()->NewDOMBindingsEnabled()), mExperimentalBindingsEnabled(ccx.GetRuntime()->ExperimentalBindingsEnabled()) { // add ourselves to the scopes list { // scoped lock XPCAutoLock lock(mRuntime->GetMapLock()); #ifdef DEBUG for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) NS_ASSERTION(aGlobal != cur->GetGlobalJSObject(), "dup object"); #endif mNext = gScopes; gScopes = this; // Grab the XPCContext associated with our context. mContext = XPCContext::GetXPCContext(ccx.GetJSContext()); mContext->AddScope(this); } if (aGlobal) SetGlobal(ccx, aGlobal, aNative); DEBUG_TrackNewScope(this); MOZ_COUNT_CTOR(XPCWrappedNativeScope); }
// static nsresult XPCWrappedNativeScope::ClearAllWrappedNativeSecurityPolicies(XPCCallContext& ccx) { // Hold the lock throughout. XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) { cur->mWrappedNativeProtoMap->Enumerate(WNProtoSecPolicyClearer, nullptr); cur->mMainThreadWrappedNativeProtoMap->Enumerate(WNProtoSecPolicyClearer, nullptr); } DEBUG_TrackScopeTraversal(); return NS_OK; }
XPCWrappedNativeScope::XPCWrappedNativeScope(XPCCallContext& ccx, JSObject* aGlobal) : mRuntime(ccx.GetRuntime()), mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)), mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)), mWrapperMap(WrappedNative2WrapperMap::newMap(XPC_WRAPPER_MAP_SIZE)), mComponents(nsnull), mNext(nsnull), mGlobalJSObject(nsnull), mPrototypeJSObject(nsnull), mPrototypeJSFunction(nsnull), mPrototypeNoHelper(nsnull) { // add ourselves to the scopes list { // scoped lock XPCAutoLock lock(mRuntime->GetMapLock()); #ifdef DEBUG for(XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) NS_ASSERTION(aGlobal != cur->GetGlobalJSObject(), "dup object"); #endif mNext = gScopes; gScopes = this; // Grab the XPCContext associated with our context. mContext = mRuntime->GetContextMap()->Find(ccx.GetJSContext()); NS_ASSERTION(mContext, "Context map is not synchronized"); mContext->AddScope(this); } if(aGlobal) SetGlobal(ccx, aGlobal); DEBUG_TrackNewScope(this); MOZ_COUNT_CTOR(XPCWrappedNativeScope); }
// static nsresult nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx, JSObject* aJSObj, REFNSIID aIID, nsISupports* aOuter, nsXPCWrappedJS** wrapperResult) { JSObject2WrappedJSMap* map; JSObject* rootJSObj; nsXPCWrappedJS* root = nsnull; nsXPCWrappedJS* wrapper = nsnull; nsXPCWrappedJSClass* clazz = nsnull; XPCJSRuntime* rt = ccx.GetRuntime(); JSBool release_root = JS_FALSE; map = rt->GetWrappedJSMap(); if (!map) { NS_ASSERTION(map,"bad map"); return NS_ERROR_FAILURE; } nsXPCWrappedJSClass::GetNewOrUsed(ccx, aIID, &clazz); if (!clazz) return NS_ERROR_FAILURE; // from here on we need to return through 'return_wrapper' // always find the root JSObject rootJSObj = clazz->GetRootJSObject(ccx, aJSObj); if (!rootJSObj) goto return_wrapper; // look for the root wrapper, and if found, hold the map lock until // we've added our ref to prevent another thread from destroying it // under us { // scoped lock XPCAutoLock lock(rt->GetMapLock()); root = map->Find(rootJSObj); if (root) { if ((nsnull != (wrapper = root->Find(aIID))) || (nsnull != (wrapper = root->FindInherited(aIID)))) { NS_ADDREF(wrapper); goto return_wrapper; } } } if (!root) { // build the root wrapper if (rootJSObj == aJSObj) { // the root will do double duty as the interface wrapper wrapper = root = new nsXPCWrappedJS(ccx, aJSObj, clazz, nsnull, aOuter); if (!root) goto return_wrapper; { // scoped lock #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)wrapper, (void*)aJSObj); #endif XPCAutoLock lock(rt->GetMapLock()); map->Add(root); } if (!CheckMainThreadOnly(root)) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(root); wrapper = NULL; } goto return_wrapper; } else { // just a root wrapper nsXPCWrappedJSClass* rootClazz = nsnull; nsXPCWrappedJSClass::GetNewOrUsed(ccx, NS_GET_IID(nsISupports), &rootClazz); if (!rootClazz) goto return_wrapper; root = new nsXPCWrappedJS(ccx, rootJSObj, rootClazz, nsnull, aOuter); NS_RELEASE(rootClazz); if (!root) goto return_wrapper; release_root = JS_TRUE; { // scoped lock #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)root, (void*)rootJSObj); #endif XPCAutoLock lock(rt->GetMapLock()); map->Add(root); } if (!CheckMainThreadOnly(root)) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(root); goto return_wrapper; } } } // at this point we have a root and may need to build the specific wrapper NS_ASSERTION(root,"bad root"); NS_ASSERTION(clazz,"bad clazz"); if (!wrapper) { wrapper = new nsXPCWrappedJS(ccx, aJSObj, clazz, root, aOuter); if (!wrapper) goto return_wrapper; #if DEBUG_xpc_leaks printf("Created nsXPCWrappedJS %p, JSObject is %p\n", (void*)wrapper, (void*)aJSObj); #endif } wrapper->mNext = root->mNext; root->mNext = wrapper; return_wrapper: if (clazz) NS_RELEASE(clazz); if (release_root) NS_RELEASE(root); if (!wrapper) return NS_ERROR_FAILURE; *wrapperResult = wrapper; return NS_OK; }
JSBool XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface) { if(IsConstant()) { const nsXPTConstant* constant; if(NS_FAILED(iface->GetInterfaceInfo()->GetConstant(mIndex, &constant))) return JS_FALSE; const nsXPTCMiniVariant& mv = *constant->GetValue(); // XXX Big Hack! nsXPTCVariant v; v.flags = 0; v.type = constant->GetType(); memcpy(&v.val, &mv.val, sizeof(mv.val)); jsval resultVal; if(!XPCConvert::NativeData2JS(ccx, &resultVal, &v.val, v.type, nsnull, nsnull, nsnull)) return JS_FALSE; { // scoped lock XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); mVal = resultVal; mFlags |= RESOLVED; } return JS_TRUE; } // else... // This is a method or attribute - we'll be needing a function object intN argc; intN flags; JSNative callback; if(IsMethod()) { const nsXPTMethodInfo* info; if(NS_FAILED(iface->GetInterfaceInfo()->GetMethodInfo(mIndex, &info))) return JS_FALSE; // Note: ASSUMES that retval is last arg. argc = (intN) info->GetParamCount(); if(argc && info->GetParam((uint8)(argc-1)).IsRetval()) argc-- ; flags = 0; callback = XPC_WN_CallMethod; } else { if(IsWritableAttribute()) flags = JSFUN_GETTER | JSFUN_SETTER; else flags = JSFUN_GETTER; argc = 0; callback = XPC_WN_GetterSetter; } // We need to use the safe context for this thread because we don't want // to parent the new (and cached forever!) function object to the current // JSContext's global object. That would be bad! JSContext* cx = ccx.GetSafeJSContext(); if(!cx) return JS_FALSE; const char *memberName = iface->GetMemberName(ccx, this); jsrefcount suspendDepth = 0; if(cx != ccx) { // Switching contexts, suspend the old and enter the new request. suspendDepth = JS_SuspendRequest(ccx); JS_BeginRequest(cx); } JSFunction *fun = JS_NewFunction(cx, callback, argc, flags, nsnull, memberName); if(suspendDepth) { JS_EndRequest(cx); JS_ResumeRequest(ccx, suspendDepth); } if(!fun) return JS_FALSE; JSObject* funobj = JS_GetFunctionObject(fun); if(!funobj) return JS_FALSE; AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(funobj)); STOBJ_CLEAR_PARENT(funobj); STOBJ_CLEAR_PROTO(funobj); if(!JS_SetReservedSlot(ccx, funobj, 0, PRIVATE_TO_JSVAL(iface))|| !JS_SetReservedSlot(ccx, funobj, 1, PRIVATE_TO_JSVAL(this))) return JS_FALSE; { // scoped lock XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); mVal = OBJECT_TO_JSVAL(funobj); mFlags |= RESOLVED; } return JS_TRUE; }
JSBool XPCDispObject::Invoke(XPCCallContext & ccx, CallMode mode) { nsresult rv = ccx.CanCallNow(); if(NS_FAILED(rv)) { // If the security manager is complaining then this is not really an // internal error in xpconnect. So, no reason to botch the assertion. NS_ASSERTION(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, "hmm? CanCallNow failed in XPCDispObject::Invoke. " "We are finding out about this late!"); XPCThrower::Throw(rv, ccx); return JS_FALSE; } // TODO: Remove type cast and change GetIDispatchMember to use the correct type XPCDispInterface::Member* member = reinterpret_cast<XPCDispInterface::Member*>(ccx.GetIDispatchMember()); XPCJSRuntime* rt = ccx.GetRuntime(); XPCContext* xpcc = ccx.GetXPCContext(); XPCPerThreadData* tls = ccx.GetThreadData(); jsval* argv = ccx.GetArgv(); uintN argc = ccx.GetArgc(); tls->SetException(nsnull); xpcc->SetLastResult(NS_ERROR_UNEXPECTED); // set up the method index and do the security check if needed PRUint32 secFlag; PRUint32 secAction; switch(mode) { case CALL_METHOD: secFlag = nsIXPCSecurityManager::HOOK_CALL_METHOD; secAction = nsIXPCSecurityManager::ACCESS_CALL_METHOD; break; case CALL_GETTER: secFlag = nsIXPCSecurityManager::HOOK_GET_PROPERTY; secAction = nsIXPCSecurityManager::ACCESS_GET_PROPERTY; break; case CALL_SETTER: secFlag = nsIXPCSecurityManager::HOOK_SET_PROPERTY; secAction = nsIXPCSecurityManager::ACCESS_SET_PROPERTY; break; default: NS_ASSERTION(0,"bad value"); return JS_FALSE; } jsval name = member->GetName(); nsIXPCSecurityManager* sm = xpcc->GetAppropriateSecurityManager(secFlag); XPCWrappedNative* wrapper = ccx.GetWrapper(); if(sm && NS_FAILED(sm->CanAccess(secAction, &ccx, ccx, ccx.GetFlattenedJSObject(), wrapper->GetIdentityObject(), wrapper->GetClassInfo(), name, wrapper->GetSecurityInfoAddr()))) { // the security manager vetoed. It should have set an exception. return JS_FALSE; } IDispatch * pObj = reinterpret_cast<IDispatch*> (ccx.GetTearOff()->GetNative()); PRUint32 args = member->GetParamCount(); uintN err; // Make sure setter has one argument if(mode == CALL_SETTER) args = 1; // Allow for optional parameters. We'll let COM handle the error if there // are not enough parameters if(argc < args) args = argc; XPCDispParams * params = new XPCDispParams(args); jsval val; // If this is a setter, we just need to convert the first parameter if(mode == CALL_SETTER) { params->SetNamedPropID(); if(!XPCDispConvert::JSToCOM(ccx, argv[0], params->GetParamRef(0), err)) { delete params; return ThrowBadParam(err, 0, ccx); } } else if(mode != CALL_GETTER) // This is a function { // Convert the arguments to the function for(PRUint32 index = 0; index < args; ++index) { const XPCDispInterface::Member::ParamInfo & paramInfo = member->GetParamInfo(index); if(paramInfo.IsIn()) { val = argv[index]; if(paramInfo.IsOut()) { if(JSVAL_IS_PRIMITIVE(val) || !OBJ_GET_PROPERTY(ccx, JSVAL_TO_OBJECT(val), rt->GetStringID(XPCJSRuntime::IDX_VALUE), &val)) { delete params; return ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, index, ccx); } paramInfo.InitializeOutputParam(params->GetOutputBuffer(index), params->GetParamRef(index)); } if(!XPCDispConvert::JSToCOM(ccx, val, params->GetParamRef(index), err, paramInfo.IsOut())) { delete params; return ThrowBadParam(err, index, ccx); } } else { paramInfo.InitializeOutputParam(params->GetOutputBuffer(index), params->GetParamRef(index)); } } } // If this is a parameterized property if(member->IsParameterizedProperty()) { // We need to get a parameterized property object to return to JS // NewInstance takes ownership of params if(XPCDispParamPropJSClass::NewInstance(ccx, wrapper, member->GetDispID(), params, &val)) { ccx.SetRetVal(val); if(!JS_IdToValue(ccx, 1, &val)) { // This shouldn't fail NS_ERROR("JS_IdToValue failed in XPCDispParamPropJSClass::NewInstance"); return JS_FALSE; } JS_SetCallReturnValue2(ccx, val); return JS_TRUE; } // NewInstance would only fail if there was an out of memory problem JS_ReportOutOfMemory(ccx); delete params; return JS_FALSE; } JSBool retval = Dispatch(ccx, pObj, member->GetDispID(), mode, params, &val, member, rt); if(retval && mode == CALL_SETTER) { ccx.SetRetVal(argv[0]); } else { ccx.SetRetVal(val); } delete params; return retval; }