js/xpconnect/src/XPCWrappedJSClass.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 /* Sharable code and data for wrapper around JSObjects. */
     9 #include "xpcprivate.h"
    10 #include "jsprf.h"
    11 #include "nsArrayEnumerator.h"
    12 #include "nsContentUtils.h"
    13 #include "nsWrapperCache.h"
    14 #include "AccessCheck.h"
    15 #include "nsJSUtils.h"
    16 #include "mozilla/Attributes.h"
    17 #include "mozilla/dom/BindingUtils.h"
    18 #include "mozilla/dom/DOMException.h"
    19 #include "mozilla/dom/DOMExceptionBinding.h"
    21 #include "jsapi.h"
    22 #include "jsfriendapi.h"
    24 using namespace xpc;
    25 using namespace JS;
    26 using namespace mozilla;
    28 NS_IMPL_ISUPPORTS(nsXPCWrappedJSClass, nsIXPCWrappedJSClass)
    30 // the value of this variable is never used - we use its address as a sentinel
    31 static uint32_t zero_methods_descriptor;
    33 bool AutoScriptEvaluate::StartEvaluating(HandleObject scope, JSErrorReporter errorReporter)
    34 {
    35     NS_PRECONDITION(!mEvaluated, "AutoScriptEvaluate::Evaluate should only be called once");
    37     if (!mJSContext)
    38         return true;
    40     mEvaluated = true;
    41     if (!JS_GetErrorReporter(mJSContext)) {
    42         JS_SetErrorReporter(mJSContext, errorReporter);
    43         mErrorReporterSet = true;
    44     }
    46     JS_BeginRequest(mJSContext);
    47     mAutoCompartment.construct(mJSContext, scope);
    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.construct(mJSContext);
    57     return true;
    58 }
    60 AutoScriptEvaluate::~AutoScriptEvaluate()
    61 {
    62     if (!mJSContext || !mEvaluated)
    63         return;
    64     mState.ref().restore();
    66     JS_EndRequest(mJSContext);
    68     if (mErrorReporterSet)
    69         JS_SetErrorReporter(mJSContext, nullptr);
    70 }
    72 // It turns out that some errors may be not worth reporting. So, this
    73 // function is factored out to manage that.
    74 bool xpc_IsReportableErrorCode(nsresult code)
    75 {
    76     if (NS_SUCCEEDED(code))
    77         return false;
    79     switch (code) {
    80         // Error codes that we don't want to report as errors...
    81         // These generally indicate bad interface design AFAIC.
    82         case NS_ERROR_FACTORY_REGISTER_AGAIN:
    83         case NS_BASE_STREAM_WOULD_BLOCK:
    84             return false;
    85         default:
    86             return true;
    87     }
    88 }
    90 // static
    91 already_AddRefed<nsXPCWrappedJSClass>
    92 nsXPCWrappedJSClass::GetNewOrUsed(JSContext* cx, REFNSIID aIID)
    93 {
    94     XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
    95     IID2WrappedJSClassMap* map = rt->GetWrappedJSClassMap();
    96     nsRefPtr<nsXPCWrappedJSClass> clasp = map->Find(aIID);
    98     if (!clasp) {
    99         nsCOMPtr<nsIInterfaceInfo> info;
   100         nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
   101         if (info) {
   102             bool canScript, isBuiltin;
   103             if (NS_SUCCEEDED(info->IsScriptable(&canScript)) && canScript &&
   104                 NS_SUCCEEDED(info->IsBuiltinClass(&isBuiltin)) && !isBuiltin &&
   105                 nsXPConnect::IsISupportsDescendant(info))
   106             {
   107                 clasp = new nsXPCWrappedJSClass(cx, aIID, info);
   108                 if (!clasp->mDescriptors)
   109                     clasp = nullptr;
   110             }
   111         }
   112     }
   113     return clasp.forget();
   114 }
   116 nsXPCWrappedJSClass::nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
   117                                          nsIInterfaceInfo* aInfo)
   118     : mRuntime(nsXPConnect::GetRuntimeInstance()),
   119       mInfo(aInfo),
   120       mName(nullptr),
   121       mIID(aIID),
   122       mDescriptors(nullptr)
   123 {
   124     mRuntime->GetWrappedJSClassMap()->Add(this);
   126     uint16_t methodCount;
   127     if (NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount))) {
   128         if (methodCount) {
   129             int wordCount = (methodCount/32)+1;
   130             if (nullptr != (mDescriptors = new uint32_t[wordCount])) {
   131                 int i;
   132                 // init flags to 0;
   133                 for (i = wordCount-1; i >= 0; i--)
   134                     mDescriptors[i] = 0;
   136                 for (i = 0; i < methodCount; i++) {
   137                     const nsXPTMethodInfo* info;
   138                     if (NS_SUCCEEDED(mInfo->GetMethodInfo(i, &info)))
   139                         SetReflectable(i, XPCConvert::IsMethodReflectable(*info));
   140                     else {
   141                         delete [] mDescriptors;
   142                         mDescriptors = nullptr;
   143                         break;
   144                     }
   145                 }
   146             }
   147         } else {
   148             mDescriptors = &zero_methods_descriptor;
   149         }
   150     }
   151 }
   153 nsXPCWrappedJSClass::~nsXPCWrappedJSClass()
   154 {
   155     if (mDescriptors && mDescriptors != &zero_methods_descriptor)
   156         delete [] mDescriptors;
   157     if (mRuntime)
   158         mRuntime->GetWrappedJSClassMap()->Remove(this);
   160     if (mName)
   161         nsMemory::Free(mName);
   162 }
   164 JSObject*
   165 nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx,
   166                                                   JSObject* jsobjArg,
   167                                                   REFNSIID aIID)
   168 {
   169     RootedObject jsobj(cx, jsobjArg);
   170     JSObject* id;
   171     RootedValue retval(cx);
   172     RootedObject retObj(cx);
   173     bool success = false;
   174     RootedValue fun(cx);
   176     // Don't call the actual function on a content object. We'll determine
   177     // whether or not a content object is capable of implementing the
   178     // interface (i.e. whether the interface is scriptable) and most content
   179     // objects don't have QI implementations anyway. Also see bug 503926.
   180     if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(jsobj))) {
   181         return nullptr;
   182     }
   184     // OK, it looks like we'll be calling into JS code.
   185     AutoScriptEvaluate scriptEval(cx);
   187     // XXX we should install an error reporter that will send reports to
   188     // the JS error console service.
   189     if (!scriptEval.StartEvaluating(jsobj))
   190         return nullptr;
   192     // check upfront for the existence of the function property
   193     HandleId funid = mRuntime->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE);
   194     if (!JS_GetPropertyById(cx, jsobj, funid, &fun) || JSVAL_IS_PRIMITIVE(fun))
   195         return nullptr;
   197     // Ensure that we are asking for a scriptable interface.
   198     // NB:  It's important for security that this check is here rather
   199     // than later, since it prevents untrusted objects from implementing
   200     // some interfaces in JS and aggregating a trusted object to
   201     // implement intentionally (for security) unscriptable interfaces.
   202     // We so often ask for nsISupports that we can short-circuit the test...
   203     if (!aIID.Equals(NS_GET_IID(nsISupports))) {
   204         nsCOMPtr<nsIInterfaceInfo> info;
   205         nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
   206         if (!info)
   207             return nullptr;
   208         bool canScript, isBuiltin;
   209         if (NS_FAILED(info->IsScriptable(&canScript)) || !canScript ||
   210             NS_FAILED(info->IsBuiltinClass(&isBuiltin)) || isBuiltin)
   211             return nullptr;
   212     }
   214     id = xpc_NewIDObject(cx, jsobj, aIID);
   215     if (id) {
   216         // Throwing NS_NOINTERFACE is the prescribed way to fail QI from JS. It
   217         // is not an exception that is ever worth reporting, but we don't want
   218         // to eat all exceptions either.
   220         {
   221             AutoSaveContextOptions asco(cx);
   222             ContextOptionsRef(cx).setDontReportUncaught(true);
   223             RootedValue arg(cx, JS::ObjectValue(*id));
   224             success = JS_CallFunctionValue(cx, jsobj, fun, arg, &retval);
   225         }
   227         if (!success && JS_IsExceptionPending(cx)) {
   228             RootedValue jsexception(cx, NullValue());
   230             if (JS_GetPendingException(cx, &jsexception)) {
   231                 nsresult rv;
   232                 if (jsexception.isObject()) {
   233                     // XPConnect may have constructed an object to represent a
   234                     // C++ QI failure. See if that is the case.
   235                     using namespace mozilla::dom;
   236                     Exception *e = nullptr;
   237                     UNWRAP_OBJECT(Exception, &jsexception.toObject(), e);
   239                     if (e &&
   240                         NS_SUCCEEDED(e->GetResult(&rv)) &&
   241                         rv == NS_NOINTERFACE) {
   242                         JS_ClearPendingException(cx);
   243                     }
   244                 } else if (JSVAL_IS_NUMBER(jsexception)) {
   245                     // JS often throws an nsresult.
   246                     if (JSVAL_IS_DOUBLE(jsexception))
   247                         // Visual Studio 9 doesn't allow casting directly from
   248                         // a double to an enumeration type, contrary to
   249                         // 5.2.9(10) of C++11, so add an intermediate cast.
   250                         rv = (nsresult)(uint32_t)(JSVAL_TO_DOUBLE(jsexception));
   251                     else
   252                         rv = (nsresult)(JSVAL_TO_INT(jsexception));
   254                     if (rv == NS_NOINTERFACE)
   255                         JS_ClearPendingException(cx);
   256                 }
   257             }
   259             // Don't report if reporting was disabled by someone else.
   260             if (!ContextOptionsRef(cx).dontReportUncaught())
   261                 JS_ReportPendingException(cx);
   262         } else if (!success) {
   263             NS_WARNING("QI hook ran OOMed - this is probably a bug!");
   264         }
   265     }
   267     if (success)
   268         success = JS_ValueToObject(cx, retval, &retObj);
   270     return success ? retObj.get() : nullptr;
   271 }
   273 /***************************************************************************/
   275 static bool
   276 GetNamedPropertyAsVariantRaw(XPCCallContext& ccx,
   277                              HandleObject aJSObj,
   278                              HandleId aName,
   279                              nsIVariant** aResult,
   280                              nsresult* pErr)
   281 {
   282     nsXPTType type = nsXPTType((uint8_t)TD_INTERFACE_TYPE);
   283     RootedValue val(ccx);
   285     return JS_GetPropertyById(ccx, aJSObj, aName, &val) &&
   286            // Note that this always takes the T_INTERFACE path through
   287            // JSData2Native, so the value passed for useAllocator
   288            // doesn't really matter. We pass true for consistency.
   289            XPCConvert::JSData2Native(aResult, val, type, true,
   290                                      &NS_GET_IID(nsIVariant), pErr);
   291 }
   293 // static
   294 nsresult
   295 nsXPCWrappedJSClass::GetNamedPropertyAsVariant(XPCCallContext& ccx,
   296                                                JSObject* aJSObjArg,
   297                                                const nsAString& aName,
   298                                                nsIVariant** aResult)
   299 {
   300     JSContext* cx = ccx.GetJSContext();
   301     RootedObject aJSObj(cx, aJSObjArg);
   303     AutoScriptEvaluate scriptEval(cx);
   304     if (!scriptEval.StartEvaluating(aJSObj))
   305         return NS_ERROR_FAILURE;
   307     // Wrap the string in a jsval after the AutoScriptEvaluate, so that the
   308     // resulting value ends up in the correct compartment.
   309     nsStringBuffer* buf;
   310     RootedValue value(cx);
   311     if (!XPCStringConvert::ReadableToJSVal(ccx, aName, &buf, &value))
   312         return NS_ERROR_OUT_OF_MEMORY;
   313     if (buf)
   314         buf->AddRef();
   316     RootedId id(cx);
   317     nsresult rv = NS_OK;
   318     if (!JS_ValueToId(cx, value, &id) ||
   319         !GetNamedPropertyAsVariantRaw(ccx, aJSObj, id, aResult, &rv)) {
   320         if (NS_FAILED(rv))
   321             return rv;
   322         return NS_ERROR_FAILURE;
   323     }
   324     return NS_OK;
   325 }
   327 /***************************************************************************/
   329 // static
   330 nsresult
   331 nsXPCWrappedJSClass::BuildPropertyEnumerator(XPCCallContext& ccx,
   332                                              JSObject* aJSObjArg,
   333                                              nsISimpleEnumerator** aEnumerate)
   334 {
   335     JSContext* cx = ccx.GetJSContext();
   336     RootedObject aJSObj(cx, aJSObjArg);
   338     AutoScriptEvaluate scriptEval(cx);
   339     if (!scriptEval.StartEvaluating(aJSObj))
   340         return NS_ERROR_FAILURE;
   342     AutoIdArray idArray(cx, JS_Enumerate(cx, aJSObj));
   343     if (!idArray)
   344         return NS_ERROR_FAILURE;
   346     nsCOMArray<nsIProperty> propertyArray(idArray.length());
   347     RootedId idName(cx);
   348     for (size_t i = 0; i < idArray.length(); i++) {
   349         idName = idArray[i];
   351         nsCOMPtr<nsIVariant> value;
   352         nsresult rv;
   353         if (!GetNamedPropertyAsVariantRaw(ccx, aJSObj, idName,
   354                                           getter_AddRefs(value), &rv)) {
   355             if (NS_FAILED(rv))
   356                 return rv;
   357             return NS_ERROR_FAILURE;
   358         }
   360         RootedValue jsvalName(cx);
   361         if (!JS_IdToValue(cx, idName, &jsvalName))
   362             return NS_ERROR_FAILURE;
   364         JSString* name = ToString(cx, jsvalName);
   365         if (!name)
   366             return NS_ERROR_FAILURE;
   368         size_t length;
   369         const jschar *chars = JS_GetStringCharsAndLength(cx, name, &length);
   370         if (!chars)
   371             return NS_ERROR_FAILURE;
   373         nsCOMPtr<nsIProperty> property =
   374             new xpcProperty(chars, (uint32_t) length, value);
   376         if (!propertyArray.AppendObject(property))
   377             return NS_ERROR_FAILURE;
   378     }
   380     return NS_NewArrayEnumerator(aEnumerate, propertyArray);
   381 }
   383 /***************************************************************************/
   385 NS_IMPL_ISUPPORTS(xpcProperty, nsIProperty)
   387 xpcProperty::xpcProperty(const char16_t* aName, uint32_t aNameLen,
   388                          nsIVariant* aValue)
   389     : mName(aName, aNameLen), mValue(aValue)
   390 {
   391 }
   393 /* readonly attribute AString name; */
   394 NS_IMETHODIMP xpcProperty::GetName(nsAString & aName)
   395 {
   396     aName.Assign(mName);
   397     return NS_OK;
   398 }
   400 /* readonly attribute nsIVariant value; */
   401 NS_IMETHODIMP xpcProperty::GetValue(nsIVariant * *aValue)
   402 {
   403     nsCOMPtr<nsIVariant> rval = mValue;
   404     rval.forget(aValue);
   405     return NS_OK;
   406 }
   408 /***************************************************************************/
   409 // This 'WrappedJSIdentity' class and singleton allow us to figure out if
   410 // any given nsISupports* is implemented by a WrappedJS object. This is done
   411 // using a QueryInterface call on the interface pointer with our ID. If
   412 // that call returns NS_OK and the pointer is to our singleton, then the
   413 // interface must be implemented by a WrappedJS object. NOTE: the
   414 // 'WrappedJSIdentity' object is not a real XPCOM object and should not be
   415 // used for anything else (hence it is declared in this implementation file).
   417 // {5C5C3BB0-A9BA-11d2-BA64-00805F8A5DD7}
   418 #define NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID                           \
   419 { 0x5c5c3bb0, 0xa9ba, 0x11d2,                                                 \
   420   { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
   422 class WrappedJSIdentity
   423 {
   424     // no instance methods...
   425 public:
   426     NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID)
   428     static void* GetSingleton()
   429     {
   430         static WrappedJSIdentity* singleton = nullptr;
   431         if (!singleton)
   432             singleton = new WrappedJSIdentity();
   433         return (void*) singleton;
   434     }
   435 };
   437 NS_DEFINE_STATIC_IID_ACCESSOR(WrappedJSIdentity,
   438                               NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID)
   440 /***************************************************************************/
   442 // static
   443 bool
   444 nsXPCWrappedJSClass::IsWrappedJS(nsISupports* aPtr)
   445 {
   446     void* result;
   447     NS_PRECONDITION(aPtr, "null pointer");
   448     return aPtr &&
   449            NS_OK == aPtr->QueryInterface(NS_GET_IID(WrappedJSIdentity), &result) &&
   450            result == WrappedJSIdentity::GetSingleton();
   451 }
   453 // NB: This will return the top JSContext on the JSContext stack if there is one,
   454 // before attempting to get the context from the wrapped JS object.
   455 static JSContext *
   456 GetContextFromObjectOrDefault(nsXPCWrappedJS* wrapper)
   457 {
   458     // First, try the cx stack.
   459     XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack();
   460     if (stack->Peek())
   461         return stack->Peek();
   463     // If the cx stack is empty, try the wrapper's JSObject.
   464     JSCompartment *c = js::GetObjectCompartment(wrapper->GetJSObject());
   465     XPCContext *xpcc = EnsureCompartmentPrivate(c)->scope->GetContext();
   466     if (xpcc) {
   467         JSContext *cx = xpcc->GetJSContext();
   468         JS_AbortIfWrongThread(JS_GetRuntime(cx));
   469         return cx;
   470     }
   472     // Fall back to the safe JSContext.
   473     return stack->GetSafeJSContext();
   474 }
   476 NS_IMETHODIMP
   477 nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
   478                                              REFNSIID aIID,
   479                                              void** aInstancePtr)
   480 {
   481     if (aIID.Equals(NS_GET_IID(nsIXPConnectJSObjectHolder))) {
   482         NS_ADDREF(self);
   483         *aInstancePtr = (void*) static_cast<nsIXPConnectJSObjectHolder*>(self);
   484         return NS_OK;
   485     }
   487     // Objects internal to xpconnect are the only objects that even know *how*
   488     // to ask for this iid. And none of them bother refcounting the thing.
   489     if (aIID.Equals(NS_GET_IID(WrappedJSIdentity))) {
   490         // asking to find out if this is a wrapper object
   491         *aInstancePtr = WrappedJSIdentity::GetSingleton();
   492         return NS_OK;
   493     }
   495     if (aIID.Equals(NS_GET_IID(nsIPropertyBag))) {
   496         // We only want to expose one implementation from our aggregate.
   497         nsXPCWrappedJS* root = self->GetRootWrapper();
   499         if (!root->IsValid()) {
   500             *aInstancePtr = nullptr;
   501             return NS_NOINTERFACE;
   502         }
   504         NS_ADDREF(root);
   505         *aInstancePtr = (void*) static_cast<nsIPropertyBag*>(root);
   506         return NS_OK;
   507     }
   509     // We can't have a cached wrapper.
   510     if (aIID.Equals(NS_GET_IID(nsWrapperCache))) {
   511         *aInstancePtr = nullptr;
   512         return NS_NOINTERFACE;
   513     }
   515     AutoPushJSContext context(GetContextFromObjectOrDefault(self));
   516     XPCCallContext ccx(NATIVE_CALLER, context);
   517     if (!ccx.IsValid()) {
   518         *aInstancePtr = nullptr;
   519         return NS_NOINTERFACE;
   520     }
   522     // We support nsISupportsWeakReference iff the root wrapped JSObject
   523     // claims to support it in its QueryInterface implementation.
   524     if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) {
   525         // We only want to expose one implementation from our aggregate.
   526         nsXPCWrappedJS* root = self->GetRootWrapper();
   528         // Fail if JSObject doesn't claim support for nsISupportsWeakReference
   529         if (!root->IsValid() ||
   530             !CallQueryInterfaceOnJSObject(ccx, root->GetJSObject(), aIID)) {
   531             *aInstancePtr = nullptr;
   532             return NS_NOINTERFACE;
   533         }
   535         NS_ADDREF(root);
   536         *aInstancePtr = (void*) static_cast<nsISupportsWeakReference*>(root);
   537         return NS_OK;
   538     }
   540     // Checks for any existing wrapper explicitly constructed for this iid.
   541     // This includes the current 'self' wrapper. This also deals with the
   542     // nsISupports case (for which it returns mRoot).
   543     // Also check if asking for an interface from which one of our wrappers inherits.
   544     if (nsXPCWrappedJS* sibling = self->FindOrFindInherited(aIID)) {
   545         NS_ADDREF(sibling);
   546         *aInstancePtr = sibling->GetXPTCStub();
   547         return NS_OK;
   548     }
   550     // else we do the more expensive stuff...
   552     // check if the JSObject claims to implement this interface
   553     RootedObject jsobj(ccx, CallQueryInterfaceOnJSObject(ccx, self->GetJSObject(),
   554                                                          aIID));
   555     if (jsobj) {
   556         // We can't use XPConvert::JSObject2NativeInterface() here
   557         // since that can find a XPCWrappedNative directly on the
   558         // proto chain, and we don't want that here. We need to find
   559         // the actual JS object that claimed it supports the interface
   560         // we're looking for or we'll potentially bypass security
   561         // checks etc by calling directly through to a native found on
   562         // the prototype chain.
   563         //
   564         // Instead, simply do the nsXPCWrappedJS part of
   565         // XPConvert::JSObject2NativeInterface() here to make sure we
   566         // get a new (or used) nsXPCWrappedJS.
   567         nsXPCWrappedJS* wrapper;
   568         nsresult rv = nsXPCWrappedJS::GetNewOrUsed(jsobj, aIID, &wrapper);
   569         if (NS_SUCCEEDED(rv) && wrapper) {
   570             // We need to go through the QueryInterface logic to make
   571             // this return the right thing for the various 'special'
   572             // interfaces; e.g.  nsIPropertyBag.
   573             rv = wrapper->QueryInterface(aIID, aInstancePtr);
   574             NS_RELEASE(wrapper);
   575             return rv;
   576         }
   577     }
   579     // else...
   580     // no can do
   581     *aInstancePtr = nullptr;
   582     return NS_NOINTERFACE;
   583 }
   585 JSObject*
   586 nsXPCWrappedJSClass::GetRootJSObject(JSContext* cx, JSObject* aJSObjArg)
   587 {
   588     RootedObject aJSObj(cx, aJSObjArg);
   589     JSObject* result = CallQueryInterfaceOnJSObject(cx, aJSObj,
   590                                                     NS_GET_IID(nsISupports));
   591     if (!result)
   592         return aJSObj;
   593     JSObject* inner = js::UncheckedUnwrap(result);
   594     if (inner)
   595         return inner;
   596     return result;
   597 }
   599 void
   600 xpcWrappedJSErrorReporter(JSContext *cx, const char *message,
   601                           JSErrorReport *report)
   602 {
   603     if (report) {
   604         // If it is an exception report, then we can just deal with the
   605         // exception later (if not caught in the JS code).
   606         if (JSREPORT_IS_EXCEPTION(report->flags)) {
   607             // XXX We have a problem with error reports from uncaught exceptions.
   608             //
   609             // http://bugzilla.mozilla.org/show_bug.cgi?id=66453
   610             //
   611             // The issue is...
   612             //
   613             // We can't assume that the exception will *stay* uncaught. So, if
   614             // we build an nsIXPCException here and the underlying exception
   615             // really is caught before our script is done running then we blow
   616             // it by returning failure to our caller when the script didn't
   617             // really fail. However, This report contains error location info
   618             // that is no longer available after the script is done. So, if the
   619             // exception really is not caught (and is a non-engine exception)
   620             // then we've lost the oportunity to capture the script location
   621             // info that we *could* have captured here.
   622             //
   623             // This is expecially an issue with nested evaluations.
   624             //
   625             // Perhaps we could capture an expception here and store it as
   626             // 'provisional' and then later if there is a pending exception
   627             // when the script is done then we could maybe compare that in some
   628             // way with the 'provisional' one in which we captured location info.
   629             // We would not want to assume that the one discovered here is the
   630             // same one that is later detected. This could cause us to lie.
   631             //
   632             // The thing is. we do not currently store the right stuff to compare
   633             // these two nsIXPCExceptions (triggered by the same exception jsval
   634             // in the engine). Maybe we should store the jsval and compare that?
   635             // Maybe without even rooting it since we will not dereference it.
   636             // This is inexact, but maybe the right thing to do?
   637             //
   638             // if (report->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)) ...
   639             //
   641             return;
   642         }
   644         if (JSREPORT_IS_WARNING(report->flags)) {
   645             // XXX printf the warning (#ifdef DEBUG only!).
   646             // XXX send the warning to the console service.
   647             return;
   648         }
   649     }
   651     XPCCallContext ccx(NATIVE_CALLER, cx);
   652     if (!ccx.IsValid())
   653         return;
   655     nsCOMPtr<nsIException> e;
   656     XPCConvert::JSErrorToXPCException(message, nullptr, nullptr, report,
   657                                       getter_AddRefs(e));
   658     if (e)
   659         ccx.GetXPCContext()->SetException(e);
   660 }
   662 bool
   663 nsXPCWrappedJSClass::GetArraySizeFromParam(JSContext* cx,
   664                                            const XPTMethodDescriptor* method,
   665                                            const nsXPTParamInfo& param,
   666                                            uint16_t methodIndex,
   667                                            uint8_t paramIndex,
   668                                            nsXPTCMiniVariant* nativeParams,
   669                                            uint32_t* result)
   670 {
   671     uint8_t argnum;
   672     nsresult rv;
   674     rv = mInfo->GetSizeIsArgNumberForParam(methodIndex, &param, 0, &argnum);
   675     if (NS_FAILED(rv))
   676         return false;
   678     const nsXPTParamInfo& arg_param = method->params[argnum];
   680     // This should be enforced by the xpidl compiler, but it's not.
   681     // See bug 695235.
   682     MOZ_ASSERT(arg_param.GetType().TagPart() == nsXPTType::T_U32,
   683                "size_is references parameter of invalid type.");
   685     if (arg_param.IsIndirect())
   686         *result = *(uint32_t*)nativeParams[argnum].val.p;
   687     else
   688         *result = nativeParams[argnum].val.u32;
   690     return true;
   691 }
   693 bool
   694 nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx,
   695                                                const XPTMethodDescriptor* method,
   696                                                const nsXPTParamInfo& param,
   697                                                uint16_t methodIndex,
   698                                                const nsXPTType& type,
   699                                                nsXPTCMiniVariant* nativeParams,
   700                                                nsID* result)
   701 {
   702     uint8_t type_tag = type.TagPart();
   704     if (type_tag == nsXPTType::T_INTERFACE) {
   705         if (NS_SUCCEEDED(GetInterfaceInfo()->
   706                          GetIIDForParamNoAlloc(methodIndex, &param, result))) {
   707             return true;
   708         }
   709     } else if (type_tag == nsXPTType::T_INTERFACE_IS) {
   710         uint8_t argnum;
   711         nsresult rv;
   712         rv = mInfo->GetInterfaceIsArgNumberForParam(methodIndex,
   713                                                     &param, &argnum);
   714         if (NS_FAILED(rv))
   715             return false;
   717         const nsXPTParamInfo& arg_param = method->params[argnum];
   718         const nsXPTType& arg_type = arg_param.GetType();
   720         if (arg_type.TagPart() == nsXPTType::T_IID) {
   721             if (arg_param.IsIndirect()) {
   722                 nsID** p = (nsID**) nativeParams[argnum].val.p;
   723                 if (!p || !*p)
   724                     return false;
   725                 *result = **p;
   726             } else {
   727                 nsID* p = (nsID*) nativeParams[argnum].val.p;
   728                 if (!p)
   729                     return false;
   730                 *result = *p;
   731             }
   732             return true;
   733         }
   734     }
   735     return false;
   736 }
   738 void
   739 nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type,
   740                                          uint32_t array_count,
   741                                          void** arrayp)
   742 {
   743     if (datum_type.IsInterfacePointer()) {
   744         nsISupports** pp = (nsISupports**) arrayp;
   745         for (uint32_t k = 0; k < array_count; k++) {
   746             nsISupports* p = pp[k];
   747             NS_IF_RELEASE(p);
   748         }
   749     } else {
   750         void** pp = (void**) arrayp;
   751         for (uint32_t k = 0; k < array_count; k++) {
   752             void* p = pp[k];
   753             if (p) nsMemory::Free(p);
   754         }
   755     }
   756 }
   758 void
   759 nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type,
   760                                               void** pp)
   761 {
   762     MOZ_ASSERT(pp,"null pointer");
   763     if (type.IsInterfacePointer()) {
   764         nsISupports* p = *((nsISupports**)pp);
   765         if (p) p->Release();
   766     } else {
   767         void* p = *((void**)pp);
   768         if (p) nsMemory::Free(p);
   769     }
   770 }
   772 class AutoClearPendingException
   773 {
   774 public:
   775   AutoClearPendingException(JSContext *cx) : mCx(cx) { }
   776   ~AutoClearPendingException() { JS_ClearPendingException(mCx); }
   777 private:
   778   JSContext* mCx;
   779 };
   781 nsresult
   782 nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
   783                                        const char * aPropertyName,
   784                                        const char * anInterfaceName,
   785                                        bool aForceReport)
   786 {
   787     XPCContext * xpcc = ccx.GetXPCContext();
   788     JSContext * cx = ccx.GetJSContext();
   789     nsCOMPtr<nsIException> xpc_exception;
   790     /* this one would be set by our error reporter */
   792     xpcc->GetException(getter_AddRefs(xpc_exception));
   793     if (xpc_exception)
   794         xpcc->SetException(nullptr);
   796     // get this right away in case we do something below to cause JS code
   797     // to run on this JSContext
   798     nsresult pending_result = xpcc->GetPendingResult();
   800     RootedValue js_exception(cx);
   801     bool is_js_exception = JS_GetPendingException(cx, &js_exception);
   803     /* JS might throw an expection whether the reporter was called or not */
   804     if (is_js_exception) {
   805         if (!xpc_exception)
   806             XPCConvert::JSValToXPCException(&js_exception, anInterfaceName,
   807                                             aPropertyName,
   808                                             getter_AddRefs(xpc_exception));
   810         /* cleanup and set failed even if we can't build an exception */
   811         if (!xpc_exception) {
   812             XPCJSRuntime::Get()->SetPendingException(nullptr); // XXX necessary?
   813         }
   814     }
   816     AutoClearPendingException acpe(cx);
   818     if (xpc_exception) {
   819         nsresult e_result;
   820         if (NS_SUCCEEDED(xpc_exception->GetResult(&e_result))) {
   821             // Figure out whether or not we should report this exception.
   822             bool reportable = xpc_IsReportableErrorCode(e_result);
   823             if (reportable) {
   824                 // Always want to report forced exceptions and XPConnect's own
   825                 // errors.
   826                 reportable = aForceReport ||
   827                     NS_ERROR_GET_MODULE(e_result) == NS_ERROR_MODULE_XPCONNECT;
   829                 // See if an environment variable was set or someone has told us
   830                 // that a user pref was set indicating that we should report all
   831                 // exceptions.
   832                 if (!reportable)
   833                     reportable = nsXPConnect::ReportAllJSExceptions();
   835                 // Finally, check to see if this is the last JS frame on the
   836                 // stack. If so then we always want to report it.
   837                 if (!reportable)
   838                     reportable = !JS::DescribeScriptedCaller(cx);
   840                 // Ugly special case for GetInterface. It's "special" in the
   841                 // same way as QueryInterface in that a failure is not
   842                 // exceptional and shouldn't be reported. We have to do this
   843                 // check here instead of in xpcwrappedjs (like we do for QI) to
   844                 // avoid adding extra code to all xpcwrappedjs objects.
   845                 if (reportable && e_result == NS_ERROR_NO_INTERFACE &&
   846                     !strcmp(anInterfaceName, "nsIInterfaceRequestor") &&
   847                     !strcmp(aPropertyName, "getInterface")) {
   848                     reportable = false;
   849                 }
   851                 // More special case, see bug 877760.
   852                 if (e_result == NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) {
   853                     reportable = false;
   854                 }
   855             }
   857             // Try to use the error reporter set on the context to handle this
   858             // error if it came from a JS exception.
   859             if (reportable && is_js_exception &&
   860                 JS_GetErrorReporter(cx) != xpcWrappedJSErrorReporter)
   861             {
   862                 // If the error reporter ignores the error, it will call
   863                 // xpc->MarkErrorUnreported().
   864                 xpcc->ClearUnreportedError();
   865                 reportable = !JS_ReportPendingException(cx);
   866                 if (!xpcc->WasErrorReported())
   867                     reportable = true;
   868             }
   870             if (reportable) {
   871                 if (nsContentUtils::DOMWindowDumpEnabled()) {
   872                     static const char line[] =
   873                         "************************************************************\n";
   874                     static const char preamble[] =
   875                         "* Call to xpconnect wrapped JSObject produced this error:  *\n";
   876                     static const char cant_get_text[] =
   877                         "FAILED TO GET TEXT FROM EXCEPTION\n";
   879                     fputs(line, stdout);
   880                     fputs(preamble, stdout);
   881                     nsCString text;
   882                     if (NS_SUCCEEDED(xpc_exception->ToString(text)) &&
   883                         !text.IsEmpty()) {
   884                         fputs(text.get(), stdout);
   885                         fputs("\n", stdout);
   886                     } else
   887                         fputs(cant_get_text, stdout);
   888                     fputs(line, stdout);
   889                 }
   891                 // Log the exception to the JS Console, so that users can do
   892                 // something with it.
   893                 nsCOMPtr<nsIConsoleService> consoleService
   894                     (do_GetService(XPC_CONSOLE_CONTRACTID));
   895                 if (nullptr != consoleService) {
   896                     nsresult rv;
   897                     nsCOMPtr<nsIScriptError> scriptError;
   898                     nsCOMPtr<nsISupports> errorData;
   899                     rv = xpc_exception->GetData(getter_AddRefs(errorData));
   900                     if (NS_SUCCEEDED(rv))
   901                         scriptError = do_QueryInterface(errorData);
   903                     if (nullptr == scriptError) {
   904                         // No luck getting one from the exception, so
   905                         // try to cook one up.
   906                         scriptError = do_CreateInstance(XPC_SCRIPT_ERROR_CONTRACTID);
   907                         if (nullptr != scriptError) {
   908                             nsCString newMessage;
   909                             rv = xpc_exception->ToString(newMessage);
   910                             if (NS_SUCCEEDED(rv)) {
   911                                 // try to get filename, lineno from the first
   912                                 // stack frame location.
   913                                 int32_t lineNumber = 0;
   914                                 nsString sourceName;
   916                                 nsCOMPtr<nsIStackFrame> location;
   917                                 xpc_exception->
   918                                     GetLocation(getter_AddRefs(location));
   919                                 if (location) {
   920                                     // Get line number w/o checking; 0 is ok.
   921                                     location->GetLineNumber(&lineNumber);
   923                                     // get a filename.
   924                                     rv = location->GetFilename(sourceName);
   925                                 }
   927                                 rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(newMessage),
   928                                                                    sourceName,
   929                                                                    EmptyString(),
   930                                                                    lineNumber, 0, 0,
   931                                                                    "XPConnect JavaScript",
   932                                                                    nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
   933                                 if (NS_FAILED(rv))
   934                                     scriptError = nullptr;
   935                             }
   936                         }
   937                     }
   938                     if (nullptr != scriptError)
   939                         consoleService->LogMessage(scriptError);
   940                 }
   941             }
   942             // Whether or not it passes the 'reportable' test, it might
   943             // still be an error and we have to do the right thing here...
   944             if (NS_FAILED(e_result)) {
   945                 XPCJSRuntime::Get()->SetPendingException(xpc_exception);
   946                 return e_result;
   947             }
   948         }
   949     } else {
   950         // see if JS code signaled failure result without throwing exception
   951         if (NS_FAILED(pending_result)) {
   952             return pending_result;
   953         }
   954     }
   955     return NS_ERROR_FAILURE;
   956 }
   958 NS_IMETHODIMP
   959 nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
   960                                 const XPTMethodDescriptor* info_,
   961                                 nsXPTCMiniVariant* nativeParams)
   962 {
   963     jsval* sp = nullptr;
   964     jsval* argv = nullptr;
   965     uint8_t i;
   966     nsresult retval = NS_ERROR_FAILURE;
   967     nsresult pending_result = NS_OK;
   968     bool success;
   969     bool readyToDoTheCall = false;
   970     nsID  param_iid;
   971     const nsXPTMethodInfo* info = static_cast<const nsXPTMethodInfo*>(info_);
   972     const char* name = info->name;
   973     bool foundDependentParam;
   975     // Make sure not to set the callee on ccx until after we've gone through
   976     // the whole nsIXPCFunctionThisTranslator bit.  That code uses ccx to
   977     // convert natives to JSObjects, but we do NOT plan to pass those JSObjects
   978     // to our real callee.
   979     AutoPushJSContext context(GetContextFromObjectOrDefault(wrapper));
   980     XPCCallContext ccx(NATIVE_CALLER, context);
   981     if (!ccx.IsValid())
   982         return retval;
   984     XPCContext *xpcc = ccx.GetXPCContext();
   985     JSContext *cx = ccx.GetJSContext();
   987     if (!cx || !xpcc || !IsReflectable(methodIndex))
   988         return NS_ERROR_FAILURE;
   990     // [implicit_jscontext] and [optional_argc] have a different calling
   991     // convention, which we don't support for JS-implemented components.
   992     if (info->WantsOptArgc() || info->WantsContext()) {
   993         const char *str = "IDL methods marked with [implicit_jscontext] "
   994                           "or [optional_argc] may not be implemented in JS";
   995         // Throw and warn for good measure.
   996         JS_ReportError(cx, str);
   997         NS_WARNING(str);
   998         return NS_ERROR_FAILURE;
   999     }
  1001     RootedValue fval(cx);
  1002     RootedObject obj(cx, wrapper->GetJSObject());
  1003     RootedObject thisObj(cx, obj);
  1005     JSAutoCompartment ac(cx, obj);
  1007     AutoValueVector args(cx);
  1008     AutoScriptEvaluate scriptEval(cx);
  1010     // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
  1011     uint8_t paramCount = info->num_args;
  1012     uint8_t argc = paramCount -
  1013         (paramCount && XPT_PD_IS_RETVAL(info->params[paramCount-1].flags) ? 1 : 0);
  1015     if (!scriptEval.StartEvaluating(obj, xpcWrappedJSErrorReporter))
  1016         goto pre_call_clean_up;
  1018     xpcc->SetPendingResult(pending_result);
  1019     xpcc->SetException(nullptr);
  1020     XPCJSRuntime::Get()->SetPendingException(nullptr);
  1022     // We use js_Invoke so that the gcthings we use as args will be rooted by
  1023     // the engine as we do conversions and prepare to do the function call.
  1025     // setup stack
  1027     // if this isn't a function call then we don't need to push extra stuff
  1028     if (!(XPT_MD_IS_SETTER(info->flags) || XPT_MD_IS_GETTER(info->flags))) {
  1029         // We get fval before allocating the stack to avoid gc badness that can
  1030         // happen if the GetProperty call leaves our request and the gc runs
  1031         // while the stack we allocate contains garbage.
  1033         // If the interface is marked as a [function] then we will assume that
  1034         // our JSObject is a function and not an object with a named method.
  1036         bool isFunction;
  1037         if (NS_FAILED(mInfo->IsFunction(&isFunction)))
  1038             goto pre_call_clean_up;
  1040         // In the xpidl [function] case we are making sure now that the
  1041         // JSObject is callable. If it is *not* callable then we silently
  1042         // fallback to looking up the named property...
  1043         // (because jst says he thinks this fallback is 'The Right Thing'.)
  1044         //
  1045         // In the normal (non-function) case we just lookup the property by
  1046         // name and as long as the object has such a named property we go ahead
  1047         // and try to make the call. If it turns out the named property is not
  1048         // a callable object then the JS engine will throw an error and we'll
  1049         // pass this along to the caller as an exception/result code.
  1051         fval = ObjectValue(*obj);
  1052         if (isFunction &&
  1053             JS_TypeOfValue(ccx, fval) == JSTYPE_FUNCTION) {
  1055             // We may need to translate the 'this' for the function object.
  1057             if (paramCount) {
  1058                 const nsXPTParamInfo& firstParam = info->params[0];
  1059                 if (firstParam.IsIn()) {
  1060                     const nsXPTType& firstType = firstParam.GetType();
  1062                     if (firstType.IsInterfacePointer()) {
  1063                         nsIXPCFunctionThisTranslator* translator;
  1065                         IID2ThisTranslatorMap* map =
  1066                             mRuntime->GetThisTranslatorMap();
  1068                         translator = map->Find(mIID);
  1070                         if (translator) {
  1071                             nsCOMPtr<nsISupports> newThis;
  1072                             if (NS_FAILED(translator->
  1073                                           TranslateThis((nsISupports*)nativeParams[0].val.p,
  1074                                                         getter_AddRefs(newThis)))) {
  1075                                 goto pre_call_clean_up;
  1077                             if (newThis) {
  1078                                 RootedValue v(cx);
  1079                                 xpcObjectHelper helper(newThis);
  1080                                 bool ok =
  1081                                   XPCConvert::NativeInterface2JSObject(
  1082                                       &v, nullptr, helper, nullptr,
  1083                                       nullptr, false, nullptr);
  1084                                 if (!ok) {
  1085                                     goto pre_call_clean_up;
  1087                                 thisObj = JSVAL_TO_OBJECT(v);
  1088                                 if (!JS_WrapObject(cx, &thisObj))
  1089                                     goto pre_call_clean_up;
  1095         } else {
  1096             if (!JS_GetProperty(cx, obj, name, &fval))
  1097                 goto pre_call_clean_up;
  1098             // XXX We really want to factor out the error reporting better and
  1099             // specifically report the failure to find a function with this name.
  1100             // This is what we do below if the property is found but is not a
  1101             // function. We just need to factor better so we can get to that
  1102             // reporting path from here.
  1104             thisObj = obj;
  1108     if (!args.resize(argc)) {
  1109         retval = NS_ERROR_OUT_OF_MEMORY;
  1110         goto pre_call_clean_up;
  1113     argv = args.begin();
  1114     sp = argv;
  1116     // build the args
  1117     // NB: This assignment *looks* wrong because we haven't yet called our
  1118     // function. However, we *have* already entered the compartmen that we're
  1119     // about to call, and that's the global that we want here. In other words:
  1120     // we're trusting the JS engine to come up with a good global to use for
  1121     // our object (whatever it was).
  1122     for (i = 0; i < argc; i++) {
  1123         const nsXPTParamInfo& param = info->params[i];
  1124         const nsXPTType& type = param.GetType();
  1125         nsXPTType datum_type;
  1126         uint32_t array_count;
  1127         bool isArray = type.IsArray();
  1128         RootedValue val(cx, NullValue());
  1129         bool isSizedString = isArray ?
  1130                 false :
  1131                 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
  1132                 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
  1135         // verify that null was not passed for 'out' param
  1136         if (param.IsOut() && !nativeParams[i].val.p) {
  1137             retval = NS_ERROR_INVALID_ARG;
  1138             goto pre_call_clean_up;
  1141         if (isArray) {
  1142             if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
  1143                                                  &datum_type)))
  1144                 goto pre_call_clean_up;
  1145         } else
  1146             datum_type = type;
  1148         if (param.IsIn()) {
  1149             nsXPTCMiniVariant* pv;
  1151             if (param.IsIndirect())
  1152                 pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
  1153             else
  1154                 pv = &nativeParams[i];
  1156             if (datum_type.IsInterfacePointer() &&
  1157                 !GetInterfaceTypeFromParam(cx, info, param, methodIndex,
  1158                                            datum_type, nativeParams,
  1159                                            &param_iid))
  1160                 goto pre_call_clean_up;
  1162             if (isArray || isSizedString) {
  1163                 if (!GetArraySizeFromParam(cx, info, param, methodIndex,
  1164                                            i, nativeParams, &array_count))
  1165                     goto pre_call_clean_up;
  1168             if (isArray) {
  1169                 if (!XPCConvert::NativeArray2JS(&val,
  1170                                                 (const void**)&pv->val,
  1171                                                 datum_type, &param_iid,
  1172                                                 array_count, nullptr))
  1173                     goto pre_call_clean_up;
  1174             } else if (isSizedString) {
  1175                 if (!XPCConvert::NativeStringWithSize2JS(&val,
  1176                                                          (const void*)&pv->val,
  1177                                                          datum_type,
  1178                                                          array_count, nullptr))
  1179                     goto pre_call_clean_up;
  1180             } else {
  1181                 if (!XPCConvert::NativeData2JS(&val, &pv->val, type,
  1182                                                &param_iid, nullptr))
  1183                     goto pre_call_clean_up;
  1187         if (param.IsOut() || param.IsDipper()) {
  1188             // create an 'out' object
  1189             RootedObject out_obj(cx, NewOutObject(cx, obj));
  1190             if (!out_obj) {
  1191                 retval = NS_ERROR_OUT_OF_MEMORY;
  1192                 goto pre_call_clean_up;
  1195             if (param.IsIn()) {
  1196                 if (!JS_SetPropertyById(cx, out_obj,
  1197                                         mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
  1198                                         val)) {
  1199                     goto pre_call_clean_up;
  1202             *sp++ = OBJECT_TO_JSVAL(out_obj);
  1203         } else
  1204             *sp++ = val;
  1207     readyToDoTheCall = true;
  1209 pre_call_clean_up:
  1210     // clean up any 'out' params handed in
  1211     for (i = 0; i < paramCount; i++) {
  1212         const nsXPTParamInfo& param = info->params[i];
  1213         if (!param.IsOut())
  1214             continue;
  1216         const nsXPTType& type = param.GetType();
  1217         if (!type.deprecated_IsPointer())
  1218             continue;
  1219         void* p;
  1220         if (!(p = nativeParams[i].val.p))
  1221             continue;
  1223         if (param.IsIn()) {
  1224             if (type.IsArray()) {
  1225                 void** pp;
  1226                 if (nullptr != (pp = *((void***)p))) {
  1228                     // we need to get the array length and iterate the items
  1229                     uint32_t array_count;
  1230                     nsXPTType datum_type;
  1232                     if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, &param,
  1233                                                             1, &datum_type)) &&
  1234                         datum_type.deprecated_IsPointer() &&
  1235                         GetArraySizeFromParam(cx, info, param, methodIndex,
  1236                                               i, nativeParams, &array_count) &&
  1237                         array_count) {
  1239                         CleanupPointerArray(datum_type, array_count, pp);
  1242                     // always release the array if it is inout
  1243                     nsMemory::Free(pp);
  1245             } else
  1246                 CleanupPointerTypeObject(type, (void**)p);
  1248         *((void**)p) = nullptr;
  1251     // Make sure "this" doesn't get deleted during this call.
  1252     nsCOMPtr<nsIXPCWrappedJSClass> kungFuDeathGrip(this);
  1254     if (!readyToDoTheCall)
  1255         return retval;
  1257     // do the deed - note exceptions
  1259     JS_ClearPendingException(cx);
  1261     RootedValue rval(cx);
  1262     if (XPT_MD_IS_GETTER(info->flags)) {
  1263         success = JS_GetProperty(cx, obj, name, &rval);
  1264     } else if (XPT_MD_IS_SETTER(info->flags)) {
  1265         rval = *argv;
  1266         success = JS_SetProperty(cx, obj, name, rval);
  1267     } else {
  1268         if (!JSVAL_IS_PRIMITIVE(fval)) {
  1269             AutoSaveContextOptions asco(cx);
  1270             ContextOptionsRef(cx).setDontReportUncaught(true);
  1272             success = JS_CallFunctionValue(cx, thisObj, fval, args, &rval);
  1273         } else {
  1274             // The property was not an object so can't be a function.
  1275             // Let's build and 'throw' an exception.
  1277             static const nsresult code =
  1278                     NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
  1279             static const char format[] = "%s \"%s\"";
  1280             const char * msg;
  1281             char* sz = nullptr;
  1283             if (nsXPCException::NameAndFormatForNSResult(code, nullptr, &msg) && msg)
  1284                 sz = JS_smprintf(format, msg, name);
  1286             nsCOMPtr<nsIException> e;
  1288             XPCConvert::ConstructException(code, sz, GetInterfaceName(), name,
  1289                                            nullptr, getter_AddRefs(e), nullptr, nullptr);
  1290             xpcc->SetException(e);
  1291             if (sz)
  1292                 JS_smprintf_free(sz);
  1293             success = false;
  1297     if (!success) {
  1298         bool forceReport;
  1299         if (NS_FAILED(mInfo->IsFunction(&forceReport)))
  1300             forceReport = false;
  1302         // May also want to check if we're moving from content->chrome and force
  1303         // a report in that case.
  1305         return CheckForException(ccx, name, GetInterfaceName(), forceReport);
  1308     XPCJSRuntime::Get()->SetPendingException(nullptr); // XXX necessary?
  1310     // convert out args and result
  1311     // NOTE: this is the total number of native params, not just the args
  1312     // Convert independent params only.
  1313     // When we later convert the dependent params (if any) we will know that
  1314     // the params upon which they depend will have already been converted -
  1315     // regardless of ordering.
  1317     foundDependentParam = false;
  1318     for (i = 0; i < paramCount; i++) {
  1319         const nsXPTParamInfo& param = info->params[i];
  1320         MOZ_ASSERT(!param.IsShared(), "[shared] implies [noscript]!");
  1321         if (!param.IsOut() && !param.IsDipper())
  1322             continue;
  1324         const nsXPTType& type = param.GetType();
  1325         if (type.IsDependent()) {
  1326             foundDependentParam = true;
  1327             continue;
  1330         RootedValue val(cx);
  1331         uint8_t type_tag = type.TagPart();
  1332         nsXPTCMiniVariant* pv;
  1334         if (param.IsDipper())
  1335             pv = (nsXPTCMiniVariant*) &nativeParams[i].val.p;
  1336         else
  1337             pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
  1339         if (param.IsRetval())
  1340             val = rval;
  1341         else if (argv[i].isPrimitive())
  1342             break;
  1343         else {
  1344             RootedObject obj(cx, &argv[i].toObject());
  1345             if (!JS_GetPropertyById(cx, obj,
  1346                                     mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
  1347                                     &val))
  1348                 break;
  1351         // setup allocator and/or iid
  1353         if (type_tag == nsXPTType::T_INTERFACE) {
  1354             if (NS_FAILED(GetInterfaceInfo()->
  1355                           GetIIDForParamNoAlloc(methodIndex, &param,
  1356                                                 &param_iid)))
  1357                 break;
  1360 // see bug #961488
  1361 #if (defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(_AIX)) && \
  1362     ((defined(__sparc) && !defined(__sparcv9) && !defined(__sparcv9__)) || \
  1363     (defined(__powerpc__) && !defined (__powerpc64__)))
  1364         if (type_tag == nsXPTType::T_JSVAL) {
  1365             if (!XPCConvert::JSData2Native(*(void**)(&pv->val), val, type,
  1366                                            !param.IsDipper(), &param_iid, nullptr))
  1367                 break;
  1368         } else
  1369 #endif
  1371             if (!XPCConvert::JSData2Native(&pv->val, val, type,
  1372                                            !param.IsDipper(), &param_iid, nullptr))
  1373                 break;
  1377     // if any params were dependent, then we must iterate again to convert them.
  1378     if (foundDependentParam && i == paramCount) {
  1379         for (i = 0; i < paramCount; i++) {
  1380             const nsXPTParamInfo& param = info->params[i];
  1381             if (!param.IsOut())
  1382                 continue;
  1384             const nsXPTType& type = param.GetType();
  1385             if (!type.IsDependent())
  1386                 continue;
  1388             RootedValue val(cx);
  1389             nsXPTCMiniVariant* pv;
  1390             nsXPTType datum_type;
  1391             uint32_t array_count;
  1392             bool isArray = type.IsArray();
  1393             bool isSizedString = isArray ?
  1394                     false :
  1395                     type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
  1396                     type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
  1398             pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
  1400             if (param.IsRetval())
  1401                 val = rval;
  1402             else {
  1403                 RootedObject obj(cx, &argv[i].toObject());
  1404                 if (!JS_GetPropertyById(cx, obj,
  1405                                         mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE),
  1406                                         &val))
  1407                     break;
  1410             // setup allocator and/or iid
  1412             if (isArray) {
  1413                 if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
  1414                                                      &datum_type)))
  1415                     break;
  1416             } else
  1417                 datum_type = type;
  1419             if (datum_type.IsInterfacePointer()) {
  1420                if (!GetInterfaceTypeFromParam(cx, info, param, methodIndex,
  1421                                               datum_type, nativeParams,
  1422                                               &param_iid))
  1423                    break;
  1426             if (isArray || isSizedString) {
  1427                 if (!GetArraySizeFromParam(cx, info, param, methodIndex,
  1428                                            i, nativeParams, &array_count))
  1429                     break;
  1432             if (isArray) {
  1433                 if (array_count &&
  1434                     !XPCConvert::JSArray2Native((void**)&pv->val, val,
  1435                                                 array_count, datum_type,
  1436                                                 &param_iid, nullptr))
  1437                     break;
  1438             } else if (isSizedString) {
  1439                 if (!XPCConvert::JSStringWithSize2Native((void*)&pv->val, val,
  1440                                                          array_count, datum_type,
  1441                                                          nullptr))
  1442                     break;
  1443             } else {
  1444                 if (!XPCConvert::JSData2Native(&pv->val, val, type,
  1445                                                true, &param_iid,
  1446                                                nullptr))
  1447                     break;
  1452     if (i != paramCount) {
  1453         // We didn't manage all the result conversions!
  1454         // We have to cleanup any junk that *did* get converted.
  1456         for (uint8_t k = 0; k < i; k++) {
  1457             const nsXPTParamInfo& param = info->params[k];
  1458             if (!param.IsOut())
  1459                 continue;
  1460             const nsXPTType& type = param.GetType();
  1461             if (!type.deprecated_IsPointer())
  1462                 continue;
  1463             void* p;
  1464             if (!(p = nativeParams[k].val.p))
  1465                 continue;
  1467             if (type.IsArray()) {
  1468                 void** pp;
  1469                 if (nullptr != (pp = *((void***)p))) {
  1470                     // we need to get the array length and iterate the items
  1471                     uint32_t array_count;
  1472                     nsXPTType datum_type;
  1474                     if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, &param,
  1475                                                             1, &datum_type)) &&
  1476                         datum_type.deprecated_IsPointer() &&
  1477                         GetArraySizeFromParam(cx, info, param, methodIndex,
  1478                                               k, nativeParams, &array_count) &&
  1479                         array_count) {
  1481                         CleanupPointerArray(datum_type, array_count, pp);
  1483                     nsMemory::Free(pp);
  1485             } else
  1486                 CleanupPointerTypeObject(type, (void**)p);
  1487             *((void**)p) = nullptr;
  1489     } else {
  1490         // set to whatever the JS code might have set as the result
  1491         retval = pending_result;
  1494     return retval;
  1497 const char*
  1498 nsXPCWrappedJSClass::GetInterfaceName()
  1500     if (!mName)
  1501         mInfo->GetName(&mName);
  1502     return mName;
  1505 static void
  1506 FinalizeStub(JSFreeOp *fop, JSObject *obj)
  1510 static const JSClass XPCOutParamClass = {
  1511     "XPCOutParam",
  1512     0,
  1513     JS_PropertyStub,
  1514     JS_DeletePropertyStub,
  1515     JS_PropertyStub,
  1516     JS_StrictPropertyStub,
  1517     JS_EnumerateStub,
  1518     JS_ResolveStub,
  1519     JS_ConvertStub,
  1520     FinalizeStub,
  1521     nullptr,   /* call */
  1522     nullptr,   /* hasInstance */
  1523     nullptr,   /* construct */
  1524     nullptr    /* trace */
  1525 };
  1527 bool
  1528 xpc::IsOutObject(JSContext* cx, JSObject* obj)
  1530     return js::GetObjectJSClass(obj) == &XPCOutParamClass;
  1533 JSObject*
  1534 xpc::NewOutObject(JSContext* cx, JSObject* scope)
  1536     RootedObject global(cx, JS_GetGlobalForObject(cx, scope));
  1537     return JS_NewObject(cx, nullptr, NullPtr(), global);
  1541 NS_IMETHODIMP
  1542 nsXPCWrappedJSClass::DebugDump(int16_t depth)
  1544 #ifdef DEBUG
  1545     depth-- ;
  1546     XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x with mRefCnt = %d", this, mRefCnt.get()));
  1547     XPC_LOG_INDENT();
  1548         char* name;
  1549         mInfo->GetName(&name);
  1550         XPC_LOG_ALWAYS(("interface name is %s", name));
  1551         if (name)
  1552             nsMemory::Free(name);
  1553         char * iid = mIID.ToString();
  1554         XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
  1555         if (iid)
  1556             NS_Free(iid);
  1557         XPC_LOG_ALWAYS(("InterfaceInfo @ %x", mInfo.get()));
  1558         uint16_t methodCount = 0;
  1559         if (depth) {
  1560             uint16_t i;
  1561             nsCOMPtr<nsIInterfaceInfo> parent;
  1562             XPC_LOG_INDENT();
  1563             mInfo->GetParent(getter_AddRefs(parent));
  1564             XPC_LOG_ALWAYS(("parent @ %x", parent.get()));
  1565             mInfo->GetMethodCount(&methodCount);
  1566             XPC_LOG_ALWAYS(("MethodCount = %d", methodCount));
  1567             mInfo->GetConstantCount(&i);
  1568             XPC_LOG_ALWAYS(("ConstantCount = %d", i));
  1569             XPC_LOG_OUTDENT();
  1571         XPC_LOG_ALWAYS(("mRuntime @ %x", mRuntime));
  1572         XPC_LOG_ALWAYS(("mDescriptors @ %x count = %d", mDescriptors, methodCount));
  1573         if (depth && mDescriptors && methodCount) {
  1574             depth--;
  1575             XPC_LOG_INDENT();
  1576             for (uint16_t i = 0; i < methodCount; i++) {
  1577                 XPC_LOG_ALWAYS(("Method %d is %s%s", \
  1578                                 i, IsReflectable(i) ? "":" NOT ","reflectable"));
  1580             XPC_LOG_OUTDENT();
  1581             depth++;
  1583     XPC_LOG_OUTDENT();
  1584 #endif
  1585     return NS_OK;

mercurial