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: #include "prlog.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsObserverService.h" michael@0: #include "nsObserverList.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsEnumeratorUtils.h" michael@0: #include "mozilla/net/NeckoCommon.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #define NOTIFY_GLOBAL_OBSERVERS michael@0: michael@0: #if defined(PR_LOGGING) michael@0: // Log module for nsObserverService logging... michael@0: // michael@0: // To enable logging (see prlog.h for full details): michael@0: // michael@0: // set NSPR_LOG_MODULES=ObserverService:5 michael@0: // set NSPR_LOG_FILE=nspr.log michael@0: // michael@0: // this enables PR_LOG_DEBUG level information and places all output in michael@0: // the file nspr.log michael@0: static PRLogModuleInfo* michael@0: GetObserverServiceLog() michael@0: { michael@0: static PRLogModuleInfo *sLog; michael@0: if (!sLog) michael@0: sLog = PR_NewLogModule("ObserverService"); michael@0: return sLog; michael@0: } michael@0: #define LOG(x) PR_LOG(GetObserverServiceLog(), PR_LOG_DEBUG, x) michael@0: #else michael@0: #define LOG(x) michael@0: #endif /* PR_LOGGING */ michael@0: michael@0: namespace mozilla { michael@0: michael@0: struct SuspectObserver { michael@0: SuspectObserver(const char* aTopic, size_t aReferentCount) michael@0: : topic(aTopic), referentCount(aReferentCount) {} michael@0: const char* topic; michael@0: size_t referentCount; michael@0: }; michael@0: michael@0: struct ObserverServiceReferentCount { michael@0: ObserverServiceReferentCount() michael@0: : numStrong(0), numWeakAlive(0), numWeakDead(0) {} michael@0: size_t numStrong; michael@0: size_t numWeakAlive; michael@0: size_t numWeakDead; michael@0: nsTArray suspectObservers; michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: using namespace mozilla; michael@0: michael@0: PLDHashOperator michael@0: nsObserverService::CountReferents(nsObserverList* aObserverList, michael@0: void* aClosure) michael@0: { michael@0: if (!aObserverList) { michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: ObserverServiceReferentCount* referentCount = michael@0: static_cast(aClosure); michael@0: michael@0: size_t numStrong = 0; michael@0: size_t numWeakAlive = 0; michael@0: size_t numWeakDead = 0; michael@0: michael@0: nsTArray& observers = aObserverList->mObservers; michael@0: for (uint32_t i = 0; i < observers.Length(); i++) { michael@0: if (observers[i].isWeakRef) { michael@0: nsCOMPtr observerRef( michael@0: do_QueryReferent(observers[i].asWeak())); michael@0: if (observerRef) { michael@0: numWeakAlive++; michael@0: } else { michael@0: numWeakDead++; michael@0: } michael@0: } else { michael@0: numStrong++; michael@0: } michael@0: } michael@0: michael@0: referentCount->numStrong += numStrong; michael@0: referentCount->numWeakAlive += numWeakAlive; michael@0: referentCount->numWeakDead += numWeakDead; michael@0: michael@0: // Keep track of topics that have a suspiciously large number michael@0: // of referents (symptom of leaks). michael@0: size_t total = numStrong + numWeakAlive + numWeakDead; michael@0: if (total > kSuspectReferentCount) { michael@0: SuspectObserver suspect(aObserverList->GetKey(), total); michael@0: referentCount->suspectObservers.AppendElement(suspect); michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsObserverService::CollectReports(nsIHandleReportCallback* aHandleReport, michael@0: nsISupports* aData) michael@0: { michael@0: ObserverServiceReferentCount referentCount; michael@0: mObserverTopicTable.EnumerateEntries(CountReferents, &referentCount); michael@0: michael@0: nsresult rv; michael@0: for (uint32_t i = 0; i < referentCount.suspectObservers.Length(); i++) { michael@0: SuspectObserver& suspect = referentCount.suspectObservers[i]; michael@0: nsPrintfCString suspectPath("observer-service-suspect/" michael@0: "referent(topic=%s)", michael@0: suspect.topic); michael@0: rv = aHandleReport->Callback(/* process */ EmptyCString(), michael@0: suspectPath, KIND_OTHER, UNITS_COUNT, suspect.referentCount, michael@0: NS_LITERAL_CSTRING("A topic with a suspiciously large number of " michael@0: "referents. This may be symptomatic of a leak " michael@0: "if the number of referents is high with " michael@0: "respect to the number of windows."), michael@0: aData); michael@0: michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: michael@0: rv = aHandleReport->Callback(/* process */ EmptyCString(), michael@0: NS_LITERAL_CSTRING("observer-service/referent/strong"), michael@0: KIND_OTHER, UNITS_COUNT, referentCount.numStrong, michael@0: NS_LITERAL_CSTRING("The number of strong references held by the " michael@0: "observer service."), michael@0: aData); michael@0: michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = aHandleReport->Callback(/* process */ EmptyCString(), michael@0: NS_LITERAL_CSTRING("observer-service/referent/weak/alive"), michael@0: KIND_OTHER, UNITS_COUNT, referentCount.numWeakAlive, michael@0: NS_LITERAL_CSTRING("The number of weak references held by the " michael@0: "observer service that are still alive."), michael@0: aData); michael@0: michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = aHandleReport->Callback(/* process */ EmptyCString(), michael@0: NS_LITERAL_CSTRING("observer-service/referent/weak/dead"), michael@0: KIND_OTHER, UNITS_COUNT, referentCount.numWeakDead, michael@0: NS_LITERAL_CSTRING("The number of weak references held by the " michael@0: "observer service that are dead."), michael@0: aData); michael@0: michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsObserverService Implementation michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: nsObserverService, michael@0: nsIObserverService, michael@0: nsObserverService, michael@0: nsIMemoryReporter) michael@0: michael@0: nsObserverService::nsObserverService() : michael@0: mShuttingDown(false) michael@0: { michael@0: } michael@0: michael@0: nsObserverService::~nsObserverService(void) michael@0: { michael@0: Shutdown(); michael@0: } michael@0: michael@0: void michael@0: nsObserverService::RegisterReporter() michael@0: { michael@0: RegisterWeakMemoryReporter(this); michael@0: } michael@0: michael@0: void michael@0: nsObserverService::Shutdown() michael@0: { michael@0: UnregisterWeakMemoryReporter(this); michael@0: michael@0: mShuttingDown = true; michael@0: michael@0: mObserverTopicTable.Clear(); michael@0: } michael@0: michael@0: nsresult michael@0: nsObserverService::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr) michael@0: { michael@0: LOG(("nsObserverService::Create()")); michael@0: michael@0: nsRefPtr os = new nsObserverService(); michael@0: michael@0: if (!os) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // The memory reporter can not be immediately registered here because michael@0: // the nsMemoryReporterManager may attempt to get the nsObserverService michael@0: // during initialization, causing a recursive GetService. michael@0: nsRefPtr > registerRunnable = michael@0: NS_NewRunnableMethod(os, &nsObserverService::RegisterReporter); michael@0: NS_DispatchToCurrentThread(registerRunnable); michael@0: michael@0: return os->QueryInterface(aIID, aInstancePtr); michael@0: } michael@0: michael@0: #define NS_ENSURE_VALIDCALL \ michael@0: if (!NS_IsMainThread()) { \ michael@0: MOZ_CRASH("Using observer service off the main thread!"); \ michael@0: return NS_ERROR_UNEXPECTED; \ michael@0: } \ michael@0: if (mShuttingDown) { \ michael@0: NS_ERROR("Using observer service after XPCOM shutdown!"); \ michael@0: return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; \ michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsObserverService::AddObserver(nsIObserver* anObserver, const char* aTopic, michael@0: bool ownsWeak) michael@0: { michael@0: LOG(("nsObserverService::AddObserver(%p: %s)", michael@0: (void*) anObserver, aTopic)); michael@0: michael@0: NS_ENSURE_VALIDCALL michael@0: if (NS_WARN_IF(!anObserver) || NS_WARN_IF(!aTopic)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: if (mozilla::net::IsNeckoChild() && !strncmp(aTopic, "http-on-", 8)) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsObserverList *observerList = mObserverTopicTable.PutEntry(aTopic); michael@0: if (!observerList) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return observerList->AddObserver(anObserver, ownsWeak); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsObserverService::RemoveObserver(nsIObserver* anObserver, const char* aTopic) michael@0: { michael@0: LOG(("nsObserverService::RemoveObserver(%p: %s)", michael@0: (void*) anObserver, aTopic)); michael@0: NS_ENSURE_VALIDCALL michael@0: if (NS_WARN_IF(!anObserver) || NS_WARN_IF(!aTopic)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic); michael@0: if (!observerList) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: /* This death grip is to protect against stupid consumers who call michael@0: RemoveObserver from their Destructor, see bug 485834/bug 325392. */ michael@0: nsCOMPtr kungFuDeathGrip(anObserver); michael@0: return observerList->RemoveObserver(anObserver); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsObserverService::EnumerateObservers(const char* aTopic, michael@0: nsISimpleEnumerator** anEnumerator) michael@0: { michael@0: NS_ENSURE_VALIDCALL michael@0: if (NS_WARN_IF(!anEnumerator) || NS_WARN_IF(!aTopic)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic); michael@0: if (!observerList) michael@0: return NS_NewEmptyEnumerator(anEnumerator); michael@0: michael@0: return observerList->GetObserverList(anEnumerator); michael@0: } michael@0: michael@0: // Enumerate observers of aTopic and call Observe on each. michael@0: NS_IMETHODIMP nsObserverService::NotifyObservers(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *someData) michael@0: { michael@0: LOG(("nsObserverService::NotifyObservers(%s)", aTopic)); michael@0: michael@0: NS_ENSURE_VALIDCALL michael@0: if (NS_WARN_IF(!aTopic)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic); michael@0: if (observerList) michael@0: observerList->NotifyObservers(aSubject, aTopic, someData); michael@0: michael@0: #ifdef NOTIFY_GLOBAL_OBSERVERS michael@0: observerList = mObserverTopicTable.GetEntry("*"); michael@0: if (observerList) michael@0: observerList->NotifyObservers(aSubject, aTopic, someData); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: UnmarkGrayObserverEntry(nsObserverList* aObserverList, void* aClosure) michael@0: { michael@0: if (aObserverList) { michael@0: aObserverList->UnmarkGrayStrongObservers(); michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsObserverService::UnmarkGrayStrongObservers() michael@0: { michael@0: NS_ENSURE_VALIDCALL michael@0: michael@0: mObserverTopicTable.EnumerateEntries(UnmarkGrayObserverEntry, nullptr); michael@0: michael@0: return NS_OK; michael@0: } michael@0: