1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is the IDispatch implementation for XPConnect.
17
* The Initial Developer of the Original Code is
19
* Portions created by the Initial Developer are Copyright (C) 2002
20
* the Initial Developer. All Rights Reserved.
24
* Alternatively, the contents of this file may be used under the terms of
25
* either the GNU General Public License Version 2 or later (the "GPL"), or
26
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
* in which case the provisions of the GPL or the LGPL are applicable instead
28
* of those above. If you wish to allow use of your version of this file only
29
* under the terms of either the GPL or the LGPL, and not to allow others to
30
* use your version of this file under the terms of the MPL, indicate your
31
* decision by deleting the provisions above and replace them with the notice
32
* and other provisions required by the GPL or the LGPL. If you do not delete
33
* the provisions above, a recipient may use your version of this file under
34
* the terms of any one of the MPL, the GPL or the LGPL.
36
* ***** END LICENSE BLOCK ***** */
39
* \file XPCDispObject.cpp
40
* Contains the XPCDispObject class implementation,
41
* XPC_IDispatch_GetterSetter, and XPC_IDispatch_CallMethod
43
#include "xpcprivate.h"
44
#include "nsIActiveXSecurityPolicy.h"
47
* This is COM's IDispatch IID, but in XPCOM's nsID type
49
const nsID NSID_IDISPATCH = { 0x00020400, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
52
XPCDispObject::WrapIDispatch(IDispatch *pDispatch, XPCCallContext &ccx,
53
JSObject *obj, jsval *rval)
60
// Wrap the desired COM object
61
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
62
nsresult rv = ccx.GetXPConnect()->WrapNative(
63
ccx, obj, NS_REINTERPRET_CAST(nsISupports*, pDispatch), NSID_IDISPATCH,
64
getter_AddRefs(holder));
65
if(NS_FAILED(rv) || !holder)
70
if(NS_FAILED(holder->GetJSObject(&jsobj)))
72
*rval = OBJECT_TO_JSVAL(jsobj);
76
HRESULT XPCDispObject::SecurityCheck(XPCCallContext & ccx, const CLSID & aCID,
77
IDispatch ** createdObject)
80
nsCOMPtr<nsIDispatchSupport> dispSupport = do_GetService(NS_IDISPATCH_SUPPORT_CONTRACTID, &rv);
81
if(NS_FAILED(rv)) return E_UNEXPECTED;
83
PRUint32 hostingFlags = nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_NOTHING;
84
dispSupport->GetHostingFlags(nsnull, &hostingFlags);
85
PRBool allowSafeObjects;
86
if(hostingFlags & (nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_SAFE_OBJECTS))
87
allowSafeObjects = PR_TRUE;
89
allowSafeObjects = PR_FALSE;
90
PRBool allowAnyObjects;
91
if(hostingFlags & (nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_ALL_OBJECTS))
92
allowAnyObjects = PR_TRUE;
94
allowAnyObjects = PR_FALSE;
95
if(!allowSafeObjects && !allowAnyObjects)
98
PRBool classExists = PR_FALSE;
100
const nsCID & ourCID = XPCDispCLSID2nsCID(aCID);
101
dispSupport->IsClassSafeToHost(ccx, ourCID, PR_FALSE, &classExists, &ok);
102
if(classExists && !ok)
105
// Test if the object is scriptable
106
PRBool isScriptable = PR_FALSE;
109
PRBool classExists = PR_FALSE;
110
dispSupport->IsClassMarkedSafeForScripting(ourCID, &classExists, &isScriptable);
112
return REGDB_E_CLASSNOTREG;
116
CComPtr<IDispatch> disp;
117
// If createdObject isn't null we need to create the object
120
HRESULT hr = disp.CoCreateInstance(aCID);
123
// if we don't allow just any object, and it wasn't marked
124
// safe for scripting then ask the object (MS idea of security)
125
if (!allowAnyObjects && !isScriptable)
127
dispSupport->IsObjectSafeForScripting(disp, NSID_IDISPATCH, &isScriptable);
131
disp.CopyTo(createdObject);
137
HRESULT XPCDispObject::COMCreateInstance(XPCCallContext & ccx, BSTR className,
138
PRBool enforceSecurity,
141
NS_ENSURE_ARG_POINTER(result);
142
// Turn the string into a CLSID
143
_bstr_t bstrName(className);
144
CLSID classID = CLSID_NULL;
145
HRESULT hr = CLSIDFromString(bstrName, &classID);
147
hr = CLSIDFromProgID(bstrName, &classID);
148
if(FAILED(hr) || ::IsEqualCLSID(classID, CLSID_NULL))
151
// If the caller cares about security do the necessary checks
152
// This results in the object being instantiated, so we'll use
155
return SecurityCheck(ccx, classID, result);
157
CComPtr<IDispatch> disp;
158
hr = disp.CoCreateInstance(classID);
168
JSBool XPCDispObject::Dispatch(XPCCallContext& ccx, IDispatch * disp,
169
DISPID dispID, CallMode mode,
170
XPCDispParams * params,
172
XPCDispInterface::Member * member,
175
_variant_t dispResult;
178
uintN argc = params->GetParamCount();
179
// Figure out what we're doing (getter/setter/method)
181
if(mode == CALL_SETTER)
183
dispFlags = DISPATCH_PROPERTYPUT;
185
else if(mode == CALL_GETTER)
187
dispFlags = DISPATCH_PROPERTYGET;
191
dispFlags = DISPATCH_METHOD;
193
HRESULT invokeResult;
197
// avoid deadlock in case the native method blocks somehow
198
AutoJSSuspendRequest req(ccx); // scoped suspend of request
199
// call IDispatch's invoke
200
invokeResult= disp->Invoke(
201
dispID, // IDispatch ID
202
IID_NULL, // Reserved must be IID_NULL
203
LOCALE_SYSTEM_DEFAULT, // The locale context, use the system's
204
dispFlags, // Type of Invoke call
205
params->GetDispParams(), // Parameters
206
&dispResult, // Where the result is stored
207
&exception, // Exception information
208
0); // Index of an argument error
210
if(SUCCEEDED(invokeResult))
212
*retval = JSVAL_VOID;
213
if(mode == CALL_METHOD)
215
NS_ASSERTION(member, "member must not be null if this is a method");
216
for(PRUint32 index = 0; index < argc; ++index)
218
const XPCDispInterface::Member::ParamInfo & paramInfo = member->GetParamInfo(index);
219
if(paramInfo.IsOut())
221
if(!XPCDispConvert::COMToJS(ccx, params->GetParamRef(index), val, err))
222
return ThrowBadParam(err, index, ccx);
224
if(paramInfo.IsRetVal())
230
jsval * argv = ccx.GetArgv();
231
// Out, in/out parameters must be objects
232
if(!JSVAL_IS_OBJECT(argv[index]) ||
233
!OBJ_SET_PROPERTY(ccx, JSVAL_TO_OBJECT(argv[index]),
234
rt->GetStringID(XPCJSRuntime::IDX_VALUE), &val))
235
return ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, index, ccx);
240
if(dispResult.vt != VT_EMPTY)
242
if(!XPCDispConvert::COMToJS(ccx, dispResult, val, err))
244
ThrowBadParam(err, 0, ccx);
249
// Set the result and throw the error if one occured
250
ccx.GetXPCContext()->SetLastResult(invokeResult);
252
if(NS_FAILED(invokeResult))
254
XPCThrower::ThrowCOMError(ccx, invokeResult, NS_ERROR_XPC_COM_ERROR,
255
invokeResult == DISP_E_EXCEPTION ?
256
&exception : nsnull);
262
JSBool XPCDispObject::Invoke(XPCCallContext & ccx, CallMode mode)
264
nsresult rv = ccx.CanCallNow();
267
// If the security manager is complaining then this is not really an
268
// internal error in xpconnect. So, no reason to botch the assertion.
269
NS_ASSERTION(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
270
"hmm? CanCallNow failed in XPCDispObject::Invoke. "
271
"We are finding out about this late!");
272
XPCThrower::Throw(rv, ccx);
276
// TODO: Remove type cast and change GetIDispatchMember to use the correct type
277
XPCDispInterface::Member* member = NS_REINTERPRET_CAST(XPCDispInterface::Member*,ccx.GetIDispatchMember());
278
XPCJSRuntime* rt = ccx.GetRuntime();
279
XPCContext* xpcc = ccx.GetXPCContext();
280
XPCPerThreadData* tls = ccx.GetThreadData();
282
jsval* argv = ccx.GetArgv();
283
uintN argc = ccx.GetArgc();
285
tls->SetException(nsnull);
286
xpcc->SetLastResult(NS_ERROR_UNEXPECTED);
288
// set up the method index and do the security check if needed
296
secFlag = nsIXPCSecurityManager::HOOK_CALL_METHOD;
297
secAction = nsIXPCSecurityManager::ACCESS_CALL_METHOD;
300
secFlag = nsIXPCSecurityManager::HOOK_GET_PROPERTY;
301
secAction = nsIXPCSecurityManager::ACCESS_GET_PROPERTY;
304
secFlag = nsIXPCSecurityManager::HOOK_SET_PROPERTY;
305
secAction = nsIXPCSecurityManager::ACCESS_SET_PROPERTY;
308
NS_ASSERTION(0,"bad value");
311
jsval name = member->GetName();
313
nsIXPCSecurityManager* sm = xpcc->GetAppropriateSecurityManager(secFlag);
314
XPCWrappedNative* wrapper = ccx.GetWrapper();
315
if(sm && NS_FAILED(sm->CanAccess(secAction, &ccx, ccx,
316
ccx.GetFlattenedJSObject(),
317
wrapper->GetIdentityObject(),
318
wrapper->GetClassInfo(), name,
319
wrapper->GetSecurityInfoAddr())))
321
// the security manager vetoed. It should have set an exception.
325
IDispatch * pObj = NS_REINTERPRET_CAST(IDispatch*,
326
ccx.GetTearOff()->GetNative());
327
PRUint32 args = member->GetParamCount();
329
// Make sure setter has one argument
330
if(mode == CALL_SETTER)
332
// Allow for optional parameters. We'll let COM handle the error if there
333
// are not enough parameters
336
XPCDispParams * params = new XPCDispParams(args);
338
// If this is a setter, we just need to convert the first parameter
339
if(mode == CALL_SETTER)
341
params->SetNamedPropID();
342
if(!XPCDispConvert::JSToCOM(ccx, argv[0], params->GetParamRef(0), err))
345
return ThrowBadParam(err, 0, ccx);
348
else if(mode != CALL_GETTER) // This is a function
350
// Convert the arguments to the function
351
for(PRUint32 index = 0; index < args; ++index)
353
const XPCDispInterface::Member::ParamInfo & paramInfo = member->GetParamInfo(index);
357
if(paramInfo.IsOut())
359
if(JSVAL_IS_PRIMITIVE(val) ||
360
!OBJ_GET_PROPERTY(ccx, JSVAL_TO_OBJECT(val),
361
rt->GetStringID(XPCJSRuntime::IDX_VALUE),
365
return ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, index, ccx);
367
paramInfo.InitializeOutputParam(params->GetOutputBuffer(index), params->GetParamRef(index));
369
if(!XPCDispConvert::JSToCOM(ccx, val, params->GetParamRef(index), err, paramInfo.IsOut()))
372
return ThrowBadParam(err, index, ccx);
377
paramInfo.InitializeOutputParam(params->GetOutputBuffer(index), params->GetParamRef(index));
381
// If this is a parameterized property
382
if(member->IsParameterizedProperty())
384
// We need to get a parameterized property object to return to JS
385
// NewInstance takes ownership of params
386
if(XPCDispParamPropJSClass::NewInstance(ccx, wrapper,
391
if(!JS_IdToValue(ccx, 1, &val))
393
// This shouldn't fail
394
NS_ERROR("JS_IdToValue failed in XPCDispParamPropJSClass::NewInstance");
397
JS_SetCallReturnValue2(ccx, val);
400
// NewInstance would only fail if there was an out of memory problem
401
JS_ReportOutOfMemory(ccx);
405
JSBool retval = Dispatch(ccx, pObj, member->GetDispID(), mode, params, &val, member, rt);
406
if(retval && mode == CALL_SETTER)
408
ccx.SetRetVal(argv[0]);
419
JSBool GetMember(XPCCallContext& ccx, JSObject* funobj, XPCNativeInterface*& iface, XPCDispInterface::Member*& member)
421
// We expect funobj to be a clone, we need the real funobj.
422
JSFunction* fun = (JSFunction*) JS_GetPrivate(ccx, funobj);
425
JSObject* realFunObj = JS_GetFunctionObject(fun);
429
if(!JS_GetReservedSlot(ccx, realFunObj, 1, &val))
431
if(!JSVAL_IS_INT(val))
433
iface = NS_REINTERPRET_CAST(XPCNativeInterface*,JSVAL_TO_PRIVATE(val));
434
if(!JS_GetReservedSlot(ccx, realFunObj, 0, &val))
436
if(!JSVAL_IS_INT(val))
438
member = NS_REINTERPRET_CAST(XPCDispInterface::Member*,JSVAL_TO_PRIVATE(val));
442
// Handy macro used in callbacks below.
443
#define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper) \
447
XPCThrower::Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
450
if(!wrapper->IsValid()) \
452
XPCThrower::Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); \
458
* Callback for functions
459
* This callback is called by JS when a function on a JSObject proxying
460
* for an IDispatch instance
461
* @param cx A pointer to a JS context
462
* @param obj JS object that the parameterized property is on
463
* @param argc Number of arguments in this call
464
* @param argv The parameters passed in if any
465
* @param vp The return value
466
* @return Returns JS_TRUE if the operation succeeded
468
JSBool JS_DLL_CALLBACK
469
XPC_IDispatch_CallMethod(JSContext* cx, JSObject* obj, uintN argc,
470
jsval* argv, jsval* vp)
472
NS_ASSERTION(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION, "bad function");
473
JSObject* funobj = JSVAL_TO_OBJECT(argv[-2]);
474
XPCCallContext ccx(JS_CALLER, cx, obj, funobj, 0, argc, argv, vp);
475
XPCWrappedNative* wrapper = ccx.GetWrapper();
476
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
477
ccx.SetArgsAndResultPtr(argc, argv, vp);
479
XPCDispInterface::Member* member;
480
XPCNativeInterface* iface;
484
GetMember(ccx, funobj, iface, member);
485
NS_ASSERTION(ok, "GetMember faild in XPC_IDispatch_CallMethod");
486
ccx.SetIDispatchInfo(iface, member);
488
return XPCDispObject::Invoke(ccx, XPCDispObject::CALL_METHOD);
492
* Callback for properties
493
* This callback is called by JS when a property is set or retrieved on a
494
* JSObject proxying for an IDispatch instance
495
* @param cx A pointer to a JS context
496
* @param obj JS object that the parameterized property is on
497
* @param argc Number of arguments in this call
498
* @param argv The parameters passed in if any
499
* @param vp The return value
500
* @return Returns JS_TRUE if the operation succeeded
502
JSBool JS_DLL_CALLBACK
503
XPC_IDispatch_GetterSetter(JSContext *cx, JSObject *obj, uintN argc,
504
jsval *argv, jsval *vp)
506
NS_ASSERTION(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION, "bad function");
507
JSObject* funobj = JSVAL_TO_OBJECT(argv[-2]);
509
XPCCallContext ccx(JS_CALLER, cx, obj, funobj);
510
XPCWrappedNative* wrapper = ccx.GetWrapper();
511
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
513
ccx.SetArgsAndResultPtr(argc, argv, vp);
514
XPCDispInterface::Member* member;
515
XPCNativeInterface* iface;
519
GetMember(ccx, funobj, iface, member);
520
NS_ASSERTION(ok, "GetMember faild in XPC_IDispatch_CallMethod");
522
ccx.SetIDispatchInfo(iface, member);
523
return XPCDispObject::Invoke(ccx, argc != 0 ? XPCDispObject::CALL_SETTER : XPCDispObject::CALL_GETTER);