js/xpconnect/src/XPCWrappedNative.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /* Wrapper object for reflecting native xpcom objects into JavaScript. */
     9 #include "xpcprivate.h"
    10 #include "nsWrapperCacheInlines.h"
    11 #include "XPCLog.h"
    12 #include "jsprf.h"
    13 #include "AccessCheck.h"
    14 #include "WrapperFactory.h"
    15 #include "XrayWrapper.h"
    17 #include "nsContentUtils.h"
    18 #include "nsCxPusher.h"
    20 #include <stdint.h>
    21 #include "mozilla/Likely.h"
    22 #include "mozilla/dom/BindingUtils.h"
    23 #include <algorithm>
    25 using namespace xpc;
    26 using namespace mozilla;
    27 using namespace mozilla::dom;
    28 using namespace JS;
    30 /***************************************************************************/
    32 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
    34 // No need to unlink the JS objects: if the XPCWrappedNative is cycle
    35 // collected then its mFlatJSObject will be cycle collected too and
    36 // finalization of the mFlatJSObject will unlink the JS objects (see
    37 // XPC_WN_NoHelper_Finalize and FlatJSObjectFinalized).
    38 NS_IMETHODIMP_(void)
    39 NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Unlink(void *p)
    40 {
    41     XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p);
    42     tmp->ExpireWrapper();
    43 }
    45 NS_IMETHODIMP
    46 NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse
    47    (void *p, nsCycleCollectionTraversalCallback &cb)
    48 {
    49     XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p);
    50     if (!tmp->IsValid())
    51         return NS_OK;
    53     if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
    54         char name[72];
    55         XPCNativeScriptableInfo* si = tmp->GetScriptableInfo();
    56         if (si)
    57             JS_snprintf(name, sizeof(name), "XPCWrappedNative (%s)",
    58                         si->GetJSClass()->name);
    59         else
    60             JS_snprintf(name, sizeof(name), "XPCWrappedNative");
    62         cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
    63     } else {
    64         NS_IMPL_CYCLE_COLLECTION_DESCRIBE(XPCWrappedNative, tmp->mRefCnt.get())
    65     }
    67     if (tmp->mRefCnt.get() > 1) {
    69         // If our refcount is > 1, our reference to the flat JS object is
    70         // considered "strong", and we're going to traverse it.
    71         //
    72         // If our refcount is <= 1, our reference to the flat JS object is
    73         // considered "weak", and we're *not* going to traverse it.
    74         //
    75         // This reasoning is in line with the slightly confusing lifecycle rules
    76         // for XPCWrappedNatives, described in a larger comment below and also
    77         // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping
    79         JSObject *obj = tmp->GetFlatJSObjectPreserveColor();
    80         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFlatJSObject");
    81         cb.NoteJSChild(obj);
    82     }
    84     // XPCWrappedNative keeps its native object alive.
    85     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mIdentity");
    86     cb.NoteXPCOMChild(tmp->GetIdentityObject());
    88     tmp->NoteTearoffs(cb);
    90     return NS_OK;
    91 }
    93 void
    94 XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb)
    95 {
    96     // Tearoffs hold their native object alive. If their JS object hasn't been
    97     // finalized yet we'll note the edge between the JS object and the native
    98     // (see nsXPConnect::Traverse), but if their JS object has been finalized
    99     // then the tearoff is only reachable through the XPCWrappedNative, so we
   100     // record an edge here.
   101     XPCWrappedNativeTearOffChunk* chunk;
   102     for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
   103         XPCWrappedNativeTearOff* to = chunk->mTearOffs;
   104         for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
   105             JSObject* jso = to->GetJSObjectPreserveColor();
   106             if (!jso) {
   107                 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative");
   108                 cb.NoteXPCOMChild(to->GetNative());
   109             }
   110         }
   111     }
   112 }
   114 #ifdef XPC_CHECK_CLASSINFO_CLAIMS
   115 static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper);
   116 #else
   117 #define DEBUG_CheckClassInfoClaims(wrapper) ((void)0)
   118 #endif
   120 /***************************************************************************/
   121 static nsresult
   122 FinishCreate(XPCWrappedNativeScope* Scope,
   123              XPCNativeInterface* Interface,
   124              nsWrapperCache *cache,
   125              XPCWrappedNative* inWrapper,
   126              XPCWrappedNative** resultWrapper);
   128 // static
   129 //
   130 // This method handles the special case of wrapping a new global object.
   131 //
   132 // The normal code path for wrapping natives goes through
   133 // XPCConvert::NativeInterface2JSObject, XPCWrappedNative::GetNewOrUsed,
   134 // and finally into XPCWrappedNative::Init. Unfortunately, this path assumes
   135 // very early on that we have an XPCWrappedNativeScope and corresponding global
   136 // JS object, which are the very things we need to create here. So we special-
   137 // case the logic and do some things in a different order.
   138 nsresult
   139 XPCWrappedNative::WrapNewGlobal(xpcObjectHelper &nativeHelper,
   140                                 nsIPrincipal *principal,
   141                                 bool initStandardClasses,
   142                                 JS::CompartmentOptions& aOptions,
   143                                 XPCWrappedNative **wrappedGlobal)
   144 {
   145     AutoJSContext cx;
   146     nsISupports *identity = nativeHelper.GetCanonical();
   148     // The object should specify that it's meant to be global.
   149     MOZ_ASSERT(nativeHelper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
   151     // We shouldn't be reusing globals.
   152     MOZ_ASSERT(!nativeHelper.GetWrapperCache() ||
   153                !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor());
   155     // Put together the ScriptableCreateInfo...
   156     XPCNativeScriptableCreateInfo sciProto;
   157     XPCNativeScriptableCreateInfo sciMaybe;
   158     const XPCNativeScriptableCreateInfo& sciWrapper =
   159         GatherScriptableCreateInfo(identity, nativeHelper.GetClassInfo(),
   160                                    sciProto, sciMaybe);
   162     // ...and then ScriptableInfo. We need all this stuff now because it's going
   163     // to tell us the JSClass of the object we're going to create.
   164     AutoMarkingNativeScriptableInfoPtr si(cx, XPCNativeScriptableInfo::Construct(&sciWrapper));
   165     MOZ_ASSERT(si.get());
   167     // Finally, we get to the JSClass.
   168     const JSClass *clasp = si->GetJSClass();
   169     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
   171     // Create the global.
   172     aOptions.setTrace(XPCWrappedNative::Trace);
   173     RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions));
   174     if (!global)
   175         return NS_ERROR_FAILURE;
   176     XPCWrappedNativeScope *scope = GetCompartmentPrivate(global)->scope;
   178     // Immediately enter the global's compartment, so that everything else we
   179     // create ends up there.
   180     JSAutoCompartment ac(cx, global);
   182     // If requested, initialize the standard classes on the global.
   183     if (initStandardClasses && ! JS_InitStandardClasses(cx, global))
   184         return NS_ERROR_FAILURE;
   186     // Make a proto.
   187     XPCWrappedNativeProto *proto =
   188         XPCWrappedNativeProto::GetNewOrUsed(scope,
   189                                             nativeHelper.GetClassInfo(), &sciProto,
   190                                             /* callPostCreatePrototype = */ false);
   191     if (!proto)
   192         return NS_ERROR_FAILURE;
   194     // Set up the prototype on the global.
   195     MOZ_ASSERT(proto->GetJSProtoObject());
   196     RootedObject protoObj(cx, proto->GetJSProtoObject());
   197     bool success = JS_SplicePrototype(cx, global, protoObj);
   198     if (!success)
   199         return NS_ERROR_FAILURE;
   201     // Construct the wrapper, which takes over the strong reference to the
   202     // native object.
   203     nsRefPtr<XPCWrappedNative> wrapper =
   204         new XPCWrappedNative(nativeHelper.forgetCanonical(), proto);
   206     //
   207     // We don't call ::Init() on this wrapper, because our setup requirements
   208     // are different for globals. We do our setup inline here, instead.
   209     //
   211     // Share mScriptableInfo with the proto.
   212     //
   213     // This is probably more trouble than it's worth, since we've already created
   214     // an XPCNativeScriptableInfo for ourselves. Moreover, most of that class is
   215     // shared internally via XPCNativeScriptableInfoShared, so the memory
   216     // savings are negligible. Nevertheless, this is what ::Init() does, and we
   217     // want to be as consistent as possible with that code.
   218     XPCNativeScriptableInfo* siProto = proto->GetScriptableInfo();
   219     if (siProto && siProto->GetCallback() == sciWrapper.GetCallback()) {
   220         wrapper->mScriptableInfo = siProto;
   221         // XPCNativeScriptableShared instances live in a map, and are
   222         // GCed, but XPCNativeScriptableInfo is per-instance and must be
   223         // manually managed. If we're switching over to that of the proto, we
   224         // need to destroy the one we've allocated, and also null out the
   225         // AutoMarkingPtr, so that it doesn't try to mark garbage data.
   226         delete si;
   227         si = nullptr;
   228     } else {
   229         wrapper->mScriptableInfo = si;
   230     }
   232     // Set the JS object to the global we already created.
   233     wrapper->mFlatJSObject = global;
   234     wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
   236     // Set the private to the XPCWrappedNative.
   237     JS_SetPrivate(global, wrapper);
   239     // There are dire comments elsewhere in the code about how a GC can
   240     // happen somewhere after wrapper initialization but before the wrapper is
   241     // added to the hashtable in FinishCreate(). It's not clear if that can
   242     // happen here, but let's just be safe for now.
   243     AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
   245     // Call the common Init finish routine. This mainly just does an AddRef
   246     // on behalf of XPConnect (the corresponding Release is in the finalizer
   247     // hook), but it does some other miscellaneous things too, so we don't
   248     // inline it.
   249     success = wrapper->FinishInit();
   250     MOZ_ASSERT(success);
   252     // Go through some extra work to find the tearoff. This is kind of silly
   253     // on a conceptual level: the point of tearoffs is to cache the results
   254     // of QI-ing mIdentity to different interfaces, and we don't need that
   255     // since we're dealing with nsISupports. But lots of code expects tearoffs
   256     // to exist for everything, so we just follow along.
   257     XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(&NS_GET_IID(nsISupports));
   258     MOZ_ASSERT(iface);
   259     nsresult status;
   260     success = wrapper->FindTearOff(iface, false, &status);
   261     if (!success)
   262         return status;
   264     // Call the common creation finish routine. This does all of the bookkeeping
   265     // like inserting the wrapper into the wrapper map and setting up the wrapper
   266     // cache.
   267     nsresult rv = FinishCreate(scope, iface, nativeHelper.GetWrapperCache(),
   268                                wrapper, wrappedGlobal);
   269     NS_ENSURE_SUCCESS(rv, rv);
   271     return NS_OK;
   272 }
   274 // static
   275 nsresult
   276 XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper,
   277                                XPCWrappedNativeScope* Scope,
   278                                XPCNativeInterface* Interface,
   279                                XPCWrappedNative** resultWrapper)
   280 {
   281     MOZ_ASSERT(Interface);
   282     AutoJSContext cx;
   283     nsWrapperCache *cache = helper.GetWrapperCache();
   285     MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor(),
   286                "We assume the caller already checked if it could get the "
   287                "wrapper from the cache.");
   289     nsresult rv;
   291     MOZ_ASSERT(!Scope->GetRuntime()->GCIsRunning(),
   292                "XPCWrappedNative::GetNewOrUsed called during GC");
   294     nsISupports *identity = helper.GetCanonical();
   296     if (!identity) {
   297         NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
   298         return NS_ERROR_FAILURE;
   299     }
   301     nsRefPtr<XPCWrappedNative> wrapper;
   303     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
   304     // Some things are nsWrapperCache subclasses but never use the cache, so go
   305     // ahead and check our map even if we have a cache and it has no existing
   306     // wrapper: we might have an XPCWrappedNative anyway.
   307     wrapper = map->Find(identity);
   309     if (wrapper) {
   310         if (!wrapper->FindTearOff(Interface, false, &rv)) {
   311             MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
   312             return rv;
   313         }
   314         wrapper.forget(resultWrapper);
   315         return NS_OK;
   316     }
   318     // There is a chance that the object wants to have the self-same JSObject
   319     // reflection regardless of the scope into which we are reflecting it.
   320     // Many DOM objects require this. The scriptable helper specifies this
   321     // in preCreate by indicating a 'parent' of a particular scope.
   322     //
   323     // To handle this we need to get the scriptable helper early and ask it.
   324     // It is possible that we will then end up forwarding this entire call
   325     // to this same function but with a different scope.
   327     // If we are making a wrapper for the nsIClassInfo interface then
   328     // We *don't* want to have it use the prototype meant for instances
   329     // of that class.
   330     bool iidIsClassInfo = Interface->GetIID()->Equals(NS_GET_IID(nsIClassInfo));
   331     uint32_t classInfoFlags;
   332     bool isClassInfoSingleton = helper.GetClassInfo() == helper.Object() &&
   333                                 NS_SUCCEEDED(helper.GetClassInfo()
   334                                                    ->GetFlags(&classInfoFlags)) &&
   335                                 (classInfoFlags & nsIClassInfo::SINGLETON_CLASSINFO);
   336     bool isClassInfo = iidIsClassInfo || isClassInfoSingleton;
   338     nsIClassInfo *info = helper.GetClassInfo();
   340     XPCNativeScriptableCreateInfo sciProto;
   341     XPCNativeScriptableCreateInfo sci;
   343     // Gather scriptable create info if we are wrapping something
   344     // other than an nsIClassInfo object. We need to not do this for
   345     // nsIClassInfo objects because often nsIClassInfo implementations
   346     // are also nsIXPCScriptable helper implementations, but the helper
   347     // code is obviously intended for the implementation of the class
   348     // described by the nsIClassInfo, not for the class info object
   349     // itself.
   350     const XPCNativeScriptableCreateInfo& sciWrapper =
   351         isClassInfo ? sci :
   352         GatherScriptableCreateInfo(identity, info, sciProto, sci);
   354     RootedObject parent(cx, Scope->GetGlobalJSObject());
   356     RootedValue newParentVal(cx, NullValue());
   358     mozilla::Maybe<JSAutoCompartment> ac;
   360     if (sciWrapper.GetFlags().WantPreCreate()) {
   361         // PreCreate may touch dead compartments.
   362         js::AutoMaybeTouchDeadZones agc(parent);
   364         RootedObject plannedParent(cx, parent);
   365         nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, cx,
   366                                                           parent, parent.address());
   367         if (NS_FAILED(rv))
   368             return rv;
   369         rv = NS_OK;
   371         MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
   372                    "Xray wrapper being used to parent XPCWrappedNative?");
   374         ac.construct(static_cast<JSContext*>(cx), parent);
   376         if (parent != plannedParent) {
   377             XPCWrappedNativeScope* betterScope = GetObjectScope(parent);
   378             if (betterScope != Scope)
   379                 return GetNewOrUsed(helper, betterScope, Interface, resultWrapper);
   381             newParentVal = OBJECT_TO_JSVAL(parent);
   382         }
   384         // Take the performance hit of checking the hashtable again in case
   385         // the preCreate call caused the wrapper to get created through some
   386         // interesting path (the DOM code tends to make this happen sometimes).
   388         if (cache) {
   389             RootedObject cached(cx, cache->GetWrapper());
   390             if (cached)
   391                 wrapper = XPCWrappedNative::Get(cached);
   392         } else {
   393             wrapper = map->Find(identity);
   394         }
   396         if (wrapper) {
   397             if (wrapper->FindTearOff(Interface, false, &rv)) {
   398                 MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
   399                 return rv;
   400             }
   401             wrapper.forget(resultWrapper);
   402             return NS_OK;
   403         }
   404     } else {
   405         ac.construct(static_cast<JSContext*>(cx), parent);
   406     }
   408     AutoMarkingWrappedNativeProtoPtr proto(cx);
   410     // If there is ClassInfo (and we are not building a wrapper for the
   411     // nsIClassInfo interface) then we use a wrapper that needs a prototype.
   413     // Note that the security check happens inside FindTearOff - after the
   414     // wrapper is actually created, but before JS code can see it.
   416     if (info && !isClassInfo) {
   417         proto = XPCWrappedNativeProto::GetNewOrUsed(Scope, info, &sciProto);
   418         if (!proto)
   419             return NS_ERROR_FAILURE;
   421         wrapper = new XPCWrappedNative(helper.forgetCanonical(), proto);
   422     } else {
   423         AutoMarkingNativeInterfacePtr iface(cx, Interface);
   424         if (!iface)
   425             iface = XPCNativeInterface::GetISupports();
   427         AutoMarkingNativeSetPtr set(cx);
   428         set = XPCNativeSet::GetNewOrUsed(nullptr, iface, 0);
   430         if (!set)
   431             return NS_ERROR_FAILURE;
   433         wrapper =
   434             new XPCWrappedNative(helper.forgetCanonical(), Scope, set);
   435     }
   437     MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
   438                "Xray wrapper being used to parent XPCWrappedNative?");
   440     // We use an AutoMarkingPtr here because it is possible for JS gc to happen
   441     // after we have Init'd the wrapper but *before* we add it to the hashtable.
   442     // This would cause the mSet to get collected and we'd later crash. I've
   443     // *seen* this happen.
   444     AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
   446     if (!wrapper->Init(parent, &sciWrapper))
   447         return NS_ERROR_FAILURE;
   449     if (!wrapper->FindTearOff(Interface, false, &rv)) {
   450         MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
   451         return rv;
   452     }
   454     return FinishCreate(Scope, Interface, cache, wrapper, resultWrapper);
   455 }
   457 static nsresult
   458 FinishCreate(XPCWrappedNativeScope* Scope,
   459              XPCNativeInterface* Interface,
   460              nsWrapperCache *cache,
   461              XPCWrappedNative* inWrapper,
   462              XPCWrappedNative** resultWrapper)
   463 {
   464     AutoJSContext cx;
   465     MOZ_ASSERT(inWrapper);
   467     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
   469     nsRefPtr<XPCWrappedNative> wrapper;
   470     // Deal with the case where the wrapper got created as a side effect
   471     // of one of our calls out of this code. Add() returns the (possibly
   472     // pre-existing) wrapper that ultimately ends up in the map, which is
   473     // what we want.
   474     wrapper = map->Add(inWrapper);
   475     if (!wrapper)
   476         return NS_ERROR_FAILURE;
   478     if (wrapper == inWrapper) {
   479         JSObject *flat = wrapper->GetFlatJSObject();
   480         MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor() ||
   481                    flat == cache->GetWrapperPreserveColor(),
   482                    "This object has a cached wrapper that's different from "
   483                    "the JSObject held by its native wrapper?");
   485         if (cache && !cache->GetWrapperPreserveColor())
   486             cache->SetWrapper(flat);
   488         // Our newly created wrapper is the one that we just added to the table.
   489         // All is well. Call PostCreate as necessary.
   490         XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
   491         if (si && si->GetFlags().WantPostCreate()) {
   492             nsresult rv = si->GetCallback()->PostCreate(wrapper, cx, flat);
   493             if (NS_FAILED(rv)) {
   494                 // PostCreate failed and that's Very Bad. We'll remove it from
   495                 // the map and mark it as invalid, but the PostCreate function
   496                 // may have handed the partially-constructed-and-now-invalid
   497                 // wrapper to someone before failing. Or, perhaps worse, the
   498                 // PostCreate call could have triggered code that reentered
   499                 // XPConnect and tried to wrap the same object. In that case
   500                 // *we* hand out the invalid wrapper since it is already in our
   501                 // map :(
   502                 NS_ERROR("PostCreate failed! This is known to cause "
   503                          "inconsistent state for some class types and may even "
   504                          "cause a crash in combination with a JS GC. Fix the "
   505                          "failing PostCreate ASAP!");
   507                 map->Remove(wrapper);
   509                 // This would be a good place to tell the wrapper not to remove
   510                 // itself from the map when it dies... See bug 429442.
   512                 if (cache)
   513                     cache->ClearWrapper();
   514                 wrapper->Release();
   515                 return rv;
   516             }
   517         }
   518     }
   520     DEBUG_CheckClassInfoClaims(wrapper);
   521     wrapper.forget(resultWrapper);
   522     return NS_OK;
   523 }
   525 // static
   526 nsresult
   527 XPCWrappedNative::GetUsedOnly(nsISupports* Object,
   528                               XPCWrappedNativeScope* Scope,
   529                               XPCNativeInterface* Interface,
   530                               XPCWrappedNative** resultWrapper)
   531 {
   532     AutoJSContext cx;
   533     MOZ_ASSERT(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object");
   534     MOZ_ASSERT(Interface);
   536     nsRefPtr<XPCWrappedNative> wrapper;
   537     nsWrapperCache* cache = nullptr;
   538     CallQueryInterface(Object, &cache);
   539     if (cache) {
   540         RootedObject flat(cx, cache->GetWrapper());
   541         if (!flat) {
   542             *resultWrapper = nullptr;
   543             return NS_OK;
   544         }
   545         wrapper = XPCWrappedNative::Get(flat);
   546     } else {
   547         nsCOMPtr<nsISupports> identity = do_QueryInterface(Object);
   549         if (!identity) {
   550             NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
   551             return NS_ERROR_FAILURE;
   552         }
   554         Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
   556         wrapper = map->Find(identity);
   557         if (!wrapper) {
   558             *resultWrapper = nullptr;
   559             return NS_OK;
   560         }
   561     }
   563     nsresult rv;
   564     if (!wrapper->FindTearOff(Interface, false, &rv)) {
   565         MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
   566         return rv;
   567     }
   569     wrapper.forget(resultWrapper);
   570     return NS_OK;
   571 }
   573 // This ctor is used if this object will have a proto.
   574 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
   575                                    XPCWrappedNativeProto* aProto)
   576     : mMaybeProto(aProto),
   577       mSet(aProto->GetSet()),
   578       mScriptableInfo(nullptr)
   579 {
   580     MOZ_ASSERT(NS_IsMainThread());
   582     mIdentity = aIdentity.take();
   583     mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
   585     MOZ_ASSERT(mMaybeProto, "bad ctor param");
   586     MOZ_ASSERT(mSet, "bad ctor param");
   587 }
   589 // This ctor is used if this object will NOT have a proto.
   590 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
   591                                    XPCWrappedNativeScope* aScope,
   592                                    XPCNativeSet* aSet)
   594     : mMaybeScope(TagScope(aScope)),
   595       mSet(aSet),
   596       mScriptableInfo(nullptr)
   597 {
   598     MOZ_ASSERT(NS_IsMainThread());
   600     mIdentity = aIdentity.take();
   601     mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
   603     MOZ_ASSERT(aScope, "bad ctor param");
   604     MOZ_ASSERT(aSet, "bad ctor param");
   605 }
   607 XPCWrappedNative::~XPCWrappedNative()
   608 {
   609     Destroy();
   610 }
   612 void
   613 XPCWrappedNative::Destroy()
   614 {
   615     XPCWrappedNativeProto* proto = GetProto();
   617     if (mScriptableInfo &&
   618         (!HasProto() ||
   619          (proto && proto->GetScriptableInfo() != mScriptableInfo))) {
   620         delete mScriptableInfo;
   621         mScriptableInfo = nullptr;
   622     }
   624     XPCWrappedNativeScope *scope = GetScope();
   625     if (scope) {
   626         Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
   628         // Post-1.9 we should not remove this wrapper from the map if it is
   629         // uninitialized.
   630         map->Remove(this);
   631     }
   633     if (mIdentity) {
   634         XPCJSRuntime* rt = GetRuntime();
   635         if (rt && rt->GetDoingFinalization()) {
   636             nsContentUtils::DeferredFinalize(mIdentity);
   637             mIdentity = nullptr;
   638         } else {
   639             NS_RELEASE(mIdentity);
   640         }
   641     }
   643     mMaybeScope = nullptr;
   644 }
   646 void
   647 XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo *si)
   648 {
   649     MOZ_ASSERT(mScriptableInfo, "UpdateScriptableInfo expects an existing scriptable info");
   651     // Write barrier for incremental GC.
   652     JSRuntime* rt = GetRuntime()->Runtime();
   653     if (IsIncrementalBarrierNeeded(rt))
   654         mScriptableInfo->Mark();
   656     mScriptableInfo = si;
   657 }
   659 void
   660 XPCWrappedNative::SetProto(XPCWrappedNativeProto* p)
   661 {
   662     MOZ_ASSERT(!IsWrapperExpired(), "bad ptr!");
   664     MOZ_ASSERT(HasProto());
   666     // Write barrier for incremental GC.
   667     JSRuntime* rt = GetRuntime()->Runtime();
   668     GetProto()->WriteBarrierPre(rt);
   670     mMaybeProto = p;
   671 }
   673 // This is factored out so that it can be called publicly
   674 // static
   675 void
   676 XPCWrappedNative::GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
   677                                                   XPCNativeScriptableCreateInfo& sciProto)
   678 {
   679     MOZ_ASSERT(classInfo, "bad param");
   680     MOZ_ASSERT(!sciProto.GetCallback(), "bad param");
   682     nsXPCClassInfo *classInfoHelper = nullptr;
   683     CallQueryInterface(classInfo, &classInfoHelper);
   684     if (classInfoHelper) {
   685         nsCOMPtr<nsIXPCScriptable> helper =
   686           dont_AddRef(static_cast<nsIXPCScriptable*>(classInfoHelper));
   687         uint32_t flags = classInfoHelper->GetScriptableFlags();
   688         sciProto.SetCallback(helper.forget());
   689         sciProto.SetFlags(flags);
   690         sciProto.SetInterfacesBitmap(classInfoHelper->GetInterfacesBitmap());
   692         return;
   693     }
   695     nsCOMPtr<nsISupports> possibleHelper;
   696     nsresult rv = classInfo->GetHelperForLanguage(nsIProgrammingLanguage::JAVASCRIPT,
   697                                                   getter_AddRefs(possibleHelper));
   698     if (NS_SUCCEEDED(rv) && possibleHelper) {
   699         nsCOMPtr<nsIXPCScriptable> helper(do_QueryInterface(possibleHelper));
   700         if (helper) {
   701             uint32_t flags = helper->GetScriptableFlags();
   702             sciProto.SetCallback(helper.forget());
   703             sciProto.SetFlags(flags);
   704         }
   705     }
   706 }
   708 // static
   709 const XPCNativeScriptableCreateInfo&
   710 XPCWrappedNative::GatherScriptableCreateInfo(nsISupports* obj,
   711                                              nsIClassInfo* classInfo,
   712                                              XPCNativeScriptableCreateInfo& sciProto,
   713                                              XPCNativeScriptableCreateInfo& sciWrapper)
   714 {
   715     MOZ_ASSERT(!sciWrapper.GetCallback(), "bad param");
   717     // Get the class scriptable helper (if present)
   718     if (classInfo) {
   719         GatherProtoScriptableCreateInfo(classInfo, sciProto);
   721         if (sciProto.GetFlags().DontAskInstanceForScriptable())
   722             return sciProto;
   723     }
   725     // Do the same for the wrapper specific scriptable
   726     nsCOMPtr<nsIXPCScriptable> helper(do_QueryInterface(obj));
   727     if (helper) {
   728         uint32_t flags = helper->GetScriptableFlags();
   729         sciWrapper.SetCallback(helper.forget());
   730         sciWrapper.SetFlags(flags);
   732         // A whole series of assertions to catch bad uses of scriptable flags on
   733         // the siWrapper...
   735         MOZ_ASSERT(!(sciWrapper.GetFlags().WantPreCreate() &&
   736                      !sciProto.GetFlags().WantPreCreate()),
   737                    "Can't set WANT_PRECREATE on an instance scriptable "
   738                    "without also setting it on the class scriptable");
   740         MOZ_ASSERT(!(sciWrapper.GetFlags().DontEnumStaticProps() &&
   741                      !sciProto.GetFlags().DontEnumStaticProps() &&
   742                      sciProto.GetCallback()),
   743                    "Can't set DONT_ENUM_STATIC_PROPS on an instance scriptable "
   744                    "without also setting it on the class scriptable (if present and shared)");
   746         MOZ_ASSERT(!(sciWrapper.GetFlags().DontEnumQueryInterface() &&
   747                      !sciProto.GetFlags().DontEnumQueryInterface() &&
   748                      sciProto.GetCallback()),
   749                    "Can't set DONT_ENUM_QUERY_INTERFACE on an instance scriptable "
   750                    "without also setting it on the class scriptable (if present and shared)");
   752         MOZ_ASSERT(!(sciWrapper.GetFlags().DontAskInstanceForScriptable() &&
   753                      !sciProto.GetFlags().DontAskInstanceForScriptable()),
   754                    "Can't set DONT_ASK_INSTANCE_FOR_SCRIPTABLE on an instance scriptable "
   755                    "without also setting it on the class scriptable");
   757         MOZ_ASSERT(!(sciWrapper.GetFlags().ClassInfoInterfacesOnly() &&
   758                      !sciProto.GetFlags().ClassInfoInterfacesOnly() &&
   759                      sciProto.GetCallback()),
   760                    "Can't set CLASSINFO_INTERFACES_ONLY on an instance scriptable "
   761                    "without also setting it on the class scriptable (if present and shared)");
   763         MOZ_ASSERT(!(sciWrapper.GetFlags().AllowPropModsDuringResolve() &&
   764                      !sciProto.GetFlags().AllowPropModsDuringResolve() &&
   765                      sciProto.GetCallback()),
   766                    "Can't set ALLOW_PROP_MODS_DURING_RESOLVE on an instance scriptable "
   767                    "without also setting it on the class scriptable (if present and shared)");
   769         MOZ_ASSERT(!(sciWrapper.GetFlags().AllowPropModsToPrototype() &&
   770                      !sciProto.GetFlags().AllowPropModsToPrototype() &&
   771                      sciProto.GetCallback()),
   772                    "Can't set ALLOW_PROP_MODS_TO_PROTOTYPE on an instance scriptable "
   773                    "without also setting it on the class scriptable (if present and shared)");
   775         return sciWrapper;
   776     }
   778     return sciProto;
   779 }
   781 bool
   782 XPCWrappedNative::Init(HandleObject parent,
   783                        const XPCNativeScriptableCreateInfo* sci)
   784 {
   785     AutoJSContext cx;
   786     // setup our scriptable info...
   788     if (sci->GetCallback()) {
   789         if (HasProto()) {
   790             XPCNativeScriptableInfo* siProto = GetProto()->GetScriptableInfo();
   791             if (siProto && siProto->GetCallback() == sci->GetCallback())
   792                 mScriptableInfo = siProto;
   793         }
   794         if (!mScriptableInfo) {
   795             mScriptableInfo =
   796                 XPCNativeScriptableInfo::Construct(sci);
   798             if (!mScriptableInfo)
   799                 return false;
   800         }
   801     }
   802     XPCNativeScriptableInfo* si = mScriptableInfo;
   804     // create our flatJSObject
   806     const JSClass* jsclazz = si ? si->GetJSClass() : Jsvalify(&XPC_WN_NoHelper_JSClass.base);
   808     // We should have the global jsclass flag if and only if we're a global.
   809     MOZ_ASSERT_IF(si, !!si->GetFlags().IsGlobalObject() == !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
   811     MOZ_ASSERT(jsclazz &&
   812                jsclazz->name &&
   813                jsclazz->flags &&
   814                jsclazz->addProperty &&
   815                jsclazz->delProperty &&
   816                jsclazz->getProperty &&
   817                jsclazz->setProperty &&
   818                jsclazz->enumerate &&
   819                jsclazz->resolve &&
   820                jsclazz->convert &&
   821                jsclazz->finalize, "bad class");
   823     RootedObject protoJSObject(cx, HasProto() ?
   824                                    GetProto()->GetJSProtoObject() :
   825                                    JS_GetObjectPrototype(cx, parent));
   826     if (!protoJSObject) {
   827         return false;
   828     }
   830     mFlatJSObject = JS_NewObject(cx, jsclazz, protoJSObject, parent);
   831     if (!mFlatJSObject) {
   832         mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
   833         return false;
   834     }
   836     mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
   837     JS_SetPrivate(mFlatJSObject, this);
   839     return FinishInit();
   840 }
   842 bool
   843 XPCWrappedNative::FinishInit()
   844 {
   845     AutoJSContext cx;
   847     // This reference will be released when mFlatJSObject is finalized.
   848     // Since this reference will push the refcount to 2 it will also root
   849     // mFlatJSObject;
   850     MOZ_ASSERT(1 == mRefCnt, "unexpected refcount value");
   851     NS_ADDREF(this);
   853     if (mScriptableInfo && mScriptableInfo->GetFlags().WantCreate() &&
   854         NS_FAILED(mScriptableInfo->GetCallback()->Create(this, cx,
   855                                                          mFlatJSObject))) {
   856         return false;
   857     }
   859     // A hack for bug 517665, increase the probability for GC.
   860     JS_updateMallocCounter(cx, 2 * sizeof(XPCWrappedNative));
   862     return true;
   863 }
   866 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCWrappedNative)
   867   NS_INTERFACE_MAP_ENTRY(nsIXPConnectWrappedNative)
   868   NS_INTERFACE_MAP_ENTRY(nsIXPConnectJSObjectHolder)
   869   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPConnectWrappedNative)
   870 NS_INTERFACE_MAP_END
   872 NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCWrappedNative)
   874 // Release calls Destroy() immediately when the refcount drops to 0 to
   875 // clear the weak references nsXPConnect has to XPCWNs and to ensure there
   876 // are no pointers to dying protos.
   877 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(XPCWrappedNative, Destroy())
   879 /*
   880  *  Wrapped Native lifetime management is messy!
   881  *
   882  *  - At creation we push the refcount to 2 (only one of which is owned by
   883  *    the native caller that caused the wrapper creation).
   884  *  - During the JS GC Mark phase we mark any wrapper with a refcount > 1.
   885  *  - The *only* thing that can make the wrapper get destroyed is the
   886  *    finalization of mFlatJSObject. And *that* should only happen if the only
   887  *    reference is the single extra (internal) reference we hold.
   888  *
   889  *  - The wrapper has a pointer to the nsISupports 'view' of the wrapped native
   890  *    object i.e... mIdentity. This is held until the wrapper's refcount goes
   891  *    to zero and the wrapper is released, or until an expired wrapper (i.e.,
   892  *    one unlinked by the cycle collector) has had its JS object finalized.
   893  *
   894  *  - The wrapper also has 'tearoffs'. It has one tearoff for each interface
   895  *    that is actually used on the native object. 'Used' means we have either
   896  *    needed to QueryInterface to verify the availability of that interface
   897  *    of that we've had to QueryInterface in order to actually make a call
   898  *    into the wrapped object via the pointer for the given interface.
   899  *
   900  *  - Each tearoff's 'mNative' member (if non-null) indicates one reference
   901  *    held by our wrapper on the wrapped native for the given interface
   902  *    associated with the tearoff. If we release that reference then we set
   903  *    the tearoff's 'mNative' to null.
   904  *
   905  *  - We use the occasion of the JavaScript GCCallback for the JSGC_MARK_END
   906  *    event to scan the tearoffs of all wrappers for non-null mNative members
   907  *    that represent unused references. We can tell that a given tearoff's
   908  *    mNative is unused by noting that no live XPCCallContexts hold a pointer
   909  *    to the tearoff.
   910  *
   911  *  - As a time/space tradeoff we may decide to not do this scanning on
   912  *    *every* JavaScript GC. We *do* want to do this *sometimes* because
   913  *    we want to allow for wrapped native's to do their own tearoff patterns.
   914  *    So, we want to avoid holding references to interfaces that we don't need.
   915  *    At the same time, we don't want to be bracketing every call into a
   916  *    wrapped native object with a QueryInterface/Release pair. And we *never*
   917  *    make a call into the object except via the correct interface for which
   918  *    we've QI'd.
   919  *
   920  *  - Each tearoff *can* have a mJSObject whose lazily resolved properties
   921  *    represent the methods/attributes/constants of that specific interface.
   922  *    This is optionally reflected into JavaScript as "foo.nsIFoo" when "foo"
   923  *    is the name of mFlatJSObject and "nsIFoo" is the name of the given
   924  *    interface associated with the tearoff. When we create the tearoff's
   925  *    mJSObject we set it's parent to be mFlatJSObject. This way we know that
   926  *    when mFlatJSObject get's collected there are no outstanding reachable
   927  *    tearoff mJSObjects. Note that we must clear the private of any lingering
   928  *    mJSObjects at this point because we have no guarentee of the *order* of
   929  *    finalization within a given gc cycle.
   930  */
   932 void
   933 XPCWrappedNative::FlatJSObjectFinalized()
   934 {
   935     if (!IsValid())
   936         return;
   938     // Iterate the tearoffs and null out each of their JSObject's privates.
   939     // This will keep them from trying to access their pointers to the
   940     // dying tearoff object. We can safely assume that those remaining
   941     // JSObjects are about to be finalized too.
   943     XPCWrappedNativeTearOffChunk* chunk;
   944     for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
   945         XPCWrappedNativeTearOff* to = chunk->mTearOffs;
   946         for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
   947             JSObject* jso = to->GetJSObjectPreserveColor();
   948             if (jso) {
   949                 MOZ_ASSERT(JS_IsAboutToBeFinalizedUnbarriered(&jso));
   950                 JS_SetPrivate(jso, nullptr);
   951                 to->JSObjectFinalized();
   952             }
   954             // We also need to release any native pointers held...
   955             nsISupports* obj = to->GetNative();
   956             if (obj) {
   957 #ifdef XP_WIN
   958                 // Try to detect free'd pointer
   959                 MOZ_ASSERT(*(int*)obj != 0xdddddddd, "bad pointer!");
   960                 MOZ_ASSERT(*(int*)obj != 0,          "bad pointer!");
   961 #endif
   962                 XPCJSRuntime* rt = GetRuntime();
   963                 if (rt) {
   964                     nsContentUtils::DeferredFinalize(obj);
   965                 } else {
   966                     obj->Release();
   967                 }
   968                 to->SetNative(nullptr);
   969             }
   971             to->SetInterface(nullptr);
   972         }
   973     }
   975     nsWrapperCache *cache = nullptr;
   976     CallQueryInterface(mIdentity, &cache);
   977     if (cache)
   978         cache->ClearWrapper();
   980     mFlatJSObject = nullptr;
   981     mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
   983     MOZ_ASSERT(mIdentity, "bad pointer!");
   984 #ifdef XP_WIN
   985     // Try to detect free'd pointer
   986     MOZ_ASSERT(*(int*)mIdentity != 0xdddddddd, "bad pointer!");
   987     MOZ_ASSERT(*(int*)mIdentity != 0,          "bad pointer!");
   988 #endif
   990     if (IsWrapperExpired()) {
   991         Destroy();
   992     }
   994     // Note that it's not safe to touch mNativeWrapper here since it's
   995     // likely that it has already been finalized.
   997     Release();
   998 }
  1000 void
  1001 XPCWrappedNative::SystemIsBeingShutDown()
  1003     if (!IsValid())
  1004         return;
  1006     // The long standing strategy is to leak some objects still held at shutdown.
  1007     // The general problem is that propagating release out of xpconnect at
  1008     // shutdown time causes a world of problems.
  1010     // We leak mIdentity (see above).
  1012     // short circuit future finalization
  1013     JS_SetPrivate(mFlatJSObject, nullptr);
  1014     mFlatJSObject = nullptr;
  1015     mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
  1017     XPCWrappedNativeProto* proto = GetProto();
  1019     if (HasProto())
  1020         proto->SystemIsBeingShutDown();
  1022     if (mScriptableInfo &&
  1023         (!HasProto() ||
  1024          (proto && proto->GetScriptableInfo() != mScriptableInfo))) {
  1025         delete mScriptableInfo;
  1028     // cleanup the tearoffs...
  1030     XPCWrappedNativeTearOffChunk* chunk;
  1031     for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
  1032         XPCWrappedNativeTearOff* to = chunk->mTearOffs;
  1033         for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
  1034             if (JSObject *jso = to->GetJSObjectPreserveColor()) {
  1035                 JS_SetPrivate(jso, nullptr);
  1036                 to->SetJSObject(nullptr);
  1038             // We leak the tearoff mNative
  1039             // (for the same reason we leak mIdentity - see above).
  1040             to->SetNative(nullptr);
  1041             to->SetInterface(nullptr);
  1045     if (mFirstChunk.mNextChunk) {
  1046         delete mFirstChunk.mNextChunk;
  1047         mFirstChunk.mNextChunk = nullptr;
  1051 /***************************************************************************/
  1053 // Dynamically ensure that two objects don't end up with the same private.
  1054 class MOZ_STACK_CLASS AutoClonePrivateGuard {
  1055 public:
  1056     AutoClonePrivateGuard(JSContext *cx, JSObject *aOld, JSObject *aNew)
  1057         : mOldReflector(cx, aOld), mNewReflector(cx, aNew)
  1059         MOZ_ASSERT(JS_GetPrivate(aOld) == JS_GetPrivate(aNew));
  1062     ~AutoClonePrivateGuard()
  1064         if (JS_GetPrivate(mOldReflector)) {
  1065             JS_SetPrivate(mNewReflector, nullptr);
  1069 private:
  1070     RootedObject mOldReflector;
  1071     RootedObject mNewReflector;
  1072 };
  1074 // static
  1075 nsresult
  1076 XPCWrappedNative::ReparentWrapperIfFound(XPCWrappedNativeScope* aOldScope,
  1077                                          XPCWrappedNativeScope* aNewScope,
  1078                                          HandleObject aNewParent,
  1079                                          nsISupports* aCOMObj)
  1081     // Check if we're near the stack limit before we get anywhere near the
  1082     // transplanting code.
  1083     AutoJSContext cx;
  1084     JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE);
  1086     XPCNativeInterface* iface = XPCNativeInterface::GetISupports();
  1087     if (!iface)
  1088         return NS_ERROR_FAILURE;
  1090     nsresult rv;
  1092     nsRefPtr<XPCWrappedNative> wrapper;
  1093     RootedObject flat(cx);
  1094     nsWrapperCache* cache = nullptr;
  1095     CallQueryInterface(aCOMObj, &cache);
  1096     if (cache) {
  1097         flat = cache->GetWrapper();
  1098         if (flat) {
  1099             wrapper = XPCWrappedNative::Get(flat);
  1100             MOZ_ASSERT(wrapper->GetScope() == aOldScope,
  1101                          "Incorrect scope passed");
  1103     } else {
  1104         rv = XPCWrappedNative::GetUsedOnly(aCOMObj, aOldScope, iface,
  1105                                            getter_AddRefs(wrapper));
  1106         if (NS_FAILED(rv))
  1107             return rv;
  1109         if (wrapper)
  1110             flat = wrapper->GetFlatJSObject();
  1113     if (!flat)
  1114         return NS_OK;
  1116     JSAutoCompartment ac(cx, aNewScope->GetGlobalJSObject());
  1118     if (aOldScope != aNewScope) {
  1119         // Oh, so now we need to move the wrapper to a different scope.
  1120         AutoMarkingWrappedNativeProtoPtr oldProto(cx);
  1121         AutoMarkingWrappedNativeProtoPtr newProto(cx);
  1123         // Cross-scope means cross-compartment.
  1124         MOZ_ASSERT(js::GetObjectCompartment(aOldScope->GetGlobalJSObject()) !=
  1125                    js::GetObjectCompartment(aNewScope->GetGlobalJSObject()));
  1126         MOZ_ASSERT(aNewParent, "won't be able to find the new parent");
  1128         if (wrapper->HasProto()) {
  1129             oldProto = wrapper->GetProto();
  1130             XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo();
  1131             XPCNativeScriptableCreateInfo ci(*info);
  1132             newProto =
  1133                 XPCWrappedNativeProto::GetNewOrUsed(aNewScope,
  1134                                                     oldProto->GetClassInfo(),
  1135                                                     &ci);
  1136             if (!newProto) {
  1137                 return NS_ERROR_FAILURE;
  1141         // First, the clone of the reflector, get a copy of its
  1142         // properties and clone its expando chain. The only part that is
  1143         // dangerous here if we have to return early is that we must avoid
  1144         // ending up with two reflectors pointing to the same WN. Other than
  1145         // that, the objects we create will just go away if we return early.
  1147         RootedObject proto(cx, newProto->GetJSProtoObject());
  1148         RootedObject newobj(cx, JS_CloneObject(cx, flat, proto, aNewParent));
  1149         if (!newobj)
  1150             return NS_ERROR_FAILURE;
  1152         // At this point, both |flat| and |newobj| point to the same wrapped
  1153         // native, which is bad, because one of them will end up finalizing
  1154         // a wrapped native it does not own. |cloneGuard| ensures that if we
  1155         // exit before calling clearing |flat|'s private the private of
  1156         // |newobj| will be set to nullptr. |flat| will go away soon, because
  1157         // we swap it with another object during the transplant and let that
  1158         // object die.
  1159         RootedObject propertyHolder(cx);
  1161             AutoClonePrivateGuard cloneGuard(cx, flat, newobj);
  1163             propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(),
  1164                                                         aNewParent);
  1165             if (!propertyHolder)
  1166                 return NS_ERROR_OUT_OF_MEMORY;
  1167             if (!JS_CopyPropertiesFrom(cx, propertyHolder, flat))
  1168                 return NS_ERROR_FAILURE;
  1170             // Expandos from other compartments are attached to the target JS object.
  1171             // Copy them over, and let the old ones die a natural death.
  1172             if (!XrayUtils::CloneExpandoChain(cx, newobj, flat))
  1173                 return NS_ERROR_FAILURE;
  1175             // We've set up |newobj|, so we make it own the WN by nulling out
  1176             // the private of |flat|.
  1177             //
  1178             // NB: It's important to do this _after_ copying the properties to
  1179             // propertyHolder. Otherwise, an object with |foo.x === foo| will
  1180             // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
  1181             JS_SetPrivate(flat, nullptr);
  1184         // Update scope maps. This section modifies global state, so from
  1185         // here on out we crash if anything fails.
  1186         Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
  1187         Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();
  1189         oldMap->Remove(wrapper);
  1191         if (wrapper->HasProto())
  1192             wrapper->SetProto(newProto);
  1194         // If the wrapper has no scriptable or it has a non-shared
  1195         // scriptable, then we don't need to mess with it.
  1196         // Otherwise...
  1198         if (wrapper->mScriptableInfo &&
  1199             wrapper->mScriptableInfo == oldProto->GetScriptableInfo()) {
  1200             // The new proto had better have the same JSClass stuff as
  1201             // the old one! We maintain a runtime wide unique map of
  1202             // this stuff. So, if these don't match then the caller is
  1203             // doing something bad here.
  1205             MOZ_ASSERT(oldProto->GetScriptableInfo()->GetScriptableShared() ==
  1206                        newProto->GetScriptableInfo()->GetScriptableShared(),
  1207                        "Changing proto is also changing JSObject Classname or "
  1208                        "helper's nsIXPScriptable flags. This is not allowed!");
  1210             wrapper->UpdateScriptableInfo(newProto->GetScriptableInfo());
  1213         // Crash if the wrapper is already in the new scope.
  1214         if (newMap->Find(wrapper->GetIdentityObject()))
  1215             MOZ_CRASH();
  1217         if (!newMap->Add(wrapper))
  1218             MOZ_CRASH();
  1220         flat = xpc::TransplantObject(cx, flat, newobj);
  1221         if (!flat)
  1222             MOZ_CRASH();
  1224         MOZ_ASSERT(flat);
  1225         wrapper->mFlatJSObject = flat;
  1226         wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
  1228         if (cache) {
  1229             bool preserving = cache->PreservingWrapper();
  1230             cache->SetPreservingWrapper(false);
  1231             cache->SetWrapper(flat);
  1232             cache->SetPreservingWrapper(preserving);
  1234         if (!JS_CopyPropertiesFrom(cx, flat, propertyHolder))
  1235             MOZ_CRASH();
  1237         // Call the scriptable hook to indicate that we transplanted.
  1238         XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
  1239         if (si->GetFlags().WantPostCreate())
  1240             (void) si->GetCallback()->PostTransplant(wrapper, cx, flat);
  1243     // Now we can just fix up the parent and return the wrapper
  1245     if (aNewParent) {
  1246         if (!JS_SetParent(cx, flat, aNewParent))
  1247             MOZ_CRASH();
  1250     return NS_OK;
  1253 // Orphans are sad little things - If only we could treat them better. :-(
  1254 //
  1255 // When a wrapper gets reparented to another scope (for example, when calling
  1256 // adoptNode), it's entirely possible that it previously served as the parent for
  1257 // other wrappers (via PreCreate hooks). When it moves, the old mFlatJSObject is
  1258 // replaced by a cross-compartment wrapper. Its descendants really _should_ move
  1259 // too, but we have no way of locating them short of a compartment-wide sweep
  1260 // (which we believe to be prohibitively expensive).
  1261 //
  1262 // So we just leave them behind. In practice, the only time this turns out to
  1263 // be a problem is during subsequent wrapper reparenting. When this happens, we
  1264 // call into the below fixup code at the last minute and straighten things out
  1265 // before proceeding.
  1266 //
  1267 // See bug 751995 for more information.
  1269 static nsresult
  1270 RescueOrphans(HandleObject obj)
  1272     AutoJSContext cx;
  1273     //
  1274     // Even if we're not an orphan at the moment, one of our ancestors might
  1275     // be. If so, we need to recursively rescue up the parent chain.
  1276     //
  1278     // First, get the parent object. If we're currently an orphan, the parent
  1279     // object is a cross-compartment wrapper. Follow the parent into its own
  1280     // compartment and fix it up there. We'll fix up |this| afterwards.
  1281     //
  1282     // NB: We pass stopAtOuter=false during the unwrap because Location objects
  1283     // are parented to outer window proxies.
  1284     nsresult rv;
  1285     RootedObject parentObj(cx, js::GetObjectParent(obj));
  1286     if (!parentObj)
  1287         return NS_OK; // Global object. We're done.
  1288     parentObj = js::UncheckedUnwrap(parentObj, /* stopAtOuter = */ false);
  1290     // PreCreate may touch dead compartments.
  1291     js::AutoMaybeTouchDeadZones agc(parentObj);
  1293     // Recursively fix up orphans on the parent chain.
  1294     rv = RescueOrphans(parentObj);
  1295     NS_ENSURE_SUCCESS(rv, rv);
  1297     // Now that we know our parent is in the right place, determine if we've
  1298     // been orphaned. If not, we have nothing to do.
  1299     if (!js::IsCrossCompartmentWrapper(parentObj))
  1300         return NS_OK;
  1302     // We've been orphaned. Find where our parent went, and follow it.
  1303     if (IS_WN_REFLECTOR(obj)) {
  1304         RootedObject realParent(cx, js::UncheckedUnwrap(parentObj));
  1305         XPCWrappedNative *wn =
  1306             static_cast<XPCWrappedNative*>(js::GetObjectPrivate(obj));
  1307         return wn->ReparentWrapperIfFound(GetObjectScope(parentObj),
  1308                                           GetObjectScope(realParent),
  1309                                           realParent, wn->GetIdentityObject());
  1312     JSAutoCompartment ac(cx, obj);
  1313     return ReparentWrapper(cx, obj);
  1316 // Recursively fix up orphans on the parent chain of a wrapper. Note that this
  1317 // can cause a wrapper to move even if it is not an orphan, since its parent
  1318 // might be an orphan and fixing the parent causes this wrapper to become an
  1319 // orphan.
  1320 nsresult
  1321 XPCWrappedNative::RescueOrphans()
  1323     AutoJSContext cx;
  1324     RootedObject flatJSObject(cx, mFlatJSObject);
  1325     return ::RescueOrphans(flatJSObject);
  1328 bool
  1329 XPCWrappedNative::ExtendSet(XPCNativeInterface* aInterface)
  1331     AutoJSContext cx;
  1333     if (!mSet->HasInterface(aInterface)) {
  1334         AutoMarkingNativeSetPtr newSet(cx);
  1335         newSet = XPCNativeSet::GetNewOrUsed(mSet, aInterface,
  1336                                             mSet->GetInterfaceCount());
  1337         if (!newSet)
  1338             return false;
  1340         mSet = newSet;
  1342     return true;
  1345 XPCWrappedNativeTearOff*
  1346 XPCWrappedNative::LocateTearOff(XPCNativeInterface* aInterface)
  1348     for (XPCWrappedNativeTearOffChunk* chunk = &mFirstChunk;
  1349          chunk != nullptr;
  1350          chunk = chunk->mNextChunk) {
  1351         XPCWrappedNativeTearOff* tearOff = chunk->mTearOffs;
  1352         XPCWrappedNativeTearOff* const end = tearOff +
  1353             XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK;
  1354         for (tearOff = chunk->mTearOffs;
  1355              tearOff < end;
  1356              tearOff++) {
  1357             if (tearOff->GetInterface() == aInterface) {
  1358                 return tearOff;
  1362     return nullptr;
  1365 XPCWrappedNativeTearOff*
  1366 XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface,
  1367                               bool needJSObject /* = false */,
  1368                               nsresult* pError /* = nullptr */)
  1370     AutoJSContext cx;
  1371     nsresult rv = NS_OK;
  1372     XPCWrappedNativeTearOff* to;
  1373     XPCWrappedNativeTearOff* firstAvailable = nullptr;
  1375     XPCWrappedNativeTearOffChunk* lastChunk;
  1376     XPCWrappedNativeTearOffChunk* chunk;
  1377     for (lastChunk = chunk = &mFirstChunk;
  1378          chunk;
  1379          lastChunk = chunk, chunk = chunk->mNextChunk) {
  1380         to = chunk->mTearOffs;
  1381         XPCWrappedNativeTearOff* const end = chunk->mTearOffs +
  1382             XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK;
  1383         for (to = chunk->mTearOffs;
  1384              to < end;
  1385              to++) {
  1386             if (to->GetInterface() == aInterface) {
  1387                 if (needJSObject && !to->GetJSObjectPreserveColor()) {
  1388                     AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
  1389                     bool ok = InitTearOffJSObject(to);
  1390                     // During shutdown, we don't sweep tearoffs.  So make sure
  1391                     // to unmark manually in case the auto-marker marked us.
  1392                     // We shouldn't ever be getting here _during_ our
  1393                     // Mark/Sweep cycle, so this should be safe.
  1394                     to->Unmark();
  1395                     if (!ok) {
  1396                         to = nullptr;
  1397                         rv = NS_ERROR_OUT_OF_MEMORY;
  1400                 if (pError)
  1401                     *pError = rv;
  1402                 return to;
  1404             if (!firstAvailable && to->IsAvailable())
  1405                 firstAvailable = to;
  1409     to = firstAvailable;
  1411     if (!to) {
  1412         auto newChunk = new XPCWrappedNativeTearOffChunk();
  1413         lastChunk->mNextChunk = newChunk;
  1414         to = newChunk->mTearOffs;
  1418         // Scope keeps |tearoff| from leaking across the rest of the function.
  1419         AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
  1420         rv = InitTearOff(to, aInterface, needJSObject);
  1421         // During shutdown, we don't sweep tearoffs.  So make sure to unmark
  1422         // manually in case the auto-marker marked us.  We shouldn't ever be
  1423         // getting here _during_ our Mark/Sweep cycle, so this should be safe.
  1424         to->Unmark();
  1425         if (NS_FAILED(rv))
  1426             to = nullptr;
  1429     if (pError)
  1430         *pError = rv;
  1431     return to;
  1434 nsresult
  1435 XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff,
  1436                               XPCNativeInterface* aInterface,
  1437                               bool needJSObject)
  1439     AutoJSContext cx;
  1441     // Determine if the object really does this interface...
  1443     const nsIID* iid = aInterface->GetIID();
  1444     nsISupports* identity = GetIdentityObject();
  1445     nsISupports* obj;
  1447     // If the scriptable helper forbids us from reflecting additional
  1448     // interfaces, then don't even try the QI, just fail.
  1449     if (mScriptableInfo &&
  1450         mScriptableInfo->GetFlags().ClassInfoInterfacesOnly() &&
  1451         !mSet->HasInterface(aInterface) &&
  1452         !mSet->HasInterfaceWithAncestor(aInterface)) {
  1453         return NS_ERROR_NO_INTERFACE;
  1456     // We are about to call out to other code.
  1457     // So protect our intended tearoff.
  1459     aTearOff->SetReserved();
  1461     if (NS_FAILED(identity->QueryInterface(*iid, (void**)&obj)) || !obj) {
  1462         aTearOff->SetInterface(nullptr);
  1463         return NS_ERROR_NO_INTERFACE;
  1466     // Guard against trying to build a tearoff for a shared nsIClassInfo.
  1467     if (iid->Equals(NS_GET_IID(nsIClassInfo))) {
  1468         nsCOMPtr<nsISupports> alternate_identity(do_QueryInterface(obj));
  1469         if (alternate_identity.get() != identity) {
  1470             NS_RELEASE(obj);
  1471             aTearOff->SetInterface(nullptr);
  1472             return NS_ERROR_NO_INTERFACE;
  1476     // Guard against trying to build a tearoff for an interface that is
  1477     // aggregated and is implemented as a nsIXPConnectWrappedJS using this
  1478     // self-same JSObject. The XBL system does this. If we mutate the set
  1479     // of this wrapper then we will shadow the method that XBL has added to
  1480     // the JSObject that it has inserted in the JS proto chain between our
  1481     // JSObject and our XPCWrappedNativeProto's JSObject. If we let this
  1482     // set mutation happen then the interface's methods will be added to
  1483     // our JSObject, but calls on those methods will get routed up to
  1484     // native code and into the wrappedJS - which will do a method lookup
  1485     // on *our* JSObject and find the same method and make another call
  1486     // into an infinite loop.
  1487     // see: http://bugzilla.mozilla.org/show_bug.cgi?id=96725
  1489     // The code in this block also does a check for the double wrapped
  1490     // nsIPropertyBag case.
  1492     nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(obj));
  1493     if (wrappedJS) {
  1494         RootedObject jso(cx, wrappedJS->GetJSObject());
  1495         if (jso == mFlatJSObject) {
  1496             // The implementing JSObject is the same as ours! Just say OK
  1497             // without actually extending the set.
  1498             //
  1499             // XXX It is a little cheesy to have FindTearOff return an
  1500             // 'empty' tearoff. But this is the centralized place to do the
  1501             // QI activities on the underlying object. *And* most caller to
  1502             // FindTearOff only look for a non-null result and ignore the
  1503             // actual tearoff returned. The only callers that do use the
  1504             // returned tearoff make sure to check for either a non-null
  1505             // JSObject or a matching Interface before proceeding.
  1506             // I think we can get away with this bit of ugliness.
  1508             NS_RELEASE(obj);
  1509             aTearOff->SetInterface(nullptr);
  1510             return NS_OK;
  1513         // Decide whether or not to expose nsIPropertyBag to calling
  1514         // JS code in the double wrapped case.
  1515         //
  1516         // Our rule here is that when JSObjects are double wrapped and
  1517         // exposed to other JSObjects then the nsIPropertyBag interface
  1518         // is only exposed on an 'opt-in' basis; i.e. if the underlying
  1519         // JSObject wants other JSObjects to be able to see this interface
  1520         // then it must implement QueryInterface and not throw an exception
  1521         // when asked for nsIPropertyBag. It need not actually *implement*
  1522         // nsIPropertyBag - xpconnect will do that work.
  1524         if (iid->Equals(NS_GET_IID(nsIPropertyBag)) && jso) {
  1525             nsRefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, *iid);
  1526             if (clasp) {
  1527                 RootedObject answer(cx, clasp->CallQueryInterfaceOnJSObject(cx, jso, *iid));
  1529                 if (!answer) {
  1530                     NS_RELEASE(obj);
  1531                     aTearOff->SetInterface(nullptr);
  1532                     return NS_ERROR_NO_INTERFACE;
  1538     nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager();
  1539     if (sm && NS_FAILED(sm->
  1540                         CanCreateWrapper(cx, *iid, identity,
  1541                                          GetClassInfo()))) {
  1542         // the security manager vetoed. It should have set an exception.
  1543         NS_RELEASE(obj);
  1544         aTearOff->SetInterface(nullptr);
  1545         return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
  1548     // If this is not already in our set we need to extend our set.
  1549     // Note: we do not cache the result of the previous call to HasInterface()
  1550     // because we unlocked and called out in the interim and the result of the
  1551     // previous call might not be correct anymore.
  1553     if (!mSet->HasInterface(aInterface) && !ExtendSet(aInterface)) {
  1554         NS_RELEASE(obj);
  1555         aTearOff->SetInterface(nullptr);
  1556         return NS_ERROR_NO_INTERFACE;
  1559     aTearOff->SetInterface(aInterface);
  1560     aTearOff->SetNative(obj);
  1561     if (needJSObject && !InitTearOffJSObject(aTearOff))
  1562         return NS_ERROR_OUT_OF_MEMORY;
  1564     return NS_OK;
  1567 bool
  1568 XPCWrappedNative::InitTearOffJSObject(XPCWrappedNativeTearOff* to)
  1570     AutoJSContext cx;
  1572     RootedObject parent(cx, mFlatJSObject);
  1573     RootedObject proto(cx, JS_GetObjectPrototype(cx, parent));
  1574     JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass),
  1575                                  proto, parent);
  1576     if (!obj)
  1577         return false;
  1579     JS_SetPrivate(obj, to);
  1580     to->SetJSObject(obj);
  1581     return true;
  1584 /***************************************************************************/
  1586 static bool Throw(nsresult errNum, XPCCallContext& ccx)
  1588     XPCThrower::Throw(errNum, ccx);
  1589     return false;
  1592 /***************************************************************************/
  1594 class MOZ_STACK_CLASS CallMethodHelper
  1596     XPCCallContext& mCallContext;
  1597     // We wait to call SetLastResult(mInvokeResult) until ~CallMethodHelper(),
  1598     // so that XPCWN-implemented functions like XPCComponents::GetLastResult()
  1599     // can still access the previous result.
  1600     nsresult mInvokeResult;
  1601     nsIInterfaceInfo* const mIFaceInfo;
  1602     const nsXPTMethodInfo* mMethodInfo;
  1603     nsISupports* const mCallee;
  1604     const uint16_t mVTableIndex;
  1605     HandleId mIdxValueId;
  1607     nsAutoTArray<nsXPTCVariant, 8> mDispatchParams;
  1608     uint8_t mJSContextIndex; // TODO make const
  1609     uint8_t mOptArgcIndex; // TODO make const
  1611     jsval* const mArgv;
  1612     const uint32_t mArgc;
  1614     MOZ_ALWAYS_INLINE bool
  1615     GetArraySizeFromParam(uint8_t paramIndex, uint32_t* result) const;
  1617     MOZ_ALWAYS_INLINE bool
  1618     GetInterfaceTypeFromParam(uint8_t paramIndex,
  1619                               const nsXPTType& datum_type,
  1620                               nsID* result) const;
  1622     MOZ_ALWAYS_INLINE bool
  1623     GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const;
  1625     MOZ_ALWAYS_INLINE bool
  1626     GatherAndConvertResults();
  1628     MOZ_ALWAYS_INLINE bool
  1629     QueryInterfaceFastPath();
  1631     nsXPTCVariant*
  1632     GetDispatchParam(uint8_t paramIndex)
  1634         if (paramIndex >= mJSContextIndex)
  1635             paramIndex += 1;
  1636         if (paramIndex >= mOptArgcIndex)
  1637             paramIndex += 1;
  1638         return &mDispatchParams[paramIndex];
  1640     const nsXPTCVariant*
  1641     GetDispatchParam(uint8_t paramIndex) const
  1643         return const_cast<CallMethodHelper*>(this)->GetDispatchParam(paramIndex);
  1646     MOZ_ALWAYS_INLINE bool InitializeDispatchParams();
  1648     MOZ_ALWAYS_INLINE bool ConvertIndependentParams(bool* foundDependentParam);
  1649     MOZ_ALWAYS_INLINE bool ConvertIndependentParam(uint8_t i);
  1650     MOZ_ALWAYS_INLINE bool ConvertDependentParams();
  1651     MOZ_ALWAYS_INLINE bool ConvertDependentParam(uint8_t i);
  1653     MOZ_ALWAYS_INLINE void CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type);
  1655     MOZ_ALWAYS_INLINE bool HandleDipperParam(nsXPTCVariant* dp,
  1656                                              const nsXPTParamInfo& paramInfo);
  1658     MOZ_ALWAYS_INLINE nsresult Invoke();
  1660 public:
  1662     CallMethodHelper(XPCCallContext& ccx)
  1663         : mCallContext(ccx)
  1664         , mInvokeResult(NS_ERROR_UNEXPECTED)
  1665         , mIFaceInfo(ccx.GetInterface()->GetInterfaceInfo())
  1666         , mMethodInfo(nullptr)
  1667         , mCallee(ccx.GetTearOff()->GetNative())
  1668         , mVTableIndex(ccx.GetMethodIndex())
  1669         , mIdxValueId(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_VALUE))
  1670         , mJSContextIndex(UINT8_MAX)
  1671         , mOptArgcIndex(UINT8_MAX)
  1672         , mArgv(ccx.GetArgv())
  1673         , mArgc(ccx.GetArgc())
  1676         // Success checked later.
  1677         mIFaceInfo->GetMethodInfo(mVTableIndex, &mMethodInfo);
  1680     ~CallMethodHelper();
  1682     MOZ_ALWAYS_INLINE bool Call();
  1684 };
  1686 // static
  1687 bool
  1688 XPCWrappedNative::CallMethod(XPCCallContext& ccx,
  1689                              CallMode mode /*= CALL_METHOD */)
  1691     MOZ_ASSERT(ccx.GetXPCContext()->CallerTypeIsJavaScript(),
  1692                "Native caller for XPCWrappedNative::CallMethod?");
  1694     nsresult rv = ccx.CanCallNow();
  1695     if (NS_FAILED(rv)) {
  1696         return Throw(rv, ccx);
  1699     return CallMethodHelper(ccx).Call();
  1702 bool
  1703 CallMethodHelper::Call()
  1705     mCallContext.SetRetVal(JSVAL_VOID);
  1707     XPCJSRuntime::Get()->SetPendingException(nullptr);
  1709     if (mVTableIndex == 0) {
  1710         return QueryInterfaceFastPath();
  1713     if (!mMethodInfo) {
  1714         Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, mCallContext);
  1715         return false;
  1718     if (!InitializeDispatchParams())
  1719         return false;
  1721     // Iterate through the params doing conversions of independent params only.
  1722     // When we later convert the dependent params (if any) we will know that
  1723     // the params upon which they depend will have already been converted -
  1724     // regardless of ordering.
  1725     bool foundDependentParam = false;
  1726     if (!ConvertIndependentParams(&foundDependentParam))
  1727         return false;
  1729     if (foundDependentParam && !ConvertDependentParams())
  1730         return false;
  1732     mInvokeResult = Invoke();
  1734     if (JS_IsExceptionPending(mCallContext)) {
  1735         return false;
  1738     if (NS_FAILED(mInvokeResult)) {
  1739         ThrowBadResult(mInvokeResult, mCallContext);
  1740         return false;
  1743     return GatherAndConvertResults();
  1746 CallMethodHelper::~CallMethodHelper()
  1748     uint8_t paramCount = mMethodInfo->GetParamCount();
  1749     if (mDispatchParams.Length()) {
  1750         for (uint8_t i = 0; i < paramCount; i++) {
  1751             nsXPTCVariant* dp = GetDispatchParam(i);
  1752             const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
  1754             if (paramInfo.GetType().IsArray()) {
  1755                 void* p = dp->val.p;
  1756                 if (!p)
  1757                     continue;
  1759                 // Clean up the array contents if necessary.
  1760                 if (dp->DoesValNeedCleanup()) {
  1761                     // We need some basic information to properly destroy the array.
  1762                     uint32_t array_count = 0;
  1763                     nsXPTType datum_type;
  1764                     if (!GetArraySizeFromParam(i, &array_count) ||
  1765                         !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex,
  1766                                                                   &paramInfo,
  1767                                                                   1, &datum_type))) {
  1768                         // XXXbholley - I'm not convinced that the above calls will
  1769                         // ever fail.
  1770                         NS_ERROR("failed to get array information, we'll leak here");
  1771                         continue;
  1774                     // Loop over the array contents. For each one, we create a
  1775                     // dummy 'val' and pass it to the cleanup helper.
  1776                     for (uint32_t k = 0; k < array_count; k++) {
  1777                         nsXPTCMiniVariant v;
  1778                         v.val.p = static_cast<void**>(p)[k];
  1779                         CleanupParam(v, datum_type);
  1783                 // always free the array itself
  1784                 nsMemory::Free(p);
  1785             } else {
  1786                 // Clean up single parameters (if requested).
  1787                 if (dp->DoesValNeedCleanup())
  1788                     CleanupParam(*dp, dp->type);
  1793     mCallContext.GetXPCContext()->SetLastResult(mInvokeResult);
  1796 bool
  1797 CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex,
  1798                                         uint32_t* result) const
  1800     nsresult rv;
  1801     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
  1803     // TODO fixup the various exceptions that are thrown
  1805     rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, &paramInfo, 0, &paramIndex);
  1806     if (NS_FAILED(rv))
  1807         return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
  1809     *result = GetDispatchParam(paramIndex)->val.u32;
  1811     return true;
  1814 bool
  1815 CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex,
  1816                                             const nsXPTType& datum_type,
  1817                                             nsID* result) const
  1819     nsresult rv;
  1820     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
  1821     uint8_t tag = datum_type.TagPart();
  1823     // TODO fixup the various exceptions that are thrown
  1825     if (tag == nsXPTType::T_INTERFACE) {
  1826         rv = mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo, result);
  1827         if (NS_FAILED(rv))
  1828             return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
  1829                                  paramIndex, mCallContext);
  1830     } else if (tag == nsXPTType::T_INTERFACE_IS) {
  1831         rv = mIFaceInfo->GetInterfaceIsArgNumberForParam(mVTableIndex, &paramInfo,
  1832                                                          &paramIndex);
  1833         if (NS_FAILED(rv))
  1834             return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
  1836         nsID* p = (nsID*) GetDispatchParam(paramIndex)->val.p;
  1837         if (!p)
  1838             return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
  1839                                  paramIndex, mCallContext);
  1840         *result = *p;
  1842     return true;
  1845 bool
  1846 CallMethodHelper::GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const
  1848     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
  1850     if ((paramInfo.IsOut() || paramInfo.IsDipper()) &&
  1851         !paramInfo.IsRetval()) {
  1852         MOZ_ASSERT(paramIndex < mArgc || paramInfo.IsOptional(),
  1853                    "Expected either enough arguments or an optional argument");
  1854         jsval arg = paramIndex < mArgc ? mArgv[paramIndex] : JSVAL_NULL;
  1855         if (paramIndex < mArgc) {
  1856             RootedObject obj(mCallContext);
  1857             if (!arg.isPrimitive())
  1858                 obj = &arg.toObject();
  1859             if (!obj || !JS_GetPropertyById(mCallContext, obj, mIdxValueId, srcp)) {
  1860                 // Explicitly passed in unusable value for out param.  Note
  1861                 // that if i >= mArgc we already know that |arg| is JSVAL_NULL,
  1862                 // and that's ok.
  1863                 ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, paramIndex,
  1864                               mCallContext);
  1865                 return false;
  1870     return true;
  1873 bool
  1874 CallMethodHelper::GatherAndConvertResults()
  1876     // now we iterate through the native params to gather and convert results
  1877     uint8_t paramCount = mMethodInfo->GetParamCount();
  1878     for (uint8_t i = 0; i < paramCount; i++) {
  1879         const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
  1880         if (!paramInfo.IsOut() && !paramInfo.IsDipper())
  1881             continue;
  1883         const nsXPTType& type = paramInfo.GetType();
  1884         nsXPTCVariant* dp = GetDispatchParam(i);
  1885         RootedValue v(mCallContext, NullValue());
  1886         uint32_t array_count = 0;
  1887         nsXPTType datum_type;
  1888         bool isArray = type.IsArray();
  1889         bool isSizedString = isArray ?
  1890                 false :
  1891                 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
  1892                 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
  1894         if (isArray) {
  1895             if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
  1896                                                       &datum_type))) {
  1897                 Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
  1898                 return false;
  1900         } else
  1901             datum_type = type;
  1903         if (isArray || isSizedString) {
  1904             if (!GetArraySizeFromParam(i, &array_count))
  1905                 return false;
  1908         nsID param_iid;
  1909         if (datum_type.IsInterfacePointer() &&
  1910             !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
  1911             return false;
  1913         nsresult err;
  1914         if (isArray) {
  1915             if (!XPCConvert::NativeArray2JS(&v, (const void**)&dp->val,
  1916                                             datum_type, &param_iid,
  1917                                             array_count, &err)) {
  1918                 // XXX need exception scheme for arrays to indicate bad element
  1919                 ThrowBadParam(err, i, mCallContext);
  1920                 return false;
  1922         } else if (isSizedString) {
  1923             if (!XPCConvert::NativeStringWithSize2JS(&v,
  1924                                                      (const void*)&dp->val,
  1925                                                      datum_type,
  1926                                                      array_count, &err)) {
  1927                 ThrowBadParam(err, i, mCallContext);
  1928                 return false;
  1930         } else {
  1931             if (!XPCConvert::NativeData2JS(&v, &dp->val, datum_type,
  1932                                            &param_iid, &err)) {
  1933                 ThrowBadParam(err, i, mCallContext);
  1934                 return false;
  1938         if (paramInfo.IsRetval()) {
  1939             mCallContext.SetRetVal(v);
  1940         } else if (i < mArgc) {
  1941             // we actually assured this before doing the invoke
  1942             MOZ_ASSERT(mArgv[i].isObject(), "out var is not object");
  1943             RootedObject obj(mCallContext, &mArgv[i].toObject());
  1944             if (!JS_SetPropertyById(mCallContext, obj, mIdxValueId, v)) {
  1945                 ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, i, mCallContext);
  1946                 return false;
  1948         } else {
  1949             MOZ_ASSERT(paramInfo.IsOptional(),
  1950                        "Expected either enough arguments or an optional argument");
  1954     return true;
  1957 bool
  1958 CallMethodHelper::QueryInterfaceFastPath()
  1960     MOZ_ASSERT(mVTableIndex == 0,
  1961                "Using the QI fast-path for a method other than QueryInterface");
  1963     if (mArgc < 1) {
  1964         Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
  1965         return false;
  1968     if (!mArgv[0].isObject()) {
  1969         ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
  1970         return false;
  1973     const nsID* iid = xpc_JSObjectToID(mCallContext, &mArgv[0].toObject());
  1974     if (!iid) {
  1975         ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
  1976         return false;
  1979     nsISupports* qiresult = nullptr;
  1980     mInvokeResult = mCallee->QueryInterface(*iid, (void**) &qiresult);
  1982     if (NS_FAILED(mInvokeResult)) {
  1983         ThrowBadResult(mInvokeResult, mCallContext);
  1984         return false;
  1987     RootedValue v(mCallContext, NullValue());
  1988     nsresult err;
  1989     bool success =
  1990         XPCConvert::NativeData2JS(&v, &qiresult,
  1991                                   nsXPTType::T_INTERFACE_IS,
  1992                                   iid, &err);
  1993     NS_IF_RELEASE(qiresult);
  1995     if (!success) {
  1996         ThrowBadParam(err, 0, mCallContext);
  1997         return false;
  2000     mCallContext.SetRetVal(v);
  2001     return true;
  2004 bool
  2005 CallMethodHelper::InitializeDispatchParams()
  2007     const uint8_t wantsOptArgc = mMethodInfo->WantsOptArgc() ? 1 : 0;
  2008     const uint8_t wantsJSContext = mMethodInfo->WantsContext() ? 1 : 0;
  2009     const uint8_t paramCount = mMethodInfo->GetParamCount();
  2010     uint8_t requiredArgs = paramCount;
  2011     uint8_t hasRetval = 0;
  2013     // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
  2014     if (paramCount && mMethodInfo->GetParam(paramCount-1).IsRetval()) {
  2015         hasRetval = 1;
  2016         requiredArgs--;
  2019     if (mArgc < requiredArgs || wantsOptArgc) {
  2020         if (wantsOptArgc)
  2021             mOptArgcIndex = requiredArgs;
  2023         // skip over any optional arguments
  2024         while (requiredArgs && mMethodInfo->GetParam(requiredArgs-1).IsOptional())
  2025             requiredArgs--;
  2027         if (mArgc < requiredArgs) {
  2028             Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
  2029             return false;
  2033     if (wantsJSContext) {
  2034         if (wantsOptArgc)
  2035             // Need to bump mOptArgcIndex up one here.
  2036             mJSContextIndex = mOptArgcIndex++;
  2037         else if (mMethodInfo->IsSetter() || mMethodInfo->IsGetter())
  2038             // For attributes, we always put the JSContext* first.
  2039             mJSContextIndex = 0;
  2040         else
  2041             mJSContextIndex = paramCount - hasRetval;
  2044     // iterate through the params to clear flags (for safe cleanup later)
  2045     for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) {
  2046         nsXPTCVariant* dp = mDispatchParams.AppendElement();
  2047         dp->ClearFlags();
  2048         dp->val.p = nullptr;
  2051     // Fill in the JSContext argument
  2052     if (wantsJSContext) {
  2053         nsXPTCVariant* dp = &mDispatchParams[mJSContextIndex];
  2054         dp->type = nsXPTType::T_VOID;
  2055         dp->val.p = mCallContext;
  2058     // Fill in the optional_argc argument
  2059     if (wantsOptArgc) {
  2060         nsXPTCVariant* dp = &mDispatchParams[mOptArgcIndex];
  2061         dp->type = nsXPTType::T_U8;
  2062         dp->val.u8 = std::min<uint32_t>(mArgc, paramCount) - requiredArgs;
  2065     return true;
  2068 bool
  2069 CallMethodHelper::ConvertIndependentParams(bool* foundDependentParam)
  2071     const uint8_t paramCount = mMethodInfo->GetParamCount();
  2072     for (uint8_t i = 0; i < paramCount; i++) {
  2073         const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
  2075         if (paramInfo.GetType().IsDependent())
  2076             *foundDependentParam = true;
  2077         else if (!ConvertIndependentParam(i))
  2078             return false;
  2082     return true;
  2085 bool
  2086 CallMethodHelper::ConvertIndependentParam(uint8_t i)
  2088     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
  2089     const nsXPTType& type = paramInfo.GetType();
  2090     uint8_t type_tag = type.TagPart();
  2091     nsXPTCVariant* dp = GetDispatchParam(i);
  2092     dp->type = type;
  2093     MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!");
  2095     // Handle dipper types separately.
  2096     if (paramInfo.IsDipper())
  2097         return HandleDipperParam(dp, paramInfo);
  2099     // Specify the correct storage/calling semantics.
  2100     if (paramInfo.IsIndirect())
  2101         dp->SetIndirect();
  2103     // The JSVal proper is always stored within the 'val' union and passed
  2104     // indirectly, regardless of in/out-ness.
  2105     if (type_tag == nsXPTType::T_JSVAL) {
  2106         // Root the value.
  2107         dp->val.j = JSVAL_VOID;
  2108         if (!js::AddRawValueRoot(mCallContext, &dp->val.j, "XPCWrappedNative::CallMethod param"))
  2109             return false;
  2112     // Flag cleanup for anything that isn't self-contained.
  2113     if (!type.IsArithmetic())
  2114         dp->SetValNeedsCleanup();
  2116     // Even if there's nothing to convert, we still need to examine the
  2117     // JSObject container for out-params. If it's null or otherwise invalid,
  2118     // we want to know before the call, rather than after.
  2119     //
  2120     // This is a no-op for 'in' params.
  2121     RootedValue src(mCallContext);
  2122     if (!GetOutParamSource(i, &src))
  2123         return false;
  2125     // All that's left to do is value conversion. Bail early if we don't need
  2126     // to do that.
  2127     if (!paramInfo.IsIn())
  2128         return true;
  2130     // We're definitely some variety of 'in' now, so there's something to
  2131     // convert. The source value for conversion depends on whether we're
  2132     // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
  2133     // so all that's left is 'in'.
  2134     if (!paramInfo.IsOut()) {
  2135         // Handle the 'in' case.
  2136         MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
  2137                    "Expected either enough arguments or an optional argument");
  2138         if (i < mArgc)
  2139             src = mArgv[i];
  2140         else if (type_tag == nsXPTType::T_JSVAL)
  2141             src = JSVAL_VOID;
  2142         else
  2143             src = JSVAL_NULL;
  2146     nsID param_iid;
  2147     if (type_tag == nsXPTType::T_INTERFACE &&
  2148         NS_FAILED(mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo,
  2149                                                     &param_iid))) {
  2150         ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, i, mCallContext);
  2151         return false;
  2154     nsresult err;
  2155     if (!XPCConvert::JSData2Native(&dp->val, src, type, true, &param_iid, &err)) {
  2156         ThrowBadParam(err, i, mCallContext);
  2157         return false;
  2160     return true;
  2163 bool
  2164 CallMethodHelper::ConvertDependentParams()
  2166     const uint8_t paramCount = mMethodInfo->GetParamCount();
  2167     for (uint8_t i = 0; i < paramCount; i++) {
  2168         const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
  2170         if (!paramInfo.GetType().IsDependent())
  2171             continue;
  2172         if (!ConvertDependentParam(i))
  2173             return false;
  2176     return true;
  2179 bool
  2180 CallMethodHelper::ConvertDependentParam(uint8_t i)
  2182     const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
  2183     const nsXPTType& type = paramInfo.GetType();
  2184     nsXPTType datum_type;
  2185     uint32_t array_count = 0;
  2186     bool isArray = type.IsArray();
  2188     bool isSizedString = isArray ?
  2189         false :
  2190         type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
  2191         type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
  2193     nsXPTCVariant* dp = GetDispatchParam(i);
  2194     dp->type = type;
  2196     if (isArray) {
  2197         if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
  2198                                                   &datum_type))) {
  2199             Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
  2200             return false;
  2202         MOZ_ASSERT(datum_type.TagPart() != nsXPTType::T_JSVAL,
  2203                    "Arrays of JSVals not currently supported - see bug 693337.");
  2204     } else {
  2205         datum_type = type;
  2208     // Specify the correct storage/calling semantics.
  2209     if (paramInfo.IsIndirect())
  2210         dp->SetIndirect();
  2212     // We have 3 possible type of dependent parameters: Arrays, Sized Strings,
  2213     // and iid_is Interface pointers. The latter two always need cleanup, and
  2214     // arrays need cleanup for all non-arithmetic types. Since the latter two
  2215     // cases also happen to be non-arithmetic, we can just inspect datum_type
  2216     // here.
  2217     if (!datum_type.IsArithmetic())
  2218         dp->SetValNeedsCleanup();
  2220     // Even if there's nothing to convert, we still need to examine the
  2221     // JSObject container for out-params. If it's null or otherwise invalid,
  2222     // we want to know before the call, rather than after.
  2223     //
  2224     // This is a no-op for 'in' params.
  2225     RootedValue src(mCallContext);
  2226     if (!GetOutParamSource(i, &src))
  2227         return false;
  2229     // All that's left to do is value conversion. Bail early if we don't need
  2230     // to do that.
  2231     if (!paramInfo.IsIn())
  2232         return true;
  2234     // We're definitely some variety of 'in' now, so there's something to
  2235     // convert. The source value for conversion depends on whether we're
  2236     // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
  2237     // so all that's left is 'in'.
  2238     if (!paramInfo.IsOut()) {
  2239         // Handle the 'in' case.
  2240         MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
  2241                      "Expected either enough arguments or an optional argument");
  2242         src = i < mArgc ? mArgv[i] : JSVAL_NULL;
  2245     nsID param_iid;
  2246     if (datum_type.IsInterfacePointer() &&
  2247         !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
  2248         return false;
  2250     nsresult err;
  2252     if (isArray || isSizedString) {
  2253         if (!GetArraySizeFromParam(i, &array_count))
  2254             return false;
  2256         if (isArray) {
  2257             if (array_count &&
  2258                 !XPCConvert::JSArray2Native((void**)&dp->val, src,
  2259                                             array_count, datum_type, &param_iid,
  2260                                             &err)) {
  2261                 // XXX need exception scheme for arrays to indicate bad element
  2262                 ThrowBadParam(err, i, mCallContext);
  2263                 return false;
  2265         } else // if (isSizedString)
  2267             if (!XPCConvert::JSStringWithSize2Native((void*)&dp->val,
  2268                                                      src, array_count,
  2269                                                      datum_type, &err)) {
  2270                 ThrowBadParam(err, i, mCallContext);
  2271                 return false;
  2274     } else {
  2275         if (!XPCConvert::JSData2Native(&dp->val, src, type, true,
  2276                                        &param_iid, &err)) {
  2277             ThrowBadParam(err, i, mCallContext);
  2278             return false;
  2282     return true;
  2285 // Performs all necessary teardown on a parameter after method invocation.
  2286 //
  2287 // This method should only be called if the value in question was flagged
  2288 // for cleanup (ie, if dp->DoesValNeedCleanup()).
  2289 void
  2290 CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type)
  2292     // We handle array elements, but not the arrays themselves.
  2293     MOZ_ASSERT(type.TagPart() != nsXPTType::T_ARRAY, "Can't handle arrays.");
  2295     // Pointers may sometimes be null even if cleanup was requested. Combine
  2296     // the null checking for all the different types into one check here.
  2297     if (type.TagPart() != nsXPTType::T_JSVAL && param.val.p == nullptr)
  2298         return;
  2300     switch (type.TagPart()) {
  2301         case nsXPTType::T_JSVAL:
  2302             js::RemoveRawValueRoot(mCallContext, (jsval*)&param.val);
  2303             break;
  2304         case nsXPTType::T_INTERFACE:
  2305         case nsXPTType::T_INTERFACE_IS:
  2306             ((nsISupports*)param.val.p)->Release();
  2307             break;
  2308         case nsXPTType::T_ASTRING:
  2309         case nsXPTType::T_DOMSTRING:
  2310             nsXPConnect::GetRuntimeInstance()->DeleteShortLivedString((nsString*)param.val.p);
  2311             break;
  2312         case nsXPTType::T_UTF8STRING:
  2313         case nsXPTType::T_CSTRING:
  2315                 nsCString* rs = (nsCString*)param.val.p;
  2316                 if (rs != &EmptyCString() && rs != &NullCString())
  2317                     delete rs;
  2319             break;
  2320         default:
  2321             MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");
  2322             nsMemory::Free(param.val.p);
  2323             break;
  2327 // Handle parameters with dipper types.
  2328 //
  2329 // Dipper types are one of the more inscrutable aspects of xpidl. In a
  2330 // nutshell, dippers are empty container objects, created and passed by
  2331 // the caller, and filled by the callee. The callee receives a
  2332 // fully-formed object, and thus does not have to construct anything. But
  2333 // the object is functionally empty, and the callee is responsible for
  2334 // putting something useful inside of it.
  2335 //
  2336 // XPIDL decides which types to make dippers. The list of these types
  2337 // is given in the isDipperType() function in typelib.py, and is currently
  2338 // limited to 4 string types.
  2339 //
  2340 // When a dipper type is declared as an 'out' parameter, xpidl internally
  2341 // converts it to an 'in', and sets the XPT_PD_DIPPER flag on it. For this
  2342 // reason, dipper types are sometimes referred to as 'out parameters
  2343 // masquerading as in'. The burden of maintaining this illusion falls mostly
  2344 // on XPConnect - we create the empty containers, and harvest the results
  2345 // after the call.
  2346 //
  2347 // This method creates these empty containers.
  2348 bool
  2349 CallMethodHelper::HandleDipperParam(nsXPTCVariant* dp,
  2350                                     const nsXPTParamInfo& paramInfo)
  2352     // Get something we can make comparisons with.
  2353     uint8_t type_tag = paramInfo.GetType().TagPart();
  2355     // Dippers always have the 'in' and 'dipper' flags set. Never 'out'.
  2356     MOZ_ASSERT(!paramInfo.IsOut(), "Dipper has unexpected flags.");
  2358     // xpidl.h specifies that dipper types will be used in exactly four
  2359     // cases, all strings. Verify that here.
  2360     MOZ_ASSERT(type_tag == nsXPTType::T_ASTRING ||
  2361                type_tag == nsXPTType::T_DOMSTRING ||
  2362                type_tag == nsXPTType::T_UTF8STRING ||
  2363                type_tag == nsXPTType::T_CSTRING,
  2364                "Unexpected dipper type!");
  2366     // ASTRING and DOMSTRING are very similar, and both use nsString.
  2367     // UTF8_STRING and CSTRING are also quite similar, and both use nsCString.
  2368     if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING)
  2369         dp->val.p = nsXPConnect::GetRuntimeInstance()->NewShortLivedString();
  2370     else
  2371         dp->val.p = new nsCString();
  2373     // Check for OOM, in either case.
  2374     if (!dp->val.p) {
  2375         JS_ReportOutOfMemory(mCallContext);
  2376         return false;
  2379     // We allocated, so we need to deallocate after the method call completes.
  2380     dp->SetValNeedsCleanup();
  2382     return true;
  2385 nsresult
  2386 CallMethodHelper::Invoke()
  2388     uint32_t argc = mDispatchParams.Length();
  2389     nsXPTCVariant* argv = mDispatchParams.Elements();
  2391     return NS_InvokeByIndex(mCallee, mVTableIndex, argc, argv);
  2394 /***************************************************************************/
  2395 // interface methods
  2397 /* JSObjectPtr GetJSObject(); */
  2398 JSObject*
  2399 XPCWrappedNative::GetJSObject()
  2401     return GetFlatJSObject();
  2404 /* readonly attribute nsISupports Native; */
  2405 NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative)
  2407     // No need to QI here, we already have the correct nsISupports
  2408     // vtable.
  2409     nsCOMPtr<nsISupports> rval = mIdentity;
  2410     rval.forget(aNative);
  2411     return NS_OK;
  2414 /* reaonly attribute JSObjectPtr JSObjectPrototype; */
  2415 NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype)
  2417     *aJSObjectPrototype = HasProto() ?
  2418                 GetProto()->GetJSProtoObject() : GetFlatJSObject();
  2419     return NS_OK;
  2422 nsIPrincipal*
  2423 XPCWrappedNative::GetObjectPrincipal() const
  2425     nsIPrincipal* principal = GetScope()->GetPrincipal();
  2426 #ifdef DEBUG
  2427     // Because of inner window reuse, we can have objects with one principal
  2428     // living in a scope with a different (but same-origin) principal. So
  2429     // just check same-origin here.
  2430     nsCOMPtr<nsIScriptObjectPrincipal> objPrin(do_QueryInterface(mIdentity));
  2431     if (objPrin) {
  2432         bool equal;
  2433         if (!principal)
  2434             equal = !objPrin->GetPrincipal();
  2435         else
  2436             principal->Equals(objPrin->GetPrincipal(), &equal);
  2437         MOZ_ASSERT(equal, "Principal mismatch.  Expect bad things to happen");
  2439 #endif
  2440     return principal;
  2443 /* XPCNativeInterface FindInterfaceWithMember (in JSHandleId name); */
  2444 NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithMember(HandleId name,
  2445                                                         nsIInterfaceInfo * *_retval)
  2447     XPCNativeInterface* iface;
  2448     XPCNativeMember*  member;
  2450     if (GetSet()->FindMember(name, &member, &iface) && iface) {
  2451         nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
  2452         temp.forget(_retval);
  2453     } else
  2454         *_retval = nullptr;
  2455     return NS_OK;
  2458 /* XPCNativeInterface FindInterfaceWithName (in JSHandleId name); */
  2459 NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithName(HandleId name,
  2460                                                       nsIInterfaceInfo * *_retval)
  2462     XPCNativeInterface* iface = GetSet()->FindNamedInterface(name);
  2463     if (iface) {
  2464         nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
  2465         temp.forget(_retval);
  2466     } else
  2467         *_retval = nullptr;
  2468     return NS_OK;
  2471 /* [notxpcom] bool HasNativeMember (in JSHandleId name); */
  2472 NS_IMETHODIMP_(bool)
  2473 XPCWrappedNative::HasNativeMember(HandleId name)
  2475     XPCNativeMember *member = nullptr;
  2476     uint16_t ignored;
  2477     return GetSet()->FindMember(name, &member, &ignored) && !!member;
  2480 /* void finishInitForWrappedGlobal (); */
  2481 NS_IMETHODIMP XPCWrappedNative::FinishInitForWrappedGlobal()
  2483     // We can only be called under certain conditions.
  2484     MOZ_ASSERT(mScriptableInfo);
  2485     MOZ_ASSERT(mScriptableInfo->GetFlags().IsGlobalObject());
  2486     MOZ_ASSERT(HasProto());
  2488     // Call PostCreateProrotype.
  2489     bool success = GetProto()->CallPostCreatePrototype();
  2490     if (!success)
  2491         return NS_ERROR_FAILURE;
  2493     return NS_OK;
  2496 /* void debugDump (in short depth); */
  2497 NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth)
  2499 #ifdef DEBUG
  2500     depth-- ;
  2501     XPC_LOG_ALWAYS(("XPCWrappedNative @ %x with mRefCnt = %d", this, mRefCnt.get()));
  2502     XPC_LOG_INDENT();
  2504         if (HasProto()) {
  2505             XPCWrappedNativeProto* proto = GetProto();
  2506             if (depth && proto)
  2507                 proto->DebugDump(depth);
  2508             else
  2509                 XPC_LOG_ALWAYS(("mMaybeProto @ %x", proto));
  2510         } else
  2511             XPC_LOG_ALWAYS(("Scope @ %x", GetScope()));
  2513         if (depth && mSet)
  2514             mSet->DebugDump(depth);
  2515         else
  2516             XPC_LOG_ALWAYS(("mSet @ %x", mSet));
  2518         XPC_LOG_ALWAYS(("mFlatJSObject of %x", mFlatJSObject.getPtr()));
  2519         XPC_LOG_ALWAYS(("mIdentity of %x", mIdentity));
  2520         XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo));
  2522         if (depth && mScriptableInfo) {
  2523             XPC_LOG_INDENT();
  2524             XPC_LOG_ALWAYS(("mScriptable @ %x", mScriptableInfo->GetCallback()));
  2525             XPC_LOG_ALWAYS(("mFlags of %x", (uint32_t)mScriptableInfo->GetFlags()));
  2526             XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptableInfo->GetJSClass()));
  2527             XPC_LOG_OUTDENT();
  2529     XPC_LOG_OUTDENT();
  2530 #endif
  2531     return NS_OK;
  2534 /***************************************************************************/
  2536 char*
  2537 XPCWrappedNative::ToString(XPCWrappedNativeTearOff* to /* = nullptr */ ) const
  2539 #ifdef DEBUG
  2540 #  define FMT_ADDR " @ 0x%p"
  2541 #  define FMT_STR(str) str
  2542 #  define PARAM_ADDR(w) , w
  2543 #else
  2544 #  define FMT_ADDR ""
  2545 #  define FMT_STR(str)
  2546 #  define PARAM_ADDR(w)
  2547 #endif
  2549     char* sz = nullptr;
  2550     char* name = nullptr;
  2552     XPCNativeScriptableInfo* si = GetScriptableInfo();
  2553     if (si)
  2554         name = JS_smprintf("%s", si->GetJSClass()->name);
  2555     if (to) {
  2556         const char* fmt = name ? " (%s)" : "%s";
  2557         name = JS_sprintf_append(name, fmt,
  2558                                  to->GetInterface()->GetNameString());
  2559     } else if (!name) {
  2560         XPCNativeSet* set = GetSet();
  2561         XPCNativeInterface** array = set->GetInterfaceArray();
  2562         uint16_t count = set->GetInterfaceCount();
  2564         if (count == 1)
  2565             name = JS_sprintf_append(name, "%s", array[0]->GetNameString());
  2566         else if (count == 2 &&
  2567                  array[0] == XPCNativeInterface::GetISupports()) {
  2568             name = JS_sprintf_append(name, "%s", array[1]->GetNameString());
  2569         } else {
  2570             for (uint16_t i = 0; i < count; i++) {
  2571                 const char* fmt = (i == 0) ?
  2572                                     "(%s" : (i == count-1) ?
  2573                                         ", %s)" : ", %s";
  2574                 name = JS_sprintf_append(name, fmt,
  2575                                          array[i]->GetNameString());
  2580     if (!name) {
  2581         return nullptr;
  2583     const char* fmt = "[xpconnect wrapped %s" FMT_ADDR FMT_STR(" (native")
  2584         FMT_ADDR FMT_STR(")") "]";
  2585     if (si) {
  2586         fmt = "[object %s" FMT_ADDR FMT_STR(" (native") FMT_ADDR FMT_STR(")") "]";
  2588     sz = JS_smprintf(fmt, name PARAM_ADDR(this) PARAM_ADDR(mIdentity));
  2590     JS_smprintf_free(name);
  2593     return sz;
  2595 #undef FMT_ADDR
  2596 #undef PARAM_ADDR
  2599 /***************************************************************************/
  2601 #ifdef XPC_CHECK_CLASSINFO_CLAIMS
  2602 static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper)
  2604     if (!wrapper || !wrapper->GetClassInfo())
  2605         return;
  2607     nsISupports* obj = wrapper->GetIdentityObject();
  2608     XPCNativeSet* set = wrapper->GetSet();
  2609     uint16_t count = set->GetInterfaceCount();
  2610     for (uint16_t i = 0; i < count; i++) {
  2611         nsIClassInfo* clsInfo = wrapper->GetClassInfo();
  2612         XPCNativeInterface* iface = set->GetInterfaceAt(i);
  2613         nsIInterfaceInfo* info = iface->GetInterfaceInfo();
  2614         const nsIID* iid;
  2615         nsISupports* ptr;
  2617         info->GetIIDShared(&iid);
  2618         nsresult rv = obj->QueryInterface(*iid, (void**)&ptr);
  2619         if (NS_SUCCEEDED(rv)) {
  2620             NS_RELEASE(ptr);
  2621             continue;
  2623         if (rv == NS_ERROR_OUT_OF_MEMORY)
  2624             continue;
  2626         // Houston, We have a problem...
  2628         char* className = nullptr;
  2629         char* contractID = nullptr;
  2630         const char* interfaceName;
  2632         info->GetNameShared(&interfaceName);
  2633         clsInfo->GetContractID(&contractID);
  2634         if (wrapper->GetScriptableInfo()) {
  2635             wrapper->GetScriptableInfo()->GetCallback()->
  2636                 GetClassName(&className);
  2640         printf("\n!!! Object's nsIClassInfo lies about its interfaces!!!\n"
  2641                "   classname: %s \n"
  2642                "   contractid: %s \n"
  2643                "   unimplemented interface name: %s\n\n",
  2644                className ? className : "<unknown>",
  2645                contractID ? contractID : "<unknown>",
  2646                interfaceName);
  2648         if (className)
  2649             nsMemory::Free(className);
  2650         if (contractID)
  2651             nsMemory::Free(contractID);
  2654 #endif
  2656 NS_IMPL_ISUPPORTS(XPCJSObjectHolder, nsIXPConnectJSObjectHolder)
  2658 JSObject*
  2659 XPCJSObjectHolder::GetJSObject()
  2661     NS_PRECONDITION(mJSObj, "bad object state");
  2662     return mJSObj;
  2665 XPCJSObjectHolder::XPCJSObjectHolder(JSObject* obj)
  2666     : mJSObj(obj)
  2668     XPCJSRuntime::Get()->AddObjectHolderRoot(this);
  2671 XPCJSObjectHolder::~XPCJSObjectHolder()
  2673     RemoveFromRootSet();
  2676 void
  2677 XPCJSObjectHolder::TraceJS(JSTracer *trc)
  2679     trc->setTracingDetails(GetTraceName, this, 0);
  2680     JS_CallHeapObjectTracer(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
  2683 // static
  2684 void
  2685 XPCJSObjectHolder::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
  2687     JS_snprintf(buf, bufsize, "XPCJSObjectHolder[0x%p].mJSObj",
  2688                 trc->debugPrintArg());
  2691 // static
  2692 XPCJSObjectHolder*
  2693 XPCJSObjectHolder::newHolder(JSObject* obj)
  2695     if (!obj) {
  2696         NS_ERROR("bad param");
  2697         return nullptr;
  2699     return new XPCJSObjectHolder(obj);

mercurial