Bug 1347791 - part4 : keep tab's block state consistent after session restore.
If the tab was resumed before, it could start playing any autoplay media without user's
permission after session restore.
MozReview-Commit-ID: C3DHIIsLtJA
--- 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");