Bug 1734063 [wpt PR 31107] - [GridNG] Fix rounding of distributed free space to flexi...
[gecko.git] / xpcom / ds / nsObserverService.cpp
blob9e7a770ae72492d80d7538ce51c4bdaa04d469f5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Logging.h"
8 #include "nsComponentManagerUtils.h"
9 #include "nsContentUtils.h"
10 #include "nsIConsoleService.h"
11 #include "nsIObserverService.h"
12 #include "nsIObserver.h"
13 #include "nsIScriptError.h"
14 #include "nsObserverService.h"
15 #include "nsObserverList.h"
16 #include "nsServiceManagerUtils.h"
17 #include "nsThreadUtils.h"
18 #include "nsEnumeratorUtils.h"
19 #include "xpcpublic.h"
20 #include "mozilla/AppShutdown.h"
21 #include "mozilla/net/NeckoCommon.h"
22 #include "mozilla/ProfilerLabels.h"
23 #include "mozilla/ProfilerMarkers.h"
24 #include "mozilla/ResultExtensions.h"
25 #include "mozilla/Telemetry.h"
26 #include "mozilla/TimeStamp.h"
27 #include "nsString.h"
29 static const uint32_t kMinTelemetryNotifyObserversLatencyMs = 1;
31 // Log module for nsObserverService logging...
33 // To enable logging (see prlog.h for full details):
35 // set MOZ_LOG=ObserverService:5
36 // set MOZ_LOG_FILE=service.log
38 // This enables LogLevel::Debug level information and places all output in
39 // the file service.log.
40 static mozilla::LazyLogModule sObserverServiceLog("ObserverService");
41 #define LOG(x) MOZ_LOG(sObserverServiceLog, mozilla::LogLevel::Debug, x)
43 using namespace mozilla;
45 NS_IMETHODIMP
46 nsObserverService::CollectReports(nsIHandleReportCallback* aHandleReport,
47 nsISupports* aData, bool aAnonymize) {
48 struct SuspectObserver {
49 SuspectObserver(const char* aTopic, size_t aReferentCount)
50 : mTopic(aTopic), mReferentCount(aReferentCount) {}
51 const char* mTopic;
52 size_t mReferentCount;
55 size_t totalNumStrong = 0;
56 size_t totalNumWeakAlive = 0;
57 size_t totalNumWeakDead = 0;
58 nsTArray<SuspectObserver> suspectObservers;
60 for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
61 nsObserverList* observerList = iter.Get();
62 if (!observerList) {
63 continue;
66 size_t topicNumStrong = 0;
67 size_t topicNumWeakAlive = 0;
68 size_t topicNumWeakDead = 0;
70 nsMaybeWeakPtrArray<nsIObserver>& observers = observerList->mObservers;
71 for (uint32_t i = 0; i < observers.Length(); i++) {
72 if (observers[i].IsWeak()) {
73 nsCOMPtr<nsIObserver> ref = observers[i].GetValue();
74 if (ref) {
75 topicNumWeakAlive++;
76 } else {
77 topicNumWeakDead++;
79 } else {
80 topicNumStrong++;
84 totalNumStrong += topicNumStrong;
85 totalNumWeakAlive += topicNumWeakAlive;
86 totalNumWeakDead += topicNumWeakDead;
88 // Keep track of topics that have a suspiciously large number
89 // of referents (symptom of leaks).
90 size_t topicTotal = topicNumStrong + topicNumWeakAlive + topicNumWeakDead;
91 if (topicTotal > kSuspectReferentCount) {
92 SuspectObserver suspect(observerList->GetKey(), topicTotal);
93 suspectObservers.AppendElement(suspect);
97 // These aren't privacy-sensitive and so don't need anonymizing.
98 for (uint32_t i = 0; i < suspectObservers.Length(); i++) {
99 SuspectObserver& suspect = suspectObservers[i];
100 nsPrintfCString suspectPath("observer-service-suspect/referent(topic=%s)",
101 suspect.mTopic);
102 aHandleReport->Callback(
103 /* process */ ""_ns, suspectPath, KIND_OTHER, UNITS_COUNT,
104 suspect.mReferentCount,
105 nsLiteralCString("A topic with a suspiciously large number of "
106 "referents. This may be symptomatic of a leak "
107 "if the number of referents is high with "
108 "respect to the number of windows."),
109 aData);
112 MOZ_COLLECT_REPORT(
113 "observer-service/referent/strong", KIND_OTHER, UNITS_COUNT,
114 totalNumStrong,
115 "The number of strong references held by the observer service.");
117 MOZ_COLLECT_REPORT(
118 "observer-service/referent/weak/alive", KIND_OTHER, UNITS_COUNT,
119 totalNumWeakAlive,
120 "The number of weak references held by the observer service that are "
121 "still alive.");
123 MOZ_COLLECT_REPORT(
124 "observer-service/referent/weak/dead", KIND_OTHER, UNITS_COUNT,
125 totalNumWeakDead,
126 "The number of weak references held by the observer service that are "
127 "dead.");
129 return NS_OK;
132 ////////////////////////////////////////////////////////////////////////////////
133 // nsObserverService Implementation
135 NS_IMPL_ISUPPORTS(nsObserverService, nsIObserverService, nsObserverService,
136 nsIMemoryReporter)
138 nsObserverService::nsObserverService() : mShuttingDown(false) {}
140 nsObserverService::~nsObserverService(void) { Shutdown(); }
142 void nsObserverService::RegisterReporter() { RegisterWeakMemoryReporter(this); }
144 void nsObserverService::Shutdown() {
145 if (mShuttingDown) {
146 return;
149 mShuttingDown = true;
150 UnregisterWeakMemoryReporter(this);
151 mObserverTopicTable.Clear();
154 nsresult nsObserverService::Create(nsISupports* aOuter, const nsIID& aIID,
155 void** aInstancePtr) {
156 LOG(("nsObserverService::Create()"));
158 RefPtr<nsObserverService> os = new nsObserverService();
160 // The memory reporter can not be immediately registered here because
161 // the nsMemoryReporterManager may attempt to get the nsObserverService
162 // during initialization, causing a recursive GetService.
163 NS_DispatchToCurrentThread(
164 NewRunnableMethod("nsObserverService::RegisterReporter", os,
165 &nsObserverService::RegisterReporter));
167 return os->QueryInterface(aIID, aInstancePtr);
170 nsresult nsObserverService::EnsureValidCall() const {
171 if (!NS_IsMainThread()) {
172 MOZ_CRASH("Using observer service off the main thread!");
173 return NS_ERROR_UNEXPECTED;
176 if (mShuttingDown) {
177 NS_ERROR("Using observer service after XPCOM shutdown!");
178 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
181 return NS_OK;
184 nsresult nsObserverService::FilterHttpOnTopics(const char* aTopic) {
185 // Specifically allow http-on-opening-request and http-on-stop-request in the
186 // child process; see bug 1269765.
187 if (mozilla::net::IsNeckoChild() && !strncmp(aTopic, "http-on-", 8) &&
188 strcmp(aTopic, "http-on-failed-opening-request") &&
189 strcmp(aTopic, "http-on-opening-request") &&
190 strcmp(aTopic, "http-on-stop-request") &&
191 strcmp(aTopic, "http-on-image-cache-response")) {
192 nsCOMPtr<nsIConsoleService> console(
193 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
194 nsCOMPtr<nsIScriptError> error(
195 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
196 error->Init(u"http-on-* observers only work in the parent process"_ns,
197 u""_ns, u""_ns, 0, 0, nsIScriptError::warningFlag,
198 "chrome javascript", false /* from private window */,
199 true /* from chrome context */);
200 console->LogMessage(error);
202 return NS_ERROR_NOT_IMPLEMENTED;
205 return NS_OK;
208 NS_IMETHODIMP
209 nsObserverService::AddObserver(nsIObserver* aObserver, const char* aTopic,
210 bool aOwnsWeak) {
211 LOG(("nsObserverService::AddObserver(%p: %s, %s)", (void*)aObserver, aTopic,
212 aOwnsWeak ? "weak" : "strong"));
214 MOZ_TRY(EnsureValidCall());
215 if (NS_WARN_IF(!aObserver) || NS_WARN_IF(!aTopic)) {
216 return NS_ERROR_INVALID_ARG;
219 MOZ_TRY(FilterHttpOnTopics(aTopic));
221 nsObserverList* observerList = mObserverTopicTable.PutEntry(aTopic);
222 if (!observerList) {
223 return NS_ERROR_OUT_OF_MEMORY;
226 return observerList->AddObserver(aObserver, aOwnsWeak);
229 NS_IMETHODIMP
230 nsObserverService::RemoveObserver(nsIObserver* aObserver, const char* aTopic) {
231 LOG(("nsObserverService::RemoveObserver(%p: %s)", (void*)aObserver, aTopic));
233 if (mShuttingDown) {
234 // The service is shutting down. Let's ignore this call.
235 return NS_OK;
238 MOZ_TRY(EnsureValidCall());
239 if (NS_WARN_IF(!aObserver) || NS_WARN_IF(!aTopic)) {
240 return NS_ERROR_INVALID_ARG;
243 nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
244 if (!observerList) {
245 return NS_ERROR_FAILURE;
248 return observerList->RemoveObserver(aObserver);
251 NS_IMETHODIMP
252 nsObserverService::EnumerateObservers(const char* aTopic,
253 nsISimpleEnumerator** anEnumerator) {
254 LOG(("nsObserverService::EnumerateObservers(%s)", aTopic));
256 MOZ_TRY(EnsureValidCall());
257 if (NS_WARN_IF(!anEnumerator) || NS_WARN_IF(!aTopic)) {
258 return NS_ERROR_INVALID_ARG;
261 nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
262 if (!observerList) {
263 return NS_NewEmptyEnumerator(anEnumerator);
266 observerList->GetObserverList(anEnumerator);
267 return NS_OK;
270 // Enumerate observers of aTopic and call Observe on each.
271 NS_IMETHODIMP nsObserverService::NotifyObservers(nsISupports* aSubject,
272 const char* aTopic,
273 const char16_t* aSomeData) {
274 LOG(("nsObserverService::NotifyObservers(%s)", aTopic));
276 MOZ_TRY(EnsureValidCall());
277 if (NS_WARN_IF(!aTopic)) {
278 return NS_ERROR_INVALID_ARG;
281 MOZ_ASSERT(AppShutdown::IsNoOrLegalShutdownTopic(aTopic));
283 mozilla::TimeStamp start = TimeStamp::Now();
285 AUTO_PROFILER_MARKER_TEXT("NotifyObservers", OTHER, MarkerStack::Capture(),
286 nsDependentCString(aTopic));
287 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
288 "nsObserverService::NotifyObservers", OTHER, aTopic);
290 nsObserverList* observerList = mObserverTopicTable.GetEntry(aTopic);
291 if (observerList) {
292 observerList->NotifyObservers(aSubject, aTopic, aSomeData);
295 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
296 if (latencyMs >= kMinTelemetryNotifyObserversLatencyMs) {
297 Telemetry::Accumulate(Telemetry::NOTIFY_OBSERVERS_LATENCY_MS,
298 nsDependentCString(aTopic), latencyMs);
301 return NS_OK;
304 NS_IMETHODIMP
305 nsObserverService::UnmarkGrayStrongObservers() {
306 MOZ_TRY(EnsureValidCall());
308 nsCOMArray<nsIObserver> strongObservers;
309 for (auto iter = mObserverTopicTable.Iter(); !iter.Done(); iter.Next()) {
310 nsObserverList* aObserverList = iter.Get();
311 if (aObserverList) {
312 aObserverList->AppendStrongObservers(strongObservers);
316 for (uint32_t i = 0; i < strongObservers.Length(); ++i) {
317 xpc_TryUnmarkWrappedGrayObject(strongObservers[i]);
320 return NS_OK;
323 namespace {
325 class NotifyWhenScriptSafeRunnable : public mozilla::Runnable {
326 public:
327 NotifyWhenScriptSafeRunnable(nsIObserverService* aObs, nsISupports* aSubject,
328 const char* aTopic, const char16_t* aData)
329 : mozilla::Runnable("NotifyWhenScriptSafeRunnable"),
330 mObs(aObs),
331 mSubject(aSubject),
332 mTopic(aTopic) {
333 if (aData) {
334 mData.Assign(aData);
335 } else {
336 mData.SetIsVoid(true);
340 NS_IMETHOD Run() {
341 const char16_t* data = mData.IsVoid() ? nullptr : mData.get();
342 return mObs->NotifyObservers(mSubject, mTopic.get(), data);
345 private:
346 nsCOMPtr<nsIObserverService> mObs;
347 nsCOMPtr<nsISupports> mSubject;
348 nsCString mTopic;
349 nsString mData;
352 } // namespace
354 nsresult nsIObserverService::NotifyWhenScriptSafe(nsISupports* aSubject,
355 const char* aTopic,
356 const char16_t* aData) {
357 if (nsContentUtils::IsSafeToRunScript()) {
358 return NotifyObservers(aSubject, aTopic, aData);
361 nsContentUtils::AddScriptRunner(MakeAndAddRef<NotifyWhenScriptSafeRunnable>(
362 this, aSubject, aTopic, aData));
363 return NS_OK;