Bug 1323129 part 1: Remove support for installing multiple xpis in InstallTrigger r=rhelmer
authorAndrew Swan <aswan@mozilla.com>
Wed, 04 Jan 2017 10:12:53 -0800
changeset 456634 d97b2eeffc46de2ef3f9312dd60e79cce68f5208
parent 456633 b14520ebf7cc599f73adba54b3d74e8b61ebf213
child 456635 308b6c4b0e8ff09723cc87229faddd3a0ed35441
push id40575
push userjwwang@mozilla.com
push dateFri, 06 Jan 2017 02:27:46 +0000
reviewersrhelmer
bugs1323129
milestone53.0a1
Bug 1323129 part 1: Remove support for installing multiple xpis in InstallTrigger r=rhelmer MozReview-Commit-ID: 567cHYjbjO4
browser/base/content/test/general/browser_bug553455.js
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/addonManager.js
toolkit/mozapps/extensions/amContentHandler.js
toolkit/mozapps/extensions/amIWebInstaller.idl
toolkit/mozapps/extensions/amInstallTrigger.js
toolkit/mozapps/extensions/moz.build
toolkit/mozapps/extensions/test/xpinstall/amosigned2.xpi
toolkit/mozapps/extensions/test/xpinstall/browser.ini
toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js
toolkit/mozapps/extensions/test/xpinstall/browser_httphash6.js
toolkit/mozapps/extensions/test/xpinstall/browser_signed_multiple.js
toolkit/mozapps/extensions/test/xpinstall/browser_signed_naming.js
toolkit/mozapps/extensions/test/xpinstall/browser_signed_no_cn.js
toolkit/mozapps/extensions/test/xpinstall/browser_signed_no_o.js
toolkit/mozapps/extensions/test/xpinstall/signed2.xpi
--- a/browser/base/content/test/general/browser_bug553455.js
+++ b/browser/base/content/test/general/browser_bug553455.js
@@ -465,56 +465,16 @@ function test_restartless() {
     Services.perms.remove(makeURI("http://example.com/"), "install");
 
     let closePromise = waitForNotificationClose();
     gBrowser.removeTab(gBrowser.selectedTab);
     yield closePromise;
   });
 },
 
-function test_multiple() {
-  return Task.spawn(function* () {
-    let pm = Services.perms;
-    pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-    let progressPromise = waitForProgressNotification();
-    let dialogPromise = waitForInstallDialog();
-    let triggers = encodeURIComponent(JSON.stringify({
-      "Unsigned XPI": "amosigned.xpi",
-      "Restartless XPI": "restartless.xpi"
-    }));
-    BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
-    let panel = yield progressPromise;
-    let installDialog = yield dialogPromise;
-
-    let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog(installDialog);
-    yield notificationPromise;
-
-    let notification = panel.childNodes[0];
-    is(notification.button.label, "Restart Now", "Should have seen the right button");
-    is(notification.getAttribute("label"),
-       "2 add-ons will be installed after you restart " + gApp + ".",
-       "Should have seen the right message");
-
-    let installs = yield getInstalls();
-    is(installs.length, 1, "Should be one pending install");
-    installs[0].cancel();
-
-    let addon = yield new Promise(resolve => {
-      AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(result) {
-        resolve(result);
-      });
-    });
-    addon.uninstall();
-    Services.perms.remove(makeURI("http://example.com/"), "install");
-    yield removeTab();
-  });
-},
-
 function test_sequential() {
   return Task.spawn(function* () {
     // This test is only relevant if using the new doorhanger UI
     // TODO: this subtest is disabled until multiple notification prompts are
     // reworked in bug 1188152
     if (true || !Preferences.get("xpinstall.customConfirmationUI", false)) {
       return;
     }
@@ -581,74 +541,16 @@ function test_sequential() {
     Services.perms.remove(makeURI("http://example.com"), "install");
     let closePromise = waitForNotificationClose();
     cancelInstallDialog(installDialog);
     yield closePromise;
     yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   });
 },
 
-function test_someUnverified() {
-  return Task.spawn(function* () {
-    // 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) ||
-        REQUIRE_SIGNING) {
-      return;
-    }
-    let pm = Services.perms;
-    pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-    let progressPromise = waitForProgressNotification();
-    let dialogPromise = waitForInstallDialog();
-    let triggers = encodeURIComponent(JSON.stringify({
-      "Extension XPI": "restartless-unsigned.xpi",
-      "Theme XPI": "theme.xpi"
-    }));
-    BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
-    yield progressPromise;
-    let installDialog = yield dialogPromise;
-
-    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");
-
-    let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog(installDialog);
-    yield notificationPromise;
-
-    let [addon, theme] = yield new Promise(resolve => {
-      AddonManager.getAddonsByIDs(["restartless-xpi@tests.mozilla.org",
-                                  "theme-xpi@tests.mozilla.org"],
-                                  function(addons) {
-        resolve(addons);
-      });
-    });
-    addon.uninstall();
-    // Installing a new theme tries to switch to it, switch back to the
-    // default theme.
-    theme.userDisabled = true;
-    theme.uninstall();
-
-    Services.perms.remove(makeURI("http://example.com/"), "install");
-    yield removeTab();
-  });
-},
-
 function test_allUnverified() {
   return Task.spawn(function* () {
     // 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) ||
         REQUIRE_SIGNING) {
       return;
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -361,98 +361,87 @@ function webAPIForAddon(addon) {
 
   return result;
 }
 
 /**
  * Listens for a browser changing origin and cancels the installs that were
  * started by it.
  */
-function BrowserListener(aBrowser, aInstallingPrincipal, aInstalls) {
+function BrowserListener(aBrowser, aInstallingPrincipal, aInstall) {
   this.browser = aBrowser;
   this.principal = aInstallingPrincipal;
-  this.installs = aInstalls;
-  this.installCount = aInstalls.length;
+  this.install = aInstall;
 
   aBrowser.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
   Services.obs.addObserver(this, "message-manager-close", true);
 
-  for (let install of this.installs)
-    install.addListener(this);
+  aInstall.addListener(this);
 
   this.registered = true;
 }
 
 BrowserListener.prototype = {
   browser: null,
-  installs: null,
-  installCount: null,
+  install: null,
   registered: false,
 
   unregister() {
     if (!this.registered)
       return;
     this.registered = false;
 
     Services.obs.removeObserver(this, "message-manager-close");
     // The browser may have already been detached
     if (this.browser.removeProgressListener)
       this.browser.removeProgressListener(this);
 
-    for (let install of this.installs)
-      install.removeListener(this);
-    this.installs = null;
+    this.install.removeListener(this);
+    this.install = null;
   },
 
-  cancelInstalls() {
-    for (let install of this.installs) {
-      try {
-        install.cancel();
-      } catch (e) {
-        // Some installs may have already failed or been cancelled, ignore these
-      }
+  cancelInstall() {
+    try {
+      this.install.cancel();
+    } catch (e) {
+      // install may have already failed or been cancelled, ignore these
     }
   },
 
   observe(subject, topic, data) {
     if (subject != this.browser.messageManager)
       return;
 
     // The browser's message manager has closed and so the browser is
-    // going away, cancel all installs
-    this.cancelInstalls();
+    // going away, cancel the install
+    this.cancelInstall();
   },
 
   onLocationChange(webProgress, request, location) {
     if (this.browser.contentPrincipal && this.principal.subsumes(this.browser.contentPrincipal))
       return;
 
-    // The browser has navigated to a new origin so cancel all installs
-    this.cancelInstalls();
+    // The browser has navigated to a new origin so cancel the install
+    this.cancelInstall();
   },
 
   onDownloadCancelled(install) {
-    // Don't need to hear more events from this install
-    install.removeListener(this);
-
-    // Once all installs have ended unregister everything
-    if (--this.installCount == 0)
-      this.unregister();
+    this.unregister();
   },
 
   onDownloadFailed(install) {
-    this.onDownloadCancelled(install);
+    this.unregister();
   },
 
   onInstallFailed(install) {
-    this.onDownloadCancelled(install);
+    this.unregister();
   },
 
   onInstallEnded(install) {
-    this.onDownloadCancelled(install);
+    this.unregister();
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
                                          Ci.nsIWebProgressListener,
                                          Ci.nsIObserver])
 };
 
 /**
@@ -2021,54 +2010,49 @@ var AddonManagerInternal = {
       if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
           callProvider(provider, "isInstallAllowed", null, aInstallingPrincipal))
         return true;
     }
     return false;
   },
 
   /**
-   * Starts installation of an array of AddonInstalls notifying the registered
+   * Starts installation of an AddonInstall notifying the registered
    * web install listener of blocked or started installs.
    *
    * @param  aMimetype
    *         The mimetype of add-ons being installed
    * @param  aBrowser
    *         The optional browser element that started the installs
    * @param  aInstallingPrincipal
    *         The nsIPrincipal that initiated the install
-   * @param  aInstalls
-   *         The array of AddonInstalls to be installed
+   * @param  aInstall
+   *         The AddonInstall to be installed
    */
-  installAddonsFromWebpage(aMimetype, aBrowser,
-                                     aInstallingPrincipal, aInstalls) {
+  installAddonFromWebpage(aMimetype, aBrowser,
+                                    aInstallingPrincipal, aInstall) {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
     if (!aMimetype || typeof aMimetype != "string")
       throw Components.Exception("aMimetype must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aBrowser && !(aBrowser instanceof Ci.nsIDOMElement))
       throw Components.Exception("aSource must be a nsIDOMElement, or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!aInstallingPrincipal || !(aInstallingPrincipal instanceof Ci.nsIPrincipal))
       throw Components.Exception("aInstallingPrincipal must be a nsIPrincipal",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    if (!Array.isArray(aInstalls))
-      throw Components.Exception("aInstalls must be an array",
-                                 Cr.NS_ERROR_INVALID_ARG);
-
     if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
-      logger.warn("No web installer available, cancelling all installs");
-      for (let install of aInstalls)
-        install.cancel();
+      logger.warn("No web installer available, cancelling install");
+      aInstall.cancel();
       return;
     }
 
     // When a chrome in-content UI has loaded a <browser> inside to host a
     // website we want to do our security checks on the inner-browser but
     // notify front-end that install events came from the outer-browser (the
     // main tab's browser). Check this by seeing if the browser we've been
     // passed is in a content type docshell and if so get the outer-browser.
@@ -2080,56 +2064,51 @@ var AddonManagerInternal = {
     if (docShell.itemType == Ci.nsIDocShellTreeItem.typeContent)
       topBrowser = docShell.chromeEventHandler;
 
     try {
       let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
                         getService(Ci.amIWebInstallListener);
 
       if (!this.isInstallEnabled(aMimetype)) {
-        for (let install of aInstalls)
-          install.cancel();
+        aInstall.cancel();
 
         weblistener.onWebInstallDisabled(topBrowser, aInstallingPrincipal.URI,
-                                         aInstalls, aInstalls.length);
+                                         [aInstall], 1);
         return;
       } else if (!aBrowser.contentPrincipal || !aInstallingPrincipal.subsumes(aBrowser.contentPrincipal)) {
-        for (let install of aInstalls)
-          install.cancel();
+        aInstall.cancel();
 
         if (weblistener instanceof Ci.amIWebInstallListener2) {
           weblistener.onWebInstallOriginBlocked(topBrowser, aInstallingPrincipal.URI,
-                                                aInstalls, aInstalls.length);
+                                                [aInstall], 1);
         }
         return;
       }
 
-      // The installs may start now depending on the web install listener,
+      // The install may start now depending on the web install listener,
       // listen for the browser navigating to a new origin and cancel the
-      // installs in that case.
-      new BrowserListener(aBrowser, aInstallingPrincipal, aInstalls);
+      // install in that case.
+      new BrowserListener(aBrowser, aInstallingPrincipal, aInstall);
 
       if (!this.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
         if (weblistener.onWebInstallBlocked(topBrowser, aInstallingPrincipal.URI,
-                                            aInstalls, aInstalls.length)) {
-          for (let install of aInstalls)
-            install.install();
+                                            [aInstall], 1)) {
+          aInstall.install();
         }
       } else if (weblistener.onWebInstallRequested(topBrowser, aInstallingPrincipal.URI,
-                                                 aInstalls, aInstalls.length)) {
-        for (let install of aInstalls)
-          install.install();
+                                                 [aInstall], 1)) {
+        aInstall.install();
       }
     } catch (e) {
       // In the event that the weblistener throws during instantiation or when
-      // calling onWebInstallBlocked or onWebInstallRequested all of the
-      // installs should get cancelled.
+      // calling onWebInstallBlocked or onWebInstallRequested the
+      // install should get cancelled.
       logger.warn("Failure calling web installer", e);
-      for (let install of aInstalls)
-        install.cancel();
+      aInstall.cancel();
     }
   },
 
   /**
    * Adds a new InstallListener if the listener is not already registered.
    *
    * @param  aListener
    *         The InstallListener to add
@@ -3419,21 +3398,20 @@ this.AddonManager = {
   isInstallEnabled(aType) {
     return AddonManagerInternal.isInstallEnabled(aType);
   },
 
   isInstallAllowed(aType, aInstallingPrincipal) {
     return AddonManagerInternal.isInstallAllowed(aType, aInstallingPrincipal);
   },
 
-  installAddonsFromWebpage(aType, aBrowser, aInstallingPrincipal,
-                                     aInstalls) {
-    AddonManagerInternal.installAddonsFromWebpage(aType, aBrowser,
-                                                  aInstallingPrincipal,
-                                                  aInstalls);
+  installAddonFromWebpage(aType, aBrowser, aInstallingPrincipal, aInstall) {
+    AddonManagerInternal.installAddonFromWebpage(aType, aBrowser,
+                                                 aInstallingPrincipal,
+                                                 aInstall);
   },
 
   installTemporaryAddon(aDirectory) {
     return AddonManagerInternal.installTemporaryAddon(aDirectory);
   },
 
   installAddonFromSources(aDirectory) {
     return AddonManagerInternal.installAddonFromSources(aDirectory);
--- a/toolkit/mozapps/extensions/addonManager.js
+++ b/toolkit/mozapps/extensions/addonManager.js
@@ -16,17 +16,17 @@ const { classes: Cc, interfaces: Ci, uti
 const EXECUTION_ERROR   = -203;
 const CANT_READ_ARCHIVE = -207;
 const USER_CANCELLED    = -210;
 const DOWNLOAD_ERROR    = -228;
 const UNSUPPORTED_TYPE  = -244;
 const SUCCESS           = 0;
 
 const MSG_INSTALL_ENABLED  = "WebInstallerIsInstallEnabled";
-const MSG_INSTALL_ADDONS   = "WebInstallerInstallAddonsFromWebpage";
+const MSG_INSTALL_ADDON    = "WebInstallerInstallAddonFromWebpage";
 const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
 
 const MSG_PROMISE_REQUEST  = "WebAPIPromiseRequest";
 const MSG_PROMISE_RESULT   = "WebAPIPromiseResult";
 const MSG_INSTALL_EVENT    = "WebAPIInstallEvent";
 const MSG_INSTALL_CLEANUP  = "WebAPICleanup";
 const MSG_ADDON_EVENT_REQ  = "WebAPIAddonEventRequest";
 const MSG_ADDON_EVENT      = "WebAPIAddonEvent";
@@ -39,17 +39,17 @@ Cu.import("resource://gre/modules/Servic
 var gSingleton = null;
 
 function amManager() {
   Cu.import("resource://gre/modules/AddonManager.jsm");
   /* globals AddonManagerPrivate*/
 
   Services.mm.loadFrameScript(CHILD_SCRIPT, true);
   Services.mm.addMessageListener(MSG_INSTALL_ENABLED, this);
-  Services.mm.addMessageListener(MSG_INSTALL_ADDONS, this);
+  Services.mm.addMessageListener(MSG_INSTALL_ADDON, this);
   Services.mm.addMessageListener(MSG_PROMISE_REQUEST, this);
   Services.mm.addMessageListener(MSG_INSTALL_CLEANUP, this);
   Services.mm.addMessageListener(MSG_ADDON_EVENT_REQ, this);
 
   Services.obs.addObserver(this, "message-manager-close", false);
   Services.obs.addObserver(this, "message-manager-disconnect", false);
 
   AddonManager.webAPI.setEventHandler(this.sendEvent);
@@ -75,84 +75,63 @@ amManager.prototype = {
   /**
    * @see amIAddonManager.idl
    */
   mapURIToAddonID(uri, id) {
     id.value = AddonManager.mapURIToAddonID(uri);
     return !!id.value;
   },
 
-  /**
-   * @see amIWebInstaller.idl
-   */
-  isInstallEnabled(aMimetype, aReferer) {
-    return AddonManager.isInstallEnabled(aMimetype);
-  },
-
-  /**
-   * @see amIWebInstaller.idl
-   */
-  installAddonsFromWebpage(aMimetype, aBrowser, aInstallingPrincipal,
-                                     aUris, aHashes, aNames, aIcons, aCallback) {
-    if (aUris.length == 0)
-      return false;
-
+  installAddonFromWebpage(aMimetype, aBrowser, aInstallingPrincipal,
+                                    aUri, aHash, aName, aIcon, aCallback) {
     let retval = true;
     if (!AddonManager.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
       aCallback = null;
       retval = false;
     }
 
-    let installs = [];
-    function buildNextInstall() {
-      if (aUris.length == 0) {
-        AddonManager.installAddonsFromWebpage(aMimetype, aBrowser, aInstallingPrincipal, installs);
+    AddonManager.getInstallForURL(aUri, function(aInstall) {
+      function callCallback(uri, status) {
+        try {
+          aCallback.onInstallEnded(uri, status);
+        } catch (e) {
+          Components.utils.reportError(e);
+        }
+      }
+
+      if (!aInstall) {
+        aCallback.onInstallEnded(aUri, UNSUPPORTED_TYPE);
         return;
       }
-      let uri = aUris.shift();
-      AddonManager.getInstallForURL(uri, function(aInstall) {
-        function callCallback(aUri, aStatus) {
-          try {
-            aCallback.onInstallEnded(aUri, aStatus);
-          } catch (e) {
-            Components.utils.reportError(e);
-          }
-        }
 
-        if (aInstall) {
-          installs.push(aInstall);
-          if (aCallback) {
-            aInstall.addListener({
-              onDownloadCancelled(aInstall) {
-                callCallback(uri, USER_CANCELLED);
-              },
+      if (aCallback) {
+        aInstall.addListener({
+          onDownloadCancelled(aInstall) {
+            callCallback(aUri, USER_CANCELLED);
+          },
 
-              onDownloadFailed(aInstall) {
-                if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE)
-                  callCallback(uri, CANT_READ_ARCHIVE);
-                else
-                  callCallback(uri, DOWNLOAD_ERROR);
-              },
-
-              onInstallFailed(aInstall) {
-                callCallback(uri, EXECUTION_ERROR);
-              },
+          onDownloadFailed(aInstall) {
+            if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE)
+              callCallback(aUri, CANT_READ_ARCHIVE);
+            else
+              callCallback(aUri, DOWNLOAD_ERROR);
+          },
 
-              onInstallEnded(aInstall, aStatus) {
-                callCallback(uri, SUCCESS);
-              }
-            });
+          onInstallFailed(aInstall) {
+            callCallback(aUri, EXECUTION_ERROR);
+          },
+
+          onInstallEnded(aInstall, aStatus) {
+            callCallback(aUri, SUCCESS);
           }
-        } else if (aCallback) {
-          aCallback.onInstallEnded(uri, UNSUPPORTED_TYPE);
-        }
-        buildNextInstall();
-      }, aMimetype, aHashes.shift(), aNames.shift(), aIcons.shift(), null, aBrowser);
-    }
-    buildNextInstall();
+        });
+      }
+
+      AddonManager.installAddonFromWebpage(aMimetype, aBrowser, aInstallingPrincipal, aInstall);
+    }, aMimetype, aHash, aName, aIcon, null, aBrowser);
 
     return retval;
   },
 
   notify(aTimer) {
     AddonManagerPrivate.backgroundUpdateTimerHandler();
   },
 
@@ -196,34 +175,34 @@ amManager.prototype = {
    */
   receiveMessage(aMessage) {
     let payload = aMessage.data;
 
     switch (aMessage.name) {
       case MSG_INSTALL_ENABLED:
         return AddonManager.isInstallEnabled(payload.mimetype);
 
-      case MSG_INSTALL_ADDONS: {
+      case MSG_INSTALL_ADDON: {
         let callback = null;
         if (payload.callbackID != -1) {
           let mm = aMessage.target.messageManager;
           callback = {
             onInstallEnded(url, status) {
               mm.sendAsyncMessage(MSG_INSTALL_CALLBACK, {
                 callbackID: payload.callbackID,
                 url,
                 status
               });
             },
           };
         }
 
-        return this.installAddonsFromWebpage(payload.mimetype,
-          aMessage.target, payload.principalToInherit, payload.uris,
-          payload.hashes, payload.names, payload.icons, callback);
+        return this.installAddonFromWebpage(payload.mimetype,
+          aMessage.target, payload.principalToInherit, payload.uri,
+          payload.hash, payload.name, payload.icon, callback);
       }
 
       case MSG_PROMISE_REQUEST: {
         let mm = aMessage.target.messageManager;
         let resolve = (value) => {
           mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
             callbackID: payload.callbackID,
             resolve: value
@@ -279,15 +258,14 @@ amManager.prototype = {
                                    Cr.NS_ERROR_NO_AGGREGATION);
 
       if (!gSingleton)
         gSingleton = new amManager();
       return gSingleton.QueryInterface(aIid);
     }
   },
   QueryInterface: XPCOMUtils.generateQI([Ci.amIAddonManager,
-                                         Ci.amIWebInstaller,
                                          Ci.nsITimerCallback,
                                          Ci.nsIObserver,
                                          Ci.nsIMessageListener])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([amManager]);
--- a/toolkit/mozapps/extensions/amContentHandler.js
+++ b/toolkit/mozapps/extensions/amContentHandler.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 const XPI_CONTENT_TYPE = "application/x-xpinstall";
-const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage";
+const MSG_INSTALL_ADDON = "WebInstallerInstallAddonFromWebpage";
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 function amContentHandler() {
 }
 
 amContentHandler.prototype = {
@@ -46,21 +46,21 @@ amContentHandler.prototype = {
 
     aRequest.cancel(Cr.NS_BINDING_ABORTED);
 
     let principalToInherit = aRequest.loadInfo.principalToInherit;
     if (!principalToInherit) {
       principalToInherit = aRequest.loadInfo.triggeringPrincipal;
     }
 
-    let installs = {
-      uris: [uri.spec],
-      hashes: [null],
-      names: [null],
-      icons: [null],
+    let install = {
+      uri: uri.spec,
+      hash: null,
+      name: null,
+      icon: null,
       mimetype: XPI_CONTENT_TYPE,
       principalToInherit,
       callbackID: -1
     };
 
     if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
       // When running in the main process this might be a frame inside an
       // in-content UI page, walk up to find the first frame element in a chrome
@@ -69,31 +69,31 @@ amContentHandler.prototype = {
       let ssm = Services.scriptSecurityManager;
       while (element && !ssm.isSystemPrincipal(element.ownerDocument.nodePrincipal))
         element = element.ownerDocument.defaultView.frameElement;
 
       if (element) {
         let listener = Cc["@mozilla.org/addons/integration;1"].
                        getService(Ci.nsIMessageListener);
         listener.wrappedJSObject.receiveMessage({
-          name: MSG_INSTALL_ADDONS,
+          name: MSG_INSTALL_ADDON,
           target: element,
-          data: installs,
+          data: install
         });
         return;
       }
     }
 
     // Fall back to sending through the message manager
     let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIDocShell)
                                .QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIContentFrameMessageManager);
 
-    messageManager.sendAsyncMessage(MSG_INSTALL_ADDONS, installs);
+    messageManager.sendAsyncMessage(MSG_INSTALL_ADDON, install);
   },
 
   classID: Components.ID("{7beb3ba8-6ec3-41b4-b67c-da89b8518922}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler]),
 
   log(aMsg) {
     let msg = "amContentHandler.js: " + (aMsg.join ? aMsg.join("") : aMsg);
     Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
deleted file mode 100644
--- a/toolkit/mozapps/extensions/amIWebInstaller.idl
+++ /dev/null
@@ -1,82 +0,0 @@
-/* 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/. */
-
-#include "nsISupports.idl"
-
-interface nsIDOMElement;
-interface nsIVariant;
-interface nsIURI;
-
-/**
- * A callback function used to notify webpages when a requested install has
- * ended.
- *
- * NOTE: This is *not* the same as InstallListener.
- */
-[scriptable, function, uuid(bb22f5c0-3ca1-48f6-873c-54e87987700f)]
-interface amIInstallCallback : nsISupports
-{
-  /**
-   * Called when an install completes or fails.
-   *
-   * @param  aUrl
-   *         The url of the add-on being installed
-   * @param  aStatus
-   *         0 if the install was successful or negative if not
-   */
-  void onInstallEnded(in AString aUrl, in int32_t aStatus);
-};
-
-
-/**
- * This interface is used to allow webpages to start installing add-ons.
- */
-[scriptable, uuid(658d6c09-15e0-4688-bee8-8551030472a9)]
-interface amIWebInstaller : nsISupports
-{
-  /**
-   * Checks if installation is enabled for a webpage.
-   *
-   * @param  aMimetype
-   *         The mimetype for the add-on to be installed
-   * @param  referer
-   *         The URL of the webpage trying to install an add-on
-   * @return true if installation is enabled
-   */
-  boolean isInstallEnabled(in AString aMimetype, in nsIURI aReferer);
-
-  /**
-   * Installs an array of add-ons at the request of a webpage
-   *
-   * @param  aMimetype
-   *         The mimetype for the add-ons
-   * @param  aBrowser
-   *         The browser installing the add-ons.
-   * @param  aReferer
-   *         The URI for the webpage installing the add-ons
-   * @param  aUris
-   *         The URIs of add-ons to be installed
-   * @param  aHashes
-   *         The hashes for the add-ons to be installed
-   * @param  aNames
-   *         The names for the add-ons to be installed
-   * @param  aIcons
-   *         The icons for the add-ons to be installed
-   * @param  aCallback
-   *         An optional callback to notify about installation success and
-   *         failure
-   * @param  aInstallCount
-   *         An optional argument including the number of add-ons to install
-   * @return true if the installation was successfully started
-   */
-  boolean installAddonsFromWebpage(in AString aMimetype,
-                                   in nsIDOMElement aBrowser,
-                                   in nsIURI aReferer,
-                                   [array, size_is(aInstallCount)] in wstring aUris,
-                                   [array, size_is(aInstallCount)] in wstring aHashes,
-                                   [array, size_is(aInstallCount)] in wstring aNames,
-                                   [array, size_is(aInstallCount)] in wstring aIcons,
-                                   [optional] in amIInstallCallback aCallback,
-                                   [optional] in uint32_t aInstallCount);
-};
--- a/toolkit/mozapps/extensions/amInstallTrigger.js
+++ b/toolkit/mozapps/extensions/amInstallTrigger.js
@@ -9,37 +9,34 @@ const {classes: Cc, interfaces: Ci, util
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 
 const XPINSTALL_MIMETYPE   = "application/x-xpinstall";
 
 const MSG_INSTALL_ENABLED  = "WebInstallerIsInstallEnabled";
-const MSG_INSTALL_ADDONS   = "WebInstallerInstallAddonsFromWebpage";
+const MSG_INSTALL_ADDON    = "WebInstallerInstallAddonFromWebpage";
 const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
 
 
 var log = Log.repository.getLogger("AddonManager.InstallTrigger");
 log.level = Log.Level[Preferences.get("extensions.logging.enabled", false) ? "Warn" : "Trace"];
 
-function CallbackObject(id, callback, urls, mediator) {
+function CallbackObject(id, callback, mediator) {
   this.id = id;
   this.callback = callback;
-  this.urls = new Set(urls);
   this.callCallback = function(url, status) {
     try {
       this.callback(url, status);
     } catch (e) {
       log.warn("InstallTrigger callback threw an exception: " + e);
     }
 
-    this.urls.delete(url);
-    if (this.urls.size == 0)
-      mediator._callbacks.delete(id);
+    mediator._callbacks.delete(id);
   };
 }
 
 function RemoteMediator(window) {
   window.QueryInterface(Ci.nsIInterfaceRequestor);
   let utils = window.getInterface(Ci.nsIDOMWindowUtils);
   this._windowID = utils.currentInnerWindowID;
 
@@ -66,59 +63,59 @@ RemoteMediator.prototype = {
 
   enabled(url) {
     let params = {
       mimetype: XPINSTALL_MIMETYPE
     };
     return this.mm.sendSyncMessage(MSG_INSTALL_ENABLED, params)[0];
   },
 
-  install(installs, principal, callback, window) {
-    let callbackID = this._addCallback(callback, installs.uris);
+  install(install, principal, callback, window) {
+    let callbackID = this._addCallback(callback);
 
-    installs.mimetype = XPINSTALL_MIMETYPE;
-    installs.principalToInherit = principal;
-    installs.callbackID = callbackID;
+    install.mimetype = XPINSTALL_MIMETYPE;
+    install.principalToInherit = principal;
+    install.callbackID = callbackID;
 
     if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
       // When running in the main process this might be a frame inside an
       // in-content UI page, walk up to find the first frame element in a chrome
       // privileged document
       let element = window.frameElement;
       let ssm = Services.scriptSecurityManager;
       while (element && !ssm.isSystemPrincipal(element.ownerDocument.nodePrincipal))
         element = element.ownerDocument.defaultView.frameElement;
 
       if (element) {
         let listener = Cc["@mozilla.org/addons/integration;1"].
                        getService(Ci.nsIMessageListener);
         return listener.wrappedJSObject.receiveMessage({
-          name: MSG_INSTALL_ADDONS,
+          name: MSG_INSTALL_ADDON,
           target: element,
-          data: installs,
+          data: install,
         });
       }
     }
 
     // Fall back to sending through the message manager
     let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIWebNavigation)
                                .QueryInterface(Ci.nsIDocShell)
                                .QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIContentFrameMessageManager);
 
-    return messageManager.sendSyncMessage(MSG_INSTALL_ADDONS, installs)[0];
+    return messageManager.sendSyncMessage(MSG_INSTALL_ADDON, install)[0];
   },
 
-  _addCallback(callback, urls) {
+  _addCallback(callback) {
     if (!callback || typeof callback != "function")
       return -1;
 
     let callbackID = this._windowID + "-" + ++this._lastCallbackID;
-    let callbackObject = new CallbackObject(callbackID, callback, urls, this);
+    let callbackObject = new CallbackObject(callbackID, callback, this);
     this._callbacks.set(callbackID, callbackObject);
     return callbackID;
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference])
 };
 
 
@@ -156,50 +153,49 @@ InstallTrigger.prototype = {
     return this._mediator.enabled(this._url.spec);
   },
 
   updateEnabled() {
     return this.enabled();
   },
 
   install(installs, callback) {
-    let installData = {
-      uris: [],
-      hashes: [],
-      names: [],
-      icons: [],
-    };
+    let keys = Object.keys(installs);
+    if (keys.length > 1) {
+      throw new this._window.Error("Only one XPI may be installed at a time");
+    }
+
+    let item = installs[keys[0]];
 
-    for (let name of Object.keys(installs)) {
-      let item = installs[name];
-      if (typeof item === "string") {
-        item = { URL: item };
-      }
-      if (!item.URL) {
-        throw new this._window.Error("Missing URL property for '" + name + "'");
-      }
+    if (typeof item === "string") {
+      item = { URL: item };
+    }
+    if (!item.URL) {
+      throw new this._window.Error("Missing URL property for '" + name + "'");
+    }
 
-      let url = this._resolveURL(item.URL);
-      if (!this._checkLoadURIFromScript(url)) {
-        throw new this._window.Error("Insufficient permissions to install: " + url.spec);
-      }
+    let url = this._resolveURL(item.URL);
+    if (!this._checkLoadURIFromScript(url)) {
+      throw new this._window.Error("Insufficient permissions to install: " + url.spec);
+    }
 
-      let iconUrl = null;
-      if (item.IconURL) {
-        iconUrl = this._resolveURL(item.IconURL);
-        if (!this._checkLoadURIFromScript(iconUrl)) {
-          iconUrl = null; // If page can't load the icon, just ignore it
-        }
+    let iconUrl = null;
+    if (item.IconURL) {
+      iconUrl = this._resolveURL(item.IconURL);
+      if (!this._checkLoadURIFromScript(iconUrl)) {
+        iconUrl = null; // If page can't load the icon, just ignore it
       }
+    }
 
-      installData.uris.push(url.spec);
-      installData.hashes.push(item.Hash || null);
-      installData.names.push(name);
-      installData.icons.push(iconUrl ? iconUrl.spec : null);
-    }
+    let installData = {
+      uri: url.spec,
+      hash: item.Hash || null,
+      name: item.name,
+      icon: iconUrl ? iconUrl.spec : null,
+    };
 
     return this._mediator.install(installData, this._principal, callback, this._window);
   },
 
   startSoftwareUpdate(url, flags) {
     let filename = Services.io.newURI(url, null, null)
                               .QueryInterface(Ci.nsIURL)
                               .filename;
--- a/toolkit/mozapps/extensions/moz.build
+++ b/toolkit/mozapps/extensions/moz.build
@@ -10,17 +10,16 @@ if CONFIG['MOZ_BUILD_APP'] == 'mobile/an
     DEFINES['MOZ_FENNEC'] = True
 
 DIRS += ['internal']
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'amIAddonManager.idl',
     'amIAddonPathService.idl',
-    'amIWebInstaller.idl',
     'amIWebInstallListener.idl',
 ]
 
 XPIDL_MODULE = 'extensions'
 
 EXTRA_COMPONENTS += [
     'addonManager.js',
     'amContentHandler.js',
deleted file mode 100644
index 74e877f26fe36641a92e66d3c5960e7f52e39e4c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/mozapps/extensions/test/xpinstall/browser.ini
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser.ini
@@ -1,12 +1,11 @@
 [DEFAULT]
 support-files =
   amosigned.xpi
-  amosigned2.xpi
   authRedirect.sjs
   bug540558.html
   bug638292.html
   bug645699.html
   concurrent_installs.html
   cookieRedirect.sjs
   corrupt.xpi
   empty.xpi
@@ -21,17 +20,16 @@ support-files =
   redirect.sjs
   restartless.xpi
   restartless-unsigned.xpi
   signed-no-cn.xpi
   signed-no-o.xpi
   signed-tampered.xpi
   signed-untrusted.xpi
   signed.xpi
-  signed2.xpi
   slowinstall.sjs
   startsoftwareupdate.html
   theme.xpi
   triggerredirect.html
   unsigned.xpi
 
 [browser_amosigned_trigger.js]
 [browser_amosigned_trigger_iframe.js]
@@ -45,17 +43,16 @@ support-files =
 [browser_badhash.js]
 [browser_badhashtype.js]
 [browser_bug540558.js]
 [browser_bug611242.js]
 [browser_bug638292.js]
 [browser_bug645699.js]
 [browser_bug672485.js]
 skip-if = true # disabled due to a leak. See bug 682410.
-[browser_cancel.js]
 [browser_concurrent_installs.js]
 [browser_cookies.js]
 [browser_cookies2.js]
 [browser_cookies3.js]
 [browser_cookies4.js]
 skip-if = true # Bug 1084646
 [browser_corrupt.js]
 [browser_datauri.js]
@@ -78,19 +75,19 @@ skip-if = true # Bug 1084646
 [browser_localfile4.js]
 [browser_navigateaway.js]
 [browser_navigateaway2.js]
 [browser_navigateaway3.js]
 skip-if = (os == "mac" || os == "win") # Bug 1198261
 [browser_navigateaway4.js]
 [browser_offline.js]
 [browser_relative.js]
-[browser_signed_multiple.js]
+[browser_signed_no_o.js]
 skip-if = require_signing
-[browser_signed_naming.js]
+[browser_signed_no_cn.js]
 skip-if = require_signing
 [browser_signed_tampered.js]
 skip-if = require_signing
 [browser_signed_trigger.js]
 skip-if = require_signing
 [browser_signed_untrusted.js]
 skip-if = require_signing
 [browser_signed_url.js]
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-// ----------------------------------------------------------------------------
-// Tests that cancelling multiple installs doesn't fail
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = install_ended;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "amosigned.xpi",
-    "Unsigned XPI 2": TESTROOT + "amosigned2.xpi",
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function get_item(items, url) {
-  for (let item of items) {
-    if (item.url == url)
-      return item;
-  }
-  ok(false, "Item for " + url + " was not listed");
-  return null;
-}
-
-function confirm_install(window) {
-  let items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 2, "Should be 2 items listed in the confirmation dialog");
-  let item = get_item(items, TESTROOT + "amosigned.xpi");
-  if (item) {
-    is(item.name, "XPI Test", "Should have seen the name from the trigger list");
-    is(item.signed, "false", "Should have listed the item as signed");
-  }
-  item = get_item(items, TESTROOT + "amosigned2.xpi");
-  if (item) {
-    is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
-    is(item.signed, "false", "Should have listed the item as signed");
-  }
-  return false;
-}
-
-function install_ended(install, addon) {
-  ok(false, "Should not have seen installs complete");
-}
-
-function finish_test(count) {
-  is(count, 0, "No add-ons should have been successfully installed");
-
-  Services.perms.remove(makeURI("http://example.com"), "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_httphash6.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_httphash6.js
@@ -58,19 +58,19 @@ function finish_failed_download() {
     "X-Target-Digest": "sha1:36ffb0acfd9c6e9682473aaebaab394d38b473c9",
     "Location": "http://example.com/browser/" + RELATIVE_DIR + "amosigned.xpi"
   });
 
   // The harness expects onNewInstall events for all installs that are about to start
   Harness.onNewInstall(gInstall);
 
   // Restart the install as a regular webpage install so the harness tracks it
-  AddonManager.installAddonsFromWebpage("application/x-xpinstall",
-                                        gBrowser.selectedBrowser,
-                                        gBrowser.contentPrincipal, [gInstall]);
+  AddonManager.installAddonFromWebpage("application/x-xpinstall",
+                                       gBrowser.selectedBrowser,
+                                       gBrowser.contentPrincipal, gInstall);
 }
 
 function install_ended(install, addon) {
   install.cancel();
 }
 
 function finish_test(count) {
   is(count, 1, "1 Add-on should have been successfully installed");
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_signed_multiple.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// ----------------------------------------------------------------------------
-// Tests installing two signed add-ons in the same trigger works.
-// This verifies bug 453545
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = install_ended;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Signed XPI": TESTROOT + "signed.xpi",
-    "Signed XPI 2": TESTROOT + "signed2.xpi",
-    "Signed XPI 3": TESTROOT + "signed-no-o.xpi",
-    "Signed XPI 4": TESTROOT + "signed-no-cn.xpi",
-    "Signed XPI 5": TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function get_item(items, url) {
-  for (let item of items) {
-    if (item.url == url)
-      return item;
-  }
-  ok(false, "Item for " + url + " was not listed");
-  return null;
-}
-
-function confirm_install(window) {
-
-  var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"].
-                       getService(Components.interfaces.nsIStringBundleService);
-  var bundle = sbs.createBundle("chrome://mozapps/locale/xpinstall/xpinstallConfirm.properties");
-
-  var expectedIntroString = bundle.formatStringFromName("itemWarnIntroMultiple", ["5"], 1);
-
-  var introStringNode = window.document.getElementById("itemWarningIntro");
-  is(introStringNode.textContent, expectedIntroString, "Should have the correct intro string");
-
-  var items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 5, "Should be 5 items listed in the confirmation dialog");
-  let item = get_item(items, TESTROOT + "signed.xpi");
-  if (item) {
-    is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
-    is(item.cert, "(Object Signer)", "Should have seen the signer");
-    is(item.signed, "true", "Should have listed the item as signed");
-  }
-  item = get_item(items, TESTROOT + "signed2.xpi");
-  if (item) {
-    is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
-    is(item.cert, "(Object Signer)", "Should have seen the signer");
-    is(item.signed, "true", "Should have listed the item as signed");
-  }
-  return true;
-}
-
-function install_ended(install, addon) {
-  install.cancel();
-}
-
-function finish_test(count) {
-  is(count, 5, "5 Add-ons should have been successfully installed");
-
-  Services.perms.remove(makeURI("http://example.com"), "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_signed_naming.js
+++ /dev/null
@@ -1,67 +0,0 @@
-// ----------------------------------------------------------------------------
-// Tests that the correct signer is presented for combinations of O and CN present.
-// The signed files have (when present) O=Mozilla Testing, CN=Object Signer
-// This verifies bug 372980
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = install_ended;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Signed XPI (O and CN)": TESTROOT + "signed.xpi",
-    "Signed XPI (CN)": TESTROOT + "signed-no-o.xpi",
-    "Signed XPI (O)": TESTROOT + "signed-no-cn.xpi",
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function get_item(items, url) {
-  for (let item of items) {
-    if (item.url == url)
-      return item;
-  }
-  ok(false, "Item for " + url + " was not listed");
-  return null;
-}
-
-function confirm_install(window) {
-  let items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 3, "Should be 3 items listed in the confirmation dialog");
-  let item = get_item(items, TESTROOT + "signed.xpi");
-  if (item) {
-    is(item.name, "Signed XPI Test", "Should have seen the name from the trigger list");
-    is(item.cert, "(Object Signer)", "Should have seen the signer");
-    is(item.signed, "true", "Should have listed the item as signed");
-  }
-  item = get_item(items, TESTROOT + "signed-no-o.xpi");
-  if (item) {
-    is(item.name, "Signed XPI Test (No Org)", "Should have seen the name from the trigger list");
-    is(item.cert, "(Object Signer)", "Should have seen the signer");
-    is(item.signed, "true", "Should have listed the item as signed");
-  }
-  item = get_item(items, TESTROOT + "signed-no-cn.xpi");
-  if (item) {
-    is(item.name, "Signed XPI Test (No Common Name)", "Should have seen the name from the trigger list");
-    is(item.cert, "(Mozilla Testing)", "Should have seen the signer");
-    is(item.signed, "true", "Should have listed the item as signed");
-  }
-  return true;
-}
-
-function install_ended(install, addon) {
-  install.cancel();
-}
-
-function finish_test(count) {
-  is(count, 3, "3 Add-ons should have been successfully installed");
-
-  Services.perms.remove(makeURI("http://example.com"), "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_no_cn.js
@@ -0,0 +1,42 @@
+// ----------------------------------------------------------------------------
+// Tests that the correct signer is presented for combinations of O and CN present.
+// The signed files have (when present) O=Mozilla Testing, CN=Object Signer
+// This verifies bug 372980
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = install_ended;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Services.perms;
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  const trigger = encodeURIComponent(JSON.stringify({
+    "Signed XPI (O)": TESTROOT + "signed-no-cn.xpi",
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + trigger);
+}
+
+function confirm_install(window) {
+  let items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should be 1 item listed in the confirmation dialog");
+  let item = items[0];
+  is(item.name, "Signed XPI Test (No Common Name)", "Should have seen the name from the trigger list");
+  is(item.cert, "(Mozilla Testing)", "Should have seen the signer");
+  is(item.signed, "true", "Should have listed the item as signed");
+  return true;
+}
+
+function install_ended(install, addon) {
+  install.cancel();
+}
+
+function finish_test(count) {
+  is(count, 1, "1 add-on should have been successfully installed");
+
+  Services.perms.remove(makeURI("http://example.com"), "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_signed_no_o.js
@@ -0,0 +1,42 @@
+// ----------------------------------------------------------------------------
+// Tests that the correct signer is presented for combinations of O and CN present.
+// The signed files have (when present) O=Mozilla Testing, CN=Object Signer
+// This verifies bug 372980
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = install_ended;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Services.perms;
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  const trigger = encodeURIComponent(JSON.stringify({
+    "Signed XPI (CN)": TESTROOT + "signed-no-o.xpi",
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + trigger);
+}
+
+function confirm_install(window) {
+  let items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should be 1 item listed in the confirmation dialog");
+  let item = items[0];
+  is(item.name, "Signed XPI Test (No Org)", "Should have seen the name from the trigger list");
+  is(item.cert, "(Object Signer)", "Should have seen the signer");
+  is(item.signed, "true", "Should have listed the item as signed");
+  return true;
+}
+
+function install_ended(install, addon) {
+  install.cancel();
+}
+
+function finish_test(count) {
+  is(count, 1, "1 add-on should have been successfully installed");
+
+  Services.perms.remove(makeURI("http://example.com"), "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
deleted file mode 100644
index 085efbbf7fdd8f9cf475e903b16552e614f9139c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001