Back to index

lightning-sunbird  0.9+nobinonly
XPCIDispatchExtension.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the IDispatch implementation for XPConnect.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * David Bradley.
00018  * Portions created by the Initial Developer are Copyright (C) 2002
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "xpcprivate.h"
00038 
00039 static const char* const IDISPATCH_NAME = "IDispatch";
00040 
00041 #define XPC_IDISPATCH_CTOR_MAX_ARG_LEN 2000
00042 
00043 PRBool XPCIDispatchExtension::mIsEnabled = PR_TRUE;
00044 
00045 static JSBool
00046 CommonConstructor(JSContext *cx, int name, JSObject *obj, uintN argc,
00047                   jsval *argv, jsval *rval, PRBool enforceSecurity)
00048 {
00049     XPCCallContext ccx(JS_CALLER, cx, JS_GetGlobalObject(cx));
00050     // Check if IDispatch is enabled, fail if not
00051     if(!nsXPConnect::IsIDispatchEnabled())
00052     {
00053         XPCThrower::Throw(NS_ERROR_XPC_IDISPATCH_NOT_ENABLED, ccx);
00054         return JS_FALSE;
00055     }
00056     XPCJSRuntime *rt = ccx.GetRuntime();
00057     if(!rt)
00058     {
00059         XPCThrower::Throw(NS_ERROR_UNEXPECTED, ccx);
00060         return JS_FALSE;
00061     } 
00062     nsIXPCSecurityManager* sm = ccx.GetXPCContext()
00063         ->GetAppropriateSecurityManager(nsIXPCSecurityManager::HOOK_CALL_METHOD);
00064     XPCWrappedNative * wrapper = ccx.GetWrapper();
00065     if(sm && NS_FAILED(sm->CanAccess(nsIXPCSecurityManager::ACCESS_CALL_METHOD,
00066                                       &ccx, ccx, ccx.GetFlattenedJSObject(),
00067                                       wrapper->GetIdentityObject(),
00068                                       wrapper->GetClassInfo(),
00069                                       rt->GetStringJSVal(name),
00070                                       wrapper->GetSecurityInfoAddr())))
00071     {
00072         // Security manager will have set an exception
00073         return JS_FALSE;
00074     }
00075     // Make sure we were called with one string parameter
00076     if(argc != 1 || (argc == 1 && !JSVAL_IS_STRING(argv[0])))
00077     {
00078         XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx);
00079         return JS_FALSE;
00080     }
00081 
00082     JSString* str = JSVAL_TO_STRING(argv[0]);
00083     PRUint32 len = JS_GetStringLength(str);
00084 
00085     // Cap constructor argument length to keep from crashing in string
00086     // code.
00087     if(len > XPC_IDISPATCH_CTOR_MAX_ARG_LEN)
00088     {
00089       XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx);
00090       return JS_FALSE;
00091     }
00092 
00093     jschar * className = JS_GetStringChars(str);
00094     CComBSTR bstrClassName(len, NS_REINTERPRET_CAST(const WCHAR *, className));
00095     if(!bstrClassName)
00096     {
00097         XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx);
00098         return JS_FALSE;
00099     }
00100     // Instantiate the desired COM object
00101     CComPtr<IDispatch> pDispatch;
00102     HRESULT rv = XPCDispObject::COMCreateInstance(ccx, bstrClassName,
00103                                                   enforceSecurity, &pDispatch);
00104     if(FAILED(rv))
00105     {
00106         XPCThrower::ThrowCOMError(ccx, rv, NS_ERROR_XPC_COM_CREATE_FAILED);
00107         return JS_FALSE;
00108     }
00109     // Get a wrapper for our object
00110     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
00111     nsresult nsrv = ccx.GetXPConnect()->WrapNative(
00112         ccx, ccx.GetOperandJSObject(), NS_REINTERPRET_CAST(nsISupports*, pDispatch.p),
00113         NSID_IDISPATCH, getter_AddRefs(holder));
00114     if(NS_FAILED(nsrv))
00115     {
00116         XPCThrower::Throw(nsrv, ccx);
00117         return JS_FALSE;
00118     }
00119     // get and return the JS object wrapper
00120     JSObject * jsobj;
00121     nsrv = holder->GetJSObject(&jsobj);
00122     if(NS_FAILED(nsrv))
00123     {
00124         XPCThrower::Throw(nsrv, ccx);
00125         return JS_FALSE;
00126     }
00127     *rval = OBJECT_TO_JSVAL(jsobj);
00128     return JS_TRUE;
00129 }
00130 
00131 JS_STATIC_DLL_CALLBACK(JSBool)
00132 COMObjectConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 
00133                      jsval *rval)
00134 {
00135     return CommonConstructor(cx, XPCJSRuntime::IDX_COM_OBJECT, obj, argc,
00136                              argv, rval, PR_FALSE);
00137 }
00138 
00139 JS_STATIC_DLL_CALLBACK(JSBool)
00140 ActiveXConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 
00141                    jsval *rval)
00142 {
00143     return CommonConstructor(cx, XPCJSRuntime::IDX_ACTIVEX_OBJECT, obj, argc, argv,
00144                              rval, PR_TRUE);
00145 }
00146 
00147 JS_STATIC_DLL_CALLBACK(JSBool)
00148 ActiveXSupports(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 
00149                 jsval *rval)
00150 {
00151     XPCCallContext ccx(JS_CALLER, cx, JS_GetGlobalObject(cx));
00152     // Check if IDispatch is enabled, fail if not
00153     if(!nsXPConnect::IsIDispatchEnabled())
00154     {
00155         XPCThrower::Throw(NS_ERROR_XPC_IDISPATCH_NOT_ENABLED, ccx);
00156         return JS_FALSE;
00157     }
00158     XPCJSRuntime *rt = ccx.GetRuntime();
00159     if(!rt)
00160     {
00161         XPCThrower::Throw(NS_ERROR_UNEXPECTED, ccx);
00162         return JS_FALSE;
00163     } 
00164     // Make sure we were called with one string parameter
00165     if(argc != 1 || (argc == 1 && !JSVAL_IS_STRING(argv[0])))
00166     {
00167         XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx);
00168         return JS_FALSE;
00169     }
00170     PRUint32 len;
00171     jschar * className = xpc_JSString2String(ccx, argv[0], &len);
00172     CComBSTR bstrClassName(len, NS_REINTERPRET_CAST(const WCHAR *, className));
00173     if(!className)
00174     {
00175         XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx);
00176         return JS_FALSE;
00177     }
00178     CLSID classID = CLSID_NULL;
00179     HRESULT hr = CLSIDFromString(bstrClassName, &classID);
00180     if(FAILED(hr) || ::IsEqualCLSID(classID, CLSID_NULL))
00181     {
00182         XPCThrower::Throw(NS_ERROR_XPC_COM_INVALID_CLASS_ID, ccx);
00183         return JS_FALSE;
00184     }
00185     // Instantiate the desired COM object
00186     HRESULT rv = XPCDispObject::SecurityCheck(ccx, classID);
00187     *rval = BOOLEAN_TO_JSVAL(SUCCEEDED(rv));
00188     return JS_TRUE;
00189 }
00190 
00191 class xpcFunctionDefiner
00192 {
00193 public:
00194     xpcFunctionDefiner(JSContext * aJSContext);
00195     JSFunction * Define(JSObject * globalObject, uintN aNameIndex,
00196                         JSNative aCall);
00197 private:
00198     XPCJSRuntime * m_Runtime;
00199     JSContext * m_JSContext;
00200 };
00201 
00202 inline
00203 xpcFunctionDefiner::xpcFunctionDefiner(JSContext * aJSContext) : 
00204     m_Runtime(nsXPConnect::GetRuntime()), m_JSContext(aJSContext)
00205 {
00206     NS_ASSERTION(m_Runtime, "nsXPConnect::GetRuntime() returned null");
00207     NS_ASSERTION(aJSContext, "xpcFunctionDefiner constructor passed a null context");
00208 }
00209 
00210 inline
00211 JSFunction * xpcFunctionDefiner::Define(JSObject * globalObject,
00212                                         uintN aNameIndex, JSNative aCall)
00213 {
00214     return JS_DefineFunction(m_JSContext, globalObject,
00215                       m_Runtime->GetStringName(aNameIndex),
00216                       aCall, 1, JSPROP_PERMANENT | JSPROP_READONLY);
00217 }
00218 
00219 JSBool XPCIDispatchExtension::Initialize(JSContext * aJSContext,
00220                                          JSObject * aGlobalJSObj)
00221 {
00222     xpcFunctionDefiner fd(aJSContext);
00223     JSFunction * func = fd.Define(aGlobalJSObj,
00224                                   XPCJSRuntime::IDX_ACTIVEX_OBJECT,
00225                                   ActiveXConstructor);
00226     if(!func)
00227         return JS_FALSE;
00228 
00229     JSObject * funcObject = JS_GetFunctionObject(func);
00230     if(!funcObject)
00231         return JS_FALSE;
00232 
00233     if(!fd.Define(funcObject, XPCJSRuntime::IDX_ACTIVEX_SUPPORTS, ActiveXSupports))
00234         return JS_FALSE;
00235 
00236 #ifdef XPC_COMOBJECT
00237     if(!fd.Define(aGlobalJSObj, XPCJSRuntime::IDX_COM_OBJECT, COMObjectConstructor))
00238         return JS_FALSE;
00239 #endif
00240     return JS_TRUE;
00241 }
00242 
00243 JSBool XPCIDispatchExtension::DefineProperty(XPCCallContext & ccx, 
00244                                              JSObject *obj, jsval idval,
00245                                              XPCWrappedNative* wrapperToReflectInterfaceNames,
00246                                              uintN propFlags, JSBool* resolved)
00247 {
00248     if(!JSVAL_IS_STRING(idval))
00249         return JS_FALSE;
00250     // Look up the native interface for IDispatch and then find a tearoff
00251     XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(ccx,
00252                                                                  "IDispatch");
00253     // The native interface isn't defined so just exit with an error
00254     if(iface == nsnull)
00255         return JS_FALSE;
00256     XPCWrappedNativeTearOff* to = 
00257         wrapperToReflectInterfaceNames->LocateTearOff(ccx, iface);
00258     // This object has no IDispatch interface so bail.
00259     if(to == nsnull)
00260         return JS_FALSE;
00261     // Look up the member in the interface
00262     const XPCDispInterface::Member * member = to->GetIDispatchInfo()->FindMember(idval);
00263     if(!member)
00264     {
00265         // IDispatch is case insensitive, so if we don't find a case sensitive
00266         // match, we'll try a more expensive case-insensisitive search
00267         // TODO: We need to create cleaner solution that doesn't create
00268         // multiple properties of different case on the JS Object
00269         member = to->GetIDispatchInfo()->FindMemberCI(ccx, idval);
00270         if(!member)
00271             return JS_FALSE;
00272     }
00273     // Get the function object
00274     jsval funval;
00275     if(!member->GetValue(ccx, iface, &funval))
00276         return JS_FALSE;
00277     // Protect the jsval 
00278     AUTO_MARK_JSVAL(ccx, funval);
00279     // clone a function we can use for this object 
00280     JSObject* funobj = xpc_CloneJSFunction(ccx, JSVAL_TO_OBJECT(funval), obj);
00281     if(!funobj)
00282         return JS_FALSE;
00283     jsid id;
00284     // If this is a function or a parameterized property
00285     if(member->IsFunction() || member->IsParameterizedProperty())
00286     {
00287         // define the function on the object
00288         AutoResolveName arn(ccx, idval);
00289         if(resolved)
00290             *resolved = JS_TRUE;
00291         return JS_ValueToId(ccx, idval, &id) &&
00292                OBJ_DEFINE_PROPERTY(ccx, obj, id, OBJECT_TO_JSVAL(funobj),
00293                                    nsnull, nsnull, propFlags, nsnull);
00294     }
00295     // Define the property on the object
00296     NS_ASSERTION(member->IsProperty(), "way broken!");
00297     propFlags |= JSPROP_GETTER | JSPROP_SHARED;
00298     if(member->IsSetter())
00299     {
00300         propFlags |= JSPROP_SETTER;
00301         propFlags &= ~JSPROP_READONLY;
00302     }
00303     AutoResolveName arn(ccx, idval);
00304     if(resolved)
00305         *resolved = JS_TRUE;
00306     return JS_ValueToId(ccx, idval, &id) &&
00307            OBJ_DEFINE_PROPERTY(ccx, obj, id, JSVAL_VOID,
00308                                (JSPropertyOp) funobj,
00309                                (JSPropertyOp) funobj,
00310                                propFlags, nsnull);
00311 
00312 }
00313 
00314 JSBool XPCIDispatchExtension::Enumerate(XPCCallContext& ccx, JSObject* obj,
00315                                         XPCWrappedNative * wrapper)
00316 {
00317     XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(
00318         ccx,&NSID_IDISPATCH);
00319     if(!iface)
00320         return JS_FALSE;
00321 
00322     XPCWrappedNativeTearOff* tearoff = wrapper->FindTearOff(ccx, iface);
00323     if(!tearoff)
00324         return JS_FALSE;
00325 
00326     XPCDispInterface* pInfo = tearoff->GetIDispatchInfo();
00327     PRUint32 members = pInfo->GetMemberCount();
00328     // Iterate over the members and force the properties to be resolved
00329     for(PRUint32 index = 0; index < members; ++index)
00330     {
00331         const XPCDispInterface::Member & member = pInfo->GetMember(index);
00332         jsval name = member.GetName();
00333         if(!xpc_ForcePropertyResolve(ccx, obj, name))
00334             return JS_FALSE;
00335     }
00336     return JS_TRUE;
00337 }
00338 
00339 nsresult XPCIDispatchExtension::IDispatchQIWrappedJS(nsXPCWrappedJS * self, 
00340                                                      void ** aInstancePtr)
00341 {
00342     // Lookup the root and create a tearoff based on that
00343     nsXPCWrappedJS* root = self->GetRootWrapper();
00344 
00345     if(!root->IsValid())
00346     {
00347         *aInstancePtr = nsnull;
00348         return NS_NOINTERFACE;
00349     }
00350     XPCDispatchTearOff* tearOff = new XPCDispatchTearOff(root);
00351     if(!tearOff)
00352         return NS_ERROR_OUT_OF_MEMORY;
00353     tearOff->AddRef();
00354     *aInstancePtr = tearOff;
00355     
00356     return NS_OK;
00357 }