LCOV - code coverage report
Current view: top level - js/xpconnect/src - XPCWrappedNative.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 575 929 61.9 %
Date: 2017-07-14 16:53:18 Functions: 37 63 58.7 %
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             : /* Wrapper object for reflecting native xpcom objects into JavaScript. */
       8             : 
       9             : #include "xpcprivate.h"
      10             : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
      11             : #include "nsWrapperCacheInlines.h"
      12             : #include "XPCLog.h"
      13             : #include "jsprf.h"
      14             : #include "jsfriendapi.h"
      15             : #include "AccessCheck.h"
      16             : #include "WrapperFactory.h"
      17             : #include "XrayWrapper.h"
      18             : 
      19             : #include "nsContentUtils.h"
      20             : #include "nsCycleCollectionNoteRootCallback.h"
      21             : 
      22             : #include <stdint.h>
      23             : #include "mozilla/DeferredFinalize.h"
      24             : #include "mozilla/Likely.h"
      25             : #include "mozilla/Unused.h"
      26             : #include "mozilla/Sprintf.h"
      27             : #include "mozilla/dom/BindingUtils.h"
      28             : #include <algorithm>
      29             : 
      30             : using namespace xpc;
      31             : using namespace mozilla;
      32             : using namespace mozilla::dom;
      33             : using namespace JS;
      34             : 
      35             : /***************************************************************************/
      36             : 
      37             : NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
      38             : 
      39             : // No need to unlink the JS objects: if the XPCWrappedNative is cycle
      40             : // collected then its mFlatJSObject will be cycle collected too and
      41             : // finalization of the mFlatJSObject will unlink the JS objects (see
      42             : // XPC_WN_NoHelper_Finalize and FlatJSObjectFinalized).
      43           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPCWrappedNative)
      44           0 :     tmp->ExpireWrapper();
      45           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      46             : 
      47           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(XPCWrappedNative)
      48           0 :     if (!tmp->IsValid())
      49           0 :         return NS_OK;
      50             : 
      51           0 :     if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
      52             :         char name[72];
      53           0 :         nsCOMPtr<nsIXPCScriptable> scr = tmp->GetScriptable();
      54           0 :         if (scr)
      55             :             SprintfLiteral(name, "XPCWrappedNative (%s)",
      56           0 :                            scr->GetJSClass()->name);
      57             :         else
      58           0 :             SprintfLiteral(name, "XPCWrappedNative");
      59             : 
      60           0 :         cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
      61             :     } else {
      62           0 :         NS_IMPL_CYCLE_COLLECTION_DESCRIBE(XPCWrappedNative, tmp->mRefCnt.get())
      63             :     }
      64             : 
      65           0 :     if (tmp->HasExternalReference()) {
      66             : 
      67             :         // If our refcount is > 1, our reference to the flat JS object is
      68             :         // considered "strong", and we're going to traverse it.
      69             :         //
      70             :         // If our refcount is <= 1, our reference to the flat JS object is
      71             :         // considered "weak", and we're *not* going to traverse it.
      72             :         //
      73             :         // This reasoning is in line with the slightly confusing lifecycle rules
      74             :         // for XPCWrappedNatives, described in a larger comment below and also
      75             :         // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping
      76             : 
      77           0 :         JSObject* obj = tmp->GetFlatJSObjectPreserveColor();
      78           0 :         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFlatJSObject");
      79           0 :         cb.NoteJSChild(JS::GCCellPtr(obj));
      80             :     }
      81             : 
      82             :     // XPCWrappedNative keeps its native object alive.
      83           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mIdentity");
      84           0 :     cb.NoteXPCOMChild(tmp->GetIdentityObject());
      85             : 
      86           0 :     tmp->NoteTearoffs(cb);
      87             : 
      88           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      89             : 
      90             : void
      91           0 : XPCWrappedNative::Suspect(nsCycleCollectionNoteRootCallback& cb)
      92             : {
      93           0 :     if (!IsValid() || IsWrapperExpired())
      94           0 :         return;
      95             : 
      96           0 :     MOZ_ASSERT(NS_IsMainThread(),
      97             :                "Suspecting wrapped natives from non-main thread");
      98             : 
      99             :     // Only record objects that might be part of a cycle as roots, unless
     100             :     // the callback wants all traces (a debug feature). Do this even if
     101             :     // the XPCWN doesn't own the JS reflector object in case the reflector
     102             :     // keeps alive other C++ things. This is safe because if the reflector
     103             :     // had died the reference from the XPCWN to it would have been cleared.
     104           0 :     JSObject* obj = GetFlatJSObjectPreserveColor();
     105           0 :     if (JS::ObjectIsMarkedGray(obj) || cb.WantAllTraces())
     106           0 :         cb.NoteJSRoot(obj);
     107             : }
     108             : 
     109             : void
     110           0 : XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb)
     111             : {
     112             :     // Tearoffs hold their native object alive. If their JS object hasn't been
     113             :     // finalized yet we'll note the edge between the JS object and the native
     114             :     // (see nsXPConnect::Traverse), but if their JS object has been finalized
     115             :     // then the tearoff is only reachable through the XPCWrappedNative, so we
     116             :     // record an edge here.
     117           0 :     for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
     118           0 :         JSObject* jso = to->GetJSObjectPreserveColor();
     119           0 :         if (!jso) {
     120           0 :             NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative");
     121           0 :             cb.NoteXPCOMChild(to->GetNative());
     122             :         }
     123             :     }
     124           0 : }
     125             : 
     126             : #ifdef XPC_CHECK_CLASSINFO_CLAIMS
     127             : static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper);
     128             : #else
     129             : #define DEBUG_CheckClassInfoClaims(wrapper) ((void)0)
     130             : #endif
     131             : 
     132             : /***************************************************************************/
     133             : static nsresult
     134             : FinishCreate(XPCWrappedNativeScope* Scope,
     135             :              XPCNativeInterface* Interface,
     136             :              nsWrapperCache* cache,
     137             :              XPCWrappedNative* inWrapper,
     138             :              XPCWrappedNative** resultWrapper);
     139             : 
     140             : // static
     141             : //
     142             : // This method handles the special case of wrapping a new global object.
     143             : //
     144             : // The normal code path for wrapping natives goes through
     145             : // XPCConvert::NativeInterface2JSObject, XPCWrappedNative::GetNewOrUsed,
     146             : // and finally into XPCWrappedNative::Init. Unfortunately, this path assumes
     147             : // very early on that we have an XPCWrappedNativeScope and corresponding global
     148             : // JS object, which are the very things we need to create here. So we special-
     149             : // case the logic and do some things in a different order.
     150             : nsresult
     151         260 : XPCWrappedNative::WrapNewGlobal(xpcObjectHelper& nativeHelper,
     152             :                                 nsIPrincipal* principal,
     153             :                                 bool initStandardClasses,
     154             :                                 JS::CompartmentOptions& aOptions,
     155             :                                 XPCWrappedNative** wrappedGlobal)
     156             : {
     157         520 :     AutoJSContext cx;
     158         260 :     nsISupports* identity = nativeHelper.GetCanonical();
     159             : 
     160             :     // The object should specify that it's meant to be global.
     161         260 :     MOZ_ASSERT(nativeHelper.GetScriptableFlags() & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT);
     162             : 
     163             :     // We shouldn't be reusing globals.
     164         260 :     MOZ_ASSERT(!nativeHelper.GetWrapperCache() ||
     165             :                !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor());
     166             : 
     167             :     // Get the nsIXPCScriptable. This will tell us the JSClass of the object
     168             :     // we're going to create.
     169         520 :     nsCOMPtr<nsIXPCScriptable> scrProto;
     170         520 :     nsCOMPtr<nsIXPCScriptable> scrWrapper;
     171         520 :     GatherScriptable(identity, nativeHelper.GetClassInfo(),
     172         780 :                      getter_AddRefs(scrProto), getter_AddRefs(scrWrapper));
     173         260 :     MOZ_ASSERT(scrWrapper);
     174             : 
     175             :     // Finally, we get to the JSClass.
     176         260 :     const JSClass* clasp = scrWrapper->GetJSClass();
     177         260 :     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
     178             : 
     179             :     // Create the global.
     180         260 :     aOptions.creationOptions().setTrace(XPCWrappedNative::Trace);
     181         260 :     if (xpc::SharedMemoryEnabled())
     182         260 :         aOptions.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
     183         520 :     RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions));
     184         260 :     if (!global)
     185           0 :         return NS_ERROR_FAILURE;
     186         260 :     XPCWrappedNativeScope* scope = CompartmentPrivate::Get(global)->scope;
     187             : 
     188             :     // Immediately enter the global's compartment, so that everything else we
     189             :     // create ends up there.
     190         520 :     JSAutoCompartment ac(cx, global);
     191             : 
     192             :     // If requested, initialize the standard classes on the global.
     193         260 :     if (initStandardClasses && ! JS_InitStandardClasses(cx, global))
     194           0 :         return NS_ERROR_FAILURE;
     195             : 
     196             :     // Make a proto.
     197             :     XPCWrappedNativeProto* proto =
     198         260 :         XPCWrappedNativeProto::GetNewOrUsed(scope,
     199             :                                             nativeHelper.GetClassInfo(),
     200             :                                             scrProto,
     201         260 :                                             /* callPostCreatePrototype = */ false);
     202         260 :     if (!proto)
     203           0 :         return NS_ERROR_FAILURE;
     204             : 
     205             :     // Set up the prototype on the global.
     206         260 :     MOZ_ASSERT(proto->GetJSProtoObject());
     207         520 :     RootedObject protoObj(cx, proto->GetJSProtoObject());
     208         260 :     bool success = JS_SplicePrototype(cx, global, protoObj);
     209         260 :     if (!success)
     210           0 :         return NS_ERROR_FAILURE;
     211             : 
     212             :     // Construct the wrapper, which takes over the strong reference to the
     213             :     // native object.
     214             :     RefPtr<XPCWrappedNative> wrapper =
     215         780 :         new XPCWrappedNative(nativeHelper.forgetCanonical(), proto);
     216             : 
     217             :     //
     218             :     // We don't call ::Init() on this wrapper, because our setup requirements
     219             :     // are different for globals. We do our setup inline here, instead.
     220             :     //
     221             : 
     222         260 :     wrapper->mScriptable = scrWrapper;
     223             : 
     224             :     // Set the JS object to the global we already created.
     225         260 :     wrapper->mFlatJSObject = global;
     226         260 :     wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
     227             : 
     228             :     // Set the private to the XPCWrappedNative.
     229         260 :     JS_SetPrivate(global, wrapper);
     230             : 
     231             :     // There are dire comments elsewhere in the code about how a GC can
     232             :     // happen somewhere after wrapper initialization but before the wrapper is
     233             :     // added to the hashtable in FinishCreate(). It's not clear if that can
     234             :     // happen here, but let's just be safe for now.
     235         520 :     AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
     236             : 
     237             :     // Call the common Init finish routine. This mainly just does an AddRef
     238             :     // on behalf of XPConnect (the corresponding Release is in the finalizer
     239             :     // hook), but it does some other miscellaneous things too, so we don't
     240             :     // inline it.
     241         260 :     success = wrapper->FinishInit();
     242         260 :     MOZ_ASSERT(success);
     243             : 
     244             :     // Go through some extra work to find the tearoff. This is kind of silly
     245             :     // on a conceptual level: the point of tearoffs is to cache the results
     246             :     // of QI-ing mIdentity to different interfaces, and we don't need that
     247             :     // since we're dealing with nsISupports. But lots of code expects tearoffs
     248             :     // to exist for everything, so we just follow along.
     249         520 :     RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(&NS_GET_IID(nsISupports));
     250         260 :     MOZ_ASSERT(iface);
     251             :     nsresult status;
     252         260 :     success = wrapper->FindTearOff(iface, false, &status);
     253         260 :     if (!success)
     254           0 :         return status;
     255             : 
     256             :     // Call the common creation finish routine. This does all of the bookkeeping
     257             :     // like inserting the wrapper into the wrapper map and setting up the wrapper
     258             :     // cache.
     259         260 :     nsresult rv = FinishCreate(scope, iface, nativeHelper.GetWrapperCache(),
     260         260 :                                wrapper, wrappedGlobal);
     261         260 :     NS_ENSURE_SUCCESS(rv, rv);
     262             : 
     263         260 :     return NS_OK;
     264             : }
     265             : 
     266             : // static
     267             : nsresult
     268        8007 : XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper,
     269             :                                XPCWrappedNativeScope* Scope,
     270             :                                XPCNativeInterface* Interface,
     271             :                                XPCWrappedNative** resultWrapper)
     272             : {
     273        8007 :     MOZ_ASSERT(Interface);
     274       16014 :     AutoJSContext cx;
     275        8007 :     nsWrapperCache* cache = helper.GetWrapperCache();
     276             : 
     277        8007 :     MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor(),
     278             :                "We assume the caller already checked if it could get the "
     279             :                "wrapper from the cache.");
     280             : 
     281             :     nsresult rv;
     282             : 
     283        8007 :     MOZ_ASSERT(!Scope->GetRuntime()->GCIsRunning(),
     284             :                "XPCWrappedNative::GetNewOrUsed called during GC");
     285             : 
     286        8007 :     nsISupports* identity = helper.GetCanonical();
     287             : 
     288        8007 :     if (!identity) {
     289           0 :         NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
     290           0 :         return NS_ERROR_FAILURE;
     291             :     }
     292             : 
     293       16014 :     RefPtr<XPCWrappedNative> wrapper;
     294             : 
     295        8007 :     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
     296             :     // Some things are nsWrapperCache subclasses but never use the cache, so go
     297             :     // ahead and check our map even if we have a cache and it has no existing
     298             :     // wrapper: we might have an XPCWrappedNative anyway.
     299        8007 :     wrapper = map->Find(identity);
     300             : 
     301        8007 :     if (wrapper) {
     302        2556 :         if (!wrapper->FindTearOff(Interface, false, &rv)) {
     303           0 :             MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
     304           0 :             return rv;
     305             :         }
     306        2556 :         wrapper.forget(resultWrapper);
     307        2556 :         return NS_OK;
     308             :     }
     309             : 
     310             :     // There is a chance that the object wants to have the self-same JSObject
     311             :     // reflection regardless of the scope into which we are reflecting it.
     312             :     // Many DOM objects require this. The scriptable helper specifies this
     313             :     // in preCreate by indicating a 'parent' of a particular scope.
     314             :     //
     315             :     // To handle this we need to get the scriptable helper early and ask it.
     316             :     // It is possible that we will then end up forwarding this entire call
     317             :     // to this same function but with a different scope.
     318             : 
     319             :     // If we are making a wrapper for an nsIClassInfo singleton then
     320             :     // We *don't* want to have it use the prototype meant for instances
     321             :     // of that class.
     322             :     uint32_t classInfoFlags;
     323        5451 :     bool isClassInfoSingleton = helper.GetClassInfo() == helper.Object() &&
     324           0 :                                 NS_SUCCEEDED(helper.GetClassInfo()
     325        5451 :                                                    ->GetFlags(&classInfoFlags)) &&
     326        5451 :                                 (classInfoFlags & nsIClassInfo::SINGLETON_CLASSINFO);
     327             : 
     328        5451 :     nsIClassInfo* info = helper.GetClassInfo();
     329             : 
     330       10902 :     nsCOMPtr<nsIXPCScriptable> scrProto;
     331       10902 :     nsCOMPtr<nsIXPCScriptable> scrWrapper;
     332             : 
     333             :     // Gather scriptable create info if we are wrapping something
     334             :     // other than an nsIClassInfo object. We need to not do this for
     335             :     // nsIClassInfo objects because often nsIClassInfo implementations
     336             :     // are also nsIXPCScriptable helper implementations, but the helper
     337             :     // code is obviously intended for the implementation of the class
     338             :     // described by the nsIClassInfo, not for the class info object
     339             :     // itself.
     340        5451 :     if (!isClassInfoSingleton)
     341       10902 :         GatherScriptable(identity, info, getter_AddRefs(scrProto),
     342       16353 :                          getter_AddRefs(scrWrapper));
     343             : 
     344       10902 :     RootedObject parent(cx, Scope->GetGlobalJSObject());
     345             : 
     346       10902 :     mozilla::Maybe<JSAutoCompartment> ac;
     347             : 
     348        5451 :     if (scrWrapper && scrWrapper->WantPreCreate()) {
     349         664 :         RootedObject plannedParent(cx, parent);
     350             :         nsresult rv =
     351         332 :             scrWrapper->PreCreate(identity, cx, parent, parent.address());
     352         332 :         if (NS_FAILED(rv))
     353           0 :             return rv;
     354         332 :         rv = NS_OK;
     355             : 
     356         332 :         MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
     357             :                    "Xray wrapper being used to parent XPCWrappedNative?");
     358             : 
     359         332 :         MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(parent) == parent,
     360             :                    "Non-global being used to parent XPCWrappedNative?");
     361             : 
     362         332 :         ac.emplace(static_cast<JSContext*>(cx), parent);
     363             : 
     364         332 :         if (parent != plannedParent) {
     365           0 :             XPCWrappedNativeScope* betterScope = ObjectScope(parent);
     366           0 :             MOZ_ASSERT(betterScope != Scope,
     367             :                        "How can we have the same scope for two different globals?");
     368           0 :             return GetNewOrUsed(helper, betterScope, Interface, resultWrapper);
     369             :         }
     370             : 
     371             :         // Take the performance hit of checking the hashtable again in case
     372             :         // the preCreate call caused the wrapper to get created through some
     373             :         // interesting path (the DOM code tends to make this happen sometimes).
     374             : 
     375         332 :         if (cache) {
     376           0 :             RootedObject cached(cx, cache->GetWrapper());
     377           0 :             if (cached)
     378           0 :                 wrapper = XPCWrappedNative::Get(cached);
     379             :         } else {
     380         332 :             wrapper = map->Find(identity);
     381             :         }
     382             : 
     383         332 :         if (wrapper) {
     384           0 :             if (wrapper->FindTearOff(Interface, false, &rv)) {
     385           0 :                 MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
     386           0 :                 return rv;
     387             :             }
     388           0 :             wrapper.forget(resultWrapper);
     389           0 :             return NS_OK;
     390             :         }
     391             :     } else {
     392        5119 :         ac.emplace(static_cast<JSContext*>(cx), parent);
     393             :     }
     394             : 
     395       10902 :     AutoMarkingWrappedNativeProtoPtr proto(cx);
     396             : 
     397             :     // If there is ClassInfo (and we are not building a wrapper for the
     398             :     // nsIClassInfo interface) then we use a wrapper that needs a prototype.
     399             : 
     400             :     // Note that the security check happens inside FindTearOff - after the
     401             :     // wrapper is actually created, but before JS code can see it.
     402             : 
     403        5451 :     if (info && !isClassInfoSingleton) {
     404        4118 :         proto = XPCWrappedNativeProto::GetNewOrUsed(Scope, info, scrProto);
     405        4118 :         if (!proto)
     406           0 :             return NS_ERROR_FAILURE;
     407             : 
     408        8236 :         wrapper = new XPCWrappedNative(helper.forgetCanonical(), proto);
     409             :     } else {
     410        2666 :         RefPtr<XPCNativeInterface> iface = Interface;
     411        1333 :         if (!iface)
     412           0 :             iface = XPCNativeInterface::GetISupports();
     413             : 
     414        2666 :         XPCNativeSetKey key(iface);
     415             :         RefPtr<XPCNativeSet> set =
     416        2666 :             XPCNativeSet::GetNewOrUsed(&key);
     417             : 
     418        1333 :         if (!set)
     419           0 :             return NS_ERROR_FAILURE;
     420             : 
     421        2666 :         wrapper = new XPCWrappedNative(helper.forgetCanonical(), Scope,
     422        2666 :                                        set.forget());
     423             :     }
     424             : 
     425        5451 :     MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
     426             :                "Xray wrapper being used to parent XPCWrappedNative?");
     427             : 
     428             :     // We use an AutoMarkingPtr here because it is possible for JS gc to happen
     429             :     // after we have Init'd the wrapper but *before* we add it to the hashtable.
     430             :     // This would cause the mSet to get collected and we'd later crash. I've
     431             :     // *seen* this happen.
     432       10902 :     AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
     433             : 
     434        5451 :     if (!wrapper->Init(scrWrapper))
     435           0 :         return NS_ERROR_FAILURE;
     436             : 
     437        5451 :     if (!wrapper->FindTearOff(Interface, false, &rv)) {
     438           0 :         MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
     439           0 :         return rv;
     440             :     }
     441             : 
     442        5451 :     return FinishCreate(Scope, Interface, cache, wrapper, resultWrapper);
     443             : }
     444             : 
     445             : static nsresult
     446        5711 : FinishCreate(XPCWrappedNativeScope* Scope,
     447             :              XPCNativeInterface* Interface,
     448             :              nsWrapperCache* cache,
     449             :              XPCWrappedNative* inWrapper,
     450             :              XPCWrappedNative** resultWrapper)
     451             : {
     452       11422 :     AutoJSContext cx;
     453        5711 :     MOZ_ASSERT(inWrapper);
     454             : 
     455        5711 :     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
     456             : 
     457       11422 :     RefPtr<XPCWrappedNative> wrapper;
     458             :     // Deal with the case where the wrapper got created as a side effect
     459             :     // of one of our calls out of this code. Add() returns the (possibly
     460             :     // pre-existing) wrapper that ultimately ends up in the map, which is
     461             :     // what we want.
     462        5711 :     wrapper = map->Add(inWrapper);
     463        5711 :     if (!wrapper)
     464           0 :         return NS_ERROR_FAILURE;
     465             : 
     466        5711 :     if (wrapper == inWrapper) {
     467        5711 :         JSObject* flat = wrapper->GetFlatJSObject();
     468        5711 :         MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor() ||
     469             :                    flat == cache->GetWrapperPreserveColor(),
     470             :                    "This object has a cached wrapper that's different from "
     471             :                    "the JSObject held by its native wrapper?");
     472             : 
     473        5711 :         if (cache && !cache->GetWrapperPreserveColor())
     474           5 :             cache->SetWrapper(flat);
     475             :     }
     476             : 
     477             :     DEBUG_CheckClassInfoClaims(wrapper);
     478        5711 :     wrapper.forget(resultWrapper);
     479        5711 :     return NS_OK;
     480             : }
     481             : 
     482             : // static
     483             : nsresult
     484           0 : XPCWrappedNative::GetUsedOnly(nsISupports* Object,
     485             :                               XPCWrappedNativeScope* Scope,
     486             :                               XPCNativeInterface* Interface,
     487             :                               XPCWrappedNative** resultWrapper)
     488             : {
     489           0 :     AutoJSContext cx;
     490           0 :     MOZ_ASSERT(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object");
     491           0 :     MOZ_ASSERT(Interface);
     492             : 
     493           0 :     RefPtr<XPCWrappedNative> wrapper;
     494           0 :     nsWrapperCache* cache = nullptr;
     495           0 :     CallQueryInterface(Object, &cache);
     496           0 :     if (cache) {
     497           0 :         RootedObject flat(cx, cache->GetWrapper());
     498           0 :         if (!flat) {
     499           0 :             *resultWrapper = nullptr;
     500           0 :             return NS_OK;
     501             :         }
     502           0 :         wrapper = XPCWrappedNative::Get(flat);
     503             :     } else {
     504           0 :         nsCOMPtr<nsISupports> identity = do_QueryInterface(Object);
     505             : 
     506           0 :         if (!identity) {
     507           0 :             NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
     508           0 :             return NS_ERROR_FAILURE;
     509             :         }
     510             : 
     511           0 :         Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
     512             : 
     513           0 :         wrapper = map->Find(identity);
     514           0 :         if (!wrapper) {
     515           0 :             *resultWrapper = nullptr;
     516           0 :             return NS_OK;
     517             :         }
     518             :     }
     519             : 
     520             :     nsresult rv;
     521           0 :     if (!wrapper->FindTearOff(Interface, false, &rv)) {
     522           0 :         MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
     523           0 :         return rv;
     524             :     }
     525             : 
     526           0 :     wrapper.forget(resultWrapper);
     527           0 :     return NS_OK;
     528             : }
     529             : 
     530             : // This ctor is used if this object will have a proto.
     531        4378 : XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
     532        4378 :                                    XPCWrappedNativeProto* aProto)
     533             :     : mMaybeProto(aProto),
     534        4378 :       mSet(aProto->GetSet())
     535             : {
     536        4378 :     MOZ_ASSERT(NS_IsMainThread());
     537             : 
     538        4378 :     mIdentity = aIdentity;
     539        4378 :     mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
     540             : 
     541        4378 :     MOZ_ASSERT(mMaybeProto, "bad ctor param");
     542        4378 :     MOZ_ASSERT(mSet, "bad ctor param");
     543        4378 : }
     544             : 
     545             : // This ctor is used if this object will NOT have a proto.
     546        1333 : XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
     547             :                                    XPCWrappedNativeScope* aScope,
     548        1333 :                                    already_AddRefed<XPCNativeSet>&& aSet)
     549             : 
     550        1333 :     : mMaybeScope(TagScope(aScope)),
     551        2666 :       mSet(aSet)
     552             : {
     553        1333 :     MOZ_ASSERT(NS_IsMainThread());
     554             : 
     555        1333 :     mIdentity = aIdentity;
     556        1333 :     mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
     557             : 
     558        1333 :     MOZ_ASSERT(aScope, "bad ctor param");
     559        1333 :     MOZ_ASSERT(mSet, "bad ctor param");
     560        1333 : }
     561             : 
     562           0 : XPCWrappedNative::~XPCWrappedNative()
     563             : {
     564           0 :     Destroy();
     565           0 : }
     566             : 
     567             : void
     568           0 : XPCWrappedNative::Destroy()
     569             : {
     570           0 :     mScriptable = nullptr;
     571             : 
     572             : #ifdef DEBUG
     573             :     // Check that this object has already been swept from the map.
     574           0 :     XPCWrappedNativeScope* scope = GetScope();
     575           0 :     if (scope) {
     576           0 :         Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
     577           0 :         MOZ_ASSERT(map->Find(GetIdentityObject()) != this);
     578             :     }
     579             : #endif
     580             : 
     581           0 :     if (mIdentity) {
     582           0 :         XPCJSRuntime* rt = GetRuntime();
     583           0 :         if (rt && rt->GetDoingFinalization()) {
     584           0 :             DeferredFinalize(mIdentity.forget().take());
     585             :         } else {
     586           0 :             mIdentity = nullptr;
     587             :         }
     588             :     }
     589             : 
     590           0 :     mMaybeScope = nullptr;
     591           0 : }
     592             : 
     593             : // This is factored out so that it can be called publicly.
     594             : // static
     595             : nsIXPCScriptable*
     596        4378 : XPCWrappedNative::GatherProtoScriptable(nsIClassInfo* classInfo)
     597             : {
     598        4378 :     MOZ_ASSERT(classInfo, "bad param");
     599             : 
     600        4378 :     nsXPCClassInfo* classInfoHelper = nullptr;
     601        4378 :     CallQueryInterface(classInfo, &classInfoHelper);
     602        4378 :     if (classInfoHelper) {
     603             :         nsCOMPtr<nsIXPCScriptable> helper =
     604         116 :           dont_AddRef(static_cast<nsIXPCScriptable*>(classInfoHelper));
     605          58 :         return helper;
     606             :     }
     607             : 
     608        8640 :     nsCOMPtr<nsIXPCScriptable> helper;
     609        4320 :     nsresult rv = classInfo->GetScriptableHelper(getter_AddRefs(helper));
     610        4320 :     if (NS_SUCCEEDED(rv) && helper) {
     611        1541 :         return helper;
     612             :     }
     613             : 
     614        2779 :     return nullptr;
     615             : }
     616             : 
     617             : // static
     618             : void
     619        5711 : XPCWrappedNative::GatherScriptable(nsISupports* aObj,
     620             :                                    nsIClassInfo* aClassInfo,
     621             :                                    nsIXPCScriptable** aScrProto,
     622             :                                    nsIXPCScriptable** aScrWrapper)
     623             : {
     624        5711 :     MOZ_ASSERT(!*aScrProto, "bad param");
     625        5711 :     MOZ_ASSERT(!*aScrWrapper, "bad param");
     626             : 
     627       11364 :     nsCOMPtr<nsIXPCScriptable> scrProto;
     628       11364 :     nsCOMPtr<nsIXPCScriptable> scrWrapper;
     629             : 
     630             :     // Get the class scriptable helper (if present)
     631        5711 :     if (aClassInfo) {
     632        4378 :         scrProto = GatherProtoScriptable(aClassInfo);
     633             : 
     634        4378 :         if (scrProto && scrProto->DontAskInstanceForScriptable()) {
     635          58 :             scrWrapper = scrProto;
     636          58 :             scrProto.forget(aScrProto);
     637          58 :             scrWrapper.forget(aScrWrapper);
     638          58 :             return;
     639             :         }
     640             :     }
     641             : 
     642             :     // Do the same for the wrapper specific scriptable
     643        5653 :     scrWrapper = do_QueryInterface(aObj);
     644        5653 :     if (scrWrapper) {
     645             :         // A whole series of assertions to catch bad uses of scriptable flags on
     646             :         // the scrWrapper...
     647             : 
     648             :         // Can't set WANT_PRECREATE on an instance scriptable without also
     649             :         // setting it on the class scriptable.
     650        2396 :         MOZ_ASSERT_IF(scrWrapper->WantPreCreate(),
     651             :                       scrProto && scrProto->WantPreCreate());
     652             : 
     653             :         // Can't set DONT_ENUM_QUERY_INTERFACE on an instance scriptable
     654             :         // without also setting it on the class scriptable (if present).
     655        2396 :         MOZ_ASSERT_IF(scrWrapper->DontEnumQueryInterface() && scrProto,
     656             :                       scrProto->DontEnumQueryInterface());
     657             : 
     658             :         // Can't set DONT_ASK_INSTANCE_FOR_SCRIPTABLE on an instance scriptable
     659             :         // without also setting it on the class scriptable.
     660        2396 :         MOZ_ASSERT_IF(scrWrapper->DontAskInstanceForScriptable(),
     661             :                       scrProto && scrProto->DontAskInstanceForScriptable());
     662             : 
     663             :         // Can't set CLASSINFO_INTERFACES_ONLY on an instance scriptable
     664             :         // without also setting it on the class scriptable (if present).
     665        2396 :         MOZ_ASSERT_IF(scrWrapper->ClassInfoInterfacesOnly() && scrProto,
     666             :                       scrProto->ClassInfoInterfacesOnly());
     667             : 
     668             :         // Can't set ALLOW_PROP_MODS_DURING_RESOLVE on an instance scriptable
     669             :         // without also setting it on the class scriptable (if present).
     670        2396 :         MOZ_ASSERT_IF(scrWrapper->AllowPropModsDuringResolve() && scrProto,
     671             :                       scrProto->AllowPropModsDuringResolve());
     672             : 
     673             :         // Can't set ALLOW_PROP_MODS_TO_PROTOTYPE on an instance scriptable
     674             :         // without also setting it on the class scriptable (if present).
     675        2396 :         MOZ_ASSERT_IF(scrWrapper->AllowPropModsToPrototype() && scrProto,
     676             :                       scrProto->AllowPropModsToPrototype());
     677             :     } else {
     678        3257 :         scrWrapper = scrProto;
     679             :     }
     680             : 
     681        5653 :     scrProto.forget(aScrProto);
     682        5653 :     scrWrapper.forget(aScrWrapper);
     683             : }
     684             : 
     685             : bool
     686        5451 : XPCWrappedNative::Init(nsIXPCScriptable* aScriptable)
     687             : {
     688       10902 :     AutoJSContext cx;
     689             : 
     690             :     // Setup our scriptable...
     691        5451 :     MOZ_ASSERT(!mScriptable);
     692        5451 :     mScriptable = aScriptable;
     693             : 
     694             :     // create our flatJSObject
     695             : 
     696             :     const JSClass* jsclazz = mScriptable
     697        5451 :                            ? mScriptable->GetJSClass()
     698       10902 :                            : Jsvalify(&XPC_WN_NoHelper_JSClass);
     699             : 
     700             :     // We should have the global jsclass flag if and only if we're a global.
     701        5451 :     MOZ_ASSERT_IF(mScriptable, !!mScriptable->IsGlobalObject() ==
     702             :                                !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
     703             : 
     704        5451 :     MOZ_ASSERT(jsclazz &&
     705             :                jsclazz->name &&
     706             :                jsclazz->flags &&
     707             :                jsclazz->getResolve() &&
     708             :                jsclazz->hasFinalize(), "bad class");
     709             : 
     710             :     // XXXbz JS_GetObjectPrototype wants an object, even though it then asserts
     711             :     // that this object is same-compartment with cx, which means it could just
     712             :     // use the cx global...
     713       10902 :     RootedObject global(cx, CurrentGlobalOrNull(cx));
     714       17686 :     RootedObject protoJSObject(cx, HasProto() ?
     715        4118 :                                    GetProto()->GetJSProtoObject() :
     716       19019 :                                    JS_GetObjectPrototype(cx, global));
     717        5451 :     if (!protoJSObject) {
     718           0 :         return false;
     719             :     }
     720             : 
     721        5451 :     mFlatJSObject = JS_NewObjectWithGivenProto(cx, jsclazz, protoJSObject);
     722        5451 :     if (!mFlatJSObject) {
     723           0 :         mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
     724           0 :         return false;
     725             :     }
     726             : 
     727        5451 :     mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
     728        5451 :     JS_SetPrivate(mFlatJSObject, this);
     729             : 
     730        5451 :     return FinishInit();
     731             : }
     732             : 
     733             : bool
     734        5711 : XPCWrappedNative::FinishInit()
     735             : {
     736       11422 :     AutoJSContext cx;
     737             : 
     738             :     // This reference will be released when mFlatJSObject is finalized.
     739             :     // Since this reference will push the refcount to 2 it will also root
     740             :     // mFlatJSObject;
     741        5711 :     MOZ_ASSERT(1 == mRefCnt, "unexpected refcount value");
     742        5711 :     NS_ADDREF(this);
     743             : 
     744             :     // A hack for bug 517665, increase the probability for GC.
     745        5711 :     JS_updateMallocCounter(cx, 2 * sizeof(XPCWrappedNative));
     746             : 
     747       11422 :     return true;
     748             : }
     749             : 
     750             : 
     751        9968 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCWrappedNative)
     752        4257 :   NS_INTERFACE_MAP_ENTRY(nsIXPConnectWrappedNative)
     753         264 :   NS_INTERFACE_MAP_ENTRY(nsIXPConnectJSObjectHolder)
     754           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPConnectWrappedNative)
     755           0 : NS_INTERFACE_MAP_END
     756             : 
     757       24025 : NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCWrappedNative)
     758             : 
     759             : // Release calls Destroy() immediately when the refcount drops to 0 to
     760             : // clear the weak references nsXPConnect has to XPCWNs and to ensure there
     761             : // are no pointers to dying protos.
     762       18312 : NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(XPCWrappedNative, Destroy())
     763             : 
     764             : /*
     765             :  *  Wrapped Native lifetime management is messy!
     766             :  *
     767             :  *  - At creation we push the refcount to 2 (only one of which is owned by
     768             :  *    the native caller that caused the wrapper creation).
     769             :  *  - During the JS GC Mark phase we mark any wrapper with a refcount > 1.
     770             :  *  - The *only* thing that can make the wrapper get destroyed is the
     771             :  *    finalization of mFlatJSObject. And *that* should only happen if the only
     772             :  *    reference is the single extra (internal) reference we hold.
     773             :  *
     774             :  *  - The wrapper has a pointer to the nsISupports 'view' of the wrapped native
     775             :  *    object i.e... mIdentity. This is held until the wrapper's refcount goes
     776             :  *    to zero and the wrapper is released, or until an expired wrapper (i.e.,
     777             :  *    one unlinked by the cycle collector) has had its JS object finalized.
     778             :  *
     779             :  *  - The wrapper also has 'tearoffs'. It has one tearoff for each interface
     780             :  *    that is actually used on the native object. 'Used' means we have either
     781             :  *    needed to QueryInterface to verify the availability of that interface
     782             :  *    of that we've had to QueryInterface in order to actually make a call
     783             :  *    into the wrapped object via the pointer for the given interface.
     784             :  *
     785             :  *  - Each tearoff's 'mNative' member (if non-null) indicates one reference
     786             :  *    held by our wrapper on the wrapped native for the given interface
     787             :  *    associated with the tearoff. If we release that reference then we set
     788             :  *    the tearoff's 'mNative' to null.
     789             :  *
     790             :  *  - We use the occasion of the JavaScript GCCallback for the JSGC_MARK_END
     791             :  *    event to scan the tearoffs of all wrappers for non-null mNative members
     792             :  *    that represent unused references. We can tell that a given tearoff's
     793             :  *    mNative is unused by noting that no live XPCCallContexts hold a pointer
     794             :  *    to the tearoff.
     795             :  *
     796             :  *  - As a time/space tradeoff we may decide to not do this scanning on
     797             :  *    *every* JavaScript GC. We *do* want to do this *sometimes* because
     798             :  *    we want to allow for wrapped native's to do their own tearoff patterns.
     799             :  *    So, we want to avoid holding references to interfaces that we don't need.
     800             :  *    At the same time, we don't want to be bracketing every call into a
     801             :  *    wrapped native object with a QueryInterface/Release pair. And we *never*
     802             :  *    make a call into the object except via the correct interface for which
     803             :  *    we've QI'd.
     804             :  *
     805             :  *  - Each tearoff *can* have a mJSObject whose lazily resolved properties
     806             :  *    represent the methods/attributes/constants of that specific interface.
     807             :  *    This is optionally reflected into JavaScript as "foo.nsIFoo" when "foo"
     808             :  *    is the name of mFlatJSObject and "nsIFoo" is the name of the given
     809             :  *    interface associated with the tearoff. When we create the tearoff's
     810             :  *    mJSObject we set it's parent to be mFlatJSObject. This way we know that
     811             :  *    when mFlatJSObject get's collected there are no outstanding reachable
     812             :  *    tearoff mJSObjects. Note that we must clear the private of any lingering
     813             :  *    mJSObjects at this point because we have no guarentee of the *order* of
     814             :  *    finalization within a given gc cycle.
     815             :  */
     816             : 
     817             : void
     818           0 : XPCWrappedNative::FlatJSObjectFinalized()
     819             : {
     820           0 :     if (!IsValid())
     821           0 :         return;
     822             : 
     823             :     // Iterate the tearoffs and null out each of their JSObject's privates.
     824             :     // This will keep them from trying to access their pointers to the
     825             :     // dying tearoff object. We can safely assume that those remaining
     826             :     // JSObjects are about to be finalized too.
     827             : 
     828           0 :     for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
     829           0 :         JSObject* jso = to->GetJSObjectPreserveColor();
     830           0 :         if (jso) {
     831           0 :             JS_SetPrivate(jso, nullptr);
     832             : #ifdef DEBUG
     833           0 :             JS_UpdateWeakPointerAfterGCUnbarriered(&jso);
     834           0 :             MOZ_ASSERT(!jso);
     835             : #endif
     836           0 :             to->JSObjectFinalized();
     837             :         }
     838             : 
     839             :         // We also need to release any native pointers held...
     840           0 :         RefPtr<nsISupports> native = to->TakeNative();
     841           0 :         if (native && GetRuntime()) {
     842           0 :             DeferredFinalize(native.forget().take());
     843             :         }
     844             : 
     845           0 :         to->SetInterface(nullptr);
     846             :     }
     847             : 
     848           0 :     nsWrapperCache* cache = nullptr;
     849           0 :     CallQueryInterface(mIdentity, &cache);
     850           0 :     if (cache)
     851           0 :         cache->ClearWrapper(mFlatJSObject.unbarrieredGetPtr());
     852             : 
     853           0 :     mFlatJSObject = nullptr;
     854           0 :     mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
     855             : 
     856           0 :     MOZ_ASSERT(mIdentity, "bad pointer!");
     857             : #ifdef XP_WIN
     858             :     // Try to detect free'd pointer
     859             :     MOZ_ASSERT(*(int*)mIdentity.get() != 0xdddddddd, "bad pointer!");
     860             :     MOZ_ASSERT(*(int*)mIdentity.get() != 0,          "bad pointer!");
     861             : #endif
     862             : 
     863           0 :     if (IsWrapperExpired()) {
     864           0 :         Destroy();
     865             :     }
     866             : 
     867             :     // Note that it's not safe to touch mNativeWrapper here since it's
     868             :     // likely that it has already been finalized.
     869             : 
     870           0 :     Release();
     871             : }
     872             : 
     873             : void
     874           0 : XPCWrappedNative::FlatJSObjectMoved(JSObject* obj, const JSObject* old)
     875             : {
     876           0 :     JS::AutoAssertGCCallback inCallback;
     877           0 :     MOZ_ASSERT(mFlatJSObject == old);
     878             : 
     879           0 :     nsWrapperCache* cache = nullptr;
     880           0 :     CallQueryInterface(mIdentity, &cache);
     881           0 :     if (cache)
     882           0 :         cache->UpdateWrapper(obj, old);
     883             : 
     884           0 :     mFlatJSObject = obj;
     885           0 : }
     886             : 
     887             : void
     888           0 : XPCWrappedNative::SystemIsBeingShutDown()
     889             : {
     890           0 :     if (!IsValid())
     891           0 :         return;
     892             : 
     893             :     // The long standing strategy is to leak some objects still held at shutdown.
     894             :     // The general problem is that propagating release out of xpconnect at
     895             :     // shutdown time causes a world of problems.
     896             : 
     897             :     // We leak mIdentity (see above).
     898             : 
     899             :     // Short circuit future finalization.
     900           0 :     JS_SetPrivate(mFlatJSObject, nullptr);
     901           0 :     mFlatJSObject = nullptr;
     902           0 :     mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
     903             : 
     904           0 :     XPCWrappedNativeProto* proto = GetProto();
     905             : 
     906           0 :     if (HasProto())
     907           0 :         proto->SystemIsBeingShutDown();
     908             : 
     909             :     // We don't clear mScriptable here. The destructor will do it.
     910             : 
     911             :     // Cleanup the tearoffs.
     912           0 :     for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
     913           0 :         if (JSObject* jso = to->GetJSObjectPreserveColor()) {
     914           0 :             JS_SetPrivate(jso, nullptr);
     915           0 :             to->SetJSObject(nullptr);
     916             :         }
     917             :         // We leak the tearoff mNative
     918             :         // (for the same reason we leak mIdentity - see above).
     919           0 :         Unused << to->TakeNative().take();
     920           0 :         to->SetInterface(nullptr);
     921             :     }
     922             : }
     923             : 
     924             : /***************************************************************************/
     925             : 
     926             : // Dynamically ensure that two objects don't end up with the same private.
     927             : class MOZ_STACK_CLASS AutoClonePrivateGuard {
     928             : public:
     929             :     AutoClonePrivateGuard(JSContext* cx, JSObject* aOld, JSObject* aNew)
     930             :         : mOldReflector(cx, aOld), mNewReflector(cx, aNew)
     931             :     {
     932             :         MOZ_ASSERT(JS_GetPrivate(aOld) == JS_GetPrivate(aNew));
     933             :     }
     934             : 
     935             :     ~AutoClonePrivateGuard()
     936             :     {
     937             :         if (JS_GetPrivate(mOldReflector)) {
     938             :             JS_SetPrivate(mNewReflector, nullptr);
     939             :         }
     940             :     }
     941             : 
     942             : private:
     943             :     RootedObject mOldReflector;
     944             :     RootedObject mNewReflector;
     945             : };
     946             : 
     947             : bool
     948         233 : XPCWrappedNative::ExtendSet(XPCNativeInterface* aInterface)
     949             : {
     950         233 :     if (!mSet->HasInterface(aInterface)) {
     951         466 :         XPCNativeSetKey key(mSet, aInterface);
     952             :         RefPtr<XPCNativeSet> newSet =
     953         466 :             XPCNativeSet::GetNewOrUsed(&key);
     954         233 :         if (!newSet)
     955           0 :             return false;
     956             : 
     957         233 :         mSet = newSet.forget();
     958             :     }
     959         233 :     return true;
     960             : }
     961             : 
     962             : XPCWrappedNativeTearOff*
     963       20332 : XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface,
     964             :                               bool needJSObject /* = false */,
     965             :                               nsresult* pError /* = nullptr */)
     966             : {
     967       40664 :     AutoJSContext cx;
     968       20332 :     nsresult rv = NS_OK;
     969             :     XPCWrappedNativeTearOff* to;
     970       20332 :     XPCWrappedNativeTearOff* firstAvailable = nullptr;
     971             : 
     972             :     XPCWrappedNativeTearOff* lastTearOff;
     973       38425 :     for (lastTearOff = to = &mFirstTearOff;
     974       38425 :          to;
     975       18093 :          lastTearOff = to, to = to->GetNextTearOff()) {
     976       30706 :         if (to->GetInterface() == aInterface) {
     977       12613 :             if (needJSObject && !to->GetJSObjectPreserveColor()) {
     978           0 :                 AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
     979           0 :                 bool ok = InitTearOffJSObject(to);
     980             :                 // During shutdown, we don't sweep tearoffs.  So make sure
     981             :                 // to unmark manually in case the auto-marker marked us.
     982             :                 // We shouldn't ever be getting here _during_ our
     983             :                 // Mark/Sweep cycle, so this should be safe.
     984           0 :                 to->Unmark();
     985           0 :                 if (!ok) {
     986           0 :                     to = nullptr;
     987           0 :                     rv = NS_ERROR_OUT_OF_MEMORY;
     988             :                 }
     989             :             }
     990       12613 :             if (pError)
     991       12574 :                 *pError = rv;
     992       12613 :             return to;
     993             :         }
     994       18093 :         if (!firstAvailable && to->IsAvailable())
     995        5714 :             firstAvailable = to;
     996             :     }
     997             : 
     998        7719 :     to = firstAvailable;
     999             : 
    1000        7719 :     if (!to) {
    1001        2005 :         to = lastTearOff->AddTearOff();
    1002             :     }
    1003             : 
    1004             :     {
    1005             :         // Scope keeps |tearoff| from leaking across the rest of the function.
    1006       15438 :         AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
    1007        7719 :         rv = InitTearOff(to, aInterface, needJSObject);
    1008             :         // During shutdown, we don't sweep tearoffs.  So make sure to unmark
    1009             :         // manually in case the auto-marker marked us.  We shouldn't ever be
    1010             :         // getting here _during_ our Mark/Sweep cycle, so this should be safe.
    1011        7719 :         to->Unmark();
    1012        7719 :         if (NS_FAILED(rv))
    1013          12 :             to = nullptr;
    1014             :     }
    1015             : 
    1016        7719 :     if (pError)
    1017        7689 :         *pError = rv;
    1018        7719 :     return to;
    1019             : }
    1020             : 
    1021             : XPCWrappedNativeTearOff*
    1022          69 : XPCWrappedNative::FindTearOff(const nsIID& iid) {
    1023         138 :     RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(&iid);
    1024         138 :     return iface ? FindTearOff(iface) : nullptr;
    1025             : }
    1026             : 
    1027             : nsresult
    1028        7719 : XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff,
    1029             :                               XPCNativeInterface* aInterface,
    1030             :                               bool needJSObject)
    1031             : {
    1032       15438 :     AutoJSContext cx;
    1033             : 
    1034             :     // Determine if the object really does this interface...
    1035             : 
    1036        7719 :     const nsIID* iid = aInterface->GetIID();
    1037        7719 :     nsISupports* identity = GetIdentityObject();
    1038             : 
    1039             :     // This is an nsRefPtr instead of an nsCOMPtr because it may not be the
    1040             :     // canonical nsISupports for this object.
    1041       15438 :     RefPtr<nsISupports> qiResult;
    1042             : 
    1043             :     // If the scriptable helper forbids us from reflecting additional
    1044             :     // interfaces, then don't even try the QI, just fail.
    1045       11160 :     if (mScriptable &&
    1046       11296 :         mScriptable->ClassInfoInterfacesOnly() &&
    1047        7858 :         !mSet->HasInterface(aInterface) &&
    1048           3 :         !mSet->HasInterfaceWithAncestor(aInterface)) {
    1049           3 :         return NS_ERROR_NO_INTERFACE;
    1050             :     }
    1051             : 
    1052             :     // We are about to call out to other code.
    1053             :     // So protect our intended tearoff.
    1054             : 
    1055        7716 :     aTearOff->SetReserved();
    1056             : 
    1057        7716 :     if (NS_FAILED(identity->QueryInterface(*iid, getter_AddRefs(qiResult))) || !qiResult) {
    1058           9 :         aTearOff->SetInterface(nullptr);
    1059           9 :         return NS_ERROR_NO_INTERFACE;
    1060             :     }
    1061             : 
    1062             :     // Guard against trying to build a tearoff for a shared nsIClassInfo.
    1063        7707 :     if (iid->Equals(NS_GET_IID(nsIClassInfo))) {
    1064           0 :         nsCOMPtr<nsISupports> alternate_identity(do_QueryInterface(qiResult));
    1065           0 :         if (alternate_identity.get() != identity) {
    1066           0 :             aTearOff->SetInterface(nullptr);
    1067           0 :             return NS_ERROR_NO_INTERFACE;
    1068             :         }
    1069             :     }
    1070             : 
    1071             :     // Guard against trying to build a tearoff for an interface that is
    1072             :     // aggregated and is implemented as a nsIXPConnectWrappedJS using this
    1073             :     // self-same JSObject. The XBL system does this. If we mutate the set
    1074             :     // of this wrapper then we will shadow the method that XBL has added to
    1075             :     // the JSObject that it has inserted in the JS proto chain between our
    1076             :     // JSObject and our XPCWrappedNativeProto's JSObject. If we let this
    1077             :     // set mutation happen then the interface's methods will be added to
    1078             :     // our JSObject, but calls on those methods will get routed up to
    1079             :     // native code and into the wrappedJS - which will do a method lookup
    1080             :     // on *our* JSObject and find the same method and make another call
    1081             :     // into an infinite loop.
    1082             :     // see: http://bugzilla.mozilla.org/show_bug.cgi?id=96725
    1083             : 
    1084             :     // The code in this block also does a check for the double wrapped
    1085             :     // nsIPropertyBag case.
    1086             : 
    1087       15414 :     nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(qiResult));
    1088        7707 :     if (wrappedJS) {
    1089         166 :         RootedObject jso(cx, wrappedJS->GetJSObject());
    1090          83 :         if (jso == mFlatJSObject) {
    1091             :             // The implementing JSObject is the same as ours! Just say OK
    1092             :             // without actually extending the set.
    1093             :             //
    1094             :             // XXX It is a little cheesy to have FindTearOff return an
    1095             :             // 'empty' tearoff. But this is the centralized place to do the
    1096             :             // QI activities on the underlying object. *And* most caller to
    1097             :             // FindTearOff only look for a non-null result and ignore the
    1098             :             // actual tearoff returned. The only callers that do use the
    1099             :             // returned tearoff make sure to check for either a non-null
    1100             :             // JSObject or a matching Interface before proceeding.
    1101             :             // I think we can get away with this bit of ugliness.
    1102             : 
    1103           0 :             aTearOff->SetInterface(nullptr);
    1104           0 :             return NS_OK;
    1105             :         }
    1106             : 
    1107             :         // Decide whether or not to expose nsIPropertyBag to calling
    1108             :         // JS code in the double wrapped case.
    1109             :         //
    1110             :         // Our rule here is that when JSObjects are double wrapped and
    1111             :         // exposed to other JSObjects then the nsIPropertyBag interface
    1112             :         // is only exposed on an 'opt-in' basis; i.e. if the underlying
    1113             :         // JSObject wants other JSObjects to be able to see this interface
    1114             :         // then it must implement QueryInterface and not throw an exception
    1115             :         // when asked for nsIPropertyBag. It need not actually *implement*
    1116             :         // nsIPropertyBag - xpconnect will do that work.
    1117             : 
    1118          83 :         if (iid->Equals(NS_GET_IID(nsIPropertyBag)) && jso) {
    1119           0 :             RefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, *iid);
    1120           0 :             if (clasp) {
    1121           0 :                 RootedObject answer(cx, clasp->CallQueryInterfaceOnJSObject(cx, jso, *iid));
    1122             : 
    1123           0 :                 if (!answer) {
    1124           0 :                     aTearOff->SetInterface(nullptr);
    1125           0 :                     return NS_ERROR_NO_INTERFACE;
    1126             :                 }
    1127             :             }
    1128             :         }
    1129             :     }
    1130             : 
    1131        7707 :     if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateWrapper(cx, *iid, identity,
    1132             :                                                                    GetClassInfo()))) {
    1133             :         // the security manager vetoed. It should have set an exception.
    1134           0 :         aTearOff->SetInterface(nullptr);
    1135           0 :         return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
    1136             :     }
    1137             : 
    1138             :     // If this is not already in our set we need to extend our set.
    1139             :     // Note: we do not cache the result of the previous call to HasInterface()
    1140             :     // because we unlocked and called out in the interim and the result of the
    1141             :     // previous call might not be correct anymore.
    1142             : 
    1143        7707 :     if (!mSet->HasInterface(aInterface) && !ExtendSet(aInterface)) {
    1144           0 :         aTearOff->SetInterface(nullptr);
    1145           0 :         return NS_ERROR_NO_INTERFACE;
    1146             :     }
    1147             : 
    1148        7707 :     aTearOff->SetInterface(aInterface);
    1149        7707 :     aTearOff->SetNative(qiResult);
    1150        7707 :     if (needJSObject && !InitTearOffJSObject(aTearOff))
    1151           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1152             : 
    1153        7707 :     return NS_OK;
    1154             : }
    1155             : 
    1156             : bool
    1157           0 : XPCWrappedNative::InitTearOffJSObject(XPCWrappedNativeTearOff* to)
    1158             : {
    1159           0 :     AutoJSContext cx;
    1160             : 
    1161           0 :     JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass));
    1162           0 :     if (!obj)
    1163           0 :         return false;
    1164             : 
    1165           0 :     JS_SetPrivate(obj, to);
    1166           0 :     to->SetJSObject(obj);
    1167             : 
    1168             :     js::SetReservedSlot(obj, XPC_WN_TEAROFF_FLAT_OBJECT_SLOT,
    1169           0 :                         JS::ObjectValue(*mFlatJSObject));
    1170           0 :     return true;
    1171             : }
    1172             : 
    1173             : /***************************************************************************/
    1174             : 
    1175           0 : static bool Throw(nsresult errNum, XPCCallContext& ccx)
    1176             : {
    1177           0 :     XPCThrower::Throw(errNum, ccx);
    1178           0 :     return false;
    1179             : }
    1180             : 
    1181             : /***************************************************************************/
    1182             : 
    1183             : class MOZ_STACK_CLASS CallMethodHelper
    1184             : {
    1185             :     XPCCallContext& mCallContext;
    1186             :     nsresult mInvokeResult;
    1187             :     nsIInterfaceInfo* const mIFaceInfo;
    1188             :     const nsXPTMethodInfo* mMethodInfo;
    1189             :     nsISupports* const mCallee;
    1190             :     const uint16_t mVTableIndex;
    1191             :     HandleId mIdxValueId;
    1192             : 
    1193             :     AutoTArray<nsXPTCVariant, 8> mDispatchParams;
    1194             :     uint8_t mJSContextIndex; // TODO make const
    1195             :     uint8_t mOptArgcIndex; // TODO make const
    1196             : 
    1197             :     Value* const mArgv;
    1198             :     const uint32_t mArgc;
    1199             : 
    1200             :     MOZ_ALWAYS_INLINE bool
    1201             :     GetArraySizeFromParam(uint8_t paramIndex, HandleValue maybeArray, uint32_t* result);
    1202             : 
    1203             :     MOZ_ALWAYS_INLINE bool
    1204             :     GetInterfaceTypeFromParam(uint8_t paramIndex,
    1205             :                               const nsXPTType& datum_type,
    1206             :                               nsID* result) const;
    1207             : 
    1208             :     MOZ_ALWAYS_INLINE bool
    1209             :     GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const;
    1210             : 
    1211             :     MOZ_ALWAYS_INLINE bool
    1212             :     GatherAndConvertResults();
    1213             : 
    1214             :     MOZ_ALWAYS_INLINE bool
    1215             :     QueryInterfaceFastPath();
    1216             : 
    1217             :     nsXPTCVariant*
    1218       58682 :     GetDispatchParam(uint8_t paramIndex)
    1219             :     {
    1220       58682 :         if (paramIndex >= mJSContextIndex)
    1221        4209 :             paramIndex += 1;
    1222       58682 :         if (paramIndex >= mOptArgcIndex)
    1223        5264 :             paramIndex += 1;
    1224       58682 :         return &mDispatchParams[paramIndex];
    1225             :     }
    1226             :     const nsXPTCVariant*
    1227          53 :     GetDispatchParam(uint8_t paramIndex) const
    1228             :     {
    1229          53 :         return const_cast<CallMethodHelper*>(this)->GetDispatchParam(paramIndex);
    1230             :     }
    1231             : 
    1232             :     MOZ_ALWAYS_INLINE bool InitializeDispatchParams();
    1233             : 
    1234             :     MOZ_ALWAYS_INLINE bool ConvertIndependentParams(bool* foundDependentParam);
    1235             :     MOZ_ALWAYS_INLINE bool ConvertIndependentParam(uint8_t i);
    1236             :     MOZ_ALWAYS_INLINE bool ConvertDependentParams();
    1237             :     MOZ_ALWAYS_INLINE bool ConvertDependentParam(uint8_t i);
    1238             : 
    1239             :     MOZ_ALWAYS_INLINE void CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type);
    1240             : 
    1241             :     MOZ_ALWAYS_INLINE bool AllocateStringClass(nsXPTCVariant* dp,
    1242             :                                                const nsXPTParamInfo& paramInfo);
    1243             : 
    1244             :     MOZ_ALWAYS_INLINE nsresult Invoke();
    1245             : 
    1246             : public:
    1247             : 
    1248       11996 :     explicit CallMethodHelper(XPCCallContext& ccx)
    1249       11996 :         : mCallContext(ccx)
    1250             :         , mInvokeResult(NS_ERROR_UNEXPECTED)
    1251       11996 :         , mIFaceInfo(ccx.GetInterface()->GetInterfaceInfo())
    1252             :         , mMethodInfo(nullptr)
    1253       11996 :         , mCallee(ccx.GetTearOff()->GetNative())
    1254       11996 :         , mVTableIndex(ccx.GetMethodIndex())
    1255       11996 :         , mIdxValueId(ccx.GetContext()->GetStringID(XPCJSContext::IDX_VALUE))
    1256             :         , mJSContextIndex(UINT8_MAX)
    1257             :         , mOptArgcIndex(UINT8_MAX)
    1258       11996 :         , mArgv(ccx.GetArgv())
    1259       71976 :         , mArgc(ccx.GetArgc())
    1260             : 
    1261             :     {
    1262             :         // Success checked later.
    1263       11996 :         mIFaceInfo->GetMethodInfo(mVTableIndex, &mMethodInfo);
    1264       11996 :     }
    1265             : 
    1266             :     ~CallMethodHelper();
    1267             : 
    1268             :     MOZ_ALWAYS_INLINE bool Call();
    1269             : 
    1270             : };
    1271             : 
    1272             : // static
    1273             : bool
    1274       11996 : XPCWrappedNative::CallMethod(XPCCallContext& ccx,
    1275             :                              CallMode mode /*= CALL_METHOD */)
    1276             : {
    1277       11996 :     nsresult rv = ccx.CanCallNow();
    1278       11996 :     if (NS_FAILED(rv)) {
    1279           0 :         return Throw(rv, ccx);
    1280             :     }
    1281             : 
    1282       11996 :     return CallMethodHelper(ccx).Call();
    1283             : }
    1284             : 
    1285             : bool
    1286       11996 : CallMethodHelper::Call()
    1287             : {
    1288       11996 :     mCallContext.SetRetVal(JS::UndefinedValue());
    1289             : 
    1290       11996 :     mCallContext.GetContext()->SetPendingException(nullptr);
    1291             : 
    1292       11996 :     if (mVTableIndex == 0) {
    1293         129 :         return QueryInterfaceFastPath();
    1294             :     }
    1295             : 
    1296       11867 :     if (!mMethodInfo) {
    1297           0 :         Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, mCallContext);
    1298           0 :         return false;
    1299             :     }
    1300             : 
    1301       11867 :     if (!InitializeDispatchParams())
    1302           0 :         return false;
    1303             : 
    1304             :     // Iterate through the params doing conversions of independent params only.
    1305             :     // When we later convert the dependent params (if any) we will know that
    1306             :     // the params upon which they depend will have already been converted -
    1307             :     // regardless of ordering.
    1308       11867 :     bool foundDependentParam = false;
    1309       11867 :     if (!ConvertIndependentParams(&foundDependentParam))
    1310           0 :         return false;
    1311             : 
    1312       11867 :     if (foundDependentParam && !ConvertDependentParams())
    1313           0 :         return false;
    1314             : 
    1315       11867 :     mInvokeResult = Invoke();
    1316             : 
    1317       11866 :     if (JS_IsExceptionPending(mCallContext)) {
    1318           0 :         return false;
    1319             :     }
    1320             : 
    1321       11866 :     if (NS_FAILED(mInvokeResult)) {
    1322        1978 :         ThrowBadResult(mInvokeResult, mCallContext);
    1323        1978 :         return false;
    1324             :     }
    1325             : 
    1326        9888 :     return GatherAndConvertResults();
    1327             : }
    1328             : 
    1329       23990 : CallMethodHelper::~CallMethodHelper()
    1330             : {
    1331       11995 :     uint8_t paramCount = mMethodInfo->GetParamCount();
    1332       11995 :     if (mDispatchParams.Length()) {
    1333       36882 :         for (uint8_t i = 0; i < paramCount; i++) {
    1334       25032 :             nsXPTCVariant* dp = GetDispatchParam(i);
    1335       25032 :             const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
    1336             : 
    1337       25032 :             if (paramInfo.GetType().IsArray()) {
    1338          32 :                 void* p = dp->val.p;
    1339          32 :                 if (!p)
    1340           5 :                     continue;
    1341             : 
    1342             :                 // Clean up the array contents if necessary.
    1343          27 :                 if (dp->DoesValNeedCleanup()) {
    1344             :                     // We need some basic information to properly destroy the array.
    1345          27 :                     uint32_t array_count = 0;
    1346          27 :                     nsXPTType datum_type;
    1347          54 :                     if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count) ||
    1348          27 :                         !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex,
    1349             :                                                                   &paramInfo,
    1350             :                                                                   1, &datum_type))) {
    1351             :                         // XXXbholley - I'm not convinced that the above calls will
    1352             :                         // ever fail.
    1353           0 :                         NS_ERROR("failed to get array information, we'll leak here");
    1354           0 :                         continue;
    1355             :                     }
    1356             : 
    1357             :                     // Loop over the array contents. For each one, we create a
    1358             :                     // dummy 'val' and pass it to the cleanup helper.
    1359         369 :                     for (uint32_t k = 0; k < array_count; k++) {
    1360         342 :                         nsXPTCMiniVariant v;
    1361         342 :                         v.val.p = static_cast<void**>(p)[k];
    1362         342 :                         CleanupParam(v, datum_type);
    1363             :                     }
    1364             :                 }
    1365             : 
    1366             :                 // always free the array itself
    1367          27 :                 free(p);
    1368             :             } else {
    1369             :                 // Clean up single parameters (if requested).
    1370       25000 :                 if (dp->DoesValNeedCleanup())
    1371       18348 :                     CleanupParam(*dp, dp->type);
    1372             :             }
    1373             :         }
    1374             :     }
    1375       11995 : }
    1376             : 
    1377             : bool
    1378          59 : CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex,
    1379             :                                         HandleValue maybeArray,
    1380             :                                         uint32_t* result)
    1381             : {
    1382             :     nsresult rv;
    1383          59 :     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
    1384             : 
    1385             :     // TODO fixup the various exceptions that are thrown
    1386             : 
    1387          59 :     rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, &paramInfo, 0, &paramIndex);
    1388          59 :     if (NS_FAILED(rv))
    1389           0 :         return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
    1390             : 
    1391             :     // If the array length wasn't passed, it might have been listed as optional.
    1392             :     // When converting arguments from JS to C++, we pass the array as |maybeArray|,
    1393             :     // and give ourselves the chance to infer the length. Once we have it, we stick
    1394             :     // it in the right slot so that we can find it again when cleaning up the params.
    1395             :     // from the array.
    1396          59 :     if (paramIndex >= mArgc && maybeArray.isObject()) {
    1397           0 :         MOZ_ASSERT(mMethodInfo->GetParam(paramIndex).IsOptional());
    1398           0 :         RootedObject arrayOrNull(mCallContext, maybeArray.isObject() ? &maybeArray.toObject()
    1399           0 :                                                                      : nullptr);
    1400             : 
    1401             :         bool isArray;
    1402           0 :         if (!JS_IsArrayObject(mCallContext, maybeArray, &isArray) ||
    1403           0 :             !isArray ||
    1404           0 :             !JS_GetArrayLength(mCallContext, arrayOrNull, &GetDispatchParam(paramIndex)->val.u32))
    1405             :         {
    1406           0 :             return Throw(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, mCallContext);
    1407             :         }
    1408             :     }
    1409             : 
    1410          59 :     *result = GetDispatchParam(paramIndex)->val.u32;
    1411             : 
    1412          59 :     return true;
    1413             : }
    1414             : 
    1415             : bool
    1416        2240 : CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex,
    1417             :                                             const nsXPTType& datum_type,
    1418             :                                             nsID* result) const
    1419             : {
    1420             :     nsresult rv;
    1421        2240 :     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
    1422        2240 :     uint8_t tag = datum_type.TagPart();
    1423             : 
    1424             :     // TODO fixup the various exceptions that are thrown
    1425             : 
    1426        2240 :     if (tag == nsXPTType::T_INTERFACE) {
    1427        2187 :         rv = mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo, result);
    1428        2187 :         if (NS_FAILED(rv))
    1429           0 :             return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
    1430           0 :                                  paramIndex, mCallContext);
    1431          53 :     } else if (tag == nsXPTType::T_INTERFACE_IS) {
    1432         106 :         rv = mIFaceInfo->GetInterfaceIsArgNumberForParam(mVTableIndex, &paramInfo,
    1433         106 :                                                          &paramIndex);
    1434          53 :         if (NS_FAILED(rv))
    1435           0 :             return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
    1436             : 
    1437          53 :         nsID* p = (nsID*) GetDispatchParam(paramIndex)->val.p;
    1438          53 :         if (!p)
    1439           0 :             return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
    1440           0 :                                  paramIndex, mCallContext);
    1441          53 :         *result = *p;
    1442             :     }
    1443        2240 :     return true;
    1444             : }
    1445             : 
    1446             : bool
    1447       24638 : CallMethodHelper::GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const
    1448             : {
    1449       24638 :     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
    1450             : 
    1451       24638 :     MOZ_ASSERT(!paramInfo.IsDipper(), "Dipper params are handled separately");
    1452       24638 :     if (paramInfo.IsOut() && !paramInfo.IsRetval()) {
    1453          37 :         MOZ_ASSERT(paramIndex < mArgc || paramInfo.IsOptional(),
    1454             :                    "Expected either enough arguments or an optional argument");
    1455          37 :         Value arg = paramIndex < mArgc ? mArgv[paramIndex] : JS::NullValue();
    1456          37 :         if (paramIndex < mArgc) {
    1457          18 :             RootedObject obj(mCallContext);
    1458           9 :             if (!arg.isPrimitive())
    1459           9 :                 obj = &arg.toObject();
    1460           9 :             if (!obj || !JS_GetPropertyById(mCallContext, obj, mIdxValueId, srcp)) {
    1461             :                 // Explicitly passed in unusable value for out param.  Note
    1462             :                 // that if i >= mArgc we already know that |arg| is JS::NullValue(),
    1463             :                 // and that's ok.
    1464           0 :                 ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, paramIndex,
    1465           0 :                               mCallContext);
    1466           0 :                 return false;
    1467             :             }
    1468             :         }
    1469             :     }
    1470             : 
    1471       24638 :     return true;
    1472             : }
    1473             : 
    1474             : bool
    1475        9888 : CallMethodHelper::GatherAndConvertResults()
    1476             : {
    1477             :     // now we iterate through the native params to gather and convert results
    1478        9888 :     uint8_t paramCount = mMethodInfo->GetParamCount();
    1479       29065 :     for (uint8_t i = 0; i < paramCount; i++) {
    1480       19177 :         const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
    1481       19177 :         if (!paramInfo.IsOut() && !paramInfo.IsDipper())
    1482       10671 :             continue;
    1483             : 
    1484        8506 :         const nsXPTType& type = paramInfo.GetType();
    1485        8506 :         nsXPTCVariant* dp = GetDispatchParam(i);
    1486       17012 :         RootedValue v(mCallContext, NullValue());
    1487        8506 :         uint32_t array_count = 0;
    1488        8506 :         nsXPTType datum_type;
    1489        8506 :         bool isArray = type.IsArray();
    1490       16987 :         bool isSizedString = isArray ?
    1491             :                 false :
    1492       16962 :                 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
    1493       16987 :                 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
    1494             : 
    1495        8506 :         if (isArray) {
    1496          25 :             if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
    1497             :                                                       &datum_type))) {
    1498           0 :                 Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
    1499           0 :                 return false;
    1500             :             }
    1501             :         } else
    1502        8481 :             datum_type = type;
    1503             : 
    1504        8506 :         if (isArray || isSizedString) {
    1505          25 :             if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count))
    1506           0 :                 return false;
    1507             :         }
    1508             : 
    1509             :         nsID param_iid;
    1510       10743 :         if (datum_type.IsInterfacePointer() &&
    1511        2237 :             !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
    1512           0 :             return false;
    1513             : 
    1514             :         nsresult err;
    1515        8506 :         if (isArray) {
    1516          25 :             if (!XPCConvert::NativeArray2JS(&v, (const void**)&dp->val,
    1517             :                                             datum_type, &param_iid,
    1518             :                                             array_count, &err)) {
    1519             :                 // XXX need exception scheme for arrays to indicate bad element
    1520           0 :                 ThrowBadParam(err, i, mCallContext);
    1521           0 :                 return false;
    1522             :             }
    1523        8481 :         } else if (isSizedString) {
    1524           0 :             if (!XPCConvert::NativeStringWithSize2JS(&v,
    1525           0 :                                                      (const void*)&dp->val,
    1526             :                                                      datum_type,
    1527             :                                                      array_count, &err)) {
    1528           0 :                 ThrowBadParam(err, i, mCallContext);
    1529           0 :                 return false;
    1530             :             }
    1531             :         } else {
    1532        8481 :             if (!XPCConvert::NativeData2JS(&v, &dp->val, datum_type,
    1533             :                                            &param_iid, &err)) {
    1534           0 :                 ThrowBadParam(err, i, mCallContext);
    1535           0 :                 return false;
    1536             :             }
    1537             :         }
    1538             : 
    1539        8506 :         if (paramInfo.IsRetval()) {
    1540        8466 :             mCallContext.SetRetVal(v);
    1541          40 :         } else if (i < mArgc) {
    1542             :             // we actually assured this before doing the invoke
    1543          12 :             MOZ_ASSERT(mArgv[i].isObject(), "out var is not object");
    1544          24 :             RootedObject obj(mCallContext, &mArgv[i].toObject());
    1545          12 :             if (!JS_SetPropertyById(mCallContext, obj, mIdxValueId, v)) {
    1546           0 :                 ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, i, mCallContext);
    1547           0 :                 return false;
    1548             :             }
    1549             :         } else {
    1550          28 :             MOZ_ASSERT(paramInfo.IsOptional(),
    1551             :                        "Expected either enough arguments or an optional argument");
    1552             :         }
    1553             :     }
    1554             : 
    1555        9888 :     return true;
    1556             : }
    1557             : 
    1558             : bool
    1559         129 : CallMethodHelper::QueryInterfaceFastPath()
    1560             : {
    1561         129 :     MOZ_ASSERT(mVTableIndex == 0,
    1562             :                "Using the QI fast-path for a method other than QueryInterface");
    1563             : 
    1564         129 :     if (mArgc < 1) {
    1565           0 :         Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
    1566           0 :         return false;
    1567             :     }
    1568             : 
    1569         129 :     if (!mArgv[0].isObject()) {
    1570           0 :         ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
    1571           0 :         return false;
    1572             :     }
    1573             : 
    1574         129 :     const nsID* iid = xpc_JSObjectToID(mCallContext, &mArgv[0].toObject());
    1575         129 :     if (!iid) {
    1576           0 :         ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
    1577           0 :         return false;
    1578             :     }
    1579             : 
    1580         129 :     nsISupports* qiresult = nullptr;
    1581         129 :     mInvokeResult = mCallee->QueryInterface(*iid, (void**) &qiresult);
    1582             : 
    1583         129 :     if (NS_FAILED(mInvokeResult)) {
    1584           0 :         ThrowBadResult(mInvokeResult, mCallContext);
    1585           0 :         return false;
    1586             :     }
    1587             : 
    1588         258 :     RootedValue v(mCallContext, NullValue());
    1589             :     nsresult err;
    1590             :     bool success =
    1591         258 :         XPCConvert::NativeData2JS(&v, &qiresult,
    1592             :                                   nsXPTType::T_INTERFACE_IS,
    1593         129 :                                   iid, &err);
    1594         129 :     NS_IF_RELEASE(qiresult);
    1595             : 
    1596         129 :     if (!success) {
    1597           0 :         ThrowBadParam(err, 0, mCallContext);
    1598           0 :         return false;
    1599             :     }
    1600             : 
    1601         129 :     mCallContext.SetRetVal(v);
    1602         129 :     return true;
    1603             : }
    1604             : 
    1605             : bool
    1606       11867 : CallMethodHelper::InitializeDispatchParams()
    1607             : {
    1608       11867 :     const uint8_t wantsOptArgc = mMethodInfo->WantsOptArgc() ? 1 : 0;
    1609       11867 :     const uint8_t wantsJSContext = mMethodInfo->WantsContext() ? 1 : 0;
    1610       11867 :     const uint8_t paramCount = mMethodInfo->GetParamCount();
    1611       11867 :     uint8_t requiredArgs = paramCount;
    1612       11867 :     uint8_t hasRetval = 0;
    1613             : 
    1614             :     // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
    1615       11867 :     if (paramCount && mMethodInfo->GetParam(paramCount-1).IsRetval()) {
    1616       10404 :         hasRetval = 1;
    1617       10404 :         requiredArgs--;
    1618             :     }
    1619             : 
    1620       11867 :     if (mArgc < requiredArgs || wantsOptArgc) {
    1621        2802 :         if (wantsOptArgc)
    1622        1925 :             mOptArgcIndex = requiredArgs;
    1623             : 
    1624             :         // skip over any optional arguments
    1625        9138 :         while (requiredArgs && mMethodInfo->GetParam(requiredArgs-1).IsOptional())
    1626        3168 :             requiredArgs--;
    1627             : 
    1628        2802 :         if (mArgc < requiredArgs) {
    1629           0 :             Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
    1630           0 :             return false;
    1631             :         }
    1632             :     }
    1633             : 
    1634       11867 :     if (wantsJSContext) {
    1635        1476 :         if (wantsOptArgc)
    1636             :             // Need to bump mOptArgcIndex up one here.
    1637        1377 :             mJSContextIndex = mOptArgcIndex++;
    1638          99 :         else if (mMethodInfo->IsSetter() || mMethodInfo->IsGetter())
    1639             :             // For attributes, we always put the JSContext* first.
    1640          12 :             mJSContextIndex = 0;
    1641             :         else
    1642          87 :             mJSContextIndex = paramCount - hasRetval;
    1643             :     }
    1644             : 
    1645             :     // iterate through the params to clear flags (for safe cleanup later)
    1646       40300 :     for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) {
    1647       28433 :         nsXPTCVariant* dp = mDispatchParams.AppendElement();
    1648       28433 :         dp->ClearFlags();
    1649       28433 :         dp->val.p = nullptr;
    1650             :     }
    1651             : 
    1652             :     // Fill in the JSContext argument
    1653       11867 :     if (wantsJSContext) {
    1654        1476 :         nsXPTCVariant* dp = &mDispatchParams[mJSContextIndex];
    1655        1476 :         dp->type = nsXPTType::T_VOID;
    1656        1476 :         dp->val.p = mCallContext;
    1657             :     }
    1658             : 
    1659             :     // Fill in the optional_argc argument
    1660       11867 :     if (wantsOptArgc) {
    1661        1925 :         nsXPTCVariant* dp = &mDispatchParams[mOptArgcIndex];
    1662        1925 :         dp->type = nsXPTType::T_U8;
    1663        1925 :         dp->val.u8 = std::min<uint32_t>(mArgc, paramCount) - requiredArgs;
    1664             :     }
    1665             : 
    1666       11867 :     return true;
    1667             : }
    1668             : 
    1669             : bool
    1670       11867 : CallMethodHelper::ConvertIndependentParams(bool* foundDependentParam)
    1671             : {
    1672       11867 :     const uint8_t paramCount = mMethodInfo->GetParamCount();
    1673       36899 :     for (uint8_t i = 0; i < paramCount; i++) {
    1674       25032 :         const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
    1675             : 
    1676       25032 :         if (paramInfo.GetType().IsDependent())
    1677          91 :             *foundDependentParam = true;
    1678       24941 :         else if (!ConvertIndependentParam(i))
    1679           0 :             return false;
    1680             : 
    1681             :     }
    1682             : 
    1683       11867 :     return true;
    1684             : }
    1685             : 
    1686             : bool
    1687       24941 : CallMethodHelper::ConvertIndependentParam(uint8_t i)
    1688             : {
    1689       24941 :     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
    1690       24941 :     const nsXPTType& type = paramInfo.GetType();
    1691       24941 :     uint8_t type_tag = type.TagPart();
    1692       24941 :     nsXPTCVariant* dp = GetDispatchParam(i);
    1693       24941 :     dp->type = type;
    1694       24941 :     MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!");
    1695             : 
    1696             :     // String classes are always "in" - those that are marked "out" are converted
    1697             :     // by the XPIDL compiler to "in+dipper". See the note above IsDipper() in
    1698             :     // xptinfo.h.
    1699             :     //
    1700             :     // Also note that the fact that we bail out early for dipper parameters means
    1701             :     // that "inout" dipper parameters don't work - see bug 687612.
    1702       24941 :     if (paramInfo.IsStringClass()) {
    1703        2481 :         if (!AllocateStringClass(dp, paramInfo))
    1704           0 :             return false;
    1705        2481 :         if (paramInfo.IsDipper()) {
    1706             :             // We've allocated our string class explicitly, so we don't need
    1707             :             // to do any conversions on the incoming argument. However, we still
    1708             :             // need to verify that it's an object, so that we don't get surprised
    1709             :             // later on when trying to assign the result to .value.
    1710         394 :             if (i < mArgc && !mArgv[i].isObject()) {
    1711           0 :                 ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, i, mCallContext);
    1712           0 :                 return false;
    1713             :             }
    1714         394 :             return true;
    1715             :         }
    1716             :     }
    1717             : 
    1718             :     // Specify the correct storage/calling semantics.
    1719       24547 :     if (paramInfo.IsIndirect())
    1720       11505 :         dp->SetIndirect();
    1721             : 
    1722             :     // The JSVal proper is always stored within the 'val' union and passed
    1723             :     // indirectly, regardless of in/out-ness.
    1724       24547 :     if (type_tag == nsXPTType::T_JSVAL) {
    1725             :         // Root the value.
    1726        2947 :         dp->val.j.setUndefined();
    1727        2947 :         if (!js::AddRawValueRoot(mCallContext, &dp->val.j, "XPCWrappedNative::CallMethod param"))
    1728           0 :             return false;
    1729             :     }
    1730             : 
    1731             :     // Flag cleanup for anything that isn't self-contained.
    1732       24547 :     if (!type.IsArithmetic())
    1733       17895 :         dp->SetValNeedsCleanup();
    1734             : 
    1735             :     // Even if there's nothing to convert, we still need to examine the
    1736             :     // JSObject container for out-params. If it's null or otherwise invalid,
    1737             :     // we want to know before the call, rather than after.
    1738             :     //
    1739             :     // This is a no-op for 'in' params.
    1740       49094 :     RootedValue src(mCallContext);
    1741       24547 :     if (!GetOutParamSource(i, &src))
    1742           0 :         return false;
    1743             : 
    1744             :     // All that's left to do is value conversion. Bail early if we don't need
    1745             :     // to do that.
    1746       24547 :     if (!paramInfo.IsIn())
    1747        9966 :         return true;
    1748             : 
    1749             :     // We're definitely some variety of 'in' now, so there's something to
    1750             :     // convert. The source value for conversion depends on whether we're
    1751             :     // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
    1752             :     // so all that's left is 'in'.
    1753       14581 :     if (!paramInfo.IsOut()) {
    1754             :         // Handle the 'in' case.
    1755       14581 :         MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
    1756             :                    "Expected either enough arguments or an optional argument");
    1757       14581 :         if (i < mArgc)
    1758       12550 :             src = mArgv[i];
    1759        2031 :         else if (type_tag == nsXPTType::T_JSVAL)
    1760         693 :             src.setUndefined();
    1761             :         else
    1762        1338 :             src.setNull();
    1763             :     }
    1764             : 
    1765             :     nsID param_iid;
    1766       17441 :     if (type_tag == nsXPTType::T_INTERFACE &&
    1767        2860 :         NS_FAILED(mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo,
    1768             :                                                     &param_iid))) {
    1769           0 :         ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, i, mCallContext);
    1770           0 :         return false;
    1771             :     }
    1772             : 
    1773             :     // Don't allow CPOWs to be passed to native code (in case they try to cast
    1774             :     // to a concrete type).
    1775       35124 :     if (src.isObject() &&
    1776        5962 :         jsipc::IsWrappedCPOW(&src.toObject()) &&
    1777       14581 :         type_tag == nsXPTType::T_INTERFACE &&
    1778           0 :         !param_iid.Equals(NS_GET_IID(nsISupports)))
    1779             :     {
    1780             :         // Allow passing CPOWs to XPCWrappedJS.
    1781           0 :         nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(mCallee));
    1782           0 :         if (!wrappedJS) {
    1783           0 :             ThrowBadParam(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, i, mCallContext);
    1784           0 :             return false;
    1785             :         }
    1786             :     }
    1787             : 
    1788             :     nsresult err;
    1789       14581 :     if (!XPCConvert::JSData2Native(&dp->val, src, type, &param_iid, &err)) {
    1790           0 :         ThrowBadParam(err, i, mCallContext);
    1791           0 :         return false;
    1792             :     }
    1793             : 
    1794       14581 :     return true;
    1795             : }
    1796             : 
    1797             : bool
    1798          91 : CallMethodHelper::ConvertDependentParams()
    1799             : {
    1800          91 :     const uint8_t paramCount = mMethodInfo->GetParamCount();
    1801         350 :     for (uint8_t i = 0; i < paramCount; i++) {
    1802         259 :         const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
    1803             : 
    1804         259 :         if (!paramInfo.GetType().IsDependent())
    1805         168 :             continue;
    1806          91 :         if (!ConvertDependentParam(i))
    1807           0 :             return false;
    1808             :     }
    1809             : 
    1810          91 :     return true;
    1811             : }
    1812             : 
    1813             : bool
    1814          91 : CallMethodHelper::ConvertDependentParam(uint8_t i)
    1815             : {
    1816          91 :     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
    1817          91 :     const nsXPTType& type = paramInfo.GetType();
    1818          91 :     nsXPTType datum_type;
    1819          91 :     uint32_t array_count = 0;
    1820          91 :     bool isArray = type.IsArray();
    1821             : 
    1822         150 :     bool isSizedString = isArray ?
    1823             :         false :
    1824         118 :         type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
    1825         150 :         type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
    1826             : 
    1827          91 :     nsXPTCVariant* dp = GetDispatchParam(i);
    1828          91 :     dp->type = type;
    1829             : 
    1830          91 :     if (isArray) {
    1831          32 :         if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
    1832             :                                                   &datum_type))) {
    1833           0 :             Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
    1834           0 :             return false;
    1835             :         }
    1836          32 :         MOZ_ASSERT(datum_type.TagPart() != nsXPTType::T_JSVAL,
    1837             :                    "Arrays of JSVals not currently supported - see bug 693337.");
    1838             :     } else {
    1839          59 :         datum_type = type;
    1840             :     }
    1841             : 
    1842             :     // Specify the correct storage/calling semantics.
    1843          91 :     if (paramInfo.IsIndirect())
    1844          84 :         dp->SetIndirect();
    1845             : 
    1846             :     // We have 3 possible type of dependent parameters: Arrays, Sized Strings,
    1847             :     // and iid_is Interface pointers. The latter two always need cleanup, and
    1848             :     // arrays need cleanup for all non-arithmetic types. Since the latter two
    1849             :     // cases also happen to be non-arithmetic, we can just inspect datum_type
    1850             :     // here.
    1851          91 :     if (!datum_type.IsArithmetic())
    1852          91 :         dp->SetValNeedsCleanup();
    1853             : 
    1854             :     // Even if there's nothing to convert, we still need to examine the
    1855             :     // JSObject container for out-params. If it's null or otherwise invalid,
    1856             :     // we want to know before the call, rather than after.
    1857             :     //
    1858             :     // This is a no-op for 'in' params.
    1859         182 :     RootedValue src(mCallContext);
    1860          91 :     if (!GetOutParamSource(i, &src))
    1861           0 :         return false;
    1862             : 
    1863             :     // All that's left to do is value conversion. Bail early if we don't need
    1864             :     // to do that.
    1865          91 :     if (!paramInfo.IsIn())
    1866          84 :         return true;
    1867             : 
    1868             :     // We're definitely some variety of 'in' now, so there's something to
    1869             :     // convert. The source value for conversion depends on whether we're
    1870             :     // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
    1871             :     // so all that's left is 'in'.
    1872           7 :     if (!paramInfo.IsOut()) {
    1873             :         // Handle the 'in' case.
    1874           7 :         MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
    1875             :                      "Expected either enough arguments or an optional argument");
    1876           7 :         src = i < mArgc ? mArgv[i] : JS::NullValue();
    1877             :     }
    1878             : 
    1879             :     nsID param_iid;
    1880          10 :     if (datum_type.IsInterfacePointer() &&
    1881           3 :         !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
    1882           0 :         return false;
    1883             : 
    1884             :     nsresult err;
    1885             : 
    1886           7 :     if (isArray || isSizedString) {
    1887           7 :         if (!GetArraySizeFromParam(i, src, &array_count))
    1888           0 :             return false;
    1889             : 
    1890          14 :         if (isArray) {
    1891          35 :             if (array_count &&
    1892          28 :                 !XPCConvert::JSArray2Native((void**)&dp->val, src,
    1893             :                                             array_count, datum_type, &param_iid,
    1894           7 :                                             &err)) {
    1895             :                 // XXX need exception scheme for arrays to indicate bad element
    1896           0 :                 ThrowBadParam(err, i, mCallContext);
    1897           0 :                 return false;
    1898             :             }
    1899             :         } else // if (isSizedString)
    1900             :         {
    1901           0 :             if (!XPCConvert::JSStringWithSize2Native((void*)&dp->val,
    1902             :                                                      src, array_count,
    1903             :                                                      datum_type, &err)) {
    1904           0 :                 ThrowBadParam(err, i, mCallContext);
    1905           0 :                 return false;
    1906             :             }
    1907             :         }
    1908             :     } else {
    1909           0 :         if (!XPCConvert::JSData2Native(&dp->val, src, type,
    1910             :                                        &param_iid, &err)) {
    1911           0 :             ThrowBadParam(err, i, mCallContext);
    1912           0 :             return false;
    1913             :         }
    1914             :     }
    1915             : 
    1916           7 :     return true;
    1917             : }
    1918             : 
    1919             : // Performs all necessary teardown on a parameter after method invocation.
    1920             : //
    1921             : // This method should only be called if the value in question was flagged
    1922             : // for cleanup (ie, if dp->DoesValNeedCleanup()).
    1923             : void
    1924       18690 : CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type)
    1925             : {
    1926             :     // We handle array elements, but not the arrays themselves.
    1927       18690 :     MOZ_ASSERT(type.TagPart() != nsXPTType::T_ARRAY, "Can't handle arrays.");
    1928             : 
    1929             :     // Pointers may sometimes be null even if cleanup was requested. Combine
    1930             :     // the null checking for all the different types into one check here.
    1931       18690 :     if (type.TagPart() != nsXPTType::T_JSVAL && param.val.p == nullptr)
    1932         303 :         return;
    1933             : 
    1934       18387 :     switch (type.TagPart()) {
    1935             :         case nsXPTType::T_JSVAL:
    1936        2947 :             js::RemoveRawValueRoot(mCallContext, (Value*)&param.val);
    1937        2947 :             break;
    1938             :         case nsXPTType::T_INTERFACE:
    1939             :         case nsXPTType::T_INTERFACE_IS:
    1940        4918 :             ((nsISupports*)param.val.p)->Release();
    1941        4918 :             break;
    1942             :         case nsXPTType::T_ASTRING:
    1943             :         case nsXPTType::T_DOMSTRING:
    1944        1018 :             mCallContext.GetContext()->mScratchStrings.Destroy((nsString*)param.val.p);
    1945        1018 :             break;
    1946             :         case nsXPTType::T_UTF8STRING:
    1947             :         case nsXPTType::T_CSTRING:
    1948        1463 :             mCallContext.GetContext()->mScratchCStrings.Destroy((nsCString*)param.val.p);
    1949        1463 :             break;
    1950             :         default:
    1951        8041 :             MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");
    1952        8041 :             free(param.val.p);
    1953        8041 :             break;
    1954             :     }
    1955             : }
    1956             : 
    1957             : bool
    1958        2481 : CallMethodHelper::AllocateStringClass(nsXPTCVariant* dp,
    1959             :                                       const nsXPTParamInfo& paramInfo)
    1960             : {
    1961             :     // Get something we can make comparisons with.
    1962        2481 :     uint8_t type_tag = paramInfo.GetType().TagPart();
    1963             : 
    1964             :     // There should be 4 cases, all strings. Verify that here.
    1965        2481 :     MOZ_ASSERT(type_tag == nsXPTType::T_ASTRING ||
    1966             :                type_tag == nsXPTType::T_DOMSTRING ||
    1967             :                type_tag == nsXPTType::T_UTF8STRING ||
    1968             :                type_tag == nsXPTType::T_CSTRING,
    1969             :                "Unexpected string class type!");
    1970             : 
    1971             :     // ASTRING and DOMSTRING are very similar, and both use nsString.
    1972             :     // UTF8_STRING and CSTRING are also quite similar, and both use nsCString.
    1973        2481 :     if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING)
    1974        1018 :         dp->val.p = mCallContext.GetContext()->mScratchStrings.Create();
    1975             :     else
    1976        1463 :         dp->val.p = mCallContext.GetContext()->mScratchCStrings.Create();
    1977             : 
    1978             :     // Check for OOM, in either case.
    1979        2481 :     if (!dp->val.p) {
    1980           0 :         JS_ReportOutOfMemory(mCallContext);
    1981           0 :         return false;
    1982             :     }
    1983             : 
    1984             :     // We allocated, so we need to deallocate after the method call completes.
    1985        2481 :     dp->SetValNeedsCleanup();
    1986             : 
    1987        2481 :     return true;
    1988             : }
    1989             : 
    1990             : nsresult
    1991       11867 : CallMethodHelper::Invoke()
    1992             : {
    1993       11867 :     uint32_t argc = mDispatchParams.Length();
    1994       11867 :     nsXPTCVariant* argv = mDispatchParams.Elements();
    1995             : 
    1996       11867 :     return NS_InvokeByIndex(mCallee, mVTableIndex, argc, argv);
    1997             : }
    1998             : 
    1999             : /***************************************************************************/
    2000             : // interface methods
    2001             : 
    2002             : JSObject*
    2003         266 : XPCWrappedNative::GetJSObject()
    2004             : {
    2005         266 :     return GetFlatJSObject();
    2006             : }
    2007             : 
    2008           0 : NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative)
    2009             : {
    2010             :     // No need to QI here, we already have the correct nsISupports
    2011             :     // vtable.
    2012           0 :     nsCOMPtr<nsISupports> rval = mIdentity;
    2013           0 :     rval.forget(aNative);
    2014           0 :     return NS_OK;
    2015             : }
    2016             : 
    2017           0 : NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype)
    2018             : {
    2019           0 :     *aJSObjectPrototype = HasProto() ?
    2020           0 :                 GetProto()->GetJSProtoObject() : GetFlatJSObject();
    2021           0 :     return NS_OK;
    2022             : }
    2023             : 
    2024             : nsIPrincipal*
    2025           0 : XPCWrappedNative::GetObjectPrincipal() const
    2026             : {
    2027           0 :     nsIPrincipal* principal = GetScope()->GetPrincipal();
    2028             : #ifdef DEBUG
    2029             :     // Because of inner window reuse, we can have objects with one principal
    2030             :     // living in a scope with a different (but same-origin) principal. So
    2031             :     // just check same-origin here.
    2032           0 :     nsCOMPtr<nsIScriptObjectPrincipal> objPrin(do_QueryInterface(mIdentity));
    2033           0 :     if (objPrin) {
    2034             :         bool equal;
    2035           0 :         if (!principal)
    2036           0 :             equal = !objPrin->GetPrincipal();
    2037             :         else
    2038           0 :             principal->Equals(objPrin->GetPrincipal(), &equal);
    2039           0 :         MOZ_ASSERT(equal, "Principal mismatch.  Expect bad things to happen");
    2040             :     }
    2041             : #endif
    2042           0 :     return principal;
    2043             : }
    2044             : 
    2045           0 : NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithMember(HandleId name,
    2046             :                                                         nsIInterfaceInfo * *_retval)
    2047             : {
    2048           0 :     RefPtr<XPCNativeInterface> iface;
    2049             :     XPCNativeMember*  member;
    2050             : 
    2051           0 :     if (GetSet()->FindMember(name, &member, &iface) && iface) {
    2052           0 :         nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
    2053           0 :         temp.forget(_retval);
    2054             :     } else
    2055           0 :         *_retval = nullptr;
    2056           0 :     return NS_OK;
    2057             : }
    2058             : 
    2059           0 : NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithName(HandleId name,
    2060             :                                                       nsIInterfaceInfo * *_retval)
    2061             : {
    2062           0 :     XPCNativeInterface* iface = GetSet()->FindNamedInterface(name);
    2063           0 :     if (iface) {
    2064           0 :         nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
    2065           0 :         temp.forget(_retval);
    2066             :     } else
    2067           0 :         *_retval = nullptr;
    2068           0 :     return NS_OK;
    2069             : }
    2070             : 
    2071             : NS_IMETHODIMP_(bool)
    2072           0 : XPCWrappedNative::HasNativeMember(HandleId name)
    2073             : {
    2074           0 :     XPCNativeMember* member = nullptr;
    2075             :     uint16_t ignored;
    2076           0 :     return GetSet()->FindMember(name, &member, &ignored) && !!member;
    2077             : }
    2078             : 
    2079           0 : NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth)
    2080             : {
    2081             : #ifdef DEBUG
    2082           0 :     depth-- ;
    2083           0 :     XPC_LOG_ALWAYS(("XPCWrappedNative @ %p with mRefCnt = %" PRIuPTR, this, mRefCnt.get()));
    2084           0 :     XPC_LOG_INDENT();
    2085             : 
    2086           0 :         if (HasProto()) {
    2087           0 :             XPCWrappedNativeProto* proto = GetProto();
    2088           0 :             if (depth && proto)
    2089           0 :                 proto->DebugDump(depth);
    2090             :             else
    2091           0 :                 XPC_LOG_ALWAYS(("mMaybeProto @ %p", proto));
    2092             :         } else
    2093           0 :             XPC_LOG_ALWAYS(("Scope @ %p", GetScope()));
    2094             : 
    2095           0 :         if (depth && mSet)
    2096           0 :             mSet->DebugDump(depth);
    2097             :         else
    2098           0 :             XPC_LOG_ALWAYS(("mSet @ %p", mSet.get()));
    2099             : 
    2100           0 :         XPC_LOG_ALWAYS(("mFlatJSObject of %p", mFlatJSObject.unbarrieredGetPtr()));
    2101           0 :         XPC_LOG_ALWAYS(("mIdentity of %p", mIdentity.get()));
    2102           0 :         XPC_LOG_ALWAYS(("mScriptable @ %p", mScriptable.get()));
    2103             : 
    2104           0 :         if (depth && mScriptable) {
    2105           0 :             XPC_LOG_INDENT();
    2106           0 :             XPC_LOG_ALWAYS(("mFlags of %x", mScriptable->GetScriptableFlags()));
    2107           0 :             XPC_LOG_ALWAYS(("mJSClass @ %p", mScriptable->GetJSClass()));
    2108           0 :             XPC_LOG_OUTDENT();
    2109             :         }
    2110           0 :     XPC_LOG_OUTDENT();
    2111             : #endif
    2112           0 :     return NS_OK;
    2113             : }
    2114             : 
    2115             : /***************************************************************************/
    2116             : 
    2117             : char*
    2118           8 : XPCWrappedNative::ToString(XPCWrappedNativeTearOff* to /* = nullptr */ ) const
    2119             : {
    2120             : #ifdef DEBUG
    2121             : #  define FMT_ADDR " @ 0x%p"
    2122             : #  define FMT_STR(str) str
    2123             : #  define PARAM_ADDR(w) , w
    2124             : #else
    2125             : #  define FMT_ADDR ""
    2126             : #  define FMT_STR(str)
    2127             : #  define PARAM_ADDR(w)
    2128             : #endif
    2129             : 
    2130          16 :     UniqueChars sz;
    2131          16 :     UniqueChars name;
    2132             : 
    2133          16 :     nsCOMPtr<nsIXPCScriptable> scr = GetScriptable();
    2134           8 :     if (scr)
    2135           1 :         name = JS_smprintf("%s", scr->GetJSClass()->name);
    2136           8 :     if (to) {
    2137           0 :         const char* fmt = name ? " (%s)" : "%s";
    2138           0 :         name = JS_sprintf_append(Move(name), fmt,
    2139           0 :                                  to->GetInterface()->GetNameString());
    2140           8 :     } else if (!name) {
    2141           7 :         XPCNativeSet* set = GetSet();
    2142           7 :         XPCNativeInterface** array = set->GetInterfaceArray();
    2143          14 :         RefPtr<XPCNativeInterface> isupp = XPCNativeInterface::GetISupports();
    2144           7 :         uint16_t count = set->GetInterfaceCount();
    2145             : 
    2146           7 :         if (count == 1)
    2147           0 :             name = JS_sprintf_append(Move(name), "%s", array[0]->GetNameString());
    2148           7 :         else if (count == 2 && array[0] == isupp) {
    2149           7 :             name = JS_sprintf_append(Move(name), "%s", array[1]->GetNameString());
    2150             :         } else {
    2151           0 :             for (uint16_t i = 0; i < count; i++) {
    2152           0 :                 const char* fmt = (i == 0) ?
    2153           0 :                                     "(%s" : (i == count-1) ?
    2154           0 :                                         ", %s)" : ", %s";
    2155           0 :                 name = JS_sprintf_append(Move(name), fmt,
    2156           0 :                                          array[i]->GetNameString());
    2157             :             }
    2158             :         }
    2159             :     }
    2160             : 
    2161           8 :     if (!name) {
    2162           0 :         return nullptr;
    2163             :     }
    2164             :     const char* fmt = "[xpconnect wrapped %s" FMT_ADDR FMT_STR(" (native")
    2165           8 :         FMT_ADDR FMT_STR(")") "]";
    2166           8 :     if (scr) {
    2167           1 :         fmt = "[object %s" FMT_ADDR FMT_STR(" (native") FMT_ADDR FMT_STR(")") "]";
    2168             :     }
    2169           8 :     sz = JS_smprintf(fmt, name.get() PARAM_ADDR(this) PARAM_ADDR(mIdentity.get()));
    2170             : 
    2171           8 :     return sz.release();
    2172             : 
    2173             : #undef FMT_ADDR
    2174             : #undef PARAM_ADDR
    2175             : }
    2176             : 
    2177             : /***************************************************************************/
    2178             : 
    2179             : #ifdef XPC_CHECK_CLASSINFO_CLAIMS
    2180             : static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper)
    2181             : {
    2182             :     if (!wrapper || !wrapper->GetClassInfo())
    2183             :         return;
    2184             : 
    2185             :     nsISupports* obj = wrapper->GetIdentityObject();
    2186             :     XPCNativeSet* set = wrapper->GetSet();
    2187             :     uint16_t count = set->GetInterfaceCount();
    2188             :     for (uint16_t i = 0; i < count; i++) {
    2189             :         nsIClassInfo* clsInfo = wrapper->GetClassInfo();
    2190             :         XPCNativeInterface* iface = set->GetInterfaceAt(i);
    2191             :         nsIInterfaceInfo* info = iface->GetInterfaceInfo();
    2192             :         const nsIID* iid;
    2193             :         nsISupports* ptr;
    2194             : 
    2195             :         info->GetIIDShared(&iid);
    2196             :         nsresult rv = obj->QueryInterface(*iid, (void**)&ptr);
    2197             :         if (NS_SUCCEEDED(rv)) {
    2198             :             NS_RELEASE(ptr);
    2199             :             continue;
    2200             :         }
    2201             :         if (rv == NS_ERROR_OUT_OF_MEMORY)
    2202             :             continue;
    2203             : 
    2204             :         // Houston, We have a problem...
    2205             : 
    2206             :         char* className = nullptr;
    2207             :         char* contractID = nullptr;
    2208             :         const char* interfaceName;
    2209             : 
    2210             :         info->GetNameShared(&interfaceName);
    2211             :         clsInfo->GetContractID(&contractID);
    2212             :         if (wrapper->GetScriptable()) {
    2213             :             wrapper->GetScriptable()->GetClassName(&className);
    2214             :         }
    2215             : 
    2216             :         printf("\n!!! Object's nsIClassInfo lies about its interfaces!!!\n"
    2217             :                "   classname: %s \n"
    2218             :                "   contractid: %s \n"
    2219             :                "   unimplemented interface name: %s\n\n",
    2220             :                className ? className : "<unknown>",
    2221             :                contractID ? contractID : "<unknown>",
    2222             :                interfaceName);
    2223             : 
    2224             :         if (className)
    2225             :             free(className);
    2226             :         if (contractID)
    2227             :             free(contractID);
    2228             :     }
    2229             : }
    2230             : #endif
    2231             : 
    2232           0 : NS_IMPL_ISUPPORTS(XPCJSObjectHolder, nsIXPConnectJSObjectHolder)
    2233             : 
    2234             : JSObject*
    2235           0 : XPCJSObjectHolder::GetJSObject()
    2236             : {
    2237           0 :     NS_PRECONDITION(mJSObj, "bad object state");
    2238           0 :     return mJSObj;
    2239             : }
    2240             : 
    2241           0 : XPCJSObjectHolder::XPCJSObjectHolder(JSContext* cx, JSObject* obj)
    2242           0 :   : mJSObj(cx, obj)
    2243             : {
    2244           0 :     MOZ_ASSERT(obj);
    2245           0 : }

Generated by: LCOV version 1.13