summaryrefslogtreecommitdiffstats
path: root/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs')
-rw-r--r--toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs300
1 files changed, 300 insertions, 0 deletions
diff --git a/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs b/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
new file mode 100644
index 0000000000..6fc1d789b9
--- /dev/null
+++ b/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
@@ -0,0 +1,300 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+// Invoke this task like `firefox.exe --backgroundtask message ...`.
+//
+// This task is complicated because it's meant for manual testing by QA but also
+// for automated testing. We might split these two functions at some point.
+//
+// First, note that the task takes significant configuration from the command
+// line. This is different than the eventual home for this functionality, the
+// background update task, which will take this configuration from the default
+// browsing profile.
+//
+// This task accepts the following command line arguments:
+//
+// --debug: enable very verbose debug logging. Note that the `MOZ_LOG`
+// environment variables still apply.
+//
+// --stage: use stage Remote Settings
+// (`https://firefox.settings.services.allizom.org/v1`) rather than production
+// (`https://firefox.settings.services.mozilla.com/v1`)
+//
+// --preview: enable Remote Settings and Experiment previews.
+//
+// `--url about:studies?...` (as copy-pasted from Experimenter): opt in to
+// `optin_branch` of experiment with `optin_slug` from `optin_collection`.
+//
+// `--url file:///path/to/recipe.json?optin_branch=...` (as downloaded from
+// Experimenter): opt in to `optin_branch` of given experiment recipe.
+//
+// `--experiments file:///path/to/recipe.json` (as downloaded from
+// Experimenter): enable given experiment recipe, possibly enrolling into a
+// branch via regular bucketing.
+//
+// `--targeting-snapshot /path/to/targeting.snapshot.json`: take default profile
+// targeting information from given JSON file.
+//
+// `--reset-storage`: clear Activity Stream storage, including lifetime limit
+// caps.
+//
+// The following flags are intended for automated testing.
+//
+// --sentinel: bracket important output with given sentinel for easy parsing.
+// --randomizationId: set Nimbus/Normandy randomization ID for deterministic bucketing.
+// --disable-alerts-service: do not show system/OS-level alerts.
+// --no-experiments: don't talk to Remote Settings server at all.
+// --no-datareporting: set `datareporting.healthreport.uploadEnabled=false` in
+// the background task profile.
+// --no-optoutstudies: set `app.shield.optoutstudies.enabled=false` in the
+// background task profile.
+
+import { EXIT_CODE } from "resource://gre/modules/BackgroundTasksManager.sys.mjs";
+// eslint-disable-next-line mozilla/no-browser-refs-in-toolkit
+import { ASRouter } from "resource:///modules/asrouter/ASRouter.sys.mjs";
+import { BackgroundTasksUtils } from "resource://gre/modules/BackgroundTasksUtils.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ ClientEnvironmentBase:
+ "resource://gre/modules/components-utils/ClientEnvironment.sys.mjs",
+ ExtensionUtils: "resource://gre/modules/ExtensionUtils.sys.mjs",
+ IndexedDB: "resource://gre/modules/IndexedDB.sys.mjs",
+ RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
+ RemoteSettingsClient:
+ "resource://services-settings/RemoteSettingsClient.sys.mjs",
+ // eslint-disable-next-line mozilla/no-browser-refs-in-toolkit
+ ToastNotification: "resource:///modules/asrouter/ToastNotification.sys.mjs",
+ Utils: "resource://services-settings/Utils.sys.mjs",
+});
+
+const SERVER_STAGE = "https://firefox.settings.services.allizom.org/v1";
+
+// Default profile targeting snapshot.
+let defaultProfileTargetingSnapshot = {};
+
+// Bracket important output with given sentinel for easy parsing.
+let outputInfo;
+outputInfo = (sentinel, info) => {
+ dump(`${sentinel}${JSON.stringify(info)}${sentinel}\n`);
+};
+
+function monkeyPatchRemoteSettingsClient({
+ last_modified = new Date().getTime(),
+ data = [],
+}) {
+ lazy.RemoteSettingsClient.prototype.get = async (options = {}) => {
+ outputInfo({ "RemoteSettingsClient.get": { options, response: { data } } });
+ return data;
+ };
+}
+
+async function handleCommandLine(commandLine) {
+ const CASE_INSENSITIVE = false;
+
+ // Output data over stdout for tests to consume and inspect.
+ let sentinel = commandLine.handleFlagWithParam("sentinel", CASE_INSENSITIVE);
+ outputInfo = outputInfo.bind(null, sentinel || "");
+
+ // We always want `nimbus.debug=true` for `about:studies?...` URLs.
+ Services.prefs.setBoolPref("nimbus.debug", true);
+
+ // Maybe drive up logging for this testing task.
+ Services.prefs.clearUserPref("services.settings.preview_enabled");
+ Services.prefs.clearUserPref(
+ "browser.newtabpage.activity-stream.asrouter.debugLogLevel"
+ );
+ Services.prefs.clearUserPref("messaging-system.log");
+ Services.prefs.clearUserPref("services.settings.loglevel");
+ Services.prefs.clearUserPref("toolkit.backgroundtasks.loglevel");
+ if (commandLine.handleFlag("debug", CASE_INSENSITIVE)) {
+ console.log("Saw --debug, making logging verbose");
+ Services.prefs.setBoolPref("services.settings.preview_enabled", true);
+ Services.prefs.setCharPref(
+ "browser.newtabpage.activity-stream.asrouter.debugLogLevel",
+ "debug"
+ );
+ Services.prefs.setCharPref("messaging-system.log", "debug");
+ Services.prefs.setCharPref("services.settings.loglevel", "debug");
+ Services.prefs.setCharPref("toolkit.backgroundtasks.loglevel", "debug");
+ }
+
+ // Always make alert service display message when showing an alert.
+ // Optionally suppress actually showing OS-level alerts.
+ let origAlertsService = lazy.ToastNotification.AlertsService;
+ let disableAlertsService = commandLine.handleFlag(
+ "disable-alerts-service",
+ CASE_INSENSITIVE
+ );
+ if (disableAlertsService) {
+ console.log("Saw --disable-alerts-service, not showing any alerts");
+ }
+ // Remove getter so that we can redefine property.
+ delete lazy.ToastNotification.AlertsService;
+ lazy.ToastNotification.AlertsService = {
+ showAlert(...args) {
+ outputInfo({ showAlert: { args } });
+ if (!disableAlertsService) {
+ origAlertsService.showAlert(...args);
+ }
+ },
+ };
+
+ let targetingSnapshotPath = commandLine.handleFlagWithParam(
+ "targeting-snapshot",
+ CASE_INSENSITIVE
+ );
+ if (targetingSnapshotPath) {
+ defaultProfileTargetingSnapshot = await IOUtils.readJSON(
+ targetingSnapshotPath
+ );
+ console.log(
+ `Saw --targeting-snapshot, read snapshot from ${targetingSnapshotPath}`
+ );
+ }
+ outputInfo({ defaultProfileTargetingSnapshot });
+
+ lazy.RemoteSettings.enablePreviewMode(false);
+ Services.prefs.clearUserPref(
+ "messaging-system.rsexperimentloader.collection_id"
+ );
+ if (commandLine.handleFlag("preview", CASE_INSENSITIVE)) {
+ console.log(
+ `Saw --preview, invoking 'RemoteSettings.enablePreviewMode(true)' and ` +
+ `setting 'messaging-system.rsexperimentloader.collection_id=\"nimbus-preview\"'`
+ );
+ lazy.RemoteSettings.enablePreviewMode(true);
+ Services.prefs.setCharPref(
+ "messaging-system.rsexperimentloader.collection_id",
+ "nimbus-preview"
+ );
+ }
+
+ Services.prefs.clearUserPref("services.settings.server");
+ Services.prefs.clearUserPref("services.settings.load_dump");
+ if (commandLine.handleFlag("stage", CASE_INSENSITIVE)) {
+ console.log(
+ `Saw --stage, setting 'services.settings.server="${SERVER_STAGE}"'`
+ );
+ Services.prefs.setCharPref("services.settings.server", SERVER_STAGE);
+ Services.prefs.setBoolPref("services.settings.load_dump", false);
+
+ if (lazy.Utils.SERVER_URL !== SERVER_STAGE) {
+ throw new Error(
+ "Pref services.settings.server ignored!" +
+ "remember to set MOZ_REMOTE_SETTINGS_DEVTOOLS=1 in beta and release."
+ );
+ }
+ }
+
+ // Allow to override Nimbus randomization ID with `--randomizationId ...`.
+ let randomizationId = commandLine.handleFlagWithParam(
+ "randomizationId",
+ CASE_INSENSITIVE
+ );
+ if (randomizationId) {
+ console.log(`Saw --randomizationId: ${randomizationId}`);
+ Services.prefs.setCharPref("app.normandy.user_id", randomizationId);
+ }
+ outputInfo({ randomizationId: lazy.ClientEnvironmentBase.randomizationId });
+
+ // Allow to override Nimbus experiments with `--experiments /path/to/data.json`.
+ let experiments = commandLine.handleFlagWithParam(
+ "experiments",
+ CASE_INSENSITIVE
+ );
+ if (experiments) {
+ let experimentsPath = commandLine.resolveFile(experiments).path;
+ let data = await IOUtils.readJSON(experimentsPath);
+ if (!Array.isArray(data)) {
+ if (data.permissions) {
+ data = data.data;
+ }
+ data = [data];
+ }
+
+ monkeyPatchRemoteSettingsClient({ data });
+
+ console.log(`Saw --experiments, read recipes from ${experimentsPath}`);
+ }
+
+ // Allow to turn off querying Remote Settings entirely, for tests.
+ if (
+ !experiments &&
+ commandLine.handleFlag("no-experiments", CASE_INSENSITIVE)
+ ) {
+ monkeyPatchRemoteSettingsClient({ data: [] });
+
+ console.log(`Saw --no-experiments, returning [] recipes`);
+ }
+
+ // Allow to test various red buttons.
+ Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled");
+ if (commandLine.handleFlag("no-datareporting", CASE_INSENSITIVE)) {
+ Services.prefs.setBoolPref(
+ "datareporting.healthreport.uploadEnabled",
+ false
+ );
+ console.log(
+ `Saw --no-datareporting, set 'datareporting.healthreport.uploadEnabled=false'`
+ );
+ }
+
+ Services.prefs.clearUserPref("app.shield.optoutstudies.enabled");
+ if (commandLine.handleFlag("no-optoutstudies", CASE_INSENSITIVE)) {
+ Services.prefs.setBoolPref("app.shield.optoutstudies.enabled", false);
+ console.log(
+ `Saw --no-datareporting, set 'app.shield.optoutstudies.enabled=false'`
+ );
+ }
+
+ outputInfo({
+ taskProfilePrefs: {
+ "app.shield.optoutstudies.enabled": Services.prefs.getBoolPref(
+ "app.shield.optoutstudies.enabled"
+ ),
+ "datareporting.healthreport.uploadEnabled": Services.prefs.getBoolPref(
+ "datareporting.healthreport.uploadEnabled"
+ ),
+ },
+ });
+
+ if (commandLine.handleFlag("reset-storage", CASE_INSENSITIVE)) {
+ console.log("Saw --reset-storage, deleting database 'ActivityStream'");
+ console.log(
+ `To hard reset, remove the contents of '${PathUtils.profileDir}'`
+ );
+ await lazy.IndexedDB.deleteDatabase("ActivityStream");
+ }
+}
+
+export async function runBackgroundTask(commandLine) {
+ console.error("runBackgroundTask: message");
+
+ // Most of the task is arranging configuration.
+ await handleCommandLine(commandLine);
+
+ // Here's where we actually start Nimbus and the Firefox Messaging
+ // System.
+ await BackgroundTasksUtils.enableNimbus(
+ commandLine,
+ defaultProfileTargetingSnapshot.environment
+ );
+
+ await BackgroundTasksUtils.enableFirefoxMessagingSystem(
+ defaultProfileTargetingSnapshot.environment
+ );
+
+ // At the time of writing, toast notifications are torn down as the
+ // process exits. Give the user a chance to see the notification.
+ await lazy.ExtensionUtils.promiseTimeout(1000);
+
+ // Everything in `ASRouter` is asynchronous, so we need to give everything a
+ // chance to complete.
+ outputInfo({ ASRouterState: ASRouter.state });
+
+ return EXIT_CODE.SUCCESS;
+}