Bug 1347791 - part4 : keep tab's block state consistent after session restore. r=dao,mikedeboer
authorAlastor Wu <alwu@mozilla.com>
Thu, 29 Jun 2017 05:46:28 -0700
changeset 366760 49b5332313881379d46654be04c912c78fed1753
parent 366759 a5fbb7e2d1d0fc6cd8b3a4f56299edd31ab4ea8a
child 366761 36fbfac0ea9698ee8e6974a296e9903da318fa97
push id32108
push usercbook@mozilla.com
push dateFri, 30 Jun 2017 10:57:12 +0000
treeherdermozilla-central@a578ce873d80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao, mikedeboer
bugs1347791
milestone56.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 1347791 - part4 : keep tab's block state consistent after session restore. r=dao,mikedeboer If the tab was resumed before, it could start playing any autoplay media without user's permission after session restore. MozReview-Commit-ID: C3DHIIsLtJA
browser/base/content/tabbrowser.xml
browser/components/sessionstore/SessionStore.jsm
browser/components/sessionstore/TabAttributes.jsm
browser/components/sessionstore/TabState.jsm
browser/components/sessionstore/test/browser_attributes.js
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2157,17 +2157,17 @@
         "goHome", "homePage", "gotoIndex", "currentURI", "documentURI",
         "preferences", "imageDocument", "isRemoteBrowser", "messageManager",
         "getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle",
         "characterSet", "fullZoom", "textZoom", "webProgress",
         "addProgressListener", "removeProgressListener", "audioPlaybackStarted",
         "audioPlaybackStopped", "pauseMedia", "stopMedia",
         "blockMedia", "resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
         "purgeSessionHistory", "stopScroll", "startScroll",
-        "userTypedValue", "userTypedClear"
+        "userTypedValue", "userTypedClear", "mediaBlocked"
       ]</field>
 
       <method name="_createLazyBrowser">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             let browser = aTab.linkedBrowser;
 
@@ -2221,18 +2221,31 @@
                       // initializing the reload.
                       aTab.addEventListener("SSTabRestoring", () => {
                         browser[name](params);
                       }, { once: true });
                       gBrowser._insertBrowser(aTab);
                     };
                   };
                   break;
+                case "blockMedia":
+                case "resumeMedia":
+                  getter = () => {
+                    return () => {
+                      // No need to insert a browser, so we just call the browser's
+                      // method.
+                      aTab.addEventListener("SSTabRestoring", () => {
+                        browser[name]();
+                      }, { once: true });
+                    };
+                  };
+                  break;
                 case "userTypedValue":
                 case "userTypedClear":
+                case "mediaBlocked":
                   getter = () => {
                     return SessionStore.getLazyTabValue(aTab, name);
                   };
                   break;
                 default:
                   getter = () => {
                     if (AppConstants.NIGHTLY_BUILD) {
                       let message =
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3593,16 +3593,22 @@ var SessionStoreInternal = {
     } else {
       tabbrowser.showTab(tab);
     }
 
     if (!!tabData.muted != browser.audioMuted) {
       tab.toggleMuteAudio(tabData.muteReason);
     }
 
+    if (tabData.mediaBlocked) {
+      browser.blockMedia();
+    } else {
+      browser.resumeMedia();
+    }
+
     if (tabData.lastAccessed) {
       tab.updateLastAccessed(tabData.lastAccessed);
     }
 
     if ("attributes" in tabData) {
       // Ensure that we persist tab attributes restored from previous sessions.
       Object.keys(tabData.attributes).forEach(a => TabAttributes.persist(a));
     }
--- a/browser/components/sessionstore/TabAttributes.jsm
+++ b/browser/components/sessionstore/TabAttributes.jsm
@@ -9,18 +9,21 @@ this.EXPORTED_SYMBOLS = ["TabAttributes"
 // We never want to directly read or write these attributes.
 // 'image' should not be accessed directly but handled by using the
 //         gBrowser.getIcon()/setIcon() methods.
 // 'muted' should not be accessed directly but handled by using the
 //         tab.linkedBrowser.audioMuted/toggleMuteAudio methods.
 // 'pending' is used internal by sessionstore and managed accordingly.
 // 'iconLoadingPrincipal' is same as 'image' that it should be handled by
 //                        using the gBrowser.getIcon()/setIcon() methods.
+// 'activemedia-blocked' should not be accessed directly but handled by using
+//                       tab's toggleMuteAudio() or linkedBrowser's methods
+//                       activeMediaBlockStarted()/activeMediaBlockBlockStopped().
 const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending", "iconLoadingPrincipal",
-                                    "skipbackgroundnotify"]);
+                                    "skipbackgroundnotify", "activemedia-blocked"]);
 
 // A set of tab attributes to persist. We will read a given list of tab
 // attributes when collecting tab data and will re-set those attributes when
 // the given tab data is restored to a new tab.
 this.TabAttributes = Object.freeze({
   persist(name) {
     return TabAttributesInternal.persist(name);
   },
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -97,16 +97,18 @@ var TabStateInternal = {
 
     tabData.hidden = tab.hidden;
 
     if (browser.audioMuted) {
       tabData.muted = true;
       tabData.muteReason = tab.muteReason;
     }
 
+    tabData.mediaBlocked = browser.mediaBlocked;
+
     // Save tab attributes.
     tabData.attributes = TabAttributes.get(tab);
 
     if (tab.__SS_extdata) {
       tabData.extData = tab.__SS_extdata;
     }
 
     // Copy data from the tab state cache only if the tab has fully finished
--- a/browser/components/sessionstore/test/browser_attributes.js
+++ b/browser/components/sessionstore/test/browser_attributes.js
@@ -1,44 +1,56 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * This test makes sure that we correctly preserve tab attributes when storing
  * and restoring tabs. It also ensures that we skip special attributes like
- * 'image', 'muted' and 'pending' that need to be handled differently or internally.
+ * 'image', 'muted', 'activemedia-blocked' and 'pending' that need to be
+ * handled differently or internally.
  */
 
 const PREF = "browser.sessionstore.restore_on_demand";
+const PREF2 = "media.block-autoplay-until-in-foreground";
 
 add_task(async function test() {
   Services.prefs.setBoolPref(PREF, true)
   registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
 
+  // Since we need to test 'activemedia-blocked' attribute.
+  Services.prefs.setBoolPref(PREF2, true)
+  registerCleanupFunction(() => Services.prefs.clearUserPref(PREF2));
+
   // Add a new tab with a nice icon.
   let tab = BrowserTestUtils.addTab(gBrowser, "about:robots");
   await promiseBrowserLoaded(tab.linkedBrowser);
 
   // Check that the tab has 'image' and 'iconLoadingPrincipal' attributes.
   ok(tab.hasAttribute("image"), "tab.image exists");
   ok(tab.hasAttribute("iconLoadingPrincipal"), "tab.iconLoadingPrincipal exists");
 
   tab.toggleMuteAudio();
   // Check that the tab has a 'muted' attribute.
   ok(tab.hasAttribute("muted"), "tab.muted exists");
 
-  // Make sure we do not persist 'image' or 'muted' attributes.
+  // Pretend to start autoplay media in tab, tab should get the notification.
+  tab.linkedBrowser.activeMediaBlockStarted();
+  ok(tab.hasAttribute("activemedia-blocked"), "tab.activemedia-blocked exists");
+
+  // Make sure we do not persist 'image','muted' and 'activemedia-blocked' attributes.
   ss.persistTabAttribute("image");
   ss.persistTabAttribute("muted");
   ss.persistTabAttribute("iconLoadingPrincipal");
+  ss.persistTabAttribute("activemedia-blocked");
   let {attributes} = JSON.parse(ss.getTabState(tab));
   ok(!("image" in attributes), "'image' attribute not saved");
   ok(!("iconLoadingPrincipal" in attributes), "'iconLoadingPrincipal' attribute not saved");
   ok(!("muted" in attributes), "'muted' attribute not saved");
   ok(!("custom" in attributes), "'custom' attribute not saved");
+  ok(!("activemedia-blocked" in attributes), "'activemedia-blocked' attribute not saved");
 
   // Test persisting a custom attribute.
   tab.setAttribute("custom", "foobar");
   ss.persistTabAttribute("custom");
 
   ({attributes} = JSON.parse(ss.getTabState(tab)));
   is(attributes.custom, "foobar", "'custom' attribute is correct");