--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -8,17 +8,17 @@ const {classes: Cc, interfaces: Ci, util
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/UpdateUtils.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
// The amount of people to be part of e10s
const TEST_THRESHOLD = {
- "beta": 0.5, // 50%
+ "beta": 0.9, // 90%
"release": 1.0, // 100%
"esr": 1.0, // 100%
};
const ADDON_ROLLOUT_POLICY = {
"beta": "50allmpc",
"release": "50allmpc",
"esr": "esrA", // WebExtensions and Addons with mpc=true
@@ -46,16 +46,20 @@ const PREF_COHORT_SAMPLE = "e10s.r
const PREF_COHORT_NAME = "e10s.rollout.cohort";
const PREF_E10S_OPTED_IN = "browser.tabs.remote.autostart";
const PREF_E10S_FORCE_ENABLED = "browser.tabs.remote.force-enable";
const PREF_E10S_FORCE_DISABLED = "browser.tabs.remote.force-disable";
const PREF_TOGGLE_E10S = "browser.tabs.remote.autostart.2";
const PREF_E10S_ADDON_POLICY = "extensions.e10s.rollout.policy";
const PREF_E10S_ADDON_BLOCKLIST = "extensions.e10s.rollout.blocklist";
const PREF_E10S_HAS_NONEXEMPT_ADDON = "extensions.e10s.rollout.hasAddon";
+const PREF_E10S_MULTI_OPTOUT = "dom.ipc.multiOptOut";
+const PREF_E10S_PROCESSCOUNT = "dom.ipc.processCount";
+const PREF_E10S_MULTI_ADDON_BLOCKS = "extensions.e10sMultiBlocksEnabling";
+const PREF_E10S_MULTI_BLOCKED_BY_ADDONS = "extensions.e10sMultiBlockedByAddons";
function startup() {
// In theory we only need to run this once (on install()), but
// it's better to also run it on every startup. If the user has
// made manual changes to the prefs, this will keep the data
// reported more accurate.
// It's also fine (and preferred) to just do it here on startup
// (instead of observing prefs), because e10s takes a restart
@@ -99,106 +103,153 @@ function defineCohort() {
} else {
Preferences.reset(PREF_E10S_ADDON_POLICY);
}
let userOptedOut = optedOut();
let userOptedIn = optedIn();
let disqualified = (Services.appinfo.multiprocessBlockPolicy != 0);
let testThreshold = TEST_THRESHOLD[updateChannel];
- let testGroup = (getUserSample() < testThreshold);
+ let testGroup = (getUserSample(false) < testThreshold);
let hasNonExemptAddon = Preferences.get(PREF_E10S_HAS_NONEXEMPT_ADDON, false);
let temporaryDisqualification = getTemporaryDisqualification();
let temporaryQualification = getTemporaryQualification();
let cohortPrefix = "";
if (disqualified) {
cohortPrefix = "disqualified-";
} else if (hasNonExemptAddon) {
cohortPrefix = `addons-set${addonPolicy}-`;
}
- if (userOptedOut) {
+ let inMultiExperiment = false;
+ if (userOptedOut.e10s || userOptedOut.multi) {
+ // If we detected that the user opted out either for multi or e10s, then
+ // the proper prefs must already be set.
setCohort("optedOut");
- } else if (userOptedIn) {
+ } else if (userOptedIn.e10s) {
setCohort("optedIn");
} else if (temporaryDisqualification != "") {
// Users who are disqualified by the backend (from multiprocessBlockPolicy)
// can be put into either the test or control groups, because e10s will
// still be denied by the backend, which is useful so that the E10S_STATUS
// telemetry probe can be correctly set.
// For these volatile disqualification reasons, however, we must not try
// to activate e10s because the backend doesn't know about it. E10S_STATUS
// here will be accumulated as "2 - Disabled", which is fine too.
setCohort(`temp-disqualified-${temporaryDisqualification}`);
Preferences.reset(PREF_TOGGLE_E10S);
+ Preferences.reset(PREF_E10S_PROCESSCOUNT + ".web");
} else if (!disqualified && testThreshold < 1.0 &&
temporaryQualification != "") {
// Users who are qualified for e10s and on channels where some population
// would not receive e10s can be pushed into e10s anyway via a temporary
// qualification which overrides the user sample value when non-empty.
setCohort(`temp-qualified-${temporaryQualification}`);
Preferences.set(PREF_TOGGLE_E10S, true);
+ inMultiExperiment = true;
} else if (testGroup) {
setCohort(`${cohortPrefix}test`);
Preferences.set(PREF_TOGGLE_E10S, true);
+ inMultiExperiment = true;
} else {
setCohort(`${cohortPrefix}control`);
Preferences.reset(PREF_TOGGLE_E10S);
+ Preferences.reset(PREF_E10S_PROCESSCOUNT + ".web");
+ }
+
+ // Now determine if this user should be in the e10s-multi experiment.
+ // - We only run the experiment on the beta channel.
+ // - We decided above whether this user qualifies for the experiment.
+ // - If the user already opted into multi, then their prefs are already set
+ // correctly, we're done.
+ // - If the user has addons that disqualify them for multi, leave them with
+ // the default number of content processes (1 on beta) but still in the
+ // test cohort.
+ if (updateChannel !== "beta" ||
+ !inMultiExperiment ||
+ userOptedIn.multi ||
+ getAddonsDisqualifyForMulti()) {
+ Preferences.reset(PREF_E10S_PROCESSCOUNT + ".web");
+ return;
+ }
+
+ // The user is in the multi experiment!
+ // Decide how many content processes to use for this user.
+ let BUCKETS = {
+ 1: .25,
+ 2: .5,
+ 4: .75,
+ 8: 1
+ };
+
+ let multiUserSample = getUserSample(true);
+ for (let sampleName of Object.getOwnPropertyNames(BUCKETS)) {
+ if (multiUserSample < BUCKETS[sampleName]) {
+ setCohort(`${cohortPrefix}multiBucket${sampleName}`);
+ Preferences.set(PREF_E10S_PROCESSCOUNT + ".web", sampleName);
+ break;
+ }
}
}
function shutdown(data, reason) {
}
function uninstall() {
}
-function getUserSample() {
- let prefValue = Preferences.get(PREF_COHORT_SAMPLE, undefined);
+function getUserSample(multi) {
+ let pref = multi ? (PREF_COHORT_SAMPLE + ".multi") : PREF_COHORT_SAMPLE;
+ let prefValue = Preferences.get(pref, undefined);
let value = 0.0;
if (typeof(prefValue) == "string") {
value = parseFloat(prefValue, 10);
return value;
}
if (typeof(prefValue) == "number") {
// convert old integer value
value = prefValue / 100;
} else {
value = Math.random();
}
- Preferences.set(PREF_COHORT_SAMPLE, value.toString().substr(0, 8));
+ Preferences.set(pref, value.toString().substr(0, 8));
return value;
}
function setCohort(cohortName) {
Preferences.set(PREF_COHORT_NAME, cohortName);
try {
if (Ci.nsICrashReporter) {
Services.appinfo.QueryInterface(Ci.nsICrashReporter).annotateCrashReport("E10SCohort", cohortName);
}
} catch (e) {}
}
function optedIn() {
- return Preferences.get(PREF_E10S_OPTED_IN, false) ||
- Preferences.get(PREF_E10S_FORCE_ENABLED, false);
+ let e10s = Preferences.get(PREF_E10S_OPTED_IN, false) ||
+ Preferences.get(PREF_E10S_FORCE_ENABLED, false);
+ let multi = Preferences.isSet(PREF_E10S_PROCESSCOUNT);
+ return { e10s, multi };
}
function optedOut() {
// Users can also opt-out by toggling back the pref to false.
// If they reset the pref instead they might be re-enabled if
// they are still part of the threshold.
- return Preferences.get(PREF_E10S_FORCE_DISABLED, false) ||
- (Preferences.isSet(PREF_TOGGLE_E10S) &&
- Preferences.get(PREF_TOGGLE_E10S) == false);
+ let e10s = Preferences.get(PREF_E10S_FORCE_DISABLED, false) ||
+ (Preferences.isSet(PREF_TOGGLE_E10S) &&
+ Preferences.get(PREF_TOGGLE_E10S) == false);
+ let multi = Preferences.get(PREF_E10S_MULTI_OPTOUT, 0) >=
+ Services.appinfo.E10S_MULTI_EXPERIMENT;
+ return { e10s, multi };
}
/* If this function returns a non-empty string, it
* means that this particular user should be temporarily
* disqualified due to some particular reason.
* If a user shouldn't be disqualified, then an empty
* string must be returned.
*/
@@ -220,8 +271,13 @@ function getTemporaryQualification() {
const PREF_OPENED_DEVTOOLS = "devtools.telemetry.tools.opened.version";
let hasOpenedDevTools = Preferences.isSet(PREF_OPENED_DEVTOOLS);
if (hasOpenedDevTools) {
return "devtools";
}
return "";
}
+
+function getAddonsDisqualifyForMulti() {
+ return Preferences.get("extensions.e10sMultiBlocksEnabling", false) &&
+ Preferences.get("extensions.e10sMultiBlockedByAddons", false);
+}