Bug 1291642 - Part 3 - Update permission notifications to use checkbox in PopupNotifications. r?paolo draft
authorJohann Hofmann <jhofmann@mozilla.com>
Thu, 25 Aug 2016 14:34:07 +0200
changeset 405323 e99ec084a9b2b05d2163668398f439abe0576445
parent 405013 03fd64ac2280d1018e7597481579c59680128ad7
child 405324 1462fd2434c0c07decb1060dcfe283b97c83b6fa
push id27485
push usermail@johann-hofmann.com
push dateThu, 25 Aug 2016 12:37:15 +0000
reviewerspaolo
bugs1291642
milestone51.0a1
Bug 1291642 - Part 3 - Update permission notifications to use checkbox in PopupNotifications. r?paolo MozReview-Commit-ID: HptoY3dSHOj
browser/components/nsBrowserGlue.js
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/webrtcUI.jsm
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2489,180 +2489,161 @@ ContentPermissionPrompt.prototype = {
    * @param aActions               An array of actions of the form:
    *                               [main action, secondary actions, ...]
    *                               Actions are of the form { stringId, action, expireType, callback }
    *                               Permission is granted if action is null or ALLOW_ACTION.
    * @param aNotificationId        The id of the PopupNotification.
    * @param aAnchorId              The id for the PopupNotification anchor.
    * @param aOptions               Options for the PopupNotification
    */
-  _showPrompt: function CPP_showPrompt(aRequest, aMessage, aPermission, aActions,
+  _showPrompt: function CPP_showPrompt(aRequest, aMessage, aPermission, aAllowAction, aDenyAction,
                                        aNotificationId, aAnchorId, aOptions) {
     var browser = this._getBrowserForRequest(aRequest);
     var chromeWin = browser.ownerGlobal;
     var requestPrincipal = aRequest.principal;
 
-    // Transform the prompt actions into PopupNotification actions.
-    var popupNotificationActions = [];
-    for (var i = 0; i < aActions.length; i++) {
-      let promptAction = aActions[i];
-
-      // Don't offer action in PB mode if the action remembers permission for more than a session.
-      if (PrivateBrowsingUtils.isWindowPrivate(chromeWin) &&
-          promptAction.expireType != Ci.nsIPermissionManager.EXPIRE_SESSION &&
-          promptAction.action) {
-        continue;
-      }
-
-      var action = {
-        label: gBrowserBundle.GetStringFromName(promptAction.stringId),
-        accessKey: gBrowserBundle.GetStringFromName(promptAction.stringId + ".accesskey"),
-        callback: function() {
-          if (promptAction.callback) {
-            promptAction.callback();
-          }
-
-          // Remember permissions.
-          if (promptAction.action) {
-            Services.perms.addFromPrincipal(requestPrincipal, aPermission,
-                                            promptAction.action, promptAction.expireType);
-          }
-
-          // Grant permission if action is null or ALLOW_ACTION.
-          if (!promptAction.action || promptAction.action == Ci.nsIPermissionManager.ALLOW_ACTION) {
-            aRequest.allow();
-          } else {
-            aRequest.cancel();
-          }
-        },
-      };
-
-      popupNotificationActions.push(action);
-    }
-
-    var mainAction = popupNotificationActions.length ?
-                       popupNotificationActions[0] : null;
-    var secondaryActions = popupNotificationActions.splice(1);
+    let mainAction = {
+      label: gBrowserBundle.GetStringFromName(aAllowAction.stringId),
+      accessKey: gBrowserBundle.GetStringFromName(aAllowAction.stringId + ".accesskey"),
+      callback: function(remember) {
+        if (aAllowAction.callback) {
+          aAllowAction.callback();
+        }
+
+        // Remember permissions.
+        if (remember) {
+          Services.perms.addFromPrincipal(requestPrincipal, aPermission,
+                                          Ci.nsIPermissionManager.ALLOW_ACTION, aAllowAction.expireType);
+        }
+
+        aRequest.allow();
+      },
+    };
+
+    let secondaryAction = {
+      label: gBrowserBundle.GetStringFromName(aDenyAction.stringId),
+      accessKey: gBrowserBundle.GetStringFromName(aDenyAction.stringId + ".accesskey"),
+      callback: function(remember) {
+        if (aDenyAction.callback) {
+          aDenyAction.callback();
+        }
+
+        // Remember permissions.
+        if (remember) {
+          Services.perms.addFromPrincipal(requestPrincipal, aPermission,
+                                          Ci.nsIPermissionManager.DENY_ACTION, aDenyAction.expireType);
+        }
+
+        aRequest.cancel();
+      },
+    };
 
     // Only allow exactly one permission request here.
     let types = aRequest.types.QueryInterface(Ci.nsIArray);
     if (types.length != 1) {
       aRequest.cancel();
       return undefined;
     }
 
     if (!aOptions)
       aOptions = {};
     aOptions.displayURI = requestPrincipal.URI;
     aOptions.persistent = true;
 
     return chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId,
-                                             mainAction, secondaryActions, aOptions);
+                                             mainAction, [secondaryAction], aOptions);
   },
 
   _promptGeo : function(aRequest) {
     var secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
-
-    var message;
-
-    // Share location action.
-    var actions = [{
-      stringId: "geolocation.shareLocation",
-      action: null,
-      expireType: null,
-      callback: function() {
-        secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION);
+    let browser = this._getBrowserForRequest(aRequest);
+    let chromeWin = browser.ownerGlobal;
+
+    let allowAction = {
+      stringId: "geolocation.allow",
+      callback: function(remember) {
+        if (remember) {
+          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE);
+        } else {
+          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION);
+        }
       },
-    }];
+    };
+
+    let denyAction = {
+      stringId: "geolocation.dontAllow",
+      callback: function(remember) {
+        if (remember) {
+          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE);
+        }
+      },
+    };
 
     let options = {
       learnMoreURL: Services.urlFormatter.formatURLPref("browser.geolocation.warning.infoURL"),
     };
 
+    let message;
+
     if (aRequest.principal.URI.schemeIs("file")) {
       message = gBrowserBundle.GetStringFromName("geolocation.shareWithFile2");
+      options.checkbox = { show: false };
     } else {
       message = gBrowserBundle.GetStringFromName("geolocation.shareWithSite2");
-      // Always share location action.
-      actions.push({
-        stringId: "geolocation.alwaysShareLocation",
-        action: Ci.nsIPermissionManager.ALLOW_ACTION,
-        expireType: null,
-        callback: function() {
-          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE);
-        },
-      });
-
-      // Never share location action.
-      actions.push({
-        stringId: "geolocation.neverShareLocation",
-        action: Ci.nsIPermissionManager.DENY_ACTION,
-        expireType: null,
-        callback: function() {
-          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE);
-        },
-      });
+
+      // Don't offer "always remember" action in PB mode
+      options.checkbox = {
+        show: !PrivateBrowsingUtils.isWindowPrivate(chromeWin)
+      };
     }
 
     secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST);
 
-    this._showPrompt(aRequest, message, "geo", actions, "geolocation",
+    this._showPrompt(aRequest, message, "geo", allowAction, denyAction, "geolocation",
                      "geo-notification-icon", options);
   },
 
   _promptWebNotifications : function(aRequest) {
+    let browser = this._getBrowserForRequest(aRequest);
+    let chromeWin = browser.ownerGlobal;
     var message = gBrowserBundle.GetStringFromName("webNotifications.receiveFromSite");
 
-    var actions;
-
-    var browser = this._getBrowserForRequest(aRequest);
-    // Only show "allow for session" in PB mode, we don't
-    // support "allow for session" in non-PB mode.
-    if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
-      actions = [
-        {
-          stringId: "webNotifications.receiveForSession",
-          action: Ci.nsIPermissionManager.ALLOW_ACTION,
-          expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
-          callback: function() {},
-        }
-      ];
-    } else {
-      actions = [
-        {
-          stringId: "webNotifications.alwaysReceive",
-          action: Ci.nsIPermissionManager.ALLOW_ACTION,
-          expireType: null,
-          callback: function() {},
-        },
-        {
-          stringId: "webNotifications.neverShow",
-          action: Ci.nsIPermissionManager.DENY_ACTION,
-          expireType: null,
-          callback: function() {},
-        },
-      ];
-    }
-
-    var options = {
+    let allowAction = { stringId: "webNotifications.allow" };
+
+    let denyAction = { stringId: "webNotifications.dontAllow" };
+
+    let options = {
+      checkbox: {
+        show: true,
+        checked: true
+      },
       learnMoreURL:
         Services.urlFormatter.formatURLPref("app.support.baseURL") + "push",
       eventCallback(type) {
         if (type == "dismissed") {
           // Bug 1259148: Hide the doorhanger icon. Unlike other permission
           // doorhangers, the user can't restore the doorhanger using the icon
           // in the location bar. Instead, the site will be notified that the
           // doorhanger was dismissed.
           this.remove();
           aRequest.cancel();
         }
       },
     };
 
-    this._showPrompt(aRequest, message, "desktop-notification", actions,
+    // In PB mode, the "always remember" checkbox should only remember for the session
+    if (PrivateBrowsingUtils.isWindowPrivate(chromeWin)) {
+      allowAction.expireType = Ci.nsIPermissionManager.EXPIRE_SESSION;
+      denyAction.expireType = Ci.nsIPermissionManager.EXPIRE_SESSION;
+
+      options.checkbox.label =
+        gBrowserBundle.GetStringFromName("webNotifications.rememberForSession");
+    }
+
+    this._showPrompt(aRequest, message, "desktop-notification", allowAction, denyAction,
                      "web-notifications",
                      "web-notifications-notification-icon", options);
   },
 
   prompt: function CPP_prompt(request) {
     // Only allow exactly one permission request here.
     let types = request.types.QueryInterface(Ci.nsIArray);
     if (types.length != 1) {
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -366,31 +366,28 @@ pu.notifyButton.accesskey=D
 puNotifyText=%S has been updated
 puAlertTitle=%S Updated
 puAlertText=Click here for details
 
 # Geolocation UI
 
 # LOCALIZATION NOTE (geolocation.shareLocation geolocation.alwaysShareLocation geolocation.neverShareLocation):
 # If you're having trouble with the word Share, please use Allow and Block in your language.
-geolocation.shareLocation=Share Location
-geolocation.shareLocation.accesskey=a
-geolocation.alwaysShareLocation=Always Share Location
-geolocation.alwaysShareLocation.accesskey=A
-geolocation.neverShareLocation=Never Share Location
-geolocation.neverShareLocation.accesskey=N
+geolocation.allow=Allow location access
+geolocation.allow.accesskey=A
+geolocation.dontAllow=Don’t allow
+geolocation.dontAllow.accesskey=n
 geolocation.shareWithSite2=Would you like to share your location with this site?
 geolocation.shareWithFile2=Would you like to share your location with this file?
 
-webNotifications.receiveForSession=Receive for this session
-webNotifications.receiveForSession.accesskey=s
-webNotifications.alwaysReceive=Always Receive Notifications
-webNotifications.alwaysReceive.accesskey=A
-webNotifications.neverShow=Always Block Notifications
-webNotifications.neverShow.accesskey=N
+webNotifications.rememberForSession=Remember decision for this session
+webNotifications.allow=Allow notifications
+webNotifications.allow.accesskey=A
+webNotifications.dontAllow=Don’t allow
+webNotifications.dontAllow.accesskey=n
 webNotifications.receiveFromSite=Would you like to receive notifications from this site?
 # LOCALIZATION NOTE (webNotifications.upgradeTitle): When using native notifications on OS X, the title may be truncated around 32 characters.
 webNotifications.upgradeTitle=Upgraded notifications
 # LOCALIZATION NOTE (webNotifications.upgradeBody): When using native notifications on OS X, the body may be truncated around 100 characters in some views.
 webNotifications.upgradeBody=You can now receive notifications from sites that are not currently loaded. Click to learn more.
 
 # Phishing/Malware Notification Bar.
 # LOCALIZATION NOTE (notADeceptiveSite, notAnAttack)
@@ -518,16 +515,20 @@ getUserMedia.shareApplication.label = Sh
 getUserMedia.shareWindow.label = Share Selected Window
 getUserMedia.shareSelectedItems.label = Share Selected Items
 getUserMedia.always.label = Always Share
 getUserMedia.always.accesskey = A
 getUserMedia.denyRequest.label = Don’t Share
 getUserMedia.denyRequest.accesskey = D
 getUserMedia.never.label = Never Share
 getUserMedia.never.accesskey = N
+# LOCALIZATION NOTE (getUserMedia.dontAllowHTTP, getUserMedia.dontAllowHTTPAudio, getUserMedia.dontAllowHTTPVideo) - %S is brandShortName
+getUserMedia.dontAllowHTTP=Your connection to this site is not secure. %S can not allow permanent access to your devices.
+getUserMedia.dontAllowHTTPAudio=Your connection to this site is not secure. %S can not allow permanent access to your microphone.
+getUserMedia.dontAllowHTTPVideo=Your connection to this site is not secure. %S can not allow permanent access to your camera.
 
 getUserMedia.sharingMenu.label = Tabs sharing devices
 getUserMedia.sharingMenu.accesskey = d
 # LOCALIZATION NOTE (getUserMedia.sharingMenuCamera
 #                    getUserMedia.sharingMenuMicrophone,
 #                    getUserMedia.sharingMenuAudioCapture,
 #                    getUserMedia.sharingMenuApplication,
 #                    getUserMedia.sharingMenuScreen,
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -13,16 +13,20 @@ const Ci = Components.interfaces;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
+  return Services.strings.createBundle('chrome://branding/locale/brand.properties');
+});
+
 this.webrtcUI = {
   init: function () {
     Services.obs.addObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished", false);
 
     let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                  .getService(Ci.nsIMessageBroadcaster);
     ppmm.addMessageListener("webrtc:UpdatingIndicators", this);
     ppmm.addMessageListener("webrtc:UpdateGlobalIndicators", this);
@@ -312,53 +316,46 @@ function prompt(aBrowser, aRequest) {
     // reject the action.
     callback: function() {}
   };
 
   let secondaryActions = [
     {
       label: stringBundle.getString("getUserMedia.denyRequest.label"),
       accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
-      callback: function () {
+      callback: function (aRemember) {
         denyRequest(notification.browser, aRequest);
+        if (aRemember) {
+          let perms = Services.perms;
+          if (audioDevices.length)
+            perms.add(uri, "microphone", perms.DENY_ACTION);
+          if (videoDevices.length)
+            perms.add(uri, "camera", perms.DENY_ACTION);
+        }
       }
     }
   ];
-  // Bug 1037438: implement 'never' for screen sharing.
-  if (!sharingScreen && !sharingAudio) {
-    secondaryActions.push({
-      label: stringBundle.getString("getUserMedia.never.label"),
-      accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
-      callback: function () {
-        denyRequest(notification.browser, aRequest);
-        // Let someone save "Never" for http sites so that they can be stopped from
-        // bothering you with doorhangers.
-        let perms = Services.perms;
-        if (audioDevices.length)
-          perms.add(uri, "microphone", perms.DENY_ACTION);
-        if (videoDevices.length)
-          perms.add(uri, "camera", perms.DENY_ACTION);
-      }
-    });
-  }
 
-  if (aRequest.secure && !sharingScreen && !sharingAudio) {
-    // Don't show the 'Always' action if the connection isn't secure, or for
-    // screen/audio sharing (because we can't guess which window the user wants
-    // to share without prompting).
-    secondaryActions.unshift({
-      label: stringBundle.getString("getUserMedia.always.label"),
-      accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
-      callback: function () {
-        mainAction.callback(true);
-      }
-    });
+  let productName = gBrandBundle.GetStringFromName("brandShortName");
+  let disabledInfo = stringBundle.getFormattedString("getUserMedia.dontAllowHTTP", [productName]);
+  if (audioDevices.length && !videoDevices.length) {
+    disabledInfo = stringBundle.getFormattedString("getUserMedia.dontAllowHTTPAudio", [productName]);
+  } else if (!audioDevices.length && videoDevices.length) {
+    disabledInfo = stringBundle.getFormattedString("getUserMedia.dontAllowHTTPVideo", [productName]);
   }
 
   let options = {
+    // Don't show the 'Always Remember' checkbox if the connection isn't secure, or for
+    // screen/audio sharing (because we can't guess which window the user wants
+    // to share without prompting).
+    checkbox: {
+      show: !sharingScreen && !sharingAudio,
+      disableMainAction: !aRequest.secure,
+      disabledInfo: disabledInfo
+    },
     persistent: true,
     eventCallback: function(aTopic, aNewBrowser) {
       if (aTopic == "swapping")
         return true;
 
       let chromeDoc = this.browser.ownerDocument;
 
       if (aTopic == "shown") {