LCOV - code coverage report
Current view: top level - js/xpconnect/src - XPCWrappedJSClass.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 381 681 55.9 %
Date: 2017-07-14 16:53:18 Functions: 21 37 56.8 %
Legend: Lines: hit not hit

          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, &param, 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, &param, 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 :                                                     &param, &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, &param,
     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, &param, 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             :                                            &param_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, &param_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             :                                                &param_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, &param,
    1288             :                                                 &param_iid)))
    1289           0 :                 break;
    1290             :         }
    1291             : 
    1292         238 :         if (!XPCConvert::JSData2Native(&pv->val, val, type,
    1293             :                                        &param_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, &param, 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             :                                               &param_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 :                                                 &param_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             :                                                &param_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             : }

Generated by: LCOV version 1.13