Bug 1232274: Make installing or enabling an add-on require a restart if e10s is on and a pref is set. r?rhelmer
--- 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]