Backed out changeset c8dc4dd369ee
authorKyle Huey <me@kylehuey.com>
Tue, 17 Aug 2010 01:07:56 -0400
changeset 50710 fafc537889901eff93e3a287bbbeb646e6764684
parent 50685 c8dc4dd369ee9b8668685d3a2011473cde7390f1
child 50711 6b6187bfe4fece3d87a49093a366366fefb59dd1
push idunknown
push userunknown
push dateunknown
milestone2.0b4pre
backs outc8dc4dd369ee9b8668685d3a2011473cde7390f1
Backed out changeset c8dc4dd369ee
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,16 +144,17 @@ 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,16 +260,17 @@
     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,25 +36,35 @@
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MODULE       = extensions
-XPIDL_MODULE = extensions
+MODULE         = extensions
+LIBRARY_NAME   = extensions
+SHORT_LIBNAME  = extnsion
+IS_COMPONENT   = 1
+MODULE_NAME    = AddonsModule
+GRE_MODULE     = 1
+LIBXUL_LIBRARY = 1
+EXPORT_LIBRARY = 1
 
 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,59 +3832,16 @@ 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
@@ -4345,16 +4302,18 @@ 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;
@@ -4402,20 +4361,23 @@ 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);
-      this.channel.notificationCallbacks =
-        new XPINotificationCallbacks(this.window, !this.hash);
-      this.channel.QueryInterface(Ci.nsIHttpChannelInternal)
-                  .forceAllowThirdPartyCookie = true;
+      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.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;
@@ -4441,16 +4403,21 @@ 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,37 +52,22 @@ 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, true);
 }
 
 amManager.prototype = {
   observe: function AMC_observe(aSubject, aTopic, aData) {
     let os = Cc["@mozilla.org/observer-service;1"].
              getService(Ci.nsIObserverService);
 
     switch (aTopic) {
@@ -177,63 +162,16 @@ 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,
-                { installerId: payload.installerId,
-                  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();
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/amInstallTrigger.cpp
@@ -0,0 +1,380 @@
+/* ***** 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;
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* aCx);
+  already_AddRefed<nsIURI> GetOriginatingURI(nsIDOMWindowInternal* aWindow);
+
+  nsCOMPtr<amIWebInstaller> mManager;
+};
deleted file mode 100644
--- a/toolkit/mozapps/extensions/content/extensions-content.js
+++ /dev/null
@@ -1,317 +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):
-#   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";
-
-var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
-                           .getService(Components.interfaces.nsIIOService);
-
-function InstallTrigger(installerId, window) {
-  this.installerId = installerId;
-  this.window = window;
-}
-
-InstallTrigger.prototype = {
-  __exposedProps__: {
-    SKIN: "r",
-    LOCALE: "r",
-    CONTENT: "r",
-    PACKAGE: "r",
-    enabled: "r",
-    updateEnabled: "r",
-    install: "r",
-    installChrome: "r",
-    startSoftwareUpdate: "r",
-    toSource: "r", // XXX workaround for bug 582100
-  },
-
-  // == 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: this.window.location.href
-    })[0];
-  },
-
-  /**
-   * @see amIInstallTriggerInstaller.idl
-   */
-  updateEnabled: function() {
-    return this.enabled();
-  },
-
-  /**
-   * @see amIInstallTriggerInstaller.idl
-   */
-  install: function(aArgs, aCallback) {
-    var params = {
-      installerId: this.installerId,
-      mimetype: "application/x-xpinstall",
-      referer: this.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 = gIoService.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;
-  },
-
-  /**
-   * 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 gIoService.newURI(aUrl, null,
-                             this.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 = this.window.content.document.nodePrincipal;
-    try {
-      secman.checkLoadURIWithPrincipal(principal, aUri,
-        Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
-      return true;
-    }
-    catch(e) {
-      return false;
-    }
-  },
-};
-
-/**
- * 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 InstallTriggerManager() {
-  this.installerIds = [];
-  this.nextInstallerId = 0;
-
-  addMessageListener(MSG_INSTALL_CALLBACK, this);
-
-  addEventListener("DOMWindowCreated", this, false);
-
-  var self = this;
-  addEventListener("unload", function() {
-    // Clean up all references, to help gc work quickly
-    for (var installerId in self.installerIds) {
-      self.installerIds[installerId].callbacks = null;
-      self.installerIds[installerId] = null;
-    }
-    self.installerIds = null;
-  }, false);
-}
-
-InstallTriggerManager.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;
-
-    // Create the public object which web scripts can see
-    var installerId = this.nextInstallerId ++;
-    var installTrigger = new InstallTrigger(installerId, window);
-    this.installerIds[installerId] = installTrigger;
-    window.wrappedJSObject.InstallTrigger = installTrigger;
-  },
-
-  /**
-   * 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 IDs of the installer and the
-   *         callback.
-   *
-   */
-  receiveMessage: function(aMessage) {
-    var payload = aMessage.json;
-    var installer = this.installerIds[payload.installerId];
-    var callbackId = payload.callbackId;
-    var url = payload.url;
-    var status = payload.status;
-    var callbackObj = installer.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)
-      installer.callbacks[callbackId] = null;
-  },
-};
-
-new InstallTriggerManager();
-
--- a/toolkit/mozapps/extensions/jar.mn
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -1,16 +1,15 @@
 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)