LCOV - code coverage report
Current view: top level - js/xpconnect/src - XPCWrappedJS.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 191 332 57.5 %
Date: 2017-07-14 16:53:18 Functions: 21 34 61.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* Class that wraps JS objects to appear as XPCOM objects. */
       8             : 
       9             : #include "xpcprivate.h"
      10             : #include "jsprf.h"
      11             : #include "mozilla/DeferredFinalize.h"
      12             : #include "mozilla/Sprintf.h"
      13             : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
      14             : #include "nsCCUncollectableMarker.h"
      15             : #include "nsContentUtils.h"
      16             : #include "nsThreadUtils.h"
      17             : 
      18             : using namespace mozilla;
      19             : 
      20             : // NOTE: much of the fancy footwork is done in xpcstubs.cpp
      21             : 
      22             : 
      23             : // nsXPCWrappedJS lifetime.
      24             : //
      25             : // An nsXPCWrappedJS is either rooting its JS object or is subject to finalization.
      26             : // The subject-to-finalization state lets wrappers support
      27             : // nsSupportsWeakReference in the case where the underlying JS object
      28             : // is strongly owned, but the wrapper itself is only weakly owned.
      29             : //
      30             : // A wrapper is rooting its JS object whenever its refcount is greater than 1. In
      31             : // this state, root wrappers are always added to the cycle collector graph. The
      32             : // wrapper keeps around an extra refcount, added in the constructor, to support
      33             : // the possibility of an eventual transition to the subject-to-finalization state.
      34             : // This extra refcount is ignored by the cycle collector, which traverses the "self"
      35             : // edge for this refcount.
      36             : //
      37             : // When the refcount of a rooting wrapper drops to 1, if there is no weak reference
      38             : // to the wrapper (which can only happen for the root wrapper), it is immediately
      39             : // Destroy()'d. Otherwise, it becomes subject to finalization.
      40             : //
      41             : // When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is
      42             : // now owned exclusively by its JS object. Either a weak reference will be turned into
      43             : // a strong ref which will bring its refcount up to 2 and change the wrapper back to
      44             : // the rooting state, or it will stay alive until the JS object dies. If the JS object
      45             : // dies, then when XPCJSContext::FinalizeCallback calls FindDyingJSObjects
      46             : // it will find the wrapper and call Release() in it, destroying the wrapper.
      47             : // Otherwise, the wrapper will stay alive, even if it no longer has a weak reference
      48             : // to it.
      49             : //
      50             : // When the wrapper is subject to finalization, it is kept alive by an implicit reference
      51             : // from the JS object which is invisible to the cycle collector, so the cycle collector
      52             : // does not traverse any children of wrappers that are subject to finalization. This will
      53             : // result in a leak if a wrapper in the non-rooting state has an aggregated native that
      54             : // keeps alive the wrapper's JS object.  See bug 947049.
      55             : 
      56             : 
      57             : // If traversing wrappedJS wouldn't release it, nor cause any other objects to be
      58             : // added to the graph, there is no need to add it to the graph at all.
      59             : bool
      60           0 : nsXPCWrappedJS::CanSkip()
      61             : {
      62           0 :     if (!nsCCUncollectableMarker::sGeneration)
      63           0 :         return false;
      64             : 
      65           0 :     if (IsSubjectToFinalization())
      66           0 :         return true;
      67             : 
      68             :     // If this wrapper holds a gray object, need to trace it.
      69           0 :     JSObject* obj = GetJSObjectPreserveColor();
      70           0 :     if (obj && JS::ObjectIsMarkedGray(obj))
      71           0 :         return false;
      72             : 
      73             :     // For non-root wrappers, check if the root wrapper will be
      74             :     // added to the CC graph.
      75           0 :     if (!IsRootWrapper()) {
      76             :         // mRoot points to null after unlinking.
      77           0 :         NS_ENSURE_TRUE(mRoot, false);
      78           0 :         return mRoot->CanSkip();
      79             :     }
      80             : 
      81             :     // For the root wrapper, check if there is an aggregated
      82             :     // native object that will be added to the CC graph.
      83           0 :     if (!IsAggregatedToNative())
      84           0 :         return true;
      85             : 
      86           0 :     nsISupports* agg = GetAggregatedNativeObject();
      87           0 :     nsXPCOMCycleCollectionParticipant* cp = nullptr;
      88           0 :     CallQueryInterface(agg, &cp);
      89           0 :     nsISupports* canonical = nullptr;
      90             :     agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
      91           0 :                         reinterpret_cast<void**>(&canonical));
      92           0 :     return cp && canonical && cp->CanSkipThis(canonical);
      93             : }
      94             : 
      95             : NS_IMETHODIMP
      96           0 : NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::TraverseNative
      97             :    (void* p, nsCycleCollectionTraversalCallback& cb)
      98             : {
      99           0 :     nsISupports* s = static_cast<nsISupports*>(p);
     100           0 :     MOZ_ASSERT(CheckForRightISupports(s), "not the nsISupports pointer we expect");
     101           0 :     nsXPCWrappedJS* tmp = Downcast(s);
     102             : 
     103           0 :     nsrefcnt refcnt = tmp->mRefCnt.get();
     104           0 :     if (cb.WantDebugInfo()) {
     105             :         char name[72];
     106           0 :         if (tmp->GetClass())
     107           0 :             SprintfLiteral(name, "nsXPCWrappedJS (%s)", tmp->GetClass()->GetInterfaceName());
     108             :         else
     109           0 :             SprintfLiteral(name, "nsXPCWrappedJS");
     110           0 :         cb.DescribeRefCountedNode(refcnt, name);
     111             :     } else {
     112           0 :         NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt)
     113             :     }
     114             : 
     115             :     // A wrapper that is subject to finalization will only die when its JS object dies.
     116           0 :     if (tmp->IsSubjectToFinalization())
     117           0 :         return NS_OK;
     118             : 
     119             :     // Don't let the extra reference for nsSupportsWeakReference keep a wrapper that is
     120             :     // not subject to finalization alive.
     121           0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self");
     122           0 :     cb.NoteXPCOMChild(s);
     123             : 
     124           0 :     if (tmp->IsValid()) {
     125           0 :         MOZ_ASSERT(refcnt > 1);
     126           0 :         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj");
     127           0 :         cb.NoteJSChild(JS::GCCellPtr(tmp->GetJSObjectPreserveColor()));
     128             :     }
     129             : 
     130           0 :     if (tmp->IsRootWrapper()) {
     131           0 :         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native");
     132           0 :         cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
     133             :     } else {
     134           0 :         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root");
     135           0 :         cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper()));
     136             :     }
     137             : 
     138           0 :     return NS_OK;
     139             : }
     140             : 
     141             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
     142             : 
     143           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
     144           0 :     tmp->Unlink();
     145           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     146             : 
     147             : // XPCJSContext keeps a table of WJS, so we can remove them from
     148             : // the purple buffer in between CCs.
     149           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS)
     150           0 :     return true;
     151             : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
     152             : 
     153           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS)
     154           0 :     return tmp->CanSkip();
     155             : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
     156             : 
     157           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS)
     158           0 :     return tmp->CanSkip();
     159             : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
     160             : 
     161             : NS_IMETHODIMP
     162          66 : nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr)
     163             : {
     164          66 :     MOZ_ASSERT(IsAggregatedToNative(), "bad AggregatedQueryInterface call");
     165          66 :     *aInstancePtr = nullptr;
     166             : 
     167          66 :     if (!IsValid())
     168           0 :         return NS_ERROR_UNEXPECTED;
     169             : 
     170             :     // Put this here rather that in DelegatedQueryInterface because it needs
     171             :     // to be in QueryInterface before the possible delegation to 'outer', but
     172             :     // we don't want to do this check twice in one call in the normal case:
     173             :     // once in QueryInterface and once in DelegatedQueryInterface.
     174          66 :     if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
     175           0 :         NS_ADDREF(this);
     176           0 :         *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
     177           0 :         return NS_OK;
     178             :     }
     179             : 
     180          66 :     return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
     181             : }
     182             : 
     183             : NS_IMETHODIMP
     184        4092 : nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
     185             : {
     186        4092 :     if (nullptr == aInstancePtr) {
     187           0 :         NS_PRECONDITION(false, "null pointer");
     188           0 :         return NS_ERROR_NULL_POINTER;
     189             :     }
     190             : 
     191        4092 :     *aInstancePtr = nullptr;
     192             : 
     193        4092 :     if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
     194        1219 :         *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS);
     195        1219 :         return NS_OK;
     196             :     }
     197             : 
     198        2873 :     if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) {
     199         280 :         *aInstancePtr =
     200         280 :             NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
     201         280 :         return NS_OK;
     202             :     }
     203             : 
     204        2593 :     if (!IsValid())
     205           0 :         return NS_ERROR_UNEXPECTED;
     206             : 
     207        2593 :     if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJSUnmarkGray))) {
     208           0 :         *aInstancePtr = nullptr;
     209             : 
     210           0 :         mJSObj.exposeToActiveJS();
     211             : 
     212             :         // Just return some error value since one isn't supposed to use
     213             :         // nsIXPConnectWrappedJSUnmarkGray objects for anything.
     214           0 :         return NS_ERROR_FAILURE;
     215             :     }
     216             : 
     217             :     // Always check for this first so that our 'outer' can get this interface
     218             :     // from us without recurring into a call to the outer's QI!
     219        2593 :     if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
     220         470 :         NS_ADDREF(this);
     221         470 :         *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
     222         470 :         return NS_OK;
     223             :     }
     224             : 
     225        2123 :     nsISupports* outer = GetAggregatedNativeObject();
     226        2123 :     if (outer)
     227           6 :         return outer->QueryInterface(aIID, aInstancePtr);
     228             : 
     229             :     // else...
     230             : 
     231        2117 :     return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
     232             : }
     233             : 
     234             : 
     235             : // For a description of nsXPCWrappedJS lifetime and reference counting, see
     236             : // the comment at the top of this file.
     237             : 
     238             : MozExternalRefCountType
     239        8217 : nsXPCWrappedJS::AddRef(void)
     240             : {
     241        8217 :     MOZ_RELEASE_ASSERT(NS_IsMainThread(),
     242             :                        "nsXPCWrappedJS::AddRef called off main thread");
     243             : 
     244        8217 :     MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
     245        8217 :     nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
     246        8217 :     nsrefcnt cnt = mRefCnt.incr(base);
     247        8217 :     NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
     248             : 
     249        8217 :     if (2 == cnt && IsValid()) {
     250         976 :         GetJSObject(); // Unmark gray JSObject.
     251         976 :         mClass->GetRuntime()->AddWrappedJSRoot(this);
     252             :     }
     253             : 
     254        8217 :     return cnt;
     255             : }
     256             : 
     257             : MozExternalRefCountType
     258        5986 : nsXPCWrappedJS::Release(void)
     259             : {
     260        5986 :     MOZ_RELEASE_ASSERT(NS_IsMainThread(),
     261             :                        "nsXPCWrappedJS::Release called off main thread");
     262        5986 :     MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
     263        5986 :     NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS);
     264             : 
     265        5986 :     bool shouldDelete = false;
     266        5986 :     nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
     267        5986 :     nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete);
     268        5986 :     NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS");
     269             : 
     270        5986 :     if (0 == cnt) {
     271         146 :         if (MOZ_UNLIKELY(shouldDelete)) {
     272           0 :             mRefCnt.stabilizeForDeletion();
     273           0 :             DeleteCycleCollectable();
     274             :         } else {
     275         146 :             mRefCnt.incr(base);
     276         146 :             Destroy();
     277         146 :             mRefCnt.decr(base);
     278             :         }
     279        5840 :     } else if (1 == cnt) {
     280         216 :         if (IsValid())
     281         216 :             RemoveFromRootSet();
     282             : 
     283             :         // If we are not a root wrapper being used from a weak reference,
     284             :         // then the extra ref is not needed and we can let outselves be
     285             :         // deleted.
     286         216 :         if (!HasWeakReferences())
     287         146 :             return Release();
     288             : 
     289          70 :         MOZ_ASSERT(IsRootWrapper(), "Only root wrappers should have weak references");
     290             :     }
     291        5840 :     return cnt;
     292             : }
     293             : 
     294             : NS_IMETHODIMP_(void)
     295         140 : nsXPCWrappedJS::DeleteCycleCollectable(void)
     296             : {
     297         140 :     delete this;
     298         140 : }
     299             : 
     300             : void
     301         479 : nsXPCWrappedJS::TraceJS(JSTracer* trc)
     302             : {
     303         479 :     MOZ_ASSERT(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
     304         479 :     JS::TraceEdge(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
     305         479 : }
     306             : 
     307             : NS_IMETHODIMP
     308          72 : nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
     309             : {
     310          72 :     if (!IsRootWrapper())
     311           0 :         return mRoot->GetWeakReference(aInstancePtr);
     312             : 
     313          72 :     return nsSupportsWeakReference::GetWeakReference(aInstancePtr);
     314             : }
     315             : 
     316             : JSObject*
     317        5147 : nsXPCWrappedJS::GetJSObject()
     318             : {
     319        5147 :     return mJSObj;
     320             : }
     321             : 
     322             : // static
     323             : nsresult
     324        1278 : nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj,
     325             :                              REFNSIID aIID,
     326             :                              nsXPCWrappedJS** wrapperResult)
     327             : {
     328             :     // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
     329        1278 :     MOZ_RELEASE_ASSERT(NS_IsMainThread(),
     330             :                        "nsXPCWrappedJS::GetNewOrUsed called off main thread");
     331             : 
     332        2556 :     AutoJSContext cx;
     333             : 
     334        1278 :     bool allowNonScriptable = mozilla::jsipc::IsWrappedCPOW(jsObj);
     335        2556 :     RefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID,
     336        2556 :                                                                           allowNonScriptable);
     337        1278 :     if (!clasp)
     338           0 :         return NS_ERROR_FAILURE;
     339             : 
     340        2556 :     JS::RootedObject rootJSObj(cx, clasp->GetRootJSObject(cx, jsObj));
     341        1278 :     if (!rootJSObj)
     342           0 :         return NS_ERROR_FAILURE;
     343             : 
     344        1278 :     xpc::CompartmentPrivate* rootComp = xpc::CompartmentPrivate::Get(rootJSObj);
     345        1278 :     MOZ_ASSERT(rootComp);
     346             : 
     347             :     // Find any existing wrapper.
     348        2556 :     RefPtr<nsXPCWrappedJS> root = rootComp->GetWrappedJSMap()->Find(rootJSObj);
     349        1278 :     MOZ_ASSERT_IF(root, !nsXPConnect::GetRuntimeInstance()->GetMultiCompartmentWrappedJSMap()->
     350             :                         Find(rootJSObj));
     351        1278 :     if (!root) {
     352             :         root = nsXPConnect::GetRuntimeInstance()->GetMultiCompartmentWrappedJSMap()->
     353         749 :             Find(rootJSObj);
     354             :     }
     355             : 
     356        1278 :     nsresult rv = NS_ERROR_FAILURE;
     357        1278 :     if (root) {
     358         688 :         RefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID);
     359         582 :         if (wrapper) {
     360         476 :             wrapper.forget(wrapperResult);
     361         476 :             return NS_OK;
     362             :         }
     363         696 :     } else if (rootJSObj != jsObj) {
     364             : 
     365             :         // Make a new root wrapper, because there is no existing
     366             :         // root wrapper, and the wrapper we are trying to make isn't
     367             :         // a root.
     368             :         RefPtr<nsXPCWrappedJSClass> rootClasp =
     369         274 :             nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports));
     370         137 :         if (!rootClasp)
     371           0 :             return NS_ERROR_FAILURE;
     372             : 
     373         274 :         root = new nsXPCWrappedJS(cx, rootJSObj, rootClasp, nullptr, &rv);
     374         137 :         if (NS_FAILED(rv)) {
     375           0 :             return rv;
     376             :         }
     377             :     }
     378             : 
     379        2406 :     RefPtr<nsXPCWrappedJS> wrapper = new nsXPCWrappedJS(cx, jsObj, clasp, root, &rv);
     380         802 :     if (NS_FAILED(rv)) {
     381           0 :         return rv;
     382             :     }
     383         802 :     wrapper.forget(wrapperResult);
     384         802 :     return NS_OK;
     385             : }
     386             : 
     387         939 : nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx,
     388             :                                JSObject* aJSObj,
     389             :                                nsXPCWrappedJSClass* aClass,
     390             :                                nsXPCWrappedJS* root,
     391         939 :                                nsresult* rv)
     392             :     : mJSObj(aJSObj),
     393             :       mClass(aClass),
     394         939 :       mRoot(root ? root : this),
     395        1878 :       mNext(nullptr)
     396             : {
     397         939 :     *rv = InitStub(GetClass()->GetIID());
     398             :     // Continue even in the failure case, so that our refcounting/Destroy
     399             :     // behavior works correctly.
     400             : 
     401             :     // There is an extra AddRef to support weak references to wrappers
     402             :     // that are subject to finalization. See the top of the file for more
     403             :     // details.
     404         939 :     NS_ADDREF_THIS();
     405             : 
     406         939 :     if (IsRootWrapper()) {
     407         696 :         MOZ_ASSERT(!IsMultiCompartment(), "mNext is always nullptr here");
     408         696 :         if (!xpc::CompartmentPrivate::Get(mJSObj)->GetWrappedJSMap()->Add(cx, this)) {
     409           0 :             *rv = NS_ERROR_OUT_OF_MEMORY;
     410             :         }
     411             :     } else {
     412         243 :         NS_ADDREF(mRoot);
     413         243 :         mNext = mRoot->mNext;
     414         243 :         mRoot->mNext = this;
     415             : 
     416             :         // We always start wrappers in the per-compartment table. If adding
     417             :         // this wrapper to the chain causes it to cross compartments, we need
     418             :         // to migrate the chain to the global table on the XPCJSContext.
     419         243 :         if (mRoot->IsMultiCompartment()) {
     420         148 :             xpc::CompartmentPrivate::Get(mRoot->mJSObj)->GetWrappedJSMap()->Remove(mRoot);
     421         148 :             auto destMap = nsXPConnect::GetRuntimeInstance()->GetMultiCompartmentWrappedJSMap();
     422         148 :             if (!destMap->Add(cx, mRoot)) {
     423           0 :                 *rv = NS_ERROR_OUT_OF_MEMORY;
     424             :             }
     425             :         }
     426             :     }
     427         939 : }
     428             : 
     429         420 : nsXPCWrappedJS::~nsXPCWrappedJS()
     430             : {
     431         140 :     Destroy();
     432         420 : }
     433             : 
     434             : void
     435         285 : XPCJSRuntime::RemoveWrappedJS(nsXPCWrappedJS* wrapper)
     436             : {
     437         285 :     AssertInvalidWrappedJSNotInTable(wrapper);
     438         285 :     if (!wrapper->IsValid())
     439          91 :         return;
     440             : 
     441             :     // It is possible for the same JS XPCOM implementation object to be wrapped
     442             :     // with a different interface in multiple JSCompartments. In this case, the
     443             :     // wrapper chain will contain references to multiple compartments. While we
     444             :     // always store single-compartment chains in the per-compartment wrapped-js
     445             :     // table, chains in the multi-compartment wrapped-js table may contain
     446             :     // single-compartment chains, if they have ever contained a wrapper in a
     447             :     // different compartment. Since removal requires a lookup anyway, we just do
     448             :     // the remove on both tables unconditionally.
     449         194 :     MOZ_ASSERT_IF(wrapper->IsMultiCompartment(),
     450             :                   !xpc::CompartmentPrivate::Get(wrapper->GetJSObjectPreserveColor())->
     451             :                       GetWrappedJSMap()->HasWrapper(wrapper));
     452         194 :     GetMultiCompartmentWrappedJSMap()->Remove(wrapper);
     453             :     xpc::CompartmentPrivate::Get(wrapper->GetJSObjectPreserveColor())->GetWrappedJSMap()->
     454         194 :         Remove(wrapper);
     455             : }
     456             : 
     457             : #ifdef DEBUG
     458             : static void
     459       51744 : NotHasWrapperAssertionCallback(JSContext* cx, void* data, JSCompartment* comp)
     460             : {
     461       51744 :     auto wrapper = static_cast<nsXPCWrappedJS*>(data);
     462       51744 :     auto xpcComp = xpc::CompartmentPrivate::Get(comp);
     463       51744 :     MOZ_ASSERT_IF(xpcComp, !xpcComp->GetWrappedJSMap()->HasWrapper(wrapper));
     464       51744 : }
     465             : #endif
     466             : 
     467             : void
     468         571 : XPCJSRuntime::AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const
     469             : {
     470             : #ifdef DEBUG
     471         571 :     if (!wrapper->IsValid()) {
     472         231 :         MOZ_ASSERT(!GetMultiCompartmentWrappedJSMap()->HasWrapper(wrapper));
     473         231 :         if (!mGCIsRunning) {
     474         231 :             JSContext* cx = XPCJSContext::Get()->Context();
     475         231 :             JS_IterateCompartments(cx, wrapper, NotHasWrapperAssertionCallback);
     476             :         }
     477             :     }
     478             : #endif
     479         571 : }
     480             : 
     481             : void
     482         286 : nsXPCWrappedJS::Destroy()
     483             : {
     484         286 :     MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion");
     485             : 
     486         286 :     if (IsRootWrapper())
     487         188 :         nsXPConnect::GetRuntimeInstance()->RemoveWrappedJS(this);
     488         286 :     Unlink();
     489         286 : }
     490             : 
     491             : void
     492         286 : nsXPCWrappedJS::Unlink()
     493             : {
     494         286 :     nsXPConnect::GetRuntimeInstance()->AssertInvalidWrappedJSNotInTable(this);
     495             : 
     496         286 :     if (IsValid()) {
     497         146 :         XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
     498         146 :         if (rt) {
     499         146 :             if (IsRootWrapper())
     500          97 :                 rt->RemoveWrappedJS(this);
     501             : 
     502         146 :             if (mRefCnt > 1)
     503           0 :                 RemoveFromRootSet();
     504             :         }
     505             : 
     506         146 :         mJSObj = nullptr;
     507             :     }
     508             : 
     509         286 :     if (IsRootWrapper()) {
     510         188 :         ClearWeakReferences();
     511          98 :     } else if (mRoot) {
     512             :         // unlink this wrapper
     513          49 :         nsXPCWrappedJS* cur = mRoot;
     514             :         while (1) {
     515          53 :             if (cur->mNext == this) {
     516          49 :                 cur->mNext = mNext;
     517          49 :                 break;
     518             :             }
     519           2 :             cur = cur->mNext;
     520           2 :             MOZ_ASSERT(cur, "failed to find wrapper in its own chain");
     521             :         }
     522             : 
     523             :         // Note: unlinking this wrapper may have changed us from a multi-
     524             :         // compartment wrapper chain to a single-compartment wrapper chain. We
     525             :         // leave the wrapper in the multi-compartment table as it is likely to
     526             :         // need to be multi-compartment again in the future and, moreover, we
     527             :         // cannot get a JSContext here.
     528             : 
     529             :         // let the root go
     530          49 :         NS_RELEASE(mRoot);
     531             :     }
     532             : 
     533         286 :     mClass = nullptr;
     534         286 :     if (mOuter) {
     535           0 :         XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
     536           0 :         if (rt->GCIsRunning()) {
     537           0 :             DeferredFinalize(mOuter.forget().take());
     538             :         } else {
     539           0 :             mOuter = nullptr;
     540             :         }
     541             :     }
     542         286 : }
     543             : 
     544             : bool
     545        1133 : nsXPCWrappedJS::IsMultiCompartment() const
     546             : {
     547        1133 :     MOZ_ASSERT(IsRootWrapper());
     548        1133 :     JSCompartment* compartment = Compartment();
     549        1133 :     nsXPCWrappedJS* next = mNext;
     550        1359 :     while (next) {
     551         261 :         if (next->Compartment() != compartment)
     552         148 :             return true;
     553         113 :         next = next->mNext;
     554             :     }
     555         985 :     return false;
     556             : }
     557             : 
     558             : nsXPCWrappedJS*
     559        2535 : nsXPCWrappedJS::Find(REFNSIID aIID)
     560             : {
     561        2535 :     if (aIID.Equals(NS_GET_IID(nsISupports)))
     562         374 :         return mRoot;
     563             : 
     564        3217 :     for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
     565        2819 :         if (aIID.Equals(cur->GetIID()))
     566        1763 :             return cur;
     567             :     }
     568             : 
     569         398 :     return nullptr;
     570             : }
     571             : 
     572             : // check if asking for an interface that some wrapper in the chain inherits from
     573             : nsXPCWrappedJS*
     574         398 : nsXPCWrappedJS::FindInherited(REFNSIID aIID)
     575             : {
     576         398 :     MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence");
     577             : 
     578         902 :     for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
     579             :         bool found;
     580        1008 :         if (NS_SUCCEEDED(cur->GetClass()->GetInterfaceInfo()->
     581         504 :                          HasAncestor(&aIID, &found)) && found)
     582           0 :             return cur;
     583             :     }
     584             : 
     585         398 :     return nullptr;
     586             : }
     587             : 
     588             : NS_IMETHODIMP
     589           0 : nsXPCWrappedJS::GetInterfaceInfo(nsIInterfaceInfo** infoResult)
     590             : {
     591           0 :     MOZ_ASSERT(GetClass(), "wrapper without class");
     592           0 :     MOZ_ASSERT(GetClass()->GetInterfaceInfo(), "wrapper class without interface");
     593             : 
     594             :     // Since failing to get this info will crash some platforms(!), we keep
     595             :     // mClass valid at shutdown time.
     596             : 
     597           0 :     nsCOMPtr<nsIInterfaceInfo> info = GetClass()->GetInterfaceInfo();
     598           0 :     if (!info)
     599           0 :         return NS_ERROR_UNEXPECTED;
     600           0 :     info.forget(infoResult);
     601           0 :     return NS_OK;
     602             : }
     603             : 
     604             : NS_IMETHODIMP
     605         679 : nsXPCWrappedJS::CallMethod(uint16_t methodIndex,
     606             :                            const XPTMethodDescriptor* info,
     607             :                            nsXPTCMiniVariant* params)
     608             : {
     609             :     // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
     610         679 :     MOZ_RELEASE_ASSERT(NS_IsMainThread(),
     611             :                        "nsXPCWrappedJS::CallMethod called off main thread");
     612             : 
     613         679 :     if (!IsValid())
     614           0 :         return NS_ERROR_UNEXPECTED;
     615         679 :     return GetClass()->CallMethod(this, methodIndex, info, params);
     616             : }
     617             : 
     618             : NS_IMETHODIMP
     619           0 : nsXPCWrappedJS::GetInterfaceIID(nsIID** iid)
     620             : {
     621           0 :     NS_PRECONDITION(iid, "bad param");
     622             : 
     623           0 :     *iid = (nsIID*) nsMemory::Clone(&(GetIID()), sizeof(nsIID));
     624           0 :     return *iid ? NS_OK : NS_ERROR_UNEXPECTED;
     625             : }
     626             : 
     627             : void
     628           0 : nsXPCWrappedJS::SystemIsBeingShutDown()
     629             : {
     630             :     // XXX It turns out that it is better to leak here then to do any Releases
     631             :     // and have them propagate into all sorts of mischief as the system is being
     632             :     // shutdown. This was learned the hard way :(
     633             : 
     634             :     // mJSObj == nullptr is used to indicate that the wrapper is no longer valid
     635             :     // and that calls should fail without trying to use any of the
     636             :     // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer.
     637             : 
     638             :     // NOTE: that mClass is retained so that GetInterfaceInfo can continue to
     639             :     // work (and avoid crashing some platforms).
     640             : 
     641             :     // Use of unsafeGet() is to avoid triggering post barriers in shutdown, as
     642             :     // this will access the chunk containing mJSObj, which may have been freed
     643             :     // at this point.
     644           0 :     *mJSObj.unsafeGet() = nullptr;
     645             : 
     646             :     // Notify other wrappers in the chain.
     647           0 :     if (mNext)
     648           0 :         mNext->SystemIsBeingShutDown();
     649           0 : }
     650             : 
     651             : size_t
     652           0 : nsXPCWrappedJS::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
     653             : {
     654             :     // mJSObject is a JS pointer, so don't measure the object.
     655             :     // mClass is not uniquely owned by this WrappedJS. Measure it in IID2WrappedJSClassMap.
     656             :     // mRoot is not measured because it is either |this| or we have already measured it.
     657             :     // mOuter is rare and probably not uniquely owned by this.
     658           0 :     size_t n = mallocSizeOf(this);
     659           0 :     n += nsAutoXPTCStub::SizeOfExcludingThis(mallocSizeOf);
     660             : 
     661             :     // Wrappers form a linked list via the mNext field, so include them all
     662             :     // in the measurement. Only root wrappers are stored in the map, so
     663             :     // everything will be measured exactly once.
     664           0 :     if (mNext)
     665           0 :         n += mNext->SizeOfIncludingThis(mallocSizeOf);
     666             : 
     667           0 :     return n;
     668             : }
     669             : 
     670             : /***************************************************************************/
     671             : 
     672             : NS_IMETHODIMP
     673           0 : nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate)
     674             : {
     675           0 :     AutoJSContext cx;
     676           0 :     XPCCallContext ccx(cx);
     677           0 :     if (!ccx.IsValid())
     678           0 :         return NS_ERROR_UNEXPECTED;
     679             : 
     680           0 :     return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, GetJSObject(),
     681           0 :                                                         aEnumerate);
     682             : }
     683             : 
     684             : NS_IMETHODIMP
     685           0 : nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant** _retval)
     686             : {
     687           0 :     AutoJSContext cx;
     688           0 :     XPCCallContext ccx(cx);
     689           0 :     if (!ccx.IsValid())
     690           0 :         return NS_ERROR_UNEXPECTED;
     691             : 
     692             :     return nsXPCWrappedJSClass::
     693           0 :         GetNamedPropertyAsVariant(ccx, GetJSObject(), name, _retval);
     694             : }
     695             : 
     696             : /***************************************************************************/
     697             : 
     698             : NS_IMETHODIMP
     699           0 : nsXPCWrappedJS::DebugDump(int16_t depth)
     700             : {
     701             : #ifdef DEBUG
     702           0 :     XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %p with mRefCnt = %" PRIuPTR, this, mRefCnt.get()));
     703           0 :         XPC_LOG_INDENT();
     704             : 
     705           0 :         XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %p",              \
     706             :                         IsRootWrapper() ? "ROOT":"non-root", mJSObj.get()));
     707             :         char* name;
     708           0 :         GetClass()->GetInterfaceInfo()->GetName(&name);
     709           0 :         XPC_LOG_ALWAYS(("interface name is %s", name));
     710           0 :         if (name)
     711           0 :             free(name);
     712           0 :         char * iid = GetClass()->GetIID().ToString();
     713           0 :         XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
     714           0 :         if (iid)
     715           0 :             free(iid);
     716           0 :         XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %p", mClass.get()));
     717             : 
     718           0 :         if (!IsRootWrapper())
     719           0 :             XPC_LOG_OUTDENT();
     720           0 :         if (mNext) {
     721           0 :             if (IsRootWrapper()) {
     722           0 :                 XPC_LOG_ALWAYS(("Additional wrappers for this object..."));
     723           0 :                 XPC_LOG_INDENT();
     724             :             }
     725           0 :             mNext->DebugDump(depth);
     726           0 :             if (IsRootWrapper())
     727           0 :                 XPC_LOG_OUTDENT();
     728             :         }
     729           0 :         if (IsRootWrapper())
     730           0 :             XPC_LOG_OUTDENT();
     731             : #endif
     732           0 :     return NS_OK;
     733             : }

Generated by: LCOV version 1.13