Bug 1458249 - Add autoplay item to about:preferences. r=johannh draft
authorDale Harvey <dale@arandomurl.com>
Thu, 03 May 2018 07:55:53 +0100
changeset 801772 c0e9a35ba33bb3732f97078ceeec6c7559a26af8
parent 800828 4d7efa2ca9e2aae089754cd85558a048c9121512
child 802594 7d62cc11c52b599f37d20d229a4fee5e9fee612f
push id111732
push userbmo:dharvey@mozilla.com
push dateWed, 30 May 2018 18:27:43 +0000
reviewersjohannh
bugs1458249
milestone62.0a1
Bug 1458249 - Add autoplay item to about:preferences. r=johannh MozReview-Commit-ID: AldNoElwH1O
browser/components/preferences/in-content/privacy.js
browser/components/preferences/in-content/privacy.xul
browser/components/preferences/in-content/tests/browser.ini
browser/components/preferences/in-content/tests/browser_site_autoplay_media_exceptions.js
browser/components/preferences/permissions.js
browser/locales/en-US/browser/preferences/permissions.ftl
browser/locales/en-US/browser/preferences/preferences.ftl
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -71,16 +71,20 @@ Preferences.addAll([
   { id: "network.cookie.lifetimePolicy", type: "int" },
   { id: "network.cookie.blockFutureCookies", type: "bool" },
   // Clear Private Data
   { id: "privacy.sanitize.sanitizeOnShutdown", type: "bool" },
   { id: "privacy.sanitize.timeSpan", type: "int" },
   // Do not track
   { id: "privacy.donottrackheader.enabled", type: "bool" },
 
+  // Media
+  { id: "media.autoplay.enabled", type: "bool" },
+  { id: "media.autoplay.enabled.user-gestures-needed", type: "bool" },
+
   // Popups
   { id: "dom.disable_open_during_load", type: "bool" },
   // Passwords
   { id: "signon.rememberSignons", type: "bool" },
 
   // Buttons
   { id: "pref.privacy.disable_button.view_passwords", type: "bool" },
   { id: "pref.privacy.disable_button.view_passwords_exceptions", type: "bool" },
@@ -249,32 +253,38 @@ var gPrivacyPane = {
   init() {
     function setEventListener(aId, aEventType, aCallback) {
       document.getElementById(aId)
         .addEventListener(aEventType, aCallback.bind(gPrivacyPane));
     }
 
     this._updateSanitizeSettingsButton();
     this.initializeHistoryMode();
+    this.updateAutoplayMediaControls();
+    this.updateAutoplayMediaControlsVisibility();
     this.updateHistoryModePane();
     this.updatePrivacyMicroControls();
     this.initAutoStartPrivateBrowsingReverter();
     this._initTrackingProtection();
     this._initTrackingProtectionPBM();
     this._initTrackingProtectionExtensionControl();
     this._initAutocomplete();
 
     Preferences.get("privacy.sanitize.sanitizeOnShutdown").on("change",
       gPrivacyPane._updateSanitizeSettingsButton.bind(gPrivacyPane));
     Preferences.get("browser.privatebrowsing.autostart").on("change",
       gPrivacyPane.updatePrivacyMicroControls.bind(gPrivacyPane));
     Preferences.get("privacy.trackingprotection.enabled").on("change",
       gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
     Preferences.get("privacy.trackingprotection.pbmode.enabled").on("change",
       gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
+    Preferences.get("media.autoplay.enabled").on("change",
+     gPrivacyPane.updateAutoplayMediaControls.bind(gPrivacyPane));
+    Preferences.get("media.autoplay.enabled.user-gestures-needed").on("change",
+     gPrivacyPane.updateAutoplayMediaControlsVisibility.bind(gPrivacyPane));
     setEventListener("historyMode", "command", function() {
       gPrivacyPane.updateHistoryModePane();
       gPrivacyPane.updateHistoryModePrefs();
       gPrivacyPane.updatePrivacyMicroControls();
       gPrivacyPane.updateAutostart();
     });
     setEventListener("clearHistoryButton", "command", function() {
       let historyMode = document.getElementById("historyMode");
@@ -327,16 +337,20 @@ var gPrivacyPane = {
     setEventListener("locationSettingsButton", "command",
       gPrivacyPane.showLocationExceptions);
     setEventListener("cameraSettingsButton", "command",
       gPrivacyPane.showCameraExceptions);
     setEventListener("microphoneSettingsButton", "command",
       gPrivacyPane.showMicrophoneExceptions);
     setEventListener("popupPolicyButton", "command",
       gPrivacyPane.showPopupExceptions);
+    setEventListener("autoplayMediaPolicy", "command",
+      gPrivacyPane.toggleAutoplayMedia);
+    setEventListener("autoplayMediaPolicyButton", "command",
+      gPrivacyPane.showAutoplayMediaExceptions);
     setEventListener("notificationsDoNotDisturb", "command",
       gPrivacyPane.toggleDoNotDisturbNotifications);
 
     if (AlertsServiceDND) {
       let notificationsDoNotDisturbBox =
         document.getElementById("notificationsDoNotDisturbBox");
       notificationsDoNotDisturbBox.removeAttribute("hidden");
       let checkbox = document.getElementById("notificationsDoNotDisturb");
@@ -966,16 +980,54 @@ var gPrivacyPane = {
 
     try {
       Services.telemetry
         .getHistogramById("WEB_NOTIFICATION_EXCEPTIONS_OPENED").add();
     } catch (e) { }
   },
 
 
+  // MEDIA
+
+  /**
+   * media.autoplay.enabled works the opposite to most of the other preferences.
+   * The checkbox enabled sets the pref to false
+   */
+  toggleAutoplayMedia(event) {
+    Services.prefs.setBoolPref("media.autoplay.enabled", !event.target.checked);
+  },
+
+  updateAutoplayMediaControls() {
+    let autoPlayEnabled = Preferences.get("media.autoplay.enabled").value;
+    document.getElementById("autoplayMediaPolicy").checked = !autoPlayEnabled;
+    document.getElementById("autoplayMediaPolicyButton").disabled = autoPlayEnabled;
+  },
+
+  /**
+   * Show the controls for the new media autoplay behaviour behind a pref for now
+   */
+  updateAutoplayMediaControlsVisibility() {
+    document.getElementById("autoplayMediaBox").hidden =
+      !Services.prefs.getBoolPref("media.autoplay.enabled.user-gestures-needed");
+  },
+
+  /**
+   * Displays the autoplay exceptions dialog where specific site autoplay preferences
+   * can be set.
+   */
+  showAutoplayMediaExceptions() {
+    var params = {
+      blockVisible: false, sessionVisible: false, allowVisible: true,
+      prefilledHost: "", permissionType: "autoplay-media"
+    };
+
+    gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
+      "resizable=yes", params);
+  },
+
   // POP-UPS
 
   /**
    * Displays the popup exceptions dialog where specific site popup preferences
    * can be set.
    */
   showPopupExceptions() {
     var params = {
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -491,17 +491,35 @@
   </grid>
 
   <vbox id="notificationsDoNotDisturbBox" hidden="true">
     <checkbox id="notificationsDoNotDisturb" class="indent"/>
   </vbox>
 
   <separator flex="1"/>
 
-  <hbox align="start">
+  <hbox align="start" id="autoplayMediaBox" hidden="true">
+    <checkbox id="autoplayMediaPolicy"
+              data-l10n-id="permissions-block-autoplay-media"
+              flex="1" />
+    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
+    <hbox>
+      <button id="autoplayMediaPolicyButton"
+              class="accessory-button"
+              data-l10n-id="permissions-block-autoplay-media-exceptions"
+              search-l10n-ids="permissions-address,
+                               permissions-button-cancel.label,
+                               permissions-button-ok.label,
+                               permissions-exceptions-autoplay-media-window.title,
+                               permissions-exceptions-autoplay-media-desc
+                               " />
+    </hbox>
+  </hbox>
+
+  <hbox>
     <checkbox id="popupPolicy" preference="dom.disable_open_during_load"
               data-l10n-id="permissions-block-popups"
               onsyncfrompreference="return gPrivacyPane.updateButtons('popupPolicyButton',
                                          'dom.disable_open_during_load');"
               flex="1" />
     <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
     <hbox>
       <button id="popupPolicyButton"
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -75,16 +75,17 @@ run-if = nightly_build
 [browser_privacypane_8.js]
 [browser_sanitizeOnShutdown_prefLocked.js]
 [browser_searchShowSuggestionsFirst.js]
 [browser_searchsuggestions.js]
 [browser_security-1.js]
 [browser_security-2.js]
 [browser_spotlight.js]
 [browser_site_login_exceptions.js]
+[browser_site_autoplay_media_exceptions.js]
 [browser_permissions_dialog.js]
 [browser_subdialogs.js]
 support-files =
   subdialog.xul
   subdialog2.xul
 [browser_sync_sanitize.js]
 [browser_telemetry.js]
 # Skip this test on Android as FHR and Telemetry are separate systems there.
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_site_autoplay_media_exceptions.js
@@ -0,0 +1,109 @@
+"use strict";
+
+ChromeUtils.import("resource:///modules/SitePermissions.jsm");
+
+const URL = "http://www.example.com";
+const PRINCIPAL = Services.scriptSecurityManager
+  .createCodebasePrincipal(Services.io.newURI(URL), {});
+
+const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul";
+const AUTOPLAY_ENABLED_KEY = "media.autoplay.enabled";
+const GESTURES_NEEDED_KEY = "media.autoplay.enabled.user-gestures-needed";
+
+var exceptionsDialog;
+
+Services.prefs.setBoolPref(AUTOPLAY_ENABLED_KEY, true);
+Services.prefs.setBoolPref(GESTURES_NEEDED_KEY, false);
+
+async function openExceptionsDialog() {
+  let dialogOpened = promiseLoadSubDialog(PERMISSIONS_URL);
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    let exceptionsButton = content.document.getElementById("autoplayMediaPolicyButton");
+    exceptionsButton.click();
+  });
+  exceptionsDialog = await dialogOpened;
+}
+
+add_task(async function ensureCheckboxHidden() {
+
+  registerCleanupFunction(async function() {
+    Services.prefs.clearUserPref(AUTOPLAY_ENABLED_KEY);
+    Services.prefs.clearUserPref(GESTURES_NEEDED_KEY);
+    gBrowser.removeCurrentTab();
+  });
+
+  await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
+  let win = gBrowser.selectedBrowser.contentWindow;
+  is_element_hidden(win.document.getElementById("autoplayMediaPolicy"),
+                    "Ensure checkbox is hidden when preffed off");
+});
+
+add_task(async function enableBlockingAutoplay() {
+
+  Services.prefs.setBoolPref(GESTURES_NEEDED_KEY, true);
+
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    let doc = content.document;
+    let autoplayCheckBox = doc.getElementById("autoplayMediaPolicy");
+    autoplayCheckBox.click();
+  });
+
+  Assert.equal(Services.prefs.getBoolPref(AUTOPLAY_ENABLED_KEY), false,
+               "Ensure we have set autoplay to false");
+});
+
+add_task(async function addException() {
+  await openExceptionsDialog();
+  let doc = exceptionsDialog.document;
+
+  let tree = doc.getElementById("permissionsTree");
+  Assert.equal(tree.view.rowCount, 0, "Row count should initially be 0");
+
+  let inputBox = doc.getElementById("url");
+  inputBox.focus();
+
+  EventUtils.sendString(URL, exceptionsDialog);
+
+  let btnAllow = doc.getElementById("btnAllow");
+  btnAllow.click();
+
+  await TestUtils.waitForCondition(() => tree.view.rowCount == 1);
+  Assert.equal(tree.view.getCellText(0, tree.treeBoxObject.columns.getColumnAt(0)),
+               URL);
+
+  let permChanged = TestUtils.topicObserved("perm-changed");
+  let btnApplyChanges = doc.getElementById("btnApplyChanges");
+  btnApplyChanges.click();
+  await permChanged;
+
+  is(Services.perms.testPermissionFromPrincipal(PRINCIPAL, "autoplay-media"),
+     Ci.nsIPermissionManager.ALLOW_ACTION, "Correctly added the exception");
+});
+
+add_task(async function deleteException() {
+  await openExceptionsDialog();
+  let doc = exceptionsDialog.document;
+
+  let tree = doc.getElementById("permissionsTree");
+  Assert.equal(tree.view.rowCount, 1, "Row count should initially be 1");
+  tree.focus();
+  tree.view.selection.select(0);
+
+  if (AppConstants.platform == "macosx") {
+    EventUtils.synthesizeKey("KEY_Backspace");
+  } else {
+    EventUtils.synthesizeKey("KEY_Delete");
+  }
+
+  await TestUtils.waitForCondition(() => tree.view.rowCount == 0);
+  is_element_visible(content.gSubDialog._dialogs[0]._box,
+    "Subdialog is visible after deleting an element");
+
+  let permChanged = TestUtils.topicObserved("perm-changed");
+  let btnApplyChanges = doc.getElementById("btnApplyChanges");
+  btnApplyChanges.click();
+  await permChanged;
+
+  is(Services.perms.testPermissionFromPrincipal(PRINCIPAL, "autoplay-media"),
+     Ci.nsIPermissionManager.UNKNOWN_ACTION, "Correctly removed the exception");
+});
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -29,16 +29,20 @@ const permissionExceptionsL10n = {
   "login-saving": {
     window: "permissions-exceptions-saved-logins-window",
     description: "permissions-exceptions-saved-logins-desc",
   },
   "install": {
     window: "permissions-exceptions-addons-window",
     description: "permissions-exceptions-addons-desc",
   },
+  "autoplay-media": {
+    window: "permissions-exceptions-autoplay-media-window",
+    description: "permissions-exceptions-autoplay-media-desc",
+  },
 };
 
 function Permission(principal, type, capability) {
   this.principal = principal;
   this.origin = principal.origin;
   this.type = type;
   this.capability = capability;
 }
--- a/browser/locales/en-US/browser/preferences/permissions.ftl
+++ b/browser/locales/en-US/browser/preferences/permissions.ftl
@@ -91,16 +91,23 @@ permissions-exceptions-saved-logins-desc
 
 ## Exceptions - Add-ons
 
 permissions-exceptions-addons-window =
     .title = Allowed Websites - Add-ons Installation
     .style = { permissions-window.style }
 permissions-exceptions-addons-desc = You can specify which websites are allowed to install add-ons. Type the exact address of the site you want to allow and then click Allow.
 
+## Exceptions - Autoplay Media
+
+permissions-exceptions-autoplay-media-window =
+    .title = Allowed Websites - Autoplay
+    .style = { permissions-window.style }
+permissions-exceptions-autoplay-media-desc = You can specify which websites are allowed to automatically play media elements. Type the exact address of the site you want to allow and then click Allow.
+
 ## Site Permissions - Notifications
 
 permissions-site-notification-window =
     .title = Settings - Notification Permissions
     .style = { permissions-window.style }
 permissions-site-notification-desc = The following websites have requested to send you notifications. You can specify which websites are allowed to send you notifications. You can also block new requests asking to allow notifications.
 permissions-site-notification-disable-label =
     .label = Block new requests asking to allow notifications
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -840,16 +840,24 @@ permissions-notification-settings =
     .label = Settings…
     .accesskey = t
 permissions-notification-link = Learn more
 
 permissions-notification-pause =
     .label = Pause notifications until { -brand-short-name } restarts
     .accesskey = n
 
+permissions-block-autoplay-media =
+    .label = Block websites from automatically playing media with sound
+    .accesskey = B
+
+permissions-block-autoplay-media-exceptions =
+    .label = Exceptions…
+    .accesskey = E
+
 permissions-block-popups =
     .label = Block pop-up windows
     .accesskey = B
 
 permissions-block-popups-exceptions =
     .label = Exceptions…
     .accesskey = E