Bug 1232274: Make installing or enabling an add-on require a restart if e10s is on and a pref is set. r?rhelmer draft
authorDave Townsend <dtownsend@oxymoronical.com>
Thu, 14 Jan 2016 15:21:01 -0800
changeset 321877 c2f50331d81b7e27ba8892d8ac9793b8a15572e9
parent 321865 b6ef37a7673e9829de96d0439244343948545955
child 512988 db39543cb991068985da240c0cdbcf37e4a22379
push id9479
push userdtownsend@mozilla.com
push dateFri, 15 Jan 2016 00:36:52 +0000
reviewersrhelmer
bugs1232274
milestone46.0a1
Bug 1232274: Make installing or enabling an add-on require a restart if e10s is on and a pref is set. r?rhelmer
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_e10s_restartless.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -107,16 +107,17 @@ const PREF_XPI_UNPACK                 = 
 const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
 const PREF_INSTALL_REQUIRESECUREORIGIN = "extensions.install.requireSecureOrigin";
 const PREF_INSTALL_DISTRO_ADDONS      = "extensions.installDistroAddons";
 const PREF_BRANCH_INSTALLED_ADDON     = "extensions.installedDistroAddon.";
 const PREF_SHOWN_SELECTION_UI         = "extensions.shownSelectionUI";
 const PREF_INTERPOSITION_ENABLED      = "extensions.interposition.enabled";
 const PREF_SYSTEM_ADDON_SET           = "extensions.systemAddonSet";
 const PREF_SYSTEM_ADDON_UPDATE_URL    = "extensions.systemAddon.update.url";
+const PREF_E10S_BLOCK_ENABLE          = "extensions.e10sBlocksEnabling";
 
 const PREF_EM_MIN_COMPAT_APP_VERSION      = "extensions.minCompatibleAppVersion";
 const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
 
 const PREF_CHECKCOMAT_THEMEOVERRIDE   = "extensions.checkCompatibility.temporaryThemeOverride_minAppVersion";
 
 const PREF_EM_HOTFIX_ID               = "extensions.hotfix.id";
 const PREF_EM_CERT_CHECKATTRIBUTES    = "extensions.hotfix.cert.checkAttributes";
@@ -2739,21 +2740,29 @@ this.XPIProvider = {
       }
 
       // Let these shutdown a little earlier when they still have access to most
       // of XPCOM
       Services.obs.addObserver({
         observe: function(aSubject, aTopic, aData) {
           XPIProvider._closing = true;
           for (let id in XPIProvider.bootstrappedAddons) {
+            // If no scope has been loaded for this add-on then there is no need
+            // to shut it down (should only happen when a bootstrapped add-on is
+            // pending enable)
+            if (!(id in XPIProvider.bootstrapScopes))
+              continue;
+
             let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
             file.persistentDescriptor = XPIProvider.bootstrappedAddons[id].descriptor;
             let addon = createAddonDetails(id, XPIProvider.bootstrappedAddons[id]);
             XPIProvider.callBootstrapMethod(addon, file, "shutdown",
                                             BOOTSTRAP_REASONS.APP_SHUTDOWN);
+            if (XPIProvider.bootstrappedAddons[id].disable)
+              delete XPIProvider.bootstrappedAddons[aId];
           }
           Services.obs.removeObserver(this, "quit-application-granted");
         }
       }, "quit-application-granted", false);
 
       // Detect final-ui-startup for telemetry reporting
       Services.obs.addObserver({
         observe: function(aSubject, aTopic, aData) {
@@ -4225,16 +4234,51 @@ this.XPIProvider = {
       case PREF_XPI_SIGNATURES_REQUIRED:
         this.updateAddonAppDisabledStates();
         break;
       }
     }
   },
 
   /**
+   * In some cases having add-ons active blocks e10s but turning off e10s
+   * requires a restart so some add-ons that are normally restartless will
+   * require a restart to install or enable.
+   *
+   * @param  aAddon
+   *         The add-on to test
+   * @return true if enabling the add-on requires a restart
+   */
+  e10sBlocksEnabling: function(aAddon) {
+    // If the preference isn't set then don't block anything
+    if (!Preferences.get(PREF_E10S_BLOCK_ENABLE, false))
+      return false;
+
+    // If e10s isn't active then don't block anything
+    if (!Services.appinfo.browserTabsRemoteAutostart)
+      return false;
+
+    // Only extensions change behaviour
+    if (aAddon.type != "extension")
+      return false;
+
+    // The hotfix is exempt
+    let hotfixID = Preferences.get(PREF_EM_HOTFIX_ID, undefined);
+    if (hotfixID && hotfixID == aAddon.id)
+      return false;
+
+    // System add-ons are exempt
+    if (aAddon._installLocation.name == KEY_APP_SYSTEM_DEFAULTS ||
+        aAddon._installLocation.name == KEY_APP_SYSTEM_ADDONS)
+      return false;
+
+    return true;
+  },
+
+  /**
    * Tests whether enabling an add-on will require a restart.
    *
    * @param  aAddon
    *         The add-on to test
    * @return true if the operation requires a restart
    */
   enableRequiresRestart: function(aAddon) {
     // If the platform couldn't have activated extensions then we can make
@@ -4258,16 +4302,19 @@ this.XPIProvider = {
         return false;
 
       // If the theme is already the theme in use then no restart is necessary.
       // This covers the case where the default theme is in use but a
       // lightweight theme is considered active.
       return aAddon.internalName != this.currentSkin;
     }
 
+    if (this.e10sBlocksEnabling(aAddon))
+      return true;
+
     return !aAddon.bootstrap;
   },
 
   /**
    * Tests whether disabling an add-on will require a restart.
    *
    * @param  aAddon
    *         The add-on to test
@@ -4348,16 +4395,19 @@ this.XPIProvider = {
         return true;
     }
 
     // If the add-on is not going to be active after installation then it
     // doesn't require a restart to install.
     if (aAddon.disabled)
       return false;
 
+    if (this.e10sBlocksEnabling(aAddon))
+      return true;
+
     // Themes will require a restart (even if dynamic switching is enabled due
     // to some caching issues) and non-bootstrapped add-ons will require a
     // restart
     return aAddon.type == "theme" || !aAddon.bootstrap;
   },
 
   /**
    * Tests whether uninstalling an add-on will require a restart.
@@ -4719,16 +4769,33 @@ this.XPIProvider = {
         else {
           if (aAddon.bootstrap) {
             this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "startup",
                                      BOOTSTRAP_REASONS.ADDON_ENABLE);
           }
           AddonManagerPrivate.callAddonListeners("onEnabled", wrapper);
         }
       }
+      else if (aAddon.bootstrap) {
+        // Something blocked the restartless add-on from enabling or disabling
+        // make sure it happens on the next startup
+        if (isDisabled) {
+          this.bootstrappedAddons[aAddon.id].disable = true;
+        }
+        else {
+          this.bootstrappedAddons[aAddon.id] = {
+            version: aAddon.version,
+            type: aAddon.type,
+            descriptor: aAddon._sourceBundle.persistentDescriptor,
+            multiprocessCompatible: aAddon.multiprocessCompatible,
+            runInSafeMode: canRunInSafeMode(aAddon),
+          };
+          this.persistBootstrappedAddons();
+        }
+      }
     }
 
     // Sync with XPIStates.
     let xpiState = XPIStates.getAddon(aAddon.location, aAddon.id);
     if (xpiState) {
       xpiState.syncWithDB(aAddon);
       XPIStates.save();
     } else {
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -257,16 +257,17 @@ function createAppInfo(id, name, version
     name: name,
     ID: id,
     version: version,
     appBuildID: "2007010101",
     platformVersion: platformVersion ? platformVersion : "1.0",
     platformBuildID: "2007010101",
 
     // nsIXULRuntime
+    browserTabsRemoteAutostart: false,
     inSafeMode: false,
     logConsoleErrors: true,
     OS: "XPCShell",
     XPCOMABI: "noarch-spidermonkey",
     invalidateCachesOnRestart: function invalidateCachesOnRestart() {
       // Do nothing
     },
 
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_e10s_restartless.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "bootstrap1@tests.mozilla.org";
+
+BootstrapMonitor.init();
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+startupManager();
+
+function* check_normal() {
+  let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+  yield promiseCompleteAllInstalls([install]);
+  do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+  do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+  BootstrapMonitor.checkAddonInstalled(ID);
+  BootstrapMonitor.checkAddonStarted(ID);
+
+  let addon = yield promiseAddonByID(ID);
+  do_check_eq(addon, install.addon);
+
+  do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE));
+  addon.userDisabled = true;
+  BootstrapMonitor.checkAddonNotStarted(ID);
+  do_check_false(addon.isActive);
+  do_check_false(hasFlag(addon.pendingOperations, AddonManager.PENDING_DISABLE));
+
+  do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE));
+  addon.userDisabled = false;
+  BootstrapMonitor.checkAddonStarted(ID);
+  do_check_true(addon.isActive);
+  do_check_false(hasFlag(addon.pendingOperations, AddonManager.PENDING_ENABLE));
+
+  do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL));
+  addon.uninstall();
+  BootstrapMonitor.checkAddonNotStarted(ID);
+  BootstrapMonitor.checkAddonNotInstalled(ID);
+
+  restartManager();
+}
+
+// Installing the add-on normally doesn't require a restart
+add_task(function*() {
+  gAppInfo.browserTabsRemoteAutostart = false;
+  Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", false);
+
+  yield check_normal();
+});
+
+// Enabling the pref doesn't change anything
+add_task(function*() {
+  gAppInfo.browserTabsRemoteAutostart = false;
+  Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+
+  yield check_normal();
+});
+
+// Default e10s doesn't change anything
+add_task(function*() {
+  gAppInfo.browserTabsRemoteAutostart = true;
+  Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", false);
+
+  yield check_normal();
+});
+
+// Pref and e10s blocks install
+add_task(function*() {
+  gAppInfo.browserTabsRemoteAutostart = true;
+  Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+
+  let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+  yield promiseCompleteAllInstalls([install]);
+  do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+  do_check_true(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+  let addon = yield promiseAddonByID(ID);
+  do_check_eq(addon, null);
+
+  yield promiseRestartManager();
+
+  BootstrapMonitor.checkAddonInstalled(ID);
+  BootstrapMonitor.checkAddonStarted(ID);
+
+  addon = yield promiseAddonByID(ID);
+  do_check_neq(addon, null);
+
+  do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE));
+  addon.userDisabled = true;
+  BootstrapMonitor.checkAddonNotStarted(ID);
+  do_check_false(addon.isActive);
+  do_check_false(hasFlag(addon.pendingOperations, AddonManager.PENDING_DISABLE));
+
+  do_check_true(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE));
+  addon.userDisabled = false;
+  BootstrapMonitor.checkAddonNotStarted(ID);
+  do_check_false(addon.isActive);
+  do_check_true(hasFlag(addon.pendingOperations, AddonManager.PENDING_ENABLE));
+
+  yield promiseRestartManager();
+
+  addon = yield promiseAddonByID(ID);
+  do_check_neq(addon, null);
+
+  do_check_true(addon.isActive);
+  BootstrapMonitor.checkAddonStarted(ID);
+
+  do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL));
+  addon.uninstall();
+  BootstrapMonitor.checkAddonNotStarted(ID);
+  BootstrapMonitor.checkAddonNotInstalled(ID);
+
+  restartManager();
+});
+
+// The hotfix is unaffected
+add_task(function*() {
+  gAppInfo.browserTabsRemoteAutostart = true;
+  Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+  Services.prefs.setCharPref("extensions.hotfix.id", ID);
+
+  yield check_normal();
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -298,8 +298,9 @@ run-sequentially = Uses global XCurProcD
 [test_sourceURI.js]
 [test_webextension_icons.js]
 [test_webextension.js]
 [test_bootstrap_globals.js]
 [test_bug1180901_2.js]
 skip-if = os != "win"
 [test_bug1180901.js]
 skip-if = os != "win"
+[test_e10s_restartless.js]