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