1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/ds/nsObserverService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,331 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "prlog.h" 1.10 +#include "nsAutoPtr.h" 1.11 +#include "nsIObserverService.h" 1.12 +#include "nsIObserver.h" 1.13 +#include "nsObserverService.h" 1.14 +#include "nsObserverList.h" 1.15 +#include "nsThreadUtils.h" 1.16 +#include "nsEnumeratorUtils.h" 1.17 +#include "mozilla/net/NeckoCommon.h" 1.18 +#include "mozilla/Services.h" 1.19 + 1.20 +#define NOTIFY_GLOBAL_OBSERVERS 1.21 + 1.22 +#if defined(PR_LOGGING) 1.23 +// Log module for nsObserverService logging... 1.24 +// 1.25 +// To enable logging (see prlog.h for full details): 1.26 +// 1.27 +// set NSPR_LOG_MODULES=ObserverService:5 1.28 +// set NSPR_LOG_FILE=nspr.log 1.29 +// 1.30 +// this enables PR_LOG_DEBUG level information and places all output in 1.31 +// the file nspr.log 1.32 +static PRLogModuleInfo* 1.33 +GetObserverServiceLog() 1.34 +{ 1.35 + static PRLogModuleInfo *sLog; 1.36 + if (!sLog) 1.37 + sLog = PR_NewLogModule("ObserverService"); 1.38 + return sLog; 1.39 +} 1.40 + #define LOG(x) PR_LOG(GetObserverServiceLog(), PR_LOG_DEBUG, x) 1.41 +#else 1.42 + #define LOG(x) 1.43 +#endif /* PR_LOGGING */ 1.44 + 1.45 +namespace mozilla { 1.46 + 1.47 +struct SuspectObserver { 1.48 + SuspectObserver(const char* aTopic, size_t aReferentCount) 1.49 + : topic(aTopic), referentCount(aReferentCount) {} 1.50 + const char* topic; 1.51 + size_t referentCount; 1.52 +}; 1.53 + 1.54 +struct ObserverServiceReferentCount { 1.55 + ObserverServiceReferentCount() 1.56 + : numStrong(0), numWeakAlive(0), numWeakDead(0) {} 1.57 + size_t numStrong; 1.58 + size_t numWeakAlive; 1.59 + size_t numWeakDead; 1.60 + nsTArray<SuspectObserver> suspectObservers; 1.61 +}; 1.62 + 1.63 +} // namespace mozilla 1.64 + 1.65 +using namespace mozilla; 1.66 + 1.67 +PLDHashOperator 1.68 +nsObserverService::CountReferents(nsObserverList* aObserverList, 1.69 + void* aClosure) 1.70 +{ 1.71 + if (!aObserverList) { 1.72 + return PL_DHASH_NEXT; 1.73 + } 1.74 + 1.75 + ObserverServiceReferentCount* referentCount = 1.76 + static_cast<ObserverServiceReferentCount*>(aClosure); 1.77 + 1.78 + size_t numStrong = 0; 1.79 + size_t numWeakAlive = 0; 1.80 + size_t numWeakDead = 0; 1.81 + 1.82 + nsTArray<ObserverRef>& observers = aObserverList->mObservers; 1.83 + for (uint32_t i = 0; i < observers.Length(); i++) { 1.84 + if (observers[i].isWeakRef) { 1.85 + nsCOMPtr<nsIObserver> observerRef( 1.86 + do_QueryReferent(observers[i].asWeak())); 1.87 + if (observerRef) { 1.88 + numWeakAlive++; 1.89 + } else { 1.90 + numWeakDead++; 1.91 + } 1.92 + } else { 1.93 + numStrong++; 1.94 + } 1.95 + } 1.96 + 1.97 + referentCount->numStrong += numStrong; 1.98 + referentCount->numWeakAlive += numWeakAlive; 1.99 + referentCount->numWeakDead += numWeakDead; 1.100 + 1.101 + // Keep track of topics that have a suspiciously large number 1.102 + // of referents (symptom of leaks). 1.103 + size_t total = numStrong + numWeakAlive + numWeakDead; 1.104 + if (total > kSuspectReferentCount) { 1.105 + SuspectObserver suspect(aObserverList->GetKey(), total); 1.106 + referentCount->suspectObservers.AppendElement(suspect); 1.107 + } 1.108 + 1.109 + return PL_DHASH_NEXT; 1.110 +} 1.111 + 1.112 +NS_IMETHODIMP 1.113 +nsObserverService::CollectReports(nsIHandleReportCallback* aHandleReport, 1.114 + nsISupports* aData) 1.115 +{ 1.116 + ObserverServiceReferentCount referentCount; 1.117 + mObserverTopicTable.EnumerateEntries(CountReferents, &referentCount); 1.118 + 1.119 + nsresult rv; 1.120 + for (uint32_t i = 0; i < referentCount.suspectObservers.Length(); i++) { 1.121 + SuspectObserver& suspect = referentCount.suspectObservers[i]; 1.122 + nsPrintfCString suspectPath("observer-service-suspect/" 1.123 + "referent(topic=%s)", 1.124 + suspect.topic); 1.125 + rv = aHandleReport->Callback(/* process */ EmptyCString(), 1.126 + suspectPath, KIND_OTHER, UNITS_COUNT, suspect.referentCount, 1.127 + NS_LITERAL_CSTRING("A topic with a suspiciously large number of " 1.128 + "referents. This may be symptomatic of a leak " 1.129 + "if the number of referents is high with " 1.130 + "respect to the number of windows."), 1.131 + aData); 1.132 + 1.133 + if (NS_WARN_IF(NS_FAILED(rv))) 1.134 + return rv; 1.135 + } 1.136 + 1.137 + rv = aHandleReport->Callback(/* process */ EmptyCString(), 1.138 + NS_LITERAL_CSTRING("observer-service/referent/strong"), 1.139 + KIND_OTHER, UNITS_COUNT, referentCount.numStrong, 1.140 + NS_LITERAL_CSTRING("The number of strong references held by the " 1.141 + "observer service."), 1.142 + aData); 1.143 + 1.144 + if (NS_WARN_IF(NS_FAILED(rv))) 1.145 + return rv; 1.146 + 1.147 + rv = aHandleReport->Callback(/* process */ EmptyCString(), 1.148 + NS_LITERAL_CSTRING("observer-service/referent/weak/alive"), 1.149 + KIND_OTHER, UNITS_COUNT, referentCount.numWeakAlive, 1.150 + NS_LITERAL_CSTRING("The number of weak references held by the " 1.151 + "observer service that are still alive."), 1.152 + aData); 1.153 + 1.154 + if (NS_WARN_IF(NS_FAILED(rv))) 1.155 + return rv; 1.156 + 1.157 + rv = aHandleReport->Callback(/* process */ EmptyCString(), 1.158 + NS_LITERAL_CSTRING("observer-service/referent/weak/dead"), 1.159 + KIND_OTHER, UNITS_COUNT, referentCount.numWeakDead, 1.160 + NS_LITERAL_CSTRING("The number of weak references held by the " 1.161 + "observer service that are dead."), 1.162 + aData); 1.163 + 1.164 + if (NS_WARN_IF(NS_FAILED(rv))) 1.165 + return rv; 1.166 + 1.167 + return NS_OK; 1.168 +} 1.169 + 1.170 +//////////////////////////////////////////////////////////////////////////////// 1.171 +// nsObserverService Implementation 1.172 + 1.173 + 1.174 +NS_IMPL_ISUPPORTS( 1.175 + nsObserverService, 1.176 + nsIObserverService, 1.177 + nsObserverService, 1.178 + nsIMemoryReporter) 1.179 + 1.180 +nsObserverService::nsObserverService() : 1.181 + mShuttingDown(false) 1.182 +{ 1.183 +} 1.184 + 1.185 +nsObserverService::~nsObserverService(void) 1.186 +{ 1.187 + Shutdown(); 1.188 +} 1.189 + 1.190 +void 1.191 +nsObserverService::RegisterReporter() 1.192 +{ 1.193 + RegisterWeakMemoryReporter(this); 1.194 +} 1.195 + 1.196 +void 1.197 +nsObserverService::Shutdown() 1.198 +{ 1.199 + UnregisterWeakMemoryReporter(this); 1.200 + 1.201 + mShuttingDown = true; 1.202 + 1.203 + mObserverTopicTable.Clear(); 1.204 +} 1.205 + 1.206 +nsresult 1.207 +nsObserverService::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr) 1.208 +{ 1.209 + LOG(("nsObserverService::Create()")); 1.210 + 1.211 + nsRefPtr<nsObserverService> os = new nsObserverService(); 1.212 + 1.213 + if (!os) 1.214 + return NS_ERROR_OUT_OF_MEMORY; 1.215 + 1.216 + // The memory reporter can not be immediately registered here because 1.217 + // the nsMemoryReporterManager may attempt to get the nsObserverService 1.218 + // during initialization, causing a recursive GetService. 1.219 + nsRefPtr<nsRunnableMethod<nsObserverService> > registerRunnable = 1.220 + NS_NewRunnableMethod(os, &nsObserverService::RegisterReporter); 1.221 + NS_DispatchToCurrentThread(registerRunnable); 1.222 + 1.223 + return os->QueryInterface(aIID, aInstancePtr); 1.224 +} 1.225 + 1.226 +#define NS_ENSURE_VALIDCALL \ 1.227 + if (!NS_IsMainThread()) { \ 1.228 + MOZ_CRASH("Using observer service off the main thread!"); \ 1.229 + return NS_ERROR_UNEXPECTED; \ 1.230 + } \ 1.231 + if (mShuttingDown) { \ 1.232 + NS_ERROR("Using observer service after XPCOM shutdown!"); \ 1.233 + return NS_ERROR_ILLEGAL_DURING_SHUTDOWN; \ 1.234 + } 1.235 + 1.236 +NS_IMETHODIMP 1.237 +nsObserverService::AddObserver(nsIObserver* anObserver, const char* aTopic, 1.238 + bool ownsWeak) 1.239 +{ 1.240 + LOG(("nsObserverService::AddObserver(%p: %s)", 1.241 + (void*) anObserver, aTopic)); 1.242 + 1.243 + NS_ENSURE_VALIDCALL 1.244 + if (NS_WARN_IF(!anObserver) || NS_WARN_IF(!aTopic)) 1.245 + return NS_ERROR_INVALID_ARG; 1.246 + 1.247 + if (mozilla::net::IsNeckoChild() && !strncmp(aTopic, "http-on-", 8)) { 1.248 + return NS_ERROR_NOT_IMPLEMENTED; 1.249 + } 1.250 + 1.251 + nsObserverList *observerList = mObserverTopicTable.PutEntry(aTopic); 1.252 + if (!observerList) 1.253 + return NS_ERROR_OUT_OF_MEMORY; 1.254 + 1.255 + return observerList->AddObserver(anObserver, ownsWeak); 1.256 +} 1.257 + 1.258 +NS_IMETHODIMP 1.259 +nsObserverService::RemoveObserver(nsIObserver* anObserver, const char* aTopic) 1.260 +{ 1.261 + LOG(("nsObserverService::RemoveObserver(%p: %s)", 1.262 + (void*) anObserver, aTopic)); 1.263 + NS_ENSURE_VALIDCALL 1.264 + if (NS_WARN_IF(!anObserver) || NS_WARN_IF(!aTopic)) 1.265 + return NS_ERROR_INVALID_ARG; 1.266 + 1.267 + nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic); 1.268 + if (!observerList) 1.269 + return NS_ERROR_FAILURE; 1.270 + 1.271 + /* This death grip is to protect against stupid consumers who call 1.272 + RemoveObserver from their Destructor, see bug 485834/bug 325392. */ 1.273 + nsCOMPtr<nsIObserver> kungFuDeathGrip(anObserver); 1.274 + return observerList->RemoveObserver(anObserver); 1.275 +} 1.276 + 1.277 +NS_IMETHODIMP 1.278 +nsObserverService::EnumerateObservers(const char* aTopic, 1.279 + nsISimpleEnumerator** anEnumerator) 1.280 +{ 1.281 + NS_ENSURE_VALIDCALL 1.282 + if (NS_WARN_IF(!anEnumerator) || NS_WARN_IF(!aTopic)) 1.283 + return NS_ERROR_INVALID_ARG; 1.284 + 1.285 + nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic); 1.286 + if (!observerList) 1.287 + return NS_NewEmptyEnumerator(anEnumerator); 1.288 + 1.289 + return observerList->GetObserverList(anEnumerator); 1.290 +} 1.291 + 1.292 +// Enumerate observers of aTopic and call Observe on each. 1.293 +NS_IMETHODIMP nsObserverService::NotifyObservers(nsISupports *aSubject, 1.294 + const char *aTopic, 1.295 + const char16_t *someData) 1.296 +{ 1.297 + LOG(("nsObserverService::NotifyObservers(%s)", aTopic)); 1.298 + 1.299 + NS_ENSURE_VALIDCALL 1.300 + if (NS_WARN_IF(!aTopic)) 1.301 + return NS_ERROR_INVALID_ARG; 1.302 + 1.303 + nsObserverList *observerList = mObserverTopicTable.GetEntry(aTopic); 1.304 + if (observerList) 1.305 + observerList->NotifyObservers(aSubject, aTopic, someData); 1.306 + 1.307 +#ifdef NOTIFY_GLOBAL_OBSERVERS 1.308 + observerList = mObserverTopicTable.GetEntry("*"); 1.309 + if (observerList) 1.310 + observerList->NotifyObservers(aSubject, aTopic, someData); 1.311 +#endif 1.312 + 1.313 + return NS_OK; 1.314 +} 1.315 + 1.316 +static PLDHashOperator 1.317 +UnmarkGrayObserverEntry(nsObserverList* aObserverList, void* aClosure) 1.318 +{ 1.319 + if (aObserverList) { 1.320 + aObserverList->UnmarkGrayStrongObservers(); 1.321 + } 1.322 + return PL_DHASH_NEXT; 1.323 +} 1.324 + 1.325 +NS_IMETHODIMP 1.326 +nsObserverService::UnmarkGrayStrongObservers() 1.327 +{ 1.328 + NS_ENSURE_VALIDCALL 1.329 + 1.330 + mObserverTopicTable.EnumerateEntries(UnmarkGrayObserverEntry, nullptr); 1.331 + 1.332 + return NS_OK; 1.333 +} 1.334 +