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 } }
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
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.
54 NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID
)
56 nsWrapperCache() : mWrapper(nullptr), mFlags(0)
61 MOZ_ASSERT(!PreservingWrapper(),
62 "Destroying cache with a preserved wrapper!");
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;
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
);
97 * Clear the wrapper. This should be called from the finalizer for the
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");
135 * Returns true if the object has a non-gray wrapper.
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
)
151 SetWrapperFlags(WRAPPER_BIT_PRESERVED
);
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()) {
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()) {
217 HoldJSObjects(aScriptObjectHolder
, aTracer
);
218 SetPreservingWrapper(true);
220 // Make sure the cycle collector will be able to traverse to the wrapper.
221 CheckCCWrapperTraversal(aScriptObjectHolder
, aTracer
);
225 void ReleaseWrapper(void* aScriptObjectHolder
);
228 void TraceWrapper(JSTracer
* aTrc
, const char* name
)
231 JS_CallObjectTracer(aTrc
, &mWrapper
, name
);
238 mWrapper
.setToCrashOnTouch();
243 JSObject
*GetWrapperJSObject() const
248 void SetWrapperJSObject(JSObject
* 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
);
283 void CheckCCWrapperTraversal(void* aScriptObjectHolder
,
284 nsScriptObjectTracer
* aTracer
);
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
;
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); \
322 #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY \
323 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY \
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___ */