Bug 474763: Add automated tests for xpinstall. r=bsmedberg
authorDave Townsend <dtownsend@oxymoronical.com>
Thu, 12 Feb 2009 13:35:03 +0000
changeset 24957 e321d96f814530a34062cf864e2927480f27a005
parent 24956 9b79ff7b47074402a7f2a1616966fe54f2ac34e7
child 24960 29d0e3e2bee01c26affb2de0bb36c484b3305139
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs474763
milestone1.9.2a1pre
Bug 474763: Add automated tests for xpinstall. r=bsmedberg
xpinstall/Makefile.in
xpinstall/tests/Makefile.in
xpinstall/tests/authRedirect.sjs
xpinstall/tests/browser_auth.js
xpinstall/tests/browser_auth2.js
xpinstall/tests/browser_auth3.js
xpinstall/tests/browser_badhash.js
xpinstall/tests/browser_badhashtype.js
xpinstall/tests/browser_cancel.js
xpinstall/tests/browser_chrome.js
xpinstall/tests/browser_cookies.js
xpinstall/tests/browser_cookies2.js
xpinstall/tests/browser_cookies3.js
xpinstall/tests/browser_cookies4.js
xpinstall/tests/browser_corrupt.js
xpinstall/tests/browser_empty.js
xpinstall/tests/browser_enabled.js
xpinstall/tests/browser_enabled2.js
xpinstall/tests/browser_enabled3.js
xpinstall/tests/browser_hash.js
xpinstall/tests/browser_installchrome.js
xpinstall/tests/browser_localfile.js
xpinstall/tests/browser_localfile2.js
xpinstall/tests/browser_navigateaway.js
xpinstall/tests/browser_navigateaway2.js
xpinstall/tests/browser_offline.js
xpinstall/tests/browser_opendialog.js
xpinstall/tests/browser_signed_multiple.js
xpinstall/tests/browser_signed_tampered.js
xpinstall/tests/browser_signed_trigger.js
xpinstall/tests/browser_signed_untrusted.js
xpinstall/tests/browser_signed_url.js
xpinstall/tests/browser_softwareupdate.js
xpinstall/tests/browser_unsigned_trigger.js
xpinstall/tests/browser_unsigned_url.js
xpinstall/tests/browser_whitelist.js
xpinstall/tests/browser_whitelist2.js
xpinstall/tests/browser_whitelist3.js
xpinstall/tests/browser_whitelist4.js
xpinstall/tests/browser_whitelist5.js
xpinstall/tests/browser_whitelist6.js
xpinstall/tests/cookieRedirect.sjs
xpinstall/tests/corrupt.xpi
xpinstall/tests/empty.xpi
xpinstall/tests/enabled.html
xpinstall/tests/harness.js
xpinstall/tests/installchrome.html
xpinstall/tests/installtrigger.html
xpinstall/tests/signed-tampered.xpi
xpinstall/tests/signed-untrusted.xpi
xpinstall/tests/signed.xpi
xpinstall/tests/signed2.xpi
xpinstall/tests/startsoftwareupdate.html
xpinstall/tests/unsigned.xpi
--- a/xpinstall/Makefile.in
+++ b/xpinstall/Makefile.in
@@ -45,9 +45,13 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= xpinstall
 DIRS		= public src
 
+ifdef ENABLE_TESTS
+DIRS += tests
+endif
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/Makefile.in
@@ -0,0 +1,99 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = xpinstall/tests
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_FILES = harness.js \
+                 browser_unsigned_url.js \
+                 browser_unsigned_trigger.js \
+                 browser_whitelist.js \
+                 browser_whitelist2.js \
+                 browser_whitelist3.js \
+                 browser_whitelist4.js \
+                 browser_whitelist5.js \
+                 browser_whitelist6.js \
+                 browser_hash.js \
+                 browser_badhash.js \
+                 browser_badhashtype.js \
+                 browser_signed_url.js \
+                 browser_signed_trigger.js \
+                 browser_signed_untrusted.js \
+                 browser_signed_tampered.js \
+                 browser_signed_multiple.js \
+                 browser_empty.js \
+                 browser_corrupt.js \
+                 browser_cookies.js \
+                 browser_cookies2.js \
+                 browser_cookies3.js \
+                 browser_cookies4.js \
+                 browser_enabled.js \
+                 browser_enabled2.js \
+                 browser_enabled3.js \
+                 browser_softwareupdate.js \
+                 browser_installchrome.js \
+                 browser_opendialog.js \
+                 browser_localfile.js \
+                 browser_localfile2.js \
+                 browser_auth.js \
+                 browser_auth2.js \
+                 browser_auth3.js \
+                 browser_offline.js \
+                 browser_chrome.js \
+                 browser_cancel.js \
+                 unsigned.xpi \
+                 signed.xpi \
+                 signed2.xpi \
+                 signed-untrusted.xpi \
+                 signed-tampered.xpi \
+                 empty.xpi \
+                 corrupt.xpi \
+                 enabled.html \
+                 installtrigger.html \
+                 startsoftwareupdate.html \
+                 installchrome.html \
+                 authRedirect.sjs \
+                 cookieRedirect.sjs \
+                 $(NULL)
+
+libs::	$(_BROWSER_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/authRedirect.sjs
@@ -0,0 +1,21 @@
+// Simple script redirects to the query part of the uri if the browser
+// authenticates with username "testuser" password "testpass"
+
+function handleRequest(request, response) {
+  if (request.hasHeader("Authorization")) {
+    if (request.getHeader("Authorization") == "Basic dGVzdHVzZXI6dGVzdHBhc3M=") {
+      response.setStatusLine(request.httpVersion, 302, "Found");
+      response.setHeader("Location", request.queryString);
+      response.write("See " + request.queryString);
+    }
+    else {
+      response.setStatusLine(request.httpVersion, 403, "Forbidden");
+      response.write("Invalid credentials");
+    }
+  }
+  else {
+    response.setStatusLine(request.httpVersion, 401, "Authentication required");
+    response.setHeader("WWW-Authenticate", "basic realm=\"XPInstall\"", false);
+    response.write("Unauthenticed request");
+  }
+}
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_auth.js
@@ -0,0 +1,50 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install succeeds when authentication is required
+// This verifies bug 312473
+function test() {
+  Harness.authenticationCallback = get_auth_info;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_auth_info() {
+  return [ "testuser", "testpass" ];
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
+                          .getService(Components.interfaces.nsIHttpAuthManager);
+  authMgr.clearAll();
+
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_auth2.js
@@ -0,0 +1,47 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install fails when authentication is required and bad
+// credentials are given
+// This verifies bug 312473
+function test() {
+  Harness.authenticationCallback = get_auth_info;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_auth_info() {
+  return [ "baduser", "badpass" ];
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -228, "Install should fail");
+}
+
+function finish_test() {
+  var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
+                          .getService(Components.interfaces.nsIHttpAuthManager);
+  authMgr.clearAll();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_auth3.js
@@ -0,0 +1,47 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install fails when authentication is required and it is
+// canceled
+// This verifies bug 312473
+function test() {
+  Harness.authenticationCallback = get_auth_info;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function get_auth_info() {
+  return null;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -228, "Install should fail");
+}
+
+function finish_test() {
+  var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
+                          .getService(Components.interfaces.nsIHttpAuthManager);
+  authMgr.clearAll();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_badhash.js
@@ -0,0 +1,41 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install fails when an invalid hash is included
+// This verifies bug 302284
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": {
+      URL: TESTROOT + "unsigned.xpi",
+      Hash: "sha1:643b08418599ddbd1ea8a511c90696578fb844b9",
+      toString: function() { return this.URL; }
+    }
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -261, "Install should fail");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_badhashtype.js
@@ -0,0 +1,41 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install fails when an unknown hash type is included
+// This verifies bug 302284
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": {
+      URL: TESTROOT + "unsigned.xpi",
+      Hash: "foo:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f",
+      toString: function() { return this.URL; }
+    }
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -261, "Install should fail");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_cancel.js
@@ -0,0 +1,45 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests that cancelling an in progress download works.
+var gManager = null;
+
+function test() {
+  waitForExplicitFinish();
+  gManager = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
+                       .createInstance(Components.interfaces.nsIXPInstallManager);
+  gManager.initManagerFromChrome([ TESTROOT + "unsigned.xpi" ],
+                                 1, listener);
+}
+
+function finish_test() {
+  finish();
+}
+
+var listener = {
+  onStateChange: function(index, state, value) {
+    is(index, 0, "There is only one download");
+    if (state == Components.interfaces.nsIXPIProgressDialog.INSTALL_DONE)
+      is(value, -210, "Install should have been cancelled");
+    else if (state == Components.interfaces.nsIXPIProgressDialog.DIALOG_CLOSE)
+      finish_test();
+  },
+
+  onProgress: function(index, value, maxValue) {
+    is(index, 0, "There is only one download");
+    gManager.QueryInterface(Components.interfaces.nsIObserver);
+    gManager.observe(null, "xpinstall-progress", "cancel");
+  },
+
+  QueryInterface: function(iid) {
+    if (iid.equals(Components.interfaces.nsIXPIProgressDialog) ||
+        iid.equals(Components.interfaces.nsISupports))
+      return this;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  }
+};
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_chrome.js
@@ -0,0 +1,45 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests that starting a download from chrome works and bypasses the whitelist
+function test() {
+  waitForExplicitFinish();
+  var xpimgr = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
+                         .createInstance(Components.interfaces.nsIXPInstallManager);
+  xpimgr.initManagerFromChrome([ TESTROOT + "unsigned.xpi" ],
+                               1, listener);
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  finish();
+}
+
+var listener = {
+  onStateChange: function(index, state, value) {
+    is(index, 0, "There is only one download");
+    if (state == Components.interfaces.nsIXPIProgressDialog.INSTALL_DONE)
+      is(value, 0, "Install should have succeeded");
+    else if (state == Components.interfaces.nsIXPIProgressDialog.DIALOG_CLOSE)
+      finish_test();
+  },
+
+  onProgress: function(index, value, maxValue) {
+    is(index, 0, "There is only one download");
+  },
+
+  QueryInterface: function(iid) {
+    if (iid.equals(Components.interfaces.nsIXPIProgressDialog) ||
+        iid.equals(Components.interfaces.nsISupports))
+      return this;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  }
+};
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_cookies.js
@@ -0,0 +1,38 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test that an install that requires cookies to be sent fails when no cookies
+// are set
+// This verifies bug 462739
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -228, "Install should fail");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_cookies2.js
@@ -0,0 +1,51 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test that an install that requires cookies to be sent succeeds when cookies
+// are set
+// This verifies bug 462739
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+                     .getService(Components.interfaces.nsICookieManager2);
+  cm.add("example.com", "/browser/xpinstall/tests", "xpinstall", "true", false,
+         false, true, (Date.now() / 1000) + 60);
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+                     .getService(Components.interfaces.nsICookieManager2);
+  cm.remove("example.com", "xpinstall", "/browser/xpinstall/tests", false);
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_cookies3.js
@@ -0,0 +1,59 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test that an install that requires cookies to be sent succeeds when cookies
+// are set and third party cookies are disabled.
+// This verifies bug 462739
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+                     .getService(Components.interfaces.nsICookieManager2);
+  cm.add("example.com", "/browser/xpinstall/tests", "xpinstall", "true", false,
+         false, true, (Date.now() / 1000) + 60);
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var prefs = Components.classes["@mozilla.org/preferences;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  prefs.setIntPref("network.cookie.cookieBehavior", 1);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+                     .getService(Components.interfaces.nsICookieManager2);
+  cm.remove("example.com", "xpinstall", "/browser/xpinstall/tests", false);
+
+  var prefs = Components.classes["@mozilla.org/preferences;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  prefs.clearUserPref("network.cookie.cookieBehavior");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_cookies4.js
@@ -0,0 +1,56 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test that an install that requires cookies to be sent fails when cookies
+// are set and third party cookies are disabled and the request is to a third
+// party.
+// This verifies bug 462739
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+                     .getService(Components.interfaces.nsICookieManager2);
+  cm.add("example.com", "/browser/xpinstall/tests", "xpinstall", "true", false,
+         false, true, (Date.now() / 1000) + 60);
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var prefs = Components.classes["@mozilla.org/preferences;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  prefs.setIntPref("network.cookie.cookieBehavior", 1);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Cookie check": TESTROOT2 + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -228, "Install should fail");
+}
+
+function finish_test() {
+  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
+                     .getService(Components.interfaces.nsICookieManager2);
+  cm.remove("example.com", "xpinstall", "/browser/xpinstall/tests", false);
+
+  var prefs = Components.classes["@mozilla.org/preferences;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  prefs.clearUserPref("network.cookie.cookieBehavior");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_corrupt.js
@@ -0,0 +1,39 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install fails when the xpi is corrupt.
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Corrupt XPI": TESTROOT + "corrupt.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -207, "Install should fail");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  var doc = gBrowser.contentDocument;
+  is(doc.getElementById("status").textContent, "-207", "Callback should have seen the failure");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_empty.js
@@ -0,0 +1,36 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install fails when there is no install script present.
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Empty XPI": TESTROOT + "empty.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -204, "Install should fail");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_enabled.js
@@ -0,0 +1,25 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an InstallTrigger.enabled is working
+function test() {
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    // Allow the in-page load handler to run first
+    executeSoon(page_loaded);
+  }, true);
+  gBrowser.loadURI(TESTROOT + "enabled.html");
+}
+
+function page_loaded() {
+  gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, false);
+  var doc = gBrowser.contentDocument;
+  is(doc.getElementById("enabled").textContent, "true", "installTrigger should have been enabled");
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_enabled2.js
@@ -0,0 +1,33 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an InstallTrigger.enabled is working
+function test() {
+  waitForExplicitFinish();
+
+  var prefs = Components.classes["@mozilla.org/preferences;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  prefs.setBoolPref("xpinstall.enabled", false);
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    // Allow the in-page load handler to run first
+    executeSoon(page_loaded);
+  }, true);
+  gBrowser.loadURI(TESTROOT + "enabled.html");
+}
+
+function page_loaded() {
+  gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, false);
+  var prefs = Components.classes["@mozilla.org/preferences;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  prefs.clearUserPref("xpinstall.enabled");
+
+  var doc = gBrowser.contentDocument;
+  is(doc.getElementById("enabled").textContent, "false", "installTrigger should have not been enabled");
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_enabled3.js
@@ -0,0 +1,37 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an InstallTrigger.install call fails when xpinstall is disabled
+function test() {
+  waitForExplicitFinish();
+
+  var prefs = Components.classes["@mozilla.org/preferences;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  prefs.setBoolPref("xpinstall.enabled", false);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    // Allow the in-page load handler to run first
+    executeSoon(page_loaded);
+  }, true);
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function page_loaded() {
+  gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, false);
+  var prefs = Components.classes["@mozilla.org/preferences;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+  prefs.clearUserPref("xpinstall.enabled");
+
+  var doc = gBrowser.contentDocument;
+  is(doc.getElementById("return").textContent, "false", "installTrigger should have not been enabled");
+  gBrowser.removeCurrentTab();
+  finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_hash.js
@@ -0,0 +1,45 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install succeeds when a valid hash is included
+// This verifies bug 302284
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": {
+      URL: TESTROOT + "unsigned.xpi",
+      Hash: "sha1:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f",
+      toString: function() { return this.URL; }
+    }
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_installchrome.js
@@ -0,0 +1,37 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests that calling InstallTrigger.installChrome works
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installchrome.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_localfile.js
@@ -0,0 +1,33 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an local file works when loading the url
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+                     .getService(Components.interfaces.nsIChromeRegistry);
+  var path = cr.convertChromeURL(makeURI(CHROMEROOT + "unsigned.xpi")).spec;
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(path);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_localfile2.js
@@ -0,0 +1,35 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install fails if the url is a local file when requested from
+// web content
+function test() {
+  waitForExplicitFinish();
+
+  var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+                     .getService(Components.interfaces.nsIChromeRegistry);
+  var path = cr.convertChromeURL(makeURI(CHROMEROOT + "unsigned.xpi")).spec;
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": path
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    // Allow the in-page load handler to run first
+    executeSoon(page_loaded);
+  }, true);
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function page_loaded() {
+  gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, false);
+
+  var doc = gBrowser.contentDocument;
+  is(doc.getElementById("return").textContent, "exception", "installTrigger should have failed");
+  gBrowser.removeCurrentTab();
+  finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_navigateaway.js
@@ -0,0 +1,47 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests that navigating away from the initiating page during the install
+// doesn't break the install.
+// This verifies bug 473060
+function test() {
+  Harness.downloadProgressCallback = download_progress;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_progress(addon, value, maxValue) {
+  gBrowser.loadURI("about:blank");
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_navigateaway2.js
@@ -0,0 +1,46 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests that closing the initiating page during the install doesn't break the
+// install.
+// This verifies bugs 473060 and 475347
+function test() {
+  Harness.downloadProgressCallback = download_progress;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_progress(addon, value, maxValue) {
+  gBrowser.removeCurrentTab();
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_offline.js
@@ -0,0 +1,43 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests that going offline cancels an in progress download.
+function test() {
+  Harness.downloadProgressCallback = download_progress;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function download_progress(addon, value, maxValue) {
+  BrowserOffline.toggleOfflineStatus();
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -210, "Install should be cancelled");
+}
+
+function finish_test() {
+  BrowserOffline.toggleOfflineStatus();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_opendialog.js
@@ -0,0 +1,42 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Test whether an install succeeds when the progress dialog is already open.
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  BrowserOpenAddonsMgr();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_signed_multiple.js
@@ -0,0 +1,58 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing two signed add-ons in the same trigger works.
+// This verifies bug 453545
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  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",
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+  items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 2, "Should be 2 items listed in the confirmation dialog");
+  is(items[0].name, "Signed XPI", "Should have seen the name from the trigger list");
+  is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
+  is(items[0].cert, "(Mozilla Testing)", "Should have seen the signer");
+  is(items[0].signed, "true", "Should have listed the item as signed");
+  is(items[1].name, "Signed XPI 2", "Should have seen the name from the trigger list");
+  is(items[1].url, TESTROOT + "signed2.xpi", "Should have listed the correct url for the item");
+  is(items[1].cert, "(Mozilla Testing)", "Should have seen the signer");
+  is(items[1].signed, "true", "Should have listed the item as signed");
+  return true;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Installs should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("signed-xpi@tests.mozilla.org");
+  em.cancelInstallItem("signed-xpi2@tests.mozilla.org");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_signed_tampered.js
@@ -0,0 +1,47 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing a signed add-on that has been tampered with after signing.
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Tampered Signed XPI": TESTROOT + "signed-tampered.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+  items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+  is(items[0].name, "Tampered Signed XPI", "Should have seen the name from the trigger list");
+  is(items[0].url, TESTROOT + "signed-tampered.xpi", "Should have listed the correct url for the item");
+  is(items[0].cert, "(Mozilla Testing)", "Should have seen the signer");
+  is(items[0].signed, "true", "Should have listed the item as signed");
+  return true;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -260, "Install should fail");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_signed_trigger.js
@@ -0,0 +1,52 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an signed add-on through an InstallTrigger call in web
+// content.
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Signed XPI": TESTROOT + "signed.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+  items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+  is(items[0].name, "Signed XPI", "Should have seen the name from the trigger list");
+  is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
+  is(items[0].cert, "(Mozilla Testing)", "Should have seen the signer");
+  is(items[0].signed, "true", "Should have listed the item as signed");
+  return true;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("signed-xpi@tests.mozilla.org");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_signed_untrusted.js
@@ -0,0 +1,48 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an add-on signed by an untrusted certificate through an
+// InstallTrigger call in web content.
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Untrusted Signed XPI": TESTROOT + "signed-untrusted.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+  items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+  is(items[0].name, "Untrusted Signed XPI", "Should have seen the name from the trigger list");
+  is(items[0].url, TESTROOT + "signed-untrusted.xpi", "Should have listed the correct url for the item");
+  is(items[0].cert, "(Unknown Organisation)", "Should have seen the supposed signer");
+  is(items[0].signed, "true", "Should have listed the item as signed");
+  return true;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, -260, "Install should fail");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_signed_url.js
@@ -0,0 +1,40 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an signed add-on by navigating directly to the url
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "signed.xpi");
+}
+
+function confirm_install(window) {
+  items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+  is(items[0].name, "signed.xpi", "Should have had the filename for the item name");
+  is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
+  is(items[0].cert, "(Mozilla Testing)", "Should have seen the signer");
+  is(items[0].signed, "true", "Should have listed the item as signed");
+  return true;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("signed-xpi@tests.mozilla.org");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_softwareupdate.js
@@ -0,0 +1,37 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests that calling InstallTrigger.startSoftwareUpdate works
+function test() {
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "startsoftwareupdate.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_unsigned_trigger.js
@@ -0,0 +1,60 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an InstallTrigger call in web
+// content.
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": {
+      URL: TESTROOT + "unsigned.xpi",
+      IconURL: TESTROOT + "icon.png",
+      toString: function() { return this.URL; }
+    }
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function confirm_install(window) {
+  items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+  is(items[0].name, "Unsigned XPI", "Should have seen the name from the trigger list");
+  is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+  is(items[0].icon, TESTROOT + "icon.png", "Should have listed the correct icon for the item");
+  is(items[0].signed, "false", "Should have listed the item as unsigned");
+  return true;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  var doc = gBrowser.contentDocument;
+  is(doc.getElementById("return").textContent, "true", "installTrigger should have claimed success");
+  is(doc.getElementById("status").textContent, "0", "Callback should have seen a success");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_unsigned_url.js
@@ -0,0 +1,40 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on by navigating directly to the url
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "unsigned.xpi");
+}
+
+function confirm_install(window) {
+  items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+  is(items[0].name, "unsigned.xpi", "Should have had the filename for the item name");
+  is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+  is(items[0].icon, "", "Should have listed no icon for the item");
+  is(items[0].signed, "false", "Should have listed the item as unsigned");
+  return true;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_whitelist.js
@@ -0,0 +1,49 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an InstallTrigger call in web
+// content. This should be blocked by the whitelist check.
+// This verifies bug 252830
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installBlockedCallback = allow_blocked;
+  Harness.installEndedCallback = check_xpi_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function allow_blocked() {
+  return true;
+}
+
+function confirm_install(window) {
+  items = window.document.getElementById("itemList").childNodes;
+  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
+  is(items[0].name, "Unsigned XPI", "Should have seen the name from the trigger list");
+  is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
+  is(items[0].signed, "false", "Should have listed the item as unsigned");
+  return true;
+}
+
+function check_xpi_install(addon, status) {
+  is(status, 0, "Install should succeed");
+}
+
+function finish_test() {
+  var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                     .getService(Components.interfaces.nsIExtensionManager);
+  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_whitelist2.js
@@ -0,0 +1,38 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an InstallTrigger call in web
+// content. This should be blocked by the whitelist check because the source
+// is not whitelisted, even though the target is.
+function test() {
+  Harness.installBlockedCallback = allow_blocked;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+}
+
+function allow_blocked() {
+  return false;
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.org", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_whitelist3.js
@@ -0,0 +1,37 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through a navigation. Should not be
+// blocked since the referer is whitelisted.
+function test() {
+  Harness.installConfirmCallback = confirm_install;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "unsigned.xpi", makeURI(TESTROOT2 + "test.html"));
+}
+
+function confirm_install(window) {
+  return false;
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.org", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_whitelist4.js
@@ -0,0 +1,37 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through a navigation. Should be
+// blocked since the referer is not whitelisted even though the target is.
+function test() {
+  Harness.installBlockedCallback = allow_blocked;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "unsigned.xpi", makeURI(TESTROOT2 + "test.html"));
+}
+
+function allow_blocked() {
+  return false;
+}
+
+function finish_test() {
+  var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                     .getService(Components.interfaces.nsIPermissionManager);
+  pm.remove("example.com", "install");
+
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_whitelist5.js
@@ -0,0 +1,27 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through a startSoftwareUpdate call in web
+// content. This should be blocked by the whitelist check.
+// This verifies bug 252830
+function test() {
+  Harness.installBlockedCallback = allow_blocked;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "startsoftwareupdate.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
+}
+
+function allow_blocked() {
+  return false;
+}
+
+function finish_test() {
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/browser_whitelist6.js
@@ -0,0 +1,27 @@
+// Load in the test harness
+var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+                             .getService(Components.interfaces.mozIJSSubScriptLoader);
+scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this);
+
+// ----------------------------------------------------------------------------
+// Tests installing an unsigned add-on through an installChrome call in web
+// content. This should be blocked by the whitelist check.
+// This verifies bug 252830
+function test() {
+  Harness.installBlockedCallback = allow_blocked;
+  Harness.installsCompletedCallback = finish_test;
+  Harness.setup();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installchrome.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
+}
+
+function allow_blocked() {
+  return false;
+}
+
+function finish_test() {
+  gBrowser.removeCurrentTab();
+  Harness.finish();
+}
+// ----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/cookieRedirect.sjs
@@ -0,0 +1,24 @@
+// Simple script redirects to the query part of the uri if the cookie "xpinstall"
+// has the value "true", otherwise gives a 500 error.
+
+function handleRequest(request, response)
+{
+  let cookie = null;
+  if (request.hasHeader("Cookie")) {
+    let cookies = request.getHeader("Cookie").split(";");
+    for (let i = 0; i < cookies.length; i++) {
+      if (cookies[i].substring(0, 10) == "xpinstall=")
+        cookie = cookies[i].substring(10);
+    }
+  }
+
+  if (cookie == "true") {
+    response.setStatusLine(request.httpVersion, 302, "Found");
+    response.setHeader("Location", request.queryString);
+    response.write("See " + request.queryString);
+  }
+  else {
+    response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+    response.write("Invalid request");
+  }
+}
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/corrupt.xpi
@@ -0,0 +1,1 @@
+This is a corrupt zip file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..74ed2b817426d02c0bf2313ed0f2ac728d354800
GIT binary patch
literal 197
zc$^FHW@Zs#U}E545KK3>dZx8L$((_K!48O78AKUUa|=o;Lqj+jm>c(1dV+9h1q;Jh
zMiB<yLyepV6nIz;gx0Ol6HqzYE^xGe%j4ID{>uxbx7oxTI$dzZAv*8rh9mZ!{Z}V4
z`%HCp$Su6a7~svwB+iV>LS6<CU<BeNjUX1(dRB<_p%n}a0p6@^5H*Yp4nR5|tOEe#
C<TDch
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/enabled.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will test if InstallTrigger seems to be enabled -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function init() {
+  document.getElementById("enabled").textContent = InstallTrigger.enabled() ? "true" : "false";
+}
+</script>
+</head>
+<body onload="init()">
+<p>InstallTrigger tests</p>
+<p id="enabled"></p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/harness.js
@@ -0,0 +1,257 @@
+const TESTROOT = "http://example.com/browser/xpinstall/tests/";
+const TESTROOT2 = "http://example.org/browser/xpinstall/tests/";
+const CHROMEROOT = "chrome://mochikit/content/browser/xpinstall/tests/"
+const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
+const PROMPT_URL = "chrome://global/content/commonDialog.xul";
+const ADDONS_URL = "chrome://mozapps/content/extensions/extensions.xul";
+
+/**
+ * This is a test harness designed to handle responding to UI during the process
+ * of installing an XPI. A test can set callbacks to hear about specific parts
+ * of the sequence.
+ * Before use setup must be called and finish must be called afterwards.
+ */
+var Harness = {
+  // If set then the install is expected to be blocked by the whitelist. The
+  // callback should return true to continue with the install anyway.
+  installBlockedCallback: null,
+  // If set will be called in the event of authentication being needed to get
+  // the xpi. Should return a 2 element array of username and password, or
+  // null to not authenticate.
+  authenticationCallback: null,
+  // If set this will be called to allow checking the contents of the xpinstall
+  // confirmation dialog. The callback should return true to continue the install.
+  installConfirmCallback: null,
+  // If set will be called when downloading of an item has begun.
+  downloadStartedCallback: null,
+  // If set will be called during the download of an item.
+  downloadProgressCallback: null,
+  // If set will be called when downloading of an item has ended.
+  downloadEndedCallback: null,
+  // If set will be called when installation by the extension manager of an xpi
+  // item starts
+  installStartedCallback: null,
+  // If set will be called when each xpi item to be installed completes
+  // installation.
+  installEndedCallback: null,
+  // If set will be called when all triggered items are installed or the install
+  // is canceled.
+  installsCompletedCallback: null,
+
+  listenerIndex: null,
+
+  // Setup and tear down functions
+  setup: function() {
+    waitForExplicitFinish();
+
+    var os = Components.classes["@mozilla.org/observer-service;1"]
+                       .getService(Components.interfaces.nsIObserverService);
+    os.addObserver(this, "xpinstall-install-blocked", false);
+
+    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+                       .getService(Components.interfaces.nsIWindowMediator);
+    wm.addListener(this);
+
+    var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                       .getService(Components.interfaces.nsIExtensionManager);
+    this.listenerIndex = em.addInstallListener(this);
+  },
+
+  finish: function() {
+    var os = Components.classes["@mozilla.org/observer-service;1"]
+                       .getService(Components.interfaces.nsIObserverService);
+    os.removeObserver(this, "xpinstall-install-blocked");
+
+    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+                       .getService(Components.interfaces.nsIWindowMediator);
+    wm.removeListener(this);
+    var win = wm.getMostRecentWindow("Extension:Manager");
+    if (win)
+      win.close();
+
+    var em = Components.classes["@mozilla.org/extensions/manager;1"]
+                       .getService(Components.interfaces.nsIExtensionManager);
+    em.removeInstallListenerAt(this.listenerIndex);
+    finish();
+  },
+
+  endTest: function() {
+    // Defer the final notification to allow things like the InstallTrigger
+    // callback to complete
+    executeSoon(this.installsCompletedCallback);
+
+    this.installBlockedCallback = null;
+    this.authenticationCallback = null;
+    this.installConfirmCallback = null;
+    this.downloadStartedCallback = null;
+    this.downloadProgressCallback = null;
+    this.downloadEndedCallback = null;
+    this.installStartedCallback = null;
+    this.installEndedCallback = null;
+    this.installsCompletedCallback = null;
+  },
+
+  // Window open handling
+  windowLoad: function(window) {
+    // Allow any other load handlers to execute
+    var self = this;
+    executeSoon(function() { self.windowReady(window); } );
+  },
+
+  windowReady: function(window) {
+    if (window.document.location.href == XPINSTALL_URL) {
+      if (this.installBlockedCallback)
+        ok(false, "Should have been blocked by the whitelist");
+
+      // If there is a confirm callback then its return status determines whether
+      // to install the items or not. If not the test is over.
+      if (this.installConfirmCallback && !this.installConfirmCallback(window)) {
+        window.document.documentElement.cancelDialog();
+        this.endTest();
+      }
+      else {
+        // Initially the accept button is disabled on a countdown timer
+        var button = window.document.documentElement.getButton("accept");
+        button.disabled = false;
+        window.document.documentElement.acceptDialog();
+      }
+    }
+    else if (window.document.location.href == PROMPT_URL) {
+      switch (window.gCommonDialogParam.GetInt(3)) {
+        case 0: if (window.opener.document.location.href == ADDONS_URL) {
+                  // A prompt opened by the add-ons manager is liable to be an
+                  // xpinstall error, just close it, we'll see the error in
+                  // onInstallEnded anyway.
+                  window.document.documentElement.acceptDialog();
+                }
+                break;
+        case 2: if (window.gCommonDialogParam.GetInt(4) != 1) {
+                  // This is a login dialog, hopefully an authentication prompt
+                  // for the xpi.
+                  if (this.authenticationCallback) {
+                    var auth = this.authenticationCallback();
+                    if (auth && auth.length == 2) {
+                      window.document.getElementById("loginTextbox").value = auth[0];
+                      window.document.getElementById("password1Textbox").value = auth[1];
+                      window.document.documentElement.acceptDialog();
+                    }
+                    else {
+                      window.document.documentElement.cancelDialog();
+                    }
+                  }
+                  else {
+                    window.document.documentElement.cancelDialog();
+                  }
+                }
+                break;
+      }
+    }
+  },
+
+  // Install blocked handling
+
+  installBlocked: function() {
+    // The browser should have a notification box animating now
+    var notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+    var self = this;
+    this.waitForNotification(notificationBox, function() { self.notificationComplete() });
+  },
+
+  // This delays until a notification has finished animating it. It's a bit of a
+  // hack due to the timeout use but should be safe.
+  waitForNotification: function(notificationBox, callback) {
+    if (!notificationBox._timer) {
+      callback();
+      return;
+    }
+  
+    setTimeout(arguments.callee, 50, notificationBox, callback);
+  },
+
+  notificationComplete: function() {
+    ok(!!this.installBlockedCallback, "Shouldn't have been blocked by the whitelist");
+    var notification = gBrowser.getNotificationBox(gBrowser.selectedBrowser)
+                               .getNotificationWithValue("xpinstall");
+    if (this.installBlockedCallback && this.installBlockedCallback()) {
+      this.installBlockedCallback = null;
+      notification.firstChild.click();
+    }
+    else {
+      notification.close();
+      this.endTest();
+    }
+  },
+
+  // nsIWindowMediatorListener
+
+  onWindowTitleChange: function(window, title) {
+  },
+
+  onOpenWindow: function(window) {
+    var domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                          .getInterface(Components.interfaces.nsIDOMWindowInternal);
+    var self = this;
+    domwindow.addEventListener("load", function() {
+      self.windowLoad(domwindow);
+    }, false);
+  },
+
+  onCloseWindow: function(window) {
+  },
+
+  // nsIAddonInstallListener
+
+  onDownloadStarted: function(addon) {
+    if (this.downloadStartedCallback)
+      this.downloadStartedCallback(addon);
+  },
+
+  onDownloadProgress: function(addon, value, maxValue) {
+    if (this.downloadProgressCallback)
+      this.downloadProgressCallback(addon, value, maxValue);
+  },
+
+  onDownloadEnded: function(addon) {
+    if (this.downloadEndedCallback)
+      this.downloadEndedCallback(addon);
+  },
+
+  onInstallStarted: function(addon) {
+    if (this.installStartedCallback)
+      this.installStartedCallback(addon);
+  },
+
+  onCompatibilityCheckStarted: function(addon) {
+  },
+
+  onCompatibilityCheckEnded: function(addon, status) {
+  },
+
+  onInstallEnded: function(addon, status) {
+    if (this.installEndedCallback)
+      this.installEndedCallback(addon, status);
+  },
+
+  onInstallsCompleted: function() {
+    this.endTest();
+  },
+
+  // nsIObserver
+
+  observe: function(subject, topic, data) {
+    // Make sure the main UI has received the event too and so has started
+    // displaying the notification.
+    var self = this;
+    executeSoon(function() { self.installBlocked() });
+  },
+
+  QueryInterface: function(iid) {
+    if (iid.equals(Components.interfaces.nsIObserver) ||
+        iid.equals(Components.interfaces.nsIAddonInstallListener) ||
+        iid.equals(Components.interfaces.nsIWindowMediatorListener) ||
+        iid.equals(Components.interfaces.nsISupports))
+      return this;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/installchrome.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will accept a url as the uri query and pass it to InstallTrigger.installChrome -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function startInstall() {
+  InstallTrigger.installChrome(InstallTrigger.SKIN,
+                               decodeURIComponent(document.location.search.substring(1)),
+                               "test");
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/installtrigger.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will accept some json as the uri query and pass it to InstallTrigger.install -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function installCallback(url, status) {
+  document.getElementById("status").textContent = status;
+}
+
+function startInstall() {
+  var text = decodeURIComponent(document.location.search.substring(1));
+  var triggers = JSON.parse(text);
+  try {
+    document.getElementById("return").textContent = InstallTrigger.install(triggers, installCallback);
+  }
+  catch (e) {
+    document.getElementById("return").textContent = "exception";
+  }
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+<p id="return"></p>
+<p id="status"></p>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8c951881e5fbe10504eee8280dd9a3d995b5c881
GIT binary patch
literal 2260
zc$|He3pCW*9>@QT8D{e87(+*%;gHuf<6UIL$RqDcN9qhSG$EQ9M4`qbuY|~3YbJS2
zBCb~<#Wg*=6CsAgM55aaGL<^xTK8P;sk`^ud#%0J-k-Jicm2NK?Q9Pcf&+qp2%s8a
zWGX+6KQkc+0DT|;fCJLNA!~Pxj;$kBFM{ajO(0QpNo3q%cPId&OoZF6kHhDLfMo$V
zfIj7I-p(u^WcRNWW~UbtF;d(UG3x9>hm@4~F-So`#ps1D2iFQw$e4(A%C@{5QPI2{
zifEo}sqP=X-grTV;bffFA9yh1Dhh5WiT4pFyhgBL-JRMlBDOlZnueK3Nf49-<$hrw
zC9tPesU2wUb1?(47Z_Am8|+c;TzI)A>_jRC)>Ce+mP^ciny6L|OTDGGgJ+$qmIWbA
zHAAjL;<3#55{P<9iOW<oQ~ls^P?+li0xfxIxO@IaW>(&D!Lyd>m^T;jL3?UYNTU^$
ztbQoUz!3GmcY)iVnv>J2F*`Z4A{;QD0()fHMW3&|{oOcvoIyd7>yzf{DvSLWi}qf#
z@<cFM!POpGt13M-LQN7yCTQWM0d39C$jNjx$Tg#nzIJY4d$mvb_l(evA^C#q6%kK7
zI#|XWmxLJSix$=0_|7n+x~rzAs$hrgB&xlh*E@1sF?~g+)qSxLW(OUjR7Fy+4M*0m
z1$*Q1IGnFH?;>JHm&IF-+#Jf31V*tMH3ytSc+pGfwdAR6huZg^+S!30_tmqLv3<0d
z8cPMYxTPYV`R<Y3<G+6kSn&}ic8u?HeW?qVLmy{%dB1v?`5lcWr8-eplXmu&-l=xn
zb_89UaKD=p5z<)TLwixFn{W3~3ggriq!Wya{?uDx{(bcx@0aGFBPWKJiPbnM9~iPl
z^<swIJ{>pVCxTY@y(ImW1SGaO`W~3Z2Pnm);S8p2RCgaBgOW8sgjnrlnr!kDD>JBa
z>AYO7oXqen7Gly8#jN#I&<Ve*Zx{@BYL#Ac*^?fgulnJa5>CiOWzu5g<D3>tQ#afw
zeMBX@GRoi{Dm^`j>iAHxj>mCG8kC4x9o!$-vP;2yE>^M0(erD|$ocD5TKB-H(FruC
z>RWwvZO={9v3D)0PxIW8vso-oIOaG1e5ViHdmpE)?zR8&;6%^!npZbID|@L#riad{
z^C*Bb-z6|$y{)?h*wmn#UGkSTvNu`7d5gnDId2n2_~*a^|KqSg+!>-TflSd2^!=g3
z%u>DV+mW)^4_qylJwe|ic^1}k%AAS1R$p0159=owR0JwP%f7QxX<@=e?d>iR^)^!8
z&*c;=^2rB3iqToKr^KPWr?k5EorQ9*cYk$>AMh*?=zkOil)@7dKmY1tbjUYV`7)MM
zGxP9t**kG#DS3)#e;f8->g&3wco}82G&4O#3`Le^=cF#Y_j1|e?O)or@7Y_>^z3Xf
zi`h%Oclf>dXkacKoYDDU30t{16V(uL^6ImyR+G7~Z?`r2M!q)aON^WzxXYpW7nL!J
zagm`B^I#L{S(&Z+yk5TKD#q95EPqJ-D1pd7mgmY9CpW6a&iD^sG@~AyT1=K0b_GxN
zGGwWORMM_v#A}3WUini4egiLPZVi6yNOq*_5GCvWl6xmnFLzcO83GD7y92*t@Tr};
z#L!Uwy)k`%YzVp7Cs^yLxWzt+;|zx9G_;3US`3~~Gu9N?AFt7KaTNm@Q8|c1#9CdB
zY>rbAIS?#0wa+qb?unmRmK}K%yX0WToqzmH@xc9H2MT!Me1noWcK}ZfJD+5de^v3>
zV5XkjE9^;j)iQ~7r-$w_Az>UYpT{F{-1Rzz(1c+TYw1$NT}K8ZaQT7xx26-BJ_Cse
zVx}Sr)#iU%^dDEa4Mj87YxO1z&)N%z6^xkrlKpq1-=0+oT!Q{rQcb?3XcO-T27Ed3
z{9xb4my~#hOu+>N=#uaoQbzJ+jNBw;NXAL0HYB`b;*)0Oo9<$Y!$}Y3Lm}G>$USxm
zRaw#j?{C~{X`Cx<pak5NT^Twb?e})<6`PB-U1bkJZ~A@27yOf}lEisA1G`|;bnI)Q
z2O)DP#H{iIlesvDH!XawdW&cmaBtV5%`X3h(3{58H!`t1j)Lx>S}pAmQEBQ=&1T+}
zDRi*Jl}?fx>gbk=pyA6VliKDUg@x|9ed4*9R(o_p$7o083rUqI7lh+`13g=}MDXX5
zCjCxg=h9c=a9i)M5#uI+SzOnmiMe|g+%rBhI;ZybGNw<Krxtv+yY)a;U;ec~xhXnQ
z=-9wukmtXS&kFWOCkloWsE9^op(8`5Vr{(I&^Gn>Cl}MWMfz^d=eFg?eaimL<4d>T
zB?C9<hGtQPOVCe7BK?qL(z_cj+t&n~?F9s7KtKCQ#s12R;!ErEkN;KhMp+*|2LKzm
zpGD4paFW0RMjZrf4xqm=eil6&41s@N$$K+nOA!4Z+*YS<#!-cS@Ivy>X@4)$&9p%<
au(f2t8zs9z0Ylbzg84m^zx|-_`sz;??~~>L
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4c40ab818ed40583847a2f9478d7839c10031e8b
GIT binary patch
literal 2276
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bd7f78b7c7712c0aca18776e08541e87898da119
GIT binary patch
literal 2250
zc$|He3pCW*9>@P4Gnl+WW4e^bY1|ZJn(=<lD07f7LK4+5gDKCM@fs21B$f9ogmXCZ
z7;j~yoJw;|C5+c8l%cK$M{*dCRJgj<?Q&1uz1QAr?X~v)ti8YM_x*0iqavaTfG{8q
z{25_jTzHGZ;Rypkj|c!L0K0%=R?b*$8+)7%o$TvHqR<c&D#68B0uVuuMB0RH43`K|
zU{Oc`K>K-_vpJQ&^Lv*HucsH1vGRh`@f!R>yQHLqSKtEw>fsBWc23oj;8)^SNjvg$
zB_;E7X_EP{a>Q95FQPD0f9%r1vqTUx6{VmrOZ1i|y@v83UC*@e;x^g{P5msStcV0f
zLhy-yg2W%+$7n|j`lR%v2C%5kN07F>smMxA$mtX;q`T4zo=47mdIeqyNx2E%y<(LI
z&jwT8w1RWM2{=wd8CavN4F9H;qhakK67DnuMa#wwc1{1Bm7VV)9AcS{{pST@(4j^Y
z(qIV%(+xxE>7(9pX9T?|xw#MaPmaA^67wHPg0xw7mQ3HSS~t8n!lWUotgBN^wWa6Z
z7nx)pIR)kLf*afpeo*f&CN*BADF)5F&?{c`HE=K<4sy!qDOru^-`U_@xt<Z${(MhC
zPBs0BTRYEi9-kQR7>jA>B0dW@Xi7EqtAiY~m1*$oW7!KHV0((rY53s4oc0p1Y8^%6
zb|jLu8tg?R5(qwCE3wesotTMvr74s#1%%?=(d>5&UAee`UcL6_y4~$}r?jpI%$c(I
z*Kj?>@r{;yk6&6SS}{K`bYR4N&40;TjNCqA>huDkpoI3|cY3{in6-{ZQ&JolAFe8M
z%WE3!cetW&C*JL%(L?VPcoz@UB5v5u$zvVv1!)JPE-rJc&DTG)tjM(nxt<<eBsUP`
zy&=fo_Qht{nra^xdm?<~uBYsIRUw%j_CANliT<kbX#~CTA8noaoCG|$L;NK4@F8gW
z6}p_I{R;z>0iwP)S&~9uO(Hj&y3d~_`^ye480T3G#c)Onmk}>nEf0p*myz5;9fpnF
z(^@Gl__BpKt<Y>B+t1yxO73R0wmH0x$nA%&U3%bZ7&y(lH4U4v^|WC>+$|w9G43H>
zpaQLX<%CeFo^8=>R5K)pCWMvsJpaRa?Pl^^mA&Bk<A0Q!eJ*NTI5EY_(NmV1o^7_X
z@Ks*j0XS}$1Oj|%UrYjQX%bsa^3@rPEzWS<W-!V5iNv9eb0DGrF<1a0knBUE(hvbY
zUoq${7_$`5qiiG$H!C>EJ4(`ZyEX~=&Ci^J%4F3xm4x?_^r{0?B`Vf=DaGMpMQk>n
z&iYZ_t4~R#`Ucf{PO5}A=_f6*^0c^#tz4+|`oL#=LjS1(q29I_pj;vG%E!Oq4UYMw
zs9na*H@<y%uHp}AL-{?lQ@xLH)+w)>ViFY9;Av($Dp(q<*w#Tq%;a*#<DH+_I}e!@
z-0KJl=5e^>7MFL@!vT3Epp0kt7jU)j-^Mi4&!l$LJusRIU#r^RGxWJxS7zv3fBk&%
z`JxJDDIq$HJ`FP3HL18=pVy0VPEs4%glr7?Zv_zb+wvqQI~-?VloMt@X~sM@#*CHe
zcLtAfnJ|VhgYv^kawaL$^TwNg-~NH(<ITSOXnwTQb6WP@1?OjEoxDjcWT;4_S&c%C
z-ZGo9z}&Cqeka}Hr%-CCckscd(il@24<_@}xI{O(yc9H@W~eD-k+8oz_5&6?q;5@s
z{&eJWbn7K`@x#ILZ%i%Irk?mpW!qARaSL{4g6YQ{Du?d|+tENXQO&B-f_@?+JnE{^
zjZ~G6-?Ma-UgFO1>lP`zn(h*}Q5nO?J^3q?d1swxqG-~fxYe$5XuUm?8L)WYeC^)p
zEbsm+P;!<E3iasxImuheg65*PR;w(dnGSa0;9dicu554p#fcF0fCY*FO1kQyV@wd%
zs?yq!^W;XZ@7R!(97v@R{QVIW;--|zszKYN40ZB!h({_!Pn>XG@^R|uxxVu(x(#lg
z3)ADgFgEsoCq|wXU0uvU4JMu(rE`sz3!+~7kJ!ZB&uj3gGWEcP+(9r@qY%wc)H?XD
z|BlI&>th8B@Qw@*5L?`9tP9fiwUrHffBH2x%x%aD_RCrXp<~?c;G&Kq<0IHcc~nj#
zYEhjQCQgFiGGQwPACg_|5qrXp$4}w2DWo_^9NgssSIf|4HtSH{JhQ&lwfd1K+h7b6
z6_}GAhn`DiLjzq?lfxZy-7v?x#%jr;qxjrEBl{ZkK|(d{ikG|K>*)tt=qAI`YTXd3
z`V8meBF1zV2Qz*~o!g3R!`yiU3rKd2eeZH<@9v%qVJq0rOQjXW_G_bB^ow*%UEr<z
z4Pu{v?JBsei^aVf)nr&W9u*Q+6#3r&DfKlbO5?uF-~LTOo7L<R0RWr0?<LKDaI(M*
z7A^v8&7Hq6zLz+g455EtiLjNiEr0$GZo5IZ;^IWV@j~kNX@9NFt+W9Uu)R`2o0Ymr
T0fE2l1a0)NjqT=QUsnGDUel1Q
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..085efbbf7fdd8f9cf475e903b16552e614f9139c
GIT binary patch
literal 2938
zc$~#n4Qvx-7{2SZgC0&`zhr=Z8>ql+`?c3@tgPGGwPhVEEnPddBC>0H>yEY8u6Lu|
z{9|)Wg(xCHArLo(7z6|&Lc(9jP|z4PAPE7~NHh>*kl+tL8Ux<7Ti3EJ(HP@3U(@^E
z=X>Ahd*AnazS<XIVjK~kpJS!k(CtkRpTQ7B7&%BTVl_GSGRtDKqKo!>s0b&Iu%yc=
zMX-`@IxWI*#l*->GzWn_*k>8id3`l~w=>ZRqJbXlI6rk*grdqxP?{kreh?ECNDw$_
z1cVOsh#>3{Jq~-&O#_$+74c}8nFOE`9`9CW!>JjVOFStXcQG^voD|Ek%4sl9n8dRu
zTSHxRAV9|FX~wUd3m+3KST?Aq{S4&;wR#7r=Bd*yw3p;4pzq*XLlK(mgb(Fs!ZN5-
z5lRhIYc%fsOhTRV3K#y<*m*D~5j)f7SxI@h1ceCf?oZtoDnYRBml4?glBl~IIT3uN
zuDEeS=xxW~xfd>HtV2P47M8oW_^CguZyqcfq_@u2oL}oXztD2nliT~&@(afK?@j&T
z$*SIsv!}1keCFmJ*ZR*7ymo56nrrSY{{H=s@=jBCJx7*5JNUZyk(0#iN}}mIXH}Mc
z3R}Lh?E1yxQ&q*gTpqtux1(?G+<VY*>?sk3BIx#9C~Q^Q+*gD4-7%o3Sd59pH|{G2
z#?zgwlA(Hh33}n;jMZJ|?k&5jIGg{adR}F3**hOtPVf0`j^pIu%&eMCS@Nsa@3@20
z*F>*w{l02?;n4>Q&Yxik<eYS6#(q)gce-g?U331<faS`X<%WUXNN=uri{Zx)qZ8jM
zII_09GyfCEm35Z}45ioamOK~w_>2B&nb+PubL5xp${y6m_e-<TDl7iW?hpcu^zFcM
zI4zkADW*(qV>n~PB2i9GX{u5}?S`{PtYT%JIA?H~?Zgw09@^H59SRhFv?A-sjVsOf
zwTHNSHSO2kDmeE0ja8OobC^rNUORNQcX0Q!J5TMqM{Za<)4TP+r~3l8B2v8a>sOyX
zeeP&s)8(G<V9yVIO#e02o!<_>owwkmf3M`vKZs=qW^X)rxazAH3I9Z?d(E!1F`xSD
z=bhc{8=}{KUG14B+OnV_ynpivRrvVkL)zYwfpyKF{tQppld@p$D%Bz>k2F;l;#zRv
z+fK`=Z%4Gio+c@GE;}BUpa>$2G)W1P4Bf)lMILM!Q&Ox$c3A@zVd)T~1xh)@t0NR&
zC!fsFsg2i=3xxt;4m<pdkSkSk0vJe^_5xFNH84;dITx=E1%njBv07lSGwJJ1z-R-D
zZBAe^TAaXHW2pybOPxs$nrtqx)KXUm3=Wfi5wJMrIKG$+Qd&SWEJp?c@`$emH`0EA
zmp<SZTBS9}yR{lMpuR@0#Q7hxgk1|<-r!0u*5Y>PJ+)2kC4r_k!p%f=I$S^|QC%9v
zqQ+LYITVHt(r#QFYb#Z>TWgA4jcOg9p3653_MCP;kHW|vOP<-WQz;~h<T@hM|D4Oe
zn>brtLt9A+(bz;0L3*KPzS~+Fto7fYxRvdV*7nd+ySj^TguDC;YfC)7TDOCsIjH+L
zWivE*V@8cDK#^vzVu`QA8>5)kQDyT%(r$*P$f`0I<!-mxI%`#sCCpx1%#vERwoZ-q
z&Hl)Gw+KOmF^Ml*3aNOx3Km4u%2i$&3kJZdM5%N|e5r~mD{#ErVKf8Y!mzrcR*nm6
z6^dvyDvuKKP{glLmX(z$ph}@q$#}mqwv*w=n2cd(72!a5;tRD@Fl}uxp^tF~DhSmI
zDk!X;Rm6t_pxj8Y-UuD$cp8B8ggQ7~QAdQ)4z1D-?L#(vlKE9aq;e{#rF|7_Tw!Ig
zFs-WM1YJ&zR<1~Zk_(1*TfuKOZX14&1Cl6?jQA-|9}WiuxlY>CsSeL2nF!t7Msro;
z#UF+SX=dnrR3u2GOr!(J*zo+k1aM?xn2=%=qd0P$9V7sSG8r&OLJS8?j1O4(<7Ol|
z63ApDd1Cmst{A$!8ts<&%@%hYH^i%!7CFHNFIVu*Q^EHoD$2*k`@ff&6!{(9!uQz!
z{v-)~am5fM)!pcOV%Xgj<R^W;7)iY~B#B$&ejlUni&RSWsXo$fk8voCmdsRl{gN?u
l$d2!A^hr+j)-VA{Ki87PxlVYIWC$n;|5?TJxGojm{sP6AWg-9o
new file mode 100644
--- /dev/null
+++ b/xpinstall/tests/startsoftwareupdate.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<!-- This page will accept a url as the uri query and pass it to InstallTrigger.startSoftwareUpdate -->
+
+<head>
+<title>InstallTrigger tests</title>
+<script type="text/javascript">
+function startInstall() {
+  InstallTrigger.startSoftwareUpdate(decodeURIComponent(document.location.search.substring(1)));
+}
+</script>
+</head>
+<body onload="startInstall()">
+<p>InstallTrigger tests</p>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..51b00475a9641ea9d608874a3ab7679da3a4374b
GIT binary patch
literal 452
zc$^FHW@Zs#U}E54_?u^9#dA2DRf~~<A%uy6ftx{;Av3SIBrzvPuP7xgG=!6Z`R(;m
zPY@2RU}5;mD8f)0<mh+UKw!^vQMJ8_JN=layLm<}3F>4Pa^m=!CAB@`#_JRJCLMTP
zI9DyV<<;!;^s=<;=fu~BJ&BG`=N0*wGwVpVQP-#IS7-g;+M)E%WsUK%S<7acl?Nw(
z&oP^SK626~lcn2k1%78f5zUn+<o=|h$w0Vj$<=4!T1TCmO+4Styx{&-<y%?H6YYoF
z!mhnY7WkLQ7jcB0`=;kq&w>+E7tLzE!`U8JT5_uOTuP|ifs?i!UE8+CEYjcYy?ajS
z8plGZ?O!gpMb-1SG`8M($7*+F>!j#D=h``2kGE-L^!%E%W6!hu!CPCtPtFy8#3UhH
zdHeBp*^fM58gJ}(X{=0b>ML;8jErXXkXxv9p6O?ovig*H-&tb1i#~sHx%bD*`FKOi
zcd5uS!$os`<_Ejg2Y53wi8JF0X<h~p0CE|YG=f+t;m!&P_s|Lkh5&C?Hi$|_1~(v`
I0n)+%08F#C6#xJL