Bug 1029926 - Add a field to install.rdf for add-ons that are compatible with electrolysis (r=irving)
authorBill McCloskey <wmccloskey@mozilla.com>
Mon, 14 Jul 2014 22:10:06 -0700
changeset 193976 5ea30521f56bfa7f0b4e8361b40a702a90088f56
parent 193975 3fadc02e2e841c3bb5f6fd7db6fc4fa9593817fa
child 193977 d899aa0fb6c502c3fad1bdef02e7f9a1329a00b8
push id27136
push usercbook@mozilla.com
push dateTue, 15 Jul 2014 12:59:16 +0000
treeherdermozilla-central@d2d56f9066bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersirving
bugs1029926
milestone33.0a1
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 1029926 - Add a field to install.rdf for add-ons that are compatible with electrolysis (r=irving)
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/internal/XPIProviderUtils.js
toolkit/xre/nsXREDirProvider.cpp
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -846,19 +846,20 @@ function loadManifestFromRDF(aUri, aStre
       throw new Error("Illegal add-on ID " + addon.id);
     if (!addon.version)
       throw new Error("No version in install manifest");
   }
 
   addon.strictCompatibility = !(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||
                               getRDFProperty(ds, root, "strictCompatibility") == "true";
 
-  // Only read the bootstrap property for extensions.
+  // Only read these properties for extensions.
   if (addon.type == "extension") {
     addon.bootstrap = getRDFProperty(ds, root, "bootstrap") == "true";
+    addon.multiprocessCompatible = getRDFProperty(ds, root, "multiprocessCompatible") == "true";
     if (addon.optionsType &&
         addon.optionsType != AddonManager.OPTIONS_TYPE_DIALOG &&
         addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE &&
         addon.optionsType != AddonManager.OPTIONS_TYPE_TAB &&
         addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_INFO) {
       throw new Error("Install manifest specifies unknown type: " + addon.optionsType);
     }
   }
@@ -2936,17 +2937,18 @@ this.XPIProvider = {
           }
         }
       }
 
       if (aOldAddon.visible && aOldAddon.active && aOldAddon.bootstrap) {
         XPIProvider.bootstrappedAddons[aOldAddon.id] = {
           version: aOldAddon.version,
           type: aOldAddon.type,
-          descriptor: aAddonState.descriptor
+          descriptor: aAddonState.descriptor,
+          multiprocessCompatible: aOldAddon.multiprocessCompatible
         };
       }
 
       return changed;
     }
 
     /**
      * Called when an add-on has been removed.
@@ -4116,39 +4118,49 @@ this.XPIProvider = {
    * @param  aId
    *         The add-on's ID
    * @param  aFile
    *         The nsIFile for the add-on
    * @param  aVersion
    *         The add-on's version
    * @param  aType
    *         The type for the add-on
+   * @param  aMultiprocessCompatible
+   *         Boolean indicating whether the add-on is compatible with electrolysis.
    * @return a JavaScript scope
    */
-  loadBootstrapScope: function XPI_loadBootstrapScope(aId, aFile, aVersion, aType) {
+  loadBootstrapScope: function XPI_loadBootstrapScope(aId, aFile, aVersion, aType,
+                                                      aMultiprocessCompatible) {
     // Mark the add-on as active for the crash reporter before loading
     this.bootstrappedAddons[aId] = {
       version: aVersion,
       type: aType,
-      descriptor: aFile.persistentDescriptor
+      descriptor: aFile.persistentDescriptor,
+      multiprocessCompatible: aMultiprocessCompatible
     };
     this.persistBootstrappedAddons();
     this.addAddonsToCrashReporter();
 
     // Locales only contain chrome and can't have bootstrap scripts
     if (aType == "locale") {
       this.bootstrapScopes[aId] = null;
       return;
     }
 
     logger.debug("Loading bootstrap scope from " + aFile.path);
 
     let principal = Cc["@mozilla.org/systemprincipal;1"].
                     createInstance(Ci.nsIPrincipal);
 
+    if (!aMultiprocessCompatible && Prefs.getBoolPref("browser.tabs.remote.autostart", false)) {
+      let interposition = Cc["@mozilla.org/addons/multiprocess-shims;1"].
+        getService(Ci.nsIAddonInterposition);
+      Cu.setAddonInterposition(aId, interposition);
+    }
+
     if (!aFile.exists()) {
       this.bootstrapScopes[aId] =
         new Cu.Sandbox(principal, { sandboxName: aFile.path,
                                     wantGlobalProperties: ["indexedDB"],
                                     addonId: aId,
                                     metadata: { addonID: aId } });
       logger.error("Attempted to load bootstrap scope from missing directory " + aFile.path);
       return;
@@ -4205,16 +4217,20 @@ this.XPIProvider = {
   /**
    * Unloads a bootstrap scope by dropping all references to it and then
    * updating the list of active add-ons with the crash reporter.
    *
    * @param  aId
    *         The add-on's ID
    */
   unloadBootstrapScope: function XPI_unloadBootstrapScope(aId) {
+    // In case the add-on was not multiprocess-compatible, deregister
+    // any interpositions for it.
+    Cu.setAddonInterposition(aId, null);
+
     delete this.bootstrapScopes[aId];
     delete this.bootstrappedAddons[aId];
     this.persistBootstrappedAddons();
     this.addAddonsToCrashReporter();
 
     // Only access BrowserToolboxProcess if ToolboxProcess.jsm has been
     // initialized as otherwise, there won't be any addon globals added to it
     if (this._toolboxProcessLoaded) {
@@ -4251,17 +4267,18 @@ this.XPIProvider = {
     if (aMethod == "startup") {
       logger.debug("Registering manifest for " + aFile.path);
       Components.manager.addBootstrappedManifestLocation(aFile);
     }
 
     try {
       // Load the scope if it hasn't already been loaded
       if (!(aAddon.id in this.bootstrapScopes))
-        this.loadBootstrapScope(aAddon.id, aFile, aAddon.version, aAddon.type);
+        this.loadBootstrapScope(aAddon.id, aFile, aAddon.version, aAddon.type,
+                                aAddon.multiprocessCompatible);
 
       // Nothing to call for locales
       if (aAddon.type == "locale")
         return;
 
       if (!(aMethod in this.bootstrapScopes[aAddon.id])) {
         logger.warn("Add-on " + aAddon.id + " is missing bootstrap method " + aMethod);
         return;
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -66,17 +66,17 @@ const PROP_JSON_FIELDS = ["id", "syncGUI
                           "internalName", "updateURL", "updateKey", "optionsURL",
                           "optionsType", "aboutURL", "iconURL", "icon64URL",
                           "defaultLocale", "visible", "active", "userDisabled",
                           "appDisabled", "pendingUninstall", "descriptor", "installDate",
                           "updateDate", "applyBackgroundUpdates", "bootstrap",
                           "skinnable", "size", "sourceURI", "releaseNotesURI",
                           "softDisabled", "foreignInstall", "hasBinaryComponents",
                           "strictCompatibility", "locales", "targetApplications",
-                          "targetPlatforms"];
+                          "targetPlatforms", "multiprocessCompatible"];
 
 // Time to wait before async save of XPI JSON database, in milliseconds
 const ASYNC_SAVE_DELAY_MS = 20;
 
 const PREFIX_ITEM_URI                 = "urn:mozilla:item:";
 const RDFURI_ITEM_ROOT                = "urn:mozilla:item:root"
 const PREFIX_NS_EM                    = "http://www.mozilla.org/2004/em-rdf#";
 
@@ -1463,16 +1463,25 @@ this.XPIDatabase = {
       for (let row of themes) {
         text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
         enabledAddons.push(encodeURIComponent(row.id) + ":" +
                            encodeURIComponent(row.version));
       }
       fullCount += count;
     }
 
+    text += "\r\n[MultiprocessIncompatibleExtensions]\r\n";
+
+    count = 0;
+    for (let row of activeAddons) {
+      if (!row.multiprocessCompatible) {
+        text += "Extension" + (count++) + "=" + row.id + "\r\n";
+      }
+    }
+
     if (fullCount > 0) {
       logger.debug("Writing add-ons list");
 
       try {
         let addonsListTmp = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST + ".tmp"],
                                               true);
         var fos = FileUtils.openFileOutputStream(addonsListTmp);
         fos.write(text, text.length);
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -3,18 +3,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsAppRunner.h"
 #include "nsToolkitCompsCID.h"
 #include "nsXREDirProvider.h"
 
 #include "jsapi.h"
+#include "xpcpublic.h"
 
 #include "nsIJSRuntimeService.h"
+#include "nsIAddonInterposition.h"
 #include "nsIAppStartup.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsIFile.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIToolkitChromeRegistry.h"
 
@@ -534,16 +536,42 @@ nsXREDirProvider::GetFiles(const char* a
   rv = NS_NewUnionEnumerator(aResult, appEnum, xreEnum);
   if (NS_FAILED(rv))
     return rv;
 
   return NS_SUCCESS_AGGREGATE_RESULT;
 }
 
 static void
+RegisterExtensionInterpositions(nsINIParser &parser)
+{
+  if (!mozilla::Preferences::GetBool("browser.tabs.remote.autostart", false))
+    return;
+
+  nsCOMPtr<nsIAddonInterposition> interposition =
+    do_GetService("@mozilla.org/addons/multiprocess-shims;1");
+
+  nsresult rv;
+  int32_t i = 0;
+  do {
+    nsAutoCString buf("Extension");
+    buf.AppendInt(i++);
+
+    nsAutoCString addonId;
+    rv = parser.GetString("MultiprocessIncompatibleExtensions", buf.get(), addonId);
+    if (NS_FAILED(rv))
+      return;
+
+    if (!xpc::SetAddonInterposition(addonId, interposition))
+      continue;
+  }
+  while (true);
+}
+
+static void
 LoadExtensionDirectories(nsINIParser &parser,
                          const char *aSection,
                          nsCOMArray<nsIFile> &aDirectories,
                          NSLocationType aType)
 {
   nsresult rv;
   int32_t i = 0;
   do {
@@ -595,16 +623,17 @@ nsXREDirProvider::LoadExtensionBundleDir
     if (!extensionsINILF)
       return;
 
     nsINIParser parser;
     nsresult rv = parser.Init(extensionsINILF);
     if (NS_FAILED(rv))
       return;
 
+    RegisterExtensionInterpositions(parser);
     LoadExtensionDirectories(parser, "ExtensionDirs", mExtensionDirectories,
                              NS_COMPONENT_LOCATION);
     LoadExtensionDirectories(parser, "ThemeDirs", mThemeDirectories,
                              NS_SKIN_LOCATION);
   }
 }
 
 void