tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsCCUncollectableMarker.cpp (15531B)


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