Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/TabGroup.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 "mozilla/dom/TabGroup.h"
8
9
#include "mozilla/dom/ContentChild.h"
10
#include "mozilla/dom/TabChild.h"
11
#include "mozilla/dom/DocGroup.h"
12
#include "mozilla/dom/TimeoutManager.h"
13
#include "mozilla/AbstractThread.h"
14
#include "mozilla/ClearOnShutdown.h"
15
#include "mozilla/StaticPtr.h"
16
#include "mozilla/Telemetry.h"
17
#include "mozilla/ThrottledEventQueue.h"
18
#include "nsIDocShell.h"
19
#include "nsIEffectiveTLDService.h"
20
#include "nsIURI.h"
21
22
namespace mozilla {
23
namespace dom {
24
25
static StaticRefPtr<TabGroup> sChromeTabGroup;
26
27
LinkedList<TabGroup>* TabGroup::sTabGroups = nullptr;
28
29
TabGroup::TabGroup(bool aIsChrome)
30
 : mLastWindowLeft(false)
31
 , mThrottledQueuesInitialized(false)
32
 , mNumOfIndexedDBTransactions(0)
33
 , mNumOfIndexedDBDatabases(0)
34
 , mIsChrome(aIsChrome)
35
 , mForegroundCount(0)
36
0
{
37
0
  if (!sTabGroups) {
38
0
    sTabGroups = new LinkedList<TabGroup>();
39
0
  }
40
0
  sTabGroups->insertBack(this);
41
0
42
0
  CreateEventTargets(/* aNeedValidation = */ !aIsChrome);
43
0
44
0
  // Do not throttle runnables from chrome windows.  In theory we should
45
0
  // not have abuse issues from these windows and many browser chrome
46
0
  // tests have races that fail if we do throttle chrome runnables.
47
0
  if (aIsChrome) {
48
0
    MOZ_ASSERT(!sChromeTabGroup);
49
0
    return;
50
0
  }
51
0
52
0
  // This constructor can be called from the IPC I/O thread. In that case, we
53
0
  // won't actually use the TabGroup on the main thread until GetFromWindowActor
54
0
  // is called, so we initialize the throttled queues there.
55
0
  if (NS_IsMainThread()) {
56
0
    EnsureThrottledEventQueues();
57
0
  }
58
0
}
59
60
TabGroup::~TabGroup()
61
0
{
62
0
  MOZ_ASSERT(mDocGroups.IsEmpty());
63
0
  MOZ_ASSERT(mWindows.IsEmpty());
64
0
  MOZ_RELEASE_ASSERT(mLastWindowLeft || mIsChrome);
65
0
66
0
  LinkedListElement<TabGroup>* listElement =
67
0
    static_cast<LinkedListElement<TabGroup>*>(this);
68
0
  listElement->remove();
69
0
70
0
  if (sTabGroups->isEmpty()) {
71
0
    delete sTabGroups;
72
0
    sTabGroups = nullptr;
73
0
  }
74
0
}
75
76
void
77
TabGroup::EnsureThrottledEventQueues()
78
0
{
79
0
  if (mThrottledQueuesInitialized) {
80
0
    return;
81
0
  }
82
0
83
0
  mThrottledQueuesInitialized = true;
84
0
85
0
  for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
86
0
    TaskCategory category = static_cast<TaskCategory>(i);
87
0
    if (category == TaskCategory::Worker || category == TaskCategory::Timer) {
88
0
      nsCOMPtr<nsISerialEventTarget> target = ThrottledEventQueue::Create(mEventTargets[i]);
89
0
      if (target) {
90
0
        // This may return nullptr during xpcom shutdown.  This is ok as we
91
0
        // do not guarantee a ThrottledEventQueue will be present.
92
0
        mEventTargets[i] = target;
93
0
      }
94
0
    }
95
0
  }
96
0
}
97
98
/* static */ TabGroup*
99
TabGroup::GetChromeTabGroup()
100
0
{
101
0
  if (!sChromeTabGroup) {
102
0
    sChromeTabGroup = new TabGroup(true /* chrome tab group */);
103
0
    ClearOnShutdown(&sChromeTabGroup);
104
0
  }
105
0
  return sChromeTabGroup;
106
0
}
107
108
/* static */ TabGroup*
109
TabGroup::GetFromWindow(mozIDOMWindowProxy* aWindow)
110
0
{
111
0
  if (TabChild* tabChild = TabChild::GetFrom(aWindow)) {
112
0
    return tabChild->TabGroup();
113
0
  }
114
0
115
0
  return nullptr;
116
0
}
117
118
/* static */ TabGroup*
119
TabGroup::GetFromActor(TabChild* aTabChild)
120
0
{
121
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
122
0
123
0
  // Middleman processes do not assign event targets to their tab children.
124
0
  if (recordreplay::IsMiddleman()) {
125
0
    return GetChromeTabGroup();
126
0
  }
127
0
128
0
  nsCOMPtr<nsIEventTarget> target = aTabChild->Manager()->GetEventTargetFor(aTabChild);
129
0
  if (!target) {
130
0
    return nullptr;
131
0
  }
132
0
133
0
  // We have an event target. We assume the IPC code created it via
134
0
  // TabGroup::CreateEventTarget.
135
0
  RefPtr<SchedulerGroup> group =
136
0
    SchedulerGroup::FromEventTarget(target);
137
0
  MOZ_RELEASE_ASSERT(group);
138
0
  auto tabGroup = group->AsTabGroup();
139
0
  MOZ_RELEASE_ASSERT(tabGroup);
140
0
141
0
  // We delay creating the event targets until now since the TabGroup
142
0
  // constructor ran off the main thread.
143
0
  tabGroup->EnsureThrottledEventQueues();
144
0
145
0
  return tabGroup;
146
0
}
147
148
already_AddRefed<DocGroup>
149
TabGroup::GetDocGroup(const nsACString& aKey)
150
0
{
151
0
  RefPtr<DocGroup> docGroup(mDocGroups.GetEntry(aKey)->mDocGroup);
152
0
  return docGroup.forget();
153
0
}
154
155
already_AddRefed<DocGroup>
156
TabGroup::AddDocument(const nsACString& aKey, nsIDocument* aDocument)
157
0
{
158
0
  MOZ_ASSERT(NS_IsMainThread());
159
0
  HashEntry* entry = mDocGroups.PutEntry(aKey);
160
0
  RefPtr<DocGroup> docGroup;
161
0
  if (entry->mDocGroup) {
162
0
    docGroup = entry->mDocGroup;
163
0
  } else {
164
0
    docGroup = new DocGroup(this, aKey);
165
0
    entry->mDocGroup = docGroup;
166
0
  }
167
0
168
0
  // Make sure that the hashtable was updated and now contains the correct value
169
0
  MOZ_ASSERT(RefPtr<DocGroup>(GetDocGroup(aKey)) == docGroup);
170
0
171
0
  docGroup->mDocuments.AppendElement(aDocument);
172
0
173
0
  return docGroup.forget();
174
0
}
175
176
/* static */ already_AddRefed<TabGroup>
177
TabGroup::Join(nsPIDOMWindowOuter* aWindow, TabGroup* aTabGroup)
178
0
{
179
0
  MOZ_ASSERT(NS_IsMainThread());
180
0
  RefPtr<TabGroup> tabGroup = aTabGroup;
181
0
  if (!tabGroup) {
182
0
    tabGroup = new TabGroup();
183
0
  }
184
0
  MOZ_RELEASE_ASSERT(!tabGroup->mLastWindowLeft);
185
0
  MOZ_ASSERT(!tabGroup->mWindows.Contains(aWindow));
186
0
  tabGroup->mWindows.AppendElement(aWindow);
187
0
188
0
  if (!aWindow->IsBackground()) {
189
0
    tabGroup->mForegroundCount++;
190
0
  }
191
0
192
0
  return tabGroup.forget();
193
0
}
194
195
void
196
TabGroup::Leave(nsPIDOMWindowOuter* aWindow)
197
0
{
198
0
  MOZ_ASSERT(NS_IsMainThread());
199
0
  MOZ_ASSERT(mWindows.Contains(aWindow));
200
0
  mWindows.RemoveElement(aWindow);
201
0
202
0
  if (!aWindow->IsBackground()) {
203
0
    MOZ_DIAGNOSTIC_ASSERT(mForegroundCount > 0);
204
0
    mForegroundCount--;
205
0
  }
206
0
207
0
  // The Chrome TabGroup doesn't have cyclical references through mEventTargets
208
0
  // to itself, meaning that we don't have to worry about nulling mEventTargets
209
0
  // out after the last window leaves.
210
0
  if (!mIsChrome && mWindows.IsEmpty()) {
211
0
    mLastWindowLeft = true;
212
0
    Shutdown(false);
213
0
  }
214
0
}
215
216
nsresult
217
TabGroup::FindItemWithName(const nsAString& aName,
218
                           nsIDocShellTreeItem* aRequestor,
219
                           nsIDocShellTreeItem* aOriginalRequestor,
220
                           nsIDocShellTreeItem** aFoundItem)
221
0
{
222
0
  MOZ_ASSERT(NS_IsMainThread());
223
0
  NS_ENSURE_ARG_POINTER(aFoundItem);
224
0
  *aFoundItem = nullptr;
225
0
226
0
  MOZ_ASSERT(!aName.LowerCaseEqualsLiteral("_blank") &&
227
0
             !aName.LowerCaseEqualsLiteral("_top") &&
228
0
             !aName.LowerCaseEqualsLiteral("_parent") &&
229
0
             !aName.LowerCaseEqualsLiteral("_self"));
230
0
231
0
  for (nsPIDOMWindowOuter* outerWindow : mWindows) {
232
0
    // Ignore non-toplevel windows
233
0
    if (outerWindow->GetScriptableParentOrNull()) {
234
0
      continue;
235
0
    }
236
0
237
0
    nsCOMPtr<nsIDocShellTreeItem> docshell = outerWindow->GetDocShell();
238
0
    if (!docshell) {
239
0
      continue;
240
0
    }
241
0
242
0
    nsCOMPtr<nsIDocShellTreeItem> root;
243
0
    docshell->GetSameTypeRootTreeItem(getter_AddRefs(root));
244
0
    MOZ_RELEASE_ASSERT(docshell == root);
245
0
    if (root && aRequestor != root) {
246
0
      root->FindItemWithName(aName, aRequestor, aOriginalRequestor,
247
0
                             /* aSkipTabGroup = */ true, aFoundItem);
248
0
      if (*aFoundItem) {
249
0
        break;
250
0
      }
251
0
    }
252
0
  }
253
0
254
0
  return NS_OK;
255
0
}
256
257
nsTArray<nsPIDOMWindowOuter*>
258
TabGroup::GetTopLevelWindows() const
259
0
{
260
0
  MOZ_ASSERT(NS_IsMainThread());
261
0
  nsTArray<nsPIDOMWindowOuter*> array;
262
0
263
0
  for (nsPIDOMWindowOuter* outerWindow : mWindows) {
264
0
    if (outerWindow->GetDocShell() &&
265
0
        !outerWindow->GetScriptableParentOrNull()) {
266
0
      array.AppendElement(outerWindow);
267
0
    }
268
0
  }
269
0
270
0
  return array;
271
0
}
272
273
TabGroup::HashEntry::HashEntry(const nsACString* aKey)
274
  : nsCStringHashKey(aKey), mDocGroup(nullptr)
275
0
{}
276
277
nsISerialEventTarget*
278
TabGroup::EventTargetFor(TaskCategory aCategory) const
279
0
{
280
0
  if (aCategory == TaskCategory::Worker || aCategory == TaskCategory::Timer) {
281
0
    MOZ_RELEASE_ASSERT(mThrottledQueuesInitialized || mIsChrome);
282
0
  }
283
0
  return SchedulerGroup::EventTargetFor(aCategory);
284
0
}
285
286
AbstractThread*
287
TabGroup::AbstractMainThreadForImpl(TaskCategory aCategory)
288
0
{
289
0
  // The mEventTargets of the chrome TabGroup are all set to do_GetMainThread().
290
0
  // We could just return AbstractThread::MainThread() without a wrapper.
291
0
  // Once we've disconnected everything, we still allow people to dispatch.
292
0
  // We'll just go directly to the main thread.
293
0
  if (this == sChromeTabGroup || NS_WARN_IF(mLastWindowLeft)) {
294
0
    return AbstractThread::MainThread();
295
0
  }
296
0
297
0
  return SchedulerGroup::AbstractMainThreadForImpl(aCategory);
298
0
}
299
300
void
301
TabGroup::WindowChangedBackgroundStatus(bool aIsNowBackground)
302
0
{
303
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
304
0
305
0
  if (aIsNowBackground) {
306
0
    MOZ_DIAGNOSTIC_ASSERT(mForegroundCount > 0);
307
0
    mForegroundCount -= 1;
308
0
  } else {
309
0
    mForegroundCount += 1;
310
0
  }
311
0
}
312
313
bool
314
TabGroup::IsBackground() const
315
0
{
316
0
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
317
0
318
#ifdef DEBUG
319
  uint32_t foregrounded = 0;
320
  for (auto& window : mWindows) {
321
    if (!window->IsBackground()) {
322
      foregrounded++;
323
    }
324
  }
325
  MOZ_ASSERT(foregrounded == mForegroundCount);
326
#endif
327
328
0
  return mForegroundCount == 0;
329
0
}
330
331
uint32_t
332
TabGroup::Count(bool aActiveOnly) const
333
0
{
334
0
  if (!aActiveOnly) {
335
0
    return mDocGroups.Count();
336
0
  }
337
0
338
0
  uint32_t count = 0;
339
0
  for (auto iter = mDocGroups.ConstIter(); !iter.Done(); iter.Next()) {
340
0
    if (iter.Get()->mDocGroup->IsActive()) {
341
0
      ++count;
342
0
    }
343
0
  }
344
0
345
0
  return count;
346
0
}
347
348
/*static*/ bool
349
TabGroup::HasOnlyThrottableTabs()
350
0
{
351
0
  if (!sTabGroups) {
352
0
    return false;
353
0
  }
354
0
355
0
  for (TabGroup* tabGroup = sTabGroups->getFirst(); tabGroup;
356
0
       tabGroup =
357
0
         static_cast<LinkedListElement<TabGroup>*>(tabGroup)->getNext()) {
358
0
    for (auto iter = tabGroup->Iter(); !iter.Done(); iter.Next()) {
359
0
      DocGroup* docGroup = iter.Get()->mDocGroup;
360
0
      for (auto* documentInDocGroup : *docGroup) {
361
0
        if (documentInDocGroup->IsCurrentActiveDocument()) {
362
0
          nsPIDOMWindowInner* win =
363
0
            documentInDocGroup->GetInnerWindow();
364
0
          if (win && win->IsCurrentInnerWindow()) {
365
0
            nsPIDOMWindowOuter* outer = win->GetOuterWindow();
366
0
            if (outer) {
367
0
              TimeoutManager& tm = win->TimeoutManager();
368
0
              if (!tm.BudgetThrottlingEnabled(outer->IsBackground())) {
369
0
                return false;
370
0
              }
371
0
            }
372
0
          }
373
0
        }
374
0
      }
375
0
    }
376
0
  }
377
0
  return true;
378
0
}
379
380
} // namespace dom
381
} // namespace mozilla