michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsAgg_h___ michael@0: #define nsAgg_h___ michael@0: michael@0: #include "nsISupports.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: // Put NS_DECL_AGGREGATED or NS_DECL_CYCLE_COLLECTING_AGGREGATED in your class's michael@0: // declaration. michael@0: #define NS_DECL_AGGREGATED \ michael@0: NS_DECL_ISUPPORTS \ michael@0: NS_DECL_AGGREGATED_HELPER michael@0: michael@0: #define NS_DECL_CYCLE_COLLECTING_AGGREGATED \ michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS \ michael@0: NS_DECL_AGGREGATED_HELPER michael@0: michael@0: #define NS_DECL_AGGREGATED_HELPER \ michael@0: public: \ michael@0: \ michael@0: /** \ michael@0: * Returns the nsISupports pointer of the inner object (aka the \ michael@0: * aggregatee). This pointer is really only useful to the outer object \ michael@0: * (aka the aggregator), which can use it to hold on to the inner \ michael@0: * object. Anything else wants the nsISupports pointer of the outer \ michael@0: * object (gotten by QI'ing inner or outer to nsISupports). This method \ michael@0: * returns a non-addrefed pointer. \ michael@0: * @return the nsISupports pointer of the inner object \ michael@0: */ \ michael@0: nsISupports* InnerObject(void) { return &fAggregated; } \ michael@0: \ michael@0: /** \ michael@0: * Returns true if this object is part of an aggregated object. \ michael@0: */ \ michael@0: bool IsPartOfAggregated(void) { return fOuter != InnerObject(); } \ michael@0: \ michael@0: private: \ michael@0: \ michael@0: /* You must implement this operation instead of the nsISupports */ \ michael@0: /* methods. */ \ michael@0: nsresult \ michael@0: AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr); \ michael@0: \ michael@0: class Internal : public nsISupports { \ michael@0: public: \ michael@0: \ michael@0: Internal() {} \ michael@0: \ michael@0: NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); \ michael@0: NS_IMETHOD_(MozExternalRefCountType) AddRef(void); \ michael@0: NS_IMETHOD_(MozExternalRefCountType) Release(void); \ michael@0: \ michael@0: NS_DECL_OWNINGTHREAD \ michael@0: }; \ michael@0: \ michael@0: friend class Internal; \ michael@0: \ michael@0: nsISupports* fOuter; \ michael@0: Internal fAggregated; \ michael@0: \ michael@0: public: \ michael@0: michael@0: #define NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(_class) \ michael@0: class NS_CYCLE_COLLECTION_INNERCLASS \ michael@0: : public nsXPCOMCycleCollectionParticipant \ michael@0: { \ michael@0: public: \ michael@0: NS_IMETHOD_(void) Unlink(void *p); \ michael@0: NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb); \ michael@0: NS_IMETHOD_(void) DeleteCycleCollectable(void* p) \ michael@0: { \ michael@0: NS_CYCLE_COLLECTION_CLASSNAME(_class):: \ michael@0: Downcast(static_cast(p))->DeleteCycleCollectable(); \ michael@0: } \ michael@0: static _class* Downcast(nsISupports* s) \ michael@0: { \ michael@0: return (_class*)((char*)(s) - offsetof(_class, fAggregated)); \ michael@0: } \ michael@0: static nsISupports* Upcast(_class *p) \ michael@0: { \ michael@0: return p->InnerObject(); \ michael@0: } \ michael@0: static nsXPCOMCycleCollectionParticipant* GetParticipant() \ michael@0: { \ michael@0: return &_class::NS_CYCLE_COLLECTION_INNERNAME; \ michael@0: } \ michael@0: }; \ michael@0: NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class); \ michael@0: static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; michael@0: michael@0: // Put this in your class's constructor: michael@0: #define NS_INIT_AGGREGATED(outer) \ michael@0: PR_BEGIN_MACRO \ michael@0: fOuter = outer ? outer : &fAggregated; \ michael@0: PR_END_MACRO michael@0: michael@0: michael@0: // Put this in your class's implementation file: michael@0: #define NS_IMPL_AGGREGATED(_class) \ michael@0: \ michael@0: NS_IMPL_AGGREGATED_HELPER(_class) \ michael@0: \ michael@0: NS_IMETHODIMP_(MozExternalRefCountType) \ michael@0: _class::Internal::AddRef(void) \ michael@0: { \ michael@0: _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \ michael@0: MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \ michael@0: NS_ASSERT_OWNINGTHREAD(_class); \ michael@0: ++agg->mRefCnt; \ michael@0: NS_LOG_ADDREF(this, agg->mRefCnt, #_class, sizeof(*this)); \ michael@0: return agg->mRefCnt; \ michael@0: } \ michael@0: \ michael@0: NS_IMETHODIMP_(MozExternalRefCountType) \ michael@0: _class::Internal::Release(void) \ michael@0: { \ michael@0: _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \ michael@0: MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release"); \ michael@0: NS_ASSERT_OWNINGTHREAD(_class); \ michael@0: --agg->mRefCnt; \ michael@0: NS_LOG_RELEASE(this, agg->mRefCnt, #_class); \ michael@0: if (agg->mRefCnt == 0) { \ michael@0: agg->mRefCnt = 1; /* stabilize */ \ michael@0: delete agg; \ michael@0: return 0; \ michael@0: } \ michael@0: return agg->mRefCnt; \ michael@0: } \ michael@0: michael@0: #define NS_IMPL_CYCLE_COLLECTING_AGGREGATED(_class) \ michael@0: \ michael@0: NS_IMPL_AGGREGATED_HELPER(_class) \ michael@0: \ michael@0: NS_IMETHODIMP_(MozExternalRefCountType) \ michael@0: _class::Internal::AddRef(void) \ michael@0: { \ michael@0: _class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \ michael@0: MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \ michael@0: NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \ michael@0: nsrefcnt count = agg->mRefCnt.incr(this); \ michael@0: NS_LOG_ADDREF(this, count, #_class, sizeof(*agg)); \ michael@0: return count; \ michael@0: } \ michael@0: NS_IMETHODIMP_(MozExternalRefCountType) \ michael@0: _class::Internal::Release(void) \ michael@0: { \ michael@0: _class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \ michael@0: MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release"); \ michael@0: NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \ michael@0: nsrefcnt count = agg->mRefCnt.decr(this); \ michael@0: NS_LOG_RELEASE(this, count, #_class); \ michael@0: return count; \ michael@0: } \ michael@0: NS_IMETHODIMP_(void) \ michael@0: _class::DeleteCycleCollectable(void) \ michael@0: { \ michael@0: delete this; \ michael@0: } michael@0: michael@0: #define NS_IMPL_AGGREGATED_HELPER(_class) \ michael@0: NS_IMETHODIMP \ michael@0: _class::QueryInterface(const nsIID& aIID, void** aInstancePtr) \ michael@0: { \ michael@0: return fOuter->QueryInterface(aIID, aInstancePtr); \ michael@0: } \ michael@0: \ michael@0: NS_IMETHODIMP_(MozExternalRefCountType) \ michael@0: _class::AddRef(void) \ michael@0: { \ michael@0: return fOuter->AddRef(); \ michael@0: } \ michael@0: \ michael@0: NS_IMETHODIMP_(MozExternalRefCountType) \ michael@0: _class::Release(void) \ michael@0: { \ michael@0: return fOuter->Release(); \ michael@0: } \ michael@0: \ michael@0: NS_IMETHODIMP \ michael@0: _class::Internal::QueryInterface(const nsIID& aIID, void** aInstancePtr) \ michael@0: { \ michael@0: _class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \ michael@0: return agg->AggregatedQueryInterface(aIID, aInstancePtr); \ michael@0: } \ michael@0: michael@0: /** michael@0: * To make aggregated objects participate in cycle collection we need to enable michael@0: * the outer object (aggregator) to traverse/unlink the objects held by the michael@0: * inner object (the aggregatee). We can't just make the inner object QI'able to michael@0: * NS_CYCLECOLLECTIONPARTICIPANT_IID, we don't want to return the inner object's michael@0: * nsCycleCollectionParticipant for the outer object (which will happen if the michael@0: * outer object doesn't participate in cycle collection itself). michael@0: * NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID enables the outer object to get michael@0: * the inner objects nsCycleCollectionParticipant. michael@0: * michael@0: * There are three cases: michael@0: * - No aggregation michael@0: * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will return the inner michael@0: * object's nsCycleCollectionParticipant. michael@0: * michael@0: * - Aggregation and outer object does not participate in cycle collection michael@0: * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will not return anything. michael@0: * michael@0: * - Aggregation and outer object does participate in cycle collection michael@0: * QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will return the outer michael@0: * object's nsCycleCollectionParticipant. The outer object's michael@0: * nsCycleCollectionParticipant can then QI the inner object to michael@0: * NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID to get the inner object's michael@0: * nsCycleCollectionParticipant, which it can use to traverse/unlink the michael@0: * objects reachable from the inner object. michael@0: */ michael@0: #define NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID \ michael@0: { \ michael@0: 0x32889b7e, \ michael@0: 0xe4fe, \ michael@0: 0x43f4, \ michael@0: { 0x85, 0x31, 0xb5, 0x28, 0x23, 0xa2, 0xe9, 0xfc } \ michael@0: } michael@0: michael@0: /** michael@0: * Just holds the IID so NS_GET_IID works. michael@0: */ michael@0: class nsAggregatedCycleCollectionParticipant michael@0: { michael@0: public: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID) michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(nsAggregatedCycleCollectionParticipant, michael@0: NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID) michael@0: michael@0: // for use with QI macros in nsISupportsUtils.h: michael@0: michael@0: #define NS_INTERFACE_MAP_BEGIN_AGGREGATED(_class) \ michael@0: NS_IMPL_AGGREGATED_QUERY_HEAD(_class) michael@0: michael@0: #define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_AGGREGATED(_class) \ michael@0: NS_IMPL_QUERY_CYCLE_COLLECTION(_class) michael@0: michael@0: #define NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(_class) \ michael@0: NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_AGGREGATED(_class) \ michael@0: NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class) michael@0: michael@0: #define NS_IMPL_AGGREGATED_QUERY_HEAD(_class) \ michael@0: nsresult \ michael@0: _class::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr) \ michael@0: { \ michael@0: NS_ASSERTION(aInstancePtr, \ michael@0: "AggregatedQueryInterface requires a non-NULL result ptr!"); \ michael@0: if ( !aInstancePtr ) \ michael@0: return NS_ERROR_NULL_POINTER; \ michael@0: nsISupports* foundInterface; \ michael@0: if ( aIID.Equals(NS_GET_IID(nsISupports)) ) \ michael@0: foundInterface = InnerObject(); \ michael@0: else michael@0: michael@0: #define NS_IMPL_AGGREGATED_QUERY_CYCLE_COLLECTION(_class) \ michael@0: if (aIID.Equals(IsPartOfAggregated() ? \ michael@0: NS_GET_IID(nsCycleCollectionParticipant) : \ michael@0: NS_GET_IID(nsAggregatedCycleCollectionParticipant))) \ michael@0: foundInterface = NS_CYCLE_COLLECTION_PARTICIPANT(_class); \ michael@0: else michael@0: michael@0: #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(_class) \ michael@0: NS_IMETHODIMP \ michael@0: NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse \ michael@0: (void *p, nsCycleCollectionTraversalCallback &cb) \ michael@0: { \ michael@0: nsISupports *s = static_cast(p); \ michael@0: MOZ_ASSERT(CheckForRightISupports(s), \ michael@0: "not the nsISupports pointer we expect"); \ michael@0: _class *tmp = static_cast<_class*>(Downcast(s)); \ michael@0: if (!tmp->IsPartOfAggregated()) \ michael@0: NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class, tmp->mRefCnt.get()) michael@0: michael@0: #define NS_GENERIC_AGGREGATED_CONSTRUCTOR(_InstanceClass) \ michael@0: static nsresult \ michael@0: _InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \ michael@0: void **aResult) \ michael@0: { \ michael@0: *aResult = nullptr; \ michael@0: if (NS_WARN_IF(aOuter && !aIID.Equals(NS_GET_IID(nsISupports)))) \ michael@0: return NS_ERROR_INVALID_ARG; \ michael@0: \ michael@0: _InstanceClass* inst = new _InstanceClass(aOuter); \ michael@0: if (!inst) { \ michael@0: return NS_ERROR_OUT_OF_MEMORY; \ michael@0: } \ michael@0: \ michael@0: nsISupports* inner = inst->InnerObject(); \ michael@0: nsresult rv = inner->QueryInterface(aIID, aResult); \ michael@0: if (NS_FAILED(rv)) { \ michael@0: delete inst; \ michael@0: } \ michael@0: \ michael@0: return rv; \ michael@0: } \ michael@0: michael@0: #define NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \ michael@0: static nsresult \ michael@0: _InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \ michael@0: void **aResult) \ michael@0: { \ michael@0: *aResult = nullptr; \ michael@0: if (NS_WARN_IF(aOuter && !aIID.Equals(NS_GET_IID(nsISupports)))) \ michael@0: return NS_ERROR_INVALID_ARG; \ michael@0: \ michael@0: _InstanceClass* inst = new _InstanceClass(aOuter); \ michael@0: if (!inst) { \ michael@0: return NS_ERROR_OUT_OF_MEMORY; \ michael@0: } \ michael@0: \ michael@0: nsISupports* inner = inst->InnerObject(); \ michael@0: NS_ADDREF(inner); \ michael@0: nsresult rv = inst->_InitMethod(); \ michael@0: if (NS_SUCCEEDED(rv)) { \ michael@0: rv = inner->QueryInterface(aIID, aResult); \ michael@0: } \ michael@0: NS_RELEASE(inner); \ michael@0: \ michael@0: return rv; \ michael@0: } \ michael@0: michael@0: #endif /* nsAgg_h___ */