/* -*- 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 nsCOMPtr_h___ #define nsCOMPtr_h___ /* * Having problems? * * See the documentation at: * https://firefox-source-docs.mozilla.org/xpcom/refptr.html * * * nsCOMPtr * better than a raw pointer * for owning objects * -- scc */ #include #include "mozilla/AlreadyAddRefed.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/RefPtr.h" #include "nsCycleCollectionNoteChild.h" #include "nsDebug.h" // for |NS_ASSERTION| #include "nsISupportsUtils.h" // for |nsresult|, |NS_ADDREF|, |NS_GET_TEMPLATE_IID| et al /* * WARNING: This file defines several macros for internal use only. These * macros begin with the prefix |NSCAP_|. Do not use these macros in your own * code. They are for internal use only for cross-platform compatibility, and * are subject to change without notice. */ #ifdef _MSC_VER // Under VC++, we win by inlining StartAssignment. # define NSCAP_FEATURE_INLINE_STARTASSIGNMENT // Also under VC++, at the highest warning level, we are overwhelmed with // warnings about (unused) inline functions being removed. This is to be // expected with templates, so we disable the warning. # pragma warning(disable : 4514) #endif #ifdef DEBUG # define NSCAP_FEATURE_TEST_DONTQUERY_CASES #endif #ifdef __GNUC__ // Our use of nsCOMPtr_base::mRawPtr violates the C++ standard's aliasing // rules. Mark it with the may_alias attribute so that gcc 3.3 and higher // don't reorder instructions based on aliasing assumptions for // this variable. Fortunately, gcc versions < 3.3 do not do any // optimizations that break nsCOMPtr. # define NS_MAY_ALIAS_PTR(t) t* __attribute__((__may_alias__)) #else # define NS_MAY_ALIAS_PTR(t) t* #endif /* * The following three macros (NSCAP_ADDREF, NSCAP_RELEASE, and * NSCAP_LOG_ASSIGNMENT) allow external clients the ability to add logging or * other interesting debug facilities. In fact, if you want |nsCOMPtr| to * participate in the standard logging facility, you provide * (e.g., in "nsISupportsImpl.h") suitable definitions * * #define NSCAP_ADDREF(this, ptr) NS_ADDREF(ptr) * #define NSCAP_RELEASE(this, ptr) NS_RELEASE(ptr) */ #ifndef NSCAP_ADDREF # define NSCAP_ADDREF(this, ptr) \ mozilla::RefPtrTraits< \ typename std::remove_reference::type>::AddRef(ptr) #endif #ifndef NSCAP_RELEASE # define NSCAP_RELEASE(this, ptr) \ mozilla::RefPtrTraits< \ typename std::remove_reference::type>::Release(ptr) #endif // Clients can define |NSCAP_LOG_ASSIGNMENT| to perform logging. #ifdef NSCAP_LOG_ASSIGNMENT // Remember that |NSCAP_LOG_ASSIGNMENT| was defined by some client so that we // know to instantiate |~nsGetterAddRefs| in turn to note the external // assignment into the |nsCOMPtr|. # define NSCAP_LOG_EXTERNAL_ASSIGNMENT #else // ...otherwise, just strip it out of the code # define NSCAP_LOG_ASSIGNMENT(this, ptr) #endif #ifndef NSCAP_LOG_RELEASE # define NSCAP_LOG_RELEASE(this, ptr) #endif namespace mozilla { template class OwningNonNull; } // namespace mozilla template inline already_AddRefed dont_AddRef(T* aRawPtr) { return already_AddRefed(aRawPtr); } template inline already_AddRefed&& dont_AddRef( already_AddRefed&& aAlreadyAddRefedPtr) { return std::move(aAlreadyAddRefedPtr); } /* * An nsCOMPtr_helper transforms commonly called getters into typesafe forms * that are more convenient to call, and more efficient to use with |nsCOMPtr|s. * Good candidates for helpers are |QueryInterface()|, |CreateInstance()|, etc. * * Here are the rules for a helper: * - it implements |operator()| to produce an interface pointer * - (except for its name) |operator()| is a valid [XP]COM `getter' * - the interface pointer that it returns is already |AddRef()|ed (as from * any good getter) * - it matches the type requested with the supplied |nsIID| argument * - its constructor provides an optional |nsresult*| that |operator()| can * fill in with an error when it is executed * * See |class nsGetInterface| for an example. */ class MOZ_STACK_CLASS nsCOMPtr_helper { public: virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const = 0; }; /* * nsQueryInterface could have been implemented as an nsCOMPtr_helper to avoid * adding specialized machinery in nsCOMPtr, but do_QueryInterface is called * often enough that the codesize savings are big enough to warrant the * specialcasing. */ class MOZ_STACK_CLASS nsQueryInterfaceISupports { public: explicit nsQueryInterfaceISupports(nsISupports* aRawPtr) : mRawPtr(aRawPtr) {} nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const; private: nsISupports* MOZ_OWNING_REF mRawPtr; }; template class MOZ_STACK_CLASS nsQueryInterface final : public nsQueryInterfaceISupports { public: explicit nsQueryInterface(T* aRawPtr) : nsQueryInterfaceISupports(ToSupports(aRawPtr)) {} nsresult NS_FASTCALL operator()(const nsIID& aIID, void** aAnswer) const { return nsQueryInterfaceISupports::operator()(aIID, aAnswer); } }; class MOZ_STACK_CLASS nsQueryInterfaceISupportsWithError { public: nsQueryInterfaceISupportsWithError(nsISupports* aRawPtr, nsresult* aError) : mRawPtr(aRawPtr), mErrorPtr(aError) {} nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const; private: nsISupports* MOZ_OWNING_REF mRawPtr; nsresult* mErrorPtr; }; template class MOZ_STACK_CLASS nsQueryInterfaceWithError final : public nsQueryInterfaceISupportsWithError { public: explicit nsQueryInterfaceWithError(T* aRawPtr, nsresult* aError) : nsQueryInterfaceISupportsWithError(ToSupports(aRawPtr), aError) {} nsresult NS_FASTCALL operator()(const nsIID& aIID, void** aAnswer) const { return nsQueryInterfaceISupportsWithError::operator()(aIID, aAnswer); } }; namespace mozilla { // PointedToType<> is needed so that do_QueryInterface() will work with a // variety of smart pointer types in addition to raw pointers. These types // include RefPtr<>, nsCOMPtr<>, and OwningNonNull<>. template using PointedToType = std::remove_pointer_t())>; } // namespace mozilla template inline nsQueryInterface> do_QueryInterface(T aPtr) { return nsQueryInterface>(aPtr); } template inline nsQueryInterfaceWithError> do_QueryInterface( T aRawPtr, nsresult* aError) { return nsQueryInterfaceWithError>(aRawPtr, aError); } template inline void do_QueryInterface(already_AddRefed&) { // This signature exists solely to _stop_ you from doing the bad thing. // Saying |do_QueryInterface()| on a pointer that is not otherwise owned by // someone else is an automatic leak. See bug 8221. } template inline void do_QueryInterface(already_AddRefed&, nsresult*) { // This signature exists solely to _stop_ you from doing the bad thing. // Saying |do_QueryInterface()| on a pointer that is not otherwise owned by // someone else is an automatic leak. See bug 8221. } //////////////////////////////////////////////////////////////////////////// // Using servicemanager with COMPtrs class nsGetServiceByCID final { public: explicit nsGetServiceByCID(const nsCID& aCID) : mCID(aCID) {} nsresult NS_FASTCALL operator()(const nsIID&, void**) const; private: const nsCID& mCID; }; class nsGetServiceByCIDWithError final { public: nsGetServiceByCIDWithError(const nsCID& aCID, nsresult* aErrorPtr) : mCID(aCID), mErrorPtr(aErrorPtr) {} nsresult NS_FASTCALL operator()(const nsIID&, void**) const; private: const nsCID& mCID; nsresult* mErrorPtr; }; class nsGetServiceByContractID final { public: explicit nsGetServiceByContractID(const char* aContractID) : mContractID(aContractID) {} nsresult NS_FASTCALL operator()(const nsIID&, void**) const; private: const char* mContractID; }; class nsGetServiceByContractIDWithError final { public: nsGetServiceByContractIDWithError(const char* aContractID, nsresult* aErrorPtr) : mContractID(aContractID), mErrorPtr(aErrorPtr) {} nsresult NS_FASTCALL operator()(const nsIID&, void**) const; private: const char* mContractID; nsresult* mErrorPtr; }; class nsIWeakReference; // Weak references class MOZ_STACK_CLASS nsQueryReferent final { public: nsQueryReferent(nsIWeakReference* aWeakPtr, nsresult* aError) : mWeakPtr(aWeakPtr), mErrorPtr(aError) {} nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const; private: nsIWeakReference* MOZ_NON_OWNING_REF mWeakPtr; nsresult* mErrorPtr; }; // template class nsGetterAddRefs; // Helper for assert_validity method template char (&TestForIID(decltype(&NS_GET_TEMPLATE_IID(T))))[2]; template char TestForIID(...); template class MOZ_IS_REFPTR nsCOMPtr final { private: void assign_with_AddRef(T*); template void assign_from_qi(const nsQueryInterface, const nsIID&); template void assign_from_qi_with_error(const nsQueryInterfaceWithError&, const nsIID&); void assign_from_gs_cid(const nsGetServiceByCID, const nsIID&); void assign_from_gs_cid_with_error(const nsGetServiceByCIDWithError&, const nsIID&); void assign_from_gs_contractid(const nsGetServiceByContractID, const nsIID&); void assign_from_gs_contractid_with_error( const nsGetServiceByContractIDWithError&, const nsIID&); void assign_from_query_referent(const nsQueryReferent&, const nsIID&); void assign_from_helper(const nsCOMPtr_helper&, const nsIID&); void** begin_assignment(); void assign_assuming_AddRef(T* aNewPtr) { T* oldPtr = mRawPtr; mRawPtr = aNewPtr; NSCAP_LOG_ASSIGNMENT(this, aNewPtr); NSCAP_LOG_RELEASE(this, oldPtr); if (oldPtr) { NSCAP_RELEASE(this, oldPtr); } } private: T* MOZ_OWNING_REF mRawPtr; void assert_validity() { static_assert(1 < sizeof(TestForIID(nullptr)), "nsCOMPtr only works " "for types with IIDs. Either use RefPtr; add an IID to " "your type with NS_DECLARE_STATIC_IID_ACCESSOR/" "NS_DEFINE_STATIC_IID_ACCESSOR; or make the nsCOMPtr point " "to a base class with an IID."); } public: typedef T element_type; ~nsCOMPtr() { NSCAP_LOG_RELEASE(this, mRawPtr); if (mRawPtr) { NSCAP_RELEASE(this, mRawPtr); } } #ifdef NSCAP_FEATURE_TEST_DONTQUERY_CASES void Assert_NoQueryNeeded() { if (!mRawPtr) { return; } if constexpr (std::is_same_v) { // FIXME: nsCOMPtr never asserted this, and it currently // fails... return; } // This can't be defined in terms of do_QueryInterface because // that bans casts from a class to itself. void* out = nullptr; mRawPtr->QueryInterface(NS_GET_TEMPLATE_IID(T), &out); T* query_result = static_cast(out); MOZ_ASSERT(query_result == mRawPtr, "QueryInterface needed"); NS_RELEASE(query_result); } # define NSCAP_ASSERT_NO_QUERY_NEEDED() Assert_NoQueryNeeded(); #else # define NSCAP_ASSERT_NO_QUERY_NEEDED() #endif // Constructors nsCOMPtr() : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); } MOZ_IMPLICIT nsCOMPtr(decltype(nullptr)) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); } nsCOMPtr(const nsCOMPtr& aSmartPtr) : mRawPtr(aSmartPtr.mRawPtr) { assert_validity(); if (mRawPtr) { NSCAP_ADDREF(this, mRawPtr); } NSCAP_LOG_ASSIGNMENT(this, aSmartPtr.mRawPtr); } template MOZ_IMPLICIT nsCOMPtr(const nsCOMPtr& aSmartPtr) : mRawPtr(aSmartPtr.get()) { // Make sure that U actually inherits from T static_assert(std::is_base_of::value, "U should be a subclass of T"); assert_validity(); if (mRawPtr) { NSCAP_ADDREF(this, mRawPtr); } NSCAP_LOG_ASSIGNMENT(this, aSmartPtr.get()); } nsCOMPtr(nsCOMPtr&& aSmartPtr) : mRawPtr(aSmartPtr.mRawPtr) { assert_validity(); aSmartPtr.mRawPtr = nullptr; NSCAP_LOG_ASSIGNMENT(this, mRawPtr); } template MOZ_IMPLICIT nsCOMPtr(nsCOMPtr&& aSmartPtr) : mRawPtr(aSmartPtr.forget().template downcast().take()) { // Make sure that U actually inherits from T static_assert(std::is_base_of::value, "U should be a subclass of T"); assert_validity(); NSCAP_LOG_ASSIGNMENT(this, mRawPtr); NSCAP_ASSERT_NO_QUERY_NEEDED(); } MOZ_IMPLICIT nsCOMPtr(T* aRawPtr) : mRawPtr(aRawPtr) { assert_validity(); if (mRawPtr) { NSCAP_ADDREF(this, mRawPtr); } NSCAP_LOG_ASSIGNMENT(this, aRawPtr); NSCAP_ASSERT_NO_QUERY_NEEDED(); } MOZ_IMPLICIT nsCOMPtr(already_AddRefed& aSmartPtr) : mRawPtr(aSmartPtr.take()) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, mRawPtr); NSCAP_ASSERT_NO_QUERY_NEEDED(); } // Construct from |otherComPtr.forget()|. MOZ_IMPLICIT nsCOMPtr(already_AddRefed&& aSmartPtr) : mRawPtr(aSmartPtr.take()) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, mRawPtr); NSCAP_ASSERT_NO_QUERY_NEEDED(); } // Construct from |std::move(otherRefPtr)|. template MOZ_IMPLICIT nsCOMPtr(RefPtr&& aSmartPtr) : mRawPtr(static_cast>(aSmartPtr.forget()).take()) { assert_validity(); // Make sure that U actually inherits from T static_assert(std::is_base_of::value, "U is not a subclass of T"); NSCAP_LOG_ASSIGNMENT(this, mRawPtr); NSCAP_ASSERT_NO_QUERY_NEEDED(); } // Construct from |already_AddRefed|. template MOZ_IMPLICIT nsCOMPtr(already_AddRefed& aSmartPtr) : mRawPtr(static_cast(aSmartPtr.take())) { assert_validity(); // But make sure that U actually inherits from T. static_assert(std::is_base_of::value, "U is not a subclass of T"); NSCAP_LOG_ASSIGNMENT(this, static_cast(mRawPtr)); NSCAP_ASSERT_NO_QUERY_NEEDED(); } // Construct from |otherComPtr.forget()|. template MOZ_IMPLICIT nsCOMPtr(already_AddRefed&& aSmartPtr) : mRawPtr(static_cast(aSmartPtr.take())) { assert_validity(); // But make sure that U actually inherits from T. static_assert(std::is_base_of::value, "U is not a subclass of T"); NSCAP_LOG_ASSIGNMENT(this, static_cast(mRawPtr)); NSCAP_ASSERT_NO_QUERY_NEEDED(); } // Construct from |do_QueryInterface(expr)|. template MOZ_IMPLICIT nsCOMPtr(const nsQueryInterface aQI) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); assign_from_qi(aQI, NS_GET_TEMPLATE_IID(T)); } // Construct from |do_QueryInterface(expr, &rv)|. template MOZ_IMPLICIT nsCOMPtr(const nsQueryInterfaceWithError& aQI) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); assign_from_qi_with_error(aQI, NS_GET_TEMPLATE_IID(T)); } // Construct from |do_GetService(cid_expr)|. MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCID aGS) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); assign_from_gs_cid(aGS, NS_GET_TEMPLATE_IID(T)); } // Construct from |do_GetService(cid_expr, &rv)|. MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCIDWithError& aGS) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); assign_from_gs_cid_with_error(aGS, NS_GET_TEMPLATE_IID(T)); } // Construct from |do_GetService(contractid_expr)|. MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractID aGS) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); assign_from_gs_contractid(aGS, NS_GET_TEMPLATE_IID(T)); } // Construct from |do_GetService(contractid_expr, &rv)|. MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractIDWithError& aGS) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); assign_from_gs_contractid_with_error(aGS, NS_GET_TEMPLATE_IID(T)); } // Construct from |do_QueryReferent(ptr)| MOZ_IMPLICIT nsCOMPtr(const nsQueryReferent& aQueryReferent) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); assign_from_query_referent(aQueryReferent, NS_GET_TEMPLATE_IID(T)); } // And finally, anything else we might need to construct from can exploit the // nsCOMPtr_helper facility. MOZ_IMPLICIT nsCOMPtr(const nsCOMPtr_helper& aHelper) : mRawPtr(nullptr) { assert_validity(); NSCAP_LOG_ASSIGNMENT(this, nullptr); assign_from_helper(aHelper, NS_GET_TEMPLATE_IID(T)); NSCAP_ASSERT_NO_QUERY_NEEDED(); } // construct from |mozilla::NotNull|. template > && std::is_convertible_v>>> MOZ_IMPLICIT nsCOMPtr(const mozilla::NotNull& aSmartPtr) : mRawPtr(nsCOMPtr(aSmartPtr.get()).forget().take()) {} // construct from |mozilla::MovingNotNull|. template > && std::is_convertible_v>>> MOZ_IMPLICIT nsCOMPtr(mozilla::MovingNotNull&& aSmartPtr) : mRawPtr( nsCOMPtr(std::move(aSmartPtr).unwrapBasePtr()).forget().take()) { } // Defined in OwningNonNull.h template MOZ_IMPLICIT nsCOMPtr(const mozilla::OwningNonNull& aOther); // Assignment operators nsCOMPtr& operator=(const nsCOMPtr& aRhs) { assign_with_AddRef(aRhs.mRawPtr); return *this; } template nsCOMPtr& operator=(const nsCOMPtr& aRhs) { // Make sure that U actually inherits from T static_assert(std::is_base_of::value, "U should be a subclass of T"); assign_with_AddRef(aRhs.get()); return *this; } nsCOMPtr& operator=(nsCOMPtr&& aRhs) { assign_assuming_AddRef(aRhs.forget().take()); return *this; } template nsCOMPtr& operator=(nsCOMPtr&& aRhs) { // Make sure that U actually inherits from T static_assert(std::is_base_of::value, "U should be a subclass of T"); assign_assuming_AddRef(aRhs.forget().template downcast().take()); NSCAP_ASSERT_NO_QUERY_NEEDED(); return *this; } nsCOMPtr& operator=(T* aRhs) { assign_with_AddRef(aRhs); NSCAP_ASSERT_NO_QUERY_NEEDED(); return *this; } nsCOMPtr& operator=(decltype(nullptr)) { assign_assuming_AddRef(nullptr); return *this; } // Assign from |already_AddRefed|. template nsCOMPtr& operator=(already_AddRefed& aRhs) { // Make sure that U actually inherits from T static_assert(std::is_base_of::value, "U is not a subclass of T"); assign_assuming_AddRef(static_cast(aRhs.take())); NSCAP_ASSERT_NO_QUERY_NEEDED(); return *this; } // Assign from |otherComPtr.forget()|. template nsCOMPtr& operator=(already_AddRefed&& aRhs) { // Make sure that U actually inherits from T static_assert(std::is_base_of::value, "U is not a subclass of T"); assign_assuming_AddRef(static_cast(aRhs.take())); NSCAP_ASSERT_NO_QUERY_NEEDED(); return *this; } // Assign from |std::move(otherRefPtr)|. template nsCOMPtr& operator=(RefPtr&& aRhs) { // Make sure that U actually inherits from T static_assert(std::is_base_of::value, "U is not a subclass of T"); assign_assuming_AddRef(static_cast(aRhs.forget().take())); NSCAP_ASSERT_NO_QUERY_NEEDED(); return *this; } // Assign from |do_QueryInterface(expr)|. template nsCOMPtr& operator=(const nsQueryInterface aRhs) { assign_from_qi(aRhs, NS_GET_TEMPLATE_IID(T)); return *this; } // Assign from |do_QueryInterface(expr, &rv)|. template nsCOMPtr& operator=(const nsQueryInterfaceWithError& aRhs) { assign_from_qi_with_error(aRhs, NS_GET_TEMPLATE_IID(T)); return *this; } // Assign from |do_GetService(cid_expr)|. nsCOMPtr& operator=(const nsGetServiceByCID aRhs) { assign_from_gs_cid(aRhs, NS_GET_TEMPLATE_IID(T)); return *this; } // Assign from |do_GetService(cid_expr, &rv)|. nsCOMPtr& operator=(const nsGetServiceByCIDWithError& aRhs) { assign_from_gs_cid_with_error(aRhs, NS_GET_TEMPLATE_IID(T)); return *this; } // Assign from |do_GetService(contractid_expr)|. nsCOMPtr& operator=(const nsGetServiceByContractID aRhs) { assign_from_gs_contractid(aRhs, NS_GET_TEMPLATE_IID(T)); return *this; } // Assign from |do_GetService(contractid_expr, &rv)|. nsCOMPtr& operator=(const nsGetServiceByContractIDWithError& aRhs) { assign_from_gs_contractid_with_error(aRhs, NS_GET_TEMPLATE_IID(T)); return *this; } // Assign from |do_QueryReferent(ptr)|. nsCOMPtr& operator=(const nsQueryReferent& aRhs) { assign_from_query_referent(aRhs, NS_GET_TEMPLATE_IID(T)); return *this; } // And finally, anything else we might need to assign from can exploit the // nsCOMPtr_helper facility. nsCOMPtr& operator=(const nsCOMPtr_helper& aRhs) { assign_from_helper(aRhs, NS_GET_TEMPLATE_IID(T)); NSCAP_ASSERT_NO_QUERY_NEEDED(); return *this; } // Assign from |mozilla::NotNull|. template >>> nsCOMPtr& operator=(const mozilla::NotNull& aSmartPtr) { assign_assuming_AddRef(nsCOMPtr(aSmartPtr.get()).forget().take()); return *this; } // Assign from |mozilla::MovingNotNull|. template >>> nsCOMPtr& operator=(mozilla::MovingNotNull&& aSmartPtr) { assign_assuming_AddRef( nsCOMPtr(std::move(aSmartPtr).unwrapBasePtr()).forget().take()); return *this; } // Defined in OwningNonNull.h template nsCOMPtr& operator=(const mozilla::OwningNonNull& aOther); // Exchange ownership with |aRhs|; can save a pair of refcount operations. void swap(nsCOMPtr& aRhs) { T* temp = aRhs.mRawPtr; NSCAP_LOG_ASSIGNMENT(&aRhs, mRawPtr); NSCAP_LOG_ASSIGNMENT(this, temp); NSCAP_LOG_RELEASE(this, mRawPtr); NSCAP_LOG_RELEASE(&aRhs, temp); aRhs.mRawPtr = mRawPtr; mRawPtr = temp; // |aRhs| maintains the same invariants, so we don't need to // |NSCAP_ASSERT_NO_QUERY_NEEDED| } // Exchange ownership with |aRhs|; can save a pair of refcount operations. void swap(T*& aRhs) { T* temp = aRhs; NSCAP_LOG_ASSIGNMENT(this, temp); NSCAP_LOG_RELEASE(this, mRawPtr); aRhs = reinterpret_cast(mRawPtr); mRawPtr = temp; NSCAP_ASSERT_NO_QUERY_NEEDED(); } // Other pointer operators // Return the value of mRawPtr and null out mRawPtr. Useful for // already_AddRefed return values. already_AddRefed MOZ_MAY_CALL_AFTER_MUST_RETURN forget() { T* temp = nullptr; swap(temp); return already_AddRefed(temp); } // Set the target of aRhs to the value of mRawPtr and null out mRawPtr. // Useful to avoid unnecessary AddRef/Release pairs with "out" parameters // where aRhs bay be a T** or an I** where I is a base class of T. template void forget(I** aRhs) { NS_ASSERTION(aRhs, "Null pointer passed to forget!"); NSCAP_LOG_RELEASE(this, mRawPtr); *aRhs = get(); mRawPtr = nullptr; } // Prefer the implicit conversion provided automatically by // |operator T*() const|. Use |get()| to resolve ambiguity or to get a // castable pointer. T* get() const { return reinterpret_cast(mRawPtr); } // Makes an nsCOMPtr act like its underlying raw pointer type whenever it is // used in a context where a raw pointer is expected. It is this operator // that makes an nsCOMPtr substitutable for a raw pointer. // // Prefer the implicit use of this operator to calling |get()|, except where // necessary to resolve ambiguity. operator T*() const& { return get(); } // Don't allow implicit conversion of temporary nsCOMPtr to raw pointer, // because the refcount might be one and the pointer will immediately become // invalid. operator T*() const&& = delete; // Needed to avoid the deleted operator above explicit operator bool() const { return !!mRawPtr; } T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { MOZ_ASSERT(mRawPtr != nullptr, "You can't dereference a NULL nsCOMPtr with operator->()."); return get(); } // These are not intended to be used by clients. See |address_of| below. nsCOMPtr* get_address() { return this; } const nsCOMPtr* get_address() const { return this; } public: T& operator*() const { MOZ_ASSERT(mRawPtr != nullptr, "You can't dereference a NULL nsCOMPtr with operator*()."); return *get(); } T** StartAssignment() { #ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT return reinterpret_cast(begin_assignment()); #else assign_assuming_AddRef(nullptr); return reinterpret_cast(&mRawPtr); #endif } }; template inline void ImplCycleCollectionUnlink(nsCOMPtr& aField) { aField = nullptr; } template inline void ImplCycleCollectionTraverse( nsCycleCollectionTraversalCallback& aCallback, nsCOMPtr& aField, const char* aName, uint32_t aFlags = 0) { CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags); } template void nsCOMPtr::assign_with_AddRef(T* aRawPtr) { if (aRawPtr) { NSCAP_ADDREF(this, aRawPtr); } assign_assuming_AddRef(aRawPtr); } template template void nsCOMPtr::assign_from_qi(const nsQueryInterface aQI, const nsIID& aIID) { // Allow QIing to nsISupports from nsISupports as a special-case, since // SameCOMIdentity uses it. static_assert( std::is_same_v || !(std::is_same_v || std::is_base_of::value), "don't use do_QueryInterface for compile-time-determinable casts"); void* newRawPtr; if (NS_FAILED(aQI(aIID, &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); } template template void nsCOMPtr::assign_from_qi_with_error( const nsQueryInterfaceWithError& aQI, const nsIID& aIID) { static_assert( !(std::is_same_v || std::is_base_of::value), "don't use do_QueryInterface for compile-time-determinable casts"); void* newRawPtr; if (NS_FAILED(aQI(aIID, &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); } template void nsCOMPtr::assign_from_gs_cid(const nsGetServiceByCID aGS, const nsIID& aIID) { void* newRawPtr; if (NS_FAILED(aGS(aIID, &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); } template void nsCOMPtr::assign_from_gs_cid_with_error( const nsGetServiceByCIDWithError& aGS, const nsIID& aIID) { void* newRawPtr; if (NS_FAILED(aGS(aIID, &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); } template void nsCOMPtr::assign_from_gs_contractid(const nsGetServiceByContractID aGS, const nsIID& aIID) { void* newRawPtr; if (NS_FAILED(aGS(aIID, &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); } template void nsCOMPtr::assign_from_gs_contractid_with_error( const nsGetServiceByContractIDWithError& aGS, const nsIID& aIID) { void* newRawPtr; if (NS_FAILED(aGS(aIID, &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); } template void nsCOMPtr::assign_from_query_referent( const nsQueryReferent& aQueryReferent, const nsIID& aIID) { void* newRawPtr; if (NS_FAILED(aQueryReferent(aIID, &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); } template void nsCOMPtr::assign_from_helper(const nsCOMPtr_helper& helper, const nsIID& aIID) { void* newRawPtr; if (NS_FAILED(helper(aIID, &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); } template void** nsCOMPtr::begin_assignment() { assign_assuming_AddRef(nullptr); union { T** mT; void** mVoid; } result; result.mT = &mRawPtr; return result.mVoid; } template inline nsCOMPtr* address_of(nsCOMPtr& aPtr) { return aPtr.get_address(); } template inline const nsCOMPtr* address_of(const nsCOMPtr& aPtr) { return aPtr.get_address(); } /** * This class is designed to be used for anonymous temporary objects in the * argument list of calls that return COM interface pointers, e.g., * * nsCOMPtr fooP; * ...->QueryInterface(iid, getter_AddRefs(fooP)) * * DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_AddRefs()| instead. * * When initialized with a |nsCOMPtr|, as in the example above, it returns * a |void**|, a |T**|, or an |nsISupports**| as needed, that the outer call * (|QueryInterface| in this case) can fill in. * * This type should be a nested class inside |nsCOMPtr|. */ template class nsGetterAddRefs { public: explicit nsGetterAddRefs(nsCOMPtr& aSmartPtr) : mTargetSmartPtr(aSmartPtr) {} #if defined(NSCAP_FEATURE_TEST_DONTQUERY_CASES) || \ defined(NSCAP_LOG_EXTERNAL_ASSIGNMENT) ~nsGetterAddRefs() { # ifdef NSCAP_LOG_EXTERNAL_ASSIGNMENT NSCAP_LOG_ASSIGNMENT(reinterpret_cast(address_of(mTargetSmartPtr)), mTargetSmartPtr.get()); # endif # ifdef NSCAP_FEATURE_TEST_DONTQUERY_CASES mTargetSmartPtr.Assert_NoQueryNeeded(); # endif } #endif operator void**() { return reinterpret_cast(mTargetSmartPtr.StartAssignment()); } operator T**() { return mTargetSmartPtr.StartAssignment(); } T*& operator*() { return *(mTargetSmartPtr.StartAssignment()); } private: nsCOMPtr& mTargetSmartPtr; }; template <> class nsGetterAddRefs { public: explicit nsGetterAddRefs(nsCOMPtr& aSmartPtr) : mTargetSmartPtr(aSmartPtr) {} #ifdef NSCAP_LOG_EXTERNAL_ASSIGNMENT ~nsGetterAddRefs() { NSCAP_LOG_ASSIGNMENT(reinterpret_cast(address_of(mTargetSmartPtr)), mTargetSmartPtr.get()); } #endif operator void**() { return reinterpret_cast(mTargetSmartPtr.StartAssignment()); } operator nsISupports**() { return mTargetSmartPtr.StartAssignment(); } nsISupports*& operator*() { return *(mTargetSmartPtr.StartAssignment()); } private: nsCOMPtr& mTargetSmartPtr; }; template inline nsGetterAddRefs getter_AddRefs(nsCOMPtr& aSmartPtr) { return nsGetterAddRefs(aSmartPtr); } template inline nsresult CallQueryInterface( T* aSource, nsGetterAddRefs aDestination) { return CallQueryInterface(aSource, static_cast(aDestination)); } // Comparing two |nsCOMPtr|s template inline bool operator==(const nsCOMPtr& aLhs, const nsCOMPtr& aRhs) { return static_cast(aLhs.get()) == static_cast(aRhs.get()); } template inline bool operator!=(const nsCOMPtr& aLhs, const nsCOMPtr& aRhs) { return static_cast(aLhs.get()) != static_cast(aRhs.get()); } // Comparing an |nsCOMPtr| to a raw pointer template inline bool operator==(const nsCOMPtr& aLhs, const U* aRhs) { return static_cast(aLhs.get()) == aRhs; } template inline bool operator==(const U* aLhs, const nsCOMPtr& aRhs) { return aLhs == static_cast(aRhs.get()); } template inline bool operator!=(const nsCOMPtr& aLhs, const U* aRhs) { return static_cast(aLhs.get()) != aRhs; } template inline bool operator!=(const U* aLhs, const nsCOMPtr& aRhs) { return aLhs != static_cast(aRhs.get()); } template inline bool operator==(const nsCOMPtr& aLhs, U* aRhs) { return static_cast(aLhs.get()) == const_cast(aRhs); } template inline bool operator==(U* aLhs, const nsCOMPtr& aRhs) { return const_cast(aLhs) == static_cast(aRhs.get()); } template inline bool operator!=(const nsCOMPtr& aLhs, U* aRhs) { return static_cast(aLhs.get()) != const_cast(aRhs); } template inline bool operator!=(U* aLhs, const nsCOMPtr& aRhs) { return const_cast(aLhs) != static_cast(aRhs.get()); } // Comparing an |nsCOMPtr| to |nullptr| template inline bool operator==(const nsCOMPtr& aLhs, decltype(nullptr)) { return aLhs.get() == nullptr; } template inline bool operator==(decltype(nullptr), const nsCOMPtr& aRhs) { return nullptr == aRhs.get(); } template inline bool operator!=(const nsCOMPtr& aLhs, decltype(nullptr)) { return aLhs.get() != nullptr; } template inline bool operator!=(decltype(nullptr), const nsCOMPtr& aRhs) { return nullptr != aRhs.get(); } // Comparing any two [XP]COM objects for identity inline bool SameCOMIdentity(nsISupports* aLhs, nsISupports* aRhs) { return nsCOMPtr(do_QueryInterface(aLhs)) == nsCOMPtr(do_QueryInterface(aRhs)); } template inline nsresult CallQueryInterface(nsCOMPtr& aSourcePtr, DestinationType** aDestPtr) { return CallQueryInterface(aSourcePtr.get(), aDestPtr); } template RefPtr::RefPtr(const nsQueryReferent& aQueryReferent) { void* newRawPtr; if (NS_FAILED(aQueryReferent(NS_GET_TEMPLATE_IID(T), &newRawPtr))) { newRawPtr = nullptr; } mRawPtr = static_cast(newRawPtr); } template RefPtr::RefPtr(const nsCOMPtr_helper& aHelper) { void* newRawPtr; if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) { newRawPtr = nullptr; } mRawPtr = static_cast(newRawPtr); } template RefPtr& RefPtr::operator=(const nsQueryReferent& aQueryReferent) { void* newRawPtr; if (NS_FAILED(aQueryReferent(NS_GET_TEMPLATE_IID(T), &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); return *this; } template RefPtr& RefPtr::operator=(const nsCOMPtr_helper& aHelper) { void* newRawPtr; if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) { newRawPtr = nullptr; } assign_assuming_AddRef(static_cast(newRawPtr)); return *this; } template inline already_AddRefed do_AddRef(const nsCOMPtr& aObj) { nsCOMPtr ref(aObj); return ref.forget(); } // MOZ_DBG support template std::ostream& operator<<(std::ostream& aOut, const nsCOMPtr& aObj) { return mozilla::DebugValue(aOut, aObj.get()); } // ToRefPtr allows to move an nsCOMPtr into a RefPtr. Be mindful when // using this, because usually RefPtr should only be used with concrete T and // nsCOMPtr should only be used with XPCOM interface T. template RefPtr ToRefPtr(nsCOMPtr&& aObj) { return aObj.forget(); } // Integration with ResultExtensions.h template auto ResultRefAsParam(nsCOMPtr& aResult) { return getter_AddRefs(aResult); } namespace mozilla::detail { template struct outparam_as_pointer; template struct outparam_as_pointer> { using type = T**; }; } // namespace mozilla::detail #endif // !defined(nsCOMPtr_h___)