From 1255d4d9f5e7e6e4d51f29e05308ff40249491ba Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Fri, 22 Jan 2016 10:10:17 +0100 Subject: [PATCH] Bug 1234675 - Ability to disable e10s for users with addons. r=Mossop --- .../extensions/internal/XPIProvider.jsm | 43 +++-- .../extensions/internal/XPIProviderUtils.js | 13 ++ .../test/xpcshell/test_e10s_restartless.js | 165 ++++++++++++++++++ toolkit/xre/nsAppRunner.cpp | 13 ++ 4 files changed, 219 insertions(+), 15 deletions(-) diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 48994b7342d7..c562136f1475 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -4239,6 +4239,33 @@ this.XPIProvider = { } }, + /** + * Determine if an add-on should be blocking e10s if enabled. + * + * @param aAddon + * The add-on to test + * @return true if enabling the add-on should block e10s + */ + isBlockingE10s: function(aAddon) { + // 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 + let locName = aAddon._installLocation ? aAddon._installLocation.name + : undefined; + if (locName == KEY_APP_SYSTEM_DEFAULTS || + locName == KEY_APP_SYSTEM_ADDONS) + return false; + + return true; + }, + /** * 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 @@ -4257,21 +4284,7 @@ this.XPIProvider = { 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; + return this.isBlockingE10s(aAddon); }, /** diff --git a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js index d4ff03d89659..43c352dc8c41 100644 --- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js +++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js @@ -48,6 +48,7 @@ const PREF_PENDING_OPERATIONS = "extensions.pendingOperations"; const PREF_EM_ENABLED_ADDONS = "extensions.enabledAddons"; const PREF_EM_DSS_ENABLED = "extensions.dss.enabled"; const PREF_EM_AUTO_DISABLED_SCOPES = "extensions.autoDisableScopes"; +const PREF_E10S_BLOCKED_BY_ADDONS = "extensions.e10sBlockedByAddons"; const KEY_APP_PROFILE = "app-profile"; const KEY_APP_SYSTEM_ADDONS = "app-system-addons"; @@ -460,6 +461,7 @@ this.XPIDatabase = { ASYNC_SAVE_DELAY_MS); } + this.updateAddonsBlockingE10s(); let promise = this._deferredSave.saveChanges(); if (!this._schemaVersionSet) { this._schemaVersionSet = true; @@ -1389,6 +1391,17 @@ this.XPIDatabase = { this.saveChanges(); }, + updateAddonsBlockingE10s: function() { + let blockE10s = false; + for (let [, addon] of this.addonDB) { + let active = (addon.visible && !addon.disabled && !addon.pendingUninstall); + + if (active && XPIProvider.isBlockingE10s(addon)) + blockE10s = true; + } + Preferences.set(PREF_E10S_BLOCKED_BY_ADDONS, blockE10s); + }, + /** * Synchronously calculates and updates all the active flags in the database. */ diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_e10s_restartless.js b/toolkit/mozapps/extensions/test/xpcshell/test_e10s_restartless.js index 4b1d80dbad01..20598e34c42e 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_e10s_restartless.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_e10s_restartless.js @@ -3,6 +3,7 @@ */ const ID = "bootstrap1@tests.mozilla.org"; +const ID2 = "bootstrap2@tests.mozilla.org"; BootstrapMonitor.init(); @@ -115,6 +116,170 @@ add_task(function*() { yield promiseRestartManager(); }); +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(); + + // After install and restart we should block. + let blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_true(blocked); + + 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)); + + yield promiseRestartManager(); + + // After disable and restart we should not block. + blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_false(blocked); + + addon = yield promiseAddonByID(ID); + addon.userDisabled = false; + BootstrapMonitor.checkAddonNotStarted(ID); + do_check_false(addon.isActive); + do_check_true(hasFlag(addon.pendingOperations, AddonManager.PENDING_ENABLE)); + + yield promiseRestartManager(); + + // After re-enable and restart we should block. + blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_true(blocked); + + 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); + + yield promiseRestartManager(); + + // After uninstall and restart we should not block. + blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_false(blocked); + + restartManager(); +}); + +add_task(function*() { + gAppInfo.browserTabsRemoteAutostart = true; + Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true); + + let install1 = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve)); + let install2 = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap2_1"), resolve)); + yield promiseCompleteAllInstalls([install1, install2]); + do_check_eq(install1.state, AddonManager.STATE_INSTALLED); + do_check_eq(install2.state, AddonManager.STATE_INSTALLED); + do_check_true(hasFlag(install1.addon.pendingOperations, AddonManager.PENDING_INSTALL)); + do_check_true(hasFlag(install2.addon.pendingOperations, AddonManager.PENDING_INSTALL)); + + let addon = yield promiseAddonByID(ID); + let addon2 = yield promiseAddonByID(ID2); + + do_check_eq(addon, null); + do_check_eq(addon2, null); + + yield promiseRestartManager(); + + // After install and restart we should block. + let blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_true(blocked); + + BootstrapMonitor.checkAddonInstalled(ID); + BootstrapMonitor.checkAddonStarted(ID); + + addon = yield promiseAddonByID(ID); + do_check_neq(addon, null); + addon2 = yield promiseAddonByID(ID2); + do_check_neq(addon2, 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)); + + yield promiseRestartManager(); + + // After disable one addon and restart we should block. + blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_true(blocked); + + addon2 = yield promiseAddonByID(ID2); + + do_check_false(hasFlag(addon2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE)); + addon2.userDisabled = true; + BootstrapMonitor.checkAddonNotStarted(ID2); + do_check_false(addon2.isActive); + do_check_false(hasFlag(addon2.pendingOperations, AddonManager.PENDING_DISABLE)); + do_check_true(hasFlag(addon2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE)); + + yield promiseRestartManager(); + + // After disable both addons and restart we should not block. + blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_false(blocked); + + addon = yield promiseAddonByID(ID); + addon.userDisabled = false; + BootstrapMonitor.checkAddonNotStarted(ID); + do_check_false(addon.isActive); + do_check_true(hasFlag(addon.pendingOperations, AddonManager.PENDING_ENABLE)); + + yield promiseRestartManager(); + + // After re-enable one addon and restart we should block. + blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_true(blocked); + + 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); + + yield promiseRestartManager(); + + // After uninstall the only enabled addon and restart we should not block. + blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons"); + do_check_false(blocked); + + addon2 = yield promiseAddonByID(ID2); + addon2.uninstall(); + + restartManager(); +}); + // The hotfix is unaffected add_task(function*() { gAppInfo.browserTabsRemoteAutostart = true; diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 56bacfccd863..f8d3364fb230 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -4652,6 +4652,7 @@ enum { kE10sDisabledForAccessibility = 4, kE10sDisabledForMacGfx = 5, kE10sDisabledForBidi = 6, + kE10sDisabledForAddons = 7, }; #ifdef XP_WIN @@ -4760,6 +4761,15 @@ mozilla::BrowserTabsRemoteAutostart() gfxPrefs::GetSingleton().LayersOffMainThreadCompositionTestingEnabled(); #endif + bool addonsCanDisable = Preferences::GetBool("extensions.e10sBlocksEnabling", false); + bool disabledByAddons = Preferences::GetBool("extensions.e10sBlockedByAddons", false); + +#ifdef MOZ_CRASHREPORTER + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AddonsShouldHaveBlockedE10s"), + disabledByAddons ? NS_LITERAL_CSTRING("1") + : NS_LITERAL_CSTRING("0")); +#endif + if (e10sAllowed && prefEnabled) { if (disabledForA11y) { status = kE10sDisabledForAccessibility; @@ -4767,6 +4777,9 @@ mozilla::BrowserTabsRemoteAutostart() } else if (disabledForBidi) { status = kE10sDisabledForBidi; LogE10sBlockedReason("Disabled for RTL locales due to broken bidi detection."); + } else if (addonsCanDisable && disabledByAddons) { + status = kE10sDisabledForAddons; + LogE10sBlockedReason("3rd party add-ons are installed and enabled."); } else { gBrowserTabsRemoteAutostart = true; }