// static void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, PRBool own) { char* sz = nsnull; if(ccx.HasInterfaceAndMember()) { XPCNativeInterface* iface = ccx.GetInterface(); jsid id = ccx.GetMember()->GetName(); JSAutoByteString bytes; const char *name = JSID_IS_VOID(id) ? "Unknown" : bytes.encode(ccx, JSID_TO_STRING(id)); if(!name) { name = ""; } sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name); } if(sz) { if(own) JS_smprintf_free(*psz); *psz = sz; } }
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); } }
bool xpc_qsUnwrapThisFromCcxImpl(XPCCallContext &ccx, const nsIID &iid, void **ppThis, nsISupports **pThisRef, jsval *vp) { nsISupports *native = ccx.GetIdentityObject(); if (!native) return xpc_qsThrow(ccx.GetJSContext(), NS_ERROR_XPC_HAS_BEEN_SHUTDOWN); RootedObject obj(ccx, ccx.GetFlattenedJSObject()); nsresult rv = getNative(native, obj, iid, ppThis, pThisRef, vp); if (NS_FAILED(rv)) return xpc_qsThrow(ccx.GetJSContext(), rv); return true; }
void XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative) { // We allow for calling this more than once. This feature is used by // nsXPConnect::InitClassesWithNewWrappedGlobal. mGlobalJSObject = aGlobal; mScriptObjectPrincipal = nsnull; // Try to find the native global object. If we didn't receive it explicitly, // we might be able to find it in the private slot. nsISupports *native; if (aNative) { native = aNative; } else { const JSClass *jsClass = js::GetObjectJSClass(aGlobal); nsISupports *priv; if (!(~jsClass->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { // Our global has an nsISupports native pointer. Let's // see whether it's what we want. priv = static_cast<nsISupports*>(xpc_GetJSPrivate(aGlobal)); } else if ((jsClass->flags & JSCLASS_IS_DOMJSCLASS) && bindings::DOMJSClass::FromJSClass(jsClass)->mDOMObjectIsISupports) { priv = bindings::UnwrapDOMObject<nsISupports>(aGlobal, jsClass); } else { priv = nsnull; } nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(priv); if (wn) native = static_cast<XPCWrappedNative*>(wn.get())->GetIdentityObject(); else native = priv; } // Now init our script object principal, if the new global has one. nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native); mScriptObjectPrincipal = sop; // Lookup 'globalObject.Object.prototype' for our wrapper's proto JSObject *objectPrototype = JS_GetObjectPrototype(ccx.GetJSContext(), aGlobal); if (objectPrototype) mPrototypeJSObject = objectPrototype; else NS_ERROR("Can't get globalObject.Object.prototype"); // Clear the no helper wrapper prototype object so that a new one // gets created if needed. mPrototypeNoHelper = nsnull; }
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); }
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 void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, PRBool own) { char* sz = nsnull; if(ccx.HasInterfaceAndMember()) { XPCNativeInterface* iface = ccx.GetInterface(); #ifdef XPC_IDISPATCH_SUPPORT NS_ASSERTION(ccx.GetIDispatchMember() == nsnull || ccx.GetMember() == nsnull, "Both IDispatch member and regular XPCOM member " "were set in XPCCallContext"); char const * name; if(ccx.GetIDispatchMember()) { XPCDispInterface::Member * member = reinterpret_cast<XPCDispInterface::Member*>(ccx.GetIDispatchMember()); if(member && JSVAL_IS_STRING(member->GetName())) { name = JS_GetStringBytes(JSVAL_TO_STRING(member->GetName())); } else name = "Unknown"; } else name = iface->GetMemberName(ccx, ccx.GetMember()); sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name); #else sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), iface->GetMemberName(ccx, ccx.GetMember())); #endif } if(sz) { if(own) JS_smprintf_free(*psz); *psz = sz; } }
static bool ToStringGuts(XPCCallContext& ccx) { char* sz; XPCWrappedNative* wrapper = ccx.GetWrapper(); if (wrapper) sz = wrapper->ToString(ccx.GetTearOff()); else sz = JS_smprintf("[xpconnect wrapped native prototype]"); if (!sz) { JS_ReportOutOfMemory(ccx); return false; } JSString* str = JS_NewStringCopyZ(ccx, sz); JS_smprintf_free(sz); if (!str) return false; ccx.SetRetVal(JS::StringValue(str)); return true; }
// 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; }
// static void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, PRBool own) { char* sz = nsnull; if(ccx.HasInterfaceAndMember()) { XPCNativeInterface* iface = ccx.GetInterface(); jsid id = JSID_VOID; #ifdef XPC_IDISPATCH_SUPPORT NS_ASSERTION(ccx.GetIDispatchMember() == nsnull || ccx.GetMember() == nsnull, "Both IDispatch member and regular XPCOM member " "were set in XPCCallContext"); if(ccx.GetIDispatchMember()) { XPCDispInterface::Member * member = reinterpret_cast<XPCDispInterface::Member*>(ccx.GetIDispatchMember()); if(member && JSID_IS_STRING(member->GetName())) { id = member->GetName(); } } else #endif { id = ccx.GetMember()->GetName(); } JSAutoByteString bytes; const char *name = JSID_IS_VOID(id) ? "Unknown" : bytes.encode(ccx, JSID_TO_STRING(id)); if(!name) { name = ""; } sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name); } if(sz) { if(own) JS_smprintf_free(*psz); *psz = sz; } }
void XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative) { // We allow for calling this more than once. This feature is used by // nsXPConnect::InitClassesWithNewWrappedGlobal. mGlobalJSObject = aGlobal; mScriptObjectPrincipal = nsnull; // Try to find the native global object. If we didn't receive it explicitly, // we might be able to find it in the private slot. nsISupports* native = aNative; if (!native && !(~js::GetObjectJSClass(aGlobal)->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { // Get the private. It might be a WN, in which case we dig deeper. native = (nsISupports*)xpc_GetJSPrivate(aGlobal); nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native); if (wn) native = static_cast<XPCWrappedNative*>(native)->GetIdentityObject(); } // Now init our script object principal, if the new global has one. nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native); mScriptObjectPrincipal = sop; // Lookup 'globalObject.Object.prototype' for our wrapper's proto JSObject *objectPrototype = JS_GetObjectPrototype(ccx.GetJSContext(), aGlobal); if (objectPrototype) mPrototypeJSObject = objectPrototype; else NS_ERROR("Can't get globalObject.Object.prototype"); // Clear the no helper wrapper prototype object so that a new one // gets created if needed. mPrototypeNoHelper = nsnull; }
PRBool XPCDispObject::WrapIDispatch(IDispatch *pDispatch, XPCCallContext &ccx, JSObject *obj, jsval *rval) { if(!pDispatch) { return PR_FALSE; } // Wrap the desired COM object nsCOMPtr<nsIXPConnectJSObjectHolder> holder; nsresult rv = ccx.GetXPConnect()->WrapNative( ccx, obj, reinterpret_cast<nsISupports*>(pDispatch), NSID_IDISPATCH, getter_AddRefs(holder)); if(NS_FAILED(rv) || !holder) { return PR_FALSE; } JSObject * jsobj; if(NS_FAILED(holder->GetJSObject(&jsobj))) return PR_FALSE; *rval = OBJECT_TO_JSVAL(jsobj); return PR_TRUE; }
// 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; }
static bool DefinePropertyIfFound(XPCCallContext& ccx, HandleObject obj, HandleId idArg, XPCNativeSet* set, XPCNativeInterface* ifaceArg, XPCNativeMember* member, XPCWrappedNativeScope* scope, bool reflectToStringAndToSource, XPCWrappedNative* wrapperToReflectInterfaceNames, XPCWrappedNative* wrapperToReflectDoubleWrap, XPCNativeScriptableInfo* scriptableInfo, unsigned propFlags, bool* resolved) { RootedId id(ccx, idArg); RefPtr<XPCNativeInterface> iface = ifaceArg; XPCJSContext* xpccx = ccx.GetContext(); bool found; const char* name; propFlags |= JSPROP_RESOLVING; if (set) { if (iface) found = true; else found = set->FindMember(id, &member, &iface); } else found = (nullptr != (member = iface->FindMember(id))); if (!found) { if (reflectToStringAndToSource) { JSNative call; uint32_t flags = 0; if (scriptableInfo) { nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface( scriptableInfo->GetCallback()); if (classInfo) { nsresult rv = classInfo->GetFlags(&flags); if (NS_FAILED(rv)) return Throw(rv, ccx); } } bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT) || Preferences::GetBool("dom.XPCToStringForDOMClasses", false); if(id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING) && overwriteToString) { call = XPC_WN_Shared_ToString; name = xpccx->GetStringName(XPCJSContext::IDX_TO_STRING); } else if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE)) { call = XPC_WN_Shared_ToSource; name = xpccx->GetStringName(XPCJSContext::IDX_TO_SOURCE); } else if (id == SYMBOL_TO_JSID( JS::GetWellKnownSymbol(ccx, JS::SymbolCode::toPrimitive))) { call = XPC_WN_Shared_toPrimitive; name = "[Symbol.toPrimitive]"; } else { call = nullptr; } if (call) { RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name)); if (!fun) { JS_ReportOutOfMemory(ccx); return false; } AutoResolveName arn(ccx, id); if (resolved) *resolved = true; RootedObject value(ccx, JS_GetFunctionObject(fun)); return JS_DefinePropertyById(ccx, obj, id, value, propFlags & ~JSPROP_ENUMERATE); } } // This *might* be a tearoff name that is not yet part of our // set. Let's lookup the name and see if it is the name of an // interface. Then we'll see if the object actually *does* this // interface and add a tearoff as necessary. if (wrapperToReflectInterfaceNames) { JSAutoByteString name; RefPtr<XPCNativeInterface> iface2; XPCWrappedNativeTearOff* to; RootedObject jso(ccx); nsresult rv = NS_OK; if (JSID_IS_STRING(id) && name.encodeLatin1(ccx, JSID_TO_STRING(id)) && (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr()), iface2) && nullptr != (to = wrapperToReflectInterfaceNames-> FindTearOff(iface2, true, &rv)) && nullptr != (jso = to->GetJSObject())) { AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, jso, propFlags & ~JSPROP_ENUMERATE); } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) { return Throw(rv, ccx); } } // This *might* be a double wrapped JSObject if (wrapperToReflectDoubleWrap && id == xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT) && GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) { // We build and add a getter function. // A security check is done on a per-get basis. JSFunction* fun; id = xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT); name = xpccx->GetStringName(XPCJSContext::IDX_WRAPPED_JSOBJECT); fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter, 0, 0, name); if (!fun) return false; RootedObject funobj(ccx, JS_GetFunctionObject(fun)); if (!funobj) return false; propFlags |= JSPROP_GETTER | JSPROP_SHARED; propFlags &= ~JSPROP_ENUMERATE; AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags, JS_DATA_TO_FUNC_PTR(JSNative, funobj.get()), nullptr); } if (resolved) *resolved = false; return true; } if (!member) { if (wrapperToReflectInterfaceNames) { XPCWrappedNativeTearOff* to = wrapperToReflectInterfaceNames->FindTearOff(iface, true); if (!to) return false; RootedObject jso(ccx, to->GetJSObject()); if (!jso) return false; AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, jso, propFlags & ~JSPROP_ENUMERATE); } if (resolved) *resolved = false; return true; } if (member->IsConstant()) { RootedValue val(ccx); AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return member->GetConstantValue(ccx, iface, val.address()) && JS_DefinePropertyById(ccx, obj, id, val, propFlags); } if (scope->HasInterposition()) { Rooted<PropertyDescriptor> desc(ccx); if (!xpc::InterposeProperty(ccx, obj, iface->GetIID(), id, &desc)) return false; if (desc.object()) { AutoResolveName arn(ccx, id); if (resolved) *resolved = true; desc.attributesRef() |= JSPROP_RESOLVING; return JS_DefinePropertyById(ccx, obj, id, desc); } } if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING) || id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE) || (scriptableInfo && scriptableInfo->GetFlags().DontEnumQueryInterface() && id == xpccx->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE))) propFlags &= ~JSPROP_ENUMERATE; RootedValue funval(ccx); if (!member->NewFunctionObject(ccx, iface, obj, funval.address())) return false; if (member->IsMethod()) { AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, funval, propFlags); } // else... MOZ_ASSERT(member->IsAttribute(), "way broken!"); propFlags |= JSPROP_GETTER | JSPROP_SHARED; propFlags &= ~JSPROP_READONLY; JSObject* funobj = funval.toObjectOrNull(); JSNative getter = JS_DATA_TO_FUNC_PTR(JSNative, funobj); JSNative setter; if (member->IsWritableAttribute()) { propFlags |= JSPROP_SETTER; setter = JS_DATA_TO_FUNC_PTR(JSNative, funobj); } else { setter = nullptr; } AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags, getter, setter); }
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; }
// static JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) { nsVoidArray* dyingWrappedJSArray; XPCJSRuntime* self = nsXPConnect::GetRuntime(); if(self) { switch(status) { case JSGC_BEGIN: { if(!NS_IsMainThread()) { return JS_FALSE; } break; } case JSGC_MARK_END: { NS_ASSERTION(!self->mDoingFinalization, "bad state"); // mThreadRunningGC indicates that GC is running { // scoped lock XPCAutoLock lock(self->GetMapLock()); NS_ASSERTION(!self->mThreadRunningGC, "bad state"); self->mThreadRunningGC = PR_GetCurrentThread(); } dyingWrappedJSArray = &self->mWrappedJSToReleaseArray; { JSDyingJSObjectData data = {cx, dyingWrappedJSArray}; // Add any wrappers whose JSObjects are to be finalized to // this array. Note that this is a nsVoidArray because // we do not want to be changing the refcount of these wrappers. // We add them to the array now and Release the array members // later to avoid the posibility of doing any JS GCThing // allocations during the gc cycle. self->mWrappedJSMap-> Enumerate(WrappedJSDyingJSObjectFinder, &data); } // Do cleanup in NativeInterfaces. This part just finds // member cloned function objects that are about to be // collected. It does not deal with collection of interfaces or // sets at this point. CX_AND_XPCRT_Data data = {cx, self}; self->mIID2NativeInterfaceMap-> Enumerate(NativeInterfaceGC, &data); // Find dying scopes... XPCWrappedNativeScope::FinishedMarkPhaseOfGC(cx, self); self->mDoingFinalization = JS_TRUE; break; } case JSGC_FINALIZE_END: { NS_ASSERTION(self->mDoingFinalization, "bad state"); self->mDoingFinalization = JS_FALSE; // Release all the members whose JSObjects are now known // to be dead. dyingWrappedJSArray = &self->mWrappedJSToReleaseArray; while(1) { nsXPCWrappedJS* wrapper; PRInt32 count = dyingWrappedJSArray->Count(); if(!count) { dyingWrappedJSArray->Compact(); break; } wrapper = static_cast<nsXPCWrappedJS*> (dyingWrappedJSArray->ElementAt(count-1)); dyingWrappedJSArray->RemoveElementAt(count-1); NS_RELEASE(wrapper); } #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING printf("--------------------------------------------------------------\n"); int setsBefore = (int) self->mNativeSetMap->Count(); int ifacesBefore = (int) self->mIID2NativeInterfaceMap->Count(); #endif // We use this occasion to mark and sweep NativeInterfaces, // NativeSets, and the WrappedNativeJSClasses... // Do the marking... XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos(); self->mDetachedWrappedNativeProtoMap-> Enumerate(DetachedWrappedNativeProtoMarker, nsnull); // Mark the sets used in the call contexts. There is a small // chance that a wrapper's set will change *while* a call is // happening which uses that wrapper's old interfface set. So, // we need to do this marking to avoid collecting those sets // that might no longer be otherwise reachable from the wrappers // or the wrapperprotos. // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { // Mark those AutoMarkingPtr lists! thread->MarkAutoRootsAfterJSFinalize(); XPCCallContext* ccxp = thread->GetCallContext(); while(ccxp) { // Deal with the strictness of callcontext that // complains if you ask for a set when // it is in a state where the set could not // possibly be valid. if(ccxp->CanGetSet()) { XPCNativeSet* set = ccxp->GetSet(); if(set) set->Mark(); } if(ccxp->CanGetInterface()) { XPCNativeInterface* iface = ccxp->GetInterface(); if(iface) iface->Mark(); } ccxp = ccxp->GetPrevCallContext(); } } } } // Do the sweeping... // We don't want to sweep the JSClasses at shutdown time. // At this point there may be JSObjects using them that have // been removed from the other maps. if(!self->GetXPConnect()->IsShuttingDown()) { self->mNativeScriptableSharedMap-> Enumerate(JSClassSweeper, nsnull); } self->mClassInfo2NativeSetMap-> Enumerate(NativeUnMarkedSetRemover, nsnull); self->mNativeSetMap-> Enumerate(NativeSetSweeper, nsnull); CX_AND_XPCRT_Data data = {cx, self}; self->mIID2NativeInterfaceMap-> Enumerate(NativeInterfaceSweeper, &data); #ifdef DEBUG XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked(); #endif #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING int setsAfter = (int) self->mNativeSetMap->Count(); int ifacesAfter = (int) self->mIID2NativeInterfaceMap->Count(); printf("\n"); printf("XPCNativeSets: before: %d collected: %d remaining: %d\n", setsBefore, setsBefore - setsAfter, setsAfter); printf("XPCNativeInterfaces: before: %d collected: %d remaining: %d\n", ifacesBefore, ifacesBefore - ifacesAfter, ifacesAfter); printf("--------------------------------------------------------------\n"); #endif // Sweep scopes needing cleanup XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC(cx); // Now we are going to recycle any unused WrappedNativeTearoffs. // We do this by iterating all the live callcontexts (on all // threads!) and marking the tearoffs in use. And then we // iterate over all the WrappedNative wrappers and sweep their // tearoffs. // // This allows us to perhaps minimize the growth of the // tearoffs. And also makes us not hold references to interfaces // on our wrapped natives that we are not actually using. // // XXX We may decide to not do this on *every* gc cycle. // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // Do the marking... { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { XPCCallContext* ccxp = thread->GetCallContext(); while(ccxp) { // Deal with the strictness of callcontext that // complains if you ask for a tearoff when // it is in a state where the tearoff could not // possibly be valid. if(ccxp->CanGetTearOff()) { XPCWrappedNativeTearOff* to = ccxp->GetTearOff(); if(to) to->Mark(); } ccxp = ccxp->GetPrevCallContext(); } } } // Do the sweeping... XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs(); } } // Now we need to kill the 'Dying' XPCWrappedNativeProtos. // We transfered these native objects to this table when their // JSObject's were finalized. We did not destroy them immediately // at that point because the ordering of JS finalization is not // deterministic and we did not yet know if any wrappers that // might still be referencing the protos where still yet to be // finalized and destroyed. We *do* know that the protos' // JSObjects would not have been finalized if there were any // wrappers that referenced the proto but where not themselves // slated for finalization in this gc cycle. So... at this point // we know that any and all wrappers that might have been // referencing the protos in the dying list are themselves dead. // So, we can safely delete all the protos in the list. self->mDyingWrappedNativeProtoMap-> Enumerate(DyingProtoKiller, nsnull); // mThreadRunningGC indicates that GC is running. // Clear it and notify waiters. { // scoped lock XPCAutoLock lock(self->GetMapLock()); NS_ASSERTION(self->mThreadRunningGC == PR_GetCurrentThread(), "bad state"); self->mThreadRunningGC = nsnull; xpc_NotifyAll(self->GetMapLock()); } break; } case JSGC_END: { // NOTE that this event happens outside of the gc lock in // the js engine. So this could be simultaneous with the // events above. // Do any deferred released of native objects. nsVoidArray* array = &self->mNativesToReleaseArray; #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - Begin deferred Release of %d nsISupports pointers\n", array->Count()); #endif while(1) { nsISupports* obj; { PRInt32 count = array->Count(); if(!count) { array->Compact(); break; } obj = reinterpret_cast<nsISupports*> (array->ElementAt(count-1)); array->RemoveElementAt(count-1); } NS_RELEASE(obj); } #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - End deferred Releases\n"); #endif break; } default: break; } } // always chain to old GCCallback if non-null. return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; }
// static JSBool XPCDispObject::Dispatch(XPCCallContext& ccx, IDispatch * disp, DISPID dispID, CallMode mode, XPCDispParams * params, jsval* retval, XPCDispInterface::Member * member, XPCJSRuntime* rt) { _variant_t dispResult; jsval val; uintN err; uintN argc = params->GetParamCount(); // Figure out what we're doing (getter/setter/method) WORD dispFlags; if(mode == CALL_SETTER) { dispFlags = DISPATCH_PROPERTYPUT; } else if(mode == CALL_GETTER) { dispFlags = DISPATCH_PROPERTYGET; } else { dispFlags = DISPATCH_METHOD; } HRESULT invokeResult; EXCEPINFO exception; // Scope the lock { // avoid deadlock in case the native method blocks somehow AutoJSSuspendRequest req(ccx); // scoped suspend of request // call IDispatch's invoke invokeResult= disp->Invoke( dispID, // IDispatch ID IID_NULL, // Reserved must be IID_NULL LOCALE_SYSTEM_DEFAULT, // The locale context, use the system's dispFlags, // Type of Invoke call params->GetDispParams(), // Parameters &dispResult, // Where the result is stored &exception, // Exception information 0); // Index of an argument error } if(SUCCEEDED(invokeResult)) { *retval = JSVAL_VOID; if(mode == CALL_METHOD) { NS_ASSERTION(member, "member must not be null if this is a method"); for(PRUint32 index = 0; index < argc; ++index) { const XPCDispInterface::Member::ParamInfo & paramInfo = member->GetParamInfo(index); if(paramInfo.IsOut()) { if(!XPCDispConvert::COMToJS(ccx, params->GetParamRef(index), val, err)) return ThrowBadParam(err, index, ccx); if(paramInfo.IsRetVal()) { *retval = val; } else { jsval * argv = ccx.GetArgv(); // Out, in/out parameters must be objects if(!JSVAL_IS_OBJECT(argv[index]) || !OBJ_SET_PROPERTY(ccx, JSVAL_TO_OBJECT(argv[index]), rt->GetStringID(XPCJSRuntime::IDX_VALUE), &val)) return ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, index, ccx); } } } } if(dispResult.vt != VT_EMPTY) { if(!XPCDispConvert::COMToJS(ccx, dispResult, val, err)) { ThrowBadParam(err, 0, ccx); } *retval = val; } } // Set the result and throw the error if one occured ccx.GetXPCContext()->SetLastResult(invokeResult); if(NS_FAILED(invokeResult)) { XPCThrower::ThrowCOMError(ccx, invokeResult, NS_ERROR_XPC_COM_ERROR, invokeResult == DISP_E_EXCEPTION ? &exception : nsnull); return JS_FALSE; } return JS_TRUE; }