Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /* Sharable code and data for wrapper around JSObjects. */
8 :
9 : #include "xpcprivate.h"
10 : #include "jsprf.h"
11 : #include "nsArrayEnumerator.h"
12 : #include "nsContentUtils.h"
13 : #include "nsIScriptError.h"
14 : #include "nsWrapperCache.h"
15 : #include "AccessCheck.h"
16 : #include "nsJSUtils.h"
17 : #include "mozilla/Attributes.h"
18 : #include "mozilla/dom/BindingUtils.h"
19 : #include "mozilla/dom/DOMException.h"
20 : #include "mozilla/dom/DOMExceptionBinding.h"
21 : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
22 :
23 : #include "jsapi.h"
24 : #include "jsfriendapi.h"
25 :
26 : using namespace xpc;
27 : using namespace JS;
28 : using namespace mozilla;
29 : using namespace mozilla::dom;
30 :
31 7309 : NS_IMPL_ISUPPORTS(nsXPCWrappedJSClass, nsIXPCWrappedJSClass)
32 :
33 : // the value of this variable is never used - we use its address as a sentinel
34 : static uint32_t zero_methods_descriptor;
35 :
36 2341 : bool AutoScriptEvaluate::StartEvaluating(HandleObject scope)
37 : {
38 2341 : NS_PRECONDITION(!mEvaluated, "AutoScriptEvaluate::Evaluate should only be called once");
39 :
40 2341 : if (!mJSContext)
41 0 : return true;
42 :
43 2341 : mEvaluated = true;
44 :
45 2341 : JS_BeginRequest(mJSContext);
46 2341 : mAutoCompartment.emplace(mJSContext, scope);
47 :
48 : // Saving the exception state keeps us from interfering with another script
49 : // that may also be running on this context. This occurred first with the
50 : // js debugger, as described in
51 : // http://bugzilla.mozilla.org/show_bug.cgi?id=88130 but presumably could
52 : // show up in any situation where a script calls into a wrapped js component
53 : // on the same context, while the context has a nonzero exception state.
54 2341 : mState.emplace(mJSContext);
55 :
56 2341 : return true;
57 : }
58 :
59 4680 : AutoScriptEvaluate::~AutoScriptEvaluate()
60 : {
61 2340 : if (!mJSContext || !mEvaluated)
62 : return;
63 2340 : mState->restore();
64 :
65 2340 : JS_EndRequest(mJSContext);
66 2340 : }
67 :
68 : // It turns out that some errors may be not worth reporting. So, this
69 : // function is factored out to manage that.
70 4 : bool xpc_IsReportableErrorCode(nsresult code)
71 : {
72 4 : if (NS_SUCCEEDED(code))
73 0 : return false;
74 :
75 4 : switch (code) {
76 : // Error codes that we don't want to report as errors...
77 : // These generally indicate bad interface design AFAIC.
78 : case NS_ERROR_FACTORY_REGISTER_AGAIN:
79 : case NS_BASE_STREAM_WOULD_BLOCK:
80 0 : return false;
81 : default:
82 4 : return true;
83 : }
84 : }
85 :
86 : // A little stack-based RAII class to help management of the XPCJSContext
87 : // PendingResult.
88 : class MOZ_STACK_CLASS AutoSavePendingResult {
89 : public:
90 679 : explicit AutoSavePendingResult(XPCJSContext* xpccx) :
91 679 : mXPCContext(xpccx)
92 : {
93 : // Save any existing pending result and reset to NS_OK for this invocation.
94 679 : mSavedResult = xpccx->GetPendingResult();
95 679 : xpccx->SetPendingResult(NS_OK);
96 679 : }
97 1356 : ~AutoSavePendingResult() {
98 678 : mXPCContext->SetPendingResult(mSavedResult);
99 678 : }
100 : private:
101 : XPCJSContext* mXPCContext;
102 : nsresult mSavedResult;
103 : };
104 :
105 : // static
106 : already_AddRefed<nsXPCWrappedJSClass>
107 1415 : nsXPCWrappedJSClass::GetNewOrUsed(JSContext* cx, REFNSIID aIID, bool allowNonScriptable)
108 : {
109 1415 : XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
110 1415 : IID2WrappedJSClassMap* map = xpcrt->GetWrappedJSClassMap();
111 2830 : RefPtr<nsXPCWrappedJSClass> clasp = map->Find(aIID);
112 :
113 1415 : if (!clasp) {
114 184 : nsCOMPtr<nsIInterfaceInfo> info;
115 92 : nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
116 92 : if (info) {
117 : bool canScript, isBuiltin;
118 368 : if (NS_SUCCEEDED(info->IsScriptable(&canScript)) && (canScript || allowNonScriptable) &&
119 368 : NS_SUCCEEDED(info->IsBuiltinClass(&isBuiltin)) && !isBuiltin &&
120 92 : nsXPConnect::IsISupportsDescendant(info))
121 : {
122 184 : clasp = new nsXPCWrappedJSClass(cx, aIID, info);
123 92 : if (!clasp->mDescriptors)
124 0 : clasp = nullptr;
125 : }
126 : }
127 : }
128 2830 : return clasp.forget();
129 : }
130 :
131 92 : nsXPCWrappedJSClass::nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
132 92 : nsIInterfaceInfo* aInfo)
133 92 : : mRuntime(nsXPConnect::GetRuntimeInstance()),
134 : mInfo(aInfo),
135 : mName(nullptr),
136 : mIID(aIID),
137 184 : mDescriptors(nullptr)
138 : {
139 92 : mRuntime->GetWrappedJSClassMap()->Add(this);
140 :
141 : uint16_t methodCount;
142 92 : if (NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount))) {
143 92 : if (methodCount) {
144 92 : int wordCount = (methodCount/32)+1;
145 184 : if (nullptr != (mDescriptors = new uint32_t[wordCount])) {
146 : int i;
147 : // init flags to 0;
148 185 : for (i = wordCount-1; i >= 0; i--)
149 93 : mDescriptors[i] = 0;
150 :
151 771 : for (i = 0; i < methodCount; i++) {
152 : const nsXPTMethodInfo* info;
153 679 : if (NS_SUCCEEDED(mInfo->GetMethodInfo(i, &info)))
154 679 : SetReflectable(i, XPCConvert::IsMethodReflectable(*info));
155 : else {
156 0 : delete [] mDescriptors;
157 0 : mDescriptors = nullptr;
158 0 : break;
159 : }
160 : }
161 : }
162 : } else {
163 0 : mDescriptors = &zero_methods_descriptor;
164 : }
165 : }
166 92 : }
167 :
168 75 : nsXPCWrappedJSClass::~nsXPCWrappedJSClass()
169 : {
170 25 : if (mDescriptors && mDescriptors != &zero_methods_descriptor)
171 25 : delete [] mDescriptors;
172 25 : if (mRuntime)
173 25 : mRuntime->GetWrappedJSClassMap()->Remove(this);
174 :
175 25 : if (mName)
176 2 : free(mName);
177 75 : }
178 :
179 : JSObject*
180 1662 : nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx,
181 : JSObject* jsobjArg,
182 : REFNSIID aIID)
183 : {
184 3324 : RootedObject jsobj(cx, jsobjArg);
185 : JSObject* id;
186 3324 : RootedValue retval(cx);
187 3324 : RootedObject retObj(cx);
188 1662 : bool success = false;
189 3324 : RootedValue fun(cx);
190 :
191 : // In bug 503926, we added a security check to make sure that we don't
192 : // invoke content QI functions. In the modern world, this is probably
193 : // unnecessary, because invoking QI involves passing an IID object to
194 : // content, which will fail. But we do a belt-and-suspenders check to
195 : // make sure that content can never trigger the rat's nest of code below.
196 : // Once we completely turn off XPConnect for the web, this can definitely
197 : // go away.
198 3324 : if (!AccessCheck::isChrome(jsobj) ||
199 1662 : !AccessCheck::isChrome(js::UncheckedUnwrap(jsobj)))
200 : {
201 0 : return nullptr;
202 : }
203 :
204 : // OK, it looks like we'll be calling into JS code.
205 3324 : AutoScriptEvaluate scriptEval(cx);
206 :
207 : // XXX we should install an error reporter that will send reports to
208 : // the JS error console service.
209 1662 : if (!scriptEval.StartEvaluating(jsobj))
210 0 : return nullptr;
211 :
212 : // check upfront for the existence of the function property
213 1662 : HandleId funid = mRuntime->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE);
214 1662 : if (!JS_GetPropertyById(cx, jsobj, funid, &fun) || fun.isPrimitive())
215 882 : return nullptr;
216 :
217 : // Ensure that we are asking for a scriptable interface.
218 : // NB: It's important for security that this check is here rather
219 : // than later, since it prevents untrusted objects from implementing
220 : // some interfaces in JS and aggregating a trusted object to
221 : // implement intentionally (for security) unscriptable interfaces.
222 : // We so often ask for nsISupports that we can short-circuit the test...
223 780 : if (!aIID.Equals(NS_GET_IID(nsISupports))) {
224 299 : bool allowNonScriptable = mozilla::jsipc::IsWrappedCPOW(jsobj);
225 :
226 516 : nsCOMPtr<nsIInterfaceInfo> info;
227 299 : nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
228 299 : if (!info)
229 82 : return nullptr;
230 : bool canScript, isBuiltin;
231 868 : if (NS_FAILED(info->IsScriptable(&canScript)) || (!canScript && !allowNonScriptable) ||
232 651 : NS_FAILED(info->IsBuiltinClass(&isBuiltin)) || isBuiltin)
233 0 : return nullptr;
234 : }
235 :
236 698 : id = xpc_NewIDObject(cx, jsobj, aIID);
237 698 : if (id) {
238 : // Throwing NS_NOINTERFACE is the prescribed way to fail QI from JS. It
239 : // is not an exception that is ever worth reporting, but we don't want
240 : // to eat all exceptions either.
241 :
242 : {
243 1396 : RootedValue arg(cx, JS::ObjectValue(*id));
244 698 : success = JS_CallFunctionValue(cx, jsobj, fun, HandleValueArray(arg), &retval);
245 : }
246 :
247 698 : if (!success && JS_IsExceptionPending(cx)) {
248 170 : RootedValue jsexception(cx, NullValue());
249 :
250 85 : if (JS_GetPendingException(cx, &jsexception)) {
251 : nsresult rv;
252 85 : if (jsexception.isObject()) {
253 : // XPConnect may have constructed an object to represent a
254 : // C++ QI failure. See if that is the case.
255 2 : JS::Rooted<JSObject*> exceptionObj(cx, &jsexception.toObject());
256 1 : Exception* e = nullptr;
257 1 : UNWRAP_OBJECT(Exception, &exceptionObj, e);
258 :
259 2 : if (e &&
260 2 : NS_SUCCEEDED(e->GetResult(&rv)) &&
261 1 : rv == NS_NOINTERFACE) {
262 1 : JS_ClearPendingException(cx);
263 : }
264 84 : } else if (jsexception.isNumber()) {
265 : // JS often throws an nsresult.
266 84 : if (jsexception.isDouble())
267 : // Visual Studio 9 doesn't allow casting directly from
268 : // a double to an enumeration type, contrary to
269 : // 5.2.9(10) of C++11, so add an intermediate cast.
270 84 : rv = (nsresult)(uint32_t)(jsexception.toDouble());
271 : else
272 0 : rv = (nsresult)(jsexception.toInt32());
273 :
274 84 : if (rv == NS_NOINTERFACE)
275 84 : JS_ClearPendingException(cx);
276 : }
277 : }
278 613 : } else if (!success) {
279 0 : NS_WARNING("QI hook ran OOMed - this is probably a bug!");
280 : }
281 : }
282 :
283 698 : if (success)
284 613 : success = JS_ValueToObject(cx, retval, &retObj);
285 :
286 698 : return success ? retObj.get() : nullptr;
287 : }
288 :
289 : /***************************************************************************/
290 :
291 : static bool
292 0 : GetNamedPropertyAsVariantRaw(XPCCallContext& ccx,
293 : HandleObject aJSObj,
294 : HandleId aName,
295 : nsIVariant** aResult,
296 : nsresult* pErr)
297 : {
298 0 : nsXPTType type = nsXPTType((uint8_t)TD_INTERFACE_TYPE);
299 0 : RootedValue val(ccx);
300 :
301 0 : return JS_GetPropertyById(ccx, aJSObj, aName, &val) &&
302 0 : XPCConvert::JSData2Native(aResult, val, type,
303 0 : &NS_GET_IID(nsIVariant), pErr);
304 : }
305 :
306 : // static
307 : nsresult
308 0 : nsXPCWrappedJSClass::GetNamedPropertyAsVariant(XPCCallContext& ccx,
309 : JSObject* aJSObjArg,
310 : const nsAString& aName,
311 : nsIVariant** aResult)
312 : {
313 0 : JSContext* cx = ccx.GetJSContext();
314 0 : RootedObject aJSObj(cx, aJSObjArg);
315 :
316 0 : AutoScriptEvaluate scriptEval(cx);
317 0 : if (!scriptEval.StartEvaluating(aJSObj))
318 0 : return NS_ERROR_FAILURE;
319 :
320 : // Wrap the string in a Value after the AutoScriptEvaluate, so that the
321 : // resulting value ends up in the correct compartment.
322 : nsStringBuffer* buf;
323 0 : RootedValue value(cx);
324 0 : if (!XPCStringConvert::ReadableToJSVal(ccx, aName, &buf, &value))
325 0 : return NS_ERROR_OUT_OF_MEMORY;
326 0 : if (buf)
327 0 : buf->AddRef();
328 :
329 0 : RootedId id(cx);
330 0 : nsresult rv = NS_OK;
331 0 : if (!JS_ValueToId(cx, value, &id) ||
332 0 : !GetNamedPropertyAsVariantRaw(ccx, aJSObj, id, aResult, &rv)) {
333 0 : if (NS_FAILED(rv))
334 0 : return rv;
335 0 : return NS_ERROR_FAILURE;
336 : }
337 0 : return NS_OK;
338 : }
339 :
340 : /***************************************************************************/
341 :
342 : // static
343 : nsresult
344 0 : nsXPCWrappedJSClass::BuildPropertyEnumerator(XPCCallContext& ccx,
345 : JSObject* aJSObjArg,
346 : nsISimpleEnumerator** aEnumerate)
347 : {
348 0 : JSContext* cx = ccx.GetJSContext();
349 0 : RootedObject aJSObj(cx, aJSObjArg);
350 :
351 0 : AutoScriptEvaluate scriptEval(cx);
352 0 : if (!scriptEval.StartEvaluating(aJSObj))
353 0 : return NS_ERROR_FAILURE;
354 :
355 0 : Rooted<IdVector> idArray(cx, IdVector(cx));
356 0 : if (!JS_Enumerate(cx, aJSObj, &idArray))
357 0 : return NS_ERROR_FAILURE;
358 :
359 0 : nsCOMArray<nsIProperty> propertyArray(idArray.length());
360 0 : RootedId idName(cx);
361 0 : for (size_t i = 0; i < idArray.length(); i++) {
362 0 : idName = idArray[i];
363 :
364 0 : nsCOMPtr<nsIVariant> value;
365 : nsresult rv;
366 0 : if (!GetNamedPropertyAsVariantRaw(ccx, aJSObj, idName,
367 0 : getter_AddRefs(value), &rv)) {
368 0 : if (NS_FAILED(rv))
369 0 : return rv;
370 0 : return NS_ERROR_FAILURE;
371 : }
372 :
373 0 : RootedValue jsvalName(cx);
374 0 : if (!JS_IdToValue(cx, idName, &jsvalName))
375 0 : return NS_ERROR_FAILURE;
376 :
377 0 : JSString* name = ToString(cx, jsvalName);
378 0 : if (!name)
379 0 : return NS_ERROR_FAILURE;
380 :
381 0 : nsAutoJSString autoStr;
382 0 : if (!autoStr.init(cx, name))
383 0 : return NS_ERROR_FAILURE;
384 :
385 : nsCOMPtr<nsIProperty> property =
386 0 : new xpcProperty(autoStr.get(), (uint32_t)autoStr.Length(), value);
387 :
388 0 : if (!propertyArray.AppendObject(property))
389 0 : return NS_ERROR_FAILURE;
390 : }
391 :
392 0 : return NS_NewArrayEnumerator(aEnumerate, propertyArray);
393 : }
394 :
395 : /***************************************************************************/
396 :
397 0 : NS_IMPL_ISUPPORTS(xpcProperty, nsIProperty)
398 :
399 0 : xpcProperty::xpcProperty(const char16_t* aName, uint32_t aNameLen,
400 0 : nsIVariant* aValue)
401 0 : : mName(aName, aNameLen), mValue(aValue)
402 : {
403 0 : }
404 :
405 0 : NS_IMETHODIMP xpcProperty::GetName(nsAString & aName)
406 : {
407 0 : aName.Assign(mName);
408 0 : return NS_OK;
409 : }
410 :
411 0 : NS_IMETHODIMP xpcProperty::GetValue(nsIVariant * *aValue)
412 : {
413 0 : nsCOMPtr<nsIVariant> rval = mValue;
414 0 : rval.forget(aValue);
415 0 : return NS_OK;
416 : }
417 :
418 : /***************************************************************************/
419 : // This 'WrappedJSIdentity' class and singleton allow us to figure out if
420 : // any given nsISupports* is implemented by a WrappedJS object. This is done
421 : // using a QueryInterface call on the interface pointer with our ID. If
422 : // that call returns NS_OK and the pointer is to our singleton, then the
423 : // interface must be implemented by a WrappedJS object. NOTE: the
424 : // 'WrappedJSIdentity' object is not a real XPCOM object and should not be
425 : // used for anything else (hence it is declared in this implementation file).
426 :
427 : // {5C5C3BB0-A9BA-11d2-BA64-00805F8A5DD7}
428 : #define NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID \
429 : { 0x5c5c3bb0, 0xa9ba, 0x11d2, \
430 : { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
431 :
432 : class WrappedJSIdentity
433 : {
434 : // no instance methods...
435 : public:
436 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID)
437 :
438 0 : static void* GetSingleton()
439 : {
440 : static WrappedJSIdentity* singleton = nullptr;
441 0 : if (!singleton)
442 0 : singleton = new WrappedJSIdentity();
443 0 : return (void*) singleton;
444 : }
445 : };
446 :
447 : NS_DEFINE_STATIC_IID_ACCESSOR(WrappedJSIdentity,
448 : NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID)
449 :
450 : /***************************************************************************/
451 :
452 : // static
453 : bool
454 0 : nsXPCWrappedJSClass::IsWrappedJS(nsISupports* aPtr)
455 : {
456 : void* result;
457 0 : NS_PRECONDITION(aPtr, "null pointer");
458 0 : return aPtr &&
459 0 : NS_OK == aPtr->QueryInterface(NS_GET_IID(WrappedJSIdentity), &result) &&
460 0 : result == WrappedJSIdentity::GetSingleton();
461 : }
462 :
463 : NS_IMETHODIMP
464 2183 : nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
465 : REFNSIID aIID,
466 : void** aInstancePtr)
467 : {
468 2183 : if (aIID.Equals(NS_GET_IID(nsIXPConnectJSObjectHolder))) {
469 0 : NS_ADDREF(self);
470 0 : *aInstancePtr = (void*) static_cast<nsIXPConnectJSObjectHolder*>(self);
471 0 : return NS_OK;
472 : }
473 :
474 : // Objects internal to xpconnect are the only objects that even know *how*
475 : // to ask for this iid. And none of them bother refcounting the thing.
476 2183 : if (aIID.Equals(NS_GET_IID(WrappedJSIdentity))) {
477 : // asking to find out if this is a wrapper object
478 0 : *aInstancePtr = WrappedJSIdentity::GetSingleton();
479 0 : return NS_OK;
480 : }
481 :
482 2183 : if (aIID.Equals(NS_GET_IID(nsIPropertyBag))) {
483 : // We only want to expose one implementation from our aggregate.
484 0 : nsXPCWrappedJS* root = self->GetRootWrapper();
485 :
486 0 : if (!root->IsValid()) {
487 0 : *aInstancePtr = nullptr;
488 0 : return NS_NOINTERFACE;
489 : }
490 :
491 0 : NS_ADDREF(root);
492 0 : *aInstancePtr = (void*) static_cast<nsIPropertyBag*>(root);
493 0 : return NS_OK;
494 : }
495 :
496 : // We can't have a cached wrapper.
497 2183 : if (aIID.Equals(NS_GET_IID(nsWrapperCache))) {
498 119 : *aInstancePtr = nullptr;
499 119 : return NS_NOINTERFACE;
500 : }
501 :
502 : // QI on an XPCWrappedJS can run script, so we need an AutoEntryScript.
503 : // This is inherently Gecko-specific.
504 : // We check both nativeGlobal and nativeGlobal->GetGlobalJSObject() even
505 : // though we have derived nativeGlobal from the JS global, because we know
506 : // there are cases where this can happen. See bug 1094953.
507 : nsIGlobalObject* nativeGlobal =
508 2064 : NativeGlobal(js::GetGlobalForObjectCrossCompartment(self->GetJSObject()));
509 2064 : NS_ENSURE_TRUE(nativeGlobal, NS_ERROR_FAILURE);
510 2064 : NS_ENSURE_TRUE(nativeGlobal->GetGlobalJSObject(), NS_ERROR_FAILURE);
511 : AutoEntryScript aes(nativeGlobal, "XPCWrappedJS QueryInterface",
512 4128 : /* aIsMainThread = */ true);
513 4128 : XPCCallContext ccx(aes.cx());
514 2064 : if (!ccx.IsValid()) {
515 0 : *aInstancePtr = nullptr;
516 0 : return NS_NOINTERFACE;
517 : }
518 :
519 : // We support nsISupportsWeakReference iff the root wrapped JSObject
520 : // claims to support it in its QueryInterface implementation.
521 2064 : if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) {
522 : // We only want to expose one implementation from our aggregate.
523 111 : nsXPCWrappedJS* root = self->GetRootWrapper();
524 :
525 : // Fail if JSObject doesn't claim support for nsISupportsWeakReference
526 222 : if (!root->IsValid() ||
527 111 : !CallQueryInterfaceOnJSObject(ccx, root->GetJSObject(), aIID)) {
528 0 : *aInstancePtr = nullptr;
529 0 : return NS_NOINTERFACE;
530 : }
531 :
532 111 : NS_ADDREF(root);
533 111 : *aInstancePtr = (void*) static_cast<nsISupportsWeakReference*>(root);
534 111 : return NS_OK;
535 : }
536 :
537 : // Checks for any existing wrapper explicitly constructed for this iid.
538 : // This includes the current 'self' wrapper. This also deals with the
539 : // nsISupports case (for which it returns mRoot).
540 : // Also check if asking for an interface from which one of our wrappers inherits.
541 1953 : if (nsXPCWrappedJS* sibling = self->FindOrFindInherited(aIID)) {
542 1661 : NS_ADDREF(sibling);
543 1661 : *aInstancePtr = sibling->GetXPTCStub();
544 1661 : return NS_OK;
545 : }
546 :
547 : // Check if the desired interface is a function interface. If so, we don't
548 : // want to QI, because the function almost certainly doesn't have a QueryInterface
549 : // property, and doesn't need one.
550 292 : bool isFunc = false;
551 584 : nsCOMPtr<nsIInterfaceInfo> info;
552 292 : nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
553 292 : if (info && NS_SUCCEEDED(info->IsFunction(&isFunc)) && isFunc) {
554 38 : RefPtr<nsXPCWrappedJS> wrapper;
555 38 : RootedObject obj(RootingCx(), self->GetJSObject());
556 19 : nsresult rv = nsXPCWrappedJS::GetNewOrUsed(obj, aIID, getter_AddRefs(wrapper));
557 :
558 : // Do the same thing we do for the "check for any existing wrapper" case above.
559 19 : if (NS_SUCCEEDED(rv) && wrapper) {
560 19 : *aInstancePtr = wrapper.forget().take()->GetXPTCStub();
561 : }
562 19 : return rv;
563 : }
564 :
565 : // else we do the more expensive stuff...
566 :
567 : // check if the JSObject claims to implement this interface
568 546 : RootedObject jsobj(ccx, CallQueryInterfaceOnJSObject(ccx, self->GetJSObject(),
569 546 : aIID));
570 273 : if (jsobj) {
571 : // We can't use XPConvert::JSObject2NativeInterface() here
572 : // since that can find a XPCWrappedNative directly on the
573 : // proto chain, and we don't want that here. We need to find
574 : // the actual JS object that claimed it supports the interface
575 : // we're looking for or we'll potentially bypass security
576 : // checks etc by calling directly through to a native found on
577 : // the prototype chain.
578 : //
579 : // Instead, simply do the nsXPCWrappedJS part of
580 : // XPConvert::JSObject2NativeInterface() here to make sure we
581 : // get a new (or used) nsXPCWrappedJS.
582 21 : RefPtr<nsXPCWrappedJS> wrapper;
583 21 : nsresult rv = nsXPCWrappedJS::GetNewOrUsed(jsobj, aIID, getter_AddRefs(wrapper));
584 21 : if (NS_SUCCEEDED(rv) && wrapper) {
585 : // We need to go through the QueryInterface logic to make
586 : // this return the right thing for the various 'special'
587 : // interfaces; e.g. nsIPropertyBag.
588 21 : rv = wrapper->QueryInterface(aIID, aInstancePtr);
589 21 : return rv;
590 : }
591 : }
592 :
593 : // else...
594 : // no can do
595 252 : *aInstancePtr = nullptr;
596 252 : return NS_NOINTERFACE;
597 : }
598 :
599 : JSObject*
600 1278 : nsXPCWrappedJSClass::GetRootJSObject(JSContext* cx, JSObject* aJSObjArg)
601 : {
602 2556 : RootedObject aJSObj(cx, aJSObjArg);
603 1278 : JSObject* result = CallQueryInterfaceOnJSObject(cx, aJSObj,
604 2556 : NS_GET_IID(nsISupports));
605 1278 : if (!result)
606 797 : result = aJSObj;
607 1278 : JSObject* inner = js::UncheckedUnwrap(result);
608 1278 : if (inner)
609 1278 : return inner;
610 0 : return result;
611 : }
612 :
613 : bool
614 6 : nsXPCWrappedJSClass::GetArraySizeFromParam(JSContext* cx,
615 : const XPTMethodDescriptor* method,
616 : const nsXPTParamInfo& param,
617 : uint16_t methodIndex,
618 : uint8_t paramIndex,
619 : nsXPTCMiniVariant* nativeParams,
620 : uint32_t* result) const
621 : {
622 : uint8_t argnum;
623 : nsresult rv;
624 :
625 6 : rv = mInfo->GetSizeIsArgNumberForParam(methodIndex, ¶m, 0, &argnum);
626 6 : if (NS_FAILED(rv))
627 0 : return false;
628 :
629 6 : const nsXPTParamInfo& arg_param = method->params[argnum];
630 :
631 : // This should be enforced by the xpidl compiler, but it's not.
632 : // See bug 695235.
633 6 : MOZ_ASSERT(arg_param.GetType().TagPart() == nsXPTType::T_U32,
634 : "size_is references parameter of invalid type.");
635 :
636 6 : if (arg_param.IsIndirect())
637 0 : *result = *(uint32_t*)nativeParams[argnum].val.p;
638 : else
639 6 : *result = nativeParams[argnum].val.u32;
640 :
641 6 : return true;
642 : }
643 :
644 : bool
645 492 : nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx,
646 : const XPTMethodDescriptor* method,
647 : const nsXPTParamInfo& param,
648 : uint16_t methodIndex,
649 : const nsXPTType& type,
650 : nsXPTCMiniVariant* nativeParams,
651 : nsID* result) const
652 : {
653 492 : uint8_t type_tag = type.TagPart();
654 :
655 492 : if (type_tag == nsXPTType::T_INTERFACE) {
656 441 : if (NS_SUCCEEDED(GetInterfaceInfo()->
657 : GetIIDForParamNoAlloc(methodIndex, ¶m, result))) {
658 441 : return true;
659 : }
660 51 : } else if (type_tag == nsXPTType::T_INTERFACE_IS) {
661 : uint8_t argnum;
662 : nsresult rv;
663 102 : rv = mInfo->GetInterfaceIsArgNumberForParam(methodIndex,
664 102 : ¶m, &argnum);
665 51 : if (NS_FAILED(rv))
666 51 : return false;
667 :
668 51 : const nsXPTParamInfo& arg_param = method->params[argnum];
669 51 : const nsXPTType& arg_type = arg_param.GetType();
670 :
671 51 : if (arg_type.TagPart() == nsXPTType::T_IID) {
672 51 : if (arg_param.IsIndirect()) {
673 0 : nsID** p = (nsID**) nativeParams[argnum].val.p;
674 0 : if (!p || !*p)
675 0 : return false;
676 0 : *result = **p;
677 : } else {
678 51 : nsID* p = (nsID*) nativeParams[argnum].val.p;
679 51 : if (!p)
680 0 : return false;
681 51 : *result = *p;
682 : }
683 51 : return true;
684 : }
685 : }
686 0 : return false;
687 : }
688 :
689 : /* static */ void
690 0 : nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type,
691 : uint32_t array_count,
692 : void** arrayp)
693 : {
694 0 : if (datum_type.IsInterfacePointer()) {
695 0 : nsISupports** pp = (nsISupports**) arrayp;
696 0 : for (uint32_t k = 0; k < array_count; k++) {
697 0 : nsISupports* p = pp[k];
698 0 : NS_IF_RELEASE(p);
699 : }
700 : } else {
701 0 : void** pp = (void**) arrayp;
702 0 : for (uint32_t k = 0; k < array_count; k++) {
703 0 : void* p = pp[k];
704 0 : if (p) free(p);
705 : }
706 : }
707 0 : }
708 :
709 : /* static */ void
710 0 : nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type,
711 : void** pp)
712 : {
713 0 : MOZ_ASSERT(pp,"null pointer");
714 0 : if (type.IsInterfacePointer()) {
715 0 : nsISupports* p = *((nsISupports**)pp);
716 0 : if (p) p->Release();
717 : } else {
718 0 : void* p = *((void**)pp);
719 0 : if (p) free(p);
720 : }
721 0 : }
722 :
723 : void
724 679 : nsXPCWrappedJSClass::CleanupOutparams(JSContext* cx, uint16_t methodIndex,
725 : const nsXPTMethodInfo* info, nsXPTCMiniVariant* nativeParams,
726 : bool inOutOnly, uint8_t n) const
727 : {
728 : // clean up any 'out' params handed in
729 2273 : for (uint8_t i = 0; i < n; i++) {
730 1594 : const nsXPTParamInfo& param = info->params[i];
731 1594 : if (!param.IsOut())
732 1360 : continue;
733 :
734 234 : const nsXPTType& type = param.GetType();
735 234 : if (!type.deprecated_IsPointer())
736 70 : continue;
737 164 : void* p = nativeParams[i].val.p;
738 164 : if (!p)
739 0 : continue;
740 :
741 : // The inOutOnly flag was introduced when consolidating two very
742 : // similar code paths in CallMethod in bug 1175513. I don't know
743 : // if and why the difference is necessary.
744 164 : if (!inOutOnly || param.IsIn()) {
745 0 : if (type.IsArray()) {
746 0 : void** pp = *static_cast<void***>(p);
747 0 : if (pp) {
748 : // we need to get the array length and iterate the items
749 : uint32_t array_count;
750 0 : nsXPTType datum_type;
751 :
752 0 : if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m,
753 0 : 1, &datum_type)) &&
754 0 : datum_type.deprecated_IsPointer() &&
755 0 : GetArraySizeFromParam(cx, info, param, methodIndex,
756 0 : i, nativeParams, &array_count) &&
757 0 : array_count) {
758 :
759 0 : CleanupPointerArray(datum_type, array_count, pp);
760 : }
761 :
762 : // always release the array if it is inout
763 0 : free(pp);
764 : }
765 : } else {
766 0 : CleanupPointerTypeObject(type, static_cast<void**>(p));
767 : }
768 : }
769 164 : *static_cast<void**>(p) = nullptr;
770 : }
771 679 : }
772 :
773 : nsresult
774 4 : nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
775 : AutoEntryScript& aes,
776 : const char * aPropertyName,
777 : const char * anInterfaceName,
778 : nsIException* aSyntheticException)
779 : {
780 4 : JSContext * cx = ccx.GetJSContext();
781 4 : MOZ_ASSERT(cx == aes.cx());
782 8 : nsCOMPtr<nsIException> xpc_exception = aSyntheticException;
783 : /* this one would be set by our error reporter */
784 :
785 4 : XPCJSContext* xpccx = ccx.GetContext();
786 :
787 : // Get this right away in case we do something below to cause JS code
788 : // to run.
789 4 : nsresult pending_result = xpccx->GetPendingResult();
790 :
791 8 : RootedValue js_exception(cx);
792 4 : bool is_js_exception = JS_GetPendingException(cx, &js_exception);
793 :
794 : /* JS might throw an expection whether the reporter was called or not */
795 4 : if (is_js_exception) {
796 0 : if (!xpc_exception)
797 0 : XPCConvert::JSValToXPCException(&js_exception, anInterfaceName,
798 : aPropertyName,
799 0 : getter_AddRefs(xpc_exception));
800 :
801 : /* cleanup and set failed even if we can't build an exception */
802 0 : if (!xpc_exception) {
803 0 : xpccx->SetPendingException(nullptr); // XXX necessary?
804 : }
805 : }
806 :
807 : // Clear the pending exception now, because xpc_exception might be JS-
808 : // implemented, so invoking methods on it might re-enter JS, which we can't
809 : // do with an exception on the stack.
810 4 : aes.ClearException();
811 :
812 4 : if (xpc_exception) {
813 : nsresult e_result;
814 4 : if (NS_SUCCEEDED(xpc_exception->GetResult(&e_result))) {
815 : // Figure out whether or not we should report this exception.
816 4 : bool reportable = xpc_IsReportableErrorCode(e_result);
817 4 : if (reportable) {
818 : // Ugly special case for GetInterface. It's "special" in the
819 : // same way as QueryInterface in that a failure is not
820 : // exceptional and shouldn't be reported. We have to do this
821 : // check here instead of in xpcwrappedjs (like we do for QI) to
822 : // avoid adding extra code to all xpcwrappedjs objects.
823 4 : if (e_result == NS_ERROR_NO_INTERFACE &&
824 0 : !strcmp(anInterfaceName, "nsIInterfaceRequestor") &&
825 0 : !strcmp(aPropertyName, "getInterface")) {
826 0 : reportable = false;
827 : }
828 :
829 : // More special case, see bug 877760.
830 4 : if (e_result == NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) {
831 4 : reportable = false;
832 : }
833 : }
834 :
835 : // Try to use the error reporter set on the context to handle this
836 : // error if it came from a JS exception.
837 4 : if (reportable && is_js_exception)
838 : {
839 : // Note that we cleared the exception above, so we need to set it again,
840 : // just so that we can tell the JS engine to pass it back to us via the
841 : // error reporting callback. This is all very dumb.
842 0 : JS_SetPendingException(cx, js_exception);
843 0 : aes.ReportException();
844 0 : reportable = false;
845 : }
846 :
847 4 : if (reportable) {
848 0 : if (nsContentUtils::DOMWindowDumpEnabled()) {
849 : static const char line[] =
850 : "************************************************************\n";
851 : static const char preamble[] =
852 : "* Call to xpconnect wrapped JSObject produced this error: *\n";
853 : static const char cant_get_text[] =
854 : "FAILED TO GET TEXT FROM EXCEPTION\n";
855 :
856 0 : fputs(line, stdout);
857 0 : fputs(preamble, stdout);
858 0 : nsCString text;
859 0 : if (NS_SUCCEEDED(xpc_exception->ToString(cx, text)) &&
860 0 : !text.IsEmpty()) {
861 0 : fputs(text.get(), stdout);
862 0 : fputs("\n", stdout);
863 : } else
864 0 : fputs(cant_get_text, stdout);
865 0 : fputs(line, stdout);
866 : }
867 :
868 : // Log the exception to the JS Console, so that users can do
869 : // something with it.
870 : nsCOMPtr<nsIConsoleService> consoleService
871 0 : (do_GetService(XPC_CONSOLE_CONTRACTID));
872 0 : if (nullptr != consoleService) {
873 : nsresult rv;
874 0 : nsCOMPtr<nsIScriptError> scriptError;
875 0 : nsCOMPtr<nsISupports> errorData;
876 0 : rv = xpc_exception->GetData(getter_AddRefs(errorData));
877 0 : if (NS_SUCCEEDED(rv))
878 0 : scriptError = do_QueryInterface(errorData);
879 :
880 0 : if (nullptr == scriptError) {
881 : // No luck getting one from the exception, so
882 : // try to cook one up.
883 0 : scriptError = do_CreateInstance(XPC_SCRIPT_ERROR_CONTRACTID);
884 0 : if (nullptr != scriptError) {
885 0 : nsCString newMessage;
886 0 : rv = xpc_exception->ToString(cx, newMessage);
887 0 : if (NS_SUCCEEDED(rv)) {
888 : // try to get filename, lineno from the first
889 : // stack frame location.
890 0 : int32_t lineNumber = 0;
891 0 : nsString sourceName;
892 :
893 0 : nsCOMPtr<nsIStackFrame> location;
894 0 : xpc_exception->
895 0 : GetLocation(getter_AddRefs(location));
896 0 : if (location) {
897 : // Get line number w/o checking; 0 is ok.
898 0 : location->GetLineNumber(cx, &lineNumber);
899 :
900 : // get a filename.
901 0 : rv = location->GetFilename(cx, sourceName);
902 : }
903 :
904 0 : rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(newMessage),
905 : sourceName,
906 0 : EmptyString(),
907 : lineNumber, 0, 0,
908 : "XPConnect JavaScript",
909 0 : nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
910 0 : if (NS_FAILED(rv))
911 0 : scriptError = nullptr;
912 : }
913 : }
914 : }
915 0 : if (nullptr != scriptError)
916 0 : consoleService->LogMessage(scriptError);
917 : }
918 : }
919 : // Whether or not it passes the 'reportable' test, it might
920 : // still be an error and we have to do the right thing here...
921 4 : if (NS_FAILED(e_result)) {
922 4 : xpccx->SetPendingException(xpc_exception);
923 4 : return e_result;
924 : }
925 : }
926 : } else {
927 : // see if JS code signaled failure result without throwing exception
928 0 : if (NS_FAILED(pending_result)) {
929 0 : return pending_result;
930 : }
931 : }
932 0 : return NS_ERROR_FAILURE;
933 : }
934 :
935 : NS_IMETHODIMP
936 679 : nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
937 : const XPTMethodDescriptor* info_,
938 : nsXPTCMiniVariant* nativeParams)
939 : {
940 679 : Value* sp = nullptr;
941 679 : Value* argv = nullptr;
942 : uint8_t i;
943 679 : nsresult retval = NS_ERROR_FAILURE;
944 : bool success;
945 679 : bool readyToDoTheCall = false;
946 : nsID param_iid;
947 679 : const nsXPTMethodInfo* info = static_cast<const nsXPTMethodInfo*>(info_);
948 679 : const char* name = info->name;
949 : bool foundDependentParam;
950 :
951 : // Make sure not to set the callee on ccx until after we've gone through
952 : // the whole nsIXPCFunctionThisTranslator bit. That code uses ccx to
953 : // convert natives to JSObjects, but we do NOT plan to pass those JSObjects
954 : // to our real callee.
955 : //
956 : // We're about to call into script via an XPCWrappedJS, so we need an
957 : // AutoEntryScript. This is probably Gecko-specific at this point, and
958 : // definitely will be when we turn off XPConnect for the web.
959 : nsIGlobalObject* nativeGlobal =
960 679 : NativeGlobal(js::GetGlobalForObjectCrossCompartment(wrapper->GetJSObject()));
961 : AutoEntryScript aes(nativeGlobal, "XPCWrappedJS method call",
962 1357 : /* aIsMainThread = */ true);
963 1357 : XPCCallContext ccx(aes.cx());
964 679 : if (!ccx.IsValid())
965 0 : return retval;
966 :
967 679 : JSContext* cx = ccx.GetJSContext();
968 :
969 679 : if (!cx || !IsReflectable(methodIndex))
970 0 : return NS_ERROR_FAILURE;
971 :
972 : // [implicit_jscontext] and [optional_argc] have a different calling
973 : // convention, which we don't support for JS-implemented components.
974 679 : if (info->WantsOptArgc() || info->WantsContext()) {
975 : const char* str = "IDL methods marked with [implicit_jscontext] "
976 0 : "or [optional_argc] may not be implemented in JS";
977 : // Throw and warn for good measure.
978 0 : JS_ReportErrorASCII(cx, "%s", str);
979 0 : NS_WARNING(str);
980 0 : return CheckForException(ccx, aes, name, GetInterfaceName());
981 : }
982 :
983 1357 : RootedValue fval(cx);
984 1357 : RootedObject obj(cx, wrapper->GetJSObject());
985 1357 : RootedObject thisObj(cx, obj);
986 :
987 1357 : JSAutoCompartment ac(cx, obj);
988 :
989 1357 : AutoValueVector args(cx);
990 1357 : AutoScriptEvaluate scriptEval(cx);
991 :
992 679 : XPCJSContext* xpccx = ccx.GetContext();
993 1357 : AutoSavePendingResult apr(xpccx);
994 :
995 : // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
996 679 : uint8_t paramCount = info->num_args;
997 969 : uint8_t argc = paramCount -
998 1351 : (paramCount && XPT_PD_IS_RETVAL(info->params[paramCount-1].flags) ? 1 : 0);
999 :
1000 679 : if (!scriptEval.StartEvaluating(obj))
1001 0 : goto pre_call_clean_up;
1002 :
1003 679 : xpccx->SetPendingException(nullptr);
1004 :
1005 : // We use js_Invoke so that the gcthings we use as args will be rooted by
1006 : // the engine as we do conversions and prepare to do the function call.
1007 :
1008 : // setup stack
1009 :
1010 : // if this isn't a function call then we don't need to push extra stuff
1011 679 : if (!(XPT_MD_IS_SETTER(info->flags) || XPT_MD_IS_GETTER(info->flags))) {
1012 : // We get fval before allocating the stack to avoid gc badness that can
1013 : // happen if the GetProperty call leaves our request and the gc runs
1014 : // while the stack we allocate contains garbage.
1015 :
1016 : // If the interface is marked as a [function] then we will assume that
1017 : // our JSObject is a function and not an object with a named method.
1018 :
1019 : bool isFunction;
1020 581 : if (NS_FAILED(mInfo->IsFunction(&isFunction)))
1021 0 : goto pre_call_clean_up;
1022 :
1023 : // In the xpidl [function] case we are making sure now that the
1024 : // JSObject is callable. If it is *not* callable then we silently
1025 : // fallback to looking up the named property...
1026 : // (because jst says he thinks this fallback is 'The Right Thing'.)
1027 : //
1028 : // In the normal (non-function) case we just lookup the property by
1029 : // name and as long as the object has such a named property we go ahead
1030 : // and try to make the call. If it turns out the named property is not
1031 : // a callable object then the JS engine will throw an error and we'll
1032 : // pass this along to the caller as an exception/result code.
1033 :
1034 581 : fval = ObjectValue(*obj);
1035 2072 : if (isFunction &&
1036 1568 : JS_TypeOfValue(ccx, fval) == JSTYPE_FUNCTION) {
1037 :
1038 : // We may need to translate the 'this' for the function object.
1039 :
1040 106 : if (paramCount) {
1041 100 : const nsXPTParamInfo& firstParam = info->params[0];
1042 100 : if (firstParam.IsIn()) {
1043 100 : const nsXPTType& firstType = firstParam.GetType();
1044 :
1045 100 : if (firstType.IsInterfacePointer()) {
1046 : nsIXPCFunctionThisTranslator* translator;
1047 :
1048 : IID2ThisTranslatorMap* map =
1049 44 : mRuntime->GetThisTranslatorMap();
1050 :
1051 44 : translator = map->Find(mIID);
1052 :
1053 44 : if (translator) {
1054 40 : nsCOMPtr<nsISupports> newThis;
1055 20 : if (NS_FAILED(translator->
1056 : TranslateThis((nsISupports*)nativeParams[0].val.p,
1057 : getter_AddRefs(newThis)))) {
1058 0 : goto pre_call_clean_up;
1059 : }
1060 20 : if (newThis) {
1061 40 : RootedValue v(cx);
1062 40 : xpcObjectHelper helper(newThis);
1063 : bool ok =
1064 40 : XPCConvert::NativeInterface2JSObject(
1065 : &v, nullptr, helper, nullptr,
1066 20 : false, nullptr);
1067 20 : if (!ok) {
1068 0 : goto pre_call_clean_up;
1069 : }
1070 20 : thisObj = v.toObjectOrNull();
1071 20 : if (!JS_WrapObject(cx, &thisObj))
1072 0 : goto pre_call_clean_up;
1073 : }
1074 : }
1075 : }
1076 : }
1077 : }
1078 : } else {
1079 475 : if (!JS_GetProperty(cx, obj, name, &fval))
1080 0 : goto pre_call_clean_up;
1081 : // XXX We really want to factor out the error reporting better and
1082 : // specifically report the failure to find a function with this name.
1083 : // This is what we do below if the property is found but is not a
1084 : // function. We just need to factor better so we can get to that
1085 : // reporting path from here.
1086 :
1087 475 : thisObj = obj;
1088 : }
1089 : }
1090 :
1091 679 : if (!args.resize(argc)) {
1092 0 : retval = NS_ERROR_OUT_OF_MEMORY;
1093 0 : goto pre_call_clean_up;
1094 : }
1095 :
1096 679 : argv = args.begin();
1097 679 : sp = argv;
1098 :
1099 : // build the args
1100 : // NB: This assignment *looks* wrong because we haven't yet called our
1101 : // function. However, we *have* already entered the compartmen that we're
1102 : // about to call, and that's the global that we want here. In other words:
1103 : // we're trusting the JS engine to come up with a good global to use for
1104 : // our object (whatever it was).
1105 1983 : for (i = 0; i < argc; i++) {
1106 1304 : const nsXPTParamInfo& param = info->params[i];
1107 1304 : const nsXPTType& type = param.GetType();
1108 1304 : nsXPTType datum_type;
1109 : uint32_t array_count;
1110 1304 : bool isArray = type.IsArray();
1111 2608 : RootedValue val(cx, NullValue());
1112 2602 : bool isSizedString = isArray ?
1113 : false :
1114 2596 : type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
1115 2602 : type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
1116 :
1117 :
1118 : // verify that null was not passed for 'out' param
1119 1304 : if (param.IsOut() && !nativeParams[i].val.p) {
1120 0 : retval = NS_ERROR_INVALID_ARG;
1121 0 : goto pre_call_clean_up;
1122 : }
1123 :
1124 1304 : if (isArray) {
1125 6 : if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, ¶m, 1,
1126 : &datum_type)))
1127 0 : goto pre_call_clean_up;
1128 : } else
1129 1298 : datum_type = type;
1130 :
1131 1304 : if (param.IsIn()) {
1132 : nsXPTCMiniVariant* pv;
1133 :
1134 1304 : if (param.IsIndirect())
1135 1 : pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
1136 : else
1137 1303 : pv = &nativeParams[i];
1138 :
1139 1745 : if (datum_type.IsInterfacePointer() &&
1140 441 : !GetInterfaceTypeFromParam(cx, info, param, methodIndex,
1141 : datum_type, nativeParams,
1142 : ¶m_iid))
1143 0 : goto pre_call_clean_up;
1144 :
1145 1304 : if (isArray || isSizedString) {
1146 6 : if (!GetArraySizeFromParam(cx, info, param, methodIndex,
1147 : i, nativeParams, &array_count))
1148 0 : goto pre_call_clean_up;
1149 : }
1150 :
1151 1304 : if (isArray) {
1152 12 : if (!XPCConvert::NativeArray2JS(&val,
1153 6 : (const void**)&pv->val,
1154 : datum_type, ¶m_iid,
1155 : array_count, nullptr))
1156 0 : goto pre_call_clean_up;
1157 1298 : } else if (isSizedString) {
1158 0 : if (!XPCConvert::NativeStringWithSize2JS(&val,
1159 0 : (const void*)&pv->val,
1160 : datum_type,
1161 : array_count, nullptr))
1162 0 : goto pre_call_clean_up;
1163 : } else {
1164 1298 : if (!XPCConvert::NativeData2JS(&val, &pv->val, type,
1165 : ¶m_iid, nullptr))
1166 0 : goto pre_call_clean_up;
1167 : }
1168 : }
1169 :
1170 1304 : if (param.IsOut() || param.IsDipper()) {
1171 : // create an 'out' object
1172 0 : RootedObject out_obj(cx, NewOutObject(cx));
1173 0 : if (!out_obj) {
1174 0 : retval = NS_ERROR_OUT_OF_MEMORY;
1175 0 : goto pre_call_clean_up;
1176 : }
1177 :
1178 0 : if (param.IsIn()) {
1179 0 : if (!JS_SetPropertyById(cx, out_obj,
1180 0 : mRuntime->GetStringID(XPCJSContext::IDX_VALUE),
1181 : val)) {
1182 0 : goto pre_call_clean_up;
1183 : }
1184 : }
1185 0 : *sp++ = JS::ObjectValue(*out_obj);
1186 : } else
1187 1304 : *sp++ = val;
1188 : }
1189 :
1190 679 : readyToDoTheCall = true;
1191 :
1192 : pre_call_clean_up:
1193 : // clean up any 'out' params handed in
1194 679 : CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ true, paramCount);
1195 :
1196 : // Make sure "this" doesn't get deleted during this call.
1197 1357 : nsCOMPtr<nsIXPCWrappedJSClass> kungFuDeathGrip(this);
1198 :
1199 679 : if (!readyToDoTheCall)
1200 0 : return retval;
1201 :
1202 : // do the deed - note exceptions
1203 :
1204 679 : MOZ_ASSERT(!aes.HasException());
1205 :
1206 1357 : nsCOMPtr<nsIException> syntheticException;
1207 1357 : RootedValue rval(cx);
1208 679 : if (XPT_MD_IS_GETTER(info->flags)) {
1209 96 : success = JS_GetProperty(cx, obj, name, &rval);
1210 583 : } else if (XPT_MD_IS_SETTER(info->flags)) {
1211 2 : rval = *argv;
1212 2 : success = JS_SetProperty(cx, obj, name, rval);
1213 : } else {
1214 581 : if (!fval.isPrimitive()) {
1215 577 : success = JS_CallFunctionValue(cx, thisObj, fval, args, &rval);
1216 : } else {
1217 : // The property was not an object so can't be a function.
1218 : // Let's build and 'throw' an exception.
1219 :
1220 : static const nsresult code =
1221 : NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
1222 : static const char format[] = "%s \"%s\"";
1223 : const char * msg;
1224 8 : UniqueChars sz;
1225 :
1226 4 : if (nsXPCException::NameAndFormatForNSResult(code, nullptr, &msg) && msg)
1227 4 : sz = JS_smprintf(format, msg, name);
1228 :
1229 4 : XPCConvert::ConstructException(code, sz.get(), GetInterfaceName(), name,
1230 8 : nullptr, getter_AddRefs(syntheticException),
1231 4 : nullptr, nullptr);
1232 4 : success = false;
1233 : }
1234 : }
1235 :
1236 678 : if (!success)
1237 4 : return CheckForException(ccx, aes, name, GetInterfaceName(),
1238 4 : syntheticException);
1239 :
1240 674 : xpccx->SetPendingException(nullptr); // XXX necessary?
1241 :
1242 : // convert out args and result
1243 : // NOTE: this is the total number of native params, not just the args
1244 : // Convert independent params only.
1245 : // When we later convert the dependent params (if any) we will know that
1246 : // the params upon which they depend will have already been converted -
1247 : // regardless of ordering.
1248 :
1249 674 : foundDependentParam = false;
1250 2245 : for (i = 0; i < paramCount; i++) {
1251 1571 : const nsXPTParamInfo& param = info->params[i];
1252 1571 : MOZ_ASSERT(!param.IsShared(), "[shared] implies [noscript]!");
1253 1571 : if (!param.IsOut() && !param.IsDipper())
1254 2615 : continue;
1255 :
1256 289 : const nsXPTType& type = param.GetType();
1257 289 : if (type.IsDependent()) {
1258 51 : foundDependentParam = true;
1259 51 : continue;
1260 : }
1261 :
1262 476 : RootedValue val(cx);
1263 238 : uint8_t type_tag = type.TagPart();
1264 : nsXPTCMiniVariant* pv;
1265 :
1266 238 : if (param.IsDipper())
1267 56 : pv = (nsXPTCMiniVariant*) &nativeParams[i].val.p;
1268 : else
1269 182 : pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
1270 :
1271 238 : if (param.IsRetval())
1272 238 : val = rval;
1273 0 : else if (argv[i].isPrimitive())
1274 0 : break;
1275 : else {
1276 0 : RootedObject obj(cx, &argv[i].toObject());
1277 0 : if (!JS_GetPropertyById(cx, obj,
1278 0 : mRuntime->GetStringID(XPCJSContext::IDX_VALUE),
1279 : &val))
1280 0 : break;
1281 : }
1282 :
1283 : // setup allocator and/or iid
1284 :
1285 238 : if (type_tag == nsXPTType::T_INTERFACE) {
1286 112 : if (NS_FAILED(GetInterfaceInfo()->
1287 : GetIIDForParamNoAlloc(methodIndex, ¶m,
1288 : ¶m_iid)))
1289 0 : break;
1290 : }
1291 :
1292 238 : if (!XPCConvert::JSData2Native(&pv->val, val, type,
1293 : ¶m_iid, nullptr))
1294 0 : break;
1295 : }
1296 :
1297 : // if any params were dependent, then we must iterate again to convert them.
1298 674 : if (foundDependentParam && i == paramCount) {
1299 204 : for (i = 0; i < paramCount; i++) {
1300 153 : const nsXPTParamInfo& param = info->params[i];
1301 153 : if (!param.IsOut())
1302 204 : continue;
1303 :
1304 51 : const nsXPTType& type = param.GetType();
1305 51 : if (!type.IsDependent())
1306 0 : continue;
1307 :
1308 102 : RootedValue val(cx);
1309 : nsXPTCMiniVariant* pv;
1310 51 : nsXPTType datum_type;
1311 : uint32_t array_count;
1312 51 : bool isArray = type.IsArray();
1313 102 : bool isSizedString = isArray ?
1314 : false :
1315 102 : type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
1316 102 : type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
1317 :
1318 51 : pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
1319 :
1320 51 : if (param.IsRetval())
1321 51 : val = rval;
1322 : else {
1323 0 : RootedObject obj(cx, &argv[i].toObject());
1324 0 : if (!JS_GetPropertyById(cx, obj,
1325 0 : mRuntime->GetStringID(XPCJSContext::IDX_VALUE),
1326 : &val))
1327 0 : break;
1328 : }
1329 :
1330 : // setup allocator and/or iid
1331 :
1332 51 : if (isArray) {
1333 0 : if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, ¶m, 1,
1334 : &datum_type)))
1335 0 : break;
1336 : } else
1337 51 : datum_type = type;
1338 :
1339 51 : if (datum_type.IsInterfacePointer()) {
1340 51 : if (!GetInterfaceTypeFromParam(cx, info, param, methodIndex,
1341 : datum_type, nativeParams,
1342 : ¶m_iid))
1343 0 : break;
1344 : }
1345 :
1346 51 : if (isArray || isSizedString) {
1347 0 : if (!GetArraySizeFromParam(cx, info, param, methodIndex,
1348 : i, nativeParams, &array_count))
1349 0 : break;
1350 : }
1351 :
1352 51 : if (isArray) {
1353 0 : if (array_count &&
1354 0 : !XPCConvert::JSArray2Native((void**)&pv->val, val,
1355 : array_count, datum_type,
1356 0 : ¶m_iid, nullptr))
1357 0 : break;
1358 51 : } else if (isSizedString) {
1359 0 : if (!XPCConvert::JSStringWithSize2Native((void*)&pv->val, val,
1360 : array_count, datum_type,
1361 : nullptr))
1362 0 : break;
1363 : } else {
1364 51 : if (!XPCConvert::JSData2Native(&pv->val, val, type,
1365 : ¶m_iid,
1366 : nullptr))
1367 0 : break;
1368 : }
1369 : }
1370 : }
1371 :
1372 674 : if (i != paramCount) {
1373 : // We didn't manage all the result conversions!
1374 : // We have to cleanup any junk that *did* get converted.
1375 0 : CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ false, i);
1376 : } else {
1377 : // set to whatever the JS code might have set as the result
1378 674 : retval = xpccx->GetPendingResult();
1379 : }
1380 :
1381 674 : return retval;
1382 : }
1383 :
1384 : const char*
1385 8 : nsXPCWrappedJSClass::GetInterfaceName()
1386 : {
1387 8 : if (!mName)
1388 2 : mInfo->GetName(&mName);
1389 8 : return mName;
1390 : }
1391 :
1392 : static const JSClass XPCOutParamClass = {
1393 : "XPCOutParam",
1394 : 0,
1395 : JS_NULL_CLASS_OPS
1396 : };
1397 :
1398 : bool
1399 0 : xpc::IsOutObject(JSContext* cx, JSObject* obj)
1400 : {
1401 0 : return js::GetObjectJSClass(obj) == &XPCOutParamClass;
1402 : }
1403 :
1404 : JSObject*
1405 0 : xpc::NewOutObject(JSContext* cx)
1406 : {
1407 0 : return JS_NewObject(cx, &XPCOutParamClass);
1408 : }
1409 :
1410 :
1411 : NS_IMETHODIMP
1412 0 : nsXPCWrappedJSClass::DebugDump(int16_t depth)
1413 : {
1414 : #ifdef DEBUG
1415 0 : depth-- ;
1416 0 : XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %p with mRefCnt = %" PRIuPTR, this, mRefCnt.get()));
1417 0 : XPC_LOG_INDENT();
1418 : char* name;
1419 0 : mInfo->GetName(&name);
1420 0 : XPC_LOG_ALWAYS(("interface name is %s", name));
1421 0 : if (name)
1422 0 : free(name);
1423 0 : char * iid = mIID.ToString();
1424 0 : XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
1425 0 : if (iid)
1426 0 : free(iid);
1427 0 : XPC_LOG_ALWAYS(("InterfaceInfo @ %p", mInfo.get()));
1428 0 : uint16_t methodCount = 0;
1429 0 : if (depth) {
1430 : uint16_t i;
1431 0 : nsCOMPtr<nsIInterfaceInfo> parent;
1432 0 : XPC_LOG_INDENT();
1433 0 : mInfo->GetParent(getter_AddRefs(parent));
1434 0 : XPC_LOG_ALWAYS(("parent @ %p", parent.get()));
1435 0 : mInfo->GetMethodCount(&methodCount);
1436 0 : XPC_LOG_ALWAYS(("MethodCount = %d", methodCount));
1437 0 : mInfo->GetConstantCount(&i);
1438 0 : XPC_LOG_ALWAYS(("ConstantCount = %d", i));
1439 0 : XPC_LOG_OUTDENT();
1440 : }
1441 0 : XPC_LOG_ALWAYS(("mRuntime @ %p", mRuntime));
1442 0 : XPC_LOG_ALWAYS(("mDescriptors @ %p count = %d", mDescriptors, methodCount));
1443 0 : if (depth && mDescriptors && methodCount) {
1444 0 : depth--;
1445 0 : XPC_LOG_INDENT();
1446 0 : for (uint16_t i = 0; i < methodCount; i++) {
1447 0 : XPC_LOG_ALWAYS(("Method %d is %s%s", \
1448 : i, IsReflectable(i) ? "":" NOT ","reflectable"));
1449 : }
1450 0 : XPC_LOG_OUTDENT();
1451 0 : depth++;
1452 : }
1453 0 : XPC_LOG_OUTDENT();
1454 : #endif
1455 0 : return NS_OK;
1456 : }
|