Bug 553169: Implement InstallTrigger, the XPI content handler and confirmation for web triggered installs. r=robstrong, r=dveditz
authorDave Townsend <dtownsend@oxymoronical.com>
Thu, 29 Apr 2010 13:11:23 -0700
changeset 42027 704046d2ddea4ff680c39e25f77d376056eadb4a
parent 42026 9d7f76acff02d63a701118e46d184af130ecd701
child 42028 bf6b3103e6a9e175bb5a6f3c81509e5d91f126b7
push idunknown
push userunknown
push dateunknown
reviewersrobstrong, dveditz
bugs553169
milestone1.9.3a5pre
Bug 553169: Implement InstallTrigger, the XPI content handler and confirmation for web triggered installs. r=robstrong, r=dveditz
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/installer/package-manifest.in
config/autoconf.mk.in
configure.in
modules/libpref/src/Makefile.in
netwerk/confvars.sh
toolkit/library/libxul-config.mk
toolkit/library/nsStaticXULComponents.cpp
toolkit/mozapps/extensions/amContentHandler.js
toolkit/mozapps/extensions/amIInstallTrigger.idl
toolkit/mozapps/extensions/amIWebInstallListener.idl
toolkit/mozapps/extensions/amInstallTrigger.cpp
toolkit/mozapps/extensions/amInstallTrigger.h
toolkit/mozapps/extensions/amWebInstallListener.js
toolkit/mozapps/xpinstall/content/xpinstallConfirm.js
toolkit/toolkit-makefiles.sh
toolkit/toolkit-tiers.mk
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -47,21 +47,16 @@
 #define UNIX_BUT_NOT_MAC
 #endif
 #endif
 
 pref("general.startup.browser", true);
 
 pref("browser.chromeURL","chrome://browser/content/");
 pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindow.xul");
-pref("xpinstall.dialog.confirm", "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul");
-pref("xpinstall.dialog.progress.skin", "chrome://mozapps/content/extensions/extensions.xul");
-pref("xpinstall.dialog.progress.chrome", "chrome://mozapps/content/extensions/extensions.xul");
-pref("xpinstall.dialog.progress.type.skin", "Extension:Manager");
-pref("xpinstall.dialog.progress.type.chrome", "Extension:Manager");
 
 // Developers can set this to |true| if they are constantly changing files in their 
 // extensions directory so that the extension system does not constantly think that
 // their extensions are being updated and thus reregistered every time the app is
 // started.
 pref("extensions.ignoreMTimeChanges", false);
 // Enables some extra Extension System Logging (can reduce performance)
 pref("extensions.logging.enabled", false);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -592,28 +592,34 @@ const gXPInstallObserver = {
     }
     return null;
   },
 
   observe: function (aSubject, aTopic, aData)
   {
     var brandBundle = document.getElementById("bundle_brand");
     switch (aTopic) {
-    case "xpinstall-install-blocked":
-      var installInfo = aSubject.QueryInterface(Components.interfaces.nsIXPIInstallInfo);
+    case "addon-install-blocked":
+      var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
       var win = installInfo.originatingWindow;
       var shell = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                      .getInterface(Components.interfaces.nsIWebNavigation)
                      .QueryInterface(Components.interfaces.nsIDocShell);
       var browser = this._getBrowser(shell);
       if (browser) {
         var host = installInfo.originatingURI.host;
         var brandShortName = brandBundle.getString("brandShortName");
         var notificationName, messageString, buttons;
-        if (!gPrefService.getBoolPref("xpinstall.enabled")) {
+        var enabled = true;
+        try {
+          enabled = gPrefService.getBoolPref("xpinstall.enabled");
+        }
+        catch (e) {
+        }
+        if (!enabled) {
           notificationName = "xpinstall-disabled"
           if (gPrefService.prefIsLocked("xpinstall.enabled")) {
             messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
             buttons = [];
           }
           else {
             messageString = gNavigatorBundle.getFormattedString("xpinstallDisabledMessage",
                                                                 [brandShortName, host]);
@@ -634,19 +640,17 @@ const gXPInstallObserver = {
           messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
                                                               [brandShortName, host]);
 
           buttons = [{
             label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
             accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
             popup: null,
             callback: function() {
-              var mgr = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
-                                  .createInstance(Components.interfaces.nsIXPInstallManager);
-              mgr.initManagerWithInstallInfo(installInfo);
+              installInfo.install();
               return false;
             }
           }];
         }
 
         var notificationBox = gBrowser.getNotificationBox(browser);
         if (!notificationBox.getNotificationWithValue(notificationName)) {
           const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
@@ -1178,17 +1182,17 @@ function prepareForStartup() {
                             OfflineApps, false);
 
   // setup simple gestures support
   gGestureSupport.init(true);
 }
 
 function delayedStartup(isLoadingBlank, mustLoadSidebar) {
   Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
-  Services.obs.addObserver(gXPInstallObserver, "xpinstall-install-blocked", false);
+  Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
 
   BrowserOffline.init();
   OfflineApps.init();
 
   gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
 
   // Ensure login manager is up and running.
   Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
@@ -1398,17 +1402,17 @@ function BrowserShutdown()
   try {
     FullZoom.destroy();
   }
   catch(ex) {
     Components.utils.reportError(ex);
   }
 
   Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
-  Services.obs.removeObserver(gXPInstallObserver, "xpinstall-install-blocked");
+  Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
   Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
 
   try {
     gBrowser.removeProgressListener(window.XULBrowserWindow);
     gBrowser.removeTabsProgressListener(window.TabsProgressListener);
   } catch (ex) {
   }
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -252,17 +252,16 @@
 @BINPATH@/components/xpcom_base.xpt
 @BINPATH@/components/xpcom_system.xpt
 @BINPATH@/components/xpcom_components.xpt
 @BINPATH@/components/xpcom_ds.xpt
 @BINPATH@/components/xpcom_io.xpt
 @BINPATH@/components/xpcom_threads.xpt
 @BINPATH@/components/xpcom_xpti.xpt
 @BINPATH@/components/xpconnect.xpt
-@BINPATH@/components/xpinstall.xpt
 @BINPATH@/components/xulapp.xpt
 @BINPATH@/components/xuldoc.xpt
 @BINPATH@/components/xultmpl.xpt
 @BINPATH@/components/zipwriter.xpt
 
 ; JavaScript components
 @BINPATH@/components/FeedProcessor.js
 @BINPATH@/components/FeedConverter.js
@@ -291,16 +290,18 @@
 #endif
 @BINPATH@/components/nsHelperAppDlg.js
 @BINPATH@/components/nsDownloadManagerUI.js
 @BINPATH@/components/nsProxyAutoConfig.js
 @BINPATH@/components/NetworkGeolocationProvider.js
 @BINPATH@/components/GPSDGeolocationProvider.js
 @BINPATH@/components/nsSidebar.js
 @BINPATH@/components/addonManager.js
+@BINPATH@/components/amContentHandler.js
+@BINPATH@/components/amWebInstallListener.js
 @BINPATH@/components/nsBlocklistService.js
 #ifdef MOZ_UPDATER
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.js
 @BINPATH@/components/pluginGlue.js
 @BINPATH@/components/nsSessionStartup.js
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -121,17 +121,16 @@ MOZ_MAIL_NEWS	= @MOZ_MAIL_NEWS@
 MOZ_PLAINTEXT_EDITOR_ONLY = @MOZ_PLAINTEXT_EDITOR_ONLY@
 BUILD_STATIC_LIBS = @BUILD_STATIC_LIBS@
 MOZ_ENABLE_LIBXUL = @MOZ_ENABLE_LIBXUL@
 ENABLE_TESTS	= @ENABLE_TESTS@
 IBMBIDI = @IBMBIDI@
 MOZ_UNIVERSALCHARDET = @MOZ_UNIVERSALCHARDET@
 ACCESSIBILITY = @ACCESSIBILITY@
 MOZ_VIEW_SOURCE = @MOZ_VIEW_SOURCE@
-MOZ_XPINSTALL = @MOZ_XPINSTALL@
 MOZ_JSLOADER  = @MOZ_JSLOADER@
 MOZ_USE_NATIVE_UCONV = @MOZ_USE_NATIVE_UCONV@
 MOZ_BRANDING_DIRECTORY = @MOZ_BRANDING_DIRECTORY@
 XPCOM_USE_LEA = @XPCOM_USE_LEA@
 MOZ_ENABLE_POSTSCRIPT = @MOZ_ENABLE_POSTSCRIPT@
 MOZ_INSTALLER	= @MOZ_INSTALLER@
 MOZ_UPDATER	= @MOZ_UPDATER@
 MOZ_UPDATE_PACKAGING	= @MOZ_UPDATE_PACKAGING@
--- a/configure.in
+++ b/configure.in
@@ -4899,17 +4899,16 @@ MOZ_STORAGE=1
 MOZ_SVG=1
 MOZ_TIMELINE=
 MOZ_TOOLKIT_SEARCH=1
 MOZ_UI_LOCALE=en-US
 MOZ_UNIVERSALCHARDET=1
 MOZ_URL_CLASSIFIER=
 MOZ_USE_NATIVE_UCONV=
 MOZ_VIEW_SOURCE=1
-MOZ_XPINSTALL=1
 MOZ_XSLT_STANDALONE=
 MOZ_XTF=1
 MOZ_XUL=1
 MOZ_ZIPWRITER=1
 NS_PRINTING=1
 NECKO_WIFI=1
 NECKO_COOKIES=1
 NECKO_DISK_CACHE=1
@@ -5712,27 +5711,16 @@ case "$target" in
     if test "$ac_cv_header_oleacc_idl" = "no"; then
         AC_MSG_ERROR([System header oleacc.idl is not available. See http://developer.mozilla.org/en/docs/oleacc.idl for details on fixing this problem.])
     fi
     ;;
 esac
 fi
 
 dnl ========================================================
-dnl xpinstall support on by default
-dnl ========================================================
-MOZ_ARG_DISABLE_BOOL(xpinstall,
-[  --disable-xpinstall     Disable xpinstall support],
-    MOZ_XPINSTALL=,
-    MOZ_XPINSTALL=1 )
-if test "$MOZ_XPINSTALL"; then
-    AC_DEFINE(MOZ_XPINSTALL)
-fi
-
-dnl ========================================================
 dnl xpcom js loader support on by default
 dnl ========================================================
 if test "$MOZ_JSLOADER"; then
     AC_DEFINE(MOZ_JSLOADER)
 fi
 
 dnl ========================================================
 dnl Disable printing
@@ -6314,20 +6302,16 @@ if test -n "$MOZ_INSTALLER" -a "$OS_ARCH
     # The Windows build for NSIS requires the iconv command line utility to
     # convert the charset of the locale files.
     MOZ_PATH_PROGS(HOST_ICONV, $HOST_ICONV "iconv", "")
     if test -z "$HOST_ICONV"; then
         AC_MSG_ERROR([To build the installer iconv is required in your path. To build without the installer reconfigure using --disable-installer.])
     fi
 fi
 
-# Automatically disable installer if xpinstall isn't built
-if test -z "$MOZ_XPINSTALL"; then
-    MOZ_INSTALLER=
-fi
 AC_SUBST(MOZ_INSTALLER)
 
 AC_MSG_CHECKING([for tar archiver])
 AC_CHECK_PROGS(TAR, gnutar gtar tar, "")
 if test -z "$TAR"; then
     AC_MSG_ERROR([no tar archiver found in \$PATH])
 fi
 AC_MSG_RESULT([$TAR])
@@ -8411,17 +8395,16 @@ AC_SUBST(MOZ_XIE_LIBS)
 AC_SUBST(MOZ_ENABLE_POSTSCRIPT)
 
 AC_SUBST(BUILD_STATIC_LIBS)
 AC_SUBST(MOZ_ENABLE_LIBXUL)
 AC_SUBST(ENABLE_TESTS)
 AC_SUBST(IBMBIDI)
 AC_SUBST(MOZ_UNIVERSALCHARDET)
 AC_SUBST(ACCESSIBILITY)
-AC_SUBST(MOZ_XPINSTALL)
 AC_SUBST(MOZ_VIEW_SOURCE)
 AC_SUBST(MOZ_SPELLCHECK)
 AC_SUBST(MOZ_USER_DIR)
 AC_SUBST(MOZ_CRASHREPORTER)
 
 AC_SUBST(ENABLE_STRIP)
 AC_SUBST(PKG_SKIP_STRIP)
 AC_SUBST(USE_ELF_DYNSTR_GC)
--- a/modules/libpref/src/Makefile.in
+++ b/modules/libpref/src/Makefile.in
@@ -80,17 +80,17 @@ EXTRA_DSO_LDOPTS = \
 include $(topsrcdir)/config/rules.mk
 
 GARBAGE		+= $(addprefix $(DIST)/bin/defaults/pref/, \
 			mailnews.js editor.js \
 			aix.js beos.js unix.js winpref.js os2prefs.js openvms.js photon.js)
 
 GARBAGE		+= greprefs.js
 
-GREPREF_FILES = $(topsrcdir)/xpinstall/public/xpinstall.js $(topsrcdir)/netwerk/base/public/security-prefs.js $(srcdir)/init/all.js
+GREPREF_FILES = $(topsrcdir)/netwerk/base/public/security-prefs.js $(srcdir)/init/all.js
 
 # Optimizer bug with GCC 3.2.2 on OS/2
 ifeq ($(OS_ARCH), OS2)
 nsPrefService.$(OBJ_SUFFIX): nsPrefService.cpp
 	$(REPORT_BUILD)
 	@$(MAKE_DEPS_AUTO_CXX)
 	$(ELOG) $(CCC) $(OUTOPTION)$@ -c $(COMPILE_CXXFLAGS:-O2=-O1) $(_VPATH_SRCS)
 endif
--- a/netwerk/confvars.sh
+++ b/netwerk/confvars.sh
@@ -34,9 +34,8 @@
 # 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 *****
 
 MOZ_APP_NAME=mozilla
 MOZ_EXTENSIONS_DEFAULT=""
 MOZ_APP_VERSION=$MOZILLA_VERSION
-MOZ_XPINSTALL=
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -118,22 +118,20 @@ STATIC_LIBS += \
 	gfxutils \
 	$(NULL)
 
 ifdef MOZ_IPC
 STATIC_LIBS += chromium_s
 endif
 
 ifndef WINCE
-ifdef MOZ_XPINSTALL
 STATIC_LIBS += \
 	mozreg_s \
 	$(NULL)
 endif
-endif
 
 # component libraries
 COMPONENT_LIBS += \
 	necko \
 	uconv \
 	i18n \
 	chardet \
 	jar$(VERSION_NUMBER) \
@@ -143,16 +141,17 @@ COMPONENT_LIBS += \
 	gklayout \
 	docshell \
 	embedcomponents \
 	webbrwsr \
 	nsappshell \
 	txmgr \
 	chrome \
 	commandlines \
+	extensions \
 	toolkitcomps \
 	pipboot \
 	pipnss \
 	appcomps \
 	$(NULL)
 
 ifdef BUILD_CTYPES
 COMPONENT_LIBS += \
@@ -190,23 +189,16 @@ endif
 endif
 
 ifneq (,$(filter windows,$(MOZ_WIDGET_TOOLKIT)))
 COMPONENT_LIBS += \
 	windowsproxy \
 	$(NULL)
 endif
 
-ifdef MOZ_XPINSTALL
-DEFINES += -DMOZ_XPINSTALL
-COMPONENT_LIBS += \
-	xpinstall \
-	$(NULL)
-endif
-
 ifdef MOZ_JSDEBUGGER
 DEFINES += -DMOZ_JSDEBUGGER
 COMPONENT_LIBS += \
 	jsd \
 	$(NULL)
 endif
 
 ifdef MOZ_PREF_EXTENSIONS
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -139,23 +139,16 @@
 
 #ifdef MOZ_PLUGINS
 #define PLUGINS_MODULES \
     MODULE(nsPluginModule)
 #else
 #define PLUGINS_MODULES
 #endif
 
-#ifdef MOZ_XPINSTALL
-#define XPINSTALL_MODULES \
-    MODULE(nsSoftwareUpdate)
-#else
-#define XPINSTALL_MODULES
-#endif
-
 #ifdef MOZ_JSDEBUGGER
 #define JSDEBUGGER_MODULES \
     MODULE(JavaScript_Debugger)
 #else
 #define JSDEBUGGER_MODULES
 #endif
 
 #if defined(MOZ_FILEVIEW) && defined(MOZ_XUL)
@@ -262,19 +255,19 @@
     MODULE(nsChromeModule)                   \
     MODULE(application)                      \
     MODULE(Apprunner)                        \
     MODULE(CommandLineModule)                \
     FILEVIEW_MODULE                          \
     STORAGE_MODULE                           \
     PLACES_MODULES                           \
     XULENABLED_MODULES                       \
+    MODULE(AddonsModule)                     \
     MODULE(nsToolkitCompsModule)             \
     XREMOTE_MODULES                          \
-    XPINSTALL_MODULES                        \
     JSDEBUGGER_MODULES                       \
     MODULE(BOOT)                             \
     MODULE(NSS)                              \
     SYSTEMPREF_MODULES                       \
     SPELLCHECK_MODULE                        \
     LAYOUT_DEBUG_MODULE                      \
     UNIXPROXY_MODULE                         \
     OSXPROXY_MODULE                          \
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/amContentHandler.js
@@ -0,0 +1,97 @@
+/*
+# ***** 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 the Extension Manager.
+#
+# The Initial Developer of the Original Code is
+# the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Dave Townsend <dtownsend@oxymoronical.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either 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 *****
+*/
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const XPI_CONTENT_TYPE = "application/x-xpinstall";
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function amContentHandler() {
+}
+
+amContentHandler.prototype = {
+  /**
+   * Handles a new request for an application/x-xpinstall file.
+   *
+   * @param   mimetype
+   *          The mimetype of the file
+   * @param   context
+   *          The context passed to nsIChannel.asyncOpen
+   */
+  handleContent: function XCH_handleContent(mimetype, context, request) {
+    if (mimetype != XPI_CONTENT_TYPE)
+      throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
+
+    if (!(request instanceof Ci.nsIChannel))
+      throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
+
+    let uri = request.URI;
+
+    let referer = null;
+    if (request instanceof Ci.nsIPropertyBag2) {
+      referer = request.getPropertyAsInterface("docshell.internalReferrer",
+                                               Ci.nsIURI);
+    }
+
+    let window = null;
+    let callbacks = request.notificationCallbacks ?
+                    request.notificationCallbacks :
+                    request.loadGroup.notificationCallbacks;
+    if (callbacks)
+      window = callbacks.getInterface(Ci.nsIDOMWindow);
+
+    request.cancel(Cr.NS_BINDING_ABORTED);
+
+    let manager = Cc["@mozilla.org/addons/integration;1"].
+                  getService(Ci.amIWebInstaller);
+    manager.installAddonsFromWebpage(mimetype, window, referer, [uri.spec],
+                                     [null], [null], [null], null, 1);
+  },
+
+  classDescription: "XPI Content Handler",
+  contractID: "@mozilla.org/uriloader/content-handler;1?type=" + XPI_CONTENT_TYPE,
+  classID: Components.ID("{7beb3ba8-6ec3-41b4-b67c-da89b8518922}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler])
+};
+
+function NSGetModule(compMgr, fileSpec)
+  XPCOMUtils.generateModule([amContentHandler]);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/amIInstallTrigger.idl
@@ -0,0 +1,126 @@
+/* ***** 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 the Extension Manager.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Townsend <dtownsend@oxymoronical.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+
+/**
+ * A callback function that webpages can implement to be notified when triggered
+ * installs complete.
+ */
+[scriptable, function, uuid(bb22f5c0-3ca1-48f6-873c-54e87987700f)]
+interface amIInstallCallback : nsISupports
+{
+  /**
+   * Called when an install completes or fails.
+   * @param   url
+   *          The url of the add-on being installed
+   * @param   status
+   *          0 if the install was successful or negative if not
+   */
+  void onInstallEnded(in AString url, in PRInt32 status);
+};
+
+/**
+ * The interface for the InstallTrigger object available to all websites.
+ */
+[scriptable, uuid(14b4e84d-001c-4403-a608-52a67ffaab40)]
+interface amIInstallTrigger : nsISupports
+{
+  /**
+   * Retained for backwards compatibility.
+   */
+  const PRUint32 SKIN    = 1;
+  const PRUint32 LOCALE  = 2;
+  const PRUint32 CONTENT = 4;
+  const PRUint32 PACKAGE = 7;
+
+  /**
+   * Tests if installation is enabled. This method is deprecated, please use
+   * "enabled" in the future.
+   */
+  boolean updateEnabled();
+
+  /**
+   * Tests if installation is enabled.
+   */
+  boolean enabled();
+
+  /**
+   * Starts a new installation of a set of add-ons.
+   *
+   * @param   args
+   *          The add-ons to install. This should be a JS object, each property
+   *          is the name of an add-on to be installed. The value of the
+   *          property should either be a string URL, or an object with the
+   *          following properties:
+   *           * URL for the add-on's URL
+   *           * IconURL for an icon for the add-on
+   *           * Hash for a hash of the add-on
+   * @param   callback
+   *          A callback to call as each installation succeeds or fails
+   * @return  true if the installations were successfully started
+   */
+  boolean install(in nsIVariant args, [optional] in amIInstallCallback callback);
+
+  /**
+   * Starts installing a new add-on. This method is deprecated, please use
+   * "install" in the future.
+   *
+   * @param   type
+   *          Unused, retained for backwards compatibility
+   * @param   url
+   *          The URL of the add-on
+   * @param   skin
+   *          Unused, retained for backwards compatibility
+   * @return  true if the installation was successfully started
+   */
+  boolean installChrome(in PRUint32 type, in AString url, in AString skin);
+
+  /**
+   * Starts installing a new add-on. This method is deprecated, please use
+   * "install" in the future.
+   *
+   * @param   url
+   *          The URL of the add-on
+   * @param   flags
+   *          Unused, retained for backwards compatibility
+   * @return  true if the installation was successfully started
+   */
+  boolean startSoftwareUpdate(in AString url, [optional] in PRInt32 flags);
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/amIWebInstallListener.idl
@@ -0,0 +1,105 @@
+/* ***** 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 the Extension Manager.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Townsend <dtownsend@oxymoronical.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIDOMWindowInternal;
+interface nsIURI;
+interface nsIVariant;
+
+/**
+ * amIWebInstallInfo is used by the default implementation of
+ * amIWebInstallListener to communicate with the running application and allow
+ * it to warn the user about blocked installs and start the installs running.
+ */
+[scriptable, uuid(8710e692-3989-4dc7-b607-40d57610ae75)]
+interface amIWebInstallInfo : nsISupports
+{
+  readonly attribute nsIDOMWindowInternal originatingWindow;
+  readonly attribute nsIURI originatingURI;
+  readonly attribute nsIVariant installs;
+
+  /**
+   * Starts all installs.
+   */
+  void install();
+};
+
+/**
+ * The registered amIWebInstallListener is used to notify about new installs
+ * triggered by websites. The default implementation displays a confirmation
+ * dialog when add-ons are ready to install and uses the observer service to
+ * notify when installations are blocked.
+ */
+[scriptable, uuid(ea806f3a-1b27-4d3d-9aee-88dec4c29fda)]
+interface amIWebInstallListener : nsISupports
+{
+  /**
+   * Called when the website is not allowed to directly prompt the user to
+   * install add-ons.
+   *
+   * @param   window
+   *          The window that triggered the installs
+   * @param   uri
+   *          The URI of the site that triggered the installs
+   * @param   installs
+   *          The AddonInstalls that were blocked
+   * @param   count
+   *          The number of AddonInstalls
+   * @return  true if the caller should start the installs
+   */
+  boolean onWebInstallBlocked(in nsIDOMWindowInternal window, in nsIURI uri,
+                              [array, size_is(count)] in nsIVariant installs,
+                              [optional] in PRUint32 count);
+
+  /**
+   * Called when a website wants to ask the user to install add-ons.
+   *
+   * @param   window
+   *          The window that triggered the installs
+   * @param   uri
+   *          The URI of the site that triggered the installs
+   * @param   installs
+   *          The AddonInstalls that were requested
+   * @param   count
+   *          The number of AddonInstalls
+   * @return  true if the caller should start the installs
+   */
+  boolean onWebInstallRequested(in nsIDOMWindowInternal window, in nsIURI uri,
+                                [array, size_is(count)] in nsIVariant installs,
+                                [optional] in PRUint32 count);
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/amInstallTrigger.cpp
@@ -0,0 +1,398 @@
+/* ***** 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 the Extension Manager.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Townsend <dtownsend@oxymoronical.com>
+ *
+ * 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 ***** */
+
+#include "amInstallTrigger.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIGenericFactory.h"
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsICategoryManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPIDLString.h"
+#include "nsIScriptNameSpaceManager.h"
+#include "nsDOMJSUtils.h"
+#include "nsIXPConnect.h"
+#include "nsContentUtils.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsNetUtil.h"
+#include "nsIScriptSecurityManager.h"
+
+//
+// Helper function for URI verification
+//
+static nsresult
+CheckLoadURIFromScript(JSContext *cx, const nsACString& uriStr)
+{
+  nsresult rv;
+  nsCOMPtr<nsIScriptSecurityManager> secman(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // get the script principal
+  nsCOMPtr<nsIPrincipal> principal;
+  rv = secman->GetSubjectPrincipal(getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!principal)
+    return NS_ERROR_FAILURE;
+
+  // convert the requested URL string to a URI
+  // Note that we use a null base URI here, since that's what we use when we
+  // actually convert the string into a URI to load.
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_NewURI(getter_AddRefs(uri), uriStr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // are we allowed to load this one?
+  rv = secman->CheckLoadURIWithPrincipal(principal, uri,
+                  nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL);
+  return rv;
+}
+
+NS_IMPL_ISUPPORTS1_CI(amInstallTrigger, amIInstallTrigger)
+
+amInstallTrigger::amInstallTrigger()
+{
+  mManager = do_GetService("@mozilla.org/addons/integration;1");
+}
+
+amInstallTrigger::~amInstallTrigger()
+{
+}
+
+JSContext*
+amInstallTrigger::GetJSContext()
+{
+  nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+
+  // get the xpconnect native call context
+  nsAXPCNativeCallContext *cc = nsnull;
+  xpc->GetCurrentNativeCallContext(&cc);
+  if (!cc)
+    return nsnull;
+
+  // Get JSContext of current call
+  JSContext* cx;
+  nsresult rv = cc->GetJSContext(&cx);
+  if (NS_FAILED(rv))
+    return nsnull;
+
+  return cx;
+}
+
+already_AddRefed<nsIDOMWindowInternal>
+amInstallTrigger::GetOriginatingWindow(JSContext* cx)
+{
+  nsIScriptGlobalObject *globalObject = nsnull;
+  nsIScriptContext *scriptContext = GetScriptContextFromJSContext(cx);
+  if (!scriptContext)
+    return nsnull;
+
+  globalObject = scriptContext->GetGlobalObject();
+  if (!globalObject)
+    return nsnull;
+
+  nsCOMPtr<nsIDOMWindowInternal> window = do_QueryInterface(globalObject);
+  return window.forget();
+}
+
+already_AddRefed<nsIURI>
+amInstallTrigger::GetOriginatingURI(nsIDOMWindowInternal* aWindow)
+{
+  if (!aWindow)
+    return nsnull;
+
+  nsCOMPtr<nsIDOMDocument> domdoc;
+  aWindow->GetDocument(getter_AddRefs(domdoc));
+  nsCOMPtr<nsIDocument> doc(do_QueryInterface(domdoc));
+  nsIURI* uri = doc->GetDocumentURI();
+  NS_IF_ADDREF(uri);
+  return uri;
+}
+
+/* boolean updateEnabled (); */
+NS_IMETHODIMP
+amInstallTrigger::UpdateEnabled(PRBool *_retval NS_OUTPARAM)
+{
+  return Enabled(_retval);
+}
+
+/* boolean enabled (); */
+NS_IMETHODIMP
+amInstallTrigger::Enabled(PRBool *_retval NS_OUTPARAM)
+{
+  nsCOMPtr<nsIDOMWindowInternal> window = GetOriginatingWindow(GetJSContext());
+  nsCOMPtr<nsIURI> referer = GetOriginatingURI(window);
+
+  return mManager->IsInstallEnabled(NS_LITERAL_STRING("application/x-xpinstall"), referer, _retval);
+}
+
+/* boolean install (in nsIVariant args, [optional] in amIInstallCallback callback); */
+NS_IMETHODIMP
+amInstallTrigger::Install(nsIVariant *args,
+                          amIInstallCallback *callback,
+                          PRBool *_retval NS_OUTPARAM)
+{
+  JSContext *cx = GetJSContext();
+  nsCOMPtr<nsIDOMWindowInternal> window = GetOriginatingWindow(cx);
+  nsCOMPtr<nsIURI> referer = GetOriginatingURI(window);
+
+  jsval params;
+  nsresult rv = args->GetAsJSVal(&params);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!JSVAL_IS_OBJECT(params) || !JSVAL_TO_OBJECT(params))
+    return NS_ERROR_INVALID_ARG;
+
+  JSIdArray *ida = JS_Enumerate(cx, JSVAL_TO_OBJECT(params));
+  if (!ida)
+    return NS_ERROR_FAILURE;
+
+  PRUint32 count = ida->length;
+
+  nsTArray<const PRUnichar*> names;
+  nsTArray<const PRUnichar*> uris;
+  nsTArray<const PRUnichar*> icons;
+  nsTArray<const PRUnichar*> hashes;
+
+  jsval v;
+  for (PRUint32 i = 0; i < count; i++ ) {
+    JS_IdToValue(cx, ida->vector[i], &v);
+    JSString *str = JS_ValueToString(cx, v);
+    if (!str) {
+      JS_DestroyIdArray(cx, ida);
+      return NS_ERROR_FAILURE;
+    }
+
+    const PRUnichar* name = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
+    const PRUnichar* uri = nsnull;
+    const PRUnichar* icon = nsnull;
+    const PRUnichar* hash = nsnull;
+
+    JS_GetUCProperty(cx, JSVAL_TO_OBJECT(params), reinterpret_cast<const jschar*>(name), nsCRT::strlen(name), &v);
+    if (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v)) {
+      jsval v2;
+      if (JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "URL", &v2) && !JSVAL_IS_VOID(v2)) {
+        JSString *str = JS_ValueToString(cx, v2);
+        if (!str) {
+          JS_DestroyIdArray(cx, ida);
+          return NS_ERROR_FAILURE;
+        }
+        uri = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
+      }
+
+      if (JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "IconURL", &v2) && !JSVAL_IS_VOID(v2)) {
+        JSString *str = JS_ValueToString(cx, v2);
+        if (!str) {
+          JS_DestroyIdArray(cx, ida);
+          return NS_ERROR_FAILURE;
+        }
+        icon = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
+      }
+
+      if (JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "Hash", &v2) && !JSVAL_IS_VOID(v2)) {
+        JSString *str = JS_ValueToString(cx, v2);
+        if (!str) {
+          JS_DestroyIdArray(cx, ida);
+          return NS_ERROR_FAILURE;
+        }
+        hash = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
+      }
+    }
+    else {
+      uri = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(JS_ValueToString(cx, v)));
+    }
+
+    if (!uri) {
+      JS_DestroyIdArray(cx, ida);
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCString tmpURI = NS_ConvertUTF16toUTF8(uri);
+    // Get relative URL to load
+    if (referer) {
+      rv = referer->Resolve(tmpURI, tmpURI);
+      if (NS_FAILED(rv)) {
+        JS_DestroyIdArray(cx, ida);
+        return rv;
+      }
+    }
+
+    rv = CheckLoadURIFromScript(cx, tmpURI);
+    if (NS_FAILED(rv)) {
+      JS_DestroyIdArray(cx, ida);
+      return rv;
+    }
+    uri = UTF8ToNewUnicode(tmpURI);
+
+    if (icon) {
+      nsCString tmpIcon = NS_ConvertUTF16toUTF8(icon);
+      if (referer) {
+        rv = referer->Resolve(tmpIcon, tmpIcon);
+        if (NS_FAILED(rv)) {
+          JS_DestroyIdArray(cx, ida);
+          return rv;
+        }
+      }
+
+      // If the page can't load the icon then just ignore it
+      rv = CheckLoadURIFromScript(cx, tmpIcon);
+      if (NS_FAILED(rv))
+        icon = nsnull;
+      else
+        icon = UTF8ToNewUnicode(tmpIcon);
+    }
+
+    names.AppendElement(name);
+    uris.AppendElement(uri);
+    icons.AppendElement(icon);
+    hashes.AppendElement(hash);
+  }
+
+  JS_DestroyIdArray(cx, ida);
+
+  rv = mManager->InstallAddonsFromWebpage(NS_LITERAL_STRING("application/x-xpinstall"),
+                                          window, referer, uris.Elements(),
+                                          hashes.Elements(), names.Elements(),
+                                          icons.Elements(), callback, count,
+                                          _retval);
+
+  for (PRUint32 i = 0; i < uris.Length(); i++) {
+    NS_Free(const_cast<PRUnichar*>(uris[i]));
+    if (icons[i])
+      NS_Free(const_cast<PRUnichar*>(icons[i]));
+  }
+
+  return rv;
+}
+
+/* boolean installChrome (in PRUint32 type, in AString url, in AString skin); */
+NS_IMETHODIMP
+amInstallTrigger::InstallChrome(PRUint32 type,
+                                const nsAString & url,
+                                const nsAString & skin,
+                                PRBool *_retval NS_OUTPARAM)
+{
+  return StartSoftwareUpdate(url, 0, _retval);
+}
+
+/* boolean startSoftwareUpdate (in AString url, [optional] in PRInt32 flags); */
+NS_IMETHODIMP
+amInstallTrigger::StartSoftwareUpdate(const nsAString & url,
+                                      PRInt32 flags,
+                                      PRBool *_retval NS_OUTPARAM)
+{
+  nsresult rv;
+
+  JSContext *cx = GetJSContext();
+  nsCOMPtr<nsIDOMWindowInternal> window = GetOriginatingWindow(cx);
+  nsCOMPtr<nsIURI> referer = GetOriginatingURI(window);
+
+  nsTArray<const PRUnichar*> names;
+  nsTArray<const PRUnichar*> uris;
+  nsTArray<const PRUnichar*> icons;
+  nsTArray<const PRUnichar*> hashes;
+
+  nsCString tmpURI = NS_ConvertUTF16toUTF8(url);
+  // Get relative URL to load
+  if (referer) {
+    rv = referer->Resolve(tmpURI, tmpURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = CheckLoadURIFromScript(cx, tmpURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  names.AppendElement((PRUnichar*)nsnull);
+  uris.AppendElement(UTF8ToNewUnicode(tmpURI));
+  icons.AppendElement((PRUnichar*)nsnull);
+  hashes.AppendElement((PRUnichar*)nsnull);
+
+  rv = mManager->InstallAddonsFromWebpage(NS_LITERAL_STRING("application/x-xpinstall"),
+                                          window, referer, uris.Elements(),
+                                          hashes.Elements(), names.Elements(),
+                                          icons.Elements(), nsnull, 1, _retval);
+
+  NS_Free(const_cast<PRUnichar*>(uris[0]));
+  return rv;
+}
+
+NS_DECL_CLASSINFO(amInstallTrigger)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(amInstallTrigger)
+
+static NS_METHOD
+RegisterInstallTrigger(nsIComponentManager *aCompMgr,
+                       nsIFile *aPath,
+                       const char *registryLocation,
+                       const char *componentType,
+                       const nsModuleComponentInfo *info)
+{
+  nsresult rv = NS_OK;
+
+  nsCOMPtr<nsICategoryManager> catman = 
+    do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsXPIDLCString previous;
+  rv = catman->AddCategoryEntry(JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY,
+                                "InstallTrigger",
+                                AM_INSTALLTRIGGER_CONTRACTID,
+                                PR_TRUE, PR_TRUE, getter_Copies(previous));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+
+// The list of components we register
+static const nsModuleComponentInfo components[] =
+{
+    { "InstallTrigger Component",
+       AM_InstallTrigger_CID,
+       AM_INSTALLTRIGGER_CONTRACTID,
+       amInstallTriggerConstructor,
+       RegisterInstallTrigger,
+       nsnull, // unregister
+       nsnull, // factory destructor
+       NS_CI_INTERFACE_GETTER_NAME(amInstallTrigger),
+       nsnull, // language helper
+       &NS_CLASSINFO_NAME(amInstallTrigger),
+       nsIClassInfo::DOM_OBJECT // flags
+    }
+};
+
+NS_IMPL_NSGETMODULE(AddonsModule, components)
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/amInstallTrigger.h
@@ -0,0 +1,65 @@
+/* ***** 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 the Extension Manager.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Townsend <dtownsend@oxymoronical.com>
+ *
+ * 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 ***** */
+
+#include "jscntxt.h"
+#include "amIInstallTrigger.h"
+#include "nsIDOMWindowInternal.h"
+#include "nsIURI.h"
+#include "amIWebInstaller.h"
+#include "nsCOMPtr.h"
+
+#define AM_InstallTrigger_CID \
+ {0xfcfcdf1e, 0xe9ef, 0x4141, {0x90, 0xd8, 0xd5, 0xff, 0x84, 0xc1, 0x7c, 0xce}}
+#define AM_INSTALLTRIGGER_CONTRACTID "@mozilla.org/addons/installtrigger;1"
+
+class amInstallTrigger : public amIInstallTrigger
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_AMIINSTALLTRIGGER
+
+  amInstallTrigger();
+
+private:
+  ~amInstallTrigger();
+
+  JSContext* GetJSContext();
+  already_AddRefed<nsIDOMWindowInternal> GetOriginatingWindow(JSContext* cx);
+  already_AddRefed<nsIURI> GetOriginatingURI(nsIDOMWindowInternal* aWindow);
+
+  nsCOMPtr<amIWebInstaller> mManager;
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/amWebInstallListener.js
@@ -0,0 +1,221 @@
+/*
+# ***** 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 the Extension Manager.
+#
+# The Initial Developer of the Original Code is
+# the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Dave Townsend <dtownsend@oxymoronical.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either 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 *****
+*/
+
+/**
+ * This is a default implementation of amIWebInstallListener that should work
+ * for most applications but can be overriden. It notifies the observer service
+ * about blocked installs. For normal installs it pops up an install
+ * confirmation when all the add-ons have been downloaded.
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+/**
+ * Logs a warning message
+ *
+ * @param   str
+ *          The string to log
+ */
+function WARN(str) {
+  dump("*** addons.weblistener: " + str + "\n");
+}
+
+/**
+ * Creates a new installer to monitor downloads and prompt to install when
+ * ready
+ *
+ * @param    window
+ *           The window that started the installations
+ * @param    url
+ *           The URL that started the installations
+ * @installs installs
+ *           An array of AddonInstalls
+ */
+function Installer(window, url, installs) {
+  this.window = window;
+  this.url = url;
+  this.downloads = installs;
+  this.installs = [];
+
+  this.bundle = Cc["@mozilla.org/intl/stringbundle;1"].
+                getService(Ci.nsIStringBundleService).
+                createBundle("chrome://mozapps/locale/extensions/extensions.properties");
+
+  this.count = installs.length;
+  installs.forEach(function(install) {
+    install.addListener(this);
+
+    // Might already be a local file
+    if (install.state == AddonManager.STATE_DOWNLOADED)
+      this.onDownloadEnded(install);
+    else
+      install.install();
+  }, this);
+}
+
+Installer.prototype = {
+  window: null,
+  downloads: null,
+  installs: null,
+  count: null,
+
+  /**
+   * Checks if all downloads are now complete and if so prompts to install.
+   */
+  checkAllDownloaded: function() {
+    if (--this.count > 0)
+      return;
+
+    // Maybe none of the downloads were sucessful
+    if (this.installs.length == 0)
+      return;
+
+    let args = {};
+    args.url = this.url;
+    args.installs = this.installs;
+    args.wrappedJSObject = args;
+
+    Services.ww.openWindow(this.window, "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul",
+                           null, "chrome,modal,centerscreen", args);
+  },
+
+  onDownloadCancelled: function(install) {
+    install.removeListener(this);
+
+    this.checkAllDownloaded();
+  },
+
+  onDownloadFailed: function(install, error) {
+    install.removeListener(this);
+
+    // TODO show some better error
+    Services.prompt.alert(this.window, "Download Failed", "The download of " + install.sourceURL + " failed: " + error);
+    this.checkAllDownloaded();
+  },
+
+  onDownloadEnded: function(install) {
+    install.removeListener(this);
+
+    if (install.addon.appDisabled) {
+      // App disabled items are not compatible
+      install.cancel();
+
+      let title = null;
+      let text = null;
+
+      let problems = "";
+      if (!install.addon.isCompatible)
+        problems += "incompatible, ";
+      if (!install.addon.providesUpdatesSecurely)
+        problems += "insecure updates, ";
+      if (install.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+        problems += "blocklisted, ";
+        title = bundle.GetStringFromName("blocklistedInstallTitle2");
+        text = this.bundle.formatStringFromName("blocklistedInstallMsg2",
+                                                [install.addon.name], 1);
+      }
+      problems = problems.substring(0, problems.length - 2);
+      WARN("Not installing " + install.addon.id + " because of the following: " + problems);
+
+      title = this.bundle.GetStringFromName("incompatibleTitle2", 1);
+      text = this.bundle.formatStringFromName("incompatibleMessage",
+                                              [install.addon.name,
+                                               install.addon.version,
+                                               Services.appinfo.name,
+                                               Services.appinfo.version], 4);
+      Services.prompt.alert(this.window, title, text);
+    }
+    else {
+      this.installs.push(install);
+    }
+
+    this.checkAllDownloaded();
+    return false;
+  },
+};
+
+function extWebInstallListener() {
+}
+
+extWebInstallListener.prototype = {
+  /**
+   * @see amIWebInstallListener.idl
+   */
+  onWebInstallBlocked: function(window, uri, installs) {
+    let info = {
+      originatingWindow: window,
+      originatingURI: uri,
+      installs: installs,
+
+      install: function() {
+        dump("Start installs\n");
+        new Installer(this.originatingWindow, this.originatingURI, this.installs);
+      },
+
+      QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
+    };
+    Services.obs.notifyObservers(info, "addon-install-blocked", null);
+
+    return false;
+  },
+
+  /**
+   * @see amIWebInstallListener.idl
+   */
+  onWebInstallRequested: function(window, uri, installs) {
+    new Installer(window, uri, installs);
+
+    // We start the installs ourself
+    return false;
+  },
+
+  classDescription: "XPI Install Handler",
+  contractID: "@mozilla.org/addons/web-install-listener;1",
+  classID: Components.ID("{0f38e086-89a3-40a5-8ffc-9b694de1d04a}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallListener])
+};
+
+function NSGetModule(compMgr, fileSpec)
+  XPCOMUtils.generateModule([extWebInstallListener]);
--- a/toolkit/mozapps/xpinstall/content/xpinstallConfirm.js
+++ b/toolkit/mozapps/xpinstall/content/xpinstallConfirm.js
@@ -30,70 +30,63 @@
 # 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 *****
 
-var XPInstallConfirm = 
-{ 
-  _param: null
-};
+var args
 
+var XPInstallConfirm = {};
 
 XPInstallConfirm.init = function ()
 {
   var _installCountdown;
   var _installCountdownInterval;
   var _focused;
   var _timeout;
 
   var bundle = document.getElementById("xpinstallConfirmStrings");
-  
-  this._param = window.arguments[0].QueryInterface(Components.interfaces.nsIDialogParamBlock);
-  if (!this._param)
-    close();
-  
-  this._param.SetInt(0, 1); // The default return value is "Cancel"
-  
+
+  args = window.arguments[0].wrappedJSObject;
+
   var _installCountdownLength = 5;
   try {
     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(Components.interfaces.nsIPrefBranch);
     var delay_in_milliseconds = prefs.getIntPref("security.dialog_enable_delay");
     _installCountdownLength = Math.round(delay_in_milliseconds / 500);
   } catch (ex) { }
   
   var itemList = document.getElementById("itemList");
   
-  var numItemsToInstall = this._param.GetInt(1);
+  var numItemsToInstall = args.installs.length;
   for (var i = 0; i < numItemsToInstall; ++i) {
     var installItem = document.createElement("installitem");
     itemList.appendChild(installItem);
 
-    installItem.name = this._param.GetString(i);
-    installItem.url = this._param.GetString(++i);
-    var icon = this._param.GetString(++i);
-    if (icon != "")
+    installItem.name = args.installs[i].addon.name;
+    installItem.url = args.installs[i].sourceURL;
+    var icon = args.installs[i].iconURL;
+    if (icon != null)
       installItem.icon = icon;
-    var cert = this._param.GetString(++i);
-    if (cert)
-      installItem.cert = bundle.getFormattedString("signed", [cert]);
-    else
+    if (args.installs[i].certName) {
+      installItem.cert = bundle.getFormattedString("signed", [args.installs[i].certName]);
+    }
+    else {
       installItem.cert = bundle.getString("unverified");
-    installItem.signed = cert ? "true" : "false";
+    }
+    installItem.signed = args.installs[i].certName ? "true" : "false";
   }
   
   var introString = bundle.getString("itemWarnIntroSingle");
   if (numItemsToInstall > 4)
     introString = bundle.getFormattedString("itemWarnIntroMultiple", [numItemsToInstall / 4]);
-  if (this._param.objects && this._param.objects.length)
-    introString = this._param.objects.queryElementAt(0, Components.interfaces.nsISupportsString).data;
   var textNode = document.createTextNode(introString);
   var introNode = document.getElementById("itemWarningIntro");
   while (introNode.hasChildNodes())
     introNode.removeChild(introNode.firstChild);
   introNode.appendChild(textNode);
   
   var okButton = document.documentElement.getButton("accept");
   okButton.focus();
@@ -168,17 +161,21 @@ XPInstallConfirm.init = function ()
     setWidgetsAfterFocus();
   }
   else
     okButton.label = bundle.getString("installButtonLabel");
 }
 
 XPInstallConfirm.onOK = function ()
 {
-  this._param.SetInt(0, 0);
+  args.installs.forEach(function(install) {
+    install.install();
+  });
   return true;
 }
 
 XPInstallConfirm.onCancel = function ()
 {
-  this._param.SetInt(0, 1);
+  args.installs.forEach(function(install) {
+    install.cancel();
+  });
   return true;
 }
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -548,22 +548,16 @@ MAKEFILES_xpcom="
 MAKEFILES_xpcom_tests="
   xpcom/tests/Makefile
   xpcom/tests/dynamic/Makefile
   xpcom/tests/services/Makefile
   xpcom/tests/windows/Makefile
   xpcom/tests/static-checker/Makefile
 "
 
-MAKEFILES_xpinstall="
-  xpinstall/Makefile
-  xpinstall/public/Makefile
-  xpinstall/src/Makefile
-"
-
 MAKEFILES_xpfe="
   widget/src/xremoteclient/Makefile
   toolkit/components/remote/Makefile
   xpfe/components/Makefile
   xpfe/components/directory/Makefile
   xpfe/components/autocomplete/Makefile
   xpfe/components/autocomplete/public/Makefile
   xpfe/components/autocomplete/src/Makefile
@@ -835,17 +829,16 @@ add_makefiles "
   $MAKEFILES_caps
   $MAKEFILES_chrome
   $MAKEFILES_view
   $MAKEFILES_docshell
   $MAKEFILES_webshell
   $MAKEFILES_widget
   $MAKEFILES_xpcom
   $MAKEFILES_xpcom_tests
-  $MAKEFILES_xpinstall
   $MAKEFILES_xpfe
   $MAKEFILES_embedding
   $MAKEFILES_xulapp
   $MAKEFILES_libpr0n
   $MAKEFILES_accessible
   $MAKEFILES_zlib
   $MAKEFILES_libmar
   $MAKEFILES_lib7z
@@ -998,17 +991,16 @@ if [ "$ENABLE_TESTS" ]; then
     toolkit/mozapps/plugins/tests/Makefile
     toolkit/mozapps/update/test/Makefile
     toolkit/spatial-navigation/tests/Makefile
     toolkit/xre/test/Makefile
     uriloader/exthandler/tests/mochitest/Makefile
     widget/tests/Makefile
     xpcom/sample/program/Makefile
     xpcom/tests/external/Makefile
-    xpinstall/tests/Makefile
   "
 fi
 
 if [ "$MOZ_ZIPWRITER" ]; then
   add_makefiles "
     modules/libjar/zipwriter/Makefile
     modules/libjar/zipwriter/public/Makefile
     modules/libjar/zipwriter/src/Makefile
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -54,20 +54,18 @@ endif
 
 tier_platform_dirs += xpcom
 
 ifndef MOZ_NATIVE_ZLIB
 tier_platform_dirs += modules/zlib
 endif
 
 ifndef WINCE
-ifneq (,$(MOZ_XPINSTALL))
 tier_platform_dirs += modules/libreg
 endif
-endif
 
 tier_platform_dirs += \
 		modules/libpref \
 		intl \
 		netwerk \
 		$(NULL)
 
 ifdef MOZ_AUTH_EXTENSION
@@ -204,20 +202,16 @@ tier_platform_dirs += widget/src/xremote
 endif
 
 ifdef MOZ_SPELLCHECK
 tier_platform_dirs	+= extensions/spellcheck
 endif
 
 tier_platform_dirs	+= toolkit
 
-ifdef MOZ_XPINSTALL
-tier_platform_dirs     +=  xpinstall
-endif
-
 ifdef MOZ_PSM
 tier_platform_dirs	+= security/manager
 else
 tier_platform_dirs	+= security/manager/boot/public security/manager/ssl/public
 endif
 
 ifdef MOZ_PREF_EXTENSIONS
 tier_platform_dirs += extensions/pref