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