bug 1468099, Add a way to check if all the tabs in a process can be throttled, r=farre
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Sat, 16 Jun 2018 22:49:41 +0300
changeset 422789 1950fa5311693d8658f9a79f64ce3ff611c5d0d1
parent 422788 d15645bc44c7f4b7e35f39a290f67ca81b5e6f8d
child 422790 1d498636e0d5b5090691cb05dd7c0af09b4c2949
push id104350
push useropettay@mozilla.com
push dateSat, 16 Jun 2018 19:50:42 +0000
treeherdermozilla-inbound@1950fa531169 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfarre
bugs1468099
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1468099, Add a way to check if all the tabs in a process can be throttled, r=farre
dom/base/TabGroup.cpp
dom/base/TabGroup.h
dom/base/TimeoutManager.h
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -4,38 +4,46 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/TabGroup.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/DocGroup.h"
+#include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThrottledEventQueue.h"
 #include "nsIDocShell.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIURI.h"
 
 namespace mozilla {
 namespace dom {
 
 static StaticRefPtr<TabGroup> sChromeTabGroup;
 
+LinkedList<TabGroup>* TabGroup::sTabGroups = nullptr;
+
 TabGroup::TabGroup(bool aIsChrome)
  : mLastWindowLeft(false)
  , mThrottledQueuesInitialized(false)
  , mNumOfIndexedDBTransactions(0)
  , mNumOfIndexedDBDatabases(0)
  , mIsChrome(aIsChrome)
  , mForegroundCount(0)
 {
+  if (!sTabGroups) {
+    sTabGroups = new LinkedList<TabGroup>();
+  }
+  sTabGroups->insertBack(this);
+
   CreateEventTargets(/* aNeedValidation = */ !aIsChrome);
 
   // Do not throttle runnables from chrome windows.  In theory we should
   // not have abuse issues from these windows and many browser chrome
   // tests have races that fail if we do throttle chrome runnables.
   if (aIsChrome) {
     MOZ_ASSERT(!sChromeTabGroup);
     return;
@@ -49,16 +57,25 @@ TabGroup::TabGroup(bool aIsChrome)
   }
 }
 
 TabGroup::~TabGroup()
 {
   MOZ_ASSERT(mDocGroups.IsEmpty());
   MOZ_ASSERT(mWindows.IsEmpty());
   MOZ_RELEASE_ASSERT(mLastWindowLeft || mIsChrome);
+
+  LinkedListElement<TabGroup>* listElement =
+    static_cast<LinkedListElement<TabGroup>*>(this);
+  listElement->remove();
+
+  if (sTabGroups->isEmpty()) {
+    delete sTabGroups;
+    sTabGroups = nullptr;
+  }
 }
 
 void
 TabGroup::EnsureThrottledEventQueues()
 {
   if (mThrottledQueuesInitialized) {
     return;
   }
@@ -318,10 +335,42 @@ TabGroup::Count(bool aActiveOnly) const
     if (iter.Get()->mDocGroup->IsActive()) {
       ++count;
     }
   }
 
   return count;
 }
 
+/*static*/ bool
+TabGroup::HasOnlyThrottableTabs()
+{
+  if (!sTabGroups) {
+    return false;
+  }
+
+  for (TabGroup* tabGroup = sTabGroups->getFirst(); tabGroup;
+       tabGroup =
+         static_cast<LinkedListElement<TabGroup>*>(tabGroup)->getNext()) {
+    for (auto iter = tabGroup->Iter(); !iter.Done(); iter.Next()) {
+      DocGroup* docGroup = iter.Get()->mDocGroup;
+      for (auto* documentInDocGroup : *docGroup) {
+        if (documentInDocGroup->IsCurrentActiveDocument()) {
+          nsPIDOMWindowInner* win =
+            documentInDocGroup->GetInnerWindow();
+          if (win && win->IsCurrentInnerWindow()) {
+            nsPIDOMWindowOuter* outer = win->GetOuterWindow();
+            if (outer) {
+              TimeoutManager& tm = win->TimeoutManager();
+              if (!tm.BudgetThrottlingEnabled(outer->IsBackground())) {
+                return false;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -41,17 +41,18 @@ class TabChild;
 // more detail, a DocGroup is actually a collection of documents, and a
 // TabGroup is a collection of DocGroups. A TabGroup typically will contain
 // (through its DocGroups) the documents from one or more tabs related by
 // window.opener. A DocGroup is a member of exactly one TabGroup.
 
 class DocGroup;
 class TabChild;
 
-class TabGroup final : public SchedulerGroup
+class TabGroup final : public SchedulerGroup,
+                       public LinkedListElement<TabGroup>
 {
 private:
   class HashEntry : public nsCStringHashKey
   {
   public:
     // NOTE: Weak reference. The DocGroup destructor removes itself from its
     // owning TabGroup.
     DocGroup* mDocGroup;
@@ -141,16 +142,26 @@ public:
     return mNumOfIndexedDBTransactions;
   }
 
   Atomic<uint32_t>& IndexedDBDatabaseCounter()
   {
     return mNumOfIndexedDBDatabases;
   }
 
+  static LinkedList<TabGroup>* GetTabGroupList()
+  {
+    return sTabGroups;
+  }
+
+  // This returns true if all the window objects in all the TabGroups are
+  // either inactive (for example in bfcache) or are in background tabs which
+  // can be throttled.
+  static bool HasOnlyThrottableTabs();
+
 private:
   virtual AbstractThread*
   AbstractMainThreadForImpl(TaskCategory aCategory) override;
 
   TabGroup* AsTabGroup() override { return this; }
 
   void EnsureThrottledEventQueues();
 
@@ -162,14 +173,16 @@ private:
   Atomic<uint32_t> mNumOfIndexedDBTransactions;
   Atomic<uint32_t> mNumOfIndexedDBDatabases;
   const bool mIsChrome;
 
   // Main thread only
   DocGroupMap mDocGroups;
   nsTArray<nsPIDOMWindowOuter*> mWindows;
   uint32_t mForegroundCount;
+
+  static LinkedList<TabGroup>* sTabGroups;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // defined(TabGroup_h)
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -106,16 +106,18 @@ public:
   }
 
   void BeginSyncOperation();
   void EndSyncOperation();
 
   nsIEventTarget*
   EventTarget();
 
+  bool BudgetThrottlingEnabled(bool aIsBackground) const;
+
   static const uint32_t InvalidFiringId;
 
 private:
   void MaybeStartThrottleTimeout();
 
   // Return true if |aTimeout| needs to be reinserted into the timeout list.
   bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout,
                          const TimeStamp& aLastCallbackTime,
@@ -144,18 +146,16 @@ private:
                          const TimeStamp& aNow = TimeStamp::Now());
 
   void RecordExecution(Timeout* aRunningTimeout,
                        Timeout* aTimeout);
 
   void UpdateBudget(const TimeStamp& aNow,
                     const TimeDuration& aDuration = TimeDuration());
 
-  bool BudgetThrottlingEnabled(bool aIsBackground) const;
-
 private:
   struct Timeouts {
     explicit Timeouts(const TimeoutManager& aManager)
       : mManager(aManager)
     {
     }
 
     // Insert aTimeout into the list, before all timeouts that would