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 41552 d2a2b786fcc9ce88ed1fa451099b5bcaf5ada1d7
parent 41551 7ebf7aac89740876d859bfa1fbcc112fd7f1dd30
child 41553 d56ea9503e6872c3b10a3f2c69e7e694c2927c01
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrobstrong, dveditz
bugs553169
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
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);
@@ -1396,17 +1400,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
@@ -4917,17 +4917,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
@@ -5736,27 +5735,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
@@ -6338,20 +6326,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])
@@ -8421,17 +8405,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 += \
 	xpconnect \
 	necko \
 	uconv \
 	i18n \
 	chardet \
@@ -145,16 +143,17 @@ COMPONENT_LIBS += \
 	gklayout \
 	docshell \
 	embedcomponents \
 	webbrwsr \
 	nsappshell \
 	txmgr \
 	chrome \
 	commandlines \
+	extensions \
 	toolkitcomps \
 	pipboot \
 	pipnss \
 	appcomps \
 	$(NULL)
 
 ifdef BUILD_CTYPES
 COMPONENT_LIBS += \
@@ -192,23 +191,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)
@@ -264,19 +257,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
@@ -549,22 +549,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
@@ -836,17 +830,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
@@ -999,17 +992,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
@@ -209,20 +207,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