content/base/src/nsCCUncollectableMarker.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsCCUncollectableMarker.h"
     7 #include "nsIObserverService.h"
     8 #include "nsIDocShell.h"
     9 #include "nsServiceManagerUtils.h"
    10 #include "nsIContentViewer.h"
    11 #include "nsIDocument.h"
    12 #include "XULDocument.h"
    13 #include "nsIWindowMediator.h"
    14 #include "nsPIDOMWindow.h"
    15 #include "nsIWebNavigation.h"
    16 #include "nsISHistory.h"
    17 #include "nsISHEntry.h"
    18 #include "nsISHContainer.h"
    19 #include "nsIWindowWatcher.h"
    20 #include "mozilla/Services.h"
    21 #include "nsIXULWindow.h"
    22 #include "nsIAppShellService.h"
    23 #include "nsAppShellCID.h"
    24 #include "nsContentUtils.h"
    25 #include "nsGlobalWindow.h"
    26 #include "nsJSEnvironment.h"
    27 #include "nsInProcessTabChildGlobal.h"
    28 #include "nsFrameLoader.h"
    29 #include "mozilla/EventListenerManager.h"
    30 #include "mozilla/dom/Element.h"
    31 #include "xpcpublic.h"
    32 #include "nsObserverService.h"
    33 #include "nsFocusManager.h"
    35 using namespace mozilla;
    36 using namespace mozilla::dom;
    38 static bool sInited = 0;
    39 uint32_t nsCCUncollectableMarker::sGeneration = 0;
    40 #ifdef MOZ_XUL
    41 #include "nsXULPrototypeCache.h"
    42 #endif
    44 NS_IMPL_ISUPPORTS(nsCCUncollectableMarker, nsIObserver)
    46 /* static */
    47 nsresult
    48 nsCCUncollectableMarker::Init()
    49 {
    50   if (sInited) {
    51     return NS_OK;
    52   }
    54   nsCOMPtr<nsIObserver> marker = new nsCCUncollectableMarker;
    55   NS_ENSURE_TRUE(marker, NS_ERROR_OUT_OF_MEMORY);
    57   nsCOMPtr<nsIObserverService> obs =
    58     mozilla::services::GetObserverService();
    59   if (!obs)
    60     return NS_ERROR_FAILURE;
    62   nsresult rv;
    64   // This makes the observer service hold an owning reference to the marker
    65   rv = obs->AddObserver(marker, "xpcom-shutdown", false);
    66   NS_ENSURE_SUCCESS(rv, rv);
    68   rv = obs->AddObserver(marker, "cycle-collector-begin", false);
    69   NS_ENSURE_SUCCESS(rv, rv);
    70   rv = obs->AddObserver(marker, "cycle-collector-forget-skippable", false);
    71   NS_ENSURE_SUCCESS(rv, rv);
    73   sInited = true;
    75   return NS_OK;
    76 }
    78 static void
    79 MarkUserData(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
    80 {
    81   nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc();
    82   if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
    83     Element::MarkUserData(aNode, aKey, aValue, aData);
    84   }
    85 }
    87 static void
    88 MarkUserDataHandler(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
    89 {
    90   nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc();
    91   if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
    92     Element::MarkUserDataHandler(aNode, aKey, aValue, aData);
    93   }
    94 }
    96 static void
    97 MarkMessageManagers()
    98 {
    99   // The global message manager only exists in the root process.
   100   if (XRE_GetProcessType() != GeckoProcessType_Default) {
   101     return;
   102   }
   103   nsCOMPtr<nsIMessageBroadcaster> strongGlobalMM =
   104     do_GetService("@mozilla.org/globalmessagemanager;1");
   105   if (!strongGlobalMM) {
   106     return;
   107   }
   108   nsIMessageBroadcaster* globalMM = strongGlobalMM;
   109   strongGlobalMM = nullptr;
   111   globalMM->MarkForCC();
   112   uint32_t childCount = 0;
   113   globalMM->GetChildCount(&childCount);
   114   for (uint32_t i = 0; i < childCount; ++i) {
   115     nsCOMPtr<nsIMessageListenerManager> childMM;
   116     globalMM->GetChildAt(i, getter_AddRefs(childMM));
   117     if (!childMM) {
   118       continue;
   119     }
   120     nsCOMPtr<nsIMessageBroadcaster> strongWindowMM = do_QueryInterface(childMM);
   121     nsIMessageBroadcaster* windowMM = strongWindowMM;
   122     childMM = nullptr;
   123     strongWindowMM = nullptr;
   124     windowMM->MarkForCC();
   125     uint32_t tabChildCount = 0;
   126     windowMM->GetChildCount(&tabChildCount);
   127     for (uint32_t j = 0; j < tabChildCount; ++j) {
   128       nsCOMPtr<nsIMessageListenerManager> childMM;
   129       windowMM->GetChildAt(j, getter_AddRefs(childMM));
   130       if (!childMM) {
   131         continue;
   132       }
   133       nsCOMPtr<nsIMessageSender> strongTabMM = do_QueryInterface(childMM);
   134       nsIMessageSender* tabMM = strongTabMM;
   135       childMM = nullptr;
   136       strongTabMM = nullptr;
   137       tabMM->MarkForCC();
   138       //XXX hack warning, but works, since we know that
   139       //    callback is frameloader.
   140       mozilla::dom::ipc::MessageManagerCallback* cb =
   141         static_cast<nsFrameMessageManager*>(tabMM)->GetCallback();
   142       if (cb) {
   143         nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
   144         EventTarget* et = fl->GetTabChildGlobalAsEventTarget();
   145         if (!et) {
   146           continue;
   147         }
   148         static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC();
   149         EventListenerManager* elm = et->GetExistingListenerManager();
   150         if (elm) {
   151           elm->MarkForCC();
   152         }
   153       }
   154     }
   155   }
   156   if (nsFrameMessageManager::sParentProcessManager) {
   157     nsFrameMessageManager::sParentProcessManager->MarkForCC();
   158     uint32_t childCount = 0;
   159     nsFrameMessageManager::sParentProcessManager->GetChildCount(&childCount);
   160     for (uint32_t i = 0; i < childCount; ++i) {
   161       nsCOMPtr<nsIMessageListenerManager> childMM;
   162       nsFrameMessageManager::sParentProcessManager->
   163         GetChildAt(i, getter_AddRefs(childMM));
   164       if (!childMM) {
   165         continue;
   166       }
   167       nsIMessageListenerManager* child = childMM;
   168       childMM = nullptr;
   169       child->MarkForCC();
   170     }
   171   }
   172   if (nsFrameMessageManager::sSameProcessParentManager) {
   173     nsFrameMessageManager::sSameProcessParentManager->MarkForCC();
   174   }
   175   if (nsFrameMessageManager::sChildProcessManager) {
   176     nsFrameMessageManager::sChildProcessManager->MarkForCC();
   177   }
   178 }
   180 void
   181 MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
   182                   bool aPrepareForCC)
   183 {
   184   if (!aViewer) {
   185     return;
   186   }
   188   nsIDocument *doc = aViewer->GetDocument();
   189   if (doc &&
   190       doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) {
   191     doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
   192     if (aCleanupJS) {
   193       EventListenerManager* elm = doc->GetExistingListenerManager();
   194       if (elm) {
   195         elm->MarkForCC();
   196       }
   197       nsCOMPtr<EventTarget> win = do_QueryInterface(doc->GetInnerWindow());
   198       if (win) {
   199         elm = win->GetExistingListenerManager();
   200         if (elm) {
   201           elm->MarkForCC();
   202         }
   203         static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers();
   204       }
   206       doc->PropertyTable(DOM_USER_DATA_HANDLER)->
   207         EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration);
   208     } else if (aPrepareForCC) {
   209       // Unfortunately we need to still mark user data just before running CC so
   210       // that it has the right generation. 
   211       doc->PropertyTable(DOM_USER_DATA)->
   212         EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration);
   213     }
   214   }
   215   if (doc) {
   216     nsPIDOMWindow* inner = doc->GetInnerWindow();
   217     if (inner) {
   218       inner->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
   219     }
   220     nsPIDOMWindow* outer = doc->GetWindow();
   221     if (outer) {
   222       outer->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
   223     }
   224   }
   225 }
   227 void MarkDocShell(nsIDocShellTreeItem* aNode, bool aCleanupJS,
   228                   bool aPrepareForCC);
   230 void
   231 MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC)
   232 {
   233   if (!aSHEntry) {
   234     return;
   235   }
   237   nsCOMPtr<nsIContentViewer> cview;
   238   aSHEntry->GetContentViewer(getter_AddRefs(cview));
   239   MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
   241   nsCOMPtr<nsIDocShellTreeItem> child;
   242   int32_t i = 0;
   243   while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) &&
   244          child) {
   245     MarkDocShell(child, aCleanupJS, aPrepareForCC);
   246   }
   248   nsCOMPtr<nsISHContainer> shCont = do_QueryInterface(aSHEntry);
   249   int32_t count;
   250   shCont->GetChildCount(&count);
   251   for (i = 0; i < count; ++i) {
   252     nsCOMPtr<nsISHEntry> childEntry;
   253     shCont->GetChildAt(i, getter_AddRefs(childEntry));
   254     MarkSHEntry(childEntry, aCleanupJS, aPrepareForCC);
   255   }
   257 }
   259 void
   260 MarkDocShell(nsIDocShellTreeItem* aNode, bool aCleanupJS, bool aPrepareForCC)
   261 {
   262   nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aNode);
   263   if (!shell) {
   264     return;
   265   }
   267   nsCOMPtr<nsIContentViewer> cview;
   268   shell->GetContentViewer(getter_AddRefs(cview));
   269   MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
   271   nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell);
   272   nsCOMPtr<nsISHistory> history;
   273   webNav->GetSessionHistory(getter_AddRefs(history));
   274   if (history) {
   275     int32_t i, historyCount;
   276     history->GetCount(&historyCount);
   277     for (i = 0; i < historyCount; ++i) {
   278       nsCOMPtr<nsISHEntry> shEntry;
   279       history->GetEntryAtIndex(i, false, getter_AddRefs(shEntry));
   281       MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC);
   282     }
   283   }
   285   int32_t i, childCount;
   286   aNode->GetChildCount(&childCount);
   287   for (i = 0; i < childCount; ++i) {
   288     nsCOMPtr<nsIDocShellTreeItem> child;
   289     aNode->GetChildAt(i, getter_AddRefs(child));
   290     MarkDocShell(child, aCleanupJS, aPrepareForCC);
   291   }
   292 }
   294 void
   295 MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS,
   296                bool aPrepareForCC)
   297 {
   298   nsCOMPtr<nsISupports> iter;
   299   while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) &&
   300          iter) {
   301     nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(iter);
   302     if (window) {
   303       nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell();
   305       MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC);
   306     }
   307   }
   308 }
   310 nsresult
   311 nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
   312                                  const char16_t* aData)
   313 {
   314   if (!strcmp(aTopic, "xpcom-shutdown")) {
   315     Element::ClearContentUnbinder();
   317     nsCOMPtr<nsIObserverService> obs =
   318       mozilla::services::GetObserverService();
   319     if (!obs)
   320       return NS_ERROR_FAILURE;
   322     // No need for kungFuDeathGrip here, yay observerservice!
   323     obs->RemoveObserver(this, "xpcom-shutdown");
   324     obs->RemoveObserver(this, "cycle-collector-begin");
   325     obs->RemoveObserver(this, "cycle-collector-forget-skippable");
   327     sGeneration = 0;
   329     return NS_OK;
   330   }
   332   NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") ||
   333                !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic");
   335   // JS cleanup can be slow. Do it only if there has been a GC.
   336   bool cleanupJS =
   337     nsJSContext::CleanupsSinceLastGC() == 0 &&
   338     !strcmp(aTopic, "cycle-collector-forget-skippable");
   340   bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");
   341   if (prepareForCC) {
   342     Element::ClearContentUnbinder();
   343   }
   345   // Increase generation to effectively unmark all current objects
   346   if (!++sGeneration) {
   347     ++sGeneration;
   348   }
   350   nsFocusManager::MarkUncollectableForCCGeneration(sGeneration);
   352   nsresult rv;
   354   // Iterate all toplevel windows
   355   nsCOMPtr<nsISimpleEnumerator> windowList;
   356   nsCOMPtr<nsIWindowMediator> med =
   357     do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
   358   if (med) {
   359     rv = med->GetEnumerator(nullptr, getter_AddRefs(windowList));
   360     NS_ENSURE_SUCCESS(rv, rv);
   362     MarkWindowList(windowList, cleanupJS, prepareForCC);
   363   }
   365   nsCOMPtr<nsIWindowWatcher> ww =
   366     do_GetService(NS_WINDOWWATCHER_CONTRACTID);
   367   if (ww) {
   368     rv = ww->GetWindowEnumerator(getter_AddRefs(windowList));
   369     NS_ENSURE_SUCCESS(rv, rv);
   371     MarkWindowList(windowList, cleanupJS, prepareForCC);
   372   }
   374   nsCOMPtr<nsIAppShellService> appShell = 
   375     do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
   376   if (appShell) {
   377     nsCOMPtr<nsIXULWindow> hw;
   378     appShell->GetHiddenWindow(getter_AddRefs(hw));
   379     if (hw) {
   380       nsCOMPtr<nsIDocShell> shell;
   381       hw->GetDocShell(getter_AddRefs(shell));
   382       MarkDocShell(shell, cleanupJS, prepareForCC);
   383     }
   384     bool hasHiddenPrivateWindow = false;
   385     appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow);
   386     if (hasHiddenPrivateWindow) {
   387       appShell->GetHiddenPrivateWindow(getter_AddRefs(hw));
   388       if (hw) {
   389         nsCOMPtr<nsIDocShell> shell;
   390         hw->GetDocShell(getter_AddRefs(shell));
   391         MarkDocShell(shell, cleanupJS, prepareForCC);
   392       }
   393     }
   394   }
   396 #ifdef MOZ_XUL
   397   nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance();
   398   if (xulCache) {
   399     xulCache->MarkInCCGeneration(sGeneration);
   400   }
   401 #endif
   403   static bool previousWasJSCleanup = false;
   404   if (cleanupJS) {
   405     nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration);
   406     MarkMessageManagers();
   408     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   409     static_cast<nsObserverService *>(obs.get())->UnmarkGrayStrongObservers();
   411     previousWasJSCleanup = true;
   412   } else if (previousWasJSCleanup) {
   413     previousWasJSCleanup = false;
   414     if (!prepareForCC) {
   415       xpc_UnmarkSkippableJSHolders();
   416     }
   417   }
   419   return NS_OK;
   420 }
   422 struct TraceClosure
   423 {
   424   TraceClosure(JSTracer* aTrc, uint32_t aGCNumber)
   425     : mTrc(aTrc), mGCNumber(aGCNumber)
   426   {}
   427   JSTracer* mTrc;
   428   uint32_t mGCNumber;
   429 };
   431 static PLDHashOperator
   432 TraceActiveWindowGlobal(const uint64_t& aId, nsGlobalWindow*& aWindow, void* aClosure)
   433 {
   434   if (aWindow->GetDocShell() && aWindow->IsOuterWindow()) {
   435     TraceClosure* closure = static_cast<TraceClosure*>(aClosure);
   436     aWindow->TraceGlobalJSObject(closure->mTrc);
   437 #ifdef MOZ_XUL
   438     nsIDocument* doc = aWindow->GetExtantDoc();
   439     if (doc && doc->IsXUL()) {
   440       XULDocument* xulDoc = static_cast<XULDocument*>(doc);
   441       xulDoc->TraceProtos(closure->mTrc, closure->mGCNumber);
   442     }
   443 #endif
   444   }
   445   return PL_DHASH_NEXT;
   446 }
   448 void
   449 mozilla::dom::TraceBlackJS(JSTracer* aTrc, uint32_t aGCNumber, bool aIsShutdownGC)
   450 {
   451 #ifdef MOZ_XUL
   452   // Mark the scripts held in the XULPrototypeCache. This is required to keep
   453   // the JS script in the cache live across GC.
   454   nsXULPrototypeCache* cache = nsXULPrototypeCache::MaybeGetInstance();
   455   if (cache) {
   456     if (aIsShutdownGC) {
   457       cache->FlushScripts();
   458     } else {
   459       cache->MarkInGC(aTrc);
   460     }
   461   }
   462 #endif
   464   if (!nsCCUncollectableMarker::sGeneration) {
   465     return;
   466   }
   468   TraceClosure closure(aTrc, aGCNumber);
   470   // Mark globals of active windows black.
   471   nsGlobalWindow::WindowByIdTable* windowsById =
   472     nsGlobalWindow::GetWindowsTable();
   473   if (windowsById) {
   474     windowsById->Enumerate(TraceActiveWindowGlobal, &closure);
   475   }
   477   // Mark the safe context black
   478   nsContentUtils::TraceSafeJSContext(aTrc);
   479 }

mercurial