1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "nsIObserverService.h"
9 #include "nsIObserver.h"
10 #include "nsObserverService.h"
11 #include "nsObserverList.h"
12 #include "nsThreadUtils.h"
13 #include "nsEnumeratorUtils.h"
14 #include "mozilla/net/NeckoCommon.h"
15 #include "mozilla/Services.h"
17 #define NOTIFY_GLOBAL_OBSERVERS
19 #if defined(PR_LOGGING)
20 // Log module for nsObserverService logging...
22 // To enable logging (see prlog.h for full details):
24 // set NSPR_LOG_MODULES=ObserverService:5
25 // set NSPR_LOG_FILE=nspr.log
27 // this enables PR_LOG_DEBUG level information and places all output in
29 static PRLogModuleInfo
*
30 GetObserverServiceLog()
32 static PRLogModuleInfo
* sLog
;
34 sLog
= PR_NewLogModule("ObserverService");
38 #define LOG(x) PR_LOG(GetObserverServiceLog(), PR_LOG_DEBUG, x)
41 #endif /* PR_LOGGING */
45 struct SuspectObserver
47 SuspectObserver(const char* aTopic
, size_t aReferentCount
)
49 , referentCount(aReferentCount
)
56 struct ObserverServiceReferentCount
58 ObserverServiceReferentCount()
67 nsTArray
<SuspectObserver
> suspectObservers
;
70 } // namespace mozilla
72 using namespace mozilla
;
75 nsObserverService::CountReferents(nsObserverList
* aObserverList
,
82 ObserverServiceReferentCount
* referentCount
=
83 static_cast<ObserverServiceReferentCount
*>(aClosure
);
86 size_t numWeakAlive
= 0;
87 size_t numWeakDead
= 0;
89 nsTArray
<ObserverRef
>& observers
= aObserverList
->mObservers
;
90 for (uint32_t i
= 0; i
< observers
.Length(); i
++) {
91 if (observers
[i
].isWeakRef
) {
92 nsCOMPtr
<nsIObserver
> observerRef(
93 do_QueryReferent(observers
[i
].asWeak()));
104 referentCount
->numStrong
+= numStrong
;
105 referentCount
->numWeakAlive
+= numWeakAlive
;
106 referentCount
->numWeakDead
+= numWeakDead
;
108 // Keep track of topics that have a suspiciously large number
109 // of referents (symptom of leaks).
110 size_t total
= numStrong
+ numWeakAlive
+ numWeakDead
;
111 if (total
> kSuspectReferentCount
) {
112 SuspectObserver
suspect(aObserverList
->GetKey(), total
);
113 referentCount
->suspectObservers
.AppendElement(suspect
);
116 return PL_DHASH_NEXT
;
120 nsObserverService::CollectReports(nsIHandleReportCallback
* aHandleReport
,
121 nsISupports
* aData
, bool aAnonymize
)
123 ObserverServiceReferentCount referentCount
;
124 mObserverTopicTable
.EnumerateEntries(CountReferents
, &referentCount
);
126 // These aren't privacy-sensitive and so don't need anonymizing.
128 for (uint32_t i
= 0; i
< referentCount
.suspectObservers
.Length(); i
++) {
129 SuspectObserver
& suspect
= referentCount
.suspectObservers
[i
];
130 nsPrintfCString
suspectPath("observer-service-suspect/"
131 "referent(topic=%s)",
133 rv
= aHandleReport
->Callback(
134 /* process */ EmptyCString(),
135 suspectPath
, KIND_OTHER
, UNITS_COUNT
, suspect
.referentCount
,
136 NS_LITERAL_CSTRING("A topic with a suspiciously large number of "
137 "referents. This may be symptomatic of a leak "
138 "if the number of referents is high with "
139 "respect to the number of windows."),
142 if (NS_WARN_IF(NS_FAILED(rv
))) {
147 rv
= aHandleReport
->Callback(
148 /* process */ EmptyCString(),
149 NS_LITERAL_CSTRING("observer-service/referent/strong"),
150 KIND_OTHER
, UNITS_COUNT
, referentCount
.numStrong
,
151 NS_LITERAL_CSTRING("The number of strong references held by the "
152 "observer service."),
155 if (NS_WARN_IF(NS_FAILED(rv
))) {
159 rv
= aHandleReport
->Callback(
160 /* process */ EmptyCString(),
161 NS_LITERAL_CSTRING("observer-service/referent/weak/alive"),
162 KIND_OTHER
, UNITS_COUNT
, referentCount
.numWeakAlive
,
163 NS_LITERAL_CSTRING("The number of weak references held by the "
164 "observer service that are still alive."),
167 if (NS_WARN_IF(NS_FAILED(rv
))) {
171 rv
= aHandleReport
->Callback(
172 /* process */ EmptyCString(),
173 NS_LITERAL_CSTRING("observer-service/referent/weak/dead"),
174 KIND_OTHER
, UNITS_COUNT
, referentCount
.numWeakDead
,
175 NS_LITERAL_CSTRING("The number of weak references held by the "
176 "observer service that are dead."),
179 if (NS_WARN_IF(NS_FAILED(rv
))) {
186 ////////////////////////////////////////////////////////////////////////////////
187 // nsObserverService Implementation
190 NS_IMPL_ISUPPORTS(nsObserverService
,
195 nsObserverService::nsObserverService()
196 : mShuttingDown(false)
200 nsObserverService::~nsObserverService(void)
206 nsObserverService::RegisterReporter()
208 RegisterWeakMemoryReporter(this);
212 nsObserverService::Shutdown()
214 UnregisterWeakMemoryReporter(this);
216 mShuttingDown
= true;
218 mObserverTopicTable
.Clear();
222 nsObserverService::Create(nsISupports
* aOuter
, const nsIID
& aIID
,
225 LOG(("nsObserverService::Create()"));
227 nsRefPtr
<nsObserverService
> os
= new nsObserverService();
230 return NS_ERROR_OUT_OF_MEMORY
;
233 // The memory reporter can not be immediately registered here because
234 // the nsMemoryReporterManager may attempt to get the nsObserverService
235 // during initialization, causing a recursive GetService.
236 nsRefPtr
<nsRunnableMethod
<nsObserverService
>> registerRunnable
=
237 NS_NewRunnableMethod(os
, &nsObserverService::RegisterReporter
);
238 NS_DispatchToCurrentThread(registerRunnable
);
240 return os
->QueryInterface(aIID
, aInstancePtr
);
243 #define NS_ENSURE_VALIDCALL \
244 if (!NS_IsMainThread()) { \
245 MOZ_CRASH("Using observer service off the main thread!"); \
246 return NS_ERROR_UNEXPECTED; \
248 if (mShuttingDown) { \
249 NS_ERROR("Using observer service after XPCOM shutdown!"); \
250 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; \
254 nsObserverService::AddObserver(nsIObserver
* aObserver
, const char* aTopic
,
257 LOG(("nsObserverService::AddObserver(%p: %s)",
258 (void*)aObserver
, aTopic
));
261 if (NS_WARN_IF(!aObserver
) || NS_WARN_IF(!aTopic
)) {
262 return NS_ERROR_INVALID_ARG
;
265 if (mozilla::net::IsNeckoChild() && !strncmp(aTopic
, "http-on-", 8)) {
266 return NS_ERROR_NOT_IMPLEMENTED
;
269 nsObserverList
* observerList
= mObserverTopicTable
.PutEntry(aTopic
);
271 return NS_ERROR_OUT_OF_MEMORY
;
274 return observerList
->AddObserver(aObserver
, aOwnsWeak
);
278 nsObserverService::RemoveObserver(nsIObserver
* aObserver
, const char* aTopic
)
280 LOG(("nsObserverService::RemoveObserver(%p: %s)",
281 (void*)aObserver
, aTopic
));
283 if (NS_WARN_IF(!aObserver
) || NS_WARN_IF(!aTopic
)) {
284 return NS_ERROR_INVALID_ARG
;
287 nsObserverList
* observerList
= mObserverTopicTable
.GetEntry(aTopic
);
289 return NS_ERROR_FAILURE
;
292 /* This death grip is to protect against stupid consumers who call
293 RemoveObserver from their Destructor, see bug 485834/bug 325392. */
294 nsCOMPtr
<nsIObserver
> kungFuDeathGrip(aObserver
);
295 return observerList
->RemoveObserver(aObserver
);
299 nsObserverService::EnumerateObservers(const char* aTopic
,
300 nsISimpleEnumerator
** anEnumerator
)
303 if (NS_WARN_IF(!anEnumerator
) || NS_WARN_IF(!aTopic
)) {
304 return NS_ERROR_INVALID_ARG
;
307 nsObserverList
* observerList
= mObserverTopicTable
.GetEntry(aTopic
);
309 return NS_NewEmptyEnumerator(anEnumerator
);
312 return observerList
->GetObserverList(anEnumerator
);
315 // Enumerate observers of aTopic and call Observe on each.
316 NS_IMETHODIMP
nsObserverService::NotifyObservers(nsISupports
* aSubject
,
318 const char16_t
* aSomeData
)
320 LOG(("nsObserverService::NotifyObservers(%s)", aTopic
));
323 if (NS_WARN_IF(!aTopic
)) {
324 return NS_ERROR_INVALID_ARG
;
327 nsObserverList
* observerList
= mObserverTopicTable
.GetEntry(aTopic
);
329 observerList
->NotifyObservers(aSubject
, aTopic
, aSomeData
);
332 #ifdef NOTIFY_GLOBAL_OBSERVERS
333 observerList
= mObserverTopicTable
.GetEntry("*");
335 observerList
->NotifyObservers(aSubject
, aTopic
, aSomeData
);
342 static PLDHashOperator
343 UnmarkGrayObserverEntry(nsObserverList
* aObserverList
, void* aClosure
)
346 aObserverList
->UnmarkGrayStrongObservers();
348 return PL_DHASH_NEXT
;
352 nsObserverService::UnmarkGrayStrongObservers()
356 mObserverTopicTable
.EnumerateEntries(UnmarkGrayObserverEntry
, nullptr);