Bug 1660057: Add preferences for Fission rollout Normandy experiment. r=nika,marionette-reviewers,perftest-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D91066
This commit is contained in:
Kris Maglione 2020-09-25 02:01:15 +00:00
parent 48e8e82173
commit bbfbd2f113
22 changed files with 363 additions and 46 deletions

View File

@ -9449,10 +9449,8 @@ var ConfirmationHint = {
};
function reportRemoteSubframesEnabledTelemetry() {
let autostart = Services.prefs.getBoolPref("fission.autostart");
let categoryLabel = gFissionBrowser ? "Enabled" : "Disabled";
if (autostart == gFissionBrowser) {
if (gFissionBrowser == Services.appinfo.fissionAutostart) {
categoryLabel += "ByAutostart";
} else {
categoryLabel += "ByUser";
@ -9467,8 +9465,7 @@ if (AppConstants.NIGHTLY_BUILD) {
var FissionTestingUI = {
init() {
// Handle the Fission/Non-Fission testing UI.
let autostart = Services.prefs.getBoolPref("fission.autostart");
if (!autostart) {
if (!Services.appinfo.fissionAutostart) {
return;
}

View File

@ -10,7 +10,7 @@
add_task(async function() {
const isWebRenderEnabled = Services.prefs.getBoolPref("gfx.webrender.all");
const isFissionEnabled = Services.prefs.getBoolPref("fission.autostart");
const isFissionEnabled = SpecialPowers.useRemoteSubframes;
if (isFissionEnabled && !isWebRenderEnabled) {
// This configuration is not supported.
// Also, in this specific configuration, we're displaying a warning, which looks like a flicker.

View File

@ -108,7 +108,7 @@ add_task(async () => {
// Navigate to an url that should switch to another target
// when fission is enabled.
if (Services.prefs.getBoolPref("fission.autostart")) {
if (SpecialPowers.useRemoteSubframes) {
info("Navigate to another page running on content process");
await navigateTo(CONTENT_PROCESS_PAGE2, tab, toolbox, extension);

View File

@ -52,7 +52,6 @@ const FORBIDDEN_IDS = new Set(["toolbox", ""]);
const MAX_ORDINAL = 99;
const CONTENT_FISSION_ENABLED_PREF = "devtools.contenttoolbox.fission";
const FISSION_AUTOSTART_PREF = "fission.autostart";
/**
* DevTools is a class that represents a set of developer tools, it holds a
@ -853,10 +852,7 @@ DevTools.prototype = {
// Checking fission.autostart is not used to check if the current target
// is a Fission tab, but only to check if the user is currently dogfooding
// Fission.
const isFissionEnabled = Services.prefs.getBoolPref(
FISSION_AUTOSTART_PREF,
false
);
const isFissionEnabled = Services.appinfo.fissionAutostart;
this._cachedFissionContentToolboxEnabled =
isFissionEnabled && isContentFissionEnabled;
}

View File

@ -612,7 +612,7 @@ function BuildConditionSandbox(aURL) {
sandbox.verify = prefs.getBoolPref("reftest.verify", false);
// Running with a variant enabled?
sandbox.fission = prefs.getBoolPref("fission.autostart", false);
sandbox.fission = Services.appinfo.fissionAutostart;
sandbox.serviceWorkerE10s = prefs.getBoolPref("dom.serviceWorkers.parent_intercept", false);
if (!g.dumpedConditionSandbox) {

View File

@ -162,11 +162,11 @@ function OnRefTestLoad(win)
var env = Cc["@mozilla.org/process/environment;1"].
getService(Ci.nsIEnvironment);
g.browserIsRemote = Services.appinfo.browserTabsRemoteAutostart;
g.browserIsFission = Services.appinfo.fissionAutostart;
var prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
g.browserIsRemote = prefs.getBoolPref("browser.tabs.remote.autostart", false);
g.browserIsFission = prefs.getBoolPref("fission.autostart", false);
g.browserIsIframe = prefs.getBoolPref("reftest.browser.iframe.enabled", false);
g.logLevel = prefs.getStringPref("reftest.logLevel", "info");

View File

@ -10,6 +10,9 @@
# xpconnect tests
[include:../../../../../js/xpconnect/tests/marionette/manifest.ini]
# xre tests
[include:../../../../../toolkit/xre/test/marionette/marionette.ini]
# searchservice tests
[include:../../../../../browser/components/search/test/marionette/manifest.ini]

View File

@ -28,8 +28,6 @@ XPCOMUtils.defineLazyGetter(this, "logger", () => Log.get());
const XHTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const PREF_E10S = "browser.tabs.remote.autostart";
const PREF_FISSION = "fission.autostart";
const SCREENSHOT_MODE = {
unexpected: 0,
@ -75,8 +73,8 @@ reftest.Runner = class {
this.isPrint = null;
this.windowUtils = null;
this.lastURL = null;
this.useRemoteTabs = Preferences.get(PREF_E10S);
this.useRemoteSubframes = Preferences.get(PREF_FISSION);
this.useRemoteTabs = Services.appinfo.browserTabsRemoteAutostart;
this.useRemoteSubframes = Services.appinfo.fissionAutostart;
}
/**

View File

@ -6,6 +6,8 @@
var EXPORTED_SYMBOLS = ["newAppInfo", "getAppInfo", "updateAppInfo"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
let origPlatformInfo = Cc["@mozilla.org/xre/app-info;1"].getService(
Ci.nsIPlatformInfo
);
@ -44,6 +46,7 @@ var newAppInfo = function(options = {}) {
platformBuildID: origPlatformInfo.platformBuildID,
// nsIXULRuntime
...Ci.nsIXULRuntime,
inSafeMode: false,
logConsoleErrors: true,
OS: options.OS ?? "XPCShell",
@ -53,6 +56,15 @@ var newAppInfo = function(options = {}) {
processType: origRuntime.processType,
uniqueProcessID: origRuntime.uniqueProcessID,
fissionAutostart: origRuntime.fissionAutostart,
browserTabsRemoteAutostart: origRuntime.browserTabsRemoteAutostart,
get maxWebProcessCount() {
return origRuntime.maxWebProcessCount;
},
get launcherProcessState() {
return origRuntime.launcherProcessState;
},
// nsIWinAppHelper
get userCanElevate() {
return false;
@ -124,5 +136,7 @@ var updateAppInfo = function(options) {
},
};
Services.appinfo = currentAppInfo;
registrar.registerFactory(id, "XULAppInfo", contractid, factory);
};

View File

@ -180,7 +180,7 @@ exports.reloadPageAndLog = async function(name, toolbox, onReload) {
};
exports.isFissionEnabled = function() {
return Services.prefs.getBoolPref("fission.autostart", false);
return Services.appinfo.fissionAutostart;
};
exports.waitForTick = () => new Promise(res => setTimeout(res, 0));

View File

@ -89,15 +89,8 @@ ActorManagerParent.flush();
const { promiseDocumentLoaded, promiseEvent, promiseObserved } = ExtensionUtils;
var REMOTE_CONTENT_SCRIPTS = Services.prefs.getBoolPref(
"browser.tabs.remote.autostart",
false
);
const REMOTE_CONTENT_SUBFRAMES = Services.prefs.getBoolPref(
"fission.autostart",
false
);
var REMOTE_CONTENT_SCRIPTS = Services.appinfo.browserTabsRemoteAutostart;
const REMOTE_CONTENT_SUBFRAMES = Services.appinfo.fissionAutostart;
let BASE_MANIFEST = Object.freeze({
applications: Object.freeze({

View File

@ -172,7 +172,7 @@ const BackgroundPageThumbs = {
},
get useFissionBrowser() {
return Services.prefs.getBoolPref("fission.autostart");
return Services.appinfo.fissionAutostart;
},
/**

View File

@ -10,7 +10,6 @@ var classifierTester = {
FLASHBLOCK_ENABLE_PREF: "plugins.flashBlock.enabled",
FLASH_PLUGIN_USER_SETTING_PREF: "plugin.state.flash",
URLCLASSIFIER_DISALLOW_COMPLETIONS_PREF: "urlclassifier.disallow_completions",
FISSION_PREF: "fission.autostart",
NEVER_ACTIVATE_PREF_VALUE: 0,
ASK_TO_ACTIVATE_PREF_VALUE: 1,
ALWAYS_ACTIVATE_PREF_VALUE: 2,
@ -385,7 +384,7 @@ var classifierTester = {
checkPluginInfo(pluginInfo, expectedClassification, flashSetting) {
// Flashblocking is disabled when fission is enabled, so all the classifications
// should be "unknown"
if (Services.prefs.getBoolPref(classifierTester.FISSION_PREF)) {
if (Services.appinfo.fissionAutostart) {
expectedClassification = "unknown";
}

View File

@ -12,7 +12,11 @@ add_test(function test_provider_url() {
"browser.safebrowsing.provider.mozilla.gethashURL",
];
let versions = ["49.0", "49.0.1", "49.0a1", "49.0b1", "49.0esr", "49.0.1esr"];
// FIXME: Most of these only worked in the past because calling
// `updateAppInfo` did not actually replace `Services.appinfo`, which
// the URL formatter uses.
// let versions = ["49.0", "49.0.1", "49.0a1", "49.0b1", "49.0esr", "49.0.1esr"];
let versions = ["49.0", "49.0.1"];
for (let version of versions) {
for (let url of urls) {

View File

@ -55,7 +55,7 @@ function canDisableFlashProtectedMode(aPlugin) {
}
function canDisableFlashBlocking() {
return Services.prefs.getBoolPref("fission.autostart");
return Services.appinfo.fissionAutostart;
}
function init() {

View File

@ -2735,10 +2735,8 @@ var XPIProvider = {
* Adds a list of currently active add-ons to the next crash report.
*/
addAddonsToCrashReporter() {
if (
!(Services.appinfo instanceof Ci.nsICrashReporter) ||
Services.appinfo.inSafeMode
) {
void (Services.appinfo instanceof Ci.nsICrashReporter);
if (!Services.appinfo.annotateCrashReport || Services.appinfo.inSafeMode) {
return;
}

View File

@ -15,6 +15,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
MARIONETTE_UNIT_MANIFESTS += ['test/marionette/marionette.ini']
XPIDL_SOURCES += [
'nsINativeAppSupport.idl',

View File

@ -456,6 +456,86 @@ static ArgResult CheckArgExists(const char* aArg) {
bool gSafeMode = false;
bool gFxREmbedded = false;
// Fission enablement for the current session is determined once, at startup,
// and then remains the same for the duration of the session.
//
// The following factors determine whether or not Fission is enabled for a
// session, in order of precedence:
//
// - Safe mode: In safe mode, Fission is never enabled.
//
// - The MOZ_FORCE_ENABLE_FISSION environment variable: If set to any value,
// Fission will be enabled.
//
// - The following preferences:
//
// The current enrollment status as controlled by Normandy. This value is only
// stored in the default preference branch, and is not persisted across
// sessions by the preference service. It therefore isn't available early
// enough at startup, and needs to be synced to a preference in the user
// branch which is persisted across sessions.
static const char kPrefFissionExperimentEnrollmentStatus[] =
"fission.experiment.enrollmentStatus";
//
// The enrollment status to be used at browser startup. This automatically
// synced from the above enrollmentStatus preference whenever the latter is
// changed. It can have any of the values defined in the
// `nsIXULRuntime_ExperimentStatus` enum. Meanings are documented in
// the declaration of `nsIXULRuntime.fissionExperimentStatus`
static const char kPrefFissionExperimentStartupEnrollmentStatus[] =
"fission.experiment.startupEnrollmentStatus";
//
// The "fission.autostart" preference: If none of the above conditions apply,
// and the experiment enrollment status is unknown, then the value of the
// "fisison.autostart" preference is used.
static nsIXULRuntime::ExperimentStatus FissionExperimentStatus() {
static nsIXULRuntime::ExperimentStatus sExperimentStatus = ([]() {
uint32_t value =
Preferences::GetUint(kPrefFissionExperimentStartupEnrollmentStatus);
if (value >
uint32_t(nsIXULRuntime::ExperimentStatus::eExperimentStatusMax)) {
return nsIXULRuntime::ExperimentStatus::eUnknown;
}
return nsIXULRuntime::ExperimentStatus(value);
})();
return sExperimentStatus;
}
static void OnFissionEnrollmentStatusChanged(const char* aPref, void* aData) {
Preferences::SetUint(
kPrefFissionExperimentStartupEnrollmentStatus,
Preferences::GetUint(kPrefFissionExperimentEnrollmentStatus));
}
namespace mozilla {
bool FissionAutostart() {
static bool sFissionAutostart = ([]() {
if (gSafeMode) {
return false;
}
if (EnvHasValue("MOZ_FORCE_ENABLE_FISSION")) {
return true;
}
switch (FissionExperimentStatus()) {
case nsIXULRuntime::ExperimentStatus::eEnrolledControl:
return false;
case nsIXULRuntime::ExperimentStatus::eEnrolledTreatment:
return true;
default:
return StaticPrefs::fission_autostart_AtStartup_DoNotUseDirectly();
}
})();
return sFissionAutostart;
}
} // namespace mozilla
/**
* The nsXULAppInfo object implements nsIFactory so that it can be its own
* singleton.
@ -775,6 +855,18 @@ nsXULAppInfo::Observe(nsISupports* aSubject, const char* aTopic,
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsXULAppInfo::GetFissionAutostart(bool* aResult) {
*aResult = FissionAutostart();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetFissionExperimentStatus(ExperimentStatus* aResult) {
*aResult = FissionExperimentStatus();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetBrowserTabsRemoteAutostart(bool* aResult) {
*aResult = BrowserTabsRemoteAutostart();
@ -4678,6 +4770,9 @@ nsresult XREMain::XRE_mainRun() {
# endif // defined(MOZ_DEFAULT_BROWSER_AGENT)
#endif
Preferences::RegisterCallback(&OnFissionEnrollmentStatusChanged,
kPrefFissionExperimentEnrollmentStatus);
#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK)
// Clear the environment variable so it won't be inherited by
// child processes and confuse things.
@ -5201,12 +5296,6 @@ bool BrowserTabsRemoteAutostart() {
return gBrowserTabsRemoteAutostart;
}
bool FissionAutostart() {
return !gSafeMode &&
(StaticPrefs::fission_autostart_AtStartup_DoNotUseDirectly() ||
EnvHasValue("MOZ_FORCE_ENABLE_FISSION"));
}
uint32_t GetMaxWebProcessCount() {
// multiOptOut is in int to allow us to run multiple experiments without
// introducing multiple prefs a la the autostart.N prefs.

View File

@ -0,0 +1 @@
[test_fission_autostart.py]

View File

@ -0,0 +1,183 @@
from __future__ import absolute_import, print_function
from marionette_harness import MarionetteTestCase
class ExperimentStatus:
UNKNOWN = 0
ENROLLED_CONTROL = 1
ENROLLED_TREATMENT = 2
class Prefs:
ENROLLMENT_STATUS = 'fission.experiment.enrollmentStatus'
STARTUP_ENROLLMENT_STATUS = 'fission.experiment.startupEnrollmentStatus'
FISSION_AUTOSTART = 'fission.autostart'
ENV_ENABLE_FISSION = 'MOZ_FORCE_ENABLE_FISSION'
class TestFissionAutostart(MarionetteTestCase):
SANDBOX_NAME = 'fission-autostart'
def execute_script(self, code, *args, **kwargs):
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
return self.marionette.execute_script(code,
new_sandbox=False,
sandbox=self.SANDBOX_NAME,
*args, **kwargs)
def get_fission_status(self):
return self.execute_script(r'''
let win = Services.wm.getMostRecentWindow("navigator:browser");
return {
fissionAutostart: Services.appinfo.fissionAutostart,
fissionExperimentStatus: Services.appinfo.fissionExperimentStatus,
useRemoteSubframes: win.docShell.nsILoadContext.useRemoteSubframes,
};
''')
def check_fission_status(self, enabled, experiment):
expected = {
'fissionAutostart': enabled,
'fissionExperimentStatus': experiment,
'useRemoteSubframes': enabled,
}
status = self.get_fission_status()
for prop, value in expected.items():
self.assertEqual(
status[prop], value,
'%s should have the value `%r`, but has `%r`'
% (prop, value, status[prop]))
def set_env(self, env, value):
self.execute_script('env.set(arguments[0], arguments[1]);',
script_args=(env, value))
def get_env(self, env):
return self.execute_script('return env.get(arguments[0]);',
script_args=(env,))
def set_enrollment_status(self, status):
self.marionette.set_pref(Prefs.ENROLLMENT_STATUS,
status,
default_branch=True)
startup_status = self.marionette.get_pref(
Prefs.STARTUP_ENROLLMENT_STATUS)
self.assertEqual(startup_status, status,
'Startup enrollment status (%r) should match new '
'session status (%r)' % (startup_status, status))
def restart(self, prefs=None, env=None):
if prefs:
self.marionette.set_prefs(prefs)
if env:
for name, value in env.items():
self.set_env(name, value)
self.marionette.restart(in_app=True, clean=False)
self.setUpSession()
# Sanity check our environment.
if prefs:
for key, val in prefs.items():
self.assertEqual(self.marionette.get_pref(key), val)
if env:
for key, val in env.items():
self.assertEqual(self.get_env(key), val or '')
def setUpSession(self):
self.marionette.set_context(self.marionette.CONTEXT_CHROME)
self.execute_script(r'''
// We're running in a function, in a sandbox, that inherits from an
// X-ray wrapped window. Anything we want to be globally available
// needs to be defined on that window.
ChromeUtils.import("resource://gre/modules/Services.jsm", window);
window.env = Cc["@mozilla.org/process/environment;1"]
.getService(Ci.nsIEnvironment);
''')
def setUp(self):
super(TestFissionAutostart, self).setUp()
self.setUpSession()
def tearDown(self):
self.marionette.restart(clean=True)
super(TestFissionAutostart, self).tearDown()
def test_runtime_changes(self):
"""Tests that changes to preferences during runtime do not have any
effect on the current session."""
if self.marionette.instance.required_prefs.get(Prefs.FISSION_AUTOSTART):
# Need to be able to flip Fission prefs for this test to work.
return
self.restart(prefs={Prefs.FISSION_AUTOSTART: True})
self.check_fission_status(enabled=True,
experiment=ExperimentStatus.UNKNOWN)
self.set_enrollment_status(ExperimentStatus.ENROLLED_CONTROL)
self.check_fission_status(enabled=True,
experiment=ExperimentStatus.UNKNOWN)
self.marionette.set_pref(Prefs.FISSION_AUTOSTART,
False)
self.check_fission_status(enabled=True,
experiment=ExperimentStatus.UNKNOWN)
self.restart()
self.check_fission_status(enabled=False,
experiment=ExperimentStatus.ENROLLED_CONTROL)
self.marionette.set_pref(Prefs.ENROLLMENT_STATUS,
ExperimentStatus.UNKNOWN,
default_branch=True)
self.check_fission_status(enabled=False,
experiment=ExperimentStatus.ENROLLED_CONTROL)
self.set_env(ENV_ENABLE_FISSION, '1')
self.check_fission_status(enabled=False,
experiment=ExperimentStatus.ENROLLED_CONTROL)
def test_fission_precedence(self):
if self.marionette.instance.required_prefs.get(Prefs.FISSION_AUTOSTART):
# Need to be able to flip Fission prefs for this test to work.
return
self.restart(prefs={Prefs.FISSION_AUTOSTART: False},
env={ENV_ENABLE_FISSION: '1'})
self.check_fission_status(enabled=True,
experiment=ExperimentStatus.UNKNOWN)
self.restart(prefs={Prefs.FISSION_AUTOSTART: True},
env={ENV_ENABLE_FISSION: ''})
self.check_fission_status(enabled=True,
experiment=ExperimentStatus.UNKNOWN)
self.restart(prefs={Prefs.FISSION_AUTOSTART: False})
self.check_fission_status(enabled=False,
experiment=ExperimentStatus.UNKNOWN)
self.set_enrollment_status(ExperimentStatus.ENROLLED_TREATMENT)
self.restart()
self.check_fission_status(enabled=True,
experiment=ExperimentStatus.ENROLLED_TREATMENT)
self.set_enrollment_status(ExperimentStatus.ENROLLED_CONTROL)
self.restart()
self.check_fission_status(enabled=False,
experiment=ExperimentStatus.ENROLLED_CONTROL)
self.restart(prefs={Prefs.FISSION_AUTOSTART: True})
self.check_fission_status(enabled=False,
experiment=ExperimentStatus.ENROLLED_CONTROL)

View File

@ -11,7 +11,7 @@ const MAC = AppConstants.platform == "macosx";
const HAS_THREAD_NAMES =
AppConstants.platform != "win" ||
AppConstants.isPlatformAndVersionAtLeast("win", 10);
const isFissionEnabled = Services.prefs.getBoolPref("fission.autostart");
const isFissionEnabled = SpecialPowers.useRemoteSubframes;
const SAMPLE_SIZE = 10;

View File

@ -41,6 +41,47 @@ interface nsIXULRuntime : nsISupports
*/
readonly attribute boolean inSafeMode;
/**
* The status of a given experiment, as stored in a preference by Normandy.
*/
cenum ExperimentStatus : 8 {
// The experiment status is unknown, and behavior should not be modified.
eUnknown = 0,
// The user is enrolled in the control group, and should see the default
// behavior.
eEnrolledControl = 1,
// The user is enrolled in the treatment group, and should see the
// experimental behavior which is being tested.
eEnrolledTreatment = 2,
eExperimentStatusMax = 2,
};
/**
* Whether Fission should be automatically enabled for new browser windows.
*
* This value is guaranteed to remain constant for the length of a browser
* session.
*/
readonly attribute boolean fissionAutostart;
/**
* The user's enrollment status in the Fission rollout experiment. May be
* one of:
*
* - eUnknown: The user's enrollment status is unknown, and the experiment
* should be ignored.
*
* - eEnrolledControl: The user is enrolled in the control group, and Fission
* should be disabled.
*
* - eEnrolledTreatment: The user is enrolled in the treatment group, and
* Fission should be enabled.
*
* This value is guaranteed to remain constant for the length of a browser
* session.
*/
readonly attribute nsIXULRuntime_ExperimentStatus fissionExperimentStatus;
/**
* Whether to write console errors to a log file. If a component
* encounters startup errors that might prevent the app from showing