Bug 1431242 - Use popupnotification's built-in bolding for webextension prompts. r?johannh,aswan draft
authorPrathiksha <prathikshaprasadsuman@gmail.com>
Fri, 19 Jan 2018 23:34:25 +0530
changeset 754776 eceda888b8eac6c53ee4b76a3e317501ad465a06
parent 754399 38b3c1d03a594664c6b32c35533734283c258f43
push id98993
push userbmo:prathikshaprasadsuman@gmail.com
push dateWed, 14 Feb 2018 08:29:19 +0000
reviewersjohannh, aswan
bugs1431242
milestone60.0a1
Bug 1431242 - Use popupnotification's built-in bolding for webextension prompts. r?johannh,aswan MozReview-Commit-ID: 52AtWUlJj4p
browser/base/content/browser-addons.js
browser/base/content/popup-notifications.inc
browser/modules/ExtensionsUI.jsm
browser/themes/linux/browser.css
browser/themes/osx/browser.css
browser/themes/windows/browser.css
toolkit/components/extensions/Extension.jsm
toolkit/modules/PopupNotifications.jsm
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -640,17 +640,18 @@ var LightWeightThemeWebInstaller = {
     }
 
     if (this._isAllowed(baseURI)) {
       this._install(data, notify);
       return;
     }
 
     let strings = {
-      header: gNavigatorBundle.getFormattedString("webextPerms.header", [data.name]),
+      header: gNavigatorBundle.getFormattedString("webextPerms.header", ["<>"]),
+      addonName: data.name,
       text: gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message2",
                                                 [uri.host]),
       acceptText: gNavigatorBundle.getString("lwthemeInstallRequest.allowButton2"),
       acceptKey: gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey2"),
       cancelText: gNavigatorBundle.getString("webextPerms.cancel.label"),
       cancelKey: gNavigatorBundle.getString("webextPerms.cancel.accessKey"),
       msgs: []
     };
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -69,27 +69,19 @@
     </popupnotification>
 
     <popupnotification id="addon-install-confirmation-notification" hidden="true">
       <popupnotificationcontent id="addon-install-confirmation-content" orient="vertical"/>
     </popupnotification>
 
     <popupnotification id="addon-webext-permissions-notification" hidden="true">
       <popupnotificationcontent class="addon-webext-perm-notification-content" orient="vertical">
-        <description id="addon-webext-perm-header" class="addon-webext-perm-header"/>
         <description id="addon-webext-perm-text" class="addon-webext-perm-text"/>
         <label id="addon-webext-perm-intro" class="addon-webext-perm-text"/>
         <html:ul id="addon-webext-perm-list" class="addon-webext-perm-list"/>
       </popupnotificationcontent>
     </popupnotification>
 
-    <popupnotification id="addon-webext-defaultsearch-notification" hidden="true">
-      <popupnotificationcontent class="addon-webext-defaultsearch-notification-content" orient="vertical">
-        <description id="addon-webext-defaultsearch-text" class="addon-webext-perm-header"/>
-      </popupnotificationcontent>
-    </popupnotification>
-
     <popupnotification id="addon-installed-notification" hidden="true">
       <popupnotificationcontent class="addon-installed-notification-content" orient="vertical">
-        <description id="addon-installed-notification-header"/>
         <description id="addon-installed-notification-message"/>
       </popupnotificationcontent>
     </popupnotification>
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -242,57 +242,54 @@ this.ExtensionsUI = {
 
       let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
 
       let strings = {};
       strings.acceptText = bundle.GetStringFromName("webext.defaultSearchYes.label");
       strings.acceptKey = bundle.GetStringFromName("webext.defaultSearchYes.accessKey");
       strings.cancelText = bundle.GetStringFromName("webext.defaultSearchNo.label");
       strings.cancelKey = bundle.GetStringFromName("webext.defaultSearchNo.accessKey");
-      let addonName = `<span class="addon-webext-name">${this._sanitizeName(name)}</span>`;
+      strings.addonName = name;
       strings.text = bundle.formatStringFromName("webext.defaultSearch.description",
-                                               [addonName, currentEngine, newEngine], 3);
+                                                 ["<>", currentEngine, newEngine], 3);
       resolve(this.showDefaultSearchPrompt(browser, strings, icon));
     }
   },
 
-  // Escape &, <, and > characters in a string so that it may be
-  // injected as part of raw markup.
-  _sanitizeName(name) {
-    return name.replace(/&/g, "&amp;")
-               .replace(/</g, "&lt;")
-               .replace(/>/g, "&gt;");
-  },
-
   // Create a set of formatted strings for a permission prompt
   _buildStrings(info) {
     let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
-
     let brandBundle = Services.strings.createBundle(BRAND_PROPERTIES);
     let appName = brandBundle.GetStringFromName("brandShortName");
-    let addonName = `<span class="addon-webext-name">${this._sanitizeName(info.addon.name)}</span>`;
+    let info2 = Object.assign({appName}, info);
 
-    let info2 = Object.assign({appName, addonName}, info);
-
-    return ExtensionData.formatPermissionStrings(info2, bundle);
+    let strings = ExtensionData.formatPermissionStrings(info2, bundle);
+    strings.addonName = info.addon.name;
+    return strings;
   },
 
   showPermissionsPrompt(browser, strings, icon, histkey) {
+    let message = {};
+    // Create the notification header element.
+    let header = strings.header;
+    header = header.split("<>");
+    message.start = header[0];
+    // Use the host element to display addon name in addon permission prompts.
+    message.host = strings.addonName;
+    message.end = header[1];
+
     function eventCallback(topic) {
       let doc = this.browser.ownerDocument;
       if (topic == "showing") {
-        // eslint-disable-next-line no-unsanitized/property
-        doc.getElementById("addon-webext-perm-header").innerHTML = strings.header;
         let textEl = doc.getElementById("addon-webext-perm-text");
-        // eslint-disable-next-line no-unsanitized/property
-        textEl.innerHTML = strings.text;
+        textEl.textContent = strings.text;
         textEl.hidden = !strings.text;
 
         let listIntroEl = doc.getElementById("addon-webext-perm-intro");
-        listIntroEl.value = strings.listIntro;
+        listIntroEl.textContent = strings.listIntro;
         listIntroEl.hidden = (strings.msgs.length == 0);
 
         let list = doc.getElementById("addon-webext-perm-list");
         while (list.firstChild) {
           list.firstChild.remove();
         }
 
         for (let msg of strings.msgs) {
@@ -333,86 +330,94 @@ this.ExtensionsUI = {
             if (histkey) {
               this.histogram.add(histkey + "Rejected");
             }
             resolve(false);
           },
         },
       ];
 
-      win.PopupNotifications.show(browser, "addon-webext-permissions", "",
+      win.PopupNotifications.show(browser, "addon-webext-permissions", message,
                                   "addons-notification-icon",
                                   action, secondaryActions, popupOptions);
     });
   },
 
   showDefaultSearchPrompt(browser, strings, icon) {
-//    const kDefaultSearchHistKey = "defaultSearch";
+    let message = {};
+    // Create the notification header element.
+    let header = strings.text;
+    header = header.split("<>");
+    message.start = header[0];
+    // Use the host element to display addon name in addon notification prompts.
+    message.host = strings.addonName;
+    message.end = header[1];
+
     return new Promise(resolve => {
       let popupOptions = {
         hideClose: true,
         popupIconURL: icon || DEFAULT_EXTENSION_ICON,
         persistent: false,
         removeOnDismissal: true,
         eventCallback(topic) {
-          if (topic == "showing") {
-            let doc = this.browser.ownerDocument;
-            // eslint-disable-next-line no-unsanitized/property
-            doc.getElementById("addon-webext-defaultsearch-text").innerHTML = strings.text;
-          } else if (topic == "removed") {
+          if (topic == "removed") {
             resolve(false);
           }
         }
       };
 
       let action = {
         label: strings.acceptText,
         accessKey: strings.acceptKey,
         disableHighlight: true,
         callback: () => {
-//          this.histogram.add(kDefaultSearchHistKey + "Accepted");
           resolve(true);
         },
       };
       let secondaryActions = [
         {
           label: strings.cancelText,
           accessKey: strings.cancelKey,
           callback: () => {
-//            this.histogram.add(kDefaultSearchHistKey + "Rejected");
             resolve(false);
           },
         },
       ];
 
       let win = browser.ownerGlobal;
-      win.PopupNotifications.show(browser, "addon-webext-defaultsearch", "",
+      win.PopupNotifications.show(browser, "addon-webext-defaultsearch", message,
                                   "addons-notification-icon",
                                   action, secondaryActions, popupOptions);
     });
   },
 
   showInstallNotification(target, addon) {
     let win = target.ownerGlobal;
     let popups = win.PopupNotifications;
 
-    let name = this._sanitizeName(addon.name);
-    let addonName = `<span class="addon-webext-name">${name}</span>`;
     let addonIcon = '<image class="addon-addon-icon"/>';
     let toolbarIcon = '<image class="addon-toolbar-icon"/>';
 
     let brandBundle = win.document.getElementById("bundle_brand");
     let appName = brandBundle.getString("brandShortName");
 
     let bundle = win.gNavigatorBundle;
-    let msg1 = bundle.getFormattedString("addonPostInstall.message1",
-                                         [addonName, appName]);
     let msg2 = bundle.getFormattedString("addonPostInstall.messageDetail",
                                          [addonIcon, toolbarIcon]);
 
+    // Create the notification header element.
+    let message = {};
+    let header = bundle.getFormattedString("addonPostInstall.message1",
+                                          ["<>", appName]);
+    header = header.split("<>");
+    message.start = header[0];
+    // Use the host element to display addon name in addon permission prompts.
+    message.host = addon.name;
+    message.end = header[1];
+
     return new Promise(resolve => {
       let action = {
         label: bundle.getString("addonPostInstall.okay.label"),
         accessKey: bundle.getString("addonPostInstall.okay.key"),
         callback: resolve,
       };
 
       let icon = addon.isWebExtension ?
@@ -420,27 +425,24 @@ this.ExtensionsUI = {
                  "chrome://browser/skin/addons/addon-install-installed.svg";
       let options = {
         hideClose: true,
         timeout: Date.now() + 30000,
         popupIconURL: icon,
         eventCallback(topic) {
           if (topic == "showing") {
             let doc = this.browser.ownerDocument;
-        // eslint-disable-next-line no-unsanitized/property
-            doc.getElementById("addon-installed-notification-header")
-               .unsafeSetInnerHTML(msg1);
             // eslint-disable-next-line no-unsanitized/property
             doc.getElementById("addon-installed-notification-message")
                .unsafeSetInnerHTML(msg2);
           } else if (topic == "dismissed") {
             resolve();
           }
         }
       };
 
-      popups.show(target, "addon-installed", "", "addons-notification-icon",
+      popups.show(target, "addon-installed", message, "addons-notification-icon",
                   action, null, options);
     });
   },
 };
 
 EventEmitter.decorate(ExtensionsUI);
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -256,36 +256,25 @@ html|*.addon-webext-perm-list {
   padding-inline-start: 10px;
 }
 
 .addon-webext-perm-text {
   margin-inline-start: 0;
 }
 
 .popup-notification-description[popupid="addon-webext-permissions"],
-.popup-notification-description[popupid="addon-installed"] {
-  display: none;
+.popup-notification-description[popupid="addon-webext-permissions-notification"] {
+  margin-inline-start: -1px;
 }
 
 .addon-webext-perm-notification-content,
 .addon-installed-notification-content {
   margin-top: 0;
 }
 
-#addon-webext-perm-header {
-  /* Align the text more closely with the icon by clearing some top line height. */
-  margin-top: -1px;
-  margin-inline-start: 0;
-}
-
-#addon-installed-notification-header {
-  /* Align the text more closely with the icon by clearing some top line height. */
-  margin-top: -1px;
-}
-
 .addon-webext-name {
   display: inline;
   font-weight: bold;
   margin: 0;
 }
 
 .addon-addon-icon,
 .addon-toolbar-icon {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -957,36 +957,25 @@ html|*.addon-webext-perm-list {
   padding-inline-start: 10px;
 }
 
 .addon-webext-perm-text {
   margin-inline-start: 0;
 }
 
 .popup-notification-description[popupid="addon-webext-permissions"],
-.popup-notification-description[popupid="addon-installed"] {
-  display: none;
+.popup-notification-description[popupid="addon-webext-permissions-notification"] {
+  margin-inline-start: -1px;
 }
 
 .addon-webext-perm-notification-content,
 .addon-installed-notification-content {
   margin-top: 0;
 }
 
-#addon-webext-perm-header {
-  /* Align the text more closely with the icon by clearing some top line height. */
-  margin-top: -1px;
-  margin-inline-start: 0;
-}
-
-#addon-installed-notification-header {
-  /* Align the text more closely with the icon by clearing some top line height. */
-  margin-top: -1px;
-}
-
 .addon-webext-name {
   display: inline;
   font-weight: bold;
   margin: 0;
 }
 
 .addon-addon-icon,
 .addon-toolbar-icon {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -765,36 +765,25 @@ html|*.addon-webext-perm-list {
   padding-inline-start: 10px;
 }
 
 .addon-webext-perm-text {
   margin-inline-start: 0;
 }
 
 .popup-notification-description[popupid="addon-webext-permissions"],
-.popup-notification-description[popupid="addon-installed"] {
-  display: none;
+.popup-notification-description[popupid="addon-webext-permissions-notification"] {
+  margin-inline-start: -1px;
 }
 
 .addon-webext-perm-notification-content,
 .addon-installed-notification-content {
   margin-top: 0;
 }
 
-#addon-webext-perm-header {
-  /* Align the text more closely with the icon by clearing some top line height. */
-  margin-top: -1px;
-  margin-inline-start: 0;
-}
-
-#addon-installed-notification-header {
-  /* Align the text more closely with the icon by clearing some top line height. */
-  margin-top: -1px;
-}
-
 .addon-webext-name {
   display: inline;
   font-weight: bold;
   margin: 0;
 }
 
 .addon-addon-icon,
 .addon-toolbar-icon {
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -861,33 +861,33 @@ class ExtensionData {
    * Formats all the strings for a permissions dialog/notification.
    *
    * @param {object} info Information about the permissions being requested.
    *
    * @param {array<string>} info.permissions.origins
    *                        Origin permissions requested.
    * @param {array<string>} info.permissions.permissions
    *                        Regular (non-origin) permissions requested.
-   * @param {AddonWrapper} info.addonName
-   *                       The name of the addon for which permissions are
-   *                       being requested.
    * @param {boolean} info.unsigned
    *                  True if the prompt is for installing an unsigned addon.
    * @param {string} info.type
    *                 The type of prompt being shown.  May be one of "update",
    *                 "sideload", "optional", or omitted for a regular
    *                 install prompt.
    * @param {string} info.appName
    *                 The localized name of the application, to be substituted
    *                 in computed strings as needed.
    * @param {nsIStringBundle} bundle
    *                          The string bundle to use for l10n.
    *
    * @returns {object} An object with properties containing localized strings
-   *                   for various elements of a permission dialog.
+   *                   for various elements of a permission dialog. The "header"
+   *                   property on this object is the notification header text
+   *                   and it has the string "<>" as a placeholder for the
+   *                   addon name.
    */
   static formatPermissionStrings(info, bundle) {
     let result = {};
 
     let perms = info.permissions || {origins: [], permissions: []};
 
     // First classify our host permissions
     let allUrls = false, wildcards = new Set(), sites = new Set();
@@ -966,48 +966,48 @@ class ExtensionData {
       } catch (err) {
         // We deliberately do not include all permissions in the prompt.
         // So if we don't find one then just skip it.
       }
     }
 
     const haveAccessKeys = (AppConstants.platform !== "android");
 
-    result.header = bundle.formatStringFromName("webextPerms.header", [info.addonName], 1);
+    result.header = bundle.formatStringFromName("webextPerms.header", ["<>"], 1);
     result.text = info.unsigned ?
                   bundle.GetStringFromName("webextPerms.unsignedWarning") : "";
     result.listIntro = bundle.GetStringFromName("webextPerms.listIntro");
 
     result.acceptText = bundle.GetStringFromName("webextPerms.add.label");
     result.cancelText = bundle.GetStringFromName("webextPerms.cancel.label");
     if (haveAccessKeys) {
       result.acceptKey = bundle.GetStringFromName("webextPerms.add.accessKey");
       result.cancelKey = bundle.GetStringFromName("webextPerms.cancel.accessKey");
     }
 
     if (info.type == "sideload") {
-      result.header = bundle.formatStringFromName("webextPerms.sideloadHeader", [info.addonName], 1);
+      result.header = bundle.formatStringFromName("webextPerms.sideloadHeader", ["<>"], 1);
       let key = result.msgs.length == 0 ?
                 "webextPerms.sideloadTextNoPerms" : "webextPerms.sideloadText2";
       result.text = bundle.GetStringFromName(key);
       result.acceptText = bundle.GetStringFromName("webextPerms.sideloadEnable.label");
       result.cancelText = bundle.GetStringFromName("webextPerms.sideloadCancel.label");
       if (haveAccessKeys) {
         result.acceptKey = bundle.GetStringFromName("webextPerms.sideloadEnable.accessKey");
         result.cancelKey = bundle.GetStringFromName("webextPerms.sideloadCancel.accessKey");
       }
     } else if (info.type == "update") {
-      result.header = "";
-      result.text = bundle.formatStringFromName("webextPerms.updateText", [info.addonName], 1);
+      result.header = bundle.formatStringFromName("webextPerms.updateText", ["<>"], 1);
+      result.text = "";
       result.acceptText = bundle.GetStringFromName("webextPerms.updateAccept.label");
       if (haveAccessKeys) {
         result.acceptKey = bundle.GetStringFromName("webextPerms.updateAccept.accessKey");
       }
     } else if (info.type == "optional") {
-      result.header = bundle.formatStringFromName("webextPerms.optionalPermsHeader", [info.addonName], 1);
+      result.header = bundle.formatStringFromName("webextPerms.optionalPermsHeader", ["<>"], 1);
       result.text = "";
       result.listIntro = bundle.GetStringFromName("webextPerms.optionalPermsListIntro");
       result.acceptText = bundle.GetStringFromName("webextPerms.optionalPermsAllow.label");
       result.cancelText = bundle.GetStringFromName("webextPerms.optionalPermsDeny.label");
       if (haveAccessKeys) {
         result.acceptKey = bundle.GetStringFromName("webextPerms.optionalPermsAllow.accessKey");
         result.cancelKey = bundle.GetStringFromName("webextPerms.optionalPermsDeny.accessKey");
       }
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -772,25 +772,19 @@ PopupNotifications.prototype = {
         popupnotification = doc.createElementNS(XUL_NS, "popupnotification");
 
       // 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("label", n.message);
       } else {
-        if (n.message.start) {
-          popupnotification.setAttribute("label", 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("label", n.message.start || "");
+        popupnotification.setAttribute("hostname", n.message.host || "");
+        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 {