Files
tubestation/toolkit/components/nimbus/lib/Migrations.sys.mjs
Beth Rennie 9af0a4ef3d Bug 1946763 - Initialize Nimbus via ExperimentAPI.init() in background tasks r=nalexander,nimbus-reviewers,chumphreys
In bug 1941961, Nimbus initialization moved from Normandy into
ExperimentAPI. This updates BackgroundTaskUtils to call into the same
API as the rest of the browser to initialize Nimbus. To support this,
`ExperimentAPI.init()` now supports options for providing additional
context to the ExperimentManager's targeting context and forcing a sync
in the RemoteSettingsExperimentLoader.

Differential Revision: https://phabricator.services.mozilla.com/D242800
2025-04-08 22:58:14 +00:00

175 lines
4.8 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
FirefoxLabs: "resource://nimbus/FirefoxLabs.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
NimbusTelemetry: "resource://nimbus/lib/Telemetry.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "log", () => {
const { Logger } = ChromeUtils.importESModule(
"resource://messaging-system/lib/Logger.sys.mjs"
);
return new Logger("NimbusMigrations");
});
function migration(name, fn) {
return { name, fn };
}
export const NIMBUS_MIGRATION_PREF = "nimbus.migrations.latest";
export const LABS_MIGRATION_FEATURE_MAP = {
"auto-pip": "firefox-labs-auto-pip",
"urlbar-ime-search": "firefox-labs-urlbar-ime-search",
"jpeg-xl": "firefox-labs-jpeg-xl",
};
async function migrateFirefoxLabsEnrollments() {
const bts = Cc["@mozilla.org/backgroundtasks;1"]?.getService(
Ci.nsIBackgroundTasks
);
if (bts?.isBackgroundTaskMode) {
// This migration does not apply to background task mode.
return;
}
await lazy.ExperimentAPI._rsLoader.finishedUpdating();
await lazy.ExperimentAPI._rsLoader.withUpdateLock(
async () => {
const labs = await lazy.FirefoxLabs.create();
let didEnroll = false;
for (const [feature, slug] of Object.entries(
LABS_MIGRATION_FEATURE_MAP
)) {
const pref =
lazy.NimbusFeatures[feature].manifest.variables.enabled.setPref.pref;
if (!labs.get(slug)) {
// If the recipe is not available then either it is no longer live or
// the targeting did not match.
continue;
}
if (!Services.prefs.getBoolPref(pref, false)) {
// Only enroll if the migration pref is set.
continue;
}
await labs.enroll(slug, "control");
// We need to overwrite the original pref value stored in the
// ExperimentStore so that unenrolling will disable the feature.
const enrollment = lazy.ExperimentAPI._manager.store.get(slug);
if (!enrollment) {
lazy.log.error(`Enrollment with ${slug} should exist but does not`);
continue;
}
if (!enrollment.active) {
lazy.log.error(
`Enrollment with slug ${slug} should be active but is not.`
);
continue;
}
const prefEntry = enrollment.prefs?.find(entry => entry.name === pref);
if (!prefEntry) {
lazy.log.error(
`Enrollment with slug ${slug} does not set pref ${pref}`
);
continue;
}
didEnroll = true;
prefEntry.originalValue = false;
}
if (didEnroll) {
// Trigger a save of the ExperimentStore since we've changed some data
// structures without using set().
// We do not have to sync these changes to child processes because the
// data is only used in the parent process.
lazy.ExperimentAPI._manager.store._store.saveSoon();
}
},
{ mode: "shared" }
);
}
export class MigrationError extends Error {
static Reason = Object.freeze({
UNKNOWN: "unknown",
});
constructor(reason) {
super(`Migration error: ${reason}`);
this.reason = reason;
}
}
export const NimbusMigrations = {
migration,
/**
* Apply any outstanding migrations.
*/
async applyMigrations() {
const latestMigration = Services.prefs.getIntPref(
NIMBUS_MIGRATION_PREF,
-1
);
let lastSuccess = latestMigration;
lazy.log.debug(`applyMigrations: latestMigration = ${latestMigration}`);
for (let i = latestMigration + 1; i < this.MIGRATIONS.length; i++) {
const migration = this.MIGRATIONS[i];
lazy.log.debug(
`applyMigrations: applying migration ${i}: ${migration.name}`
);
try {
await migration.fn();
} catch (e) {
lazy.log.error(
`applyMigrations: error running migration ${i} (${migration.name}): ${e}`
);
let reason = MigrationError.Reason.UNKNOWN;
if (e instanceof MigrationError) {
reason = e.reason;
}
lazy.NimbusTelemetry.recordMigration(migration.name, reason);
break;
}
lastSuccess = i;
lazy.log.debug(
`applyMigrations: applied migration ${i}: ${migration.name}`
);
lazy.NimbusTelemetry.recordMigration(migration.name);
}
if (latestMigration != lastSuccess) {
Services.prefs.setIntPref(NIMBUS_MIGRATION_PREF, lastSuccess);
}
},
MIGRATIONS: [
migration("firefox-labs-enrollments", migrateFirefoxLabsEnrollments),
],
};