LCOV - code coverage report
Current view: top level - dom/base - nsWrapperCache.h (source / functions) Hit Total Coverage
Test: output.info Lines: 88 94 93.6 %
Date: 2017-07-14 16:53:18 Functions: 23 25 92.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "nsCycleCollectionParticipant.h"
      11             : #include "mozilla/Assertions.h"
      12             : #include "js/Id.h"          // must come before js/RootingAPI.h
      13             : #include "js/Value.h"       // must come before js/RootingAPI.h
      14             : #include "js/RootingAPI.h"
      15             : #include "js/TracingAPI.h"
      16             : 
      17             : namespace mozilla {
      18             : namespace dom {
      19             : class TabChildGlobal;
      20             : class ProcessGlobal;
      21             : } // namespace dom
      22             : } // namespace mozilla
      23             : class SandboxPrivate;
      24             : class nsInProcessTabChildGlobal;
      25             : class nsWindowRoot;
      26             : 
      27             : #define NS_WRAPPERCACHE_IID \
      28             : { 0x6f3179a1, 0x36f7, 0x4a5c, \
      29             :   { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } }
      30             : 
      31             : // There are two sets of flags used by DOM nodes. One comes from reusing the
      32             : // remaining bits of the inherited nsWrapperCache flags (mFlags), and another is
      33             : // exclusive to nsINode (mBoolFlags).
      34             : //
      35             : // Both sets of flags are 32 bits. On 64-bit platforms, this can cause two
      36             : // wasted 32-bit fields due to alignment requirements. Some compilers are
      37             : // smart enough to coalesce the fields if we make mBoolFlags the first member
      38             : // of nsINode, but others (such as MSVC) are not.
      39             : //
      40             : // So we just store mBoolFlags directly on nsWrapperCache on 64-bit platforms.
      41             : // This may waste space for some other nsWrapperCache-derived objects that have
      42             : // a 32-bit field as their first member, but those objects are unlikely to be as
      43             : // numerous or performance-critical as DOM nodes.
      44             : #if defined(_M_X64) || defined(__LP64__)
      45             : static_assert(sizeof(void*) == 8, "These architectures should be 64-bit");
      46             : #define BOOL_FLAGS_ON_WRAPPER_CACHE
      47             : #else
      48             : static_assert(sizeof(void*) == 4, "Only support 32-bit and 64-bit");
      49             : #endif
      50             : 
      51             : /**
      52             :  * Class to store the wrapper for an object. This can only be used with objects
      53             :  * that only have one non-security wrapper at a time (for an XPCWrappedNative
      54             :  * this is usually ensured by setting an explicit parent in the PreCreate hook
      55             :  * for the class).
      56             :  *
      57             :  * An instance of nsWrapperCache can be gotten from an object that implements
      58             :  * a wrapper cache by calling QueryInterface on it. Note that this breaks XPCOM
      59             :  * rules a bit (this object doesn't derive from nsISupports).
      60             :  *
      61             :  * The cache can store objects other than wrappers. We allow wrappers to use a
      62             :  * separate JSObject to store their state (mostly expandos). If the wrapper is
      63             :  * collected and we want to preserve this state we actually store the state
      64             :  * object in the cache.
      65             :  *
      66             :  * The cache can store 2 types of objects:
      67             :  *
      68             :  *  If WRAPPER_IS_NOT_DOM_BINDING is set (IsDOMBinding() returns false):
      69             :  *    - the JSObject of an XPCWrappedNative wrapper
      70             :  *
      71             :  *  If WRAPPER_IS_NOT_DOM_BINDING is not set (IsDOMBinding() returns true):
      72             :  *    - a DOM binding object (regular JS object or proxy)
      73             :  *
      74             :  * The finalizer for the wrapper clears the cache.
      75             :  *
      76             :  * A compacting GC can move the wrapper object. Pointers to moved objects are
      77             :  * usually found and updated by tracing the heap, however non-preserved wrappers
      78             :  * are weak references and are not traced, so another approach is
      79             :  * necessary. Instead a class hook (objectMovedOp) is provided that is called
      80             :  * when an object is moved and is responsible for ensuring pointers are
      81             :  * updated. It does this by calling UpdateWrapper() on the wrapper
      82             :  * cache. SetWrapper() asserts that the hook is implemented for any wrapper set.
      83             :  *
      84             :  * A number of the methods are implemented in nsWrapperCacheInlines.h because we
      85             :  * have to include some JS headers that don't play nicely with the rest of the
      86             :  * codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
      87             :  */
      88             : 
      89           0 : class nsWrapperCache
      90             : {
      91             : public:
      92             :   NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID)
      93             : 
      94        9960 :   nsWrapperCache()
      95        9960 :     : mWrapper(nullptr)
      96             :     , mFlags(0)
      97             : #ifdef BOOL_FLAGS_ON_WRAPPER_CACHE
      98        9960 :     , mBoolFlags(0)
      99             : #endif
     100             :   {
     101        9960 :   }
     102         332 :   ~nsWrapperCache()
     103         664 :   {
     104         332 :     MOZ_ASSERT(!PreservingWrapper(),
     105             :                "Destroying cache with a preserved wrapper!");
     106         332 :   }
     107             : 
     108             :   /**
     109             :    * Get the cached wrapper.
     110             :    *
     111             :    * This getter clears the gray bit before handing out the JSObject which means
     112             :    * that the object is guaranteed to be kept alive past the next CC.
     113             :    */
     114             :   JSObject* GetWrapper() const;
     115             : 
     116             :   /**
     117             :    * Get the cached wrapper.
     118             :    *
     119             :    * This getter does not change the color of the JSObject meaning that the
     120             :    * object returned is not guaranteed to be kept alive past the next CC.
     121             :    *
     122             :    * This should only be called if you are certain that the return value won't
     123             :    * be passed into a JSAPI function and that it won't be stored without being
     124             :    * rooted (or otherwise signaling the stored value to the CC).
     125             :    */
     126             :   JSObject* GetWrapperPreserveColor() const;
     127             : 
     128             :   /**
     129             :    * Get the cached wrapper.
     130             :    *
     131             :    * This getter does not check whether the wrapper is dead and in the process
     132             :    * of being finalized.
     133             :    *
     134             :    * This should only be called if you really need to see the raw contents of
     135             :    * this cache, for example as part of finalization. Don't store the result
     136             :    * anywhere or pass it into JSAPI functions that may cause the value to
     137             :    * escape.
     138             :    */
     139         215 :   JSObject* GetWrapperMaybeDead() const
     140             :   {
     141         215 :     return mWrapper;
     142             :   }
     143             : 
     144             : #ifdef DEBUG
     145             : private:
     146             :   static bool HasJSObjectMovedOp(JSObject* aWrapper);
     147             : 
     148             : public:
     149             : #endif
     150             : 
     151        2598 :   void SetWrapper(JSObject* aWrapper)
     152             :   {
     153        2598 :     MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
     154        2598 :     MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
     155        2598 :     MOZ_ASSERT(HasJSObjectMovedOp(aWrapper),
     156             :                "Object has not provided the hook to update the wrapper if it is moved");
     157             : 
     158        2598 :     SetWrapperJSObject(aWrapper);
     159        2598 :   }
     160             : 
     161             :   /**
     162             :    * Clear the cache.
     163             :    */
     164          58 :   void ClearWrapper()
     165             :   {
     166          58 :     MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
     167          58 :     SetWrapperJSObject(nullptr);
     168          58 :   }
     169             : 
     170             :   /**
     171             :    * Clear the cache if it still contains a specific wrapper object. This should
     172             :    * be called from the finalizer for the wrapper.
     173             :    */
     174          58 :   void ClearWrapper(JSObject* obj)
     175             :   {
     176          58 :     if (obj == mWrapper) {
     177          58 :       ClearWrapper();
     178             :     }
     179          58 :   }
     180             : 
     181             :   /**
     182             :    * Update the wrapper if the object it contains is moved.
     183             :    *
     184             :    * This method must be called from the objectMovedOp class extension hook for
     185             :    * any wrapper cached object.
     186             :    */
     187           3 :   void UpdateWrapper(JSObject* aNewObject, const JSObject* aOldObject)
     188             :   {
     189           3 :     if (mWrapper) {
     190           3 :       MOZ_ASSERT(mWrapper == aOldObject);
     191           3 :       mWrapper = aNewObject;
     192             :     }
     193           3 :   }
     194             : 
     195       12172 :   bool PreservingWrapper() const
     196             :   {
     197       12172 :     return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
     198             :   }
     199             : 
     200        5195 :   bool IsDOMBinding() const
     201             :   {
     202        5195 :     return !HasWrapperFlag(WRAPPER_IS_NOT_DOM_BINDING);
     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, JS::Handle<JSObject*> aGivenProto) = 0;
     210             : 
     211             :   /**
     212             :    * Returns true if the object has a wrapper that is known live from the point
     213             :    * of view of cycle collection.
     214             :    */
     215             :   bool HasKnownLiveWrapper() const;
     216             : 
     217             :   /**
     218             :    * Returns true if the object has a known-live wrapper (from the CC point of
     219             :    * view) and all the GC things it is keeping alive are already known-live from
     220             :    * CC's point of view.
     221             :    */
     222             :   bool HasKnownLiveWrapperAndDoesNotNeedTracing(nsISupports* aThis);
     223             : 
     224             :   bool HasNothingToTrace(nsISupports* aThis);
     225             : 
     226             :   /**
     227             :    * Mark our wrapper, if any, as live as far as the CC is concerned.
     228             :    */
     229             :   void MarkWrapperLive();
     230             : 
     231             :   // Only meant to be called by code that preserves a wrapper.
     232        4885 :   void SetPreservingWrapper(bool aPreserve)
     233             :   {
     234        4885 :     if(aPreserve) {
     235        2616 :       SetWrapperFlags(WRAPPER_BIT_PRESERVED);
     236             :     }
     237             :     else {
     238        2269 :       UnsetWrapperFlags(WRAPPER_BIT_PRESERVED);
     239             :     }
     240        4885 :   }
     241             : 
     242        5794 :   void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure)
     243             :   {
     244        5794 :     if (PreservingWrapper() && mWrapper) {
     245        5396 :       aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
     246             :     }
     247        5794 :   }
     248             : 
     249             :   /*
     250             :    * The following methods for getting and manipulating flags allow the unused
     251             :    * bits of mFlags to be used by derived classes.
     252             :    */
     253             : 
     254             :   typedef uint32_t FlagsType;
     255             : 
     256         211 :   FlagsType GetFlags() const
     257             :   {
     258         211 :     return mFlags & ~kWrapperFlagsMask;
     259             :   }
     260             : 
     261      613044 :   bool HasFlag(FlagsType aFlag) const
     262             :   {
     263      613044 :     MOZ_ASSERT((aFlag & kWrapperFlagsMask) == 0, "Bad flag mask");
     264      613044 :     return !!(mFlags & aFlag);
     265             :   }
     266             : 
     267        3250 :   void SetFlags(FlagsType aFlagsToSet)
     268             :   {
     269        3250 :     MOZ_ASSERT((aFlagsToSet & kWrapperFlagsMask) == 0, "Bad flag mask");
     270        3250 :     mFlags |= aFlagsToSet;
     271        3250 :   }
     272             : 
     273       16972 :   void UnsetFlags(FlagsType aFlagsToUnset)
     274             :   {
     275       16972 :     MOZ_ASSERT((aFlagsToUnset & kWrapperFlagsMask) == 0, "Bad flag mask");
     276       16972 :     mFlags &= ~aFlagsToUnset;
     277       16972 :   }
     278             : 
     279         713 :   void PreserveWrapper(nsISupports* aScriptObjectHolder)
     280             :   {
     281         713 :     if (PreservingWrapper()) {
     282         454 :       return;
     283             :     }
     284             : 
     285             :     nsISupports* ccISupports;
     286             :     aScriptObjectHolder->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
     287         259 :                                         reinterpret_cast<void**>(&ccISupports));
     288         259 :     MOZ_ASSERT(ccISupports);
     289             : 
     290             :     nsXPCOMCycleCollectionParticipant* participant;
     291         259 :     CallQueryInterface(ccISupports, &participant);
     292         259 :     PreserveWrapper(ccISupports, participant);
     293             :   }
     294             : 
     295         259 :   void PreserveWrapper(void* aScriptObjectHolder, nsScriptObjectTracer* aTracer)
     296             :   {
     297         259 :     if (PreservingWrapper()) {
     298           0 :       return;
     299             :     }
     300             : 
     301         259 :     HoldJSObjects(aScriptObjectHolder, aTracer);
     302         259 :     SetPreservingWrapper(true);
     303             : #ifdef DEBUG
     304             :     // Make sure the cycle collector will be able to traverse to the wrapper.
     305         259 :     CheckCCWrapperTraversal(aScriptObjectHolder, aTracer);
     306             : #endif
     307             :   }
     308             : 
     309             :   void ReleaseWrapper(void* aScriptObjectHolder);
     310             : 
     311             : protected:
     312           3 :   void TraceWrapper(JSTracer* aTrc, const char* name)
     313             :   {
     314           3 :     if (mWrapper) {
     315           3 :       js::UnsafeTraceManuallyBarrieredEdge(aTrc, &mWrapper, name);
     316             :     }
     317           3 :   }
     318             : 
     319           0 :   void PoisonWrapper()
     320             :   {
     321           0 :     if (mWrapper) {
     322             :       // Set the pointer to a value that will cause a crash if it is
     323             :       // dereferenced.
     324           0 :       mWrapper = reinterpret_cast<JSObject*>(1);
     325             :     }
     326           0 :   }
     327             : 
     328             : private:
     329             :   // Friend declarations for things that need to be able to call
     330             :   // SetIsNotDOMBinding().  The goal is to get rid of all of these, and
     331             :   // SetIsNotDOMBinding() too.
     332             :   friend class mozilla::dom::TabChildGlobal;
     333             :   friend class mozilla::dom::ProcessGlobal;
     334             :   friend class SandboxPrivate;
     335             :   friend class nsInProcessTabChildGlobal;
     336             :   friend class nsWindowRoot;
     337          30 :   void SetIsNotDOMBinding()
     338             :   {
     339          30 :     MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_NOT_DOM_BINDING),
     340             :                "This flag should be set before creating any wrappers.");
     341          30 :     SetWrapperFlags(WRAPPER_IS_NOT_DOM_BINDING);
     342          30 :   }
     343             : 
     344             :   void SetWrapperJSObject(JSObject* aWrapper);
     345             : 
     346          30 :   FlagsType GetWrapperFlags() const
     347             :   {
     348          30 :     return mFlags & kWrapperFlagsMask;
     349             :   }
     350             : 
     351       17367 :   bool HasWrapperFlag(FlagsType aFlag) const
     352             :   {
     353       17367 :     MOZ_ASSERT((aFlag & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
     354       17367 :     return !!(mFlags & aFlag);
     355             :   }
     356             : 
     357        2646 :   void SetWrapperFlags(FlagsType aFlagsToSet)
     358             :   {
     359        2646 :     MOZ_ASSERT((aFlagsToSet & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
     360        2646 :     mFlags |= aFlagsToSet;
     361        2646 :   }
     362             : 
     363        4925 :   void UnsetWrapperFlags(FlagsType aFlagsToUnset)
     364             :   {
     365        4925 :     MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
     366        4925 :     mFlags &= ~aFlagsToUnset;
     367        4925 :   }
     368             : 
     369             :   void HoldJSObjects(void* aScriptObjectHolder,
     370             :                      nsScriptObjectTracer* aTracer);
     371             : 
     372             : #ifdef DEBUG
     373             : public:
     374             :   void CheckCCWrapperTraversal(void* aScriptObjectHolder,
     375             :                                nsScriptObjectTracer* aTracer);
     376             : private:
     377             : #endif // DEBUG
     378             : 
     379             :   /**
     380             :    * If this bit is set then we're preserving the wrapper, which in effect ties
     381             :    * the lifetime of the JS object stored in the cache to the lifetime of the
     382             :    * native object. We rely on the cycle collector to break the cycle that this
     383             :    * causes between the native object and the JS object, so it is important that
     384             :    * any native object that supports preserving of its wrapper
     385             :    * traces/traverses/unlinks the cached JS object (see
     386             :    * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER and
     387             :    * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER).
     388             :    */
     389             :   enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
     390             : 
     391             :   /**
     392             :    * If this bit is set then the wrapper for the native object is not a DOM
     393             :    * binding.
     394             :    */
     395             :   enum { WRAPPER_IS_NOT_DOM_BINDING = 1 << 1 };
     396             : 
     397             :   enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_NOT_DOM_BINDING) };
     398             : 
     399             :   JSObject* mWrapper;
     400             :   FlagsType mFlags;
     401             : protected:
     402             : #ifdef BOOL_FLAGS_ON_WRAPPER_CACHE
     403             :   uint32_t mBoolFlags;
     404             : #endif
     405             : };
     406             : 
     407             : enum { WRAPPER_CACHE_FLAGS_BITS_USED = 2 };
     408             : 
     409             : NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
     410             : 
     411             : #define NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY                                 \
     412             :   if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
     413             :     *aInstancePtr = static_cast<nsWrapperCache*>(this);                       \
     414             :     return NS_OK;                                                             \
     415             :   }
     416             : 
     417             : #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY                                   \
     418             :   NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY                                       \
     419             :   else
     420             : 
     421             : 
     422             : // Cycle collector macros for wrapper caches.
     423             : 
     424             : #define NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \
     425             :   tmp->TraceWrapper(aCallbacks, aClosure);
     426             : 
     427             : #define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
     428             :   tmp->ReleaseWrapper(p);
     429             : 
     430             : #define NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class) \
     431             :   NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class)              \
     432             :     NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER        \
     433             :   NS_IMPL_CYCLE_COLLECTION_TRACE_END
     434             : 
     435             : #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(_class) \
     436             :   NS_IMPL_CYCLE_COLLECTION_CLASS(_class)                \
     437             :   NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)         \
     438             :     NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER   \
     439             :   NS_IMPL_CYCLE_COLLECTION_UNLINK_END                   \
     440             :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)       \
     441             :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                 \
     442             :   NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class)
     443             : 
     444             : #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(_class, ...) \
     445             :   NS_IMPL_CYCLE_COLLECTION_CLASS(_class)                   \
     446             :   NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)            \
     447             :     NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__)           \
     448             :     NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER      \
     449             :   NS_IMPL_CYCLE_COLLECTION_UNLINK_END                      \
     450             :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)          \
     451             :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)         \
     452             :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                    \
     453             :   NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class)
     454             : 
     455             : #endif /* nsWrapperCache_h___ */

Generated by: LCOV version 1.13