From 64676a5373d04883fc3948e5885d4d5041cbfbf3 Mon Sep 17 00:00:00 2001 From: "dbradley%netscape.com" Date: Tue, 12 Nov 2002 08:22:35 +0000 Subject: [PATCH] Bug 173146 - Add support to XPConnect for IDispatch interface. Second round of changes. r=adamlock, sr=jst. Most changes are not part of build. --- js/src/xpconnect/idl/Makefile.in | 6 + js/src/xpconnect/idl/XPCIDispatch.idl | 3 +- js/src/xpconnect/idl/nsIDispatchSupport.idl | 78 ++++ js/src/xpconnect/src/Makefile.in | 14 +- js/src/xpconnect/src/XPCDispConvert.cpp | 53 ++- js/src/xpconnect/src/XPCDispInlines.h | 112 ++++-- js/src/xpconnect/src/XPCDispInterface.cpp | 47 ++- js/src/xpconnect/src/XPCDispObject.cpp | 369 +++++++++++------- .../xpconnect/src/XPCDispParamPropJSClass.cpp | 196 ++++++++++ js/src/xpconnect/src/XPCDispParams.cpp | 115 ++++++ js/src/xpconnect/src/XPCDispPrivate.h | 198 +++++++--- js/src/xpconnect/src/XPCDispTearOff.cpp | 8 +- js/src/xpconnect/src/XPCDispTypeInfo.cpp | 3 +- .../xpconnect/src/XPCIDispatchExtension.cpp | 75 +++- js/src/xpconnect/src/nsDispatchSupport.cpp | 87 +++++ js/src/xpconnect/src/xpccallcontext.cpp | 7 + js/src/xpconnect/src/xpcforwards.h | 1 + js/src/xpconnect/src/xpcinlines.h | 29 +- js/src/xpconnect/src/xpcmodule.cpp | 11 + js/src/xpconnect/src/xpcprivate.h | 10 +- js/src/xpconnect/src/xpcwrappednative.cpp | 6 +- .../xpconnect/src/xpcwrappednativejsops.cpp | 8 +- 22 files changed, 1149 insertions(+), 287 deletions(-) create mode 100644 js/src/xpconnect/idl/nsIDispatchSupport.idl create mode 100644 js/src/xpconnect/src/XPCDispParamPropJSClass.cpp create mode 100644 js/src/xpconnect/src/XPCDispParams.cpp create mode 100644 js/src/xpconnect/src/nsDispatchSupport.cpp diff --git a/js/src/xpconnect/idl/Makefile.in b/js/src/xpconnect/idl/Makefile.in index ae2514e23348..896d270e021f 100644 --- a/js/src/xpconnect/idl/Makefile.in +++ b/js/src/xpconnect/idl/Makefile.in @@ -59,4 +59,10 @@ XPIDLSRCS = \ XPCIDispatch.idl \ $(NULL) +ifdef XPC_IDISPATCH_SUPPORT +XPIDLSRCS += \ + nsIDispatchSupport.idl \ + $(NULL) +endif + include $(topsrcdir)/config/rules.mk diff --git a/js/src/xpconnect/idl/XPCIDispatch.idl b/js/src/xpconnect/idl/XPCIDispatch.idl index 05245f4a3e2c..35b89e9a1fc5 100644 --- a/js/src/xpconnect/idl/XPCIDispatch.idl +++ b/js/src/xpconnect/idl/XPCIDispatch.idl @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version diff --git a/js/src/xpconnect/idl/nsIDispatchSupport.idl b/js/src/xpconnect/idl/nsIDispatchSupport.idl new file mode 100644 index 000000000000..1db70b270e7d --- /dev/null +++ b/js/src/xpconnect/idl/nsIDispatchSupport.idl @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the IDispatch implementation for XPConnect + * + * The Initial Developer of the Original Code is + * David Bradley. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" +#include "nsIVariant.idl" + +%{ C++ +// {40c4883d-079f-43db-82a9-df0a59d37998} +#define NS_IDISPATCH_SUPPORT_CID \ + { 0x40c4883d, 0x079f, 0x43db, \ + { 0x82, 0xa9, 0xdf, 0x0a, 0x59, 0xd3, 0x79, 0x98 } } + +#define NS_IDISPATCH_SUPPORT_CONTRACTID "@mozilla.org/nsdispatchsupport;1" +%} + +native COMVARIANT(VARIANT); +[ptr] native COMVARIANTPtr(VARIANT); +native JSVal(jsval); + +interface IDispatch; + +[uuid(38df70e9-12f8-4732-af91-df36c38dc6f6)] +interface nsIDispatchSupport : nsISupports +{ + /** + * Converts a COM Variant to a jsval + * @param comvar The COM Variant to be converted + * @param val The jsval to receive the converted value + */ + void COMVariant2JSVal(in COMVARIANTPtr comvar, out JSVal val); + /** + * Converts a jsval to a COM Variant + * @param var The jsval to be converted + * @param comvar The COM Variant to receive the converted value + */ + void JSVal2COMVariant(in JSVal var, out COMVARIANT comvar); + /** + * Instantiate a COM component + * This checks to see if the component is scriptable. If it is, it instantiates it + * @param className contract ID or class ID of the desired component to instantiate + * @return The IDispatch interface pointer if instantiated + */ + IDispatch CreateInstance(in string className, in PRBool testScriptability); +}; diff --git a/js/src/xpconnect/src/Makefile.in b/js/src/xpconnect/src/Makefile.in index 77a3297c3b72..39052549e504 100644 --- a/js/src/xpconnect/src/Makefile.in +++ b/js/src/xpconnect/src/Makefile.in @@ -82,16 +82,20 @@ CPPSRCS = \ xpcwrappednativeinfo.cpp \ xpcwrappednativejsops.cpp \ xpcwrappednativeproto.cpp \ - xpcwrappednativescope.cpp + xpcwrappednativescope.cpp \ + $(NULL) ifdef XPC_IDISPATCH_SUPPORT CPPSRCS += XPCDispObject.cpp \ XPCDispInterface.cpp \ XPCDispConvert.cpp \ XPCDispTypeInfo.cpp \ XPCDispTearOff.cpp \ - XPCIDispatchExtension.cpp + XPCIDispatchExtension.cpp \ + XPCDispParams.cpp \ + XPCDispParamPropJSClass.cpp \ + nsDispatchSupport.cpp \ + $(NULL) endif -CPPSRCS += $(NULL) include $(topsrcdir)/config/config.mk @@ -119,6 +123,10 @@ endif ifdef XPC_IDISPATCH_SUPPORT DEFINES += -DXPC_IDISPATCH_SUPPORT +ifdef XPC_COMOBJECT +DEFINES += -DXPC_COMOBJECT +endif + endif ifeq ($(OS_ARCH),WINNT) diff --git a/js/src/xpconnect/src/XPCDispConvert.cpp b/js/src/xpconnect/src/XPCDispConvert.cpp index 34207416a93b..5c96649f253c 100644 --- a/js/src/xpconnect/src/XPCDispConvert.cpp +++ b/js/src/xpconnect/src/XPCDispConvert.cpp @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -76,7 +77,7 @@ VARTYPE XPCDispConvert::JSTypeToCOMType(XPCCallContext& ccx, jsval val) } JSBool XPCDispConvert::JSArrayToCOMArray(XPCCallContext& ccx, JSObject *obj, - VARIANT & var, uintN& err) + VARIANT & var, nsresult& err) { err = NS_OK; jsuint len; @@ -89,7 +90,7 @@ JSBool XPCDispConvert::JSArrayToCOMArray(XPCCallContext& ccx, JSObject *obj, SAFEARRAY * array = SafeArrayCreateVector(VT_VARIANT, 0, len); for(long index = 0; index < len; ++index) { - VARIANT arrayVar; + _variant_t arrayVar; jsval val; if(JS_GetElement(ccx, obj, index, &val) && JSToCOM(ccx, val, arrayVar, err)) @@ -113,7 +114,7 @@ JSBool XPCDispConvert::JSArrayToCOMArray(XPCCallContext& ccx, JSObject *obj, JSBool XPCDispConvert::JSToCOM(XPCCallContext& ccx, jsval src, VARIANT & dest, - uintN& err) + nsresult& err) { err = NS_OK; if(JSVAL_IS_STRING(src)) @@ -191,8 +192,9 @@ JSBool XPCDispConvert::JSToCOM(XPCCallContext& ccx, return JS_TRUE; } -JSBool XPCDispConvert::COMArrayToJSArray(XPCCallContext& ccx, const VARIANT & src, - jsval & dest,uintN& err) +JSBool XPCDispConvert::COMArrayToJSArray(XPCCallContext& ccx, + const _variant_t & src, + jsval & dest, nsresult& err) { err = NS_OK; // We only support one dimensional arrays for now @@ -214,7 +216,7 @@ JSBool XPCDispConvert::COMArrayToJSArray(XPCCallContext& ccx, const VARIANT & sr err = NS_ERROR_OUT_OF_MEMORY; } // Devine the type of our array - VARIANT var; + _variant_t var; if((src.vt & VT_ARRAY) != 0) { var.vt = src.vt & ~VT_ARRAY; @@ -240,12 +242,34 @@ JSBool XPCDispConvert::COMArrayToJSArray(XPCCallContext& ccx, const VARIANT & sr return JS_TRUE; } -JSBool XPCDispConvert::COMToJS(XPCCallContext& ccx, const VARIANT & src, - jsval & dest,uintN& err) +inline +jsval NumberToJSVal(JSContext* cx, int value) +{ + jsval val; + if (INT_FITS_IN_JSVAL(value)) + val = INT_TO_JSVAL(value); + else + { + if (!JS_NewDoubleValue(cx, NS_STATIC_CAST(jsdouble,value), &val)) + val = JSVAL_ZERO; + } + return val; +} + +inline +jsval NumberToJSVal(JSContext* cx, double value) +{ + jsval val; + if (JS_NewDoubleValue(cx, NS_STATIC_CAST(jsdouble, value), &val)) + return val; + else + return JSVAL_ZERO; +} + +JSBool XPCDispConvert::COMToJS(XPCCallContext& ccx, const _variant_t & src, + jsval& dest, nsresult& err) { err = NS_OK; - int x = VT_BSTR; - int y = VT_BYREF; if(src.vt & VT_ARRAY || src.vt == VT_SAFEARRAY) { return COMArrayToJSArray(ccx, src, dest, err); @@ -269,7 +293,7 @@ JSBool XPCDispConvert::COMToJS(XPCCallContext& ccx, const VARIANT & src, break; case VT_I4: { - dest = INT_TO_JSVAL(src.lVal); + dest = NumberToJSVal(ccx, src.lVal); } break; case VT_UI1: @@ -284,12 +308,12 @@ JSBool XPCDispConvert::COMToJS(XPCCallContext& ccx, const VARIANT & src, break; case VT_R4: { - dest = DOUBLE_TO_JSVAL(NS_STATIC_CAST(float,src.fltVal)); + dest = NumberToJSVal(ccx, src.fltVal); } break; case VT_R8: { - dest = DOUBLE_TO_JSVAL(src.dblVal); + dest = NumberToJSVal(ccx, src.dblVal); } break; case VT_BOOL: @@ -311,7 +335,6 @@ JSBool XPCDispConvert::COMToJS(XPCCallContext& ccx, const VARIANT & src, case VT_CY: case VT_DATE: case VT_UNKNOWN: - case VT_ARRAY: case VT_I1: case VT_UI2: case VT_UI4: diff --git a/js/src/xpconnect/src/XPCDispInlines.h b/js/src/xpconnect/src/XPCDispInlines.h index 4708b0661a49..aff5685be808 100644 --- a/js/src/xpconnect/src/XPCDispInlines.h +++ b/js/src/xpconnect/src/XPCDispInlines.h @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -46,13 +47,6 @@ PRBool nsXPConnect::IsIDispatchEnabled() return XPCIDispatchExtension::IsEnabled(); } -// XPCDispObject inlines -inline -void XPCDispObject::CleanupVariant(VARIANT & var) -{ - VariantClear(&var); -} - // XPCDispInterface inlines inline @@ -63,11 +57,10 @@ XPCDispInterface::Member::ParamInfo::ParamInfo( inline JSBool XPCDispInterface::Member::ParamInfo::InitializeOutputParam( - char * varBuffer, VARIANT & var) const + void * varBuffer, VARIANT & var) const { var.vt = GetType() | VT_BYREF; - // TODO: This is a bit hacky, but we just pick one of the pointer types; - var.byref = NS_REINTERPRET_CAST(BSTR*,varBuffer); + var.byref = varBuffer; return JS_TRUE; } @@ -81,7 +74,7 @@ PRBool XPCDispInterface::Member::ParamInfo::IsFlagSet( inline PRBool XPCDispInterface::Member::ParamInfo::IsIn() const { - return IsFlagSet(PARAMFLAG_FIN); + return IsFlagSet(PARAMFLAG_FIN) || mParamInfo->paramdesc.wParamFlags == 0; } inline @@ -165,19 +158,19 @@ PRBool XPCDispInterface::Member::IsFlagSet(unsigned short flag) const inline PRBool XPCDispInterface::Member::IsSetter() const { - return IsFlagSet(GET_PROPERTY); + return IsFlagSet(SET_PROPERTY); } inline PRBool XPCDispInterface::Member::IsGetter() const { - return IsFlagSet(SET_PROPERTY); + return IsFlagSet(GET_PROPERTY); } inline PRBool XPCDispInterface::Member::IsProperty() const { - return IsFlagSet(SET_PROPERTY) || IsFlagSet(GET_PROPERTY); + return IsSetter() || IsGetter(); } inline @@ -186,6 +179,12 @@ PRBool XPCDispInterface::Member::IsFunction() const return IsFlagSet(FUNCTION); } +inline +PRBool XPCDispInterface::Member::IsParameterizedProperty() const +{ + return (IsSetter() && GetParamCount() > 1) || (IsGetter() && GetParamCount() > 0); +} + inline jsval XPCDispInterface::Member::GetName() const { @@ -277,7 +276,19 @@ PRUint32 XPCDispInterface::GetMemberCount() const inline void XPCDispInterface::operator delete(void * p) { - free(p); + PR_Free(p); +} + +inline +XPCDispInterface::~XPCDispInterface() +{ + // Cleanup our members, the first gets cleaned up by the destructor + // We have to cleanup the rest manually. These members are allocated + // as part of the XPCIDispInterface object at the end + for (PRUint32 index = 1; index < GetMemberCount(); ++index) + { + mMembers[index].~Member(); + } } inline @@ -292,7 +303,7 @@ XPCDispInterface::XPCDispInterface(JSContext* cx, inline void * XPCDispInterface::operator new (size_t, PRUint32 members) { - return malloc(sizeof(XPCDispInterface) + sizeof(Member) * (members - 1)); + return PR_Malloc(sizeof(XPCDispInterface) + sizeof(Member) * (members - 1)); } // XPCDispNameArray inlines @@ -359,9 +370,9 @@ inline jsval XPCDispIDArray::Item(JSContext* cx, PRUint32 index) const { jsval val; - if (!JS_IdToValue(cx, - NS_REINTERPRET_CAST(jsid, - mIDArray.ElementAt(index)), &val)) + if(!JS_IdToValue(cx, + NS_REINTERPRET_CAST(jsid, + mIDArray.ElementAt(index)), &val)) return JSVAL_NULL; return val; } @@ -410,17 +421,66 @@ nsCString XPCDispTypeInfo::GetNameForDispID(DISPID dispID) } inline -const nsIID & XPCDispGUID2nsIID(const struct _GUID & guid, nsIID & iid) +const nsIID & XPCDispGUID2nsIID(const struct _GUID & guid) { NS_ASSERTION(sizeof(struct _GUID) == sizeof(nsIID), "GUID is not the same as nsIID"); - iid = NS_REINTERPRET_CAST(const nsIID &,guid); - return iid; + return NS_REINTERPRET_CAST(const nsIID &,guid); } inline -const GUID & XPCDispIID2GUID(const nsIID & iid, struct _GUID & guid) +const GUID & XPCDispIID2GUID(const nsIID & iid) { NS_ASSERTION(sizeof(struct _GUID) == sizeof(nsIID), "GUID is not the same as IID"); - guid = NS_REINTERPRET_CAST(const struct _GUID &, iid); - return guid; + return NS_REINTERPRET_CAST(const struct _GUID &, iid); } + +inline +const nsCID & XPCDispGUID2nsCID(const struct _GUID & guid) +{ + NS_ASSERTION(sizeof(struct _GUID) == sizeof(nsCID), "GUID is not the same as nsCID"); + return NS_REINTERPRET_CAST(const nsCID &,guid); +} + +inline +const GUID & XPCDispCID2GUID(const nsCID & iid) +{ + NS_ASSERTION(sizeof(struct _GUID) == sizeof(nsCID), "GUID is not the same as IID"); + return NS_REINTERPRET_CAST(const struct _GUID &, iid); +} + +inline +void XPCDispParams::SetNamedPropID() +{ + mDispParams.rgdispidNamedArgs = &mPropID; + mDispParams.cNamedArgs = 1; +} + +inline +VARIANT & XPCDispParams::GetParamRef(PRUint32 index) +{ + NS_ASSERTION(index < mDispParams.cArgs, "XPCDispParams::GetParam bounds error"); + return mDispParams.rgvarg[mDispParams.cArgs - index - 1]; +} + +inline +_variant_t XPCDispParams::GetParam(PRUint32 index) const +{ + return NS_CONST_CAST(XPCDispParams*,this)->GetParamRef(index); +} + +inline +void * XPCDispParams::GetOutputBuffer(PRUint32 index) +{ + NS_ASSERTION(index < mDispParams.cArgs, "XPCDispParams::GetParam bounds error"); + return mVarBuffer + VARIANT_UNION_SIZE * index; +} + +inline +JSBool XPCDispParamPropJSClass::Invoke(XPCCallContext& ccx, + XPCDispObject::CallMode mode, + jsval* retval) +{ + return XPCDispObject::Dispatch(ccx, mDispObj, mDispID, mode, mDispParams, + retval); +} + diff --git a/js/src/xpconnect/src/XPCDispInterface.cpp b/js/src/xpconnect/src/XPCDispInterface.cpp index 79ca28f0a271..eb11704e7832 100644 --- a/js/src/xpconnect/src/XPCDispInterface.cpp +++ b/js/src/xpconnect/src/XPCDispInterface.cpp @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -40,6 +41,7 @@ */ #include "xpcprivate.h" +#include /** * Is this function reflectable @@ -198,6 +200,46 @@ void XPCDispInterface::InspectIDispatch(JSContext * cx, ITypeInfo * pTypeInfo, P } } +inline +PRUnichar* JSString2PRUnichar(XPCCallContext& ccx, jsval val, size_t* length) +{ + JSString* str = JS_ValueToString(ccx, val); + if(!str) + return nsnull; + *length = JS_GetStringLength(str); + PRUnichar * string = NS_REINTERPRET_CAST(PRUnichar*,JS_GetStringChars(str)); + return string; +} + +inline +PRBool CaseInsensitiveCompare(XPCCallContext& ccx, const PRUnichar* lhs, size_t lhsLength, jsval rhs) +{ + size_t rhsLength; + PRUnichar* rhsString = JSString2PRUnichar(ccx, rhs, &rhsLength); + return rhsString && + lhsLength == rhsLength && + _wcsnicmp(lhs, rhsString, lhsLength * sizeof(PRUnichar)) == 0; +} + +const XPCDispInterface::Member* XPCDispInterface::FindMemberCI(XPCCallContext& ccx, jsval name) const +{ + size_t nameLength; + PRUnichar* sName = JSString2PRUnichar(ccx, name, &nameLength); + if(!sName) + return nsnull; + // Iterate backwards to save time + const Member* member = mMembers + mMemberCount; + while(member > mMembers) + { + --member; + if(CaseInsensitiveCompare(ccx, sName, nameLength, member->GetName())) + { + return member; + } + } + return nsnull; +} + JSBool XPCDispInterface::Member::GetValue(XPCCallContext& ccx, XPCNativeInterface * iface, jsval * retval) const @@ -216,7 +258,8 @@ JSBool XPCDispInterface::Member::GetValue(XPCCallContext& ccx, intN argc; intN flags; JSNative callback; - if(IsFunction()) + // Is this a function or a parameterized getter/setter + if(IsFunction() || IsParameterizedProperty()) { argc = GetParamCount(); flags = 0; diff --git a/js/src/xpconnect/src/XPCDispObject.cpp b/js/src/xpconnect/src/XPCDispObject.cpp index 837c4ce33a6c..bb5f05ef0ad4 100644 --- a/js/src/xpconnect/src/XPCDispObject.cpp +++ b/js/src/xpconnect/src/XPCDispObject.cpp @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -67,11 +68,51 @@ XPCDispObject::COMCreateFromIDispatch(IDispatch *pDispatch, JSContext *cx, JSObj return PR_TRUE; } -IDispatch * XPCDispObject::COMCreateInstance(const char * className) +static boolean HasSafeScriptingCategory(const GUID & classID) +{ + CComPtr catInfo; + HRESULT hr = catInfo.CoCreateInstance(CLSID_StdComponentCategoriesMgr); + if (catInfo == NULL) + { + // Must fail if we can't open the category manager + return false; + } + + // See what categories the class implements + CComPtr enumCATID; + if (FAILED(catInfo->EnumImplCategoriesOfClass(classID, &enumCATID))) + { + // Can't enumerate classes in category so fail + return false; + } + + // Search for matching categories + BOOL bFound = FALSE; + CATID catidNext = GUID_NULL; + while (enumCATID->Next(1, &catidNext, NULL) == S_OK) + { + if (::IsEqualCATID(CATID_SafeForScripting, catidNext)) + { + bFound = TRUE; + } + } + if (!bFound) + { + return false; + } + + return true; +} + +inline +boolean ScriptOK(DWORD value) +{ + return value & (INTERFACESAFE_FOR_UNTRUSTED_CALLER | + INTERFACESAFE_FOR_UNTRUSTED_DATA); +} + +nsresult XPCDispObject::COMCreateInstance(const char * className, PRBool testScriptability, IDispatch ** result) { - // TODO: This needs to have some error handling. We could probably - // capture some information from the GetLastError - // allows us to convert to BSTR for CLSID functions below _bstr_t bstrName(className); CLSID classID; HRESULT hr; @@ -84,27 +125,129 @@ IDispatch * XPCDispObject::COMCreateInstance(const char * className) { hr = CLSIDFromProgID(bstrName, &classID); } - if(SUCCEEDED(hr)) + if(FAILED(hr)) + return hr; + PRBool scriptableOK = testScriptability; + if (testScriptability) { - IDispatch * pDisp; - hr = CoCreateInstance( - classID, - 0, - CLSCTX_INPROC_SERVER, - IID_IDispatch, - (LPVOID*)&pDisp); - if(SUCCEEDED(hr)) - { - pDisp->AddRef(); - return pDisp; - } + const DWORD scriptOk = INTERFACESAFE_FOR_UNTRUSTED_CALLER | + INTERFACESAFE_FOR_UNTRUSTED_DATA; + scriptableOK = HasSafeScriptingCategory(classID); } - return nsnull; + + // Didn't have the safe for scripting category so lets look at IObjectSafety + CComPtr disp; + if (scriptableOK || !testScriptability) + { + HRESULT hResult = disp.CoCreateInstance(classID); + if (FAILED(hResult)) + return hResult; + } + // If we're testing scriptability and it didn't have a scripting category + // we'll check via the IObjectSafety interface + if (testScriptability && !scriptableOK) + { + CComQIPtr objSafety(disp); + // Didn't have IObjectSafety so we'll bail + if (objSafety == 0) + return E_FAIL; + DWORD supported; + DWORD state; + hr = objSafety->GetInterfaceSafetyOptions(IID_IDispatch, &supported, &state); + if (FAILED(hr)) + return hr; + if (!ScriptOK(supported) || !ScriptOK(state)) + return E_FAIL; + } + disp.CopyTo(result); + return S_OK; +} + +// static +JSBool XPCDispObject::Dispatch(XPCCallContext& ccx, IDispatch * disp, + DISPID dispID, CallMode mode, + XPCDispParams & params, + jsval* retval, + XPCDispInterface::Member * member, + XPCJSRuntime* rt) +{ + // avoid deadlock in case the native method blocks somehow + AutoJSSuspendRequest req(ccx); // scoped suspend of request + + _variant_t dispResult; + jsval val; + uintN err; + uintN argc = params.GetParamCount(); + + WORD dispFlags; + if(mode == CALL_SETTER) + { + dispFlags = DISPATCH_PROPERTYPUT; + } + else if(mode == CALL_GETTER) + { + dispFlags = DISPATCH_PROPERTYGET; + } + else + { + dispFlags = DISPATCH_METHOD; + } + HRESULT 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 + nsnull, // Exception information + 0); // Index of an argument error + if(SUCCEEDED(invokeResult)) + { + 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 + { + // we actually assured this before doing the invoke + NS_ASSERTION(JSVAL_IS_OBJECT(val), "out var is not an object"); + if(!OBJ_SET_PROPERTY(ccx, JSVAL_TO_OBJECT(val), + 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; + } + } + ccx.GetXPCContext()->SetLastResult(invokeResult); + + if(NS_FAILED(invokeResult)) + { + XPCThrower::ThrowCOMError(ccx, invokeResult); + return JS_FALSE; + } + return JS_TRUE; } -// This is the size of the largest member in the union in VARIANT -const PRUint32 VARIANT_UNION_SIZE = sizeof(VARIANT) - sizeof(VARTYPE) - sizeof(unsigned short) * 3; -#define VARIANT_BUFFER_SIZE(count) (VARIANT_UNION_SIZE * count) JSBool XPCDispObject::Invoke(XPCCallContext & ccx, CallMode mode) { @@ -119,7 +262,6 @@ JSBool XPCDispObject::Invoke(XPCCallContext & ccx, CallMode mode) return Throw(rv, ccx); } - JSBool retval = JS_FALSE; // TODO: Remove type cast and change GetIDispatchMember to use the correct type XPCDispInterface::Member* member = NS_REINTERPRET_CAST(XPCDispInterface::Member*,ccx.GetIDispatchMember()); XPCJSRuntime* rt = ccx.GetRuntime(); @@ -134,8 +276,6 @@ JSBool XPCDispObject::Invoke(XPCCallContext & ccx, CallMode mode) // set up the method index and do the security check if needed - PRBool setter; - PRBool getter; PRUint32 secFlag; PRUint32 secAction; @@ -144,19 +284,14 @@ JSBool XPCDispObject::Invoke(XPCCallContext & ccx, CallMode mode) case CALL_METHOD: secFlag = nsIXPCSecurityManager::HOOK_CALL_METHOD; secAction = nsIXPCSecurityManager::ACCESS_CALL_METHOD; - getter = setter = PR_FALSE; break; case CALL_GETTER: secFlag = nsIXPCSecurityManager::HOOK_GET_PROPERTY; secAction = nsIXPCSecurityManager::ACCESS_GET_PROPERTY; - setter = PR_FALSE; - getter = PR_TRUE; break; case CALL_SETTER: secFlag = nsIXPCSecurityManager::HOOK_SET_PROPERTY; secAction = nsIXPCSecurityManager::ACCESS_SET_PROPERTY; - setter = PR_TRUE; - getter = PR_FALSE; break; default: NS_ASSERTION(0,"bad value"); @@ -181,155 +316,81 @@ JSBool XPCDispObject::Invoke(XPCCallContext & ccx, CallMode mode) uintN err; // TODO: I'm not sure why we need to do this I would have expected COM // to report one parameter - if(setter) + if(mode == CALL_SETTER) args = 1; if(argc < args) args = argc; - const PRUint32 DEFAULT_ARG_ARRAY_SIZE = 8; - VARIANT stackArgs[DEFAULT_ARG_ARRAY_SIZE]; - char varStackBuffer[VARIANT_BUFFER_SIZE(DEFAULT_ARG_ARRAY_SIZE)]; - char * varBuffer = args <= DEFAULT_ARG_ARRAY_SIZE ? varStackBuffer : new char[VARIANT_BUFFER_SIZE(args)]; - memset(varBuffer, 0, VARIANT_BUFFER_SIZE(args)); - VARIANT dispResult; - DISPPARAMS dispParams; - dispParams.cArgs = args; - dispParams.rgvarg = args <= DEFAULT_ARG_ARRAY_SIZE ? stackArgs : new VARIANT[args]; - DISPID propID; + XPCDispParams params(args); + jsval val; // If this is a setter, we just need to convert the first parameter - if(setter) + if(mode == CALL_SETTER) { - propID = DISPID_PROPERTYPUT; - dispParams.rgdispidNamedArgs = &propID; - dispParams.cNamedArgs = 1; - if(!XPCDispConvert::JSToCOM(ccx, argv[0],dispParams.rgvarg[0], err)) + params.SetNamedPropID(); + if(!XPCDispConvert::JSToCOM(ccx, argv[0],params.GetParamRef(0), err)) return ThrowBadParam(err, 0, ccx); } - else if(getter) + else if(mode != CALL_GETTER) { - dispParams.rgdispidNamedArgs = 0; - dispParams.cNamedArgs = 0; - } - else - { - dispParams.cNamedArgs = 0; - dispParams.rgdispidNamedArgs = 0; - VARIANT* pVar = dispParams.rgvarg + args - 1; - for(PRUint32 index = 0; index < args; ++index, --pVar) + for(PRUint32 index = 0; index < args; ++index) { const XPCDispInterface::Member::ParamInfo & paramInfo = member->GetParamInfo(index); if(paramInfo.IsIn()) { - jsval val = argv[index]; + val = argv[index]; if(paramInfo.IsOut()) { - if(JSVAL_IS_PRIMITIVE(argv[index]) || - !OBJ_GET_PROPERTY(ccx, JSVAL_TO_OBJECT(argv[index]), + if(JSVAL_IS_PRIMITIVE(val) || + !OBJ_GET_PROPERTY(ccx, JSVAL_TO_OBJECT(val), rt->GetStringID(XPCJSRuntime::IDX_VALUE), &val)) { ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, index, ccx); } } - if(!XPCDispConvert::JSToCOM(ccx, val,*pVar, err)) + if(!XPCDispConvert::JSToCOM(ccx, val,params.GetParamRef(index), err)) { ThrowBadParam(err, index, ccx); } } else { - paramInfo.InitializeOutputParam(varBuffer + VARIANT_UNION_SIZE * index, *pVar); + paramInfo.InitializeOutputParam(params.GetOutputBuffer(index), params.GetParamRef(index)); } } } - HRESULT invokeResult = E_FAIL; + // If this is a parameterized property + if(member->IsParameterizedProperty()) { - // avoid deadlock in case the native method blocks somehow - AutoJSSuspendRequest req(ccx); // scoped suspend of request - IDispatch * pDisp; - // TODO: I'm not sure this QI is really needed - nsresult result = pObj->QueryInterface(NSID_IDISPATCH, (void**)&pDisp); - if(NS_SUCCEEDED(result)) + // We need to get a parameterized property object to return to JS + if(XPCDispParamPropJSClass::GetNewOrUsed(ccx, wrapper, + member->GetDispID(), + params, &val)) { - WORD dispFlags; - if(setter) - { - dispFlags = DISPATCH_PROPERTYPUT; - } - else if(getter) - { - dispFlags = DISPATCH_PROPERTYGET; - } - else - { - dispFlags = DISPATCH_METHOD; - } - invokeResult= pDisp->Invoke(member->GetDispID(), IID_NULL, LOCALE_SYSTEM_DEFAULT, dispFlags, &dispParams, &dispResult, 0, 0); - if(SUCCEEDED(invokeResult)) - { - if(!setter && !getter) - { - VARIANT* pVar = dispParams.rgvarg + args - 1; - for(PRUint32 index = 0; index < args; ++index, --pVar) - { - const XPCDispInterface::Member::ParamInfo & paramInfo = member->GetParamInfo(index); - if(paramInfo.IsOut()) - { - if(paramInfo.IsRetVal()) - { - if(!ccx.GetReturnValueWasSet()) - ccx.SetRetVal(argv[index]); - } - else - { - jsval val; - if(!XPCDispConvert::COMToJS(ccx, *pVar, val, err)) - ThrowBadParam(err, index, ccx); - // we actually assured this before doing the invoke - NS_ASSERTION(JSVAL_IS_OBJECT(argv[index]), "out var is not object"); - if(!OBJ_SET_PROPERTY(ccx, JSVAL_TO_OBJECT(argv[index]), - rt->GetStringID(XPCJSRuntime::IDX_VALUE), &val)) - ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, index, ccx); - } - CleanupVariant(*pVar); - } - } - } - if(!ccx.GetReturnValueWasSet()) - { - if(dispResult.vt != VT_EMPTY) - { - jsval val; - if(!XPCDispConvert::COMToJS(ccx, dispResult, val, err)) - { - ThrowBadParam(err, 0, ccx); - } - ccx.SetRetVal(val); - } - else if(setter) - { - ccx.SetRetVal(argv[0]); - } - } - retval = JS_TRUE; - } + ccx.SetRetVal(val); + if(!JS_IdToValue(ccx, 1, &val)) + return JS_FALSE; + JS_SetCallReturnValue2(ccx, val); + return JS_TRUE; } + return JS_FALSE; } - // TODO: I think this needs to be moved up, to only occur if invoke is actually called. - xpcc->SetLastResult(invokeResult); - - if(NS_FAILED(invokeResult)) + IDispatch * pDisp; + // TODO: I'm not sure this QI is really needed + nsresult result = pObj->QueryInterface(NSID_IDISPATCH, (void**)&pDisp); + if(NS_SUCCEEDED(result)) { - XPCThrower::ThrowCOMError(ccx, invokeResult); + JSBool retval = Dispatch(ccx, pDisp, member->GetDispID(), mode, params, &val, member, rt); + if(retval && mode == CALL_SETTER) + { + ccx.SetRetVal(argv[0]); + } + else + { + ccx.SetRetVal(val); + } + return retval; } - - for(PRUint32 index = 0; index < args; ++index) - CleanupVariant(dispParams.rgvarg[index]); - // Cleanup if we allocated the variant array - if(dispParams.rgvarg != stackArgs) - delete [] dispParams.rgvarg; - if(varBuffer != varStackBuffer) - delete [] varBuffer; - return retval; + return JS_FALSE; } /** @@ -381,10 +442,16 @@ JSBool GetMember(XPCCallContext& ccx, JSObject* funobj, XPCNativeInterface*& ifa * Callback for functions * This callback is called by JS when a function on a JSObject proxying * for an IDispatch instance + * @param cx A pointer to a JS context + * @param obj JS object that the parameterized property is on + * @param argc Number of arguments in this call + * @param argv The parameters passed in if any + * @param vp The return value + * @return Returns JS_TRUE if the operation succeeded */ JSBool JS_DLL_CALLBACK -XPC_IDispatch_CallMethod(JSContext* cx, JSObject* obj, - uintN argc, jsval* argv, jsval* vp) +XPC_IDispatch_CallMethod(JSContext* cx, JSObject* obj, uintN argc, + jsval* argv, jsval* vp) { NS_ASSERTION(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION, "bad function"); JSObject* funobj = JSVAL_TO_OBJECT(argv[-2]); @@ -408,10 +475,16 @@ XPC_IDispatch_CallMethod(JSContext* cx, JSObject* obj, * Callback for properties * This callback is called by JS when a property is set or retrieved on a * JSObject proxying for an IDispatch instance + * @param cx A pointer to a JS context + * @param obj JS object that the parameterized property is on + * @param argc Number of arguments in this call + * @param argv The parameters passed in if any + * @param vp The return value + * @return Returns JS_TRUE if the operation succeeded */ JSBool JS_DLL_CALLBACK -XPC_IDispatch_GetterSetter(JSContext *cx, JSObject *obj, - uintN argc, jsval *argv, jsval *vp) +XPC_IDispatch_GetterSetter(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *vp) { NS_ASSERTION(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION, "bad function"); JSObject* funobj = JSVAL_TO_OBJECT(argv[-2]); diff --git a/js/src/xpconnect/src/XPCDispParamPropJSClass.cpp b/js/src/xpconnect/src/XPCDispParamPropJSClass.cpp new file mode 100644 index 000000000000..450812add9c5 --- /dev/null +++ b/js/src/xpconnect/src/XPCDispParamPropJSClass.cpp @@ -0,0 +1,196 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the IDispatch implementation for XPConnect + * + * The Initial Developer of the Original Code is + * David Bradley. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** \file XPCDispParamPropJSClass.cpp + * Implementation for the XPCDispParamPropJSClass class + * This file contains the implementation of the XPCDispParamPropJSClass class + */ + +#include "xpcprivate.h" + +inline +XPCDispParamPropJSClass* GetParamProp(JSContext* cx, JSObject* obj) +{ + return NS_REINTERPRET_CAST(XPCDispParamPropJSClass*, + JS_GetPrivate(cx, obj)); +} + +/** + * Handles getting a property via a parameterized property. + * This object is used as part of the parameterized property mechanism. + * property get requests are forward to our owner and on to IDispatch's + * Invoke + * @param cx A pointer to a JS context + * @param obj The object to perform the get on + * @param id ID of the parameter to get + * @param vp Pointer to the return value + * @return JSBool JS_TRUE if property was retrieved + */ +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_PP_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + XPCDispParamPropJSClass* paramProp = GetParamProp(cx, obj); + JSObject* originalObj = paramProp->GetWrapper()->GetFlatJSObject(); + XPCCallContext ccx(JS_CALLER, cx, originalObj, nsnull, id, + paramProp->GetParams().GetParamCount(), nsnull, vp); + return paramProp->Invoke(ccx, XPCDispObject::CALL_GETTER, vp); +} + +/** + * Handles getting a property via a parameterized property. + * This object is used as part of the parameterized property mechanism. + * property get requests are forward to our owner and on to IDispatch's + * Invoke + * @param cx A pointer to a JS context + * @param obj The object to perform the get on + * @param id ID of the parameter to get + * @param vp Pointer to the return value + * @return JSBool JS_TRUE if property was retrieved + */ +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_PP_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + XPCDispParamPropJSClass* paramProp = GetParamProp(cx, obj); + JSObject* originalObj = paramProp->GetWrapper()->GetFlatJSObject(); + XPCCallContext ccx(JS_CALLER, cx, originalObj, nsnull, id, + paramProp->GetParams().GetParamCount(), nsnull, vp); + _variant_t var; + uintN err; + if(!XPCDispConvert::JSToCOM(ccx, *vp, var, err)) + return JS_FALSE; + XPCDispParams& params = paramProp->GetParams(); + params.SetNamedPropID(); + // params will own var + params.InsertParam(var); + return paramProp->Invoke(ccx, XPCDispObject::CALL_SETTER, vp); +} + +/** + * Handles getting a property via a parameterized property. + * This object is used as part of the parameterized property mechanism. + * property get requests are forward to our owner and on to IDispatch's + * Invoke + * @param cx A pointer to a JS context + * @param obj The object to perform the get on + * @param id ID of the parameter to get + * @param vp Pointer to the return value + * @return JSBool JS_TRUE if property was retrieved + */ +JS_STATIC_DLL_CALLBACK(void) +XPC_PP_Finalize(JSContext *cx, JSObject *obj) +{ + delete GetParamProp(cx, obj); +} + +JS_STATIC_DLL_CALLBACK(uint32) +XPC_PP_Mark(JSContext *cx, JSObject *obj, void *arg) +{ + XPCDispParamPropJSClass* paramProp = GetParamProp(cx, obj); + if(paramProp) + { + XPCWrappedNative* wrapper = paramProp->GetWrapper(); + if(wrapper && wrapper->IsValid()) + xpc_MarkForValidWrapper(cx, wrapper, arg); + } + return 0; +} + +static JSClass ParamPropClass = { + "XPCDispParamPropJSCass", // Name + JSCLASS_HAS_PRIVATE, // flags + + /* Mandatory non-null function pointer members. */ + JS_PropertyStub, // addProperty + JS_PropertyStub, // delProperty + XPC_PP_GetProperty, // getProperty + XPC_PP_SetProperty, // setProperty + JS_EnumerateStub, // enumerate + JS_ResolveStub, // resolve + JS_ConvertStub, // convert + XPC_PP_Finalize, // finalize + + /* Optionally non-null members start here. */ + nsnull, // getObjectOps; + nsnull, // checkAccess; + nsnull, // call; + nsnull, // construct; + nsnull, // xdrObject; + nsnull, // hasInstance; + XPC_PP_Mark, // mark; + nsnull // spare; +}; + +// static +JSBool XPCDispParamPropJSClass::GetNewOrUsed(XPCCallContext& ccx, + XPCWrappedNative* wrapper, + PRUint32 dispID, + XPCDispParams& dispParams, + jsval* paramPropObj) +{ + XPCDispParamPropJSClass* pDispParam = + new XPCDispParamPropJSClass(wrapper, ccx.GetTearOff()->GetNative(), + dispID, dispParams); + if(!pDispParam) + return JS_FALSE; + JSObject * obj = JS_NewObject(ccx, &ParamPropClass, nsnull, nsnull); + if(!obj) + return JS_FALSE; + if(!JS_SetPrivate(ccx, obj, pDispParam)) + return JS_FALSE; + *paramPropObj = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +XPCDispParamPropJSClass::XPCDispParamPropJSClass(XPCWrappedNative* wrapper, + nsISupports * dispObj, + PRUint32 dispID, + XPCDispParams& dispParams) : + mWrapper(wrapper), + mDispID(dispID), + mDispParams(dispParams) +{ + nsresult result = dispObj->QueryInterface(NSID_IDISPATCH, + NS_REINTERPRET_CAST(void**, + &mDispObj)); + if(NS_FAILED(result)) + mDispObj = nsnull; +} + +XPCDispParamPropJSClass::~XPCDispParamPropJSClass() +{ + mDispObj->Release(); +} diff --git a/js/src/xpconnect/src/XPCDispParams.cpp b/js/src/xpconnect/src/XPCDispParams.cpp new file mode 100644 index 000000000000..c579790cf619 --- /dev/null +++ b/js/src/xpconnect/src/XPCDispParams.cpp @@ -0,0 +1,115 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the IDispatch implementation for XPConnect + * + * The Initial Developer of the Original Code is + * David Bradley. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "xpcprivate.h" + +XPCDispParams::XPCDispParams(PRUint32 args) : + mVarBuffer(args <= DEFAULT_ARG_ARRAY_SIZE ? + mVarStackBuffer : new char[XPC_VARIANT_BUFFER_SIZE(args)]), + mPropID(DISPID_PROPERTYPUT) +{ + memset(mVarBuffer, 0, XPC_VARIANT_BUFFER_SIZE(args)); + // Initialize the IDispatch parameters + mDispParams.cArgs = args; + mDispParams.rgvarg = args <= DEFAULT_ARG_ARRAY_SIZE ? mStackArgs : new VARIANT[args]; + mDispParams.rgdispidNamedArgs = nsnull; + mDispParams.cNamedArgs = 0; +} + +// This transfers the variants, it does not copy +XPCDispParams::XPCDispParams(XPCDispParams & other) : + mPropID(DISPID_PROPERTYPUT) +{ + mDispParams.rgdispidNamedArgs = nsnull; + mDispParams.cNamedArgs = 0; + mDispParams.cArgs = other.mDispParams.cArgs; + if(other.mVarBuffer == other.mVarStackBuffer) + { + mVarBuffer = mVarStackBuffer; + memcpy(mVarBuffer, other.mVarBuffer, + XPC_VARIANT_BUFFER_SIZE(other.mDispParams.cArgs)); + } + else + { + mVarBuffer = other.mVarBuffer; + other.mVarBuffer = nsnull; + } + if(other.mDispParams.rgvarg == other.mStackArgs) + { + mDispParams.rgvarg = mStackArgs; + memcpy(mDispParams.rgvarg, other.mStackArgs, + sizeof(VARIANT) * other.mDispParams.cArgs); + other.mDispParams.cArgs; + } + else + { + mDispParams.rgvarg = other.mDispParams.rgvarg; + other.mDispParams.rgvarg = 0; + } + other.mDispParams.cArgs = 0; +} + + +XPCDispParams::~XPCDispParams() +{ + for(PRUint32 index = 0; index < mDispParams.cArgs; ++index) + VariantClear(mDispParams.rgvarg + index); + // Cleanup if we allocated the variant array + if(mDispParams.rgvarg != mStackArgs) + delete [] mDispParams.rgvarg; + if(mVarBuffer != mVarStackBuffer) + delete [] mVarBuffer; +} + +void XPCDispParams::InsertParam(_variant_t & var) +{ + VARIANT* oldArgs = mDispParams.rgvarg; + char * oldVarBuffer = mVarBuffer; + if(mDispParams.cNamedArgs >= DEFAULT_ARG_ARRAY_SIZE) + { + if(mDispParams.rgvarg == mStackArgs) + mDispParams.rgvarg = new VARIANT[mDispParams.cArgs + 1]; + if(mVarBuffer == mVarStackBuffer) + mVarBuffer = new char [XPC_VARIANT_BUFFER_SIZE(mDispParams.cArgs + 1)]; + } + // Move the data up, using position save copy + memmove(mDispParams.rgvarg + 1, oldArgs, sizeof(VARIANT) * mDispParams.cArgs); + memmove(mVarBuffer + VARIANT_UNION_SIZE,oldVarBuffer, XPC_VARIANT_BUFFER_SIZE(mDispParams.cArgs)); + mDispParams.rgvarg[0] = var.Detach(); + memset(mVarBuffer, 0, VARIANT_UNION_SIZE); + ++mDispParams.cArgs; +} diff --git a/js/src/xpconnect/src/XPCDispPrivate.h b/js/src/xpconnect/src/XPCDispPrivate.h index 7589fe7e865a..e0cfaa2f4672 100644 --- a/js/src/xpconnect/src/XPCDispPrivate.h +++ b/js/src/xpconnect/src/XPCDispPrivate.h @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -41,6 +42,8 @@ #error "DispPrivate.h should not be included directly, please use XPCPrivate.h" #endif +#include "nsIDispatchSupport.h" + #define NS_DECL_IUNKNOWN \ public: \ STDMETHOD(QueryInterface)(const struct _GUID& aIID, \ @@ -109,10 +112,10 @@ public: * @param src JS Value to convert * @param dest COM variant to receive the converted value * @param err receives the error code if any of a failed conversion - * @return Returns true if the conversion succeeded + * @return True if the conversion succeeded */ static - JSBool JSToCOM(XPCCallContext& ccx, jsval src, VARIANT & dest, uintN & err); + JSBool JSToCOM(XPCCallContext& ccx, jsval src, VARIANT & dest, nsresult& err); /** * Converts a COM variant to a jsval @@ -123,65 +126,31 @@ public: * @return Returns true if the conversion succeeded */ static - JSBool COMToJS(XPCCallContext& ccx, const VARIANT & src, jsval & dest, - uintN & err); + JSBool COMToJS(XPCCallContext& ccx, const _variant_t & src, jsval & dest, + nsresult& err); private: /** * Converts a JS Array to a safe array + * @param ccx XPConnect call context + * @param ccx */ static JSBool JSArrayToCOMArray(XPCCallContext& ccx, JSObject *obj, VARIANT & var, - uintN & err); + nsresult& err); /** * Converts a COM Array to a JS Array */ static - JSBool COMArrayToJSArray(XPCCallContext& ccx, const VARIANT & src, - jsval & dest,uintN& err); + JSBool COMArrayToJSArray(XPCCallContext& ccx, const _variant_t & src, + jsval & dest, nsresult& err); }; JSBool JS_DLL_CALLBACK -XPC_IDispatch_CallMethod(JSContext *cx, JSObject *obj, - uintN argc, jsval *argv, jsval *vp); +XPC_IDispatch_CallMethod(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *vp); JSBool JS_DLL_CALLBACK -XPC_IDispatch_GetterSetter(JSContext *cx, JSObject *obj, - uintN argc, jsval *argv, jsval *vp); - -/** - * Used to invoke IDispatch methods - */ -class XPCDispObject -{ -public: - enum CallMode {CALL_METHOD, CALL_GETTER, CALL_SETTER}; - /** - * Used to invoke an IDispatch method - */ - static - JSBool Invoke(XPCCallContext & ccx, CallMode mode); - /** - * Instantiates a COM object given a class ID or a prog ID - */ - static - IDispatch * COMCreateInstance(const char * className); - /** - * Create a COM object from an existing IDispatch interface (e.g. returned by another object) - */ - static - PRBool COMCreateFromIDispatch(IDispatch *pDispatch, JSContext *cx, JSObject *obj, jsval *rval); - - /** - * Throws an error, converting the errNum to an exception - */ - static - JSBool Throw(uintN errNum, JSContext* cx); - /** - * Cleans up a variant if it was allocated - */ - inline - static - void CleanupVariant(VARIANT & var); -}; +XPC_IDispatch_GetterSetter(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *vp); /** * This class holds an array of names. It indexes based on a *one based* dispid @@ -510,7 +479,7 @@ public: { public: ParamInfo(const ELEMDESC * paramInfo); - JSBool InitializeOutputParam(char * varBuffer, + JSBool InitializeOutputParam(void * varBuffer, VARIANT & var) const; /** * Tests if a specific flag is set @@ -531,7 +500,7 @@ public: /** * Placement new is needed to initialize array in class XPCDispInterface */ - void* operator new(size_t, Member* p); + void* operator new(size_t, Member* p) CPP_THROW_NEW; PRBool IsSetter() const; /** * Returns true if this is a getter @@ -541,6 +510,10 @@ public: * Returns true if this is a setter */ PRBool IsProperty() const; + /** + * Returns true if this is a parameterized property + */ + PRBool IsParameterizedProperty() const; /** * Returns true if this is a function */ @@ -619,17 +592,25 @@ public: JSObject* GetJSObject() const; void SetJSObject(JSObject* jsobj); const Member * FindMember(jsval name) const; + /** + * Looksup a member ignoring case + * @param ccx A call context + * @param name The name of the member + * @return A pointer to a member or nsnull if not found + */ + const Member* FindMemberCI(XPCCallContext& ccx, jsval name) const; const Member & GetMember(PRUint32 index); PRUint32 GetMemberCount() const; static XPCDispInterface* NewInstance(JSContext* cx, nsISupports * pIface); void operator delete(void * p); + ~XPCDispInterface(); private: XPCDispInterface(JSContext* cx, ITypeInfo * pTypeInfo, PRUint32 members); - void * operator new (size_t, PRUint32 members); + void * operator new (size_t, PRUint32 members) CPP_THROW_NEW; JSObject* mJSObject; PRUint32 mMemberCount; @@ -639,6 +620,42 @@ private: PRUint32 members); }; +/** + * Used to invoke IDispatch methods + */ +class XPCDispObject +{ +public: + enum CallMode {CALL_METHOD, CALL_GETTER, CALL_SETTER}; + static + JSBool Dispatch(XPCCallContext& ccx, IDispatch * pDisp, + DISPID dispID, CallMode mode, XPCDispParams & params, + jsval* retval, XPCDispInterface::Member* member = nsnull, + XPCJSRuntime* rt = nsnull); + /** + * Used to invoke an IDispatch method + */ + static + JSBool Invoke(XPCCallContext & ccx, CallMode mode); + /** + * Instantiates a COM object given a class ID or a prog ID + */ + static + nsresult COMCreateInstance(const char * className, + PRBool testScriptability, IDispatch ** result); + /** + * Create a COM object from an existing IDispatch interface (e.g. returned by another object) + */ + static + PRBool COMCreateFromIDispatch(IDispatch *pDispatch, JSContext *cx, + JSObject *obj, jsval *rval); + /** + * Throws an error, converting the errNum to an exception + */ + static + JSBool Throw(uintN errNum, JSContext* cx); +}; + class XPCIDispatchExtension { public: @@ -660,6 +677,85 @@ private: static PRBool mIsEnabled; }; +class XPCDispParams +{ +public: + XPCDispParams(PRUint32 args); + /** + * Makes a copy + * Makes a copy and transfers ownership of buffers + */ + XPCDispParams(XPCDispParams & other); + ~XPCDispParams(); + void SetNamedPropID(); + VARIANT & GetParamRef(PRUint32 index); + _variant_t GetParam(PRUint32 index) const; + void * GetOutputBuffer(PRUint32 index); + DISPPARAMS* GetDispParams() { return &mDispParams; } + uintN GetParamCount() const { return mDispParams.cArgs; } + void InsertParam(_variant_t & var); +private: + XPCDispParams& operator =(const XPCDispParams&) { + NS_ERROR("XPCDispParams can't be assigned"); } + + + enum + { + DEFAULT_ARG_ARRAY_SIZE = 8, +// This is the size of the largest member in the union in VARIANT + VARIANT_UNION_SIZE = sizeof(VARIANT) - sizeof(VARTYPE) - sizeof(unsigned short) * 3 + }; +#define XPC_VARIANT_BUFFER_SIZE(count) (VARIANT_UNION_SIZE * count) + + DISPPARAMS mDispParams; + char* mVarBuffer; + char mVarStackBuffer[XPC_VARIANT_BUFFER_SIZE(DEFAULT_ARG_ARRAY_SIZE)]; + VARIANT mStackArgs[DEFAULT_ARG_ARRAY_SIZE]; + DISPID mPropID; +}; + +/** + * Parameterized property object JSClass + * This class is used to support parameterized properties for IDispatch + */ +class XPCDispParamPropJSClass +{ +public: + static JSBool GetNewOrUsed(XPCCallContext& ccx, XPCWrappedNative* wrapper, + PRUint32 dispID, + XPCDispParams& dispParams, + jsval* paramPropObj); + ~XPCDispParamPropJSClass(); + XPCWrappedNative* GetWrapper() const { return mWrapper; } + JSBool Invoke(XPCCallContext& ccx, + XPCDispObject::CallMode mode, + jsval* retval); + XPCDispParams& GetParams() { return mDispParams; } +private: + XPCDispParamPropJSClass(XPCWrappedNative* wrapper, nsISupports* dispObj, + PRUint32 dispID, XPCDispParams& dispParams); + + XPCWrappedNative* mWrapper; + PRUint32 mDispID; + XPCDispParams mDispParams; + IDispatch* mDispObj; +}; + +class nsDispatchSupport : public nsIDispatchSupport +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDISPATCHSUPPORT + + nsDispatchSupport(); + virtual ~nsDispatchSupport(); + static nsDispatchSupport* GetSingleton(); + static void FreeSingleton() { NS_IF_RELEASE(mInstance); } + +private: + static nsDispatchSupport* mInstance; +}; + #include "XPCDispInlines.h" #endif diff --git a/js/src/xpconnect/src/XPCDispTearOff.cpp b/js/src/xpconnect/src/XPCDispTearOff.cpp index f983b32d7c41..7c7f3ca28c3e 100644 --- a/js/src/xpconnect/src/XPCDispTearOff.cpp +++ b/js/src/xpconnect/src/XPCDispTearOff.cpp @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -155,8 +156,7 @@ STDMETHODIMP XPCDispatchTearOff::QueryInterface(const struct _GUID & guid, return NS_OK; } - nsIID iid; - return mWrappedJS->QueryInterface(XPCDispGUID2nsIID(guid, iid), pPtr); + return mWrappedJS->QueryInterface(XPCDispGUID2nsIID(guid), pPtr); } STDMETHODIMP XPCDispatchTearOff::GetTypeInfoCount(unsigned int FAR * pctinfo) @@ -600,7 +600,7 @@ pre_call_clean_up: { if((pDispParams->rgvarg[index].vt & VT_BYREF) != 0) { - XPCDispObject::CleanupVariant(pDispParams->rgvarg[i]); + VariantClear(pDispParams->rgvarg + i); } } } diff --git a/js/src/xpconnect/src/XPCDispTypeInfo.cpp b/js/src/xpconnect/src/XPCDispTypeInfo.cpp index 8f3e8bb69783..e4279d6f25c6 100644 --- a/js/src/xpconnect/src/XPCDispTypeInfo.cpp +++ b/js/src/xpconnect/src/XPCDispTypeInfo.cpp @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version diff --git a/js/src/xpconnect/src/XPCIDispatchExtension.cpp b/js/src/xpconnect/src/XPCIDispatchExtension.cpp index d6b1d054caf5..b3e3db422606 100644 --- a/js/src/xpconnect/src/XPCIDispatchExtension.cpp +++ b/js/src/xpconnect/src/XPCIDispatchExtension.cpp @@ -40,9 +40,9 @@ static const char* const IDISPATCH_NAME = "IDispatch"; PRBool XPCIDispatchExtension::mIsEnabled = PR_TRUE; -JS_STATIC_DLL_CALLBACK(JSBool) -COMObjectConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) +static JSBool +CommonConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval, PRBool testScriptability) { // Make sure we were called with one string parameter if(argc != 1 || (argc == 1 && !JSVAL_IS_STRING(argv[0]))) @@ -65,12 +65,18 @@ COMObjectConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, } // Instantiate the desired COM object - IDispatch* pDispatch = XPCDispObject::COMCreateInstance(bytes); + IDispatch* pDispatch = nsnull;; + nsresult rv = XPCDispObject::COMCreateInstance(bytes, testScriptability, &pDispatch); + if (NS_FAILED(rv)) + { + // TODO: error reporting + return JS_FALSE; + } nsCOMPtr holder; - nsresult rv = nsXPConnect::GetXPConnect()->WrapNative(cx, obj, + rv = nsXPConnect::GetXPConnect()->WrapNative(cx, obj, NS_REINTERPRET_CAST(nsISupports*, pDispatch), NSID_IDISPATCH, getter_AddRefs(holder)); - if(FAILED(rv) || !holder) + if(FAILED(rv)) { // TODO: error reporting return JS_FALSE; @@ -82,33 +88,64 @@ COMObjectConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_TRUE; } +JS_STATIC_DLL_CALLBACK(JSBool) +COMObjectConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return CommonConstructor(cx, obj, argc, argv, rval, PR_FALSE); +} + +JS_STATIC_DLL_CALLBACK(JSBool) +ActiveXConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return CommonConstructor(cx, obj, argc, argv, rval, PR_TRUE); +} + JSBool XPCIDispatchExtension::Initialize(JSContext * aJSContext, JSObject * aGlobalJSObj) { // TODO: Cleanup error code - return JS_DefineFunction(aJSContext, aGlobalJSObj, "COMObject", - COMObjectConstructor, 1, 0) ? PR_TRUE : PR_FALSE; + JSBool result = JS_DefineFunction(aJSContext, aGlobalJSObj, "ActiveXObject", + ActiveXConstructor, 1, 0) != nsnull; +#ifdef XPC_COMOBJECT + if (result) + result = JS_DefineFunction(aJSContext, aGlobalJSObj, "COMObject", + COMObjectConstructor, 1, 0) != nsnull; +#endif + return result; } -JSBool XPCIDispatchExtension::DefineProperty(XPCCallContext & ccx, JSObject *obj, jsval idval, - XPCWrappedNative* wrapperToReflectInterfaceNames, - uintN propFlags, JSBool* resolved) +JSBool XPCIDispatchExtension::DefineProperty(XPCCallContext & ccx, + JSObject *obj, jsval idval, + XPCWrappedNative* wrapperToReflectInterfaceNames, + uintN propFlags, JSBool* resolved) { XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(ccx, "IDispatch"); - if (iface == nsnull) + if(iface == nsnull) return JS_FALSE; XPCWrappedNativeTearOff* to = wrapperToReflectInterfaceNames->FindTearOff(ccx, iface, JS_TRUE); - if (to == nsnull) + if(to == nsnull) return JS_FALSE; JSObject* jso = to->GetJSObject(); - if (jso == nsnull) + if(jso == nsnull) return JS_FALSE; - const XPCDispInterface::Member * member = to->GetIDispatchInfo()->FindMember(idval); - if (member == nsnull) + if(!JSVAL_IS_STRING(idval)) return JS_FALSE; + const XPCDispInterface::Member * member = to->GetIDispatchInfo()->FindMember(idval); + if(!member) + { + // IDispatch is case insensitive, so if we don't find a case sensitive + // match, we'll try a more expensive case-insensisitive search + // TODO: We need to create cleaner solution that doesn't create + // multiple properties of different case on the JS Object + member = to->GetIDispatchInfo()->FindMemberCI(ccx, idval); + if(!member) + return JS_FALSE; + } jsval funval; if(!member->GetValue(ccx, iface, &funval)) return JS_FALSE; @@ -116,7 +153,7 @@ JSBool XPCIDispatchExtension::DefineProperty(XPCCallContext & ccx, JSObject *obj if(!funobj) return JS_FALSE; jsid id; - if (member->IsFunction()) + if(member->IsFunction() || member->IsParameterizedProperty()) { AutoResolveName arn(ccx, idval); if(resolved) @@ -125,9 +162,9 @@ JSBool XPCIDispatchExtension::DefineProperty(XPCCallContext & ccx, JSObject *obj OBJ_DEFINE_PROPERTY(ccx, obj, id, OBJECT_TO_JSVAL(funobj), nsnull, nsnull, propFlags, nsnull); } - NS_ASSERTION(!member || member->IsSetter(), "way broken!"); + NS_ASSERTION(member->IsProperty(), "way broken!"); propFlags |= JSPROP_GETTER | JSPROP_SHARED; - if (member->IsSetter()) + if(member->IsSetter()) { propFlags |= JSPROP_SETTER; propFlags &= ~JSPROP_READONLY; diff --git a/js/src/xpconnect/src/nsDispatchSupport.cpp b/js/src/xpconnect/src/nsDispatchSupport.cpp new file mode 100644 index 000000000000..4a7fec634589 --- /dev/null +++ b/js/src/xpconnect/src/nsDispatchSupport.cpp @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the IDispatch implementation for XPConnect + * + * The Initial Developer of the Original Code is + * David Bradley. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "XPCPrivate.h" + +nsDispatchSupport* nsDispatchSupport::mInstance = nsnull; + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsDispatchSupport, nsIDispatchSupport) + +nsDispatchSupport::nsDispatchSupport() +{ + NS_INIT_ISUPPORTS(); + /* member initializers and constructor code */ +} + +nsDispatchSupport::~nsDispatchSupport() +{ + /* destructor code */ +} + +/* void COMVariant2JSVal (in COMVARIANTPtr comvar, out JSVal val); */ +NS_IMETHODIMP nsDispatchSupport::COMVariant2JSVal(VARIANT * comvar, jsval *val) +{ + XPCCallContext ccx(NATIVE_CALLER); + nsresult retval; + XPCDispConvert::COMToJS(ccx, *comvar, *val, retval); + return retval; +} + +/* void COMArray2JSArray (in COMVARIANTPtr comvar, out JSObjectPtr obj); */ +NS_IMETHODIMP nsDispatchSupport::JSVal2COMVariant(jsval val, VARIANT * comvar) +{ + XPCCallContext ccx(NATIVE_CALLER); + nsresult retval; + XPCDispConvert::JSToCOM(ccx, val, *comvar, retval); + return retval; +} + +NS_IMETHODIMP nsDispatchSupport::CreateInstance(const char * className, PRBool testScriptability, IDispatch ** result) +{ + return XPCDispObject::COMCreateInstance(className, testScriptability, result); +} + +nsDispatchSupport* nsDispatchSupport::GetSingleton() +{ + if(!mInstance) + { + mInstance = new nsDispatchSupport; + NS_IF_ADDREF(mInstance); + } + NS_IF_ADDREF(mInstance); + return mInstance; +} diff --git a/js/src/xpconnect/src/xpccallcontext.cpp b/js/src/xpconnect/src/xpccallcontext.cpp index 17d7ac756365..916b2f0c5f0f 100644 --- a/js/src/xpconnect/src/xpccallcontext.cpp +++ b/js/src/xpconnect/src/xpccallcontext.cpp @@ -168,6 +168,9 @@ XPCCallContext::SetName(jsval name) mName = name; +#ifdef XPC_IDISPATCH_SUPPORT + mIDispatchMember = nsnull; +#endif if(mTearOff) { mSet = nsnull; @@ -220,6 +223,9 @@ XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member, if(mState < HAVE_NAME) mState = HAVE_NAME; +#ifdef XPC_IDISPATCH_SUPPORT + mIDispatchMember = nsnull; +#endif } void @@ -476,6 +482,7 @@ XPCCallContext::SetIDispatchInfo(XPCNativeInterface* iface, mSet = nsnull; mInterface = iface; + mMember = nsnull; mIDispatchMember = member; mName = NS_REINTERPRET_CAST(XPCDispInterface::Member*,member)->GetName(); diff --git a/js/src/xpconnect/src/xpcforwards.h b/js/src/xpconnect/src/xpcforwards.h index 66642f3a2898..a1bf5501003d 100644 --- a/js/src/xpconnect/src/xpcforwards.h +++ b/js/src/xpconnect/src/xpcforwards.h @@ -94,6 +94,7 @@ class xpcPropertyBagEnumerator; // Forwards class XPCDispInterface; struct IDispatch; +class XPCDispParams; #endif diff --git a/js/src/xpconnect/src/xpcinlines.h b/js/src/xpconnect/src/xpcinlines.h index 987691f4f835..64dd976511a8 100644 --- a/js/src/xpconnect/src/xpcinlines.h +++ b/js/src/xpconnect/src/xpcinlines.h @@ -563,17 +563,17 @@ inline void XPCNativeSet::ASSERT_NotMarked() /***************************************************************************/ inline -JSObject* XPCWrappedNativeTearOff::GetJSObject() const +JSObject* XPCWrappedNativeTearOff::GetJSObject() const { #ifdef XPC_IDISPATCH_SUPPORT - return IsIDispatch() ? GetIDispatchInfo()->GetJSObject() : mJSObject; + return IsIDispatch() ? GetIDispatchInfo()->GetJSObject() : mJSObject; #else return mJSObject; #endif } inline -void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj) +void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj) { #ifdef XPC_IDISPATCH_SUPPORT if(IsIDispatch()) @@ -585,27 +585,38 @@ void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj) #ifdef XPC_IDISPATCH_SUPPORT inline void -XPCWrappedNativeTearOff::SetIDispatch(JSContext* cx) +XPCWrappedNativeTearOff::SetIDispatch(JSContext* cx) { mJSObject = (JSObject*)(((jsword) - ::XPCDispInterface::NewInstance(cx, - mNative)) | 2); + ::XPCDispInterface::NewInstance(cx, + mNative)) | 2); } inline XPCDispInterface* -XPCWrappedNativeTearOff::GetIDispatchInfo() const +XPCWrappedNativeTearOff::GetIDispatchInfo() const { return NS_REINTERPRET_CAST(XPCDispInterface*, (((jsword)mJSObject) & ~JSOBJECT_MASK)); } -inline JSBool -XPCWrappedNativeTearOff::IsIDispatch() const +inline JSBool +XPCWrappedNativeTearOff::IsIDispatch() const { return (JSBool)(((jsword)mJSObject) & IDISPATCH_BIT); } #endif + +inline +XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff() +{ + NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObject()), "tearoff not empty in dtor"); +#ifdef XPC_IDISPATCH_SUPPORT + if (IsIDispatch()) + delete GetIDispatchInfo(); +#endif +} + /***************************************************************************/ inline JSBool diff --git a/js/src/xpconnect/src/xpcmodule.cpp b/js/src/xpconnect/src/xpcmodule.cpp index 38d2d58efcbd..571d8d3e9909 100644 --- a/js/src/xpconnect/src/xpcmodule.cpp +++ b/js/src/xpconnect/src/xpcmodule.cpp @@ -63,6 +63,10 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIJSRuntimeService, nsJSRuntimeService NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptError) NS_GENERIC_FACTORY_CONSTRUCTOR(nsXPCComponents_Interfaces) +#ifdef XPC_IDISPATCH_SUPPORT +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIDispatchSupport, nsDispatchSupport::GetSingleton) +#endif + NS_DECL_CLASSINFO(nsXPCException) #ifdef XPCONNECT_STANDALONE @@ -87,6 +91,10 @@ static const nsModuleComponentInfo components[] = { { "JS subscript loader", MOZ_JSSUBSCRIPTLOADER_CID, mozJSSubScriptLoadContractID, mozJSSubScriptLoaderConstructor }, #endif +#ifdef XPC_IDISPATCH_SUPPORT + { nsnull, NS_IDISPATCH_SUPPORT_CID, NS_IDISPATCH_SUPPORT_CONTRACTID, + nsIDispatchSupportConstructor } +#endif }; PR_STATIC_CALLBACK(void) @@ -97,6 +105,9 @@ xpcModuleDtor(nsIModule* self) nsXPCThreadJSContextStackImpl::FreeSingleton(); nsJSRuntimeServiceImpl::FreeSingleton(); xpc_DestroyJSxIDClassObjects(); +#ifdef XPC_IDISPATCH_SUPPORT + nsDispatchSupport::FreeSingleton(); +#endif } NS_IMPL_NSGETMODULE_WITH_DTOR(xpconnect, components, xpcModuleDtor) diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index f9d302c1f6ca..72f324cc68a0 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -120,6 +120,7 @@ extern CComModule _Module; #include +#include // MS clutters the global namespace with so many macro names :-( // I tried to keep these includes in the CPP's but it became too // convoluted @@ -973,6 +974,10 @@ XPC_WN_GetterSetter(JSContext *cx, JSObject *obj, extern JSBool xpc_InitWrappedNativeJSOps(); +// Comes from xpcwrappednativeops.cpp +extern void +xpc_MarkForValidWrapper(JSContext *cx, XPCWrappedNative* wrapper, void *arg); + /***************************************************************************/ /***************************************************************************/ @@ -1682,12 +1687,11 @@ public: void SetNative(nsISupports* Native) {mNative = Native;} void SetJSObject(JSObject* JSObj); - void JSObjectFinalized() {mJSObject = nsnull;} + void JSObjectFinalized() {SetJSObject(nsnull);} XPCWrappedNativeTearOff() : mInterface(nsnull), mNative(nsnull), mJSObject(nsnull) {} - ~XPCWrappedNativeTearOff() - {NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObject()), "tearoff not empty in dtor");} + ~XPCWrappedNativeTearOff(); void Mark() {mJSObject = (JSObject*)(((jsword)mJSObject) | 1);} void Unmark() {mJSObject = (JSObject*)(((jsword)mJSObject) & ~1);} diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 9af979bbbb71..633a848052a1 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -216,7 +216,7 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, // in a pointer that hasn't been QI'd to IDispatch properly this could // create multiple wrappers for the same object, creating a fair bit of // confusion. - if(!nsXPConnect::IsIDispatchEnabled() && Interface->GetIID()->Equals(NSID_IDISPATCH)) + if(nsXPConnect::IsIDispatchEnabled() && Interface->GetIID()->Equals(NSID_IDISPATCH)) identity = Object; else #endif @@ -937,6 +937,10 @@ XPCWrappedNative::SystemIsBeingShutDown(XPCCallContext& ccx) if(to->GetJSObject()) { JS_SetPrivate(ccx, to->GetJSObject(), nsnull); +#ifdef XPC_IDISPATCH_SUPPORT + if(to->IsIDispatch()) + delete to->GetIDispatchInfo(); +#endif to->SetJSObject(nsnull); } // We leak the tearoff mNative diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index af21e44f2eb5..4d91427fdf36 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -647,8 +647,8 @@ MarkScopeJSObjects(JSContext *cx, XPCWrappedNativeScope* scope, void *arg) } } -static void -MarkForValidWrapper(JSContext *cx, XPCWrappedNative* wrapper, void *arg) +void +xpc_MarkForValidWrapper(JSContext *cx, XPCWrappedNative* wrapper, void *arg) { // NOTE: It might be nice to also do the wrapper->Mark() call here too. // That call marks the wrapper's and wrapper's proto's interface sets. @@ -687,7 +687,7 @@ XPC_WN_Shared_Mark(JSContext *cx, JSObject *obj, void *arg) XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); if(wrapper && wrapper->IsValid()) - MarkForValidWrapper(cx, wrapper, arg); + xpc_MarkForValidWrapper(cx, wrapper, arg); return 1; } @@ -881,7 +881,7 @@ XPC_WN_Helper_Mark(JSContext *cx, JSObject *obj, void *arg) if(wrapper && wrapper->IsValid()) { wrapper->GetScriptableCallback()->Mark(wrapper, cx, obj, arg, &ignored); - MarkForValidWrapper(cx, wrapper, arg); + xpc_MarkForValidWrapper(cx, wrapper, arg); } return (uint32) ignored; }