1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsCCUncollectableMarker.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,479 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 "nsCCUncollectableMarker.h" 1.10 +#include "nsIObserverService.h" 1.11 +#include "nsIDocShell.h" 1.12 +#include "nsServiceManagerUtils.h" 1.13 +#include "nsIContentViewer.h" 1.14 +#include "nsIDocument.h" 1.15 +#include "XULDocument.h" 1.16 +#include "nsIWindowMediator.h" 1.17 +#include "nsPIDOMWindow.h" 1.18 +#include "nsIWebNavigation.h" 1.19 +#include "nsISHistory.h" 1.20 +#include "nsISHEntry.h" 1.21 +#include "nsISHContainer.h" 1.22 +#include "nsIWindowWatcher.h" 1.23 +#include "mozilla/Services.h" 1.24 +#include "nsIXULWindow.h" 1.25 +#include "nsIAppShellService.h" 1.26 +#include "nsAppShellCID.h" 1.27 +#include "nsContentUtils.h" 1.28 +#include "nsGlobalWindow.h" 1.29 +#include "nsJSEnvironment.h" 1.30 +#include "nsInProcessTabChildGlobal.h" 1.31 +#include "nsFrameLoader.h" 1.32 +#include "mozilla/EventListenerManager.h" 1.33 +#include "mozilla/dom/Element.h" 1.34 +#include "xpcpublic.h" 1.35 +#include "nsObserverService.h" 1.36 +#include "nsFocusManager.h" 1.37 + 1.38 +using namespace mozilla; 1.39 +using namespace mozilla::dom; 1.40 + 1.41 +static bool sInited = 0; 1.42 +uint32_t nsCCUncollectableMarker::sGeneration = 0; 1.43 +#ifdef MOZ_XUL 1.44 +#include "nsXULPrototypeCache.h" 1.45 +#endif 1.46 + 1.47 +NS_IMPL_ISUPPORTS(nsCCUncollectableMarker, nsIObserver) 1.48 + 1.49 +/* static */ 1.50 +nsresult 1.51 +nsCCUncollectableMarker::Init() 1.52 +{ 1.53 + if (sInited) { 1.54 + return NS_OK; 1.55 + } 1.56 + 1.57 + nsCOMPtr<nsIObserver> marker = new nsCCUncollectableMarker; 1.58 + NS_ENSURE_TRUE(marker, NS_ERROR_OUT_OF_MEMORY); 1.59 + 1.60 + nsCOMPtr<nsIObserverService> obs = 1.61 + mozilla::services::GetObserverService(); 1.62 + if (!obs) 1.63 + return NS_ERROR_FAILURE; 1.64 + 1.65 + nsresult rv; 1.66 + 1.67 + // This makes the observer service hold an owning reference to the marker 1.68 + rv = obs->AddObserver(marker, "xpcom-shutdown", false); 1.69 + NS_ENSURE_SUCCESS(rv, rv); 1.70 + 1.71 + rv = obs->AddObserver(marker, "cycle-collector-begin", false); 1.72 + NS_ENSURE_SUCCESS(rv, rv); 1.73 + rv = obs->AddObserver(marker, "cycle-collector-forget-skippable", false); 1.74 + NS_ENSURE_SUCCESS(rv, rv); 1.75 + 1.76 + sInited = true; 1.77 + 1.78 + return NS_OK; 1.79 +} 1.80 + 1.81 +static void 1.82 +MarkUserData(void* aNode, nsIAtom* aKey, void* aValue, void* aData) 1.83 +{ 1.84 + nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc(); 1.85 + if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) { 1.86 + Element::MarkUserData(aNode, aKey, aValue, aData); 1.87 + } 1.88 +} 1.89 + 1.90 +static void 1.91 +MarkUserDataHandler(void* aNode, nsIAtom* aKey, void* aValue, void* aData) 1.92 +{ 1.93 + nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc(); 1.94 + if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) { 1.95 + Element::MarkUserDataHandler(aNode, aKey, aValue, aData); 1.96 + } 1.97 +} 1.98 + 1.99 +static void 1.100 +MarkMessageManagers() 1.101 +{ 1.102 + // The global message manager only exists in the root process. 1.103 + if (XRE_GetProcessType() != GeckoProcessType_Default) { 1.104 + return; 1.105 + } 1.106 + nsCOMPtr<nsIMessageBroadcaster> strongGlobalMM = 1.107 + do_GetService("@mozilla.org/globalmessagemanager;1"); 1.108 + if (!strongGlobalMM) { 1.109 + return; 1.110 + } 1.111 + nsIMessageBroadcaster* globalMM = strongGlobalMM; 1.112 + strongGlobalMM = nullptr; 1.113 + 1.114 + globalMM->MarkForCC(); 1.115 + uint32_t childCount = 0; 1.116 + globalMM->GetChildCount(&childCount); 1.117 + for (uint32_t i = 0; i < childCount; ++i) { 1.118 + nsCOMPtr<nsIMessageListenerManager> childMM; 1.119 + globalMM->GetChildAt(i, getter_AddRefs(childMM)); 1.120 + if (!childMM) { 1.121 + continue; 1.122 + } 1.123 + nsCOMPtr<nsIMessageBroadcaster> strongWindowMM = do_QueryInterface(childMM); 1.124 + nsIMessageBroadcaster* windowMM = strongWindowMM; 1.125 + childMM = nullptr; 1.126 + strongWindowMM = nullptr; 1.127 + windowMM->MarkForCC(); 1.128 + uint32_t tabChildCount = 0; 1.129 + windowMM->GetChildCount(&tabChildCount); 1.130 + for (uint32_t j = 0; j < tabChildCount; ++j) { 1.131 + nsCOMPtr<nsIMessageListenerManager> childMM; 1.132 + windowMM->GetChildAt(j, getter_AddRefs(childMM)); 1.133 + if (!childMM) { 1.134 + continue; 1.135 + } 1.136 + nsCOMPtr<nsIMessageSender> strongTabMM = do_QueryInterface(childMM); 1.137 + nsIMessageSender* tabMM = strongTabMM; 1.138 + childMM = nullptr; 1.139 + strongTabMM = nullptr; 1.140 + tabMM->MarkForCC(); 1.141 + //XXX hack warning, but works, since we know that 1.142 + // callback is frameloader. 1.143 + mozilla::dom::ipc::MessageManagerCallback* cb = 1.144 + static_cast<nsFrameMessageManager*>(tabMM)->GetCallback(); 1.145 + if (cb) { 1.146 + nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb); 1.147 + EventTarget* et = fl->GetTabChildGlobalAsEventTarget(); 1.148 + if (!et) { 1.149 + continue; 1.150 + } 1.151 + static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC(); 1.152 + EventListenerManager* elm = et->GetExistingListenerManager(); 1.153 + if (elm) { 1.154 + elm->MarkForCC(); 1.155 + } 1.156 + } 1.157 + } 1.158 + } 1.159 + if (nsFrameMessageManager::sParentProcessManager) { 1.160 + nsFrameMessageManager::sParentProcessManager->MarkForCC(); 1.161 + uint32_t childCount = 0; 1.162 + nsFrameMessageManager::sParentProcessManager->GetChildCount(&childCount); 1.163 + for (uint32_t i = 0; i < childCount; ++i) { 1.164 + nsCOMPtr<nsIMessageListenerManager> childMM; 1.165 + nsFrameMessageManager::sParentProcessManager-> 1.166 + GetChildAt(i, getter_AddRefs(childMM)); 1.167 + if (!childMM) { 1.168 + continue; 1.169 + } 1.170 + nsIMessageListenerManager* child = childMM; 1.171 + childMM = nullptr; 1.172 + child->MarkForCC(); 1.173 + } 1.174 + } 1.175 + if (nsFrameMessageManager::sSameProcessParentManager) { 1.176 + nsFrameMessageManager::sSameProcessParentManager->MarkForCC(); 1.177 + } 1.178 + if (nsFrameMessageManager::sChildProcessManager) { 1.179 + nsFrameMessageManager::sChildProcessManager->MarkForCC(); 1.180 + } 1.181 +} 1.182 + 1.183 +void 1.184 +MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS, 1.185 + bool aPrepareForCC) 1.186 +{ 1.187 + if (!aViewer) { 1.188 + return; 1.189 + } 1.190 + 1.191 + nsIDocument *doc = aViewer->GetDocument(); 1.192 + if (doc && 1.193 + doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) { 1.194 + doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); 1.195 + if (aCleanupJS) { 1.196 + EventListenerManager* elm = doc->GetExistingListenerManager(); 1.197 + if (elm) { 1.198 + elm->MarkForCC(); 1.199 + } 1.200 + nsCOMPtr<EventTarget> win = do_QueryInterface(doc->GetInnerWindow()); 1.201 + if (win) { 1.202 + elm = win->GetExistingListenerManager(); 1.203 + if (elm) { 1.204 + elm->MarkForCC(); 1.205 + } 1.206 + static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers(); 1.207 + } 1.208 + 1.209 + doc->PropertyTable(DOM_USER_DATA_HANDLER)-> 1.210 + EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration); 1.211 + } else if (aPrepareForCC) { 1.212 + // Unfortunately we need to still mark user data just before running CC so 1.213 + // that it has the right generation. 1.214 + doc->PropertyTable(DOM_USER_DATA)-> 1.215 + EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration); 1.216 + } 1.217 + } 1.218 + if (doc) { 1.219 + nsPIDOMWindow* inner = doc->GetInnerWindow(); 1.220 + if (inner) { 1.221 + inner->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); 1.222 + } 1.223 + nsPIDOMWindow* outer = doc->GetWindow(); 1.224 + if (outer) { 1.225 + outer->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); 1.226 + } 1.227 + } 1.228 +} 1.229 + 1.230 +void MarkDocShell(nsIDocShellTreeItem* aNode, bool aCleanupJS, 1.231 + bool aPrepareForCC); 1.232 + 1.233 +void 1.234 +MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC) 1.235 +{ 1.236 + if (!aSHEntry) { 1.237 + return; 1.238 + } 1.239 + 1.240 + nsCOMPtr<nsIContentViewer> cview; 1.241 + aSHEntry->GetContentViewer(getter_AddRefs(cview)); 1.242 + MarkContentViewer(cview, aCleanupJS, aPrepareForCC); 1.243 + 1.244 + nsCOMPtr<nsIDocShellTreeItem> child; 1.245 + int32_t i = 0; 1.246 + while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) && 1.247 + child) { 1.248 + MarkDocShell(child, aCleanupJS, aPrepareForCC); 1.249 + } 1.250 + 1.251 + nsCOMPtr<nsISHContainer> shCont = do_QueryInterface(aSHEntry); 1.252 + int32_t count; 1.253 + shCont->GetChildCount(&count); 1.254 + for (i = 0; i < count; ++i) { 1.255 + nsCOMPtr<nsISHEntry> childEntry; 1.256 + shCont->GetChildAt(i, getter_AddRefs(childEntry)); 1.257 + MarkSHEntry(childEntry, aCleanupJS, aPrepareForCC); 1.258 + } 1.259 + 1.260 +} 1.261 + 1.262 +void 1.263 +MarkDocShell(nsIDocShellTreeItem* aNode, bool aCleanupJS, bool aPrepareForCC) 1.264 +{ 1.265 + nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aNode); 1.266 + if (!shell) { 1.267 + return; 1.268 + } 1.269 + 1.270 + nsCOMPtr<nsIContentViewer> cview; 1.271 + shell->GetContentViewer(getter_AddRefs(cview)); 1.272 + MarkContentViewer(cview, aCleanupJS, aPrepareForCC); 1.273 + 1.274 + nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell); 1.275 + nsCOMPtr<nsISHistory> history; 1.276 + webNav->GetSessionHistory(getter_AddRefs(history)); 1.277 + if (history) { 1.278 + int32_t i, historyCount; 1.279 + history->GetCount(&historyCount); 1.280 + for (i = 0; i < historyCount; ++i) { 1.281 + nsCOMPtr<nsISHEntry> shEntry; 1.282 + history->GetEntryAtIndex(i, false, getter_AddRefs(shEntry)); 1.283 + 1.284 + MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC); 1.285 + } 1.286 + } 1.287 + 1.288 + int32_t i, childCount; 1.289 + aNode->GetChildCount(&childCount); 1.290 + for (i = 0; i < childCount; ++i) { 1.291 + nsCOMPtr<nsIDocShellTreeItem> child; 1.292 + aNode->GetChildAt(i, getter_AddRefs(child)); 1.293 + MarkDocShell(child, aCleanupJS, aPrepareForCC); 1.294 + } 1.295 +} 1.296 + 1.297 +void 1.298 +MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS, 1.299 + bool aPrepareForCC) 1.300 +{ 1.301 + nsCOMPtr<nsISupports> iter; 1.302 + while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) && 1.303 + iter) { 1.304 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(iter); 1.305 + if (window) { 1.306 + nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell(); 1.307 + 1.308 + MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC); 1.309 + } 1.310 + } 1.311 +} 1.312 + 1.313 +nsresult 1.314 +nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, 1.315 + const char16_t* aData) 1.316 +{ 1.317 + if (!strcmp(aTopic, "xpcom-shutdown")) { 1.318 + Element::ClearContentUnbinder(); 1.319 + 1.320 + nsCOMPtr<nsIObserverService> obs = 1.321 + mozilla::services::GetObserverService(); 1.322 + if (!obs) 1.323 + return NS_ERROR_FAILURE; 1.324 + 1.325 + // No need for kungFuDeathGrip here, yay observerservice! 1.326 + obs->RemoveObserver(this, "xpcom-shutdown"); 1.327 + obs->RemoveObserver(this, "cycle-collector-begin"); 1.328 + obs->RemoveObserver(this, "cycle-collector-forget-skippable"); 1.329 + 1.330 + sGeneration = 0; 1.331 + 1.332 + return NS_OK; 1.333 + } 1.334 + 1.335 + NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") || 1.336 + !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic"); 1.337 + 1.338 + // JS cleanup can be slow. Do it only if there has been a GC. 1.339 + bool cleanupJS = 1.340 + nsJSContext::CleanupsSinceLastGC() == 0 && 1.341 + !strcmp(aTopic, "cycle-collector-forget-skippable"); 1.342 + 1.343 + bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin"); 1.344 + if (prepareForCC) { 1.345 + Element::ClearContentUnbinder(); 1.346 + } 1.347 + 1.348 + // Increase generation to effectively unmark all current objects 1.349 + if (!++sGeneration) { 1.350 + ++sGeneration; 1.351 + } 1.352 + 1.353 + nsFocusManager::MarkUncollectableForCCGeneration(sGeneration); 1.354 + 1.355 + nsresult rv; 1.356 + 1.357 + // Iterate all toplevel windows 1.358 + nsCOMPtr<nsISimpleEnumerator> windowList; 1.359 + nsCOMPtr<nsIWindowMediator> med = 1.360 + do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); 1.361 + if (med) { 1.362 + rv = med->GetEnumerator(nullptr, getter_AddRefs(windowList)); 1.363 + NS_ENSURE_SUCCESS(rv, rv); 1.364 + 1.365 + MarkWindowList(windowList, cleanupJS, prepareForCC); 1.366 + } 1.367 + 1.368 + nsCOMPtr<nsIWindowWatcher> ww = 1.369 + do_GetService(NS_WINDOWWATCHER_CONTRACTID); 1.370 + if (ww) { 1.371 + rv = ww->GetWindowEnumerator(getter_AddRefs(windowList)); 1.372 + NS_ENSURE_SUCCESS(rv, rv); 1.373 + 1.374 + MarkWindowList(windowList, cleanupJS, prepareForCC); 1.375 + } 1.376 + 1.377 + nsCOMPtr<nsIAppShellService> appShell = 1.378 + do_GetService(NS_APPSHELLSERVICE_CONTRACTID); 1.379 + if (appShell) { 1.380 + nsCOMPtr<nsIXULWindow> hw; 1.381 + appShell->GetHiddenWindow(getter_AddRefs(hw)); 1.382 + if (hw) { 1.383 + nsCOMPtr<nsIDocShell> shell; 1.384 + hw->GetDocShell(getter_AddRefs(shell)); 1.385 + MarkDocShell(shell, cleanupJS, prepareForCC); 1.386 + } 1.387 + bool hasHiddenPrivateWindow = false; 1.388 + appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow); 1.389 + if (hasHiddenPrivateWindow) { 1.390 + appShell->GetHiddenPrivateWindow(getter_AddRefs(hw)); 1.391 + if (hw) { 1.392 + nsCOMPtr<nsIDocShell> shell; 1.393 + hw->GetDocShell(getter_AddRefs(shell)); 1.394 + MarkDocShell(shell, cleanupJS, prepareForCC); 1.395 + } 1.396 + } 1.397 + } 1.398 + 1.399 +#ifdef MOZ_XUL 1.400 + nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance(); 1.401 + if (xulCache) { 1.402 + xulCache->MarkInCCGeneration(sGeneration); 1.403 + } 1.404 +#endif 1.405 + 1.406 + static bool previousWasJSCleanup = false; 1.407 + if (cleanupJS) { 1.408 + nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration); 1.409 + MarkMessageManagers(); 1.410 + 1.411 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.412 + static_cast<nsObserverService *>(obs.get())->UnmarkGrayStrongObservers(); 1.413 + 1.414 + previousWasJSCleanup = true; 1.415 + } else if (previousWasJSCleanup) { 1.416 + previousWasJSCleanup = false; 1.417 + if (!prepareForCC) { 1.418 + xpc_UnmarkSkippableJSHolders(); 1.419 + } 1.420 + } 1.421 + 1.422 + return NS_OK; 1.423 +} 1.424 + 1.425 +struct TraceClosure 1.426 +{ 1.427 + TraceClosure(JSTracer* aTrc, uint32_t aGCNumber) 1.428 + : mTrc(aTrc), mGCNumber(aGCNumber) 1.429 + {} 1.430 + JSTracer* mTrc; 1.431 + uint32_t mGCNumber; 1.432 +}; 1.433 + 1.434 +static PLDHashOperator 1.435 +TraceActiveWindowGlobal(const uint64_t& aId, nsGlobalWindow*& aWindow, void* aClosure) 1.436 +{ 1.437 + if (aWindow->GetDocShell() && aWindow->IsOuterWindow()) { 1.438 + TraceClosure* closure = static_cast<TraceClosure*>(aClosure); 1.439 + aWindow->TraceGlobalJSObject(closure->mTrc); 1.440 +#ifdef MOZ_XUL 1.441 + nsIDocument* doc = aWindow->GetExtantDoc(); 1.442 + if (doc && doc->IsXUL()) { 1.443 + XULDocument* xulDoc = static_cast<XULDocument*>(doc); 1.444 + xulDoc->TraceProtos(closure->mTrc, closure->mGCNumber); 1.445 + } 1.446 +#endif 1.447 + } 1.448 + return PL_DHASH_NEXT; 1.449 +} 1.450 + 1.451 +void 1.452 +mozilla::dom::TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber, bool aIsShutdownGC) 1.453 +{ 1.454 +#ifdef MOZ_XUL 1.455 + // Mark the scripts held in the XULPrototypeCache. This is required to keep 1.456 + // the JS script in the cache live across GC. 1.457 + nsXULPrototypeCache* cache = nsXULPrototypeCache::MaybeGetInstance(); 1.458 + if (cache) { 1.459 + if (aIsShutdownGC) { 1.460 + cache->FlushScripts(); 1.461 + } else { 1.462 + cache->MarkInGC(aTrc); 1.463 + } 1.464 + } 1.465 +#endif 1.466 + 1.467 + if (!nsCCUncollectableMarker::sGeneration) { 1.468 + return; 1.469 + } 1.470 + 1.471 + TraceClosure closure(aTrc, aGCNumber); 1.472 + 1.473 + // Mark globals of active windows black. 1.474 + nsGlobalWindow::WindowByIdTable* windowsById = 1.475 + nsGlobalWindow::GetWindowsTable(); 1.476 + if (windowsById) { 1.477 + windowsById->Enumerate(TraceActiveWindowGlobal, &closure); 1.478 + } 1.479 + 1.480 + // Mark the safe context black 1.481 + nsContentUtils::TraceSafeJSContext(aTrc); 1.482 +}