Coverage Report

Created: 2018-09-25 14:53

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