tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XPCWrappedJS.cpp (22804B)


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