Bug 550936 - Make InstallTrigger support cross-process communication r=Mossop a=blocking-beta4+,blocking-fennec2.0a1+
☠☠ backed out by fafc53788990 ☠ ☠
authorAlon Zakai <azakai@mozilla.com>
Mon, 16 Aug 2010 16:41:46 -0400
changeset 50685 c8dc4dd369ee9b8668685d3a2011473cde7390f1
parent 50684 8541ae9ea9b3102d068981b3d4c232e54d2959c8
child 50686 235a99fd59fda60eeffe99952a24fe4978121163
child 50710 fafc537889901eff93e3a287bbbeb646e6764684
push idunknown
push userunknown
push dateunknown
reviewersMossop, blocking-beta4
bugs550936
milestone2.0b4pre
Bug 550936 - Make InstallTrigger support cross-process communication r=Mossop a=blocking-beta4+,blocking-fennec2.0a1+
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, 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,63 @@ 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();
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,317 @@
+/*
+# ***** 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,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)