Bug 1495032 - Proactively adjust tab priority on switch r=mconley
authorDoug Thayer <dothayer@mozilla.com>
Mon, 01 Oct 2018 17:05:32 +0000
changeset 494763 bcdbc281a48d53c76737abd2ab776deefaf59338
parent 494762 6617886468a29c28e68bea594967fd99853886ad
child 494764 c4758f66e313122b753ed49c5bad0d803b2a604a
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1495032
milestone64.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 1495032 - Proactively adjust tab priority on switch r=mconley Our current prioritization mechanism doesn't account for tab warming, or for the fact that the current tab should be deprioritized. This corrects that. Differential Revision: https://phabricator.services.mozilla.com/D7205
browser/modules/AsyncTabSwitcher.jsm
dom/interfaces/base/nsITabParent.idl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
toolkit/content/widgets/browser.xml
--- a/browser/modules/AsyncTabSwitcher.jsm
+++ b/browser/modules/AsyncTabSwitcher.jsm
@@ -990,16 +990,18 @@ class AsyncTabSwitcher {
       Services.telemetry
         .getHistogramById("FX_TAB_SWITCH_REQUEST_TAB_WARMING_STATE")
         .add(warmingState);
     }
 
     this.logState("requestTab " + this.tinfo(tab));
     this.startTabSwitch();
 
+    let oldBrowser = this.requestedTab.linkedBrowser;
+    oldBrowser.deprioritize();
     this.requestedTab = tab;
     if (tabState == this.STATE_LOADED) {
       this.maybeVisibleTabs.clear();
       if (tab.linkedBrowser.isRemoteBrowser) {
         tab.linkedBrowser.forceRepaint();
       }
     }
 
--- a/dom/interfaces/base/nsITabParent.idl
+++ b/dom/interfaces/base/nsITabParent.idl
@@ -38,16 +38,22 @@ interface nsITabParent : nsISupports
   /**
    * Sends a message to the child ensuring that they paint as early as
    * possible. This will send the message to paint even if renderLayers
    * is already true.
    */
   void forceRepaint();
 
   /**
+   * Adjusts the tab's active state in the process priority manager,
+   * allowing its process to be given a lower priority.
+   */
+  void deprioritize();
+
+  /**
    * As an optimisation, setting the docshell's active state to
    * inactive also triggers a layer invalidation to free up some
    * potentially unhelpful memory usage. Calling preserveLayers
    * will cause the layers to be preserved even for inactive
    * docshells.
    */
   void preserveLayers(in boolean aPreserveLayers);
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -165,16 +165,17 @@ TabParent::TabParent(nsIContentParent* a
   , mTabSetsCursor(false)
   , mHasContentOpener(false)
 #ifdef DEBUG
   , mActiveSupressDisplayportCount(0)
 #endif
   , mLayerTreeEpoch{1}
   , mPreserveLayers(false)
   , mRenderLayers(true)
+  , mActiveInPriorityManager(false)
   , mHasLayers(false)
   , mHasPresented(false)
   , mHasBeforeUnload(false)
   , mIsMouseEnterIntoWidgetEventSuppressed(false)
   , mIsActiveRecordReplayTab(false)
 {
   MOZ_ASSERT(aManager);
   // When the input event queue is disabled, we don't need to handle the case
@@ -2853,20 +2854,16 @@ TabParent::SetDocShellIsActive(bool isAc
         } else {
           a11y::nsWinUtils::HideNativeWindow(window);
         }
       }
     }
   }
 #endif
 
-  // Let's inform the priority manager. This operation can end up with the
-  // changing of the process priority.
-  ProcessPriorityManager::TabActivityChanged(this, isActive);
-
   // Keep track of how many active recording/replaying tabs there are.
   if (Manager()->AsContentParent()->IsRecordingOrReplaying()) {
     SetIsActiveRecordReplayTab(isActive);
   }
 
   return NS_OK;
 }
 
@@ -2875,16 +2872,23 @@ TabParent::GetDocShellIsActive(bool* aIs
 {
   *aIsActive = mDocShellIsActive;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::SetRenderLayers(bool aEnabled)
 {
+  if (mActiveInPriorityManager != aEnabled) {
+    mActiveInPriorityManager = aEnabled;
+    // Let's inform the priority manager. This operation can end up with the
+    // changing of the process priority.
+    ProcessPriorityManager::TabActivityChanged(this, aEnabled);
+  }
+
   if (aEnabled == mRenderLayers) {
     if (aEnabled && mHasLayers && mPreserveLayers) {
       // RenderLayers might be called when we've been preserving layers,
       // and already had layers uploaded. In that case, the MozLayerTreeReady
       // event will not naturally arrive, which can confuse the front-end
       // layer. So we fire the event here.
       RefPtr<TabParent> self = this;
       LayersObserverEpoch epoch = mLayerTreeEpoch;
@@ -2921,18 +2925,36 @@ TabParent::GetRenderLayers(bool* aResult
 NS_IMETHODIMP
 TabParent::GetHasLayers(bool* aResult)
 {
   *aResult = mHasLayers;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TabParent::Deprioritize()
+{
+  if (mActiveInPriorityManager)   {
+    ProcessPriorityManager::TabActivityChanged(this, false);
+    mActiveInPriorityManager = false;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TabParent::ForceRepaint()
 {
+  if (!mActiveInPriorityManager) {
+    // If a tab is left and then returned to very rapidly, it can be
+    // deprioritized without losing its loaded status. In this case we won't
+    // go through SetRenderLayers.
+    mActiveInPriorityManager = true;
+    ProcessPriorityManager::TabActivityChanged(this, true);
+  }
+
   SetRenderLayersInternal(true /* aEnabled */,
                           true /* aForceRepaint */);
   return NS_OK;
 }
 
 void
 TabParent::SetRenderLayersInternal(bool aEnabled, bool aForceRepaint)
 {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -770,16 +770,19 @@ private:
   // the tab's docshell is inactive.
   bool mPreserveLayers;
 
   // Holds the most recent value passed to the RenderLayers function. This
   // does not necessarily mean that the layers have finished rendering
   // and have uploaded - for that, use mHasLayers.
   bool mRenderLayers;
 
+  // Whether this is active for the ProcessPriorityManager or not.
+  bool mActiveInPriorityManager;
+
   // True if the compositor has reported that the TabChild has uploaded
   // layers.
   bool mHasLayers;
 
   // True if this TabParent has had its layer tree sent to the compositor
   // at least once.
   bool mHasPresented;
 
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -247,16 +247,28 @@
           }
           let {frameLoader} = this;
           if (frameLoader.tabParent) {
             frameLoader.tabParent.preserveLayers(preserve);
           }
         ]]></body>
       </method>
 
+      <method name="deprioritize">
+        <body><![CDATA[
+          if (!this.isRemoteBrowser) {
+            return;
+          }
+          let {frameLoader} = this;
+          if (frameLoader.tabParent) {
+            frameLoader.tabParent.deprioritize();
+          }
+        ]]></body>
+      </method>
+
 
       <property name="renderLayers">
         <getter>
           <![CDATA[
             if (this.isRemoteBrowser) {
               let {frameLoader} = this;
               if (frameLoader && frameLoader.tabParent) {
                 return frameLoader.tabParent.renderLayers;