Bug 1461656 - Ask permission when site wants to autoplay media. r=johannh, r=flod draft
authorDale Harvey <dale@arandomurl.com>
Fri, 18 May 2018 12:54:33 +0100
changeset 808018 e62b9328b1882cc7c6fffa99461d5c5340700bb9
parent 807769 f7bf77f5b69a7e98273cbea1ea888617863e5198
push id113254
push userbmo:dharvey@mozilla.com
push dateSun, 17 Jun 2018 01:49:35 +0000
reviewersjohannh, flod
bugs1461656
milestone62.0a1
Bug 1461656 - Ask permission when site wants to autoplay media. r=johannh, r=flod MozReview-Commit-ID: Fc2IUZK90eu
browser/base/content/browser-siteIdentity.js
browser/base/content/browser.xul
browser/components/nsBrowserGlue.js
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/PermissionUI.jsm
browser/modules/SitePermissions.jsm
browser/modules/test/browser/browser_PermissionUI_prompts.js
browser/themes/shared/jar.inc.mn
browser/themes/shared/notification-icons.inc.css
browser/themes/shared/notification-icons/autoplay-media-blocked.svg
browser/themes/shared/notification-icons/autoplay-media.svg
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -1051,17 +1051,19 @@ var gIdentityHandler = {
       if (aPermission.state == SitePermissions.getDefault(aPermission.id)) {
         menulist.value = "0";
       } else {
         menulist.value = aPermission.state;
       }
 
       // Avoiding listening to the "select" event on purpose. See Bug 1404262.
       menulist.addEventListener("command", () => {
-        SitePermissions.set(gBrowser.currentURI, "popup", menulist.selectedItem.value);
+        SitePermissions.set(gBrowser.currentURI,
+                            aPermission.id,
+                            menulist.selectedItem.value);
       });
 
       container.appendChild(img);
       container.appendChild(nameLabel);
       container.appendChild(menulist);
       block.appendChild(container);
 
       return block;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -819,32 +819,36 @@
                   <image data-permission-id="microphone" class="blocked-permission-icon microphone-icon" role="button"
                          tooltiptext="&urlbar.microphoneBlocked.tooltip;"/>
                   <image data-permission-id="screen" class="blocked-permission-icon screen-icon" role="button"
                          tooltiptext="&urlbar.screenBlocked.tooltip;"/>
                   <image data-permission-id="persistent-storage" class="blocked-permission-icon persistent-storage-icon" role="button"
                          tooltiptext="&urlbar.persistentStorageBlocked.tooltip;"/>
                   <image data-permission-id="popup" class="blocked-permission-icon popup-icon" role="button"
                          tooltiptext="&urlbar.popupBlocked.tooltip;"/>
+                  <image data-permission-id="autoplay-media" class="blocked-permission-icon autoplay-media-icon" role="button"
+                         tooltiptext="&urlbar.autoplayMediaBlocked.tooltip;"/>
                   <image data-permission-id="canvas" class="blocked-permission-icon canvas-icon" role="button"
                          tooltiptext="&urlbar.canvasBlocked.tooltip;"/>
                   <image data-permission-id="plugin:flash" class="blocked-permission-icon plugin-icon" role="button"
                          tooltiptext="&urlbar.flashPluginBlocked.tooltip;"/>
                   <image data-permission-id="midi" class="blocked-permission-icon midi-icon" role="button"
                          tooltiptext="&urlbar.midiBlocked.tooltip;"/>
                 </box>
                 <box id="notification-popup-box"
                      hidden="true"
                      onmouseover="document.getElementById('identity-box').classList.add('no-hover');"
                      onmouseout="document.getElementById('identity-box').classList.remove('no-hover');"
                      align="center">
                   <image id="default-notification-icon" class="notification-anchor-icon" role="button"
                          tooltiptext="&urlbar.defaultNotificationAnchor.tooltip;"/>
                   <image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
                          tooltiptext="&urlbar.geolocationNotificationAnchor.tooltip;"/>
+                  <image id="autoplay-media-notification-icon" class="notification-anchor-icon autoplay-media-icon" role="button"
+                         tooltiptext="&urlbar.autoplayNotificationAnchor.tooltip;"/>
                   <image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
                          tooltiptext="&urlbar.addonsNotificationAnchor.tooltip;"/>
                   <image id="canvas-notification-icon" class="notification-anchor-icon" role="button"
                          tooltiptext="&urlbar.canvasNotificationAnchor.tooltip;"/>
                   <image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
                          tooltiptext="&urlbar.indexedDBNotificationAnchor.tooltip;"/>
                   <image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
                          tooltiptext="&urlbar.passwordNotificationAnchor.tooltip;"/>
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2838,16 +2838,19 @@ const ContentPermissionIntegration = {
         return new PermissionUI.DesktopNotificationPermissionPrompt(request);
       }
       case "persistent-storage": {
         return new PermissionUI.PersistentStoragePermissionPrompt(request);
       }
       case "midi": {
         return new PermissionUI.MIDIPermissionPrompt(request);
       }
+      case "autoplay-media": {
+        return new PermissionUI.AutoplayPermissionPrompt(request);
+      }
     }
     return undefined;
   },
 };
 
 function ContentPermissionPrompt() {}
 
 ContentPermissionPrompt.prototype = {
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -208,16 +208,17 @@ These should match what Safari and other
 <!ENTITY goEndCap.tooltip             "Go to the address in the Location Bar">
 <!ENTITY printButton.label            "Print">
 <!ENTITY printButton.tooltip          "Print this page">
 
 <!ENTITY urlbar.viewSiteInfo.label                      "View site information">
 
 <!ENTITY urlbar.defaultNotificationAnchor.tooltip         "Open message panel">
 <!ENTITY urlbar.geolocationNotificationAnchor.tooltip     "Open location request panel">
+<!ENTITY urlbar.autoplayNotificationAnchor.tooltip        "Open autoplay panel">
 <!ENTITY urlbar.addonsNotificationAnchor.tooltip          "Open add-on installation message panel">
 <!ENTITY urlbar.canvasNotificationAnchor.tooltip          "Manage canvas extraction permission">
 <!ENTITY urlbar.indexedDBNotificationAnchor.tooltip       "Open offline storage message panel">
 <!ENTITY urlbar.passwordNotificationAnchor.tooltip        "Open save password message panel">
 <!ENTITY urlbar.pluginsNotificationAnchor.tooltip         "Manage plug-in use">
 <!ENTITY urlbar.webNotificationAnchor.tooltip             "Change whether you can receive notifications from the site">
 <!ENTITY urlbar.persistentStorageNotificationAnchor.tooltip     "Store data in Persistent Storage">
 <!ENTITY urlbar.remoteControlNotificationAnchor.tooltip   "Browser is under remote control">
@@ -235,16 +236,17 @@ These should match what Safari and other
 
 <!ENTITY urlbar.cameraBlocked.tooltip            "You have blocked your camera for this website.">
 <!ENTITY urlbar.microphoneBlocked.tooltip        "You have blocked your microphone for this website.">
 <!ENTITY urlbar.screenBlocked.tooltip            "You have blocked this website from sharing your screen.">
 <!ENTITY urlbar.geolocationBlocked.tooltip       "You have blocked location information for this website.">
 <!ENTITY urlbar.webNotificationsBlocked.tooltip  "You have blocked notifications for this website.">
 <!ENTITY urlbar.persistentStorageBlocked.tooltip "You have blocked persistent storage for this website.">
 <!ENTITY urlbar.popupBlocked.tooltip             "You have blocked pop-ups for this website.">
+<!ENTITY urlbar.autoplayMediaBlocked.tooltip     "You have blocked autoplay media with sound for this website.">
 <!ENTITY urlbar.canvasBlocked.tooltip            "You have blocked canvas data extraction for this website.">
 <!ENTITY urlbar.flashPluginBlocked.tooltip       "You have blocked this website from using the Adobe Flash plugin.">
 <!ENTITY urlbar.midiBlocked.tooltip              "You have blocked MIDI access for this website.">
 
 <!ENTITY urlbar.openHistoryPopup.tooltip                "Show history">
 
 <!ENTITY searchItem.title             "Search">
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -946,12 +946,20 @@ midi.DontAllow.accesskey = N
 midi.remember=Remember this decision
 midi.shareWithFile.message = Will you allow this local file to access your MIDI Devices?
 # LOCALIZATION NOTE (midi.shareWithSite.message): %S is the name of the site URL (https://...) requesting MIDI access
 midi.shareWithSite.message = Will you allow %S to access your MIDI Devices?
 midi.shareSysexWithFile.message = Will you allow this local file to access your MIDI devices and send/receive SysEx messages?
 # LOCALIZATION NOTE (midi.shareSysexWithSite.message): %S is the name of the site URL (https://...) requesting MIDI access
 midi.shareSysexWithSite.message = Will you allow %S to access your MIDI devices and send/receive SysEx messages?
 
+autoplay.Allow.label = Allow Autoplay
+autoplay.Allow.accesskey = A
+autoplay.DontAllow.label = Don’t Allow
+autoplay.DontAllow.accesskey = n
+autoplay.remember = Remember this decision
+# LOCALIZATION NOTE (autoplay.message): %S is the name of the site URL (https://...) trying to autoplay media
+autoplay.message = Will you allow %S to autoplay media with sound?
+autoplay.messageWithFile = Will you allow this file to autoplay media with sound?
 # LOCALIZATION NOTE (panel.back):
 # This is used by screen readers to label the "back" button in various browser
 # popup panels, including the sliding subviews of the main menu.
 panel.back = Back
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -742,8 +742,69 @@ MIDIPermissionPrompt.prototype = {
     }];
   },
 
   onBeforeShow() {
   },
 };
 
 PermissionUI.MIDIPermissionPrompt = MIDIPermissionPrompt;
+
+function AutoplayPermissionPrompt(request) {
+  this.request = request;
+}
+
+AutoplayPermissionPrompt.prototype = {
+  __proto__: PermissionPromptForRequestPrototype,
+
+  get permissionKey() {
+    return "autoplay-media";
+  },
+
+  get popupOptions() {
+    let checkbox = {
+      show: !PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal) &&
+        !this.principal.URI.schemeIs("file")
+    };
+    if (checkbox.show) {
+      checkbox.checked = true;
+      checkbox.label = gBrowserBundle.GetStringFromName("autoplay.remember");
+    }
+    return {
+      checkbox,
+      displayURI: false,
+      name: this.principal.URI.hostPort,
+    };
+  },
+
+  get notificationID() {
+    return "autoplay-media";
+  },
+
+  get anchorID() {
+    return "autoplay-media-icon";
+  },
+
+  get message() {
+    if (this.principal.URI.schemeIs("file")) {
+      return gBrowserBundle.GetStringFromName("autoplay.messageWithFile");
+    }
+    return gBrowserBundle.formatStringFromName("autoplay.message", ["<>"], 1);
+  },
+
+  get promptActions() {
+    return [{
+        label: gBrowserBundle.GetStringFromName("autoplay.Allow.label"),
+        accessKey: gBrowserBundle.GetStringFromName("autoplay.Allow.accesskey"),
+        action: Ci.nsIPermissionManager.ALLOW_ACTION,
+      },
+      {
+        label: gBrowserBundle.GetStringFromName("autoplay.DontAllow.label"),
+        accessKey: gBrowserBundle.GetStringFromName("autoplay.DontAllow.accesskey"),
+        action: Ci.nsIPermissionManager.DENY_ACTION,
+    }];
+  },
+
+  onBeforeShow() {
+  },
+};
+
+PermissionUI.AutoplayPermissionPrompt = AutoplayPermissionPrompt;
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -606,18 +606,19 @@ var gPermissionObject = {
    */
 
   "autoplay-media": {
     exactHostMatch: true,
     getDefault() {
       if (Services.prefs.getBoolPref("media.autoplay.enabled")) {
         return SitePermissions.ALLOW;
       }
-      return SitePermissions.BLOCK;
-    }
+      return SitePermissions.UNKNOWN;
+    },
+    labelID: "autoplay-media"
   },
 
   "image": {
     states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
   },
 
   "cookie": {
     states: [ SitePermissions.ALLOW, SitePermissions.ALLOW_COOKIES_FOR_SESSION, SitePermissions.BLOCK ],
--- a/browser/modules/test/browser/browser_PermissionUI_prompts.js
+++ b/browser/modules/test/browser/browser_PermissionUI_prompts.js
@@ -25,16 +25,23 @@ add_task(async function test_persistent_
   await testPrompt(PermissionUI.PersistentStoragePermissionPrompt);
 });
 
 // Tests that MidiPrompt works as expected
 add_task(async function test_midi_permission_prompt() {
   await testPrompt(PermissionUI.MIDIPermissionPrompt);
 });
 
+// Tests that AutoplayPermissionPrompt works as expected
+add_task(async function test_autoplay_permission_prompt() {
+  Services.prefs.setBoolPref("media.autoplay.enabled", false);
+  await testPrompt(PermissionUI.AutoplayPermissionPrompt);
+  Services.prefs.clearUserPref("media.autoplay.enabled");
+});
+
 async function testPrompt(Prompt) {
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: "http://example.com",
   }, async function(browser) {
     let mockRequest = makeMockPermissionRequest(browser);
     let principal = mockRequest.principal;
     let TestPrompt = new Prompt(mockRequest);
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -50,16 +50,18 @@
   skin/classic/browser/connection-mixed-active-loaded.svg      (../shared/identity-block/connection-mixed-active-loaded.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-icon-notice.svg                (../shared/identity-block/identity-icon-notice.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
   skin/classic/browser/searchReset.css                         (../shared/searchReset.css)
 
   skin/classic/browser/illustrations/error-session-restore.svg (../shared/illustrations/error-session-restore.svg)
 
+  skin/classic/browser/notification-icons/autoplay-media.svg                (../shared/notification-icons/autoplay-media.svg)
+  skin/classic/browser/notification-icons/autoplay-media-blocked.svg        (../shared/notification-icons/autoplay-media-blocked.svg)
   skin/classic/browser/notification-icons/camera-blocked.svg                (../shared/notification-icons/camera-blocked.svg)
   skin/classic/browser/notification-icons/camera.svg                        (../shared/notification-icons/camera.svg)
   skin/classic/browser/notification-icons/canvas-blocked.svg                (../shared/notification-icons/canvas-blocked.svg)
   skin/classic/browser/notification-icons/canvas.svg                        (../shared/notification-icons/canvas.svg)
   skin/classic/browser/notification-icons/default-info.svg                  (../shared/notification-icons/default-info.svg)
   skin/classic/browser/notification-icons/desktop-notification-blocked.svg  (../shared/notification-icons/desktop-notification-blocked.svg)
   skin/classic/browser/notification-icons/desktop-notification.svg          (../shared/notification-icons/desktop-notification.svg)
   skin/classic/browser/notification-icons/focus-tab-by-prompt.svg           (../shared/notification-icons/focus-tab-by-prompt.svg)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -52,16 +52,25 @@
 .geo-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/geo.svg);
 }
 
 .geo-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/geo-blocked.svg);
 }
 
+.popup-notification-icon[popupid="autoplay-media"],
+.autoplay-media-icon {
+  list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media.svg);
+}
+
+.autoplay-media-icon.blocked-permission-icon {
+  list-style-image: url(chrome://browser/skin/notification-icons/autoplay-media-blocked.svg);
+}
+
 .popup-notification-icon[popupid="geolocation"] {
   list-style-image: url(chrome://browser/skin/notification-icons/geo-detailed.svg);
 }
 
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .indexedDB-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/indexedDB.svg);
 }
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/autoplay-media-blocked.svg
@@ -0,0 +1,10 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
+    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M9.94970307,6.4383516 C8.40256713,7.13481327 7.27358134,8.59523862 7.04329506,10.3390783 L5.85914977,11.0232511 C5.68193745,11.1255647 5.46360318,11.1255661 5.28638956,11.0232548 C5.10917593,10.9209434 5.0000052,10.7318625 5,10.5272353 L5,4.57046357 C5.00082111,4.36624583 5.11031415,4.17791321 5.28737873,4.0761601 C5.46444331,3.97440698 5.68229154,3.97462749 5.85914977,4.07673884 L9.94970307,6.4383516 Z" fill="#000000" fill-rule="nonzero"></path>
+        <path d="M13.5616811,5.14867232 C12.6193302,2.72101341 10.2606472,1 7.5,1 C3.91014913,1 1,3.91014913 1,7.5 C1,10.8017358 3.46176769,13.528495 6.64994254,13.9449169" stroke="#000000" stroke-width="1.5" stroke-linecap="round"></path>
+        <path d="M12,7 C9.790861,7 8,8.790861 8,11 C8,13.209139 9.790861,15 12,15 C14.209139,15 16,13.209139 16,11 C16,8.790861 14.209139,7 12,7 Z M12,8 C12.6220042,8.00091132 13.2280791,8.19675778 13.733,8.56 L9.56,12.733 C8.90610356,11.8203472 8.81838019,10.6183679 9.33283825,9.62044577 C9.8472963,8.62252365 10.8772778,7.99675388 12,8 Z M12,14 C11.3779958,13.9990887 10.7719209,13.8032422 10.267,13.44 L14.44,9.267 C15.0938964,10.1796528 15.1816198,11.3816321 14.6671618,12.3795542 C14.1527037,13.3774764 13.1227222,14.0032461 12,14 Z" fill="#000000" fill-rule="nonzero"></path>
+    </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/notification-icons/autoplay-media.svg
@@ -0,0 +1,7 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M8 2a6 6 0 1 0 6 6 6.007 6.007 0 0 0-6-6zm0 11a5 5 0 1 1 5-5 5.006 5.006 0 0 1-5 5z"></path>
+  <path d="M6.75 4.969A.5.5 0 0 0 6 5.4v5.2a.5.5 0 0 0 .75.433l4.5-2.6a.5.5 0 0 0 0-.866z"></path>
+</svg>