Bug 1148021: Warn users when installing unsigned add-ons. r=dão
authorDave Townsend <dtownsend@oxymoronical.com>
Thu, 30 Apr 2015 11:55:36 -0700
changeset 242929 b3b825c069146b057ff797f5e7a4a1b502d4ccf8
parent 242928 3a906efc756de66d3f9bd59217f9d6bc654adaad
child 242930 6b09f09f41e3a2d23170dda82f156668bac27b87
push id28714
push userkwierso@gmail.com
push dateFri, 08 May 2015 17:29:48 +0000
treeherdermozilla-central@5e8adf0e7f2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersd
bugs1148021
milestone40.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 1148021: Warn users when installing unsigned add-ons. r=dão
browser/base/content/browser-addons.js
browser/base/content/test/general/browser_bug553455.js
browser/locales/en-US/chrome/browser/browser.properties
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -159,35 +159,49 @@ const gXPInstallObserver = {
         messageString = messageString.replace("#4", Services.appinfo.version);
 
         PopupNotifications.show(browser, notificationID, messageString, anchorID,
                                 action, null, options);
       }
       this._removeProgressNotification(browser);
       break; }
     case "addon-install-confirmation": {
+      let unsigned = installInfo.installs.filter(i => i.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING);
+      let someUnsigned = unsigned.length > 0 && unsigned.length < installInfo.installs.length;
+
       options.eventCallback = (aEvent) => {
         switch (aEvent) {
           case "removed":
             if (installInfo) {
               for (let install of installInfo.installs)
                 install.cancel();
             }
             this.acceptInstallation = null;
             break;
           case "shown":
             let addonList = document.getElementById("addon-install-confirmation-content");
             while (addonList.firstChild)
               addonList.firstChild.remove();
 
             for (let install of installInfo.installs) {
+              let container = document.createElement("hbox");
+
               let name = document.createElement("label");
               name.setAttribute("value", install.addon.name);
               name.setAttribute("class", "addon-install-confirmation-name");
-              addonList.appendChild(name);
+              container.appendChild(name);
+
+              if (someUnsigned && install.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
+                let unsigned = document.createElement("label");
+                unsigned.setAttribute("value", gNavigatorBundle.getString("addonInstall.unsigned"));
+                unsigned.setAttribute("class", "addon-install-confirmation-unsigned");
+                container.appendChild(unsigned);
+              }
+
+              addonList.appendChild(container);
             }
 
             this.acceptInstallation = () => {
               for (let install of installInfo.installs)
                 install.install();
               installInfo = null;
 
               Services.telemetry
@@ -196,17 +210,30 @@ const gXPInstallObserver = {
             };
             break;
         }
       };
 
       options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") +
                              "find-and-install-add-ons";
 
-      messageString = gNavigatorBundle.getString("addonConfirmInstall.message");
+      if (unsigned.length == installInfo.installs.length) {
+        // None of the add-ons are verified
+        messageString = gNavigatorBundle.getString("addonConfirmInstallUnsigned.message");
+      }
+      else if (unsigned.length == 0) {
+        // All add-ons are verified or don't need to be verified
+        messageString = gNavigatorBundle.getString("addonConfirmInstall.message");
+      }
+      else {
+        // Some of the add-ons are unverified, the list of names will indicate
+        // which
+        messageString = gNavigatorBundle.getString("addonConfirmInstallSomeUnsigned.message");
+      }
+
       messageString = PluralForm.get(installInfo.installs.length, messageString);
       messageString = messageString.replace("#1", brandShortName);
       messageString = messageString.replace("#2", installInfo.installs.length);
 
       let cancelButton = document.getElementById("addon-install-confirmation-cancel");
       cancelButton.label = gNavigatorBundle.getString("addonInstall.cancelButton.label");
       cancelButton.accessKey = gNavigatorBundle.getString("addonInstall.cancelButton.accesskey");
 
--- a/browser/base/content/test/general/browser_bug553455.js
+++ b/browser/base/content/test/general/browser_bug553455.js
@@ -450,16 +450,120 @@ function test_multiple() {
   var triggers = encodeURIComponent(JSON.stringify({
     "Unsigned XPI": "unsigned.xpi",
     "Restartless XPI": "restartless.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
+function test_someunverified() {
+  // This test is only relevant if using the new doorhanger UI and allowing
+  // unsigned add-ons
+  if (!Preferences.get("xpinstall.customConfirmationUI", false) ||
+      Preferences.get("xpinstall.signatures.required", true)) {
+    runNextTest();
+    return;
+  }
+
+  // Wait for the progress notification
+  wait_for_progress_notification(function(aPanel) {
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function() {
+      let notification = document.getElementById("addon-install-confirmation-notification");
+      let message = notification.getAttribute("label");
+      is(message, "Caution: This site would like to install 2 add-ons in " + gApp + ", some of which are unverified. Proceed at your own risk.",
+         "Should see the right message");
+
+      let container = document.getElementById("addon-install-confirmation-content");
+      is(container.childNodes.length, 2, "Should be two items listed");
+      is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
+      is(container.childNodes[0].lastChild.getAttribute("class"),
+         "addon-install-confirmation-unsigned", "Should have the unverified marker");
+      is(container.childNodes[1].firstChild.getAttribute("value"), "Theme Test", "Should have the right add-on");
+      is(container.childNodes[1].childNodes.length, 1, "Shouldn't have the unverified marker");
+
+      // Wait for the complete notification
+      wait_for_notification("addon-install-complete", function(aPanel) {
+        AddonManager.getAddonsByIDs(["restartless-xpi@tests.mozilla.org",
+                                     "theme-xpi@tests.mozilla.org"], function([a, t]) {
+          a.uninstall();
+          // Installing a new theme tries to switch to it, switch back to the
+          // default theme.
+          t.userDisabled = true;
+          t.uninstall();
+
+          Services.perms.remove("example.com", "install");
+          wait_for_notification_close(runNextTest);
+          gBrowser.removeTab(gBrowser.selectedTab);
+        });
+      });
+
+      accept_install_dialog();
+    });
+  });
+
+  var pm = Services.perms;
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Extension XPI": "restartless.xpi",
+    "Theme XPI": "theme.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+},
+
+function test_allunverified() {
+  // This test is only relevant if using the new doorhanger UI and allowing
+  // unsigned add-ons
+  if (!Preferences.get("xpinstall.customConfirmationUI", false) ||
+      Preferences.get("xpinstall.signatures.required", true)) {
+    runNextTest();
+    return;
+  }
+
+  // Wait for the progress notification
+  wait_for_progress_notification(function(aPanel) {
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function() {
+      let notification = document.getElementById("addon-install-confirmation-notification");
+      let message = notification.getAttribute("label");
+      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");
+
+      // Wait for the complete notification
+      wait_for_notification("addon-install-complete", function(aPanel) {
+        AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) {
+          aAddon.uninstall();
+
+          Services.perms.remove("example.com", "install");
+          wait_for_notification_close(runNextTest);
+          gBrowser.removeTab(gBrowser.selectedTab);
+        });
+      });
+
+      accept_install_dialog();
+    });
+  });
+
+  var pm = Services.perms;
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Extension XPI": "restartless.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+},
+
 function test_url() {
   // Wait for the progress notification
   wait_for_progress_notification(function(aPanel) {
     // Wait for the install confirmation dialog
     wait_for_install_dialog(function() {
       // Wait for the complete notification
       wait_for_notification("addon-install-complete", function(aPanel) {
         let notification = aPanel.childNodes[0];
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -32,27 +32,36 @@ xpinstallDisabledButton.accesskey=n
 
 # LOCALIZATION NOTE (addonDownloadingAndVerifying):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # Also see https://bugzilla.mozilla.org/show_bug.cgi?id=570012 for mockups
 addonDownloadingAndVerifying=Downloading and verifying add-on…;Downloading and verifying #1 add-ons…
 addonDownloadVerifying=Verifying
 
+addonInstall.unsigned=(Unverified)
 addonInstall.cancelButton.label=Cancel
 addonInstall.cancelButton.accesskey=C
 addonInstall.acceptButton.label=Install
 addonInstall.acceptButton.accesskey=I
 
-# LOCALIZATION NOTE (addonConfirmInstallMessage):
+# LOCALIZATION NOTE (addonConfirmInstallMessage,addonConfirmInstallUnsigned):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is brandShortName
 # #2 is the number of add-ons being installed
 addonConfirmInstall.message=This site would like to install an add-on in #1:;This site would like to install #2 add-ons in #1:
+addonConfirmInstallUnsigned.message=Caution: This site would like to install an unverified add-on in #1. Proceed at your own risk.;Caution: This site would like to install #2 unverified add-ons in #1. Proceed at your own risk.
+
+# LOCALIZATION NOTE (addonConfirmInstallSomeUnsigned.message):
+# Semicolon-separated list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is brandShortName
+# #2 is the total number of add-ons being installed (at least 2)
+addonConfirmInstallSomeUnsigned.message=;Caution: This site would like to install #2 add-ons in #1, some of which are unverified. Proceed at your own risk.
 
 addonwatch.slow=%1$S might be making %2$S run slowly
 addonwatch.disable.label=Disable %S
 addonwatch.ignoreSession.label=Ignore for now
 addonwatch.ignoreSession.accesskey=I
 addonwatch.ignorePerm.label=Ignore permanently
 addonwatch.ignorePerm.accesskey=p
 addonwatch.restart.message=To disable %1$S you must restart %2$S