Bumping manifests a=b2g-bump
[gecko.git] / dom / base / nsWrapperCache.h
blob9b9d178e24eacd004a8decfafc57b19ae17eeaf4
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef nsWrapperCache_h___
7 #define nsWrapperCache_h___
9 #include "nsCycleCollectionParticipant.h"
10 #include "mozilla/Assertions.h"
11 #include "js/Id.h" // must come before js/RootingAPI.h
12 #include "js/Value.h" // must come before js/RootingAPI.h
13 #include "js/RootingAPI.h"
14 #include "js/TracingAPI.h"
16 class XPCWrappedNativeScope;
18 #define NS_WRAPPERCACHE_IID \
19 { 0x6f3179a1, 0x36f7, 0x4a5c, \
20 { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } }
22 /**
23 * Class to store the wrapper for an object. This can only be used with objects
24 * that only have one non-security wrapper at a time (for an XPCWrappedNative
25 * this is usually ensured by setting an explicit parent in the PreCreate hook
26 * for the class).
28 * An instance of nsWrapperCache can be gotten from an object that implements
29 * a wrapper cache by calling QueryInterface on it. Note that this breaks XPCOM
30 * rules a bit (this object doesn't derive from nsISupports).
32 * The cache can store objects other than wrappers. We allow wrappers to use a
33 * separate JSObject to store their state (mostly expandos). If the wrapper is
34 * collected and we want to preserve this state we actually store the state
35 * object in the cache.
37 * The cache can store 2 types of objects:
39 * If WRAPPER_IS_DOM_BINDING is not set (IsDOMBinding() returns false):
40 * - a slim wrapper or the JSObject of an XPCWrappedNative wrapper
42 * If WRAPPER_IS_DOM_BINDING is set (IsDOMBinding() returns true):
43 * - a DOM binding object (regular JS object or proxy)
45 * The finalizer for the wrapper clears the cache.
47 * A number of the methods are implemented in nsWrapperCacheInlines.h because we
48 * have to include some JS headers that don't play nicely with the rest of the
49 * codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
51 class nsWrapperCache
53 public:
54 NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID)
56 nsWrapperCache() : mWrapper(nullptr), mFlags(0)
59 ~nsWrapperCache()
61 MOZ_ASSERT(!PreservingWrapper(),
62 "Destroying cache with a preserved wrapper!");
65 /**
66 * Get the cached wrapper.
68 * This getter clears the gray bit before handing out the JSObject which means
69 * that the object is guaranteed to be kept alive past the next CC.
71 JSObject* GetWrapper() const;
73 /**
74 * Get the cached wrapper.
76 * This getter does not change the color of the JSObject meaning that the
77 * object returned is not guaranteed to be kept alive past the next CC.
79 * This should only be called if you are certain that the return value won't
80 * be passed into a JS API function and that it won't be stored without being
81 * rooted (or otherwise signaling the stored value to the CC).
83 JSObject* GetWrapperPreserveColor() const
85 return GetWrapperJSObject();
88 void SetWrapper(JSObject* aWrapper)
90 MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
91 MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
93 SetWrapperJSObject(aWrapper);
96 /**
97 * Clear the wrapper. This should be called from the finalizer for the
98 * wrapper.
100 void ClearWrapper()
102 MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
104 SetWrapperJSObject(nullptr);
107 bool PreservingWrapper()
109 return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
112 void SetIsDOMBinding()
114 MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_DOM_BINDING),
115 "This flag should be set before creating any wrappers.");
116 SetWrapperFlags(WRAPPER_IS_DOM_BINDING);
119 bool IsDOMBinding() const
121 return HasWrapperFlag(WRAPPER_IS_DOM_BINDING);
125 * Wrap the object corresponding to this wrapper cache. If non-null is
126 * returned, the object has already been stored in the wrapper cache.
128 virtual JSObject* WrapObject(JSContext* cx)
130 MOZ_ASSERT(!IsDOMBinding(), "Someone forgot to override WrapObject");
131 return nullptr;
135 * Returns true if the object has a non-gray wrapper.
137 bool IsBlack();
140 * Returns true if the object has a black wrapper,
141 * and all the GC things it is keeping alive are black too.
143 bool IsBlackAndDoesNotNeedTracing(nsISupports* aThis);
145 bool HasNothingToTrace(nsISupports* aThis);
147 // Only meant to be called by code that preserves a wrapper.
148 void SetPreservingWrapper(bool aPreserve)
150 if(aPreserve) {
151 SetWrapperFlags(WRAPPER_BIT_PRESERVED);
153 else {
154 UnsetWrapperFlags(WRAPPER_BIT_PRESERVED);
158 void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure)
160 if (PreservingWrapper() && mWrapper) {
161 aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
166 * The following methods for getting and manipulating flags allow the unused
167 * bits of mFlags to be used by derived classes.
170 typedef uint32_t FlagsType;
172 FlagsType GetFlags() const
174 return mFlags & ~kWrapperFlagsMask;
177 bool HasFlag(FlagsType aFlag) const
179 MOZ_ASSERT((aFlag & kWrapperFlagsMask) == 0, "Bad flag mask");
180 return !!(mFlags & aFlag);
183 void SetFlags(FlagsType aFlagsToSet)
185 MOZ_ASSERT((aFlagsToSet & kWrapperFlagsMask) == 0, "Bad flag mask");
186 mFlags |= aFlagsToSet;
189 void UnsetFlags(FlagsType aFlagsToUnset)
191 MOZ_ASSERT((aFlagsToUnset & kWrapperFlagsMask) == 0, "Bad flag mask");
192 mFlags &= ~aFlagsToUnset;
195 void PreserveWrapper(nsISupports* aScriptObjectHolder)
197 if (PreservingWrapper()) {
198 return;
201 nsISupports* ccISupports;
202 aScriptObjectHolder->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
203 reinterpret_cast<void**>(&ccISupports));
204 MOZ_ASSERT(ccISupports);
206 nsXPCOMCycleCollectionParticipant* participant;
207 CallQueryInterface(ccISupports, &participant);
208 PreserveWrapper(ccISupports, participant);
211 void PreserveWrapper(void* aScriptObjectHolder, nsScriptObjectTracer* aTracer)
213 if (PreservingWrapper()) {
214 return;
217 HoldJSObjects(aScriptObjectHolder, aTracer);
218 SetPreservingWrapper(true);
219 #ifdef DEBUG
220 // Make sure the cycle collector will be able to traverse to the wrapper.
221 CheckCCWrapperTraversal(aScriptObjectHolder, aTracer);
222 #endif
225 void ReleaseWrapper(void* aScriptObjectHolder);
227 protected:
228 void TraceWrapper(JSTracer* aTrc, const char* name)
230 if (mWrapper) {
231 JS_CallObjectTracer(aTrc, &mWrapper, name);
235 void PoisonWrapper()
237 if (mWrapper) {
238 mWrapper.setToCrashOnTouch();
242 private:
243 JSObject *GetWrapperJSObject() const
245 return mWrapper;
248 void SetWrapperJSObject(JSObject* aWrapper)
250 mWrapper = aWrapper;
251 UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_DOM_BINDING);
254 void TraceWrapperJSObject(JSTracer* aTrc, const char* aName);
256 FlagsType GetWrapperFlags() const
258 return mFlags & kWrapperFlagsMask;
261 bool HasWrapperFlag(FlagsType aFlag) const
263 MOZ_ASSERT((aFlag & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
264 return !!(mFlags & aFlag);
267 void SetWrapperFlags(FlagsType aFlagsToSet)
269 MOZ_ASSERT((aFlagsToSet & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
270 mFlags |= aFlagsToSet;
273 void UnsetWrapperFlags(FlagsType aFlagsToUnset)
275 MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
276 mFlags &= ~aFlagsToUnset;
279 static void HoldJSObjects(void* aScriptObjectHolder,
280 nsScriptObjectTracer* aTracer);
282 #ifdef DEBUG
283 void CheckCCWrapperTraversal(void* aScriptObjectHolder,
284 nsScriptObjectTracer* aTracer);
285 #endif // DEBUG
288 * If this bit is set then we're preserving the wrapper, which in effect ties
289 * the lifetime of the JS object stored in the cache to the lifetime of the
290 * native object. We rely on the cycle collector to break the cycle that this
291 * causes between the native object and the JS object, so it is important that
292 * any native object that supports preserving of its wrapper
293 * traces/traverses/unlinks the cached JS object (see
294 * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER,
295 * NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS and
296 * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER).
298 enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
301 * If this bit is set then the wrapper for the native object is a DOM binding
302 * (regular JS object or proxy).
304 enum { WRAPPER_IS_DOM_BINDING = 1 << 1 };
306 enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING) };
308 JS::Heap<JSObject*> mWrapper;
309 FlagsType mFlags;
312 enum { WRAPPER_CACHE_FLAGS_BITS_USED = 2 };
314 NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
316 #define NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY \
317 if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) { \
318 *aInstancePtr = static_cast<nsWrapperCache*>(this); \
319 return NS_OK; \
322 #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY \
323 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY \
324 else
327 // Cycle collector macros for wrapper caches.
329 #define NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \
330 tmp->TraceWrapper(aCallbacks, aClosure);
332 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
333 tmp->ReleaseWrapper(p);
335 #define NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class) \
336 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class) \
337 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \
338 NS_IMPL_CYCLE_COLLECTION_TRACE_END
340 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(_class) \
341 NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
342 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
343 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
344 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
345 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \
346 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS \
347 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \
348 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class)
350 #define NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(_class, ...) \
351 NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \
352 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class) \
353 NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__) \
354 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
355 NS_IMPL_CYCLE_COLLECTION_UNLINK_END \
356 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class) \
357 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__) \
358 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS \
359 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \
360 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(_class)
362 #endif /* nsWrapperCache_h___ */