Inside gecko
This is a note for helping developers to understand the codes which are be used frequently in Gecko.
Contents
- 1 Essential COM[1]
- 2 Smart Pointer
- 3 Cycle collector
- 4 Event
- 5 nsISupportImpl
- 6 Reference
- 7 Author
Essential COM[1]
This section is a summary of chapter 1-2 of the book Essential COM[1].
What is COM(Component Object Model)?
COM 是要提供一個可重複使用的軟體架構,讓binary component可以替換,所以要做到language independent and compiler independent。
It is a language independent and compiler independent universal substrate for binary components.
Why do we need it? What kind of problem we faced?
How to use C++ as a component substrate for better software reuse?
- Static library(.lib, .a)
- Dynamic library(.dll, .so)
But...poor portability due to:
- Name mangling.
- Language features which depends on compiler vendors.
A lot of language features in C++ are not suitable for providing a binary compatible library.
How does COM works?
Compiler independent
Separating Interface from Implementation
簡單的說,介面要跟實作分離開來,另外界面不能有data member。在idl的attribute其實也是個function。
The problem with C++ is that this principle does not apply at a binary level, as a C++ class is both interface and implementation simultaneously. The C++ interface class should not contain any of the data members that will be used in the implementation of the object. Instead, the interface class should contain only method declarations for each public operation of the object. The C++ implementation class will contain the actual data members required to implement the object's functionality. A simple example:
// faststringitf.h
class __declspec(dllexport) FastStringItf {
class FastString; // introduce name of impl. class
FastString *m_pThis; // opaque pointer
// (size remains constant)
public:
FastStringItf(const char *psz);
-FastStringItf(void);
int Length(void) const; // returns # of characters
int Find(const char *psz) const; // returns offset
} ;
Drawbacks: Note that the interface class needs to forward each method call to the implementation class explicitly. For a large class library with hundreds or thousands of methods, writing these forwarding routines would become quite tedious and potentially error prone. Also, for performance-critical domains, the cost of making two function calls for each method (one call to the interface, one nested call to the implementation) is less than ideal. Finally, the handle class technique does not completely address the problems of compiler/linker compatibility, which ultimately must be solved if we are to have a truly usable substrate for reusable components.
Abstract Bases as Binary Interfaces
有了分離介面跟實作的概念之後,要利用所有compiler都共通的language features來做到compiler independent,其中一個重要得特性就是abstract class在各個C++的compiler都是同樣的實作方式。另外解構子不是compiler independent,所以需要有個delete函式。
As noted previously, the compatibility problems stem from various compilers having different ideas of (1) how to represent language features at runtime and (2) how symbolic names will be represented at link time. Ensuring that the binary firewall imposed by the C++ interface class uses no compiler-variant language features can solve the problem of compiler/linker dependence.
To accomplish this independence, one must first identify the aspects of the language that have uniform implementations across compilers. Certainly, the runtime representation of composite types such as C-style structs can be held invariant across compilers. The second assumption that can be made is that all compilers can be coerced to pass function parameters in the same order (right to left, left to right) and that stack cleanup can be done in a uniform manner. The third assumption of compiler invariance is the most critical assumption of all, as it enables the definition of a binary interface: All C++ compilers on a given platform implement the virtual function call mechanism equivalently.
It turns out that this is not the tremendous leap of faith that it may seem to be. The runtime implementation of virtual functions in C++ takes the form ofvptrs and vtbls in virtually all production compilers. This technique is based on the compiler silently generating a static array of function pointers for each class that contains virtual functions. This array is called the virtual function
table (or vtbl) and contains one function pointer for each virtual function defined in the class or its base class. Each instance of the class contains a single invisible data member called the virtual function pointer (or vptr) that is automatically initialized by the constructor to point to the class's vtbl. When a client calls a virtual function, the compiler generates the code to dereference the vptr, index into the vtbl, and call through the function pointer found at the designated location. This is how polymorphism and dynamic call dispatching are implemented in C++.
//ifaststring.h
class IFastString {
public:
virtual int Length(void) canst = 0;
virtual int Find(const char *psz) canst = 0;
};
class FastString : public IFastString {
canst int m_cch; // count of characters
char *m_psz;
public:
FastString(const char *psz);
-FastString(void);
int Length(void) const; // returns # of characters
int Find(const char *psz) const; // returns offset
};
extern "C"
IFastString *CreateFastString(const char *psz);
//faststring.cpp (part of DLL)
IFastString *CreateFastString (const char *psz) {
return new FastString(psz);
}
The last remaining barrier to overcome is related to object destruction. The following client code will compile, but the results are unexpected:
int f(void) {
IFastString *pfs = CreateFastString("Deface me");
int n = pfs->Find("ace me");
delete pfs;
return n;
}
The unexpected behavior stems from the fact that the destructor for the interface class is not virtual. This means that the call to the delete operator will not dynamically find the mDst derived destructor and recursively destroy the object from the outermost type to the base type. One workable solution to this problem is to add an explicit Delete method to the interface as another pure virtual function and have the derived class delete itself in its implementation of this method.
Object Extensibility
介面需要能擴充,一種是用繼承,一種是另外產生一個新介面,實作的時候再利用多重繼承來擴充,另外界面不能使用多重繼承,一樣是因為compiler independent的問題。還有在實作上的多重繼承不能使用虛擬繼承(compiler independent),所以在回傳基底指標時不能轉型成基底類別。另外也不能用RTTI,所以需要一個|Dynamic_Cast|在介面。
Despite the immutability of interfaces, it is often necessary to expose additional functionality that may not have been anticipated when an interface was initially designed.
This can be achieved either by designing an interface to derive from another related interface or by allowing an implementation class to inherit from several unrelated interface classes. In either case, the client could use C++'s Runtime Type Identification (RTTI) feature to interrogate the object at runtime to ensure that the requested functionality is indeed supported by the object currently in use.
class IFastString2 : public IFastString {
public:
// real version 2.0
virtual int FindN(const char *psz, int n) = 0;
};
int FindlOthBobCIFastString *pfs) {
IFastString2 *pfs2=dynamic_cast<IFastString2*>(pfs);
if (pfs2) // the object derives from IFastString2
return pfs2->FindN("Bob", 10);
else { // object doesn't derive from IFastString2
error("Cannot find 10th occurrence of Bob");
return -1;
}
}
However, to make the IPersistentObject interface as generic as possible, it should really be its own interface and not derive from IFastString. This does not prohibit the FastString implementation from becoming persistent; it simply means that the persistent version of FastString must implement both the IFastString and IPersistentObject interfaces. To save a FastString to disk, clients can simply use RTTI to bind a pointer to the IPersistentObject interface that is exposed by the object.
class IPersistentObject {
public:
virtual void Delete(void) = 0;
virtual bool Load(const char *pszFileName) = 0;
virtual bool Save(const char *pszFileName) = 0;
};
class FastString : public IFastString,
public IPersistentObject {
int m_cch; // count of characters
char *m_psz;
public:
FastString(const char *psz);
~FastString(void);
// Common methods
void Delete(void); // deletes this instance
// IFastString methods
int Length(void) canst; // returns # of characters
int Find(const char *psz) canst; // returns offset
// IPersistentObject methods
bool Load(const char *pszFileName);
bool Save(const char *pszFileName);
} ;
bool SaveString(IFastString *pfs, canst char *pszFN){
bool bResult = false;
IPersistentObject *ppo =
dynamic_cast<IPersistentObject*>(pfs);
if (ppo)
bResult = ppo->Save(pszFN);
return bResult;
}
RTTI is a very compiler-dependent feature. One very tractable solution to the problem is to leverage the semantics of dynamic_cast without using the actual compiler-dependent language feature. Exposing a well-known method explicitly from each interface that will perform the semantic equivalent of dynamic_cast can achieve the desired effect without requiring all entities to use the same C++ compiler. Note that when asked for the common base interface IExtensibleObject, the implementation statically casts itself to IFastString. If IExtensibleObject was a virtual base class of both IFastString and IPersistentObject, then this cast would not be ambiguous and the statement would compile. However, introducing virtual base classes adds needless runtime complexity to the resultant object and also introduces compiler dependences. This is because virtual bases are yet another C++ language feature that has several proprietary implementations.
class IExtensibleObject {
public:
virtual void *Dynamic_Cast(const char* pszType) = 0;
virtual void Delete(void) = 0;
} ;
class IPersistentObject : public IExtensibleObject {
public:
virtual bool Load(const char *pszFileName) = 0;
virtual bool Save(const char *pszFileName) = 0;
} ;
class IFastString : public IExtensibleObject {
public:
virtual int Length(void) = 0;
virtual int Find(const char *psz) = 0;
} ;
bool SaveString(IFastString *pfs, const char *pszFN){
bool bResult = false;
IPersistentObject *ppo = (IPersistentObject*)
pfs->Dynamic_Cast("IPersistentObject");
if (ppo)
bResult = ppo->Save(pszFN);
return bResult;
}
class FastString : public IFastString,
public IPersistentObject {
...
}
void *FastString: : Dynamic_Cast(const char *pszType) {
if (strcmp(pszType, "IFastString") == 0)
return static_cast<IFastString*>(this);
else if (strcmp(pszType, "IPersistentObject") == 0)
return static_cast<IPersistentObject*>(this);
else if (strcmp(pszType, "IExtensibleObject") == 0)
return static_cast<IFastString*>(this);
else
return 0; // request for unsupported interface
}
Resource Management
IExtensibleObject就像是COM的IUNKNOW,也就是XPCOM的 nsISupport,如果要直接call |Delete|,程式碼難維護,所以COM的架構使用reference counting機制來做resource management。因此所有COM繼承的基底類別就要提供三種功能的function,轉型,增加跟減少reference counting。
One remaining problem is we need to call |Delete|. One simple solution to this problem is to have each object maintain a reference count that is incremented when an interface pointer is duplicated and decremented when an interface pointer is destroyed.
class IExtensibleObject {
public:
virtual void *Dynamic_Cast(const char* pszType) =0;
virtual void DuplicatePointer(void) = 0;
virtual void DestroyPointer(void) = 0;
};
Language independent( through IDL)
logical name是用在介面上人類容易記的名字,為了避免命名碰撞,需要有physical name,做法是用guid。See Generating GUIDs. 基本上GUID是利用時間跟地點來做到unique。例如利用獨特的地點(IP)跟時間,就有辦法產生一組唯一的128 bit組合。
Each interface definition took the form of a C++ abstract base class definition in a C++ header file. The fact that the interface definition resides in a file that is parsable from only one language betrays one remaining implementation detail of the object: the language used to produce it. Ultimately, the object should be accessible from any language, not just the language chosen by the object implementor. By providing only a C++-compatible interface definition, the object implementor is forcing the component's target audience also to work in C++.
COM provides such a language that takes the basic well-known syntax of C and adds the ability to disambiguate precisely any C language features that are subject to interpretation in other languages. This language is called the Interface Definition Language or IDL.
Interfaces and IDL
To understand why COM interfaces require a physical name that is distinct. from the logical name of the interface, consider the following situation. Two developers independently decide to develop an interface that models a hand-held calculator. The two interface definitions would probably be similar given the common problem domain, but in all likelihood the actual order of
method definitions and perhaps the method signatures would be somewhat different. However, both developers would probably choose the same logical name, ICalculator.
- uuid: GUIDs are 128-bit extremely large numbers that are guaranteed to be unique in both time and space. COM GUIDs are based on the Universally Unique Identifiers (UUIDs) used in DCE RPC. The algorithm uses the local machine's network interface address, wall clock time, and a pair of persistent counters to compensate for clock resolution and abnormal system clock changes (e.g., daylight savings time, manual time adjustment of system clock).
Take DOMEvent as an example:
nsIDDOMEvent.idl
[builtinclass, uuid(02d54f52-a1f5-4ad2-b560-36f14012935e)]
interface nsIDOMEvent : nsISupports
{
};
We get nsIDOMEvent.h in obj-xxx-xxx/dist/include/. And we write Event.h and Event.cpp in tree/dom/events/Event.[h/cpp].
In nsIDOMEvent.h:
/* starting interface: nsIDOMEvent */
#define NS_IDOMEVENT_IID_STR "02d54f52-a1f5-4ad2-b560-36f14012935e"
#define NS_IDOMEVENT_IID \
{0x02d54f52, 0xa1f5, 0x4ad2, \
{ 0xb5, 0x60, 0x36, 0xf1, 0x40, 0x12, 0x93, 0x5e }}
...
/*
template<typename T, typename U> \
struct COMTypeInfo;
*/
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOMEVENT_IID)
...
/*
template<typename T> \
struct nsIDOMEvent::COMTypeInfo<nsIDOMEvent, T> { \
static const nsIID kIID __attribute__ ((visibility ("hidden"))); \
}; \
template<typename T> \
const nsIID nsIDOMEvent::COMTypeInfo<nsIDOMEvent, T>::kIID __attribute__ ((visibility ("hidden"))) = {0x02d54f52, 0xa1f5, 0x4ad2, \
{ 0xb5, 0x60, 0x36, 0xf1, 0x40, 0x12, 0x93, 0x5e }};
*/
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMEvent, NS_IDOMEVENT_IID)
In nsID.h:
struct nsID
{
/**
* @name Identifier values
*/
//@{
uint32_t m0;
uint16_t m1;
uint16_t m2;
uint8_t m3[8];
//@}
/**
* @name Methods
*/
...
};
typedef nsID nsIID
COM interfaces cannot derive directly from more than one interface. The following is not legal in COM:
[object, uuid(DF12E156-A29A-lldO-8C2D-0080C73925BA)]
interface ICatDog : ICat, IDog { // illegal, multiple bases
HRESULT Meowbark(void);
}
But a COM-based CatIDog is still possible at the implementation level:
class CatDog : public ICat, public IDog {
...
} ;
IUnknown(nsISupports in Gecko)
IUnknown is functionally equivalent to IExtensi bl eObj ect. The Querylnterface method is used for runtime type discovery and is analogous to C++'s dynami c_cast operator. The AddRef method is used to notify the object that an interface pointer has been duplicated. The Rel ease method is used to notify the object that an interface pointer has been destroyed and any resources the object held on behalf of the client can be released. The primary distinction between IUnknown and the interface defined in the previous chapter is that IUnknown uses GUIDs, not strings, to identify interface types at runtime.
// unknwn.idl - system IDL file
[
local,
object,
uuid(00000000-0000-0000-COOO-000000000046)
]
interface IUnknown {
HRESULT Querylnterface([in] REFIID riid,
[out] void **ppv);
ULONG AddRef(void);
ULONG Release(void);
}
[scriptable, uuid(00000000-0000-0000-c000-000000000046)]
interface nsISupports {
void QueryInterface(in nsIIDRef uuid,
[iid_is(uuid),retval] out nsQIResult result);
[noscript, notxpcom] nsrefcnt AddRef();
[noscript, notxpcom] nsrefcnt Release();
};
#define NS_ISUPPORTS_IID \
{ 0x00000000, 0x0000, 0x0000, \
{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46} }
class NS_NO_VTABLE nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISUPPORTS_IID)
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) = 0;
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsISupports, NS_ISUPPORTS_IID)
Resource Management and IUnknown
三項公理:
- 如果一個界面指標被其他的指標複製,且它所指的物件不是null,則需要呼叫|AddRef|。
- 一個非nullptr被overwrite之前,那個之前被指向的物件要先呼叫|Release|。
- 有些重複的|AddRef|跟|Release|可以被最佳化掉。
COM's reference counting rules can be distilled down to the following three simple axioms:
1. When a non-null interface pointer is copied from one memory location to another, AddRef should be called to notify the object of the additional reference.
2. Release must be called prior to overwriting a memory location that contains a non-null interface pointer to notify the object that the reference is being destroyed.
3. Redundant calls to AddRef and Release can be optimized away if there is special knowledge about the relationship between two or more memory locations.
The following are some common situations that require calls to the AddRef method:
A1. When writing a non-null interface pointer to a local variable.
A2. When a callee writes a non-null interface pointer to an [out] or [in, out] parameter of a method or function.
A3. When a callee returns a non-null interface pointer as the physical result of a function.
A4. When writing a non-null interface pointer to a data member of an object.
The common situations that require calls to the Release method include:
R1. Prior to overwriting a non-null local variable or data member.
R2. Prior to leaving the scope of a non-null local variable.
R3. When callee overwrites an [in, out] parameter of a method or function whose initial value is non-null. Note that [out] parameters are assumed to be null on input and must never be released by the callee.
R4. Prior to overwriting a non-null data member of an object.
R5. Prior to leaving the destructor of an object that has a non-null interface pointer as a data member.
void GetObject([out] IUnknown **ppUnk);
void UseObject([in] IUnknown *pUnk);
void GetAndUse(/* [out] */ IUnknown ** ppUnkOut) {
IUnknown *pUnk1 = 0, *pUnk2 = 0;
*ppUnkOut = 0; // R3
// get pointers to one (or two) objects
GetObject(&pUnk1); // A2
GetObject(&pUnk2); // A2
// set pUnk2 to point to first object
if (pUnk2) pUnk2->Release(); // R1
if (pUnk2 = pUnk1) pUnk2->AddRef(); // A1
// pass pUnk2 to some other function
UseObject(pUnk2); // S1
// return pUnk2 to caller using ppUnkOut parameter
if (*ppUnkOut = pUnk2) (*ppUnkOut)->AddRef(); // A2
// falling out of scope so clean up
if (pUnk1) pUnkl->Release(); // R2
if (pUnk2) pUnk2->Release(); // R2
}
Smart Pointer
This section is basically from [2], [3] and [4].
COMPtr
nsCOMPtr is a "smart pointer". It is a template class that acts, syntactically, just like an ordinary pointer in C or C++. nsCOMPtr manages AddRef, Release, and QueryInterface for you.
A rule of XPCOM is that any function that creates or returns an interface pointer will have already AddRefed it. The caller can then hold onto the reference indefinitely, calling Release when it no longer needs it. When the last pointer to an interface is Released, the interface (and consequently, typically the underlying object) will delete itself. As long as there is an outstanding AddRef against the interface, it continues to exist. If you forget to call Release, the object will leak, i.e., the storage for that object will never be reclaimed. Leaks are bad :-).
XPCOM ownership guidelines
- If you made it, you own it.
- Needing it isn't, by itself, a valid reason for owning it.
- If you own it, it shouldn't own you.
- You don't need to own it if it's lifetime is guaranteed to be longer than yours.
- Parents own their children (and not the reverse).
- Containers own the things they contain (and not the reverse). Use nsCOMPtrs to implement owning-pointers.
A reference through which you will call AddRef and Release is called an owning reference. You use an owning reference when
- you created the object;
- you got the object from a function that might have created it, e.g., any `getter' function, such as QueryInterface, or CreateInstance. All good getters AddRef the interface pointers they produce, thus providing you with an owning reference;
- you will hold onto the reference longer than the scope of the function in which you acquired it, e.g., you got it as a parameter, but you're hanging onto it in a member variable (see, for example, Comparison 1, below).
You don't need an owning reference when
- the object is passed in as a parameter, and you don't need to keep it any longer than the scope of this function;
- the object's lifetime is known to contain yours in some well defined way, e.g., in the nodes of a tree, parent nodes keep owning references to their children, children need not keep owning references to their parents.
How does nsCOMPtr help?
For instance, here is a typical snippet of code (at its most compact) where you assign a XPCOM interface pointer into a member variable, i.e., the body of a `setter' function, side-by-side using raw XPCOM interface pointers and nsCOMPtr
s.
// raw XPCOM interface pointers... // given: |nsIFoo* mFooPtr;| /* |AddRef| the new value if it's not |NULL|; assign it in; and |Release| the old value, if any (so we don't leak it). This order of assignment is special and must be used to avoid particular ownership bugs. */ NS_IF_ADDREF(aFooPtr); nsIFoo* temp = mFooPtr; mFooPtr = aFooPtr; NS_IF_RELEASE(temp); |
// |nsCOMPtr|... // given: |nsCOMPtr<nsIFoo> mFooPtr;| /* This assignment automatically |Release|s the old value in |mFooPtr|, if any, and |AddRef|s the new one, in the appropriate sequence to avoid the ownership bug mentioned earlier. */ mFooPtr = aFooPtr; |
Using nsCOMPtr
The Basics
In most cases, you'll use an nsCOMPtr
exactly as you would a raw XPCOM interface pointer. Note the slight difference in declaration.
// raw XPCOM interface pointers... nsIFoo* fooPtr = 0; // ... fooPtr->SomeFunction(x, y, z); AnotherFunction(fooPtr); if ( fooPtr ) // ... if ( fooPtr == foo2Ptr ) // ... |
// |nsCOMPtr|... nsCOMPtr<nsIFoo> fooPtr; // ... fooPtr->SomeFunction(x, y, z); AnotherFunction(fooPtr); if ( fooPtr ) // ... if ( fooPtr == foo2Ptr ) // ... |
There are two main differences. First: you no longer need, nor are you allowed, to call AddRef
or Release
.
// raw XPCOM interface pointers... // given: |nsIFoo* mFooPtr;| /* Note: this sequence is not the correct order to do assign raw pointers anyway (see {{ Anch("Comparison 1") }}) but I need it for this comparison. */ NS_IF_RELEASE(mFooPtr); mFooPtr = aFooPtr; NS_IF_ADDREF(mFooPtr); |
// |nsCOMPtr|... // given: |nsCOMPtr<nsIFoo> mFooPtr;| /* You no longer need, nor will the compiler let you, call |AddRef|, or |Release|. */ NS_IF_RELEASE(mFooPtr); // Error: |Release| is private mFooPtr = aFooPtr; NS_IF_ADDREF(mFooPtr); // Error: |AddRef| is private |
Second: you can't just pass the address of an nsCOMPtr
to a getter expecting to return a result through a raw XPCOM interface pointer parameter. You have to `annotate' the nsCOMPtr
with the getter_AddRefs directive.
// raw XPCOM interface pointers... nsIFoo* foo; GetFoo(&foo); |
// |nsCOMPtr|s... nsCOMPtr<nsIFoo> foo; GetFoo(getter_AddRefs(foo)); |
A Few Details
// A way (though not the best way) to |QueryInterface| into an |nsCOMPtr|... nsCOMPtr<nsIFoo> foo; nsresult rv = bar->QueryInterface(NS_GET_IID(nsIFoo), getter_AddRefs(foo)); // Or, if you're a savvy XPCOM programmer, // you use the type-safe version... nsresult rv = CallQueryInterface(bar, getter_AddRefs(foo)); |
QueryInterface
is used so frequently, though, that nsCOMPtr
has a special facility to call it. This facility is type-safe, and it enables an nsCOMPtr
to be directly constructed from the result of QueryInterface
. Construction from the correct value is more efficient that construction followed by assignment. This facility is the do_QueryInterface, the sample above would look like this
// The best way to |QueryInterface| into an |nsCOMPtr|... nsresult rv; nsCOMPtr<nsIFoo> foo(do_QueryInterface(bar, &rv)); // Or, if you don't care about the |nsresult| nsCOMPtr<nsIFoo> foo(do_QueryInterface(bar)); |
class nsIBar : public nsIFoo ... { ... }; nsIBar* p = ...; // C++ thinks every |nsIBar*| is an // |nsIFoo*|, therefore, C++ allows // this... nsCOMPtr<nsIFoo> foo = p; // ...even though it is an XPCOM // type error |
class nsIBar : public nsIFoo ... { ... }; nsIBar* p = ...; // No type error here... nsCOMPtr<nsIFoo> foo(do_QueryInterface(p)); |
dont_AddRef
</a> is a similar directive that helps you when you assign in a pointer that has already been AddRef
ed, e.g., because you called a getter that returned the pointer as its function result.</p>
nsCOMPtr<nsIFoo> foo(dont_AddRef(CreateFoo())); // |CreateFoo| |AddRef|s its result, as all good getters do |
Something nsCOMPtr Doesn't Do
這邊解釋為什麼要先用getter_AddRefs傳給XXX**這種函式參數。
// Incorrect assumptions about |nsCOMPtr|... nsresult nsCacheRecord::GetFileSpec( nsIFileSpec** aFileSpec ) /* ...fills in the callers |nsFileSpec*| (which the caller supplied the address of) with a copy of my member variable |mFileSpec|, an |nsCOMPtr|. I.e., this function is a `getter'. Remember: good XPCOM getters always |AddRef| their result. */ { // ... *aFileSpec = mFileSpec; // the |nsCOMPtr| should take care of the refcount here, right? return NS_OK; } |
Plainly, the author believed (though perhaps with some question) that the nsCOMPtr, mFileSpec, would AddRef automatically as it was assigned into *aFileSpec. This is not the case. An nsCOMPtr automatically calls AddRef and Release (only) on its own behalf. In all other situations, it is designed to be a drop in replacement for a raw XPCOM pointer. Where ever an nsCOMPtr is used in a situation where a raw pointer is needed, the nsCOMPtr automatically provides one.
// |nsCOMPtr| produces a raw pointer when needed... nsCOMPtr<nsIFoo> foo = ...; // 1. Assigning into a raw pointer nsIFoo* raw_foo = foo; // 2. Assigning into another |nsCOMPtr| nsCOMPtr<nsIFoo> foo2 = foo; // 3. As a parameter SetFoo(foo); // 4. Testing the value in an |if| expression // 5. Calling a member function if ( foo ) foo->DoSomething(); |
In all of these cases, pretty much the exact same code is executed (case 2 is slightly different, but the intent is the same). In each case, you are essentially extracting the raw pointer value for your own purpose. If the nsCOMPtr AddRefed the value each time you did that, cases 4 and 5 would obviously always generate leaks. SetFoo, from case 3, would have to be written two different ways when given an nsCOMPtr, it would know the value was already AddRefed, and when given a raw pointer it would assume the value was not AddRefed. Actually the contradictions run deeper than that. All these cases show that automatically AddRefing on `output' makes nsCOMPtrs and raw-pointers act differently from the point of view of the clients. The goal is to make them act the same so that nsCOMPtrs can be a drop in replacement (modulo managing its own `ownership').
nsCOMPtrs in function signatures
nsCOMPtr<T> f() don't return an nsCOMPtr
This practice is dangerous. Returning an AddRefed pointer in almost any form as a function result leads to several potential errors, some of which are leaks, some of which are dangling pointers. Returning an nsCOMPtr may seem like a good idea (since it tells clients you are giving them ownership), however it can be the cause of an dangling pointer. Consider:
// Don't return |nsCOMPtr|s...
nsCOMPtr<nsIFoo> CreateFoo();
// ...
nsIFoo* myFoo = CreateFoo(); // Oops: |myFoo| now dangles!
// |CreateFoo| returns an |nsCOMPtr|, which
// automatically |Release|s right after this
// assignment. Now |myFoo| refers to a
// deleted object.
You can tell callers you are giving them ownership in a way that doesn't pose this hazard by returning a already_AddRefed<T> (see bug #59212). An nsCOMPtr knows not to AddRef a value that is already_AddRefed.
// Preferred form: if you must return a pointer, use |already_AddRefed|...
already_AddRefed<nsIFoo> CreateFoo();
// ...
nsIFoo* myFoo1 = CreateFoo(); // doesn't dangle
nsCOMPtr<nsIFoo> myFoo2( CreateFoo() ); // doesn't leak
nsCOMPtr<nsIFoo> myFoo3( dont_AddRef(CreateFoo()) ); // redundant, but legal and correct
Compare this to the most frequent leaks caused by returning a raw pointer you have already AddRefed:
// Don't return raw pointers; that incites leaks...
nsIFoo* CreateFoo(); // returns an |AddRef|ed pointer
// ...
MOZ_IMPLICIT nsCOMPtr::nsCOMPtr(T* aRawPtr)
: NSCAP_CTOR_BASE(aRawPtr)
{
if (mRawPtr) {
NSCAP_ADDREF(this, mRawPtr);
}
NSCAP_LOG_ASSIGNMENT(this, aRawPtr);
NSCAP_ASSERT_NO_QUERY_NEEDED();
}
nsCOMPtr<nsIFoo> myFoo = CreateFoo(); // Oops: leak;
nsCOMPtr<nsIFoo> myFoo( dont_AddRef(CreateFoo()) );
// Since |CreateFoo| already |AddRef|s its result, we must remind
// our |nsCOMPtr| not to. It's easy to forget. Prevent it in advance
// by not returning pointers as function results, or else by returning
// an |already_AddRefed<T>| as above.
Let's see more details about already_AddRefed.
template<class T>
struct already_AddRefed
{
already_AddRefed() : mRawPtr(nullptr) {}
explicit already_AddRefed(T* aRawPtr) : mRawPtr(aRawPtr) {}
already_AddRefed(already_AddRefed<T>&& aOther) : mRawPtr(aOther.take()) {}
MOZ_WARN_UNUSED_RESULT T* take()
{
T* rawPtr = mRawPtr;
mRawPtr = nullptr;
return rawPtr;
}
private:
T* MOZ_OWNING_REF mRawPtr;
};
already_AddRefed<T> nsCOMPtr::forget()
{
T* temp = 0;
swap(temp);
return already_AddRefed<T>(temp);
}
already_AddRefed<nsIContentIterator>
NS_NewContentIterator()
{
nsCOMPtr<nsIContentIterator> iter = new nsContentIterator(false);
return iter.forget();
}
TemporaryRef is just like already_AddRefed is doing. TemporaryRef is for RefPtr. already_AddRefed is for nsCOMPtr and nsRefPtr. The biggest difference between already_AddRefed and TemporaryRef is the constructor for T* aVal case. TemporaryRef(new T()) is reasonable.
template<typename T>
class TemporaryRef
{
// To allow it to construct TemporaryRef from a bare T*
friend class RefPtr<T>;
typedef typename RefPtr<T>::DontRef DontRef;
public:
MOZ_IMPLICIT TemporaryRef(T* aVal) : mPtr(RefPtr<T>::ref(aVal)) {}
TemporaryRef(const TemporaryRef& aOther) : mPtr(aOther.take()) {}
template<typename U>
TemporaryRef(const TemporaryRef<U>& aOther) : mPtr(aOther.take()) {}
~TemporaryRef() { RefPtr<T>::unref(mPtr); }
MOZ_WARN_UNUSED_RESULT T* take() const
{
T* tmp = mPtr;
mPtr = nullptr;
return tmp;
}
private:
TemporaryRef(T* aVal, const DontRef&) : mPtr(aVal) {}
mutable T* MOZ_OWNING_REF mPtr;
TemporaryRef() = delete;
void operator=(const TemporaryRef&) = delete;
};
void f( nsCOMPtr<T> ) don't pass an nsCOMPtr by value
This practice is wasteful, but not otherwise harmful. There is no need to AddRef parameters, as they are guaranteed to live as long as the function call. You only need to AddRef them as you store them in a structure that will live longer than the function call. Which means the appropriate member of that structure should be an nsCOMPtr, not the function parameter. Additionally, this signature may confuse callers into thinking they need an nsCOMPtr just to call the function.
void f( const nsCOMPtr<T>& ) don't pass an nsCOMPtr by const reference
Exactly as the signature above, this practice is wasteful, but not otherwise harmful, and has the same impact as passing an nsCOMPtr by value if the caller only supplied a raw pointer.
void f( nsCOMPtr<T>* ) avoid passing an nsCOMPtr by address, if possible
// Passing an |nsCOMPtr| by pointer requires extra work...
void f( nsCOMPtr<nsIFoo>* );
// ...
nsCOMPtr<nsIFoo> myFoo = ...;
f( address_of(myFoo) );
This practice requires callers to have an nsCOMPtr, and requires them to do a little extra work, as operator& for nsCOMPtrs is private (to help prevent leaks caused by casting; also see Template:Bug(59414)). This is an acceptable way to declare `in/out' parameters, but prefer passing nsCOMPtrs by reference, as below.
void f( nsCOMPtr<T>& ) do pass an nsCOMPtr by reference for `in/out' parameters
This is the prefered scheme for providing `in/out' parameters. If you were to use a raw pointer instead, your function couldn't know what ownership relationship the caller had to the input value, and hence, couldn't know whether to Release it or not before assigning in the new value. By declaring the parameter as an nsCOMPtr&, the relationship is explicit.
nsRefPtr
nsRefPtr is used to hold a pointer for any type that implements AddRef() and Release(). You can take this as a smart pointer for non-COM object but will co-work with nsCOMPtr related codes a lot. When should I use nsCOMPtr versus nsRefPtr?
Any time you are holding an XPCOM interface pointer, you should be using nsCOMPtr.
nsCOMPtr<nsISupports> a;
nsCOMPtr<nsIFoo> foo;
Any time you are holding a pointer to a concrete class--even if it implements one or more XPCOM interfaces--you should be using nsRefPtr:
nsRefPtr<nsFoo> foo; // class that implements nsIFoo;
nsRefPtr<Bar> bar; // some random class that I want ref-counted but has nothing to do with XPCOM:
// Just implement AddRef() and Release() and it will work with nsRefPtr
Note: in the above example, "nsCOMPtr<nsFoo>" might compile and work OK (it won't if your XPCOM class multiply-inherits nsISupports). But this is considered Bad Form, and may soon be made a compile-time error. Don't do it!
Can I QueryInterface a nsRefPtr to get a nsCOMPtr from the object it points to?
Sure. Instead of using "do_QueryInterface()" (which is used for nsCOMPtrs), use "do_QueryObject()", which works with nsRefPtrs:
nsRefPtr<nsFoo> foo; // nsFoo implements nsIFoo and nsIBar XPCOM interfaces
nsCOMPtr<nsIBar> bar(do_QueryObject(foo)); // constructor initiatialization, slightly faster
or
bar = do_QueryObject(foo);
if (bar) { ... } // generally you want to check for success
class Foo {
NS_INLINE_DECL_REFCOUNTING(Foo) // helper for implementing AddRef/Release
};
nsRefPtr<Foo> fooPtr = new Foo();
void someGetterFunction(Foo**); // function using output parameter.
nsRefPtr<Foo> outputPtr;
someGetterFunction(getter_AddRefs(outputPtr));
already_AddRefed<Foo> someFunction(); // function return an object.
nsRefPtr<Foo> returnPtr = someFunction();
RefPtr
RefPtr is a reference-counted pointer which defined in MFBT. Any class that might be used before XPCOM startup should use RefPtr as a shard pointer. RefPtr.h implements various smart pointer templates to simplify reference counting of values of particular classes. You can use RefPtr on XPCOM with limited features. Because nsCOMPtr has more member function for XPCOM specific features.
class SourceSurface : public RefCounted<SourceSurface>
{...};
or
class Foo
{
NS_INLINE_DECL_REFCOUNTING
...
};
class Foo: public RefCounted<Foo> {};
RefPtr<Foo> fooPtr = new Foo();
{
RefPtr<Foo> anotherPtr = fooPtr; // AddRef() invoked, reference count is 2 now.
// Release() invoked after exiting scope.
}
void someGetterFunction(Foo**); // function using output parameter.
RefPtr<Foo> outputPtr;
someGetterFunction(byRef(outputPtr));
TemporaryRef<Foo> someFunction(); // TemporaryRef represents a rvalue of a RefPtr
RefPtr<Foo> retPtr = someFunction(); // someFunction() = fooPtr; will not compile.
/**
* Use this macro to declare and implement the AddRef & Release methods for a
* given non-XPCOM <i>_class</i>.
*
* @param _class The name of the class implementing the method
* @param optional override Mark the AddRef & Release methods as overrides.
*/
#define NS_INLINE_DECL_REFCOUNTING(_class, ...) \
public: \
NS_METHOD_(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD(_class); \
++mRefCnt; \
NS_LOG_ADDREF(this, mRefCnt, #_class, sizeof(*this)); \
return mRefCnt; \
} \
NS_METHOD_(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
NS_ASSERT_OWNINGTHREAD(_class); \
--mRefCnt; \
NS_LOG_RELEASE(this, mRefCnt, #_class); \
if (mRefCnt == 0) { \
NS_ASSERT_OWNINGTHREAD(_class); \
mRefCnt = 1; /* stabilize */ \
delete this; \
return 0; \
} \
return mRefCnt; \
} \
protected: \
nsAutoRefCnt mRefCnt; \
NS_DECL_OWNINGTHREAD \
public:
/**
* Use this macro to declare and implement the AddRef & Release methods for a
* given non-XPCOM <i>_class</i> in a threadsafe manner.
*
* DOES NOT DO REFCOUNT STABILIZATION!
*
* @param _class The name of the class implementing the method
*/
#define NS_INLINE_DECL_THREADSAFE_REFCOUNTING(_class, ...) \
public: \
NS_METHOD_(MozExternalRefCountType) AddRef(void) __VA_ARGS__ { \
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
nsrefcnt count = ++mRefCnt; \
NS_LOG_ADDREF(this, count, #_class, sizeof(*this)); \
return (nsrefcnt) count; \
} \
NS_METHOD_(MozExternalRefCountType) Release(void) __VA_ARGS__ { \
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
nsrefcnt count = --mRefCnt; \
NS_LOG_RELEASE(this, count, #_class); \
if (count == 0) { \
delete (this); \
return 0; \
} \
return count; \
} \
protected: \
::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
public:
nsAutoRef/nsCountedRef
nsAutoRef/nsCountedRef provide auto resource management for anything doesn’t have destructor or anything we cannot change the type definition. nsAutoRef dosen't provide copy constructor nor copy assignment. nsCountedRef provides copy constructor and assignment operators that enable more than one concurrent reference to the same resource.
class nsAutoRefTraits<Foo> {
typedef Foo* RawRef;
static RawRef Void() { return nullptr; }
static void Release(RawRef aRawRef) {
// do something about releasing the aRawRef, e.g. close(aRawRef);
}
static void AddRef(RawRef aRawRef) {
// do something about copying reference, e.g. AddReferenceFoo(aRawRef);
}
};
nsAutoRef<Foo> autoRef = new Foo();
nsReturnRef<Foo> someFunction(); // nsReturnRef represents a rvalue of a nsAutoRef
nsAutoRef<Foo> returnRef = someFunction();
nsCountedRef<Foo> countRef1 = new Foo();
{
nsCountedRef<Foo> counteRef2 = countRef1; // nsAutoRefTraits<Foo>::AddRef() is invoked.
// nsAutoRefTraits<Foo>::Release() is invoked after exiting scope.
}
template <class T>
class nsPointerRefTraits
{
public:
// The handle is a pointer to T.
typedef T* RawRef;
// A nullptr does not have a resource.
static RawRef Void()
{
return nullptr;
}
};
// This allows the use of nsAutoRefs for an ogg_packet that properly free the
// contents of the packet.
template <>
class nsAutoRefTraits<ogg_packet> : public nsPointerRefTraits<ogg_packet>
{
public:
static void Release(ogg_packet* aPacket) {
mozilla::OggCodecState::ReleasePacket(aPacket);
}
};
bool
TheoraState::DecodeHeader(ogg_packet* aPacket)
{
nsAutoRef<ogg_packet> autoRelease(aPacket);
mPacketCount++;
int ret = th_decode_headerin(&mInfo,
&mComment,
&mSetup,
aPacket);
bool isSetupHeader = aPacket->bytes > 0 && aPacket->packet[0] == 0x82;
if (ret < 0 || mPacketCount > 3) {
return false;
} else if (ret > 0 && isSetupHeader && mPacketCount == 3) {
mDoneReadingHeaders = true;
}
return true;
}
// Example: An Automatically closing file descriptor
// References that are simple integral types (as file-descriptors are)
// usually need a new class to represent the resource and how to handle its
// references.
class nsRawFD;
// Specializing nsAutoRefTraits<nsRawFD> describes how to manage file
// descriptors, so that nsAutoRef<nsRawFD> provides automatic closing of
// its file descriptor on destruction.
template <>
class nsAutoRefTraits<nsRawFD> {
public:
// The file descriptor is held in an int.
typedef int RawRef;
// -1 means that there is no file associated with the handle.
static int Void() { return -1; }
// The file associated with a file descriptor is released with close().
static void Release(RawRef aFD) { close(aFD); }
};
// A function returning a file descriptor that must be closed.
nsReturnRef<nsRawFD> get_file(const char *filename) {
// Constructing from a raw file descriptor assumes ownership.
nsAutoRef<nsRawFD> fd(open(filename, O_RDONLY));
fcntl(fd, F_SETFD, FD_CLOEXEC);
return fd.out();
}
void f() {
unsigned char buf[1024];
// Hold a file descriptor for /etc/hosts in fd1.
nsAutoRef<nsRawFD> fd1(get_file("/etc/hosts"));
nsAutoRef<nsRawFD> fd2;
fd2.steal(fd1); // fd2 takes the file descriptor from fd1
ssize_t count = read(fd1, buf, 1024); // error fd1 has no file
count = read(fd2, buf, 1024); // reads from /etc/hosts
// If the file descriptor is not stored then it is closed.
get_file("/etc/login.defs"); // login.defs is closed
// Now use fd1 to hold a file descriptor for /etc/passwd.
fd1 = get_file("/etc/passwd");
// The nsAutoRef<nsRawFD> can give up the file descriptor if explicitly
// instructed, but the caller must then ensure that the file is closed.
int rawfd = fd1.disown();
// Assume ownership of another file descriptor.
fd1.own(open("/proc/1/maps");
// On destruction, fd1 closes /proc/1/maps and fd2 closes /etc/hosts,
// but /etc/passwd is not closed.
}
nsAutoPtr/nsAutoArrayPtr
nsAutoPtr is an smart pointer that hold a reference to a resource that allocated by new operator. Assignment of a nsAutoPtr will transfer the ownership of the resource. nsAutoPtr cannot be the return value type of a function. nsAutoArrayPtr is the smart pointer for array that allocated by new [] operator.
class Foo {};
nsAutoPtr<Foo> autoPtr = new Foo();
nsAutoPtr<Foo> autoPtr2 = autoPtr; // autoPtr no longer point to the Foo object.
void someGetterFunction(Foo**); // function using output parameter
nsAutoPtr<Foo> outputPtr;
someGetterFunction(getter_Transfers(outputPtr));
nsAutoArrayPtr<uint8_t> iarray = new uint8_t[10]; // delete [] will be invoked after exiting scope
nsAutoArrayPtr<uint8_t> iarray2 = iarray; // transfer uint8_t[10] from iarray to iarray2
Cycle collector
This section excerpts some contents in [5], [6], [7]. 首先來討論環狀參照在哪種情況下會被視為Garbage. 先看一下[ 7 ].
What the cycle collector does
The cycle collector spends most of its time accumulating (and forgetting about) pointers to XPCOM objects that might be involved in garbage cycles. This is the idle stage of the collector's operation, in which special variants of nsAutoRefCnt register and unregister themselves very rapidly with the collector, as they pass through a "suspicious" refcount event (from N+1 to N, for nonzero N).
Periodically the collector wakes up and examines any suspicious pointers that have been sitting in its buffer for a while. This is the scanning stage of the collector's operation. In this stage the collector repeatedly asks each candidate for a singleton cycle-collection helper class, and if that helper exists, the collector asks the helper to describe the candidate's (owned) children. This way the collector builds a picture of the ownership subgraph reachable from suspicious objects.<>br
If the collector finds a group of objects that all refer back to one another, and establishes that the objects' reference counts are all accounted for by internal pointers within the group, it considers that group cyclical garbage, which it then attempts to free. This is the unlinking stage of the collectors operation. In this stage the collector walks through the garbage objects it has found, again consulting with their helper objects, asking the helper objects to "unlink" each object from its immediate children.
Note that the collector also knows how to walk through the JS heap, and can locate ownership cycles that pass in and out of it.
How the collector can fail
The cycle collector is a conservative device. There are situations in which it will fail to collect a garbage cycle.
- It does not suspect any pointers by default; objects must suspect themselves, typically by using an nsCycleCollectingAutoRefCnt rather than a nsAutoRefCnt.
- It only traverses objects that return a helper object when QI'ed to nsICycleCollectionParticipant. If it encounters an unknown edge during its traversal, it gives up on that edge; this means that every edge involved in a cycle must be participating, otherwise the cycle will not be found.
- The Traverse and Unlink methods on the helper object are not magic; they are programmer-supplied and must be correct, or else the collector will fail.
- The collector does not know how to find temporary owning pointers that exist on the stack, so it is important that it only run from near the top-loop of the program. It will not crash if there are extra owning pointers, but it will find itself unable to account for the reference counts it finds in the owned objects, so may fail to collect cycles.
How?
C++ objects participate in cycle collection by:
- Modifying their reference counting to use the cycle collector.
- Implementing a “cycle collection participant”, a set of functions that tell the cycle collector how to inspect the object.
- Modifying their QueryInterface implementation to return the participant when asked.
Like many things in Gecko, this involves lots of macros.
The reference counting is modified by replacing existing macros:
- NS_DECL_ISUPPORTS becomes NS_DECL_CYCLE_COLLECTING_ISUPPORTS.
- NS_IMPL_ADDREF becomes NS_IMPL_CYCLE_COLLECTING_ADDREF.
- NS_IMPL_RELEASE becomes NS_IMPL_CYCLE_COLLECTING_RELEASE.
The cycle collection participant is a helper class that provides up to three functions:
- A ‘Trace’ function is provided by participants that represent objects that use direct JavaScript object references. It reports those JavaScript references to the cycle collector.
- A ‘Traverse’ function is provided by all participants. It reports strong C++ references to the cycle collector,
- An ‘Unlink’ function is provided by (virtually) all participants. It clears out both JavaScript and C++ references, breaking the cycle.
The cycle collection participant is implemented by placing one of the following macros in the header:
- NS_DECL_CYCLE_COLLECTION_CLASS is the normal choice. It is used for classes that only have C++ references to report. This participant has Traverse and Unlink functions.
- NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS is a version of the previous macro for classes that multiply inherit from nsISupports.
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS is used for classes that have JS references or a mix of JS and C++ references to report. This participant has Trace, Traverse, and Unlink methods.
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS is the ambiguous version of the previous macro.
And by doing one of the following in the cpp file:
- For very simple classes, that don’t have JS references and only have nsCOMPtrs, you can use the NS_IMPL_CYCLE_COLLECTION_N macros, where N is the number of nsCOMPtrs the class has.
- For classes that almost meet the above requirements, but inherit from nsWrapperCache, you can use the NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_N macros, where N is the number of nsCOMPtrs the class has.
- Otherwise, use the NS_IMPL_CYCLE_COLLECTION_CLASS macro and separate macros to implement the Traverse, Unlink, and Trace (if appropriate) methods. To implement those, use the NS_IMPL_CYCLE_COLLECTION_[TRAVERSE|UNLINK|TRACE]_* macros to construct Traverse, Unlink, and Trace methods.
When?
C++ objects need to participate in cycle collection whenever they can be part of a reference cycle that is not guaranteed to be broken through other means. C++ objects also need to participate in cycle collection if they hold direct references to objects that are managed by the JavaScript garbage collector (a jsval, JS::Value, JSObject*, etc.).
In practice, this means most DOM objects need to be cycle collected.
- Does the object inherit from nsWrapperCache (directly or indirectly)? If so, it must be cycle collected.
- Does the object have direct references to JavaScript values (jsval, JS::Value, JSObject*, etc)? If so, it must be cycle collected. Note that interface pointers to interfaces implemented by JavaScript (e.g. nsIDOMEventListener) do *not* count here.
- Does the object hold no strong references (e.g. it has no member variables of type nsCOMPtr or nsRefPtr, it has no arrays of those (nsTArray<nsCOMPtr>, nsTArray<nsRefPtr>, or nsCOMArray), no hashtables of them (nsInterfaceHashtable, nsRefPtrHashtable), and does not directly own any object that has these (via new/delete or nsAutoPtr))? If so, it does not need to be cycle collected.
- Is the object threadsafe (e.g. an nsRunnable, or something that uses the threadsafe AddRef/Release macros)? Threadsafe objects cannot participate in cycle collection and must break ownership cycles manually.
- Is the object a service or other long lived object? Long lived objects should break ownership cycles manually. Adding cycle collection may prevent shutdown leaks, but it will just replace that with a leak until shutdown, which is just as bad but doesn’t show up on our tools.
- Does the object hold strong references to other things that are cycle collected? If so, and the object does not have a well-defined lifetime (e.g. it can be accessed from Javascript) it must be cycle collected.
- Does the object have strong references only to other things that are not cycle collected (e.g. interfaces from XPCOM, Necko, etc)? If so, it probably does not need to be cycle collected.
- Can the object be accessed from Javascript? Then it probably needs to be cycle collected.
The last two are kind of vague on purpose. Determining exactly when a class needs to participate in cycle collection is a bit tricky and involves some engineering judgement. If you’re not sure, ask your reviewer or relevant peers/module owners.
How to make your classes participate
Assuming you are modifying class nsFoo with two nsCOMPtr edges mBar and mBaz, the process can be distilled to a few simple modifications:
- Include the header nsCycleCollectionParticipant.h in both nsFoo.h and nsFoo.cpp.
- Add a line "NS_IMPL_CYCLE_COLLECTION_CLASS(nsFoo)" declaring that your class nsFoo participates in the cycle collection in nsFoo.cpp
- Change the line NS_DECL_ISUPPORTS to NS_DECL_CYCLE_COLLECTING_ISUPPORTS in the definition of nsFoo.
- Add a line NS_DECL_CYCLE_COLLECTION_CLASS(nsFoo) within the public portion of definition of nsFoo. Or NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFoo, nsIBar) if nsFoo inherits from multiple interfaces, where nsIBar is the interface which is returned when you QueryInterface nsFoo to nsISupports. (We call nsIBar the canonical ISupports type for nsFoo.)
- Add a line NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsFoo) to the interface map of nsFoo in nsFoo.cpp. Or if that doesn't work:
- Change the line NS_IMPL_ADDREF(nsFoo) to NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFoo) in nsFoo.cpp, and similarly change the line NS_IMPL_RELEASE(nsFoo) to NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFoo) in nsFoo.cpp.
- Add the appropriate NS_IMPL_CYCLE_COLLECTION_# macro, where # is the number of member variables in your class. For instance, if nsFoo contains two member variables, mBar and mBaz, we'd add NS_IMPL_CYCLE_COLLECTION_2(nsFoo, mBar, mBaz) in nsFoo.cpp.
NS_DECL_CYCLE_COLLECTION_CLASS(The simplest case)
// The simplest case:
// Accessible.h
class Accessible : public nsISupports
{
public:
Accessible(nsIContent* aContent, DocAccessible* aDoc);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(Accessible)
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCESSIBLE_IMPL_IID)
...
// Data Members
nsCOMPtr<nsIContent> mContent;
DocAccessible* mDoc;
nsRefPtr<Accessible> mParent;
nsTArray<nsRefPtr<Accessible> > mChildren;
int32_t mIndexInParent;
...
};
class Accessible : public nsISupports
{
public:
Accessible(nsIContent* aContent, DocAccessible* aDoc);
public: \
virtual nsresult QueryInterface(const nsIID& aIID, \
void** aInstancePtr) ; \
virtual MozExternalRefCountType AddRef(void) ; \
virtual MozExternalRefCountType Release(void) ; \
virtual void DeleteCycleCollectable(void); \
protected: \
nsCycleCollectingAutoRefCnt mRefCnt; \
\
public:
// NS_DECL_CYCLE_COLLECTING_ISUPPORTS
class cycleCollection \
: public nsXPCOMCycleCollectionParticipant \
{ \
public: \
virtual nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
; \
virtual void DeleteCycleCollectable(void *p) \
{ \
DowncastCCParticipant<Accessible>(p)->DeleteCycleCollectable(); \
} \
static Accessible* Downcast(nsISupports* s) \
{ \
return static_cast<Accessible*>(static_cast<Accessible*>(s)); \
} \
static nsISupports* Upcast(Accessible *p) \
{ \
return static_cast<nsISupports*>(static_cast<Accessible*>(p)); \
} \
template<typename T> \
friend nsISupports* \
ToSupports(T* p, cycleCollection* dummy); \
virtual void Unlink(void *p) ; \
static nsXPCOMCycleCollectionParticipant* GetParticipant() \
{ \
return &Accessible::_cycleCollectorGlobal; \
} \
}; \
\
static cycleCollection _cycleCollectorGlobal; \
// NS_DECL_CYCLE_COLLECTION_CLASS(Accessible)
#define NS_DECL_CYCLE_COLLECTION_CLASS(_class) \
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _class)
// See https://dxr.mozilla.org/mozilla-central/source/xpcom/glue/nsCycleCollectionNoteChild.h#50 for template<typename T> friend nsISupports* ToSupports(T* p, cycleCollection* dummy);
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCESSIBLE_IMPL_IID)
...
// Data Members
nsCOMPtr<nsIContent> mContent;
DocAccessible* mDoc;
nsRefPtr<Accessible> mParent;
nsTArray<nsRefPtr<Accessible> > mChildren;
int32_t mIndexInParent;
...
};
//-----------------------------------------CPP----------------------
// Accessible.cpp
////////////////////////////////////////////////////////////////////////////////
// Accessible: nsISupports and cycle collection
NS_IMPL_CYCLE_COLLECTION(Accessible,
mContent, mParent, mChildren)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Accessible)
if (aIID.Equals(NS_GET_IID(Accessible)))
foundInterface = this;
else
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, Accessible)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessible)
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
Accessible::cycleCollection Accessible::_cycleCollectorGlobal; \
void \
Accessible::cycleCollection::Unlink(void *p) \
{ \
Accessible *tmp = DowncastCCParticipant<Accessible >(p); \
static_assert( \
sizeof("(mContent, mParent, mChildren)") != sizeof("()") && \
(13) > 10 && \
(int)(0.03) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mContent); ImplCycleCollectionUnlink(tmp->mParent); ImplCycleCollectionUnlink(tmp->mChildren); \
(void)tmp; \
} \
nsresult \
Accessible::cycleCollection::Traverse \
(void *p, nsCycleCollectionTraversalCallback &cb) \
{ \
Accessible *tmp = DowncastCCParticipant<Accessible >(p); \
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "Accessible"); \
static_assert( \
sizeof("(mContent, mParent, mChildren)") != sizeof("()") && \
(13) > 10 && \
(int)(0.03) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mContent, "mContent", 0); ImplCycleCollectionTraverse(cb, tmp->mParent, "mParent", 0); ImplCycleCollectionTraverse(cb, tmp->mChildren, "mChildren", 0); \
(void)tmp; \
return NS_OK; \
}
// NS_IMPL_CYCLE_COLLECTION(Accessible,
// mContent, mParent, mChildren)
// See https://dxr.mozilla.org/mozilla-central/source/xpcom/glue/nsCOMPtr.h#1007 and https://dxr.mozilla.org/mozilla-central/source/xpcom/base/nsRefPtr.h#317 for ImplCycleCollectionUnlink
// See https://dxr.mozilla.org/mozilla-central/source/xpcom/glue/nsCycleCollectionTraversalCallback.h#15 for nsCycleCollectionTraversalCallback
// See https://dxr.mozilla.org/mozilla-central/source/xpcom/glue/nsCOMPtr.h#1014 and https://dxr.mozilla.org/mozilla-central/source/xpcom/base/nsRefPtr.h#324 for ImplCycleCollectionTraverse
// See https://dxr.mozilla.org/mozilla-central/source/xpcom/glue/nsCycleCollectionNoteChild.h#86 for the implementation of ImplCycleCollectionTraverse.
nsresult Accessible::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
{ \
do { /* nothing */ } while(0); \
nsISupports* foundInterface; \
if ( aIID.Equals((nsXPCOMCycleCollectionParticipant::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>::kIID)) ) { \
*aInstancePtr = Accessible::cycleCollection::GetParticipant(); \
return NS_OK; \
} else \
if ( aIID.Equals((nsCycleCollectionISupports::COMTypeInfo<nsCycleCollectionISupports, void>::kIID)) ) { \
*aInstancePtr = Accessible::cycleCollection::Upcast(this); \
return NS_OK; \
} else
// NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Accessible)
if (aIID.Equals(NS_GET_IID(Accessible)))
foundInterface = this;
else
// NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, Accessible)
foundInterface = 0; \
nsresult status; \
if ( !foundInterface ) \
{ \
/* nsISupports should be handled by this point. If not, fail. */ \
do { } while (0); \
status = NS_NOINTERFACE; \
} \
else \
{ \
(foundInterface)->AddRef(); \
status = NS_OK; \
} \
*aInstancePtr = foundInterface; \
return status; \
}
// NS_INTERFACE_MAP_END
MozExternalRefCountType Accessible::AddRef(void) \
{ \
\
do { } while (0); \
((void)0); \
nsISupports *base = Accessible::cycleCollection::Upcast(this); \
nsrefcnt count = mRefCnt.incr(base); \
; \
return count; \
}
// NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessible)
MozExternalRefCountType Accessible::Release(void) \
{ \
do { } while (0); \
((void)0); \
nsISupports *base = Accessible::cycleCollection::Upcast(this); \
nsrefcnt count = mRefCnt.decr(base); \
; \
return count; \
} \
void Accessible::DeleteCycleCollectable(void) \
{ \
LastRelease(); \
}
// NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
#define NS_IMPL_CYCLE_COLLECTING_RELEASE(_class) \
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, delete (this))
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED
這跟NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS的差別在你需要一個父類別是CYCLE COLLECTION CLASS 且你想用父類別的Upcast DeletCycleCollectable就可以用這個NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED In this case, CustomEvent inherited from Event and nsIDOMCustomEvent. And Event already call NS_DECL_CYCLE_COLLECTING_ISUPPORTS and NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Event). So we don't need to call NS_DECL_CYCLE_COLLECTING_ISUPPORTS in CustomEvent. Also in inherited case, we need to inherit the cycleCollection of parent class by calling NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED.
class CustomEvent MOZ_FINAL : public Event,
public nsIDOMCustomEvent
{
private:
virtual ~CustomEvent();
nsCOMPtr<nsIVariant> mDetail;
public:
explicit CustomEvent(mozilla::dom::EventTarget* aOwner,
nsPresContext* aPresContext = nullptr,
mozilla::WidgetEvent* aEvent = nullptr);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CustomEvent, Event)
NS_FORWARD_TO_EVENT
NS_DECL_NSIDOMCUSTOMEVENT
...
};
class CustomEvent MOZ_FINAL : public Event,
public nsIDOMCustomEvent
{
private:
...
/**
* Declare that you're going to inherit from something that already
* implements nsISupports, but also implements an additional interface, thus
* causing an ambiguity. In this case you don't need another mRefCnt, you
* just need to forward the definitions to the appropriate superclass. E.g.
*
* class Bar : public Foo, public nsIBar { // both provide nsISupports
* public:
* NS_DECL_ISUPPORTS_INHERITED
* ...other nsIBar and Bar methods...
* };
*/
public: \
virtual nsresult QueryInterface(const nsIID& aIID, \
void** aInstancePtr) ; \
virtual MozExternalRefCountType AddRef(void) ; \
virtual MozExternalRefCountType Release(void) ;
// NS_DECL_ISUPPORTS_INHERITED
class cycleCollection \
: public Event::cycleCollection \
{ \
public: \
public: \
virtual nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
; \
static CustomEvent* Downcast(nsISupports* s) \
{ \
return static_cast<CustomEvent*>(static_cast<Event*>( \
Event::cycleCollection::Downcast(s))); \
} \
virtual void Unlink(void *p) ; \
static nsXPCOMCycleCollectionParticipant* GetParticipant() \
{ \
return &CustomEvent::_cycleCollectorGlobal; \
} \
}; \
\
static cycleCollection _cycleCollectorGlobal;
\\NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED
...
};
// --------------------------------------------------------------
// CustomEvent.cpp
NS_IMPL_CYCLE_COLLECTION_CLASS(CustomEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CustomEvent, Event)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDetail)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CustomEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDetail)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(CustomEvent, Event)
NS_IMPL_RELEASE_INHERITED(CustomEvent, Event)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CustomEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMCustomEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)
CustomEvent::cycleCollection CustomEvent::_cycleCollectorGlobal;
//NS_IMPL_CYCLE_COLLECTION_CLASS(CustomEvent)
void \
CustomEvent::cycleCollection::Unlink(void *p) \
{ \
CustomEvent *tmp = DowncastCCParticipant<CustomEvent >(p); \
nsISupports *s = static_cast<nsISupports*>(p); \
Event::cycleCollection::Unlink(s);
//NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CustomEvent, Event)
static_assert( \
sizeof("(mDetail)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mDetail);
// NS_IMPL_CYCLE_COLLECTION_UNLINK(mDetail)
(void)tmp; \
}
//NS_IMPL_CYCLE_COLLECTION_UNLINK_END
nsresult \
CustomEvent::cycleCollection::Traverse \
(void *p, nsCycleCollectionTraversalCallback &cb) \
{ \
CustomEvent *tmp = DowncastCCParticipant<CustomEvent >(p); \
nsISupports *s = static_cast<nsISupports*>(p); \
if (Event::cycleCollection::Traverse(s, cb) \
== NS_SUCCESS_INTERRUPTED_TRAVERSE) { \
return NS_SUCCESS_INTERRUPTED_TRAVERSE; \
}
//NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CustomEvent, Event)
static_assert( \
sizeof("(mDetail)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mDetail, "mDetail", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDetail)
(void)tmp; \
return NS_OK; \
}
//NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
MozExternalRefCountType CustomEvent::AddRef(void) \
{ \
\
nsrefcnt r = Event::AddRef(); \
; \
return r; \
}
//NS_IMPL_ADDREF_INHERITED(CustomEvent, Event)
MozExternalRefCountType CustomEvent::Release(void) \
{ \
nsrefcnt r = Event::Release(); \
; \
return r; \
}
//NS_IMPL_RELEASE_INHERITED(CustomEvent, Event)
nsresult CustomEvent::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
{ \
do { /* nothing */ } while(0); \
nsISupports* foundInterface; \
if ( aIID.Equals((nsXPCOMCycleCollectionParticipant::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>::kIID)) ) { \
*aInstancePtr = CustomEvent::cycleCollection::GetParticipant(); \
return NS_OK; \
} else
//NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CustomEvent)
if ( aIID.Equals((nsIDOMCustomEvent::COMTypeInfo<nsIDOMCustomEvent, void>::kIID)) ) \
foundInterface = static_cast<nsIDOMCustomEvent*>(this); \
else
// NS_INTERFACE_MAP_ENTRY(nsIDOMCustomEvent)
foundInterface = 0; \
nsresult status; \
if ( !foundInterface ) \
status = Event::QueryInterface(aIID, (void**)&foundInterface); \
else \
{ \
(foundInterface)->AddRef(); \
status = NS_OK; \
} \
*aInstancePtr = foundInterface; \
return status; \
}
//NS_INTERFACE_MAP_END_INHERITING(Event)
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS
class CameraPermissionRequest : public nsIContentPermissionRequest
, public nsIRunnable
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSICONTENTPERMISSIONREQUEST
NS_DECL_NSIRUNNABLE
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CameraPermissionRequest,
nsIContentPermissionRequest)
...
protected:
...
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsPIDOMWindow> mWindow;
nsRefPtr<nsDOMCameraManager> mCameraManager;
uint32_t mCameraId;
CameraConfiguration mInitialConfig;
nsRefPtr<Promise> mPromise;
};
NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mPromise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest)
NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraPermissionRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraPermissionRequest)
class CameraPermissionRequest : public nsIContentPermissionRequest
, public nsIRunnable
{
public:
public: \
virtual nsresult QueryInterface(const nsIID& aIID, \
void** aInstancePtr) ; \
virtual MozExternalRefCountType AddRef(void) ; \
virtual MozExternalRefCountType Release(void) ; \
virtual void DeleteCycleCollectable(void); \
protected: \
nsCycleCollectingAutoRefCnt mRefCnt; \
\
public:
// NS_DECL_CYCLE_COLLECTING_ISUPPORTS
virtual nsresult GetTypes(nsIArray * *aTypes) ; \
virtual nsresult GetPrincipal(nsIPrincipal * *aPrincipal) ; \
virtual nsresult GetWindow(nsIDOMWindow * *aWindow) ; \
virtual nsresult GetElement(nsIDOMElement * *aElement) ; \
virtual nsresult Cancel(void) ; \
virtual nsresult Allow(JS::HandleValue choices) ;
// NS_DECL_NSICONTENTPERMISSIONREQUEST
virtual nsresult Run(void) ;
// NS_DECL_NSIRUNNABLE
class cycleCollection \
: public nsXPCOMCycleCollectionParticipant \
{ \
public: \
virtual nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
; \
virtual void DeleteCycleCollectable(void *p) \
{ \
DowncastCCParticipant<CameraPermissionRequest>(p)->DeleteCycleCollectable(); \
} \
static CameraPermissionRequest* Downcast(nsISupports* s) \
{ \
return static_cast<CameraPermissionRequest*>(static_cast<nsIContentPermissionRequest*>(s)); \
} \
static nsISupports* Upcast(CameraPermissionRequest *p) \
{ \
return static_cast<nsISupports*>(static_cast<nsIContentPermissionRequest*>(p)); \
} \
template<typename T> \
friend nsISupports* \
ToSupports(T* p, cycleCollection* dummy); \
virtual void Unlink(void *p) ; \
static nsXPCOMCycleCollectionParticipant* GetParticipant() \
{ \
return &CameraPermissionRequest::_cycleCollectorGlobal; \
} \
}; \
\
static cycleCollection _cycleCollectorGlobal; \
// NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(CameraPermissionRequest,
nsIContentPermissionRequest)
...
protected:
...
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsPIDOMWindow> mWindow;
nsRefPtr<nsDOMCameraManager> mCameraManager;
uint32_t mCameraId;
CameraConfiguration mInitialConfig;
nsRefPtr<Promise> mPromise;
};
//-----------------------------------------CPP----------------------
CameraPermissionRequest::cycleCollection CameraPermissionRequest::_cycleCollectorGlobal; \
void \
CameraPermissionRequest::cycleCollection::Unlink(void *p) \
{ \
CameraPermissionRequest *tmp = DowncastCCParticipant<CameraPermissionRequest >(p); \
static_assert( \
sizeof("(mWindow, mPromise)") != sizeof("()") && \
(12) > 10 && \
(int)(0.02) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mWindow); ImplCycleCollectionUnlink(tmp->mPromise); \
(void)tmp; \
} \
nsresult \
CameraPermissionRequest::cycleCollection::Traverse \
(void *p, nsCycleCollectionTraversalCallback &cb) \
{ \
CameraPermissionRequest *tmp = DowncastCCParticipant<CameraPermissionRequest >(p); \
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "CameraPermissionRequest"); \
static_assert( \
sizeof("(mWindow, mPromise)") != sizeof("()") && \
(12) > 10 && \
(int)(0.02) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mWindow, "mWindow", 0); ImplCycleCollectionTraverse(cb, tmp->mPromise, "mPromise", 0); \
(void)tmp; \
return NS_OK; \
}
// NS_IMPL_CYCLE_COLLECTION(CameraPermissionRequest, mWindow, mPromise)
nsresult CameraPermissionRequest::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
{ \
do { /* nothing */ } while(0); \
nsISupports* foundInterface; \
if ( aIID.Equals((nsXPCOMCycleCollectionParticipant::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>::kIID)) ) { \
*aInstancePtr = CameraPermissionRequest::cycleCollection::GetParticipant(); \
return NS_OK; \
} else \
if ( aIID.Equals((nsCycleCollectionISupports::COMTypeInfo<nsCycleCollectionISupports, void>::kIID)) ) { \
*aInstancePtr = CameraPermissionRequest::cycleCollection::Upcast(this); \
return NS_OK; \
} else
// NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraPermissionRequest)
if ( aIID.Equals((nsIContentPermissionRequest::COMTypeInfo<nsIContentPermissionRequest, void>::kIID)) ) \
foundInterface = static_cast<nsIContentPermissionRequest*>(this); \
else
// NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
if ( aIID.Equals((nsIRunnable::COMTypeInfo<nsIRunnable, void>::kIID)) ) \
foundInterface = static_cast<nsIRunnable*>(this); \
else
// NS_INTERFACE_MAP_ENTRY(nsIRunnable)
if ( aIID.Equals((nsISupports::COMTypeInfo<nsISupports, void>::kIID)) ) \
foundInterface = static_cast<nsISupports*>( \
static_cast<nsIContentPermissionRequest*>(this)); \
else
// NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
foundInterface = 0; \
nsresult status; \
if ( !foundInterface ) \
{ \
/* nsISupports should be handled by this point. If not, fail. */ \
do { } while (0); \
status = NS_NOINTERFACE; \
} \
else \
{ \
(foundInterface)->AddRef(); \
status = NS_OK; \
} \
*aInstancePtr = foundInterface; \
return status; \
}
// NS_INTERFACE_MAP_END
MozExternalRefCountType CameraPermissionRequest::AddRef(void) \
{ \
\
do { } while (0); \
((void)0); \
nsISupports *base = CameraPermissionRequest::cycleCollection::Upcast(this); \
nsrefcnt count = mRefCnt.incr(base); \
; \
return count; \
}
// NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraPermissionRequest)
MozExternalRefCountType CameraPermissionRequest::Release(void) \
{ \
do { } while (0); \
((void)0); \
nsISupports *base = CameraPermissionRequest::cycleCollection::Upcast(this); \
nsrefcnt count = mRefCnt.decr(base); \
; \
return count; \
} \
void CameraPermissionRequest::DeleteCycleCollectable(void) \
{ \
delete (this); \
}
// NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraPermissionRequest)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS
See [8] for the details of Wrapper Cache.
class EventBase : public nsIDOMEvent
{
};
class Event : public EventBase,
public nsWrapperCache
{
...
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Event)
...
mozilla::WidgetEvent* mEvent;
nsRefPtr<nsPresContext> mPresContext;
nsCOMPtr<EventTarget> mExplicitOriginalTarget;
nsCOMPtr<nsIGlobalObject> mOwner;
bool mEventIsInternal;
bool mPrivateDataDuplicated;
bool mIsMainThreadEvent;
// True when popup control check should rely on event.type, not
// WidgetEvent.message.
bool mWantsPopupControlCheck;
};
class Event : public EventBase,
public nsWrapperCache
{
...
public: \
virtual nsresult QueryInterface(const nsIID& aIID, \
void** aInstancePtr) ; \
virtual MozExternalRefCountType AddRef(void) ; \
virtual MozExternalRefCountType Release(void) ; \
virtual void DeleteCycleCollectable(void); \
protected: \
nsCycleCollectingAutoRefCnt mRefCnt; \
\
public:
// NS_DECL_CYCLE_COLLECTING_ISUPPORTS
class cycleCollection \
: public nsXPCOMCycleCollectionParticipant \
{ \
public: \
virtual nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
; \
virtual void DeleteCycleCollectable(void *p) \
{ \
DowncastCCParticipant<Event>(p)->DeleteCycleCollectable(); \
} \
static Event* Downcast(nsISupports* s) \
{ \
return static_cast<Event*>(static_cast<Event*>(s)); \
} \
static nsISupports* Upcast(Event *p) \
{ \
return static_cast<nsISupports*>(static_cast<Event*>(p)); \
} \
template<typename T> \
friend nsISupports* \
ToSupports(T* p, cycleCollection* dummy); \
virtual void Unlink(void *p) ; \
virtual void Trace(void *p, const TraceCallbacks &cb, void *closure) \
; \
static nsXPCOMCycleCollectionParticipant* GetParticipant() \
{ \
return &Event::_cycleCollectorGlobal; \
} \
}; \
\
static cycleCollection _cycleCollectorGlobal; \
// NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Event)
...
mozilla::WidgetEvent* mEvent;
nsRefPtr<nsPresContext> mPresContext;
nsCOMPtr<EventTarget> mExplicitOriginalTarget;
nsCOMPtr<nsIGlobalObject> mOwner;
bool mEventIsInternal;
bool mPrivateDataDuplicated;
bool mIsMainThreadEvent;
// True when popup control check should rely on event.type, not
// WidgetEvent.message.
bool mWantsPopupControlCheck;
};
//-----------------------------------------CPP----------------------
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Event)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIDOMEvent)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Event)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Event)
NS_IMPL_CYCLE_COLLECTION_CLASS(Event)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Event)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event)
if (tmp->mEventIsInternal) {
tmp->mEvent->target = nullptr;
tmp->mEvent->currentTarget = nullptr;
tmp->mEvent->originalTarget = nullptr;
switch (tmp->mEvent->mClass) {
case eMouseEventClass:
case eMouseScrollEventClass:
case eWheelEventClass:
case eSimpleGestureEventClass:
case ePointerEventClass:
tmp->mEvent->AsMouseEventBase()->relatedTarget = nullptr;
break;
case eDragEventClass: {
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
dragEvent->dataTransfer = nullptr;
dragEvent->relatedTarget = nullptr;
break;
}
case eClipboardEventClass:
tmp->mEvent->AsClipboardEvent()->clipboardData = nullptr;
break;
case eMutationEventClass:
tmp->mEvent->AsMutationEvent()->mRelatedNode = nullptr;
break;
case eFocusEventClass:
tmp->mEvent->AsFocusEvent()->relatedTarget = nullptr;
break;
default:
break;
}
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPresContext);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mExplicitOriginalTarget);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
if (tmp->mEventIsInternal) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->target)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->currentTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->originalTarget)
switch (tmp->mEvent->mClass) {
case eMouseEventClass:
case eMouseScrollEventClass:
case eWheelEventClass:
case eSimpleGestureEventClass:
case ePointerEventClass:
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget");
cb.NoteXPCOMChild(tmp->mEvent->AsMouseEventBase()->relatedTarget);
break;
case eDragEventClass: {
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->dataTransfer");
cb.NoteXPCOMChild(dragEvent->dataTransfer);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget");
cb.NoteXPCOMChild(dragEvent->relatedTarget);
break;
}
case eClipboardEventClass:
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->clipboardData");
cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->clipboardData);
break;
case eMutationEventClass:
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedNode");
cb.NoteXPCOMChild(tmp->mEvent->AsMutationEvent()->mRelatedNode);
break;
case eFocusEventClass:
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget");
cb.NoteXPCOMChild(tmp->mEvent->AsFocusEvent()->relatedTarget);
break;
default:
break;
}
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
//------Macro Expansion----------
nsresult Event::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
{ \
do { /* nothing */ } while(0); \
nsISupports* foundInterface; \
if ( aIID.Equals((nsXPCOMCycleCollectionParticipant::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>::kIID)) ) { \
*aInstancePtr = Event::cycleCollection::GetParticipant(); \
return NS_OK; \
} else \
if ( aIID.Equals((nsCycleCollectionISupports::COMTypeInfo<nsCycleCollectionISupports, void>::kIID)) ) { \
*aInstancePtr = Event::cycleCollection::Upcast(this); \
return NS_OK; \
} else
//NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Event)
if ( aIID.Equals((nsWrapperCache::COMTypeInfo<nsWrapperCache, void>::kIID)) ) { \
*aInstancePtr = static_cast<nsWrapperCache*>(this); \
return NS_OK; \
} \
else
// NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
if ( aIID.Equals((nsISupports::COMTypeInfo<nsISupports, void>::kIID)) ) \
foundInterface = static_cast<nsISupports*>(this); \
else
// NS_INTERFACE_MAP_ENTRY(nsISupports)
if ( aIID.Equals((nsIDOMEvent::COMTypeInfo<nsIDOMEvent, void>::kIID)) ) \
foundInterface = static_cast<nsIDOMEvent*>(this); \
else
// NS_INTERFACE_MAP_ENTRY(nsIDOMEvent)
foundInterface = 0; \
nsresult status; \
if ( !foundInterface ) \
{ \
/* nsISupports should be handled by this point. If not, fail. */ \
do { } while (0); \
status = NS_NOINTERFACE; \
} \
else \
{ \
(foundInterface)->AddRef(); \
status = NS_OK; \
} \
*aInstancePtr = foundInterface; \
return status; \
}
//NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Event)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Event)
Event::cycleCollection Event::_cycleCollectorGlobal;
//NS_IMPL_CYCLE_COLLECTION_CLASS(Event)
void \
Event::cycleCollection::Trace(void *p, \
const TraceCallbacks &aCallbacks, \
void *aClosure) \
{ \
Event *tmp = DowncastCCParticipant<Event >(p);
//NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Event)
tmp->TraceWrapper(aCallbacks, aClosure);
// NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
(void)tmp; \
}
//NS_IMPL_CYCLE_COLLECTION_TRACE_END
void \
Event::cycleCollection::Unlink(void *p) \
{ \
Event *tmp = DowncastCCParticipant<Event >(p);
//NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event)
if (tmp->mEventIsInternal) {
tmp->mEvent->target = nullptr;
tmp->mEvent->currentTarget = nullptr;
tmp->mEvent->originalTarget = nullptr;
switch (tmp->mEvent->mClass) {
case eMouseEventClass:
case eMouseScrollEventClass:
case eWheelEventClass:
case eSimpleGestureEventClass:
case ePointerEventClass:
tmp->mEvent->AsMouseEventBase()->relatedTarget = nullptr;
break;
case eDragEventClass: {
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
dragEvent->dataTransfer = nullptr;
dragEvent->relatedTarget = nullptr;
break;
}
case eClipboardEventClass:
tmp->mEvent->AsClipboardEvent()->clipboardData = nullptr;
break;
case eMutationEventClass:
tmp->mEvent->AsMutationEvent()->mRelatedNode = nullptr;
break;
case eFocusEventClass:
tmp->mEvent->AsFocusEvent()->relatedTarget = nullptr;
break;
default:
break;
}
}
static_assert( \
sizeof("(mPresContext)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mPresContext);
// NS_IMPL_CYCLE_COLLECTION_UNLINK(mPresContext);
static_assert( \
sizeof("(mExplicitOriginalTarget)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mExplicitOriginalTarget);
// NS_IMPL_CYCLE_COLLECTION_UNLINK(mExplicitOriginalTarget);
static_assert( \
sizeof("(mOwner)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mOwner);
// NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
tmp->ReleaseWrapper(p);
// NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
(void)tmp; \
}
//NS_IMPL_CYCLE_COLLECTION_UNLINK_END
nsresult \
Event::cycleCollection::Traverse \
(void *p, nsCycleCollectionTraversalCallback &cb) \
{ \
Event *tmp = DowncastCCParticipant<Event >(p); \
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), "Event");
//NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
if (tmp->mEventIsInternal) {
static_assert( \
sizeof("(mEvent->target)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mEvent->target, "mEvent->target", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->target)
static_assert( \
sizeof("(mEvent->currentTarget)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mEvent->currentTarget, "mEvent->currentTarget", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->currentTarget)
static_assert( \
sizeof("(mEvent->originalTarget)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mEvent->originalTarget, "mEvent->originalTarget", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->originalTarget)
switch (tmp->mEvent->mClass) {
case eMouseEventClass:
case eMouseScrollEventClass:
case eWheelEventClass:
case eSimpleGestureEventClass:
case ePointerEventClass:
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget");
cb.NoteXPCOMChild(tmp->mEvent->AsMouseEventBase()->relatedTarget);
break;
case eDragEventClass: {
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->dataTransfer");
cb.NoteXPCOMChild(dragEvent->dataTransfer);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget");
cb.NoteXPCOMChild(dragEvent->relatedTarget);
break;
}
case eClipboardEventClass:
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->clipboardData");
cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->clipboardData);
break;
case eMutationEventClass:
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedNode");
cb.NoteXPCOMChild(tmp->mEvent->AsMutationEvent()->mRelatedNode);
break;
case eFocusEventClass:
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget");
cb.NoteXPCOMChild(tmp->mEvent->AsFocusEvent()->relatedTarget);
break;
default:
break;
}
}
static_assert( \
sizeof("(mPresContext)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mPresContext, "mPresContext", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext)
static_assert( \
sizeof("(mExplicitOriginalTarget)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mExplicitOriginalTarget, "mExplicitOriginalTarget", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget)
static_assert( \
sizeof("(mOwner)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mOwner, "mOwner", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
{ \
TraceCallbackFunc noteJsChild(&nsScriptObjectTracer::NoteJSChild); \
Trace(p, noteJsChild, &cb); \
}
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
(void)tmp; \
return NS_OK; \
}
//NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED
有用JS::Heap<JS::Value> mData;所以需要用NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK在|Trace| 在|Unlink|要呼叫JS::Heap<JS::Value>::setUndefined();
class MessageEvent MOZ_FINAL : public Event,
public nsIDOMMessageEvent
{
public:
...
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MessageEvent, Event)
...
private:
JS::Heap<JS::Value> mData;
nsString mOrigin;
nsString mLastEventId;
nsCOMPtr<nsIDOMWindow> mWindowSource;
nsRefPtr<MessagePortBase> mPortSource;
nsRefPtr<MessagePortList> mPorts;
};
// Macro Expansion
class MessageEvent MOZ_FINAL : public Event,
public nsIDOMMessageEvent
{
public:
...
public: \
virtual nsresult QueryInterface(const nsIID& aIID, \
void** aInstancePtr) ; \
virtual MozExternalRefCountType AddRef(void) ; \
virtual MozExternalRefCountType Release(void) ;
// NS_DECL_ISUPPORTS_INHERITED
class cycleCollection \
: public Event::cycleCollection \
{ \
public: \
virtual nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
; \
static MessageEvent* Downcast(nsISupports* s) \
{ \
return static_cast<MessageEvent*>(static_cast<Event*>( \
Event::cycleCollection::Downcast(s))); \
} \
virtual void Unlink(void *p) ; \
virtual void Trace(void *p, const TraceCallbacks &cb, void *closure) \
; \
static nsXPCOMCycleCollectionParticipant* GetParticipant() \
{ \
return &MessageEvent::_cycleCollectorGlobal; \
} \
}; \
\
static cycleCollection _cycleCollectorGlobal;
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MessageEvent, Event)
...
private:
JS::Heap<JS::Value> mData;
nsString mOrigin;
nsString mLastEventId;
nsCOMPtr<nsIDOMWindow> mWindowSource;
nsRefPtr<MessagePortBase> mPortSource;
nsRefPtr<MessagePortList> mPorts;
};
//-----------------------------------------CPP----------------------
NS_IMPL_CYCLE_COLLECTION_CLASS(MessageEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessageEvent, Event)
tmp->mData.setUndefined();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowSource)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPortSource)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessageEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowSource)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPortSource)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MessageEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessageEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMMessageEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)
NS_IMPL_ADDREF_INHERITED(MessageEvent, Event)
NS_IMPL_RELEASE_INHERITED(MessageEvent, Event)
// Macro Expansion
MessageEvent::cycleCollection MessageEvent::_cycleCollectorGlobal;
//NS_IMPL_CYCLE_COLLECTION_CLASS(MessageEvent)
void \
MessageEvent::cycleCollection::Unlink(void *p) \
{ \
MessageEvent *tmp = DowncastCCParticipant<MessageEvent >(p); \
nsISupports *s = static_cast<nsISupports*>(p); \
Event::cycleCollection::Unlink(s);
//NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessageEvent, Event)
tmp->mData.setUndefined();
static_assert( \
sizeof("(mWindowSource)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mWindowSource);
// NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowSource)
static_assert( \
sizeof("(mPortSource)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mPortSource);
// NS_IMPL_CYCLE_COLLECTION_UNLINK(mPortSource)
static_assert( \
sizeof("(mPorts)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionUnlink(tmp->mPorts);
// NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts)
(void)tmp; \
}
//NS_IMPL_CYCLE_COLLECTION_UNLINK_END
nsresult \
MessageEvent::cycleCollection::Traverse \
(void *p, nsCycleCollectionTraversalCallback &cb) \
{ \
MessageEvent *tmp = DowncastCCParticipant<MessageEvent >(p); \
nsISupports *s = static_cast<nsISupports*>(p); \
if (Event::cycleCollection::Traverse(s, cb) \
== NS_SUCCESS_INTERRUPTED_TRAVERSE) { \
return NS_SUCCESS_INTERRUPTED_TRAVERSE; \
}
//NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessageEvent, Event)
static_assert( \
sizeof("(mWindowSource)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mWindowSource, "mWindowSource", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowSource)
static_assert( \
sizeof("(mPortSource)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mPortSource, "mPortSource", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPortSource)
static_assert( \
sizeof("(mPorts)") != sizeof("()") && \
(11) > 10 && \
(int)(0.01) == 0, \
"MOZ_STATIC_ASSERT_VALID_ARG_COUNT requires 1 to 50 arguments"); \
ImplCycleCollectionTraverse(cb, tmp->mPorts, "mPorts", 0);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
(void)tmp; \
return NS_OK; \
}
//NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
void \
MessageEvent::cycleCollection::Trace(void *p, \
const TraceCallbacks &aCallbacks, \
void *aClosure) \
{ \
MessageEvent *tmp = DowncastCCParticipant<MessageEvent >(p); \
nsISupports *s = static_cast<nsISupports*>(p); \
Event::cycleCollection::Trace(s, aCallbacks, aClosure);
//NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MessageEvent, Event)
aCallbacks.Trace(&tmp->mData, "mData", aClosure);
//NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData)
(void)tmp; \
}
//NS_IMPL_CYCLE_COLLECTION_TRACE_END
nsresult MessageEvent::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
{ \
do { /* nothing */ } while(0); \
nsISupports* foundInterface; \
if ( aIID.Equals((nsXPCOMCycleCollectionParticipant::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>::kIID)) ) { \
*aInstancePtr = MessageEvent::cycleCollection::GetParticipant(); \
return NS_OK; \
} else
//NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessageEvent)
if ( aIID.Equals((nsIDOMMessageEvent::COMTypeInfo<nsIDOMMessageEvent, void>::kIID)) ) \
foundInterface = static_cast<nsIDOMMessageEvent*>(this); \
else
// NS_INTERFACE_MAP_ENTRY(nsIDOMMessageEvent)
foundInterface = 0; \
nsresult status; \
if ( !foundInterface ) \
status = Event::QueryInterface(aIID, (void**)&foundInterface); \
else \
{ \
(foundInterface)->AddRef(); \
status = NS_OK; \
} \
*aInstancePtr = foundInterface; \
return status; \
}
//NS_INTERFACE_MAP_END_INHERITING(Event)
MozExternalRefCountType MessageEvent::AddRef(void) \
{ \
\
nsrefcnt r = Event::AddRef(); \
; \
return r; \
}
//NS_IMPL_ADDREF_INHERITED(MessageEvent, Event)
MozExternalRefCountType MessageEvent::Release(void) \
{ \
nsrefcnt r = Event::Release(); \
; \
return r; \
}
//NS_IMPL_RELEASE_INHERITED(MessageEvent, Event)
In summary (Pseudo Code)
if (Should_I_Use_CycleCollector) {
if (Own_JSValue_Or_WrapperCache) {
// Will add |Trace| for JS part cycle trace.
if (Parent_Is_CC) {
// Will use parent's mRefCount, |Travese|, |Trace| and |Unlink|
Use NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED
} else if (Parent_Mulitple_Inherited_From_nsIsupport){
// Should take care |QueryInterface|
Use NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS
} else {
// Need tale care |Trace| and |QueryInterface|
Use NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS
}
} else {
// No JS part
if (Parent_Is_CC) {
// Will use parent's mRefCount, |Travese| and |Unlink|
Use NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED
} else if (Parent_Mulitple_Inherited_From_nsIsupport){
// Should take care |QueryInterface|
Use NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS
} else {
// Simplest one
Use NS_DECL_CYCLE_COLLECTION_CLASS
}
}
}
Event
nsISupportImpl
Reference
[1]: Essential COM, 1998 by Don Box
[2]: Getting Started Guide
[3]: XPCOM ownership guidelines
[4]: the family of smart pointers in gecko
[5]: Interfacing with the XPCOM cycle collector
[6]: Cycle Collection
[7]: 淺談 Cycle Collection
[8]: DOM Object Reflection: How does it work?
Author
CTai