LCOV - code coverage report
Current view: top level - dom/base - TabGroup.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 80 134 59.7 %
Date: 2017-07-14 16:53:18 Functions: 12 17 70.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13