Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsCCUncollectableMarker.cpp
blobc906a2002156519fc1bf30aa52a3ad3cdcfc62ab
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 "nsCCUncollectableMarker.h"
8 #include "nsIObserverService.h"
9 #include "nsIDocShell.h"
10 #include "nsServiceManagerUtils.h"
11 #include "nsIDocumentViewer.h"
12 #include "mozilla/dom/Document.h"
13 #include "InProcessBrowserChildMessageManager.h"
14 #include "nsIWindowMediator.h"
15 #include "nsPIDOMWindow.h"
16 #include "nsIWebNavigation.h"
17 #include "nsISHistory.h"
18 #include "nsISHEntry.h"
19 #include "nsIWindowWatcher.h"
20 #include "mozilla/Services.h"
21 #include "nsIAppWindow.h"
22 #include "nsIAppShellService.h"
23 #include "nsAppShellCID.h"
24 #include "nsContentUtils.h"
25 #include "nsGlobalWindowInner.h"
26 #include "nsGlobalWindowOuter.h"
27 #include "nsJSEnvironment.h"
28 #include "nsFrameLoader.h"
29 #include "mozilla/CycleCollectedJSContext.h"
30 #include "mozilla/CycleCollectedJSRuntime.h"
31 #include "mozilla/EventListenerManager.h"
32 #include "mozilla/dom/ChromeMessageBroadcaster.h"
33 #include "mozilla/dom/ContentFrameMessageManager.h"
34 #include "mozilla/dom/ContentProcessMessageManager.h"
35 #include "mozilla/dom/CustomElementRegistry.h"
36 #include "mozilla/dom/Element.h"
37 #include "mozilla/dom/ParentProcessMessageManager.h"
38 #include "mozilla/dom/BrowserChild.h"
39 #include "mozilla/dom/TimeoutManager.h"
40 #include "xpcpublic.h"
41 #include "nsObserverService.h"
42 #include "nsFocusManager.h"
43 #include "nsIInterfaceRequestorUtils.h"
44 #include "nsIXULRuntime.h"
46 using namespace mozilla;
47 using namespace mozilla::dom;
49 static bool sInited = 0;
50 // The initial value of sGeneration should not be the same as the
51 // value it is given at xpcom-shutdown, because this will make any GCs
52 // before we first CC benignly violate the black-gray invariant, due
53 // to dom::TraceBlackJS().
54 uint32_t nsCCUncollectableMarker::sGeneration = 1;
55 #include "nsXULPrototypeCache.h"
57 NS_IMPL_ISUPPORTS(nsCCUncollectableMarker, nsIObserver)
59 /* static */
60 nsresult nsCCUncollectableMarker::Init() {
61 if (sInited) {
62 return NS_OK;
65 nsCOMPtr<nsIObserver> marker = new nsCCUncollectableMarker;
67 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
68 if (!obs) return NS_ERROR_FAILURE;
70 nsresult rv;
72 // This makes the observer service hold an owning reference to the marker
73 rv = obs->AddObserver(marker, "xpcom-shutdown", false);
74 NS_ENSURE_SUCCESS(rv, rv);
76 rv = obs->AddObserver(marker, "cycle-collector-begin", false);
77 NS_ENSURE_SUCCESS(rv, rv);
78 rv = obs->AddObserver(marker, "cycle-collector-forget-skippable", false);
79 NS_ENSURE_SUCCESS(rv, rv);
81 sInited = true;
83 return NS_OK;
86 static void MarkChildMessageManagers(MessageBroadcaster* aMM) {
87 aMM->MarkForCC();
89 uint32_t browserChildCount = aMM->ChildCount();
90 for (uint32_t j = 0; j < browserChildCount; ++j) {
91 RefPtr<MessageListenerManager> childMM = aMM->GetChildAt(j);
92 if (!childMM) {
93 continue;
96 RefPtr<MessageBroadcaster> strongNonLeafMM =
97 MessageBroadcaster::From(childMM);
98 MessageBroadcaster* nonLeafMM = strongNonLeafMM;
100 MessageListenerManager* tabMM = childMM;
102 strongNonLeafMM = nullptr;
103 childMM = nullptr;
105 if (nonLeafMM) {
106 MarkChildMessageManagers(nonLeafMM);
107 continue;
110 tabMM->MarkForCC();
112 // XXX hack warning, but works, since we know that
113 // callback is frameloader.
114 mozilla::dom::ipc::MessageManagerCallback* cb = tabMM->GetCallback();
115 if (cb) {
116 nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
117 InProcessBrowserChildMessageManager* et =
118 fl->GetBrowserChildMessageManager();
119 if (!et) {
120 continue;
122 et->MarkForCC();
123 EventListenerManager* elm = et->GetExistingListenerManager();
124 if (elm) {
125 elm->MarkForCC();
131 static void MarkMessageManagers() {
132 if (nsFrameMessageManager::GetChildProcessManager()) {
133 // ContentProcessMessageManager's MarkForCC also marks ChildProcessManager.
134 ContentProcessMessageManager* pg = ContentProcessMessageManager::Get();
135 if (pg) {
136 pg->MarkForCC();
140 // The global message manager only exists in the root process.
141 if (!XRE_IsParentProcess()) {
142 return;
144 RefPtr<ChromeMessageBroadcaster> strongGlobalMM =
145 nsFrameMessageManager::GetGlobalMessageManager();
146 if (!strongGlobalMM) {
147 return;
149 ChromeMessageBroadcaster* globalMM = strongGlobalMM;
150 strongGlobalMM = nullptr;
151 MarkChildMessageManagers(globalMM);
153 if (nsFrameMessageManager::sParentProcessManager) {
154 nsFrameMessageManager::sParentProcessManager->MarkForCC();
155 uint32_t childCount =
156 nsFrameMessageManager::sParentProcessManager->ChildCount();
157 for (uint32_t i = 0; i < childCount; ++i) {
158 RefPtr<MessageListenerManager> childMM =
159 nsFrameMessageManager::sParentProcessManager->GetChildAt(i);
160 if (!childMM) {
161 continue;
163 MessageListenerManager* child = childMM;
164 childMM = nullptr;
165 child->MarkForCC();
168 if (nsFrameMessageManager::sSameProcessParentManager) {
169 nsFrameMessageManager::sSameProcessParentManager->MarkForCC();
173 void MarkDocumentViewer(nsIDocumentViewer* aViewer, bool aCleanupJS) {
174 if (!aViewer) {
175 return;
178 Document* doc = aViewer->GetDocument();
179 if (doc &&
180 doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) {
181 doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
182 if (aCleanupJS) {
183 EventListenerManager* elm = doc->GetExistingListenerManager();
184 if (elm) {
185 elm->MarkForCC();
187 RefPtr<nsGlobalWindowInner> win =
188 nsGlobalWindowInner::Cast(doc->GetInnerWindow());
189 if (win) {
190 elm = win->GetExistingListenerManager();
191 if (elm) {
192 elm->MarkForCC();
194 win->TimeoutManager().UnmarkGrayTimers();
198 if (doc) {
199 if (nsPIDOMWindowInner* inner = doc->GetInnerWindow()) {
200 inner->MarkUncollectableForCCGeneration(
201 nsCCUncollectableMarker::sGeneration);
203 if (nsPIDOMWindowOuter* outer = doc->GetWindow()) {
204 outer->MarkUncollectableForCCGeneration(
205 nsCCUncollectableMarker::sGeneration);
210 void MarkDocShell(nsIDocShellTreeItem* aNode, bool aCleanupJS);
212 void MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS) {
213 if (!aSHEntry) {
214 return;
217 nsCOMPtr<nsIDocumentViewer> viewer;
218 aSHEntry->GetDocumentViewer(getter_AddRefs(viewer));
219 MarkDocumentViewer(viewer, aCleanupJS);
221 nsCOMPtr<nsIDocShellTreeItem> child;
222 int32_t i = 0;
223 while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) &&
224 child) {
225 MarkDocShell(child, aCleanupJS);
228 int32_t count;
229 aSHEntry->GetChildCount(&count);
230 for (i = 0; i < count; ++i) {
231 nsCOMPtr<nsISHEntry> childEntry;
232 aSHEntry->GetChildAt(i, getter_AddRefs(childEntry));
233 MarkSHEntry(childEntry, aCleanupJS);
237 void MarkDocShell(nsIDocShellTreeItem* aNode, bool aCleanupJS) {
238 nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aNode);
239 if (!shell) {
240 return;
243 nsCOMPtr<nsIDocumentViewer> viewer;
244 shell->GetDocViewer(getter_AddRefs(viewer));
245 MarkDocumentViewer(viewer, aCleanupJS);
247 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell);
248 RefPtr<ChildSHistory> history = webNav->GetSessionHistory();
249 IgnoredErrorResult ignore;
250 nsISHistory* legacyHistory =
251 history ? history->GetLegacySHistory(ignore) : nullptr;
252 if (legacyHistory) {
253 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
254 int32_t historyCount = history->Count();
255 for (int32_t i = 0; i < historyCount; ++i) {
256 nsCOMPtr<nsISHEntry> shEntry;
257 legacyHistory->GetEntryAtIndex(i, getter_AddRefs(shEntry));
259 MarkSHEntry(shEntry, aCleanupJS);
263 int32_t i, childCount;
264 aNode->GetInProcessChildCount(&childCount);
265 for (i = 0; i < childCount; ++i) {
266 nsCOMPtr<nsIDocShellTreeItem> child;
267 aNode->GetInProcessChildAt(i, getter_AddRefs(child));
268 MarkDocShell(child, aCleanupJS);
272 void MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS) {
273 nsCOMPtr<nsISupports> iter;
274 while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) && iter) {
275 if (nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(iter)) {
276 nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell();
278 MarkDocShell(rootDocShell, aCleanupJS);
280 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(rootDocShell);
281 if (browserChild) {
282 RefPtr<BrowserChildMessageManager> mm =
283 browserChild->GetMessageManager();
284 if (mm) {
285 // MarkForCC ends up calling UnmarkGray on message listeners, which
286 // TraceBlackJS can't do yet.
287 mm->MarkForCC();
294 nsresult nsCCUncollectableMarker::Observe(nsISupports* aSubject,
295 const char* aTopic,
296 const char16_t* aData) {
297 if (!strcmp(aTopic, "xpcom-shutdown")) {
298 Element::ClearContentUnbinder();
300 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
301 if (!obs) return NS_ERROR_FAILURE;
303 // No need for kungFuDeathGrip here, yay observerservice!
304 obs->RemoveObserver(this, "xpcom-shutdown");
305 obs->RemoveObserver(this, "cycle-collector-begin");
306 obs->RemoveObserver(this, "cycle-collector-forget-skippable");
308 sGeneration = 0;
310 return NS_OK;
313 NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") ||
314 !strcmp(aTopic, "cycle-collector-forget-skippable"),
315 "wrong topic");
317 // JS cleanup can be slow. Do it only if this is the first forget-skippable
318 // after a GC.
319 const bool cleanupJS = nsJSContext::HasHadCleanupSinceLastGC() &&
320 !strcmp(aTopic, "cycle-collector-forget-skippable");
322 const bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");
323 if (prepareForCC) {
324 Element::ClearContentUnbinder();
327 // Increase generation to effectively unmark all current objects
328 if (!++sGeneration) {
329 ++sGeneration;
332 nsFocusManager::MarkUncollectableForCCGeneration(sGeneration);
334 nsresult rv;
336 // Iterate all toplevel windows
337 nsCOMPtr<nsISimpleEnumerator> windowList;
338 nsCOMPtr<nsIWindowMediator> med = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
339 if (med) {
340 rv = med->GetEnumerator(nullptr, getter_AddRefs(windowList));
341 NS_ENSURE_SUCCESS(rv, rv);
343 MarkWindowList(windowList, cleanupJS);
346 nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
347 if (ww) {
348 rv = ww->GetWindowEnumerator(getter_AddRefs(windowList));
349 NS_ENSURE_SUCCESS(rv, rv);
351 MarkWindowList(windowList, cleanupJS);
354 nsCOMPtr<nsIAppShellService> appShell =
355 do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
356 if (appShell) {
357 bool hasHiddenWindow = false;
358 appShell->GetHasHiddenWindow(&hasHiddenWindow);
359 if (hasHiddenWindow) {
360 nsCOMPtr<nsIAppWindow> hw;
361 appShell->GetHiddenWindow(getter_AddRefs(hw));
362 nsCOMPtr<nsIDocShell> shell;
363 hw->GetDocShell(getter_AddRefs(shell));
364 MarkDocShell(shell, cleanupJS);
368 nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance();
369 if (xulCache) {
370 xulCache->MarkInCCGeneration(sGeneration);
373 enum ForgetSkippableCleanupState {
374 eInitial = 0,
375 eUnmarkJSEventListeners = 1,
376 eUnmarkMessageManagers = 2,
377 eUnmarkStrongObservers = 3,
378 eUnmarkJSHolders = 4,
379 eDone = 5
382 static_assert(eDone == kMajorForgetSkippableCalls,
383 "There must be one forgetSkippable call per cleanup state.");
385 static uint32_t sFSState = eDone;
386 if (prepareForCC) {
387 sFSState = eDone;
388 return NS_OK;
391 if (cleanupJS) {
392 // After a GC we start clean up phases from the beginning,
393 // but we don't want to do the additional clean up phases here
394 // since we have done already plenty of gray unmarking while going through
395 // frame message managers and docshells.
396 sFSState = eInitial;
397 return NS_OK;
398 } else {
399 ++sFSState;
402 switch (sFSState) {
403 case eUnmarkJSEventListeners: {
404 nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments();
405 break;
407 case eUnmarkMessageManagers: {
408 MarkMessageManagers();
409 break;
411 case eUnmarkStrongObservers: {
412 nsCOMPtr<nsIObserverService> obs =
413 mozilla::services::GetObserverService();
414 static_cast<nsObserverService*>(obs.get())->UnmarkGrayStrongObservers();
415 break;
417 case eUnmarkJSHolders: {
418 xpc_UnmarkSkippableJSHolders();
419 break;
421 default: {
422 break;
426 return NS_OK;
429 void mozilla::dom::TraceBlackJS(JSTracer* aTrc) {
430 if (!nsCCUncollectableMarker::sGeneration) {
431 return;
434 if (ContentProcessMessageManager::WasCreated() &&
435 nsFrameMessageManager::GetChildProcessManager()) {
436 auto* pg = ContentProcessMessageManager::Get();
437 if (pg) {
438 mozilla::TraceScriptHolder(ToSupports(pg), aTrc);
442 // Mark globals of active windows black.
443 nsGlobalWindowOuter::OuterWindowByIdTable* windowsById =
444 nsGlobalWindowOuter::GetWindowsTable();
445 if (windowsById) {
446 for (nsGlobalWindowOuter* window : windowsById->Values()) {
447 if (!window->IsCleanedUp()) {
448 nsGlobalWindowInner* inner = nullptr;
449 for (PRCList* win = PR_LIST_HEAD(window); win != window;
450 win = PR_NEXT_LINK(inner)) {
451 inner = static_cast<nsGlobalWindowInner*>(win);
452 if (inner->IsCurrentInnerWindow() ||
453 (inner->GetExtantDoc() &&
454 inner->GetExtantDoc()->GetBFCacheEntry())) {
455 inner->TraceGlobalJSObject(aTrc);
456 EventListenerManager* elm = inner->GetExistingListenerManager();
457 if (elm) {
458 elm->TraceListeners(aTrc);
460 CustomElementRegistry* cer = inner->GetExistingCustomElements();
461 if (cer) {
462 cer->TraceDefinitions(aTrc);
467 if (window->IsRootOuterWindow()) {
468 // In child process trace all the BrowserChildMessageManagers.
469 // Since there is one root outer window per
470 // BrowserChildMessageManager, we need to look for only those windows,
471 // not all.
472 nsIDocShell* ds = window->GetDocShell();
473 if (ds) {
474 nsCOMPtr<nsIBrowserChild> browserChild = ds->GetBrowserChild();
475 if (browserChild) {
476 RefPtr<ContentFrameMessageManager> mm;
477 browserChild->GetMessageManager(getter_AddRefs(mm));
478 if (mm) {
479 nsCOMPtr<nsISupports> browserChildAsSupports =
480 do_QueryInterface(browserChild);
481 mozilla::TraceScriptHolder(browserChildAsSupports, aTrc);
482 EventListenerManager* elm = mm->GetExistingListenerManager();
483 if (elm) {
484 elm->TraceListeners(aTrc);
486 // As of now there isn't an easy way to trace message listeners.