Back to index

lightning-sunbird  0.9+nobinonly
XPCDispObject.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the IDispatch implementation for XPConnect.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * David Bradley.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00043 #include "xpcprivate.h"
00044 #include "nsIActiveXSecurityPolicy.h"
00045 
00049 const nsID NSID_IDISPATCH = { 0x00020400, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
00050 
00051 PRBool
00052 XPCDispObject::WrapIDispatch(IDispatch *pDispatch, XPCCallContext &ccx,
00053                              JSObject *obj, jsval *rval)
00054 {
00055     if(!pDispatch)
00056     {
00057         return PR_FALSE;
00058     }
00059 
00060     // Wrap the desired COM object
00061     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
00062     nsresult rv = ccx.GetXPConnect()->WrapNative(
00063         ccx, obj, NS_REINTERPRET_CAST(nsISupports*, pDispatch), NSID_IDISPATCH,
00064         getter_AddRefs(holder));
00065     if(NS_FAILED(rv) || !holder)
00066     {
00067         return PR_FALSE;
00068     }
00069     JSObject * jsobj;
00070     if(NS_FAILED(holder->GetJSObject(&jsobj)))
00071         return PR_FALSE;
00072     *rval = OBJECT_TO_JSVAL(jsobj);
00073     return PR_TRUE;
00074 }
00075 
00076 HRESULT XPCDispObject::SecurityCheck(XPCCallContext & ccx, const CLSID & aCID,
00077                                      IDispatch ** createdObject)
00078 {
00079     nsresult rv;
00080     nsCOMPtr<nsIDispatchSupport> dispSupport = do_GetService(NS_IDISPATCH_SUPPORT_CONTRACTID, &rv);
00081     if(NS_FAILED(rv)) return E_UNEXPECTED;
00082 
00083     PRUint32 hostingFlags = nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_NOTHING;
00084     dispSupport->GetHostingFlags(nsnull, &hostingFlags);
00085     PRBool allowSafeObjects;
00086     if(hostingFlags & (nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_SAFE_OBJECTS))
00087         allowSafeObjects = PR_TRUE;
00088     else
00089         allowSafeObjects = PR_FALSE;
00090     PRBool allowAnyObjects;
00091     if(hostingFlags & (nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_ALL_OBJECTS))
00092         allowAnyObjects = PR_TRUE;
00093     else
00094         allowAnyObjects = PR_FALSE;
00095     if(!allowSafeObjects && !allowAnyObjects)
00096         return E_FAIL;
00097 
00098     PRBool classExists = PR_FALSE;
00099     PRBool ok = PR_FALSE;
00100     const nsCID & ourCID = XPCDispCLSID2nsCID(aCID);
00101     dispSupport->IsClassSafeToHost(ccx, ourCID, PR_FALSE, &classExists, &ok);
00102     if(classExists && !ok)
00103         return E_FAIL;
00104 
00105     // Test if the object is scriptable
00106     PRBool isScriptable = PR_FALSE;
00107     if(!allowAnyObjects)
00108     {
00109         PRBool classExists = PR_FALSE;
00110         dispSupport->IsClassMarkedSafeForScripting(ourCID, &classExists, &isScriptable);
00111         if(!classExists)
00112             return REGDB_E_CLASSNOTREG;
00113     }
00114 
00115     // Create the object
00116     CComPtr<IDispatch> disp;
00117     // If createdObject isn't null we need to create the object
00118     if (createdObject)
00119     {
00120         HRESULT hr = disp.CoCreateInstance(aCID);
00121         if(FAILED(hr))
00122             return hr;
00123         // if we don't allow just any object, and it wasn't marked 
00124         // safe for scripting then ask the object (MS idea of security)
00125         if (!allowAnyObjects && !isScriptable)
00126         {
00127             dispSupport->IsObjectSafeForScripting(disp, NSID_IDISPATCH, &isScriptable);
00128             if(!isScriptable)
00129                 return E_FAIL;
00130         }
00131         disp.CopyTo(createdObject);
00132     }
00133 
00134     return S_OK;
00135 }
00136 
00137 HRESULT XPCDispObject::COMCreateInstance(XPCCallContext & ccx, BSTR className,
00138                                          PRBool enforceSecurity,
00139                                          IDispatch ** result)
00140 {
00141     NS_ENSURE_ARG_POINTER(result);
00142     // Turn the string into a CLSID
00143     _bstr_t bstrName(className);
00144     CLSID classID = CLSID_NULL;
00145     HRESULT hr = CLSIDFromString(bstrName, &classID);
00146     if(FAILED(hr))
00147         hr = CLSIDFromProgID(bstrName, &classID);
00148     if(FAILED(hr) || ::IsEqualCLSID(classID, CLSID_NULL))
00149         return hr;
00150     
00151     // If the caller cares about security do the necessary checks
00152     // This results in the object being instantiated, so we'll use
00153     // it
00154     if(enforceSecurity)
00155         return SecurityCheck(ccx, classID, result);
00156     
00157     CComPtr<IDispatch> disp;
00158     hr = disp.CoCreateInstance(classID);
00159     if(FAILED(hr))
00160         return hr;
00161 
00162     disp.CopyTo(result);
00163 
00164     return S_OK;
00165 }
00166 
00167 // static
00168 JSBool XPCDispObject::Dispatch(XPCCallContext& ccx, IDispatch * disp,
00169                                DISPID dispID, CallMode mode, 
00170                                XPCDispParams * params,
00171                                jsval* retval,
00172                                XPCDispInterface::Member * member,
00173                                XPCJSRuntime* rt)
00174 {
00175     _variant_t dispResult;
00176     jsval val;
00177     uintN err;
00178     uintN argc = params->GetParamCount();
00179     // Figure out what we're doing (getter/setter/method)
00180     WORD dispFlags;
00181     if(mode == CALL_SETTER)
00182     {
00183         dispFlags = DISPATCH_PROPERTYPUT;
00184     }
00185     else if(mode == CALL_GETTER)
00186     {
00187         dispFlags = DISPATCH_PROPERTYGET;
00188     }
00189     else
00190     {
00191         dispFlags = DISPATCH_METHOD;
00192     }
00193     HRESULT invokeResult;
00194     EXCEPINFO exception;
00195     // Scope the lock
00196     {
00197         // avoid deadlock in case the native method blocks somehow
00198         AutoJSSuspendRequest req(ccx);  // scoped suspend of request
00199         // call IDispatch's invoke
00200         invokeResult= disp->Invoke(
00201             dispID,                  // IDispatch ID
00202             IID_NULL,                // Reserved must be IID_NULL
00203             LOCALE_SYSTEM_DEFAULT,   // The locale context, use the system's
00204             dispFlags,               // Type of Invoke call
00205             params->GetDispParams(), // Parameters
00206             &dispResult,             // Where the result is stored
00207             &exception,              // Exception information
00208             0);                      // Index of an argument error
00209     }
00210     if(SUCCEEDED(invokeResult))
00211     {
00212         *retval = JSVAL_VOID;
00213         if(mode == CALL_METHOD)
00214         {
00215             NS_ASSERTION(member, "member must not be null if this is a method");
00216             for(PRUint32 index = 0; index < argc; ++index)
00217             {
00218                 const XPCDispInterface::Member::ParamInfo & paramInfo = member->GetParamInfo(index);
00219                 if(paramInfo.IsOut())
00220                 {
00221                     if(!XPCDispConvert::COMToJS(ccx, params->GetParamRef(index), val, err))
00222                         return ThrowBadParam(err, index, ccx);
00223 
00224                     if(paramInfo.IsRetVal())
00225                     {
00226                         *retval = val;
00227                     }
00228                     else
00229                     {
00230                         jsval * argv = ccx.GetArgv();
00231                         // Out, in/out parameters must be objects
00232                         if(!JSVAL_IS_OBJECT(argv[index]) ||
00233                             !OBJ_SET_PROPERTY(ccx, JSVAL_TO_OBJECT(argv[index]),
00234                                 rt->GetStringID(XPCJSRuntime::IDX_VALUE), &val))
00235                             return ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, index, ccx);
00236                     }
00237                 }
00238             }
00239         }
00240         if(dispResult.vt != VT_EMPTY)
00241         {
00242             if(!XPCDispConvert::COMToJS(ccx, dispResult, val, err))
00243             {
00244                 ThrowBadParam(err, 0, ccx);
00245             }
00246             *retval = val;
00247         }
00248     }
00249     // Set the result and throw the error if one occured
00250     ccx.GetXPCContext()->SetLastResult(invokeResult);
00251 
00252     if(NS_FAILED(invokeResult))
00253     {
00254         XPCThrower::ThrowCOMError(ccx, invokeResult, NS_ERROR_XPC_COM_ERROR, 
00255                                   invokeResult == DISP_E_EXCEPTION ? 
00256                                       &exception : nsnull);
00257         return JS_FALSE;
00258     }
00259     return JS_TRUE;
00260 }
00261 
00262 JSBool XPCDispObject::Invoke(XPCCallContext & ccx, CallMode mode)
00263 {
00264     nsresult rv = ccx.CanCallNow();
00265     if(NS_FAILED(rv))
00266     {
00267         // If the security manager is complaining then this is not really an
00268         // internal error in xpconnect. So, no reason to botch the assertion.
00269         NS_ASSERTION(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, 
00270                      "hmm? CanCallNow failed in XPCDispObject::Invoke. "
00271                      "We are finding out about this late!");
00272         XPCThrower::Throw(rv, ccx);
00273         return JS_FALSE;
00274     }
00275 
00276     // TODO: Remove type cast and change GetIDispatchMember to use the correct type
00277     XPCDispInterface::Member* member = NS_REINTERPRET_CAST(XPCDispInterface::Member*,ccx.GetIDispatchMember());
00278     XPCJSRuntime* rt = ccx.GetRuntime();
00279     XPCContext* xpcc = ccx.GetXPCContext();
00280     XPCPerThreadData* tls = ccx.GetThreadData();
00281     
00282     jsval* argv = ccx.GetArgv();
00283     uintN argc = ccx.GetArgc();
00284 
00285     tls->SetException(nsnull);
00286     xpcc->SetLastResult(NS_ERROR_UNEXPECTED);
00287 
00288     // set up the method index and do the security check if needed
00289 
00290     PRUint32 secFlag;
00291     PRUint32 secAction;
00292 
00293     switch(mode)
00294     {
00295         case CALL_METHOD:
00296             secFlag   = nsIXPCSecurityManager::HOOK_CALL_METHOD;
00297             secAction = nsIXPCSecurityManager::ACCESS_CALL_METHOD;
00298             break;
00299         case CALL_GETTER:
00300             secFlag   = nsIXPCSecurityManager::HOOK_GET_PROPERTY;
00301             secAction = nsIXPCSecurityManager::ACCESS_GET_PROPERTY;
00302             break;
00303         case CALL_SETTER:
00304             secFlag   = nsIXPCSecurityManager::HOOK_SET_PROPERTY;
00305             secAction = nsIXPCSecurityManager::ACCESS_SET_PROPERTY;
00306             break;
00307         default:
00308             NS_ASSERTION(0,"bad value");
00309             return JS_FALSE;
00310     }
00311     jsval name = member->GetName();
00312 
00313     nsIXPCSecurityManager* sm = xpcc->GetAppropriateSecurityManager(secFlag);
00314     XPCWrappedNative* wrapper = ccx.GetWrapper();
00315     if(sm && NS_FAILED(sm->CanAccess(secAction, &ccx, ccx,
00316                                      ccx.GetFlattenedJSObject(),
00317                                      wrapper->GetIdentityObject(),
00318                                      wrapper->GetClassInfo(), name,
00319                                      wrapper->GetSecurityInfoAddr())))
00320     {
00321         // the security manager vetoed. It should have set an exception.
00322         return JS_FALSE;
00323     }
00324 
00325     IDispatch * pObj = NS_REINTERPRET_CAST(IDispatch*,
00326                                             ccx.GetTearOff()->GetNative());
00327     PRUint32 args = member->GetParamCount();
00328     uintN err;
00329     // Make sure setter has one argument
00330     if(mode == CALL_SETTER)
00331         args = 1;
00332     // Allow for optional parameters. We'll let COM handle the error if there
00333     // are not enough parameters
00334     if(argc < args)
00335         args = argc;
00336     XPCDispParams * params = new XPCDispParams(args);
00337     jsval val;
00338     // If this is a setter, we just need to convert the first parameter
00339     if(mode == CALL_SETTER)
00340     {
00341         params->SetNamedPropID();
00342         if(!XPCDispConvert::JSToCOM(ccx, argv[0], params->GetParamRef(0), err))
00343         {
00344             delete params;
00345             return ThrowBadParam(err, 0, ccx);
00346         }
00347     }
00348     else if(mode != CALL_GETTER)    // This is a function
00349     {
00350         // Convert the arguments to the function
00351         for(PRUint32 index = 0; index < args; ++index)
00352         {
00353             const XPCDispInterface::Member::ParamInfo & paramInfo = member->GetParamInfo(index);
00354             if(paramInfo.IsIn())
00355             {
00356                 val = argv[index];
00357                 if(paramInfo.IsOut())
00358                 {
00359                     if(JSVAL_IS_PRIMITIVE(val) ||
00360                         !OBJ_GET_PROPERTY(ccx, JSVAL_TO_OBJECT(val),
00361                                           rt->GetStringID(XPCJSRuntime::IDX_VALUE),
00362                                           &val))
00363                     {
00364                         delete params;
00365                         return ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, index, ccx);
00366                     }
00367                     paramInfo.InitializeOutputParam(params->GetOutputBuffer(index), params->GetParamRef(index));
00368                 }
00369                 if(!XPCDispConvert::JSToCOM(ccx, val, params->GetParamRef(index), err, paramInfo.IsOut()))
00370                 {
00371                     delete params;
00372                     return ThrowBadParam(err, index, ccx);
00373                 }
00374             }
00375             else
00376             {
00377                 paramInfo.InitializeOutputParam(params->GetOutputBuffer(index), params->GetParamRef(index));
00378             }
00379         }
00380     }
00381     // If this is a parameterized property
00382     if(member->IsParameterizedProperty())
00383     {
00384         // We need to get a parameterized property object to return to JS
00385         // NewInstance takes ownership of params
00386         if(XPCDispParamPropJSClass::NewInstance(ccx, wrapper,
00387                                                 member->GetDispID(),
00388                                                 params, &val))
00389         {
00390             ccx.SetRetVal(val);
00391             if(!JS_IdToValue(ccx, 1, &val))
00392             {
00393                 // This shouldn't fail
00394                 NS_ERROR("JS_IdToValue failed in XPCDispParamPropJSClass::NewInstance");
00395                 return JS_FALSE;
00396             }
00397             JS_SetCallReturnValue2(ccx, val);
00398             return JS_TRUE;
00399         }
00400         // NewInstance would only fail if there was an out of memory problem
00401         JS_ReportOutOfMemory(ccx);
00402         delete params;
00403         return JS_FALSE;
00404     }
00405     JSBool retval = Dispatch(ccx, pObj, member->GetDispID(), mode, params, &val, member, rt);
00406     if(retval && mode == CALL_SETTER)
00407     {
00408         ccx.SetRetVal(argv[0]);
00409     }
00410     else
00411     {
00412         ccx.SetRetVal(val);
00413     }
00414     delete params;
00415     return retval;
00416 }
00417 
00418 static
00419 JSBool GetMember(XPCCallContext& ccx, JSObject* funobj, XPCNativeInterface*& iface, XPCDispInterface::Member*& member)
00420 {
00421     jsval val;
00422     if(!JS_GetReservedSlot(ccx, funobj, 1, &val))
00423         return JS_FALSE;
00424     if(!JSVAL_IS_INT(val))
00425         return JS_FALSE;
00426     iface = NS_REINTERPRET_CAST(XPCNativeInterface*,JSVAL_TO_PRIVATE(val));
00427     if(!JS_GetReservedSlot(ccx, funobj, 0, &val))
00428         return JS_FALSE;
00429     if(!JSVAL_IS_INT(val))
00430         return JS_FALSE;
00431     member = NS_REINTERPRET_CAST(XPCDispInterface::Member*,JSVAL_TO_PRIVATE(val));
00432     return JS_TRUE;
00433 }
00434 
00435 // Handy macro used in callbacks below.
00436 #define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper)                         \
00437     PR_BEGIN_MACRO                                                           \
00438     if(!wrapper)                                                             \
00439     {                                                                        \
00440         XPCThrower::Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);              \
00441         return JS_FALSE;                                                     \
00442     }                                                                        \
00443     if(!wrapper->IsValid())                                                  \
00444     {                                                                        \
00445         XPCThrower::Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx);               \
00446         return JS_FALSE;                                                     \
00447     }                                                                        \
00448     PR_END_MACRO
00449 
00461 JSBool JS_DLL_CALLBACK
00462 XPC_IDispatch_CallMethod(JSContext* cx, JSObject* obj, uintN argc,
00463                          jsval* argv, jsval* vp)
00464 {
00465     NS_ASSERTION(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION, "bad function");
00466     JSObject* funobj = JSVAL_TO_OBJECT(argv[-2]);
00467     XPCCallContext ccx(JS_CALLER, cx, obj, funobj, 0, argc, argv, vp);
00468     XPCWrappedNative* wrapper = ccx.GetWrapper();
00469     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
00470     ccx.SetArgsAndResultPtr(argc, argv, vp);
00471 
00472     XPCDispInterface::Member* member;
00473     XPCNativeInterface* iface;
00474 #ifdef DEBUG
00475     PRBool ok =
00476 #endif
00477     GetMember(ccx, funobj, iface, member);
00478     NS_ASSERTION(ok, "GetMember faild in XPC_IDispatch_CallMethod");
00479     ccx.SetIDispatchInfo(iface, member);
00480 
00481     return XPCDispObject::Invoke(ccx, XPCDispObject::CALL_METHOD);
00482 }
00483 
00495 JSBool JS_DLL_CALLBACK
00496 XPC_IDispatch_GetterSetter(JSContext *cx, JSObject *obj, uintN argc,
00497                            jsval *argv, jsval *vp)
00498 {
00499     NS_ASSERTION(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION, "bad function");
00500     JSObject* funobj = JSVAL_TO_OBJECT(argv[-2]);
00501 
00502     XPCCallContext ccx(JS_CALLER, cx, obj, funobj);
00503     XPCWrappedNative* wrapper = ccx.GetWrapper();
00504     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
00505 
00506     ccx.SetArgsAndResultPtr(argc, argv, vp);
00507     XPCDispInterface::Member* member;
00508     XPCNativeInterface* iface;
00509 #ifdef DEBUG
00510     PRBool ok =
00511 #endif
00512     GetMember(ccx, funobj, iface, member);
00513     NS_ASSERTION(ok, "GetMember faild in XPC_IDispatch_CallMethod");
00514 
00515     ccx.SetIDispatchInfo(iface, member);
00516     return XPCDispObject::Invoke(ccx, argc != 0 ? XPCDispObject::CALL_SETTER : XPCDispObject::CALL_GETTER);
00517 }