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 "nsIContentViewer.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
)
60 nsresult
nsCCUncollectableMarker::Init() {
65 nsCOMPtr
<nsIObserver
> marker
= new nsCCUncollectableMarker
;
67 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
68 if (!obs
) return NS_ERROR_FAILURE
;
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
);
86 static void MarkChildMessageManagers(MessageBroadcaster
* aMM
) {
89 uint32_t browserChildCount
= aMM
->ChildCount();
90 for (uint32_t j
= 0; j
< browserChildCount
; ++j
) {
91 RefPtr
<MessageListenerManager
> childMM
= aMM
->GetChildAt(j
);
96 RefPtr
<MessageBroadcaster
> strongNonLeafMM
=
97 MessageBroadcaster::From(childMM
);
98 MessageBroadcaster
* nonLeafMM
= strongNonLeafMM
;
100 MessageListenerManager
* tabMM
= childMM
;
102 strongNonLeafMM
= nullptr;
106 MarkChildMessageManagers(nonLeafMM
);
112 // XXX hack warning, but works, since we know that
113 // callback is frameloader.
114 mozilla::dom::ipc::MessageManagerCallback
* cb
= tabMM
->GetCallback();
116 nsFrameLoader
* fl
= static_cast<nsFrameLoader
*>(cb
);
117 InProcessBrowserChildMessageManager
* et
=
118 fl
->GetBrowserChildMessageManager();
123 EventListenerManager
* elm
= et
->GetExistingListenerManager();
131 static void MarkMessageManagers() {
132 if (nsFrameMessageManager::GetChildProcessManager()) {
133 // ContentProcessMessageManager's MarkForCC also marks ChildProcessManager.
134 ContentProcessMessageManager
* pg
= ContentProcessMessageManager::Get();
140 // The global message manager only exists in the root process.
141 if (!XRE_IsParentProcess()) {
144 RefPtr
<ChromeMessageBroadcaster
> strongGlobalMM
=
145 nsFrameMessageManager::GetGlobalMessageManager();
146 if (!strongGlobalMM
) {
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
);
163 MessageListenerManager
* child
= childMM
;
168 if (nsFrameMessageManager::sSameProcessParentManager
) {
169 nsFrameMessageManager::sSameProcessParentManager
->MarkForCC();
173 void MarkContentViewer(nsIContentViewer
* aViewer
, bool aCleanupJS
) {
178 Document
* doc
= aViewer
->GetDocument();
180 doc
->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration
) {
181 doc
->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration
);
183 EventListenerManager
* elm
= doc
->GetExistingListenerManager();
187 RefPtr
<nsGlobalWindowInner
> win
=
188 nsGlobalWindowInner::Cast(doc
->GetInnerWindow());
190 elm
= win
->GetExistingListenerManager();
194 win
->TimeoutManager().UnmarkGrayTimers();
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
) {
217 nsCOMPtr
<nsIContentViewer
> cview
;
218 aSHEntry
->GetContentViewer(getter_AddRefs(cview
));
219 MarkContentViewer(cview
, aCleanupJS
);
221 nsCOMPtr
<nsIDocShellTreeItem
> child
;
223 while (NS_SUCCEEDED(aSHEntry
->ChildShellAt(i
++, getter_AddRefs(child
))) &&
225 MarkDocShell(child
, aCleanupJS
);
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
);
243 nsCOMPtr
<nsIContentViewer
> cview
;
244 shell
->GetContentViewer(getter_AddRefs(cview
));
245 MarkContentViewer(cview
, 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;
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
);
282 RefPtr
<BrowserChildMessageManager
> mm
=
283 browserChild
->GetMessageManager();
285 // MarkForCC ends up calling UnmarkGray on message listeners, which
286 // TraceBlackJS can't do yet.
294 nsresult
nsCCUncollectableMarker::Observe(nsISupports
* aSubject
,
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");
313 NS_ASSERTION(!strcmp(aTopic
, "cycle-collector-begin") ||
314 !strcmp(aTopic
, "cycle-collector-forget-skippable"),
317 // JS cleanup can be slow. Do it only if this is the first forget-skippable
319 const bool cleanupJS
= nsJSContext::HasHadCleanupSinceLastGC() &&
320 !strcmp(aTopic
, "cycle-collector-forget-skippable");
322 const bool prepareForCC
= !strcmp(aTopic
, "cycle-collector-begin");
324 Element::ClearContentUnbinder();
327 // Increase generation to effectively unmark all current objects
328 if (!++sGeneration
) {
332 nsFocusManager::MarkUncollectableForCCGeneration(sGeneration
);
336 // Iterate all toplevel windows
337 nsCOMPtr
<nsISimpleEnumerator
> windowList
;
338 nsCOMPtr
<nsIWindowMediator
> med
= do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
);
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
);
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
);
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();
370 xulCache
->MarkInCCGeneration(sGeneration
);
373 enum ForgetSkippableCleanupState
{
375 eUnmarkJSEventListeners
= 1,
376 eUnmarkMessageManagers
= 2,
377 eUnmarkStrongObservers
= 3,
378 eUnmarkJSHolders
= 4,
382 static_assert(eDone
== kMajorForgetSkippableCalls
,
383 "There must be one forgetSkippable call per cleanup state.");
385 static uint32_t sFSState
= eDone
;
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.
403 case eUnmarkJSEventListeners
: {
404 nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments();
407 case eUnmarkMessageManagers
: {
408 MarkMessageManagers();
411 case eUnmarkStrongObservers
: {
412 nsCOMPtr
<nsIObserverService
> obs
=
413 mozilla::services::GetObserverService();
414 static_cast<nsObserverService
*>(obs
.get())->UnmarkGrayStrongObservers();
417 case eUnmarkJSHolders
: {
418 xpc_UnmarkSkippableJSHolders();
429 void mozilla::dom::TraceBlackJS(JSTracer
* aTrc
) {
430 if (!nsCCUncollectableMarker::sGeneration
) {
434 if (ContentProcessMessageManager::WasCreated() &&
435 nsFrameMessageManager::GetChildProcessManager()) {
436 auto* pg
= ContentProcessMessageManager::Get();
438 mozilla::TraceScriptHolder(ToSupports(pg
), aTrc
);
442 // Mark globals of active windows black.
443 nsGlobalWindowOuter::OuterWindowByIdTable
* windowsById
=
444 nsGlobalWindowOuter::GetWindowsTable();
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();
458 elm
->TraceListeners(aTrc
);
460 CustomElementRegistry
* cer
= inner
->GetExistingCustomElements();
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,
472 nsIDocShell
* ds
= window
->GetDocShell();
474 nsCOMPtr
<nsIBrowserChild
> browserChild
= ds
->GetBrowserChild();
476 RefPtr
<ContentFrameMessageManager
> mm
;
477 browserChild
->GetMessageManager(getter_AddRefs(mm
));
479 nsCOMPtr
<nsISupports
> browserChildAsSupports
=
480 do_QueryInterface(browserChild
);
481 mozilla::TraceScriptHolder(browserChildAsSupports
, aTrc
);
482 EventListenerManager
* elm
= mm
->GetExistingListenerManager();
484 elm
->TraceListeners(aTrc
);
486 // As of now there isn't an easy way to trace message listeners.