Bug 1274919 - part1 : resume video decoding when cursor is hovering over the tab, and suspend it again when cursor leaves the tab. draft
authorAlastor Wu <alwu@mozilla.com>
Fri, 14 Jul 2017 16:17:35 +0800
changeset 608868 c6979124cce1cc023ef256581138a999de3bd6d0
parent 603204 6f8f10f48ace5692256efd91f011bd23054ee2ec
child 608869 768056fd058dafe99e5dd99abdac98c7c35f5c78
push id68429
push useralwu@mozilla.com
push dateFri, 14 Jul 2017 08:20:09 +0000
bugs1274919
milestone56.0a1
Bug 1274919 - part1 : resume video decoding when cursor is hovering over the tab, and suspend it again when cursor leaves the tab. If the tab is in the background and contains playing video, we would stop the video decoding in order to reduce the power consumption. It would be resumed after the tab goes back to foreground. When resuming the video, we need to seek the video to the specific time. If the video seeking is too slowly, the audio and video would not be synchronized and user would see the black image. To avoid this situation, we need to resume video decoding in advance. When the cursor is hovering over the tab, we assume user would open that tab soon, so we can start to resume the video decoding. MozReview-Commit-ID: 7LaN6UMxlXS
browser/base/content/tabbrowser.xml
modules/libpref/init/all.js
toolkit/content/browser-content.js
toolkit/content/widgets/browser.xml
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -7511,16 +7511,41 @@
 
       <!--
       While it would make sense to track this in a field, the field will get nuked
       once the node is gone from the DOM, which causes us to think the tab is not
       closed, which causes us to make wrong decisions. So we use an expando instead.
       <field name="closing">false</field>
       -->
 
+      <method name="resumeBackgroundVideoWhenTabIsHoveringByCursor">
+        <body><![CDATA[
+          if (!Services.prefs.getBoolPref("media.suspend-bkgnd-video.resume-when-tab-is-hovering-by-cursor")) {
+            return;
+          }
+
+          // When the tab is hovering by cursor, we assume that the user would
+          // open that tab soon. Therefore, in advance to resume the background
+          // video to avoid user see the incorrect video frame.
+          this.linkedBrowser.resumeBackgroundVideo();
+        ]]></body>
+      </method>
+
+      <method name="suspendBackgroundVideoWhenTabIsNotHoveringByCursor">
+        <body><![CDATA[
+          if (!Services.prefs.getBoolPref("media.suspend-bkgnd-video.resume-when-tab-is-hovering-by-cursor")) {
+            return;
+          }
+
+          // Suspend the video decoding again for background tab to reduce the
+          // power consumption.
+          this.linkedBrowser.suspendBackgroundVideo();
+        ]]></body>
+      </method>
+
       <method name="_mouseenter">
         <body><![CDATA[
           if (this.hidden || this.closing)
             return;
 
           let tabContainer = this.parentNode;
           let visibleTabs = tabContainer.tabbrowser.visibleTabs;
           let tabIndex = visibleTabs.indexOf(this);
@@ -7544,32 +7569,38 @@
             let candidate = visibleTabs[tabIndex + 1];
             if (!candidate.selected) {
               tabContainer._afterHoveredTab = candidate;
               candidate.setAttribute("afterhovered", "true");
             }
           }
 
           tabContainer._hoveredTab = this;
+          if (!this.selected) {
+            this.resumeBackgroundVideoWhenTabIsHoveringByCursor();
+          }
         ]]></body>
       </method>
 
       <method name="_mouseleave">
         <body><![CDATA[
           let tabContainer = this.parentNode;
           if (tabContainer._beforeHoveredTab) {
             tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
             tabContainer._beforeHoveredTab = null;
           }
           if (tabContainer._afterHoveredTab) {
             tabContainer._afterHoveredTab.removeAttribute("afterhovered");
             tabContainer._afterHoveredTab = null;
           }
 
           tabContainer._hoveredTab = null;
+          if (!this.selected) {
+            this.suspendBackgroundVideoWhenTabIsNotHoveringByCursor();
+          }
         ]]></body>
       </method>
 
       <method name="startMediaBlockTimer">
         <body><![CDATA[
           TelemetryStopwatch.start("TAB_MEDIA_BLOCKING_TIME_MS", this);
         ]]></body>
       </method>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -445,16 +445,20 @@ pref("media.decoder-doctor.wmf-disabled-
 // URL to report decode issues
 pref("media.decoder-doctor.new-issue-endpoint", "https://webcompat.com/issues/new");
 
 // Whether to suspend decoding of videos in background tabs.
 pref("media.suspend-bkgnd-video.enabled", true);
 // Delay, in ms, from time window goes to background to suspending
 // video decoders. Defaults to 10 seconds.
 pref("media.suspend-bkgnd-video.delay-ms", 10000);
+// If the cursor is hovering over the tab which has suspended playing video,
+// we would resume it in advance to avoid users see the incorrect video frame
+// after opening the tab.
+pref("media.suspend-bkgnd-video.resume-when-tab-is-hovering-by-cursor", true);;
 
 #ifdef MOZ_WEBRTC
 pref("media.navigator.enabled", true);
 pref("media.navigator.video.enabled", true);
 pref("media.navigator.load_adapt", true);
 pref("media.navigator.load_adapt.encoder_only", true);
 pref("media.navigator.load_adapt.measure_interval",1000);
 pref("media.navigator.load_adapt.avg_seconds",3);
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -957,16 +957,20 @@ addMessageListener("WebChannelMessageToC
     } else {
       Cu.reportError("WebChannel message failed. Principal mismatch.");
     }
   } else {
     Cu.reportError("WebChannel message failed. No message data.");
   }
 });
 
+addMessageListener("Media:BackgroundVideoDecoding", message => {
+  Services.obs.notifyObservers(content.window, "background-video-decoding", message.data.type);
+});
+
 var AudioPlaybackListener = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   init() {
     Services.obs.addObserver(this, "audio-playback");
 
     addMessageListener("AudioPlayback", this);
     addEventListener("unload", () => {
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -845,16 +845,44 @@
               let event = document.createEvent("Events");
               event.initEvent("DOMAudioPlaybackBlockStopped", true, false);
               this.dispatchEvent(event);
             }
           ]]>
         </body>
       </method>
 
+      <method name="suspendBackgroundVideo">
+        <body>
+          <![CDATA[
+            if (!Services.prefs.getBoolPref("media.suspend-bkgnd-video.enabled") ||
+                !Services.prefs.getBoolPref("media.suspend-bkgnd-video.resume-when-tab-is-hovering-by-cursor")) {
+              return;
+            }
+            // TODO : only dispatch event when the tab has playing video
+            this.messageManager.sendAsyncMessage("Media:BackgroundVideoDecoding",
+              { type: "suspend" });
+          ]]>
+        </body>
+      </method>
+
+      <method name="resumeBackgroundVideo">
+        <body>
+          <![CDATA[
+            if (!Services.prefs.getBoolPref("media.suspend-bkgnd-video.enabled") ||
+                !Services.prefs.getBoolPref("media.suspend-bkgnd-video.resume-when-tab-is-hovering-by-cursor")) {
+              return;
+            }
+            // TODO : only dispatch event when the tab has playing video
+            this.messageManager.sendAsyncMessage("Media:BackgroundVideoDecoding",
+              { type: "resume" });
+          ]]>
+        </body>
+      </method>
+
       <property name="securityUI">
         <getter>
           <![CDATA[
             if (!this.docShell.securityUI) {
               const SECUREBROWSERUI_CONTRACTID = "@mozilla.org/secure_browser_ui;1";
               if (!this.hasAttribute("disablesecurity") &&
                   SECUREBROWSERUI_CONTRACTID in Components.classes) {
                 var securityUI = Components.classes[SECUREBROWSERUI_CONTRACTID]