Bug 1369482 - Make hostnames in permission prompts much more prominent. r=johannh
authorPrathiksha <prathikshaprasadsuman@gmail.com>
Thu, 14 Dec 2017 21:00:28 +0530
changeset 453746 b2c2c5e26ddc71fbd4390b5ba2661737c0dee653
parent 453745 55b7690ea8c554e12d11ea015c90a0e2bd115389
child 453747 b2588a5584c200b9dac8a6a0b0500f043cd335fe
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1369482
milestone59.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 1369482 - Make hostnames in permission prompts much more prominent. r=johannh MozReview-Commit-ID: 89m2QVzj8DG
browser/base/content/test/general/browser_bug553455.js
browser/base/content/test/popupNotifications/head.js
browser/modules/PermissionUI.jsm
browser/modules/webrtcUI.jsm
toolkit/content/widgets/notification.xml
toolkit/modules/PopupNotifications.jsm
--- a/browser/base/content/test/general/browser_bug553455.js
+++ b/browser/base/content/test/general/browser_bug553455.js
@@ -239,17 +239,17 @@ async function test_disabledInstall() {
   let triggers = encodeURIComponent(JSON.stringify({
     "XPI": "amosigned.xpi"
   }));
   BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
   let panel = await notificationPromise;
 
   let notification = panel.childNodes[0];
   is(notification.button.label, "Enable", "Should have seen the right button");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "Software installation is currently disabled. Click Enable and try again.");
 
   let closePromise = waitForNotificationClose();
   // Click on Enable
   EventUtils.synthesizeMouseAtCenter(notification.button, {});
   await closePromise;
 
   try {
@@ -270,17 +270,17 @@ async function test_blockedInstall() {
   }));
   BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
   let panel = await notificationPromise;
 
   let notification = panel.childNodes[0];
   is(notification.button.label, "Allow", "Should have seen the right button");
   is(notification.getAttribute("origin"), "example.com",
      "Should have seen the right origin host");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      gApp + " prevented this site from asking you to install software on your computer.",
      "Should have seen the right message");
 
   let dialogPromise = waitForInstallDialog();
   // Click on Allow
   EventUtils.synthesizeMouse(notification.button, 20, 10, {});
   // Notification should have changed to progress notification
   ok(PopupNotifications.isPanelOpen, "Notification should still be open");
@@ -289,17 +289,17 @@ async function test_blockedInstall() {
   let installDialog = await dialogPromise;
 
   notificationPromise = waitForNotification("addon-install-restart");
   acceptInstallDialog(installDialog);
   panel = await notificationPromise;
 
   notification = panel.childNodes[0];
   is(notification.button.label, "Restart Now", "Should have seen the right button");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "XPI Test will be installed after you restart " + gApp + ".",
      "Should have seen the right message");
 
   let installs = await getInstalls();
   is(installs.length, 1, "Should be one pending install");
   installs[0].cancel();
   await removeTab();
 },
@@ -326,17 +326,17 @@ async function test_whitelistedInstall()
      "tab selected in response to the addon-install-confirmation notification");
 
   let notificationPromise = waitForNotification("addon-install-restart");
   acceptInstallDialog(installDialog);
   let panel = await notificationPromise;
 
   let notification = panel.childNodes[0];
   is(notification.button.label, "Restart Now", "Should have seen the right button");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "XPI Test will be installed after you restart " + gApp + ".",
      "Should have seen the right message");
 
   let installs = await getInstalls();
   is(installs.length, 1, "Should be one pending install");
   installs[0].cancel();
 
   Services.perms.remove(makeURI("http://example.com/"), "install");
@@ -352,17 +352,17 @@ async function test_failedDownload() {
   let triggers = encodeURIComponent(JSON.stringify({
     "XPI": "missing.xpi"
   }));
   BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
   await progressPromise;
   let panel = await failPromise;
 
   let notification = panel.childNodes[0];
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "The add-on could not be downloaded because of a connection failure.",
      "Should have seen the right message");
 
   Services.perms.remove(makeURI("http://example.com/"), "install");
   await removeTab();
 },
 
 async function test_corruptFile() {
@@ -374,17 +374,17 @@ async function test_corruptFile() {
   let triggers = encodeURIComponent(JSON.stringify({
     "XPI": "corrupt.xpi"
   }));
   BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
   await progressPromise;
   let panel = await failPromise;
 
   let notification = panel.childNodes[0];
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "The add-on downloaded from this site could not be installed " +
      "because it appears to be corrupt.",
      "Should have seen the right message");
 
   Services.perms.remove(makeURI("http://example.com/"), "install");
   await removeTab();
 },
 
@@ -397,17 +397,17 @@ async function test_incompatible() {
   let triggers = encodeURIComponent(JSON.stringify({
     "XPI": "incompatible.xpi"
   }));
   BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
   await progressPromise;
   let panel = await failPromise;
 
   let notification = panel.childNodes[0];
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "XPI Test could not be installed because it is not compatible with " +
      gApp + " " + gVersion + ".",
      "Should have seen the right message");
 
   Services.perms.remove(makeURI("http://example.com/"), "install");
   await removeTab();
 },
 
@@ -536,17 +536,17 @@ async function test_allUnverified() {
   let triggers = encodeURIComponent(JSON.stringify({
     "Extension XPI": "restartless-unsigned.xpi"
   }));
   BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
   await progressPromise;
   let installDialog = await dialogPromise;
 
   let notification = document.getElementById("addon-install-confirmation-notification");
-  let message = notification.getAttribute("label");
+  let message = notification.getAttribute("startlabel");
   is(message, "Caution: This site would like to install an unverified add-on in " + gApp + ". Proceed at your own risk.");
 
   let container = document.getElementById("addon-install-confirmation-content");
   is(container.childNodes.length, 1, "Should be one item listed");
   is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
   is(container.childNodes[0].childNodes.length, 1, "Shouldn't have the unverified marker");
 
   let notificationPromise = waitForNotification("addon-installed");
@@ -574,17 +574,17 @@ async function test_url() {
   let installDialog = await dialogPromise;
 
   let notificationPromise = waitForNotification("addon-install-restart");
   acceptInstallDialog(installDialog);
   let panel = await notificationPromise;
 
   let notification = panel.childNodes[0];
   is(notification.button.label, "Restart Now", "Should have seen the right button");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "XPI Test will be installed after you restart " + gApp + ".",
      "Should have seen the right message");
 
   let installs = await getInstalls();
   is(installs.length, 1, "Should be one pending install");
   installs[0].cancel();
 
   await removeTab();
@@ -611,17 +611,17 @@ async function test_localFile() {
   gBrowser.loadURI(path);
   await failPromise;
 
   // Wait for the browser code to add the failure notification
   await waitForSingleNotification();
 
   let notification = PopupNotifications.panel.childNodes[0];
   is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "This add-on could not be installed because it appears to be corrupt.",
      "Should have seen the right message");
 
   await removeTab();
 },
 
 async function test_tabClose() {
   if (!Services.prefs.getBoolPref("xpinstall.customConfirmationUI", false)) {
@@ -700,17 +700,17 @@ async function test_urlBar() {
   let installDialog = await dialogPromise;
 
   let notificationPromise = waitForNotification("addon-install-restart");
   acceptInstallDialog(installDialog);
   let panel = await notificationPromise;
 
   let notification = panel.childNodes[0];
   is(notification.button.label, "Restart Now", "Should have seen the right button");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "XPI Test will be installed after you restart " + gApp + ".",
      "Should have seen the right message");
 
   let installs = await getInstalls();
   is(installs.length, 1, "Should be one pending install");
   installs[0].cancel();
 
   await removeTab();
@@ -726,17 +726,17 @@ async function test_wrongHost() {
 
   let progressPromise = waitForProgressNotification();
   let notificationPromise = waitForNotification("addon-install-failed");
   gBrowser.loadURI(TESTROOT + "corrupt.xpi");
   await progressPromise;
   let panel = await notificationPromise;
 
   let notification = panel.childNodes[0];
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "The add-on downloaded from this site could not be installed " +
      "because it appears to be corrupt.",
      "Should have seen the right message");
 
   await removeTab();
 },
 
 async function test_reload() {
@@ -753,17 +753,17 @@ async function test_reload() {
   let installDialog = await dialogPromise;
 
   let notificationPromise = waitForNotification("addon-install-restart");
   acceptInstallDialog(installDialog);
   let panel = await notificationPromise;
 
   let notification = panel.childNodes[0];
   is(notification.button.label, "Restart Now", "Should have seen the right button");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "XPI Test will be installed after you restart " + gApp + ".",
      "Should have seen the right message");
 
   function testFail() {
     ok(false, "Reloading should not have hidden the notification");
   }
   PopupNotifications.panel.addEventListener("popuphiding", testFail);
   let requestedUrl = TESTROOT2 + "enabled.html";
@@ -794,17 +794,17 @@ async function test_theme() {
   let installDialog = await dialogPromise;
 
   let notificationPromise = waitForNotification("addon-install-restart");
   acceptInstallDialog(installDialog);
   let panel = await notificationPromise;
 
   let notification = panel.childNodes[0];
   is(notification.button.label, "Restart Now", "Should have seen the right button");
-  is(notification.getAttribute("label"),
+  is(notification.getAttribute("startlabel"),
      "Theme Test will be installed after you restart " + gApp + ".",
      "Should have seen the right message");
 
   let addon = await new Promise(resolve => {
     AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", function(result) {
       resolve(result);
     });
   });
--- a/browser/base/content/test/popupNotifications/head.js
+++ b/browser/base/content/test/popupNotifications/head.js
@@ -198,17 +198,25 @@ function checkPopup(popup, notifyObj) {
     return;
   let icon = document.getAnonymousElementByAttribute(notification, "class",
                                                      "popup-notification-icon");
   if (notifyObj.id == "geolocation") {
     isnot(icon.boxObject.width, 0, "icon for geo displayed");
     ok(popup.anchorNode.classList.contains("notification-anchor-icon"),
        "notification anchored to icon");
   }
-  is(notification.getAttribute("label"), notifyObj.message, "message matches");
+
+  if (typeof notifyObj.message == "string") {
+    is(notification.getAttribute("startlabel"), notifyObj.message, "message matches");
+  } else {
+    is(notification.getAttribute("startlabel"), notifyObj.message.start, "message matches");
+    is(notification.getAttribute("hostname"), notifyObj.message.host, "message matches");
+    is(notification.getAttribute("endlabel"), notifyObj.message.end, "message matches");
+  }
+
   is(notification.id, notifyObj.id + "-notification", "id matches");
   if (notifyObj.mainAction) {
     is(notification.getAttribute("buttonlabel"), notifyObj.mainAction.label,
        "main action label matches");
     is(notification.getAttribute("buttonaccesskey"),
        notifyObj.mainAction.accessKey, "main action accesskey matches");
     is(notification.getAttribute("buttonhighlight"),
        (!notifyObj.mainAction.disableHighlight).toString(),
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -456,26 +456,30 @@ GeolocationPermissionPrompt.prototype = 
     return "geolocation";
   },
 
   get anchorID() {
     return "geo-notification-icon";
   },
 
   get message() {
-    let message;
+    let message = {};
     if (this.principal.URI.schemeIs("file")) {
-      message = gBrowserBundle.GetStringFromName("geolocation.shareWithFile3");
+      message.start = gBrowserBundle.GetStringFromName("geolocation.shareWithFile3");
     } else {
-      let hostPort = "<>";
+      let header = gBrowserBundle.formatStringFromName("geolocation.shareWithSite3",
+                                                    ["<>"], 1);
+      header = header.split("<>");
+      message.end = header[1];
+      message.start = header[0];
+
+      message.host = "<>";
       try {
-        hostPort = this.principal.URI.hostPort;
+        message.host = this.principal.URI.hostPort;
       } catch (ex) { }
-      message = gBrowserBundle.formatStringFromName("geolocation.shareWithSite3",
-                                                    [hostPort], 1);
     }
     return message;
   },
 
   get promptActions() {
     // We collect Telemetry data on Geolocation prompts and how users
     // respond to them. The probe keys are a bit verbose, so let's alias them.
     const SHARE_LOCATION =
@@ -554,22 +558,30 @@ DesktopNotificationPermissionPrompt.prot
     return "web-notifications";
   },
 
   get anchorID() {
     return "web-notifications-notification-icon";
   },
 
   get message() {
-    let hostPort = "<>";
+    let message = {};
+
+    message.host = "<>";
     try {
-      hostPort = this.principal.URI.hostPort;
+      message.host = this.principal.URI.hostPort;
     } catch (ex) { }
-    return gBrowserBundle.formatStringFromName("webNotifications.receiveFromSite2",
-                                               [hostPort], 1);
+
+    let header = gBrowserBundle.formatStringFromName("webNotifications.receiveFromSite2",
+                                                    ["<>"], 1);
+    header = header.split("<>");
+    message.end = header[1];
+    message.start = header[0];
+
+    return message;
   },
 
   get promptActions() {
     let actions = [
       {
         label: gBrowserBundle.GetStringFromName("webNotifications.allow"),
         accessKey:
           gBrowserBundle.GetStringFromName("webNotifications.allow.accesskey"),
@@ -625,35 +637,44 @@ PersistentStoragePermissionPrompt.protot
     if (checkbox.show) {
       checkbox.checked = true;
       checkbox.label = gBrowserBundle.GetStringFromName("persistentStorage.remember");
     }
     let learnMoreURL =
       Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
     return {
       checkbox,
-      learnMoreURL
+      learnMoreURL,
+      displayURI: false
     };
   },
 
   get notificationID() {
     return "persistent-storage";
   },
 
   get anchorID() {
     return "persistent-storage-notification-icon";
   },
 
   get message() {
-    let hostPort = "<>";
+    let message = {};
+
+    message.host = "<>";
     try {
-      hostPort = this.principal.URI.hostPort;
+      message.host = this.principal.URI.hostPort;
     } catch (ex) {}
-    return gBrowserBundle.formatStringFromName(
-      "persistentStorage.allowWithSite", [hostPort], 1);
+
+    let header = gBrowserBundle.formatStringFromName("persistentStorage.allowWithSite",
+                                                    ["<>"], 1);
+    header = header.split("<>");
+    message.end = header[1];
+    message.start = header[0];
+
+    return message;
   },
 
   get promptActions() {
     return [
       {
         label: gBrowserBundle.GetStringFromName("persistentStorage.allow"),
         accessKey:
           gBrowserBundle.GetStringFromName("persistentStorage.allow.accesskey"),
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -370,17 +370,18 @@ function prompt(aBrowser, aRequest) {
 
   let uri;
   try {
     // This fails for principals that serialize to "null", e.g. file URIs.
     uri = Services.io.newURI(aRequest.origin);
   } catch (e) {
     uri = Services.io.newURI(aRequest.documentURI);
   }
-  let host = getHost(uri);
+  let message = {};
+  message.host = getHost(uri);
   let chromeDoc = aBrowser.ownerDocument;
   let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
 
   // Mind the order, because for simplicity we're iterating over the list using
   // "includes()". This allows the rotation of string identifiers. We list the
   // full identifiers here so they can be cross-referenced more easily.
   let joinedRequestTypes = requestTypes.join("And");
   let stringId = [
@@ -391,17 +392,20 @@ function prompt(aBrowser, aRequest) {
     "getUserMedia.shareAudioCapture2.message",
     // Combinations of the above request types last.
     "getUserMedia.shareCameraAndMicrophone2.message",
     "getUserMedia.shareCameraAndAudioCapture2.message",
     "getUserMedia.shareScreenAndMicrophone3.message",
     "getUserMedia.shareScreenAndAudioCapture3.message",
   ].find(id => id.includes(joinedRequestTypes));
 
-  let message = stringBundle.getFormattedString(stringId, [host]);
+  let header = stringBundle.getFormattedString(stringId, ["<>"], 1);
+  header = header.split("<>");
+  message.end = header[1];
+  message.start = header[0];
 
   let notification; // Used by action callbacks.
   let mainAction = {
     label: stringBundle.getString("getUserMedia.allow.label"),
     accessKey: stringBundle.getString("getUserMedia.allow.accesskey"),
     // The real callback will be set during the "showing" event. The
     // empty function here is so that PopupNotifications.show doesn't
     // reject the action.
--- a/toolkit/content/widgets/notification.xml
+++ b/toolkit/content/widgets/notification.xml
@@ -7,17 +7,18 @@
 <!DOCTYPE bindings [
 <!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
 %notificationDTD;
 ]>
 
 <bindings id="notificationBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xbl="http://www.mozilla.org/xbl"
-          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+          xmlns:html = "http://www.w3.org/1999/xhtml">
 
   <binding id="notificationbox">
     <content>
       <xul:stack xbl:inherits="hidden=notificationshidden"
                  class="notificationbox-stack">
         <xul:spacer/>
         <children includes="notification"/>
       </xul:stack>
@@ -506,18 +507,21 @@
                    xbl:inherits="popupid,src=icon,class=iconclass"/>
         <xul:vbox flex="1" pack="start"
                   class="popup-notification-body" xbl:inherits="popupid">
           <xul:hbox align="start">
             <xul:vbox flex="1">
               <xul:label class="popup-notification-origin header"
                          xbl:inherits="value=origin,tooltiptext=origin"
                          crop="center"/>
-              <xul:description class="popup-notification-description"
-                               xbl:inherits="xbl:text=label,popupid"/>
+              <xul:description>
+                <html:span xbl:inherits="xbl:text=startlabel,popupid"/>
+                <html:b    xbl:inherits="xbl:text=hostname,popupid"/>
+                <html:span xbl:inherits="xbl:text=endlabel,popupid"/>
+              </xul:description>
             </xul:vbox>
             <xul:toolbarbutton anonid="closebutton"
                                class="messageCloseButton close-icon popup-notification-closebutton tabbable"
                                xbl:inherits="oncommand=closebuttoncommand,hidden=closebuttonhidden"
                                tooltiptext="&closeNotification.tooltip;"/>
           </xul:hbox>
           <children includes="popupnotificationcontent"/>
           <xul:label class="text-link popup-notification-learnmore-link"
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -326,17 +326,23 @@ PopupNotifications.prototype = {
    *        The <xul:browser> element associated with the notification. Must not
    *        be null.
    * @param id
    *        A unique ID that identifies the type of notification (e.g.
    *        "geolocation"). Only one notification with a given ID can be visible
    *        at a time. If a notification already exists with the given ID, it
    *        will be replaced.
    * @param message
-   *        The text to be displayed in the notification.
+   *        A JavaScript object or a string containing the text to be displayed in the
+   *        notification header. It must have the following properties:
+   *          - start(string): First part of the notification header text. Optionally,
+   *            it is also the entire header text when the notification header does not
+   *            contain a host name. eg. file URIs.
+   *          - host(string): Hostname of the site displaying the notifiation.
+   *          - end(string): An optional end label to the notification header text.
    * @param anchorID
    *        The ID of the element that should be used as this notification
    *        popup's anchor. May be null, in which case the notification will be
    *        anchored to the iconBox.
    * @param mainAction
    *        A JavaScript object literal describing the notification button's
    *        action. If present, it must have the following properties:
    *          - label (string): the button's label.
@@ -762,17 +768,33 @@ PopupNotifications.prototype = {
       // If the chrome document provides a popupnotification with this id, use
       // that. Otherwise create it ad-hoc.
       let popupnotification = doc.getElementById(popupnotificationID);
       if (popupnotification)
         gNotificationParents.set(popupnotification, popupnotification.parentNode);
       else
         popupnotification = doc.createElementNS(XUL_NS, "popupnotification");
 
-      popupnotification.setAttribute("label", n.message);
+      // Create the notification description element.
+
+      // Adding an if condition to check if n.message(i.e. the notification-description-text) is a string or an object.
+      if (typeof n.message == "string") {
+        popupnotification.setAttribute("startlabel", n.message);
+      } else {
+        if (n.message.start) {
+          popupnotification.setAttribute("startlabel", n.message.start);
+        }
+        if (n.message.host) {
+          popupnotification.setAttribute("hostname", n.message.host);
+        }
+        if (n.message.end) {
+          popupnotification.setAttribute("endlabel", n.message.end);
+        }
+      }
+
       popupnotification.setAttribute("id", popupnotificationID);
       popupnotification.setAttribute("popupid", n.id);
       popupnotification.setAttribute("oncommand", "PopupNotifications._onCommand(event);");
       if (Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton")) {
         popupnotification.setAttribute("closebuttoncommand", "PopupNotifications._onButtonEvent(event, 'secondarybuttoncommand');");
       } else {
         popupnotification.setAttribute("closebuttoncommand", `PopupNotifications._dismiss(event, ${TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON});`);
       }