Bug 550936: Make InstallTrigger support cross-process communication, r=Mossop
☠☠ backed out by 32a6a3231e50 ☠ ☠
authorAlon Zakai <azakai@mozilla.com>
Mon, 02 Aug 2010 23:52:59 -0400
changeset 48763 bdb98838d6f6b790d45748a817d70526f0675fe7
parent 48762 3b59889221bcb6d8ffc27500070dd26e1f08691b
child 48764 1d2eda34572a31f6e6adbfada36d8d70b493737d
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)
reviewersMossop
bugs550936
milestone2.0b3pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 550936: Make InstallTrigger support cross-process communication, r=Mossop
toolkit/library/libxul-config.mk
toolkit/library/nsStaticXULComponents.cpp
toolkit/mozapps/extensions/Makefile.in
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/addonManager.js
toolkit/mozapps/extensions/amInstallTrigger.cpp
toolkit/mozapps/extensions/amInstallTrigger.h
toolkit/mozapps/extensions/content/extensions-content.js
toolkit/mozapps/extensions/jar.mn
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -144,17 +144,16 @@ COMPONENT_LIBS += \
 	imglib2 \
 	gklayout \
 	docshell \
 	embedcomponents \
 	webbrwsr \
 	nsappshell \
 	txmgr \
 	commandlines \
-	extensions \
 	toolkitcomps \
 	pipboot \
 	pipnss \
 	appcomps \
 	$(NULL)
 
 ifdef MOZ_IPC
 COMPONENT_LIBS +=  jetpack_s
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -260,17 +260,16 @@
     COMPOSER_MODULE                          \
     MODULE(application)                      \
     MODULE(Apprunner)                        \
     MODULE(CommandLineModule)                \
     FILEVIEW_MODULE                          \
     STORAGE_MODULE                           \
     PLACES_MODULES                           \
     XULENABLED_MODULES                       \
-    MODULE(AddonsModule)                     \
     MODULE(nsToolkitCompsModule)             \
     XREMOTE_MODULES                          \
     JSDEBUGGER_MODULES                       \
     MODULE(BOOT)                             \
     MODULE(NSS)                              \
     SYSTEMPREF_MODULES                       \
     SPELLCHECK_MODULE                        \
     LAYOUT_DEBUG_MODULE                      \
--- a/toolkit/mozapps/extensions/Makefile.in
+++ b/toolkit/mozapps/extensions/Makefile.in
@@ -36,35 +36,25 @@
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MODULE         = extensions
-LIBRARY_NAME   = extensions
-SHORT_LIBNAME  = extnsion
-IS_COMPONENT   = 1
-MODULE_NAME    = AddonsModule
-GRE_MODULE     = 1
-LIBXUL_LIBRARY = 1
-EXPORT_LIBRARY = 1
+MODULE       = extensions
+XPIDL_MODULE = extensions
 
 XPIDLSRCS = \
   amIInstallTrigger.idl \
   amIWebInstallListener.idl \
   amIWebInstaller.idl \
   $(NULL)
 
-CPPSRCS		= \
-  amInstallTrigger.cpp \
-  $(NULL)
-
 EXTRA_PP_COMPONENTS = \
   extensions.manifest \
   nsBlocklistService.js \
   addonManager.js \
   amContentHandler.js \
   amWebInstallListener.js \
   $(NULL)
 
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -3832,16 +3832,59 @@ var XPIDatabase = {
     fos.write(text, text.length);
     FileUtils.closeSafeFileOutputStream(fos);
 
     Services.prefs.setCharPref(PREF_EM_ENABLED_ADDONS, enabledAddons.join(","));
   }
 };
 
 /**
+ * Handles callbacks for HTTP channels of XPI downloads. We support
+ * prompting for auth dialogs and, optionally, to ignore bad certs.
+ *
+ * @param  aWindow
+ *         An optional DOM Element related to the request
+ * @param  aNeedBadCertHandling
+ *         Whether we should handle bad certs or not
+ */
+function XPINotificationCallbacks(aWindow, aNeedBadCertHandling) {
+  this.window = aWindow;
+
+  // Verify that we don't end up on an insecure channel if we haven't got a
+  // hash to verify with (see bug 537761 for discussion)
+  this.needBadCertHandling = aNeedBadCertHandling;
+
+  if (this.needBadCertHandling) {
+    Components.utils.import("resource://gre/modules/CertUtils.jsm");
+    this.badCertHandler = new BadCertHandler();
+  }
+}
+
+XPINotificationCallbacks.prototype = {
+  QueryInterface: function(iid) {
+    if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIInterfaceRequestor))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  getInterface: function(iid) {
+    if (iid.equals(Components.interfaces.nsIAuthPrompt2)) {
+      var factory = Cc["@mozilla.org/prompter;1"].
+                    getService(Ci.nsIPromptFactory);
+      return factory.getPrompt(this.window, Ci.nsIAuthPrompt);
+    }
+
+    if (this.needBadCertHandling)
+      return this.badCertHandler.getInterface(iid);
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+};
+
+/**
  * Instantiates an AddonInstall and passes the new object to a callback when
  * it is complete.
  *
  * @param  aCallback
  *         The callback to pass the AddonInstall to
  * @param  aInstallLocation
  *         The install location the add-on will be installed into
  * @param  aUrl
@@ -4302,18 +4345,16 @@ AddonInstall.prototype = {
     // Network is going offline
     this.cancel();
   },
 
   /**
    * Starts downloading the add-on's XPI file.
    */
   startDownload: function AI_startDownload() {
-    Components.utils.import("resource://gre/modules/CertUtils.jsm");
-
     this.state = AddonManager.STATE_DOWNLOADING;
     if (!AddonManagerPrivate.callInstallListeners("onDownloadStarted",
                                                   this.listeners, this.wrapper)) {
       this.state = AddonManager.STATE_CANCELLED;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onDownloadCancelled",
                                                this.listeners, this.wrapper)
       return;
@@ -4361,23 +4402,20 @@ AddonInstall.prototype = {
       return;
     }
 
     let listener = Cc["@mozilla.org/network/stream-listener-tee;1"].
                    createInstance(Ci.nsIStreamListenerTee);
     listener.init(this, this.stream);
     try {
       this.channel = NetUtil.newChannel(this.sourceURI);
-      if (this.loadGroup)
-        this.channel.loadGroup = this.loadGroup;
-
-      // Verify that we don't end up on an insecure channel if we haven't got a
-      // hash to verify with (see bug 537761 for discussion)
-      if (!this.hash)
-        this.channel.notificationCallbacks = new BadCertHandler();
+      this.channel.notificationCallbacks =
+        new XPINotificationCallbacks(this.window, !this.hash);
+      this.channel.QueryInterface(Ci.nsIHttpChannelInternal)
+                  .forceAllowThirdPartyCookie = true;
       this.channel.asyncOpen(listener, null);
 
       Services.obs.addObserver(this, "network:offline-about-to-go-offline", false);
     }
     catch (e) {
       WARN("Failed to start download: " + e);
       this.state = AddonManager.STATE_DOWNLOAD_FAILED;
       this.error = AddonManager.ERROR_NETWORK_FAILURE;
@@ -4403,21 +4441,16 @@ AddonInstall.prototype = {
   },
 
   /**
    * This is the first chance to get at real headers on the channel.
    *
    * @see nsIStreamListener
    */
   onStartRequest: function AI_onStartRequest(aRequest, aContext) {
-    // We must remove the request from the load group otherwise if the user
-    // closes the page that triggered it the download will be cancelled
-    if (this.loadGroup)
-      this.loadGroup.removeRequest(aRequest, null, Cr.NS_BINDING_RETARGETED);
-
     this.progress = 0;
     if (aRequest instanceof Ci.nsIChannel) {
       try {
         this.maxProgress = aRequest.contentLength;
       }
       catch (e) {
       }
       LOG("Download started for " + this.sourceURI.spec + " to file " +
--- a/toolkit/mozapps/extensions/addonManager.js
+++ b/toolkit/mozapps/extensions/addonManager.js
@@ -52,22 +52,37 @@ const PREF_EM_UPDATE_INTERVAL = "extensi
 // The old XPInstall error codes
 const EXECUTION_ERROR   = -203;
 const CANT_READ_ARCHIVE = -207;
 const USER_CANCELLED    = -210;
 const DOWNLOAD_ERROR    = -228;
 const UNSUPPORTED_TYPE  = -244;
 const SUCCESS = 0;
 
+const MSG_INSTALL_ENABLED  = "WebInstallerIsInstallEnabled";
+const MSG_INSTALL_ADDONS   = "WebInstallerInstallAddonsFromWebpage";
+const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
+
+const CHILD_SCRIPT =
+  "chrome://mozapps/content/extensions/extensions-content.js";
+
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
 
 var gSingleton = null;
 
 function amManager() {
   Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+  var messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
+                       getService(Ci.nsIChromeFrameMessageManager);
+
+  messageManager.addMessageListener(MSG_INSTALL_ENABLED, this);
+  messageManager.addMessageListener(MSG_INSTALL_ADDONS, this);
+  messageManager.loadFrameScript(CHILD_SCRIPT, true);
 }
 
 amManager.prototype = {
   observe: function AMC_observe(aSubject, aTopic, aData) {
     let os = Cc["@mozilla.org/observer-service;1"].
              getService(Ci.nsIObserverService);
 
     switch (aTopic) {
@@ -162,16 +177,62 @@ amManager.prototype = {
 
     return retval;
   },
 
   notify: function AMC_notify(aTimer) {
     AddonManagerPrivate.backgroundUpdateCheck();
   },
 
+  /**
+   * messageManager callback function.
+   *
+   * Listens to requests from child processes for InstallTrigger
+   * activity, and sends back callbacks.
+   */
+  receiveMessage: function(aMessage) {
+    var payload = aMessage.json;
+    var referer = Services.io.newURI(payload.referer, null, null);
+    switch (aMessage.name) {
+      case MSG_INSTALL_ENABLED:
+        return this.isInstallEnabled(payload.mimetype, referer);
+
+      case MSG_INSTALL_ADDONS:
+        var callback = null;
+        if (payload.callbackId != -1) {
+          callback = {
+            onInstallEnded: function ITP_callback(url, status) {
+              // Doing it this way, instead of aMessage.target.messageManager,
+              // ensures it works in Firefox and not only Fennec. See bug
+              // 578172. TODO: Clean up this code once that bug is fixed
+              var flo = aMessage.target.QueryInterface(Ci.nsIFrameLoaderOwner);
+              var returnMessageManager = flo.frameLoader.messageManager;
+              returnMessageManager.sendAsyncMessage(MSG_INSTALL_CALLBACK,
+                { callbackId: payload.callbackId, url: url, status: status }
+              );
+            },
+          };
+        }
+        var window;
+        try {
+          // Normal approach for single-process mode
+          window = aMessage.target.docShell
+                           .QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindow).content;
+        } catch (e) {
+          // Fallback for multiprocess (e10s) mode. Appears to work but has
+          // not had a full suite of automated tests run on it.
+          window = aMessage.target.ownerDocument.defaultView;
+        }
+        return this.installAddonsFromWebpage(payload.mimetype,
+          window, referer, payload.uris, payload.hashes, payload.names,
+          payload.icons, callback, payload.uris.length);
+    }
+  },
+
   classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
   _xpcom_factory: {
     createInstance: function(aOuter, aIid) {
       if (aOuter != null)
         throw Cr.NS_ERROR_NO_AGGREGATION;
   
       if (!gSingleton)
         gSingleton = new amManager();
deleted file mode 100644
--- a/toolkit/mozapps/extensions/amInstallTrigger.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-/* ***** 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 "nsIComponentManager.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"
-#include "mozilla/ModuleUtils.h"
-
-//
-// Helper function for URI verification
-//
-static nsresult
-CheckLoadURIFromScript(JSContext *aCx, const nsACString& aUriStr)
-{
-  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), aUriStr);
-  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_CLASSINFO(amInstallTrigger, NULL, nsIClassInfo::DOM_OBJECT,
-                  AM_InstallTrigger_CID)
-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* aCx)
-{
-  nsIScriptGlobalObject *globalObject = nsnull;
-  nsIScriptContext *scriptContext = GetScriptContextFromJSContext(aCx);
-  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 *aArgs,
-                          amIInstallCallback *aCallback,
-                          PRBool *_retval NS_OUTPARAM)
-{
-  JSContext *cx = GetJSContext();
-  nsCOMPtr<nsIDOMWindowInternal> window = GetOriginatingWindow(cx);
-  nsCOMPtr<nsIURI> referer = GetOriginatingURI(window);
-
-  jsval params;
-  nsresult rv = aArgs->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(), aCallback, 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 aType,
-                                const nsAString & aUrl,
-                                const nsAString & aSkin,
-                                PRBool *_retval NS_OUTPARAM)
-{
-  return StartSoftwareUpdate(aUrl, 0, _retval);
-}
-
-/* boolean startSoftwareUpdate (in AString url, [optional] in PRInt32 flags); */
-NS_IMETHODIMP
-amInstallTrigger::StartSoftwareUpdate(const nsAString & aUrl,
-                                      PRInt32 aFlags,
-                                      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(aUrl);
-  // 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_GENERIC_FACTORY_CONSTRUCTOR(amInstallTrigger)
-
-NS_DEFINE_NAMED_CID(AM_InstallTrigger_CID);
-
-static const mozilla::Module::CIDEntry kInstallTriggerCIDs[] = {
-  { &kAM_InstallTrigger_CID, false, NULL, amInstallTriggerConstructor },
-  { NULL }
-};
-
-static const mozilla::Module::ContractIDEntry kInstallTriggerContracts[] = {
-  { AM_INSTALLTRIGGER_CONTRACTID, &kAM_InstallTrigger_CID },
-  { NULL }
-};
-
-static const mozilla::Module::CategoryEntry kInstallTriggerCategories[] = {
-  { JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY, "InstallTrigger", AM_INSTALLTRIGGER_CONTRACTID },
-  { NULL }
-};
-
-static const mozilla::Module kInstallTriggerModule = {
-  mozilla::Module::kVersion,
-  kInstallTriggerCIDs,
-  kInstallTriggerContracts,
-  kInstallTriggerCategories
-};
-
-NSMODULE_DEFN(AddonsModule) = &kInstallTriggerModule;
deleted file mode 100644
--- a/toolkit/mozapps/extensions/amInstallTrigger.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* ***** 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* aCx);
-  already_AddRefed<nsIURI> GetOriginatingURI(nsIDOMWindowInternal* aWindow);
-
-  nsCOMPtr<amIWebInstaller> mManager;
-};
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/extensions-content.js
@@ -0,0 +1,293 @@
+/*
+# ***** 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):
+#   Alon Zakai <azakai@mozilla.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 Cu = Components.utils;
+
+const MSG_INSTALL_ENABLED  = "WebInstallerIsInstallEnabled";
+const MSG_INSTALL_ADDONS   = "WebInstallerInstallAddonsFromWebpage";
+const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+/**
+ * Child part of InstallTrigger e10s handling.
+ *
+ * Sets up InstallTriggers on newly-created windows,
+ * that will relay messages for InstallTrigger
+ * activity. We also process the parameters for
+ * the InstallTrigger to proper parameters for
+ * amIWebInstaller.
+ */
+function InstallTriggerChild() {
+  addEventListener("DOMWindowCreated", this, false);
+}
+
+var that = this;
+
+InstallTriggerChild.prototype = {
+  handleEvent: function handleEvent(aEvent) {
+    var window = aEvent.originalTarget.defaultView.content;
+
+    // Need to make sure we are called on what we care about -
+    // content windows. DOMWindowCreated is called on *all* HTMLDocuments,
+    // some of which belong to ChromeWindows or lack defaultView.content
+    // altogether.
+    //
+    // Note about the syntax used here: |"wrappedJSObject" in window|
+    // will silently fail, without even letting us catch it as an
+    // exception, and checking in the way that we do check in some
+    // cases still throws an exception; see bug 582108 about both.
+    try {
+      if (!window || !window.wrappedJSObject) {
+        return;
+      }
+    }
+    catch(e) {
+      return;
+    }
+
+    // This event happens for each HTMLDocument, so it can happen more than
+    // once per Window. We only need to work once per Window though.
+    if (window.wrappedJSObject.InstallTrigger)
+        return;
+
+    // The public object which web scripts can see (limited by
+    // the __exposedProps__ defined below)
+    window.wrappedJSObject.InstallTrigger = {
+      __exposedProps__: {
+        SKIN: "r",
+        LOCALE: "r",
+        CONTENT: "r",
+        PACKAGE: "r",
+        enabled: "r",
+        updateEnabled: "r",
+        install: "r",
+        installChrome: "r",
+        startSoftwareUpdate: "r",
+      },
+
+      // == Public interface ==
+
+      SKIN: Ci.amIInstallTrigger.SKIN,
+      LOCALE: Ci.amIInstallTrigger.LOCALE,
+      CONTENT: Ci.amIInstallTrigger.CONTENT,
+      PACKAGE: Ci.amIInstallTrigger.PACKAGE,
+
+      /**
+       * @see amIInstallTriggerInstaller.idl
+       */
+      enabled: function() {
+        return sendSyncMessage(MSG_INSTALL_ENABLED, {
+          mimetype: "application/x-xpinstall", referer: window.location.href
+        })[0];
+      },
+
+      /**
+       * @see amIInstallTriggerInstaller.idl
+       */
+      updateEnabled: function() {
+        return this.enabled();
+      },
+
+      /**
+       * @see amIInstallTriggerInstaller.idl
+       */
+      install: function(aArgs, aCallback) {
+        var params = {
+          mimetype: "application/x-xpinstall",
+          referer: window.location.href,
+          uris: [],
+          hashes: [],
+          names: [],
+          icons: [],
+        };
+
+        for (var name in aArgs) {
+          var item = aArgs[name];
+          if (typeof item === 'string') {
+            item = { URL: item };
+          } else if (!("URL" in item)) {
+            throw new Error("Missing URL property for '" + name + "'");
+          }
+
+          // Resolve and validate urls
+          var url = this.resolveURL(item.URL);
+          if (!this.checkLoadURIFromScript(url))
+            throw new Error("insufficient permissions to install: " + url);
+
+          var iconUrl = null;
+          if ("IconURL" in item) {
+            iconUrl = this.resolveURL(item.IconURL);
+            if (!this.checkLoadURIFromScript(iconUrl)) {
+              iconUrl = null; // If page can't load the icon, just ignore it
+            }
+          }
+          params.uris.push(url.spec);
+          params.hashes.push("Hash" in item ? item.Hash : null);
+          params.names.push(name);
+          params.icons.push(iconUrl ? iconUrl.spec : null);
+        }
+        // Add callback Id, done here, so only if we actually got here
+        params.callbackId = this.addCallback(aCallback, params.uris);
+        // Send message
+        return sendSyncMessage(MSG_INSTALL_ADDONS, params)[0];
+      },
+
+      /**
+       * @see amIInstallTriggerInstaller.idl
+       */
+      startSoftwareUpdate: function(aUrl, aFlags) {
+        var url = Services.io.newURI(aUrl, null, null)
+                          .QueryInterface(Ci.nsIURL).filename;
+        var object = {};
+        object[url] = { "URL": aUrl };
+        return this.install(object);
+      },
+
+      /**
+       * @see amIInstallTriggerInstaller.idl
+       */
+      installChrome: function(aType, aUrl, aSkin) {
+        return this.startSoftwareUpdate(aUrl);
+      },
+
+      // == Internal, hidden machinery ==
+
+      callbacks: {},
+
+      /**
+       * Adds a callback to the list of callbacks we may receive messages
+       * about from the parent process. We save them here; only callback IDs
+       * are sent over IPC.
+       *
+       * @param  callback
+       *         The callback function
+       * @param  urls
+       *         The urls this callback function will receive responses for.
+       *         After all the callbacks have arrived, we can forget about the
+       *         callback.
+       *
+       * @return The callback ID, an integer identifying this callback.
+       */
+      addCallback: function(aCallback, aUrls) {
+        if (!aCallback)
+          return -1;
+        var callbackId = 0;
+        while (callbackId in this.callbacks)
+          callbackId++;
+        this.callbacks[callbackId] = {
+          callback: aCallback,
+          urls: aUrls.slice(0), // Clone the urls for our own use (it lets
+                                // us know when no further callbacks will
+                                // occur)
+        };
+        return callbackId;
+      },
+
+      /**
+       * Receives a message about a callback. Performs the actual callback
+       * (for the callback with the ID we are given). When
+       * all URLs are exhausted, can free the callbackId and linked stuff.
+       *
+       * @param  message
+       *         The IPC message. Contains callbackId, the ID of the callback.
+       *
+       */
+      receiveMessage: function(aMessage) {
+        var payload = aMessage.json;
+        var callbackId = payload.callbackId;
+        var url = payload.url;
+        var status = payload.status;
+        var callbackObj = this.callbacks[callbackId];
+        if (!callbackObj)
+          return;
+        try {
+          callbackObj.callback(url, status);
+        }
+        catch (e) {
+          dump("InstallTrigger callback threw an exception: " + e + "\n");
+        }
+        callbackObj.urls.splice(callbackObj.urls.indexOf(url), 1);
+        if (callbackObj.urls.length == 0)
+          this.callbacks[callbackId] = null;
+      },
+
+      /**
+       * Resolves a URL in the context of our current window. We need to do
+       * this before sending URLs to the parent process.
+       *
+       * @param  aUrl
+       *         The url to resolve.
+       *
+       * @return A resolved, absolute nsURI object.
+       */
+      resolveURL: function(aUrl) {
+        return Services.io.newURI(aUrl, null,
+                                  window.document.documentURIObject);
+      },
+
+      /**
+       * @see amInstallTrigger.cpp
+       * TODO: When e10s lands on m-c, consider removing amInstallTrigger.cpp
+       *       See bug 571166
+       */
+      checkLoadURIFromScript: function(aUri) {
+        var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].
+                     getService(Ci.nsIScriptSecurityManager);
+        var principal = window.content.document.nodePrincipal;
+        try {
+          secman.checkLoadURIWithPrincipal(principal, aUri,
+            Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+          return true;
+        }
+        catch(e) {
+          return false;
+        }
+      },
+    };
+
+    addMessageListener(MSG_INSTALL_CALLBACK,
+      window.wrappedJSObject.InstallTrigger);
+  },
+};
+
+new InstallTriggerChild();
+
--- a/toolkit/mozapps/extensions/jar.mn
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -1,15 +1,16 @@
 toolkit.jar:
 % content mozapps %content/mozapps/
   content/mozapps/extensions/extensions.xul                     (content/extensions.xul)
   content/mozapps/extensions/extensions.css                     (content/extensions.css)
   content/mozapps/extensions/extensions.js                      (content/extensions.js)
   content/mozapps/extensions/extensions.xml                     (content/extensions.xml)
   content/mozapps/extensions/updateinfo.xsl                     (content/updateinfo.xsl)
+  content/mozapps/extensions/extensions-content.js              (content/extensions-content.js)
 * content/mozapps/extensions/about.xul                          (content/about.xul)
 * content/mozapps/extensions/about.js                           (content/about.js)
 * content/mozapps/extensions/list.xul                           (content/list.xul)
 * content/mozapps/extensions/list.js                            (content/list.js)
 * content/mozapps/extensions/blocklist.xul                      (content/blocklist.xul)
 * content/mozapps/extensions/blocklist.js                       (content/blocklist.js)
 * content/mozapps/extensions/blocklist.css                      (content/blocklist.css)
 * content/mozapps/extensions/blocklist.xml                      (content/blocklist.xml)