commit df083c2c45d1e7e81624a19cac3bf30cc7504c80
parent fc569a3a0b1831cb1174a5ffdde497786a94c21c
Author: Meg Viar <lmegviar@gmail.com>
Date: Tue, 30 Sep 2025 22:05:19 +0000
Bug 1990897 - Ensure legacy telemetry can be ungated via the new user TOU modal flow r=dmose,toolkit-telemetry-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D266232
Diffstat:
5 files changed, 209 insertions(+), 8 deletions(-)
diff --git a/toolkit/components/telemetry/app/TelemetryReportingPolicy.sys.mjs b/toolkit/components/telemetry/app/TelemetryReportingPolicy.sys.mjs
@@ -748,11 +748,19 @@ var TelemetryReportingPolicyImpl = {
},
/**
- * Check if we are allowed to upload data. In order to submit data both these conditions
- * should be true:
- * - The data submission preference should be true.
- * - The datachoices infobar should have been displayed.
+ * Check if we are allowed to upload data.
+ * Prerequisite: data submission is enabled (this.dataSubmissionEnabled).
*
+ * In order to submit data, at least ONE of these conditions should be true:
+ * 1. The TOU flow is bypassed via a pref or Nimbus variable AND the legacy
+ * notification flow bypass pref is set, so users bypass BOTH flows.
+ * 2. The TOU flow is bypassed via a pref or Nimbus variable and the legacy
+ * notification flow bypass pref is NOT set, so has been been shown the
+ * legacy flow (the data submission pref should be true and the
+ * datachoices infobar should have been displayed).
+ * 3. The user has accepted the Terms of Use AND the user has opted-in to
+ * sharing technical interaction data (the upload enabled pref should be
+ * true).
* @return {Boolean} True if we are allowed to upload data, false otherwise.
*/
canUpload() {
@@ -762,13 +770,31 @@ var TelemetryReportingPolicyImpl = {
return false;
}
- // Submission is enabled. We enable upload if user is notified or we need to bypass
- // the policy.
- const bypassNotification = Services.prefs.getBoolPref(
+ const bypassLegacyFlow = Services.prefs.getBoolPref(
TelemetryUtils.Preferences.BypassNotification,
false
);
- return this.isUserNotifiedOfCurrentPolicy || bypassNotification;
+ // TOU flow is bypassed if the Nimbus preonboarding feature is disabled
+ // (disabled by default for Linux via the fallback
+ // browser.preonboarding.enabled pref) or if the explicit bypass pref is
+ // set.
+ const bypassTOUFlow =
+ Services.prefs.getBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false) ||
+ !this._nimbusVariables.enabled;
+ const allowInteractionData = Services.prefs.getBoolPref(
+ "datareporting.healthreport.uploadEnabled",
+ false
+ );
+
+ // Condition 1
+ const canUploadBypassLegacyAndTOU = bypassLegacyFlow && bypassTOUFlow;
+ // Condition 2
+ const canUploadLegacy =
+ bypassTOUFlow && !bypassLegacyFlow && this.isUserNotifiedOfCurrentPolicy;
+ // Condition 3
+ const canUploadTOU = this.hasUserAcceptedCurrentTOU && allowInteractionData;
+
+ return canUploadBypassLegacyAndTOU || canUploadLegacy || canUploadTOU;
},
isFirstRun() {
diff --git a/toolkit/components/telemetry/tests/unit/test_HealthPing.js b/toolkit/components/telemetry/tests/unit/test_HealthPing.js
@@ -45,6 +45,15 @@ add_setup(async function setup() {
true
);
+ // Bypass TOU flow and legacy datareporting flow so that telemetry upload is allowed
+ Services.prefs.setBoolPref("termsofuse.bypassNotification", true);
+ Services.prefs.setBoolPref(
+ "datareporting.policy.dataSubmissionPolicyBypassNotification",
+ true
+ );
+ // Upload must be enabled to permit sending telemetry
+ Services.prefs.setBoolPref("datareporting.healthreport.uploadEnabled", true);
+
await TelemetryController.testSetup();
PingServer.start();
TelemetrySend.setServer("http://localhost:" + PingServer.port);
@@ -56,6 +65,11 @@ add_setup(async function setup() {
registerCleanupFunction(async function cleanup() {
await PingServer.stop();
+ Services.prefs.clearUserPref("termsofuse.bypassNotification");
+ Services.prefs.clearUserPref(
+ "datareporting.policy.dataSubmissionPolicyBypassNotification"
+ );
+ Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled");
});
add_task(async function test_sendImmediately() {
diff --git a/toolkit/components/telemetry/tests/unit/test_TOUNotificationFlow.js b/toolkit/components/telemetry/tests/unit/test_TOUNotificationFlow.js
@@ -674,3 +674,154 @@ add_task(
sinon.restore();
}
);
+
+add_task(
+ async function test_canUpload_unblocked_by_tou_accepted_and_uploadEnabled() {
+ if (AppConstants.platform === "linux") {
+ info("Skipping test for Linux where TOU flow is disabled by default");
+ return;
+ }
+ TelemetryReportingPolicy.reset();
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(
+ TelemetryUtils.Preferences.BypassNotification
+ );
+ Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled");
+ Services.prefs.clearUserPref("termsofuse.acceptedDate");
+ Services.prefs.clearUserPref("termsofuse.acceptedVersion");
+ TelemetryReportingPolicy.reset();
+ });
+
+ Services.prefs.setBoolPref(
+ TelemetryUtils.Preferences.BypassNotification,
+ false
+ );
+
+ // This is true by default for most users
+ Services.prefs.setBoolPref(
+ "datareporting.healthreport.uploadEnabled",
+ true
+ );
+
+ Assert.ok(
+ !TelemetryReportingPolicy.canUpload(),
+ "Before accepting TOU legacy upload is blocked"
+ );
+
+ Assert.ok(
+ !Services.prefs.getIntPref(
+ "datareporting.policy.dataSubmissionPolicyAcceptedVersion",
+ 0
+ ),
+ "Legacy policy flow accepted version not set"
+ );
+
+ Assert.ok(
+ !Services.prefs.getBoolPref(
+ "datareporting.policy.dataSubmissionPolicyNotifiedDate",
+ 0
+ ),
+ "Legacy policy flow accepted date not set"
+ );
+
+ Services.prefs.setStringPref("termsofuse.acceptedDate", String(Date.now()));
+ Services.prefs.setIntPref(
+ "termsofuse.acceptedVersion",
+ Services.prefs.getIntPref(TOU_CURRENT_VERSION_PREF)
+ );
+
+ Assert.ok(
+ TelemetryReportingPolicy.userHasAcceptedTOU(),
+ "TOU acceptance is recorded"
+ );
+
+ Assert.ok(TelemetryReportingPolicy.canUpload(), "Legacy upload allowed");
+
+ Services.prefs.setBoolPref(
+ "datareporting.healthreport.uploadEnabled",
+ false
+ );
+
+ Assert.ok(
+ !TelemetryReportingPolicy.canUpload(),
+ "Legacy upload blocked when user opts out of sharing interaction data"
+ );
+ }
+);
+
+add_task(async function test_canUpload_allowed_when_both_bypass_prefs_true() {
+ TelemetryReportingPolicy.reset();
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(TelemetryUtils.Preferences.BypassNotification);
+ Services.prefs.clearUserPref("termsofuse.bypassNotification");
+ TelemetryReportingPolicy.reset();
+ });
+
+ Services.prefs.setBoolPref(
+ TelemetryUtils.Preferences.BypassNotification,
+ true
+ );
+ Services.prefs.setBoolPref("termsofuse.bypassNotification", true);
+
+ Assert.ok(
+ TelemetryReportingPolicy.canUpload(),
+ "Upload allowed when both legacy and TOU notification flows are bypassed"
+ );
+
+ Services.prefs.setBoolPref("termsofuse.bypassNotification", false);
+
+ Assert.ok(
+ !TelemetryReportingPolicy.canUpload(),
+ "Upload blocked when only one bypass is true and no other allow path conditions are met"
+ );
+});
+
+add_task(
+ async function test_canUpload_allowed_when_tou_bypass_and_legacy_notified() {
+ TelemetryReportingPolicy.reset();
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(
+ TelemetryUtils.Preferences.BypassNotification
+ );
+ Services.prefs.clearUserPref("termsofuse.bypassNotification");
+ Services.prefs.clearUserPref(
+ "datareporting.policy.dataSubmissionPolicyNotifiedDate"
+ );
+ Services.prefs.clearUserPref(
+ "TelemetryUtils.Preferences.DataSubmissionEnabled"
+ );
+ TelemetryReportingPolicy.reset();
+ });
+
+ Services.prefs.setBoolPref("termsofuse.bypassNotification", true);
+ Services.prefs.setBoolPref(
+ TelemetryUtils.Preferences.BypassNotification,
+ false
+ );
+ Services.prefs.setBoolPref(
+ TelemetryUtils.Preferences.DataSubmissionEnabled,
+ true
+ );
+
+ Assert.ok(
+ !TelemetryReportingPolicy.canUpload(),
+ "Upload not allowed when TOU bypass is true, legacy bypass is false, and legacy policy has NOT been notified"
+ );
+ // Mark legacy “notified” via the actual code path triggered via
+ // testInfobarShown. This calls _recordNotificationData, which updates the
+ // legacy notification accepted time and version prefs. It also calls
+ // _userNotified which in turn calls lazy.TelemetrySend.notifyCanUpload.
+ // This simulates the user being notified of the legacy infobar flow.
+ TelemetryReportingPolicy.testInfobarShown();
+ // Ensure the policy re-reads state after pref mutations.
+ TelemetryReportingPolicy.reset();
+
+ Assert.ok(
+ TelemetryReportingPolicy.canUpload(),
+ "Upload allowed when TOU bypass is true, legacy bypass is false, and legacy policy has been notified"
+ );
+ }
+);
diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.toml b/toolkit/components/telemetry/tests/unit/xpcshell.toml
@@ -54,6 +54,8 @@ tags = "addons"
skip-if = ["os == 'android'"]
["test_TOUNotificationFlow.js"]
+skip-if = ["os == 'android'"]
+# The TOU notification flow is desktop only
["test_TelemetryClientID_reset.js"]
skip-if = ["os == 'android'"] # Disabled as Android/GeckoView doesn't run TelemetryController
@@ -84,6 +86,8 @@ skip-if = [
["test_TelemetryHistograms.js"]
["test_TelemetryReportingPolicy.js"]
+skip-if = ["os == 'android'"]
+# Desktop only
tags = "addons"
["test_TelemetryScalars.js"]
diff --git a/toolkit/crashreporter/test/unit/test_crashreporter_crash.js b/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
@@ -16,6 +16,10 @@ add_task(async function run_test() {
is_win7_or_newer = true;
}
+ registerCleanupFunction(async () => {
+ Services.prefs.clearUserPref("termsofuse.bypassNotification");
+ });
+
// try a basic crash
await do_content_crash(null, function (mdump, extra) {
Assert.ok(mdump.exists());
@@ -108,6 +112,7 @@ add_task(async function run_test() {
function () {
// Enable the FHR, official policy bypass (since we're in a test) and
// specify a telemetry server & client ID.
+ Services.prefs.setBoolPref("termsofuse.bypassNotification", true);
Services.prefs.setBoolPref(
"datareporting.policy.dataSubmissionPolicyBypassNotification",
true
@@ -178,6 +183,7 @@ add_task(async function run_test() {
"datareporting.policy.dataSubmissionPolicyBypassNotification",
true
);
+ Services.prefs.setBoolPref("termsofuse.bypassNotification", true);
Services.prefs.setBoolPref(
"datareporting.healthreport.uploadEnabled",
false