tor-browser

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

nsWrapperCache.h (30475B)


      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 #ifndef nsWrapperCache_h___
      8 #define nsWrapperCache_h___
      9 
     10 #include <type_traits>
     11 
     12 #include "js/HeapAPI.h"
     13 #include "js/RootingAPI.h"
     14 #include "js/TracingAPI.h"
     15 #include "js/TypeDecls.h"
     16 #include "mozilla/Assertions.h"
     17 #include "mozilla/RustCell.h"
     18 #include "mozilla/ServoUtils.h"
     19 #include "nsCycleCollectionParticipant.h"
     20 #include "nsISupports.h"
     21 #include "nsISupportsUtils.h"
     22 
     23 namespace mozilla::dom::binding_detail {
     24 class CastableToWrapperCacheHelper;
     25 }  // namespace mozilla::dom::binding_detail
     26 
     27 #define NS_WRAPPERCACHE_IID \
     28  {0x6f3179a1, 0x36f7, 0x4a5c, {0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87}}
     29 
     30 // There are two sets of flags used by DOM nodes. One comes from reusing the
     31 // remaining bits of the inherited nsWrapperCache flags (mFlags), and another is
     32 // exclusive to nsINode (mBoolFlags).
     33 //
     34 // Both sets of flags are 32 bits. On 64-bit platforms, this can cause two
     35 // wasted 32-bit fields due to alignment requirements. Some compilers are
     36 // smart enough to coalesce the fields if we make mBoolFlags the first member
     37 // of nsINode, but others (such as MSVC, but that's not officially supported
     38 // by us anymore) are not. Also note that this kind of coalascing tends to
     39 // interact poorly with rust's bindgen, see:
     40 // https://github.com/rust-lang/rust-bindgen/issues/380
     41 //
     42 // So we just store mBoolFlags directly on nsWrapperCache on 64-bit platforms.
     43 // This may waste space for some other nsWrapperCache-derived objects that have
     44 // a 32-bit field as their first member, but those objects are unlikely to be as
     45 // numerous or performance-critical as DOM nodes.
     46 #ifdef HAVE_64BIT_BUILD
     47 static_assert(sizeof(void*) == 8, "These architectures should be 64-bit");
     48 #  define BOOL_FLAGS_ON_WRAPPER_CACHE
     49 #else
     50 static_assert(sizeof(void*) == 4, "Only support 32-bit and 64-bit");
     51 #endif
     52 
     53 /**
     54 * Class to store the wrapper for an object. This can only be used with objects
     55 * that only have one non-security wrapper at a time (for an XPCWrappedNative
     56 * this is usually ensured by setting an explicit parent in the PreCreate hook
     57 * for the class).
     58 *
     59 * An instance of nsWrapperCache can be gotten from an object that implements
     60 * a wrapper cache by calling QueryInterface on it. Note that this breaks XPCOM
     61 * rules a bit (this object doesn't derive from nsISupports).
     62 *
     63 * The cache can store objects other than wrappers. We allow wrappers to use a
     64 * separate JSObject to store their state (mostly expandos). If the wrapper is
     65 * collected and we want to preserve this state we actually store the state
     66 * object in the cache.
     67 *
     68 * The cache can store 3 types of objects: a DOM binding object (regular JS
     69 * object or proxy), an nsOuterWindowProxy or an XPCWrappedNative wrapper.
     70 *
     71 * The finalizer for the wrapper clears the cache.
     72 *
     73 * A compacting GC can move the wrapper object. Pointers to moved objects are
     74 * usually found and updated by tracing the heap, however non-preserved wrappers
     75 * are weak references and are not traced, so another approach is
     76 * necessary. Instead a class hook (objectMovedOp) is provided that is called
     77 * when an object is moved and is responsible for ensuring pointers are
     78 * updated. It does this by calling UpdateWrapper() on the wrapper
     79 * cache. SetWrapper() asserts that the hook is implemented for any wrapper set.
     80 *
     81 * A number of the methods are implemented in nsWrapperCacheInlines.h because we
     82 * have to include some JS headers that don't play nicely with the rest of the
     83 * codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
     84 */
     85 
     86 class JS_HAZ_ROOTED nsWrapperCache {
     87 public:
     88  NS_INLINE_DECL_STATIC_IID(NS_WRAPPERCACHE_IID)
     89 
     90  nsWrapperCache() = default;
     91  ~nsWrapperCache() {
     92    // Preserved wrappers should never end up getting cleared, but this can
     93    // happen during shutdown when a leaked wrapper object is finalized, causing
     94    // its wrapper to be cleared.
     95    MOZ_ASSERT(!PreservingWrapper() || js::RuntimeIsBeingDestroyed(),
     96               "Destroying cache with a preserved wrapper!");
     97  }
     98 
     99  /**
    100   * Get the cached wrapper.
    101   *
    102   * This getter clears the gray bit before handing out the JSObject which means
    103   * that the object is guaranteed to be kept alive past the next CC.
    104   */
    105  JSObject* GetWrapper() const;
    106 
    107  /**
    108   * Get the cached wrapper.
    109   *
    110   * This getter does not change the color of the JSObject meaning that the
    111   * object returned is not guaranteed to be kept alive past the next CC.
    112   *
    113   * This should only be called if you are certain that the return value won't
    114   * be passed into a JSAPI function and that it won't be stored without being
    115   * rooted (or otherwise signaling the stored value to the CC).
    116   */
    117  JSObject* GetWrapperPreserveColor() const;
    118 
    119  /**
    120   * Get the cached wrapper.
    121   *
    122   * This getter does not check whether the wrapper is dead and in the process
    123   * of being finalized.
    124   *
    125   * This should only be called if you really need to see the raw contents of
    126   * this cache, for example as part of finalization. Don't store the result
    127   * anywhere or pass it into JSAPI functions that may cause the value to
    128   * escape.
    129   */
    130  JSObject* GetWrapperMaybeDead() const { return mWrapper; }
    131 
    132 #ifdef DEBUG
    133 private:
    134  static bool HasJSObjectMovedOp(JSObject* aWrapper);
    135 
    136  static void AssertUpdatedWrapperZone(const JSObject* aNewObject,
    137                                       const JSObject* aOldObject);
    138 
    139 public:
    140 #endif
    141 
    142  void SetWrapper(JSObject* aWrapper) {
    143    MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
    144    MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
    145    MOZ_ASSERT(HasJSObjectMovedOp(aWrapper),
    146               "Object has not provided the hook to update the wrapper if it "
    147               "is moved");
    148 
    149    SetWrapperJSObject(aWrapper);
    150  }
    151 
    152  /**
    153   * Clear the cache.
    154   */
    155  void ClearWrapper() {
    156    // Preserved wrappers should never end up getting cleared, but this can
    157    // happen during shutdown when a leaked wrapper object is finalized, causing
    158    // its wrapper to be cleared.
    159    MOZ_ASSERT(!PreservingWrapper() || js::RuntimeIsBeingDestroyed(),
    160               "Clearing a preserved wrapper!");
    161    SetWrapperJSObject(nullptr);
    162  }
    163 
    164  /**
    165   * Clear the cache if it still contains a specific wrapper object. This should
    166   * be called from the finalizer for the wrapper.
    167   */
    168  void ClearWrapper(JSObject* obj) {
    169    if (obj == mWrapper) {
    170      ClearWrapper();
    171    }
    172  }
    173 
    174  /**
    175   * Update the wrapper when the object moves between globals.
    176   */
    177  template <typename T>
    178  void UpdateWrapperForNewGlobal(T* aScriptObjectHolder, JSObject* aNewWrapper);
    179 
    180  /**
    181   * Update the wrapper if the object it contains is moved.
    182   *
    183   * This method must be called from the objectMovedOp class extension hook for
    184   * any wrapper cached object.
    185   */
    186  void UpdateWrapper(JSObject* aNewObject, const JSObject* aOldObject) {
    187 #ifdef DEBUG
    188    AssertUpdatedWrapperZone(aNewObject, aOldObject);
    189 #endif
    190    if (mWrapper) {
    191      MOZ_ASSERT(mWrapper == aOldObject);
    192      mWrapper = aNewObject;
    193      if (PreservingWrapper() && !JS::ObjectIsTenured(mWrapper)) {
    194        // Can pass prevp as null here since a previous store buffer entry has
    195        // been cleared by the current nursery collection.
    196        JS::HeapObjectPostWriteBarrier(&mWrapper, nullptr, mWrapper);
    197      }
    198    }
    199  }
    200 
    201  bool PreservingWrapper() const {
    202    return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
    203  }
    204 
    205  /**
    206   * Wrap the object corresponding to this wrapper cache. If non-null is
    207   * returned, the object has already been stored in the wrapper cache.
    208   */
    209  virtual JSObject* WrapObject(JSContext* cx,
    210                               JS::Handle<JSObject*> aGivenProto) = 0;
    211 
    212  /**
    213   * Returns true if the object has a wrapper that is known live from the point
    214   * of view of cycle collection.
    215   */
    216  bool HasKnownLiveWrapper() const;
    217 
    218  /**
    219   * Returns true if the object has a known-live wrapper (from the CC point of
    220   * view) and all the GC things it is keeping alive are already known-live from
    221   * CC's point of view.
    222   */
    223  bool HasKnownLiveWrapperAndDoesNotNeedTracing(nsISupports* aThis);
    224 
    225  bool HasNothingToTrace(nsISupports* aThis);
    226 
    227  /**
    228   * Mark our wrapper, if any, as live as far as the CC is concerned.
    229   */
    230  void MarkWrapperLive();
    231 
    232  // Only meant to be called by code that preserves a wrapper.
    233  void SetPreservingWrapper(bool aPreserve) {
    234    if (aPreserve) {
    235      SetWrapperFlags(WRAPPER_BIT_PRESERVED);
    236    } else {
    237      UnsetWrapperFlags(WRAPPER_BIT_PRESERVED);
    238    }
    239  }
    240 
    241  void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure) {
    242    if (PreservingWrapper() && mWrapper) {
    243      aCallbacks.Trace(this, "Preserved wrapper", aClosure);
    244    }
    245  }
    246 
    247  /*
    248   * The following methods for getting and manipulating flags allow the unused
    249   * bits of mFlags to be used by derived classes.
    250   */
    251 
    252  using FlagsType = uint32_t;
    253 
    254  FlagsType GetFlags() const {
    255    MOZ_ASSERT(NS_IsMainThread());
    256    MOZ_ASSERT(!mozilla::IsInServoTraversal());
    257    return mFlags.Get() & ~kWrapperFlagsMask;
    258  }
    259 
    260  // This can be called from stylo threads too, so it needs to be atomic, as
    261  // this value may be mutated from multiple threads during servo traversal from
    262  // rust.
    263  bool HasFlag(FlagsType aFlag) const {
    264    MOZ_ASSERT((aFlag & kWrapperFlagsMask) == 0, "Bad flag mask");
    265    return __atomic_load_n(mFlags.AsPtr(), __ATOMIC_RELAXED) & aFlag;
    266  }
    267 
    268  // Identical to HasFlag, but more explicit about its handling of multiple
    269  // flags. This can be called from stylo threads too.
    270  bool HasAnyOfFlags(FlagsType aFlags) const { return HasFlag(aFlags); }
    271 
    272  // This can also be called from stylo, in the sequential part of the
    273  // traversal, though it's probably not worth differentiating them for the
    274  // purposes of assertions.
    275  bool HasAllFlags(FlagsType aFlags) const {
    276    MOZ_ASSERT((aFlags & kWrapperFlagsMask) == 0, "Bad flag mask");
    277    return (__atomic_load_n(mFlags.AsPtr(), __ATOMIC_RELAXED) & aFlags) ==
    278           aFlags;
    279  }
    280 
    281  void SetFlags(FlagsType aFlagsToSet) {
    282    MOZ_ASSERT(NS_IsMainThread());
    283    MOZ_ASSERT((aFlagsToSet & kWrapperFlagsMask) == 0, "Bad flag mask");
    284    mFlags.Set(mFlags.Get() | aFlagsToSet);
    285  }
    286 
    287  void UnsetFlags(FlagsType aFlagsToUnset) {
    288    MOZ_ASSERT(NS_IsMainThread());
    289    MOZ_ASSERT((aFlagsToUnset & kWrapperFlagsMask) == 0, "Bad flag mask");
    290    mFlags.Set(mFlags.Get() & ~aFlagsToUnset);
    291  }
    292 
    293  void PreserveWrapper(nsISupports* aScriptObjectHolder) {
    294    if (PreservingWrapper()) {
    295      return;
    296    }
    297 
    298    nsISupports* ccISupports;
    299    aScriptObjectHolder->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
    300                                        reinterpret_cast<void**>(&ccISupports));
    301    MOZ_ASSERT(ccISupports);
    302 
    303    nsXPCOMCycleCollectionParticipant* participant;
    304    CallQueryInterface(ccISupports, &participant);
    305    PreserveWrapper(ccISupports, participant);
    306  }
    307 
    308  void PreserveWrapper(void* aScriptObjectHolder,
    309                       nsScriptObjectTracer* aTracer) {
    310    if (PreservingWrapper()) {
    311      return;
    312    }
    313 
    314    JSObject* wrapper = GetWrapper();  // Read barrier for incremental GC.
    315    HoldJSObjects(aScriptObjectHolder, aTracer, JS::GetObjectZone(wrapper));
    316    SetPreservingWrapper(true);
    317 #ifdef DEBUG
    318    // Make sure the cycle collector will be able to traverse to the wrapper.
    319    CheckCCWrapperTraversal(aScriptObjectHolder, aTracer);
    320 #endif
    321  }
    322 
    323  void ReleaseWrapper(void* aScriptObjectHolder);
    324 
    325  // Special case version of ReleaseWrapper. For use by
    326  // Rule::UnlinkDeclarationWrapper only.
    327  void ReleaseWrapperWithoutDrop();
    328 
    329  void TraceWrapper(JSTracer* aTrc, const char* name) {
    330    if (mWrapper) {
    331      js::UnsafeTraceManuallyBarrieredEdge(aTrc, &mWrapper, name);
    332    }
    333  }
    334 
    335 protected:
    336  void PoisonWrapper() {
    337    if (mWrapper) {
    338      // Set the pointer to a value that will cause a crash if it is
    339      // dereferenced.
    340      mWrapper = reinterpret_cast<JSObject*>(1);
    341    }
    342  }
    343 
    344 private:
    345  void SetWrapperJSObject(JSObject* aWrapper);
    346 
    347  void ReleaseWrapperAndMaybeDropHolder(void* aScriptObjectHolderToDrop);
    348 
    349  // We'd like to assert that these aren't used from servo threads, but we don't
    350  // have a great way to do that because:
    351  //  * We can't just assert that they get used on the main thread, because
    352  //    these are used from workers.
    353  //  * We can't just assert that they aren't used when IsInServoTraversal(),
    354  //    because the traversal has a sequential, main-thread-only phase, where we
    355  //    run animations that can fiddle with JS promises.
    356  FlagsType GetWrapperFlags() const { return mFlags.Get() & kWrapperFlagsMask; }
    357 
    358  bool HasWrapperFlag(FlagsType aFlag) const {
    359    MOZ_ASSERT((aFlag & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
    360    return !!(mFlags.Get() & aFlag);
    361  }
    362 
    363  void SetWrapperFlags(FlagsType aFlagsToSet) {
    364    MOZ_ASSERT((aFlagsToSet & ~kWrapperFlagsMask) == 0,
    365               "Bad wrapper flag bits");
    366    mFlags.Set(mFlags.Get() | aFlagsToSet);
    367  }
    368 
    369  void UnsetWrapperFlags(FlagsType aFlagsToUnset) {
    370    MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0,
    371               "Bad wrapper flag bits");
    372    mFlags.Set(mFlags.Get() & ~aFlagsToUnset);
    373  }
    374 
    375  void HoldJSObjects(void* aScriptObjectHolder, nsScriptObjectTracer* aTracer,
    376                     JS::Zone* aZone);
    377 
    378 #ifdef DEBUG
    379 public:
    380  void CheckCCWrapperTraversal(void* aScriptObjectHolder,
    381                               nsScriptObjectTracer* aTracer);
    382 #endif  // DEBUG
    383 
    384 private:
    385  friend class mozilla::dom::binding_detail::CastableToWrapperCacheHelper;
    386 
    387  /**
    388   * If this bit is set then we're preserving the wrapper, which in effect ties
    389   * the lifetime of the JS object stored in the cache to the lifetime of the
    390   * native object. We rely on the cycle collector to break the cycle that this
    391   * causes between the native object and the JS object, so it is important that
    392   * any native object that supports preserving of its wrapper
    393   * traces/traverses/unlinks the cached JS object (see
    394   * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER and
    395   * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER).
    396   */
    397  enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
    398 
    399  enum { kWrapperFlagsMask = WRAPPER_BIT_PRESERVED };
    400 
    401  JSObject* mWrapper = nullptr;
    402 
    403  // Rust code needs to read and write some flags atomically, but we don't want
    404  // to make the wrapper flags atomic whole-sale because main-thread code would
    405  // become more expensive (loads wouldn't change, but flag setting /
    406  // unsetting could become slower enough to be noticeable). Making this an
    407  // Atomic whole-sale needs more measuring.
    408  //
    409  // In order to not mess with aliasing rules the type should not be frozen, so
    410  // we use a RustCell, which contains an UnsafeCell internally. See also the
    411  // handling of ServoData (though that's a bit different).
    412  mozilla::RustCell<FlagsType> mFlags{0};
    413 
    414 protected:
    415 #ifdef BOOL_FLAGS_ON_WRAPPER_CACHE
    416  uint32_t mBoolFlags = 0;
    417 #endif
    418 };
    419 
    420 enum { WRAPPER_CACHE_FLAGS_BITS_USED = 1 };
    421 
    422 #define NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY           \
    423  if (aIID.Equals(NS_GET_IID(nsWrapperCache))) {        \
    424    *aInstancePtr = static_cast<nsWrapperCache*>(this); \
    425    return NS_OK;                                       \
    426  }
    427 
    428 #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY \
    429  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY     \
    430  else
    431 
    432 // Cycle collector macros for wrapper caches.
    433 //
    434 // The NS_DECL_*WRAPPERCACHE_* macros make it easier to mark classes as holding
    435 // just a single pointer to a JS value. That information is then used for
    436 // certain GC optimizations.
    437 
    438 #define NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_AMBIGUOUS(_class, _base)   \
    439  class NS_CYCLE_COLLECTION_INNERCLASS                                         \
    440      : public nsXPCOMCycleCollectionParticipant {                             \
    441   public:                                                                     \
    442    constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags = 0)        \
    443        : nsXPCOMCycleCollectionParticipant(aFlags |                           \
    444                                            FlagMaybeSingleZoneJSHolder) {}    \
    445                                                                               \
    446   private:                                                                    \
    447    NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base)                         \
    448    NS_IMETHOD_(void)                                                          \
    449    Trace(void* p, const TraceCallbacks& cb, void* closure) override;          \
    450    NS_IMETHOD_(void)                                                          \
    451    TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \
    452    NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class)                     \
    453  };                                                                           \
    454  NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class)                                  \
    455  static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;         \
    456  NOT_INHERITED_CANT_OVERRIDE
    457 
    458 #define NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class) \
    459  NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_AMBIGUOUS(_class, _class)
    460 
    461 #define NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_INHERITED(_class,          \
    462                                                              _base_class)     \
    463  class NS_CYCLE_COLLECTION_INNERCLASS                                         \
    464      : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) {                    \
    465   public:                                                                     \
    466    constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags)            \
    467        : NS_CYCLE_COLLECTION_CLASSNAME(_base_class)(                          \
    468              aFlags | FlagMaybeSingleZoneJSHolder) {}                         \
    469                                                                               \
    470   private:                                                                    \
    471    NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class)         \
    472    NS_IMETHOD_(void)                                                          \
    473    Trace(void* p, const TraceCallbacks& cb, void* closure) override;          \
    474    NS_IMETHOD_(void)                                                          \
    475    TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \
    476    NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class)                     \
    477  };                                                                           \
    478  NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class)                        \
    479  static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
    480 
    481 #define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS_AMBIGUOUS(       \
    482    _class, _base)                                                             \
    483  class NS_CYCLE_COLLECTION_INNERCLASS                                         \
    484      : public nsXPCOMCycleCollectionParticipant {                             \
    485   public:                                                                     \
    486    constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags)            \
    487        : nsXPCOMCycleCollectionParticipant(aFlags | FlagMightSkip |           \
    488                                            FlagMaybeSingleZoneJSHolder) {}    \
    489                                                                               \
    490   private:                                                                    \
    491    NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base)                         \
    492    NS_IMETHOD_(void)                                                          \
    493    Trace(void* p, const TraceCallbacks& cb, void* closure) override;          \
    494    NS_IMETHOD_(void)                                                          \
    495    TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \
    496    NS_IMETHOD_(bool) CanSkipReal(void* p, bool aRemovingAllowed) override;    \
    497    NS_IMETHOD_(bool) CanSkipInCCReal(void* p) override;                       \
    498    NS_IMETHOD_(bool) CanSkipThisReal(void* p) override;                       \
    499    NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class)                     \
    500  };                                                                           \
    501  NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class)                                  \
    502  static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;         \
    503  NOT_INHERITED_CANT_OVERRIDE
    504 
    505 #define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS(_class)     \
    506  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS_AMBIGUOUS(_class, \
    507                                                                  _class)
    508 
    509 #define NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS_INHERITED(       \
    510    _class, _base_class)                                                       \
    511  class NS_CYCLE_COLLECTION_INNERCLASS                                         \
    512      : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) {                    \
    513   public:                                                                     \
    514    constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags = 0)        \
    515        : NS_CYCLE_COLLECTION_CLASSNAME(_base_class)(                          \
    516              aFlags | FlagMightSkip | FlagMaybeSingleZoneJSHolder) {}         \
    517                                                                               \
    518   private:                                                                    \
    519    NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base_class)                   \
    520    NS_IMETHOD_(void)                                                          \
    521    Trace(void* p, const TraceCallbacks& cb, void* closure) override;          \
    522    NS_IMETHOD_(void)                                                          \
    523    TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \
    524    NS_IMETHOD_(bool) CanSkipReal(void* p, bool aRemovingAllowed) override;    \
    525    NS_IMETHOD_(bool) CanSkipInCCReal(void* p) override;                       \
    526    NS_IMETHOD_(bool) CanSkipThisReal(void* p) override;                       \
    527    NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class)                     \
    528  };                                                                           \
    529  NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL_INHERITED(_class)                        \
    530  static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
    531 
    532 #define NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(_class)             \
    533  void DeleteCycleCollectable(void) { delete this; }                           \
    534  class NS_CYCLE_COLLECTION_INNERCLASS : public nsScriptObjectTracer {         \
    535   public:                                                                     \
    536    constexpr explicit NS_CYCLE_COLLECTION_INNERCLASS(Flags aFlags = 0)        \
    537        : nsScriptObjectTracer(aFlags | FlagMaybeSingleZoneJSHolder) {}        \
    538                                                                               \
    539   private:                                                                    \
    540    NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class)                         \
    541    NS_IMETHOD_(void)                                                          \
    542    Trace(void* p, const TraceCallbacks& cb, void* closure) override;          \
    543    NS_IMETHOD_(void)                                                          \
    544    TraceWrapper(void* aPtr, const TraceCallbacks& aCb, void* aClosure) final; \
    545    static constexpr nsScriptObjectTracer* GetParticipant() {                  \
    546      return &_class::NS_CYCLE_COLLECTION_INNERNAME;                           \
    547    }                                                                          \
    548  };                                                                           \
    549  static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
    550 
    551 #define NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \
    552  tmp->TraceWrapper(aCallbacks, aClosure);
    553 
    554 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
    555  tmp->ReleaseWrapper(p);
    556 
    557 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class)        \
    558  static_assert(std::is_base_of<nsWrapperCache, _class>::value,    \
    559                "Class should inherit nsWrapperCache");            \
    560  NS_IMPL_CYCLE_COLLECTION_SINGLE_ZONE_SCRIPT_HOLDER_CLASS(_class) \
    561  NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class)                     \
    562    TraceWrapper(p, aCallbacks, aClosure);                         \
    563  NS_IMPL_CYCLE_COLLECTION_TRACE_END                               \
    564  void NS_CYCLE_COLLECTION_CLASSNAME(_class)::TraceWrapper(        \
    565      void* p, const TraceCallbacks& aCallbacks, void* aClosure) { \
    566    _class* tmp = DowncastCCParticipant<_class>(p);                \
    567    NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER               \
    568  }
    569 
    570 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(_class) \
    571  NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class)   \
    572  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)         \
    573    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER   \
    574  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                   \
    575  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)       \
    576  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    577 
    578 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(_class, ...) \
    579  NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class)      \
    580  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)            \
    581    NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__)           \
    582    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER      \
    583  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                      \
    584  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)          \
    585    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)         \
    586  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    587 
    588 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK(_class, ...) \
    589  NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class)           \
    590  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)                 \
    591    NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__)                \
    592    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER           \
    593    NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE              \
    594  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                           \
    595  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)               \
    596    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)              \
    597  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    598 
    599 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(_class, ...) \
    600  NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class)               \
    601  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)                     \
    602    NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__)                    \
    603    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER               \
    604    NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR                        \
    605  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                               \
    606  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)                   \
    607    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)                  \
    608  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    609 
    610 // This is used for wrapper cached classes that inherit from cycle
    611 // collected non-wrapper cached classes.
    612 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(_class, _base, ...)   \
    613  static_assert(!std::is_base_of<nsWrapperCache, _base>::value,               \
    614                "Base class should not inherit nsWrapperCache");              \
    615  NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(_class)                         \
    616  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base)              \
    617    NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__)                              \
    618    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER                         \
    619  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                         \
    620  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base)            \
    621    /* Assert somewhere, in this case in the traverse method, that the */     \
    622    /* parent isn't a single zone holder*/                                    \
    623    MOZ_ASSERT(!_base::NS_CYCLE_COLLECTION_INNERNAME.IsSingleZoneJSHolder()); \
    624    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)                            \
    625  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    626 
    627 // if NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS is used to implement
    628 // a wrappercache class, one needs to use
    629 // NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS and its variants in the class
    630 // declaration.
    631 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(              \
    632    class_, native_members_, js_members_)                                   \
    633  static_assert(std::is_base_of<nsWrapperCache, class_>::value,             \
    634                "Class should inherit nsWrapperCache");                     \
    635  NS_IMPL_CYCLE_COLLECTION_CLASS(class_)                                    \
    636  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(class_)                             \
    637    using ::ImplCycleCollectionUnlink;                                      \
    638    NS_IMPL_CYCLE_COLLECTION_UNLINK(                                        \
    639        MOZ_FOR_EACH_EXPAND_HELPER native_members_)                         \
    640    NS_IMPL_CYCLE_COLLECTION_UNLINK(MOZ_FOR_EACH_EXPAND_HELPER js_members_) \
    641    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER                       \
    642  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                       \
    643  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(class_)                           \
    644    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(                                      \
    645        MOZ_FOR_EACH_EXPAND_HELPER native_members_)                         \
    646  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                                     \
    647  NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(class_)                              \
    648    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBERS(                              \
    649        MOZ_FOR_EACH_EXPAND_HELPER js_members_)                             \
    650    NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER                        \
    651  NS_IMPL_CYCLE_COLLECTION_TRACE_END
    652 
    653 #endif /* nsWrapperCache_h___ */