void MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS, bool aPrepareForCC) { nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aNode); if (!shell) { return; } nsCOMPtr<nsIContentViewer> cview; shell->GetContentViewer(getter_AddRefs(cview)); MarkContentViewer(cview, aCleanupJS, aPrepareForCC); nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell); nsCOMPtr<nsISHistory> history; webNav->GetSessionHistory(getter_AddRefs(history)); if (history) { PRInt32 i, historyCount; history->GetCount(&historyCount); for (i = 0; i < historyCount; ++i) { nsCOMPtr<nsIHistoryEntry> historyEntry; history->GetEntryAtIndex(i, false, getter_AddRefs(historyEntry)); nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(historyEntry); MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC); } } PRInt32 i, childCount; aNode->GetChildCount(&childCount); for (i = 0; i < childCount; ++i) { nsCOMPtr<nsIDocShellTreeItem> child; aNode->GetChildAt(i, getter_AddRefs(child)); MarkDocShell(child, aCleanupJS, aPrepareForCC); } }
void MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC) { if (!aSHEntry) { return; } nsCOMPtr<nsIContentViewer> cview; aSHEntry->GetContentViewer(getter_AddRefs(cview)); MarkContentViewer(cview, aCleanupJS, aPrepareForCC); nsCOMPtr<nsIDocShellTreeItem> child; PRInt32 i = 0; while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) && child) { MarkDocShell(child, aCleanupJS, aPrepareForCC); } nsCOMPtr<nsISHContainer> shCont = do_QueryInterface(aSHEntry); PRInt32 count; shCont->GetChildCount(&count); for (i = 0; i < count; ++i) { nsCOMPtr<nsISHEntry> childEntry; shCont->GetChildAt(i, getter_AddRefs(childEntry)); MarkSHEntry(childEntry, aCleanupJS, aPrepareForCC); } }
void MarkWindowList(nsISimpleEnumerator* aWindowList) { nsCOMPtr<nsISupports> iter; while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) && iter) { nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(iter); if (window) { nsCOMPtr<nsIDocShellTreeNode> rootDocShell = do_QueryInterface(window->GetDocShell()); MarkDocShell(rootDocShell); } } }
nsresult nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { if (!strcmp(aTopic, "xpcom-shutdown")) { nsGenericElement::ClearContentUnbinder(); nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (!obs) return NS_ERROR_FAILURE; // No need for kungFuDeathGrip here, yay observerservice! obs->RemoveObserver(this, "xpcom-shutdown"); obs->RemoveObserver(this, "cycle-collector-begin"); obs->RemoveObserver(this, "cycle-collector-forget-skippable"); sGeneration = 0; return NS_OK; } NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") || !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic"); // JS cleanup can be slow. Do it only if there has been a GC. bool cleanupJS = nsJSContext::CleanupsSinceLastGC() == 0 && !strcmp(aTopic, "cycle-collector-forget-skippable"); bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin"); if (prepareForCC) { nsGenericElement::ClearContentUnbinder(); } // Increase generation to effectivly unmark all current objects if (!++sGeneration) { ++sGeneration; } nsresult rv; // Iterate all toplevel windows nsCOMPtr<nsISimpleEnumerator> windowList; nsCOMPtr<nsIWindowMediator> med = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); if (med) { rv = med->GetEnumerator(nullptr, getter_AddRefs(windowList)); NS_ENSURE_SUCCESS(rv, rv); MarkWindowList(windowList, cleanupJS, prepareForCC); } nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID); if (ww) { rv = ww->GetWindowEnumerator(getter_AddRefs(windowList)); NS_ENSURE_SUCCESS(rv, rv); MarkWindowList(windowList, cleanupJS, prepareForCC); } nsCOMPtr<nsIAppShellService> appShell = do_GetService(NS_APPSHELLSERVICE_CONTRACTID); if (appShell) { nsCOMPtr<nsIXULWindow> hw; appShell->GetHiddenWindow(getter_AddRefs(hw)); if (hw) { nsCOMPtr<nsIDocShell> shell; hw->GetDocShell(getter_AddRefs(shell)); nsCOMPtr<nsIDocShellTreeNode> shellTreeNode = do_QueryInterface(shell); MarkDocShell(shellTreeNode, cleanupJS, prepareForCC); } } #ifdef MOZ_XUL nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance(); if (xulCache) { xulCache->MarkInCCGeneration(sGeneration); } #endif static bool previousWasJSCleanup = false; if (cleanupJS) { nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration); MarkMessageManagers(); nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); static_cast<nsObserverService *>(obs.get())->UnmarkGrayStrongObservers(); previousWasJSCleanup = true; } else if (previousWasJSCleanup) { previousWasJSCleanup = false; if (!prepareForCC) { xpc_UnmarkSkippableJSHolders(); } } return NS_OK; }