nsISupports* castNativeFromWrapper(JSContext *cx, JSObject *obj, uint32_t interfaceBit, uint32_t protoID, int32_t protoDepth, nsISupports **pRef, jsval *pVal, nsresult *rv) { XPCWrappedNative *wrapper; XPCWrappedNativeTearOff *tearoff; JSObject *cur; if (IS_WRAPPER_CLASS(js::GetObjectClass(obj))) { cur = obj; wrapper = IS_WN_WRAPPER_OBJECT(cur) ? (XPCWrappedNative*)xpc_GetJSPrivate(obj) : nullptr; tearoff = nullptr; } else { *rv = getWrapper(cx, obj, &wrapper, &cur, &tearoff); if (NS_FAILED(*rv)) return nullptr; } nsISupports *native; if (wrapper) { native = wrapper->GetIdentityObject(); cur = wrapper->GetFlatJSObject(); if (!native || !HasBitInInterfacesBitmap(cur, interfaceBit)) { native = nullptr; } } else if (cur && IS_SLIM_WRAPPER(cur)) { native = static_cast<nsISupports*>(xpc_GetJSPrivate(cur)); if (!native || !HasBitInInterfacesBitmap(cur, interfaceBit)) { native = nullptr; } } else if (cur && protoDepth >= 0) { const mozilla::dom::DOMClass* domClass = mozilla::dom::GetDOMClass(cur); native = mozilla::dom::UnwrapDOMObject<nsISupports>(cur); if (native && (uint32_t)domClass->mInterfaceChain[protoDepth] != protoID) { native = nullptr; } } else { native = nullptr; } if (native) { *pRef = nullptr; *pVal = OBJECT_TO_JSVAL(cur); *rv = NS_OK; } else { *rv = NS_ERROR_XPC_BAD_CONVERT_JS; } return native; }
nsISupports* castNativeFromWrapper(JSContext *cx, JSObject *obj, uint32_t interfaceBit, uint32_t protoID, int32_t protoDepth, nsISupports **pRef, MutableHandleValue pVal, nsresult *rv) { XPCWrappedNative *wrapper; XPCWrappedNativeTearOff *tearoff; JSObject *cur; if (IS_WN_REFLECTOR(obj)) { cur = obj; wrapper = XPCWrappedNative::Get(obj); tearoff = nullptr; } else { *rv = getWrapper(cx, obj, &wrapper, &cur, &tearoff); if (NS_FAILED(*rv)) return nullptr; } nsISupports *native; if (wrapper) { native = wrapper->GetIdentityObject(); cur = wrapper->GetFlatJSObject(); if (!native || !HasBitInInterfacesBitmap(cur, interfaceBit)) { native = nullptr; } } else if (cur && protoDepth >= 0) { const mozilla::dom::DOMClass* domClass = mozilla::dom::GetDOMClass(cur); native = mozilla::dom::UnwrapDOMObject<nsISupports>(cur); if (native && (uint32_t)domClass->mInterfaceChain[protoDepth] != protoID) { native = nullptr; } } else { native = nullptr; } if (native) { *pRef = nullptr; pVal.setObjectOrNull(cur); *rv = NS_OK; } else { *rv = NS_ERROR_XPC_BAD_CONVERT_JS; } return native; }
// note: returned pointer is only valid while |obj| remains alive! const nsID* xpc_JSObjectToID(JSContext *cx, JSObject* obj) { if (!cx || !obj) return nsnull; // NOTE: this call does NOT addref XPCWrappedNative* wrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); if (wrapper && (wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) || wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) || wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)))) { return ((nsIJSID*)wrapper->GetIdentityObject())->GetID(); } return nsnull; }
// note: returned pointer is only valid while |obj| remains alive! const nsID* xpc_JSObjectToID(JSContext* cx, JSObject* obj) { if (!cx || !obj) return nullptr; // NOTE: this call does NOT addref XPCWrappedNative* wrapper = nullptr; obj = js::CheckedUnwrap(obj); if (obj && IS_WN_REFLECTOR(obj)) wrapper = XPCWrappedNative::Get(obj); if (wrapper && (wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) || wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) || wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)))) { return ((nsIJSID*)wrapper->GetIdentityObject())->GetID(); } return nullptr; }
static JSBool CommonConstructor(JSContext *cx, int name, JSObject *obj, uintN argc, jsval *argv, jsval *rval, PRBool enforceSecurity) { XPCCallContext ccx(JS_CALLER, cx, JS_GetGlobalObject(cx)); // Check if IDispatch is enabled, fail if not if(!nsXPConnect::IsIDispatchEnabled()) { XPCThrower::Throw(NS_ERROR_XPC_IDISPATCH_NOT_ENABLED, ccx); return JS_FALSE; } XPCJSRuntime *rt = ccx.GetRuntime(); if(!rt) { XPCThrower::Throw(NS_ERROR_UNEXPECTED, ccx); return JS_FALSE; } nsIXPCSecurityManager* sm = ccx.GetXPCContext() ->GetAppropriateSecurityManager(nsIXPCSecurityManager::HOOK_CALL_METHOD); XPCWrappedNative * wrapper = ccx.GetWrapper(); if(sm && NS_FAILED(sm->CanAccess(nsIXPCSecurityManager::ACCESS_CALL_METHOD, &ccx, ccx, ccx.GetFlattenedJSObject(), wrapper->GetIdentityObject(), wrapper->GetClassInfo(), rt->GetStringJSVal(name), wrapper->GetSecurityInfoAddr()))) { // Security manager will have set an exception return JS_FALSE; } // Make sure we were called with one string parameter if(argc != 1 || (argc == 1 && !JSVAL_IS_STRING(argv[0]))) { XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx); return JS_FALSE; } JSString* str = JSVAL_TO_STRING(argv[0]); PRUint32 len = JS_GetStringLength(str); // Cap constructor argument length to keep from crashing in string // code. if(len > XPC_IDISPATCH_CTOR_MAX_ARG_LEN) { XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx); return JS_FALSE; } jschar * className = JS_GetStringChars(str); CComBSTR bstrClassName(len, reinterpret_cast<const WCHAR *>(className)); if(!bstrClassName) { XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx); return JS_FALSE; } // Instantiate the desired COM object CComPtr<IDispatch> pDispatch; HRESULT rv = XPCDispObject::COMCreateInstance(ccx, bstrClassName, enforceSecurity, &pDispatch); if(FAILED(rv)) { XPCThrower::ThrowCOMError(ccx, rv, NS_ERROR_XPC_COM_CREATE_FAILED); return JS_FALSE; } // Get a wrapper for our object nsCOMPtr<nsIXPConnectJSObjectHolder> holder; nsresult nsrv = ccx.GetXPConnect()->WrapNative( ccx, ccx.GetOperandJSObject(), reinterpret_cast<nsISupports*>(pDispatch.p), NSID_IDISPATCH, getter_AddRefs(holder)); if(NS_FAILED(nsrv)) { XPCThrower::Throw(nsrv, ccx); return JS_FALSE; } // get and return the JS object wrapper JSObject * jsobj; nsrv = holder->GetJSObject(&jsobj); if(NS_FAILED(nsrv)) { XPCThrower::Throw(nsrv, ccx); return JS_FALSE; } *rval = OBJECT_TO_JSVAL(jsobj); 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 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; }