dom/base/nsWrapperCache.h
author Carsten "Tomcat" Book <cbook@mozilla.com>
Fri, 09 May 2014 14:13:29 +0200
changeset 182330 420f4c65a67f
parent 181089 9a965d101659
child 189486 6e62d26d659c
permissions -rw-r--r--
merge b2g-inbound to mozilla-central
peterv@30001
     1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
gerv@94475
     2
/* This Source Code Form is subject to the terms of the Mozilla Public
gerv@94475
     3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
gerv@94475
     4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
peterv@21618
     5
peterv@21618
     6
#ifndef nsWrapperCache_h___
peterv@21618
     7
#define nsWrapperCache_h___
peterv@21618
     8
peterv@21618
     9
#include "nsCycleCollectionParticipant.h"
bzbarsky@120382
    10
#include "mozilla/Assertions.h"
nnethercote@144443
    11
#include "js/Id.h"          // must come before js/RootingAPI.h
nnethercote@143871
    12
#include "js/Value.h"       // must come before js/RootingAPI.h
nnethercote@143871
    13
#include "js/RootingAPI.h"
terrence@179304
    14
#include "js/TracingAPI.h"
peterv@21618
    15
gal@78424
    16
class XPCWrappedNativeScope;
peterv@30001
    17
peterv@21618
    18
#define NS_WRAPPERCACHE_IID \
bzbarsky@78421
    19
{ 0x6f3179a1, 0x36f7, 0x4a5c, \
bzbarsky@78421
    20
  { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } }
peterv@21618
    21
peterv@21618
    22
/**
peterv@78456
    23
 * Class to store the wrapper for an object. This can only be used with objects
peterv@78456
    24
 * that only have one non-security wrapper at a time (for an XPCWrappedNative
peterv@78456
    25
 * this is usually ensured by setting an explicit parent in the PreCreate hook
peterv@78456
    26
 * for the class).
peterv@78456
    27
 *
peterv@78456
    28
 * An instance of nsWrapperCache can be gotten from an object that implements
peterv@78456
    29
 * a wrapper cache by calling QueryInterface on it. Note that this breaks XPCOM
peterv@21618
    30
 * rules a bit (this object doesn't derive from nsISupports).
peterv@78456
    31
 *
peterv@78456
    32
 * The cache can store objects other than wrappers. We allow wrappers to use a
peterv@78456
    33
 * separate JSObject to store their state (mostly expandos). If the wrapper is
peterv@78456
    34
 * collected and we want to preserve this state we actually store the state
peterv@78456
    35
 * object in the cache.
peterv@78456
    36
 *
peterv@89603
    37
 * The cache can store 2 types of objects:
peterv@78456
    38
 *
peterv@90137
    39
 *  If WRAPPER_IS_DOM_BINDING is not set (IsDOMBinding() returns false):
peterv@78456
    40
 *    - a slim wrapper or the JSObject of an XPCWrappedNative wrapper
peterv@78456
    41
 *
peterv@90137
    42
 *  If WRAPPER_IS_DOM_BINDING is set (IsDOMBinding() returns true):
peterv@90770
    43
 *    - a DOM binding object (regular JS object or proxy)
peterv@78456
    44
 *
peterv@89603
    45
 * The finalizer for the wrapper clears the cache.
peterv@78456
    46
 *
peterv@78456
    47
 * A number of the methods are implemented in nsWrapperCacheInlines.h because we
peterv@78456
    48
 * have to include some JS headers that don't play nicely with the rest of the
peterv@78456
    49
 * codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
peterv@21618
    50
 */
peterv@21618
    51
class nsWrapperCache
peterv@21618
    52
{
peterv@21618
    53
public:
peterv@21618
    54
  NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID)
peterv@21618
    55
jcoppeard@134449
    56
  nsWrapperCache() : mWrapper(nullptr), mFlags(0)
peterv@21618
    57
  {
peterv@21618
    58
  }
peterv@21618
    59
  ~nsWrapperCache()
peterv@21618
    60
  {
bzbarsky@118946
    61
    MOZ_ASSERT(!PreservingWrapper(),
bzbarsky@118946
    62
               "Destroying cache with a preserved wrapper!");
peterv@21618
    63
  }
peterv@21618
    64
bent@62690
    65
  /**
peterv@78456
    66
   * Get the cached wrapper.
peterv@78456
    67
   *
bent@62690
    68
   * This getter clears the gray bit before handing out the JSObject which means
bent@62690
    69
   * that the object is guaranteed to be kept alive past the next CC.
bent@62690
    70
   */
peterv@78428
    71
  JSObject* GetWrapper() const;
bent@62690
    72
bent@62690
    73
  /**
peterv@78456
    74
   * Get the cached wrapper.
peterv@78456
    75
   *
bent@62690
    76
   * This getter does not change the color of the JSObject meaning that the
bent@62690
    77
   * object returned is not guaranteed to be kept alive past the next CC.
bent@62690
    78
   *
bent@62690
    79
   * This should only be called if you are certain that the return value won't
bent@62690
    80
   * be passed into a JS API function and that it won't be stored without being
bent@62690
    81
   * rooted (or otherwise signaling the stored value to the CC).
bent@62690
    82
   */
peterv@89603
    83
  JSObject* GetWrapperPreserveColor() const
peterv@89603
    84
  {
jcoppeard@134449
    85
    return GetWrapperJSObject();
peterv@89603
    86
  }
peterv@89603
    87
peterv@89603
    88
  void SetWrapper(JSObject* aWrapper)
peterv@89603
    89
  {
bzbarsky@118946
    90
    MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
bzbarsky@118946
    91
    MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
peterv@89603
    92
jcoppeard@134449
    93
    SetWrapperJSObject(aWrapper);
peterv@89603
    94
  }
peterv@21618
    95
peterv@78456
    96
  /**
peterv@89603
    97
   * Clear the wrapper. This should be called from the finalizer for the
peterv@89603
    98
   * wrapper.
peterv@78456
    99
   */
peterv@89603
   100
  void ClearWrapper()
peterv@89603
   101
  {
bzbarsky@118946
   102
    MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
peterv@21618
   103
jcoppeard@134449
   104
    SetWrapperJSObject(nullptr);
peterv@89603
   105
  }
peterv@21618
   106
mwu@77799
   107
  bool PreservingWrapper()
bent@62690
   108
  {
jcoppeard@134449
   109
    return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
bent@62690
   110
  }
bent@62690
   111
peterv@90137
   112
  void SetIsDOMBinding()
bent@62690
   113
  {
Olli@135494
   114
    MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_DOM_BINDING),
bzbarsky@118946
   115
               "This flag should be set before creating any wrappers.");
jcoppeard@134449
   116
    SetWrapperFlags(WRAPPER_IS_DOM_BINDING);
peterv@79015
   117
  }
bent@62690
   118
peterv@90137
   119
  bool IsDOMBinding() const
bent@62690
   120
  {
jcoppeard@134449
   121
    return HasWrapperFlag(WRAPPER_IS_DOM_BINDING);
bent@62690
   122
  }
bent@62690
   123
bzbarsky@78421
   124
  /**
peterv@78439
   125
   * Wrap the object corresponding to this wrapper cache. If non-null is
VYV03354@124439
   126
   * returned, the object has already been stored in the wrapper cache.
bzbarsky@78421
   127
   */
bzbarsky@177628
   128
  virtual JSObject* WrapObject(JSContext* cx)
peterv@78439
   129
  {
bzbarsky@120382
   130
    MOZ_ASSERT(!IsDOMBinding(), "Someone forgot to override WrapObject");
ayg@100844
   131
    return nullptr;
bzbarsky@78421
   132
  }
bzbarsky@78421
   133
Olli@83500
   134
  /**
Olli@83500
   135
   * Returns true if the object has a non-gray wrapper.
Olli@83500
   136
   */
Olli@83500
   137
  bool IsBlack();
Olli@83500
   138
Olli@108410
   139
  /**
Olli@108410
   140
   * Returns true if the object has a black wrapper,
Olli@108410
   141
   * and all the GC things it is keeping alive are black too.
Olli@108410
   142
   */
Olli@108410
   143
  bool IsBlackAndDoesNotNeedTracing(nsISupports* aThis);
Olli@108410
   144
Olli@157273
   145
  bool HasNothingToTrace(nsISupports* aThis);
Olli@157273
   146
peterv@89603
   147
  // Only meant to be called by code that preserves a wrapper.
mwu@77799
   148
  void SetPreservingWrapper(bool aPreserve)
peterv@21618
   149
  {
peterv@30001
   150
    if(aPreserve) {
jcoppeard@134449
   151
      SetWrapperFlags(WRAPPER_BIT_PRESERVED);
peterv@21618
   152
    }
peterv@30001
   153
    else {
jcoppeard@134449
   154
      UnsetWrapperFlags(WRAPPER_BIT_PRESERVED);
peterv@30001
   155
    }
peterv@21618
   156
  }
peterv@89603
   157
jcoppeard@133029
   158
  void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure)
jcoppeard@133029
   159
  {
jcoppeard@134449
   160
    if (PreservingWrapper() && mWrapper) {
jcoppeard@135458
   161
      aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
jcoppeard@133029
   162
    }
jcoppeard@133029
   163
  }
jcoppeard@133029
   164
jcoppeard@134449
   165
  /* 
jcoppeard@134449
   166
   * The following methods for getting and manipulating flags allow the unused
jcoppeard@134449
   167
   * bits of mFlags to be used by derived classes.
jcoppeard@134449
   168
   */
jcoppeard@134449
   169
jcoppeard@134449
   170
  uint32_t GetFlags() const
peterv@78428
   171
  {
jcoppeard@134449
   172
    return mFlags & ~kWrapperFlagsMask;
peterv@78428
   173
  }
peterv@21618
   174
jcoppeard@134449
   175
  bool HasFlag(uint32_t aFlag) const
jcoppeard@134449
   176
  {
jcoppeard@134449
   177
    MOZ_ASSERT((aFlag & kWrapperFlagsMask) == 0, "Bad flag mask");
jcoppeard@134449
   178
    return !!(mFlags & aFlag);
jcoppeard@134449
   179
  }
jcoppeard@134449
   180
jcoppeard@134449
   181
  void SetFlags(uint32_t aFlagsToSet)
jcoppeard@134449
   182
  {
jcoppeard@134449
   183
    MOZ_ASSERT((aFlagsToSet & kWrapperFlagsMask) == 0, "Bad flag mask");
jcoppeard@134449
   184
    mFlags |= aFlagsToSet;
jcoppeard@134449
   185
  }
jcoppeard@134449
   186
jcoppeard@134449
   187
  void UnsetFlags(uint32_t aFlagsToUnset)
jcoppeard@134449
   188
  {
jcoppeard@134449
   189
    MOZ_ASSERT((aFlagsToUnset & kWrapperFlagsMask) == 0, "Bad flag mask");
jcoppeard@134449
   190
    mFlags &= ~aFlagsToUnset;
jcoppeard@134449
   191
  }
jcoppeard@134449
   192
ms2ger@136155
   193
  void PreserveWrapper(nsISupports* aScriptObjectHolder)
ms2ger@136155
   194
  {
ms2ger@136155
   195
    if (PreservingWrapper()) {
ms2ger@136155
   196
      return;
ms2ger@136155
   197
    }
ms2ger@136155
   198
ms2ger@136155
   199
    nsISupports* ccISupports;
ms2ger@136155
   200
    aScriptObjectHolder->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
ms2ger@136155
   201
                                        reinterpret_cast<void**>(&ccISupports));
ms2ger@136155
   202
    MOZ_ASSERT(ccISupports);
ms2ger@136155
   203
ms2ger@136155
   204
    nsXPCOMCycleCollectionParticipant* participant;
ms2ger@136155
   205
    CallQueryInterface(ccISupports, &participant);
ms2ger@136155
   206
    PreserveWrapper(ccISupports, participant);
ms2ger@136155
   207
  }
ms2ger@136155
   208
ms2ger@136155
   209
  void PreserveWrapper(void* aScriptObjectHolder, nsScriptObjectTracer* aTracer)
ms2ger@136155
   210
  {
ms2ger@136155
   211
    if (PreservingWrapper()) {
ms2ger@136155
   212
      return;
ms2ger@136155
   213
    }
ms2ger@136155
   214
ms2ger@136155
   215
    HoldJSObjects(aScriptObjectHolder, aTracer);
ms2ger@136155
   216
    SetPreservingWrapper(true);
ms2ger@136154
   217
#ifdef DEBUG
ms2ger@136155
   218
    // Make sure the cycle collector will be able to traverse to the wrapper.
ms2ger@136155
   219
    CheckCCWrapperTraversal(aScriptObjectHolder, aTracer);
ms2ger@136155
   220
#endif
ms2ger@136155
   221
  }
ms2ger@136154
   222
ms2ger@140952
   223
  void ReleaseWrapper(void* aScriptObjectHolder);
ms2ger@140952
   224
peterv@177572
   225
protected:
peterv@177572
   226
  void TraceWrapper(JSTracer* aTrc, const char* name)
peterv@177572
   227
  {
peterv@177572
   228
    if (mWrapper) {
peterv@177572
   229
      JS_CallHeapObjectTracer(aTrc, &mWrapper, name);
peterv@177572
   230
    }
peterv@177572
   231
  }
peterv@177572
   232
peterv@177572
   233
  void PoisonWrapper()
peterv@177572
   234
  {
peterv@177572
   235
    if (mWrapper) {
peterv@177572
   236
      mWrapper.setToCrashOnTouch();
peterv@177572
   237
    }
peterv@177572
   238
  }
peterv@177572
   239
jcoppeard@134449
   240
private:
jcoppeard@134449
   241
  JSObject *GetWrapperJSObject() const
jcoppeard@134449
   242
  {
jcoppeard@134449
   243
    return mWrapper;
jcoppeard@134449
   244
  }
jcoppeard@134449
   245
jcoppeard@134449
   246
  void SetWrapperJSObject(JSObject* aWrapper)
jcoppeard@134449
   247
  {
jcoppeard@134449
   248
    mWrapper = aWrapper;
jcoppeard@134449
   249
    UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_DOM_BINDING);
jcoppeard@134449
   250
  }
jcoppeard@134449
   251
jcoppeard@134449
   252
  void TraceWrapperJSObject(JSTracer* aTrc, const char* aName);
jcoppeard@134449
   253
jcoppeard@134449
   254
  uint32_t GetWrapperFlags() const
jcoppeard@134449
   255
  {
jcoppeard@134449
   256
    return mFlags & kWrapperFlagsMask;
jcoppeard@134449
   257
  }
jcoppeard@134449
   258
jcoppeard@134449
   259
  bool HasWrapperFlag(uint32_t aFlag) const
jcoppeard@134449
   260
  {
jcoppeard@134449
   261
    MOZ_ASSERT((aFlag & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
jcoppeard@134449
   262
    return !!(mFlags & aFlag);
jcoppeard@134449
   263
  }
jcoppeard@134449
   264
jcoppeard@134449
   265
  void SetWrapperFlags(uint32_t aFlagsToSet)
jcoppeard@134449
   266
  {
jcoppeard@134449
   267
    MOZ_ASSERT((aFlagsToSet & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
jcoppeard@134449
   268
    mFlags |= aFlagsToSet;
jcoppeard@134449
   269
  }
jcoppeard@134449
   270
jcoppeard@134449
   271
  void UnsetWrapperFlags(uint32_t aFlagsToUnset)
jcoppeard@134449
   272
  {
jcoppeard@134449
   273
    MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
jcoppeard@134449
   274
    mFlags &= ~aFlagsToUnset;
jcoppeard@134449
   275
  }
terrence@131296
   276
ms2ger@136155
   277
  static void HoldJSObjects(void* aScriptObjectHolder,
ms2ger@136155
   278
                            nsScriptObjectTracer* aTracer);
ms2ger@136155
   279
ms2ger@136155
   280
#ifdef DEBUG
ms2ger@136155
   281
  void CheckCCWrapperTraversal(void* aScriptObjectHolder,
ms2ger@136155
   282
                               nsScriptObjectTracer* aTracer);
ms2ger@136155
   283
#endif // DEBUG
ms2ger@136155
   284
peterv@78456
   285
  /**
peterv@78456
   286
   * If this bit is set then we're preserving the wrapper, which in effect ties
peterv@78456
   287
   * the lifetime of the JS object stored in the cache to the lifetime of the
peterv@78456
   288
   * native object. We rely on the cycle collector to break the cycle that this
peterv@78456
   289
   * causes between the native object and the JS object, so it is important that
peterv@78456
   290
   * any native object that supports preserving of its wrapper
peterv@78456
   291
   * traces/traverses/unlinks the cached JS object (see
peterv@78456
   292
   * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER,
peterv@78456
   293
   * NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS and
peterv@78456
   294
   * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER).
peterv@78456
   295
   */
peterv@21618
   296
  enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
peterv@78456
   297
peterv@78456
   298
  /**
peterv@90137
   299
   * If this bit is set then the wrapper for the native object is a DOM binding
peterv@90770
   300
   * (regular JS object or proxy).
peterv@78456
   301
   */
peterv@90137
   302
  enum { WRAPPER_IS_DOM_BINDING = 1 << 1 };
peterv@78456
   303
bobbyholley@174333
   304
  enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING) };
peterv@21618
   305
jcoppeard@135458
   306
  JS::Heap<JSObject*> mWrapper;
jcoppeard@135458
   307
  uint32_t            mFlags;
peterv@21618
   308
};
peterv@21618
   309
bobbyholley@174333
   310
enum { WRAPPER_CACHE_FLAGS_BITS_USED = 2 };
jcoppeard@134449
   311
peterv@21618
   312
NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
peterv@21618
   313
peterv@21618
   314
#define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY                                   \
peterv@21618
   315
  if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
peterv@21618
   316
    *aInstancePtr = static_cast<nsWrapperCache*>(this);                       \
peterv@21618
   317
    return NS_OK;                                                             \
peterv@21618
   318
  }
peterv@21618
   319
amccreight@93879
   320
amccreight@93879
   321
// Cycle collector macros for wrapper caches.
amccreight@93879
   322
amccreight@93879
   323
#define NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \
jcoppeard@133029
   324
  tmp->TraceWrapper(aCallbacks, aClosure);
amccreight@93879
   325
amccreight@93879
   326
#define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
ms2ger@140952
   327
  tmp->ReleaseWrapper(p);
peterv@110221
   328
amccreight@93879
   329
#define NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class) \
amccreight@93879
   330
  NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class)              \
amccreight@93879
   331
    NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER        \
amccreight@93879
   332
  NS_IMPL_CYCLE_COLLECTION_TRACE_END
amccreight@93879
   333
amccreight@93879
   334
#define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(_class) \
mh+mozilla@141055
   335
  NS_IMPL_CYCLE_COLLECTION_CLASS(_class)                \
amccreight@93879
   336
  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)         \
amccreight@93879
   337
    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER   \
amccreight@93879
   338
  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                   \
amccreight@93879
   339
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)       \
amccreight@93879
   340
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS    \
amccreight@93879
   341
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                 \
amccreight@93879
   342
  NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class)
amccreight@93879
   343
birunthan@181087
   344
#define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(_class, ...) \
birunthan@181087
   345
  NS_IMPL_CYCLE_COLLECTION_CLASS(_class)                   \
birunthan@181087
   346
  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)            \
birunthan@181087
   347
    NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__)           \
birunthan@181087
   348
    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER      \
birunthan@181087
   349
  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                      \
birunthan@181087
   350
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)          \
birunthan@181087
   351
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)         \
birunthan@181087
   352
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS       \
birunthan@181087
   353
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                    \
amccreight@93879
   354
  NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class)
amccreight@93879
   355
peterv@21618
   356
#endif /* nsWrapperCache_h___ */