tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 5b13ebca0cddb9b98a46e6e938fdfbc83aecb5d5
parent 147f3a4ed7742cdf6186faf86d89ceafd59d3cb1
Author: Meg Viar <lmegviar@gmail.com>
Date:   Sat, 11 Oct 2025 01:18:03 +0000

Bug 1993295 - Allow legacy telemetry upload for users who predate the new user TOU experience r=toolkit-telemetry-reviewers,dmose,TravisLong

Differential Revision: https://phabricator.services.mozilla.com/D267989

Diffstat:
Mtoolkit/components/telemetry/app/TelemetryReportingPolicy.sys.mjs | 54++++++++++++++++++++++++++++++++++++++++--------------
Mtoolkit/components/telemetry/tests/unit/test_TOUNotificationFlow.js | 192++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 227 insertions(+), 19 deletions(-)

diff --git a/toolkit/components/telemetry/app/TelemetryReportingPolicy.sys.mjs b/toolkit/components/telemetry/app/TelemetryReportingPolicy.sys.mjs @@ -761,12 +761,12 @@ var TelemetryReportingPolicyImpl = { * 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). + * 1. Terms of Use flow is bypassed + * OR + * Legacy policy has been bypassed AND user does not qualify to see the + * TOU flow + * 2. Legacy policy has been accepted AND user does not qualify to see the + * TOU flow * 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). @@ -787,25 +787,52 @@ var TelemetryReportingPolicyImpl = { // (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) || + const bypassTOUFlowViaPref = Services.prefs.getBoolPref( + TOU_BYPASS_NOTIFICATION_PREF, + false + ); + // In some cases, canUpload can be called before the Nimbus variables are + // set. When this happens, we call _configureFromNimbus to initialize these + // variables before evaluating them. This ensures we have accurate data + // regarding whether preonboarding is enabled for the user. When + // preonboarding is explicitly disabled, it should be treated the same the + // bypassing the TOU flow via the bypass pref. + if ( + // Only bother configuring from nimbus if not already bypassing via pref + !bypassTOUFlowViaPref && + (!this._nimbusVariables || + (typeof this._nimbusVariables === "object" && + Object.keys(this._nimbusVariables).length === 0)) + ) { + this._configureFromNimbus(); + } + + const bypassTOUFlowViaNimbusVariables = (!Services.prefs.getBoolPref("browser.preonboarding.enabled", false) && this._nimbusVariables?.enabled !== true) || this._nimbusVariables?.enabled === false; + + const bypassTOUFlow = + bypassTOUFlowViaPref || bypassTOUFlowViaNimbusVariables; + const allowInteractionData = Services.prefs.getBoolPref( "datareporting.healthreport.uploadEnabled", false ); // Condition 1 - const canUploadBypassLegacyAndTOU = bypassLegacyFlow && bypassTOUFlow; + const bypassFlow = + bypassTOUFlow || (!this._shouldShowTOU() && bypassLegacyFlow); // Condition 2 + // Note that isUserNotifiedOfCurrentPolicy should really be named + // hasUserAcceptedLegacyPolicy as it returns true if the use has accepted + // (been notified of) the legacy policy const canUploadLegacy = - bypassTOUFlow && !bypassLegacyFlow && this.isUserNotifiedOfCurrentPolicy; + !this._shouldShowTOU() && this.isUserNotifiedOfCurrentPolicy; // Condition 3 const canUploadTOU = this.hasUserAcceptedCurrentTOU && allowInteractionData; - return canUploadBypassLegacyAndTOU || canUploadLegacy || canUploadTOU; + return bypassFlow || canUploadLegacy || canUploadTOU; }, isFirstRun() { @@ -1096,7 +1123,7 @@ var TelemetryReportingPolicyImpl = { // _during_ the Firefox process lifetime; right now, we only notify the user // at Firefox startup. this.updateTOUPrefsForLegacyUsers(); - await this._configureFromNimbus(); + this._configureFromNimbus(); if (this.isFirstRun()) { // We're performing the first run, flip firstRun preference for subsequent runs. @@ -1233,13 +1260,12 @@ var TelemetryReportingPolicyImpl = { * Capture Nimbus configuration: record feature variables for future use and * set Gecko preferences based on values. */ - async _configureFromNimbus() { + _configureFromNimbus() { if (AppConstants.MOZ_BUILD_APP != "browser") { // OnboardingMessageProvider is browser/ only return; } this._nimbusVariables = lazy.NimbusFeatures.preonboarding.getAllVariables(); - if (this._nimbusVariables.enabled === null) { const preonboardingMessage = lazy.OnboardingMessageProvider.getPreonboardingMessages().find( diff --git a/toolkit/components/telemetry/tests/unit/test_TOUNotificationFlow.js b/toolkit/components/telemetry/tests/unit/test_TOUNotificationFlow.js @@ -775,8 +775,8 @@ add_task(async function test_canUpload_allowed_when_both_bypass_prefs_true() { Services.prefs.setBoolPref("browser.preonboarding.enabled", true); Assert.ok( - !TelemetryReportingPolicy.canUpload(), - "Upload blocked when only one bypass is true and no other allow path conditions are met" + TelemetryReportingPolicy.canUpload(), + "Upload allowed when only legacy bypass is true" ); Services.prefs.setBoolPref("browser.preonboarding.enabled", false); Assert.ok( @@ -786,7 +786,7 @@ add_task(async function test_canUpload_allowed_when_both_bypass_prefs_true() { }); add_task( - async function test_canUpload_allowed_when_tou_bypass_and_legacy_notified() { + async function test_canUpload_allowed_under_various_bypass_conditions() { TelemetryReportingPolicy.reset(); registerCleanupFunction(() => { @@ -814,8 +814,8 @@ add_task( ); Assert.ok( - !TelemetryReportingPolicy.canUpload(), - "Upload not allowed when TOU bypass is true, legacy bypass is false, and legacy policy has NOT been notified" + TelemetryReportingPolicy.canUpload(), + "Upload allowed when TOU bypass is true even if 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 @@ -832,3 +832,185 @@ add_task( ); } ); + +add_task( + async function test_canUpload_does_not_reconfigure_when_nimbus_ready() { + if (AppConstants.platform === "linux") { + info("Skipping on Linux where TOU flow is disabled by default"); + return; + } + + TelemetryReportingPolicy.reset(); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref("browser.preonboarding.enabled"); + Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled"); + Services.prefs.clearUserPref( + TelemetryUtils.Preferences.BypassNotification + ); + Services.prefs.clearUserPref(TOU_BYPASS_NOTIFICATION_PREF); + Services.prefs.clearUserPref(TOU_ACCEPTED_VERSION_PREF); + Services.prefs.clearUserPref(TOU_ACCEPTED_DATE_PREF); + TelemetryReportingPolicy.reset(); + }); + + Services.prefs.setBoolPref( + TelemetryUtils.Preferences.BypassNotification, + false + ); + Services.prefs.setBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false); + + Services.prefs.setBoolPref( + "datareporting.healthreport.uploadEnabled", + true + ); + + // Enroll so that _configureFromNimbus has something to read during startup. + const unenroll = await NimbusTestUtils.enrollWithFeatureConfig( + { + featureId: NimbusFeatures.preonboarding.featureId, + value: { + enabled: true, + currentVersion: 4, + minimumVersion: 4, + screens: [{ id: "test" }], + }, + }, + { isRollout: false } + ); + + // Simulate startup, this triggers the one-time _configureFromNimbus(). + await Policy.fakeSessionRestoreNotification(); + + const getVarsStub = sinon + .stub(NimbusFeatures.preonboarding, "getAllVariables") + .callsFake(() => { + throw new Error( + "getAllVariables must not be called by canUpload() when already configured" + ); + }); + + TelemetryReportingPolicy.canUpload(); + + Assert.equal( + getVarsStub.callCount, + 0, + "canUpload() should not re-read Nimbus variables when already configured" + ); + + await unenroll(); + sinon.restore(); + } +); + +add_task(async function test_canUpload_reconfigures_when_nimbus_not_ready() { + TelemetryReportingPolicy.reset(); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref("browser.preonboarding.enabled"); + Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled"); + Services.prefs.clearUserPref(TelemetryUtils.Preferences.BypassNotification); + Services.prefs.clearUserPref(TOU_BYPASS_NOTIFICATION_PREF); + Services.prefs.clearUserPref(TOU_ACCEPTED_VERSION_PREF); + Services.prefs.clearUserPref(TOU_ACCEPTED_DATE_PREF); + sinon.restore(); + TelemetryReportingPolicy.reset(); + }); + + Services.prefs.setBoolPref( + TelemetryUtils.Preferences.BypassNotification, + false + ); + Services.prefs.setBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false); + Services.prefs.setBoolPref("datareporting.healthreport.uploadEnabled", true); + + // Don’t trigger the startup config path. + + // Fallback pref disables TOU flow and Nimbus returns enabled as false + Services.prefs.setBoolPref("browser.preonboarding.enabled", false); + + const getVarsSpy = sinon + .stub(NimbusFeatures.preonboarding, "getAllVariables") + .returns({ enabled: false }); + + const result = TelemetryReportingPolicy.canUpload(); + + Assert.equal( + getVarsSpy.callCount, + 1, + "canUpload() should trigger _configureFromNimbus() when variables are not yet set" + ); + + Assert.ok( + result, + "With Nimbus enabled set as false we are bypassing TOU and should allow upload" + ); + + sinon.restore(); +}); + +add_task( + skipIfNotBrowser(), + async function test_legacy_bypass_blocked_when_TOU_should_show_and_not_accepted() { + TelemetryReportingPolicy.reset(); + + registerCleanupFunction(async () => { + Services.prefs.clearUserPref("browser.preonboarding.enabled"); + Services.prefs.clearUserPref( + TelemetryUtils.Preferences.BypassNotification + ); + Services.prefs.clearUserPref(TOU_BYPASS_NOTIFICATION_PREF); + Services.prefs.clearUserPref(TOU_ACCEPTED_VERSION_PREF); + Services.prefs.clearUserPref(TOU_ACCEPTED_DATE_PREF); + Services.prefs.clearUserPref( + "datareporting.policy.dataSubmissionPolicyNotifiedDate" + ); + Services.prefs.clearUserPref( + "datareporting.policy.dataSubmissionPolicyAcceptedVersion" + ); + TelemetryReportingPolicy.reset(); + await unenroll(); + }); + + // Legacy bypass ON, TOU bypass OFF + Services.prefs.setBoolPref( + TelemetryUtils.Preferences.BypassNotification, + true + ); + Services.prefs.setBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false); + + // User has NOT accepted TOU + Services.prefs.clearUserPref(TOU_ACCEPTED_VERSION_PREF); + Services.prefs.clearUserPref(TOU_ACCEPTED_DATE_PREF); + + // User has NOT accepted via Legacy flow + Services.prefs.clearUserPref( + "datareporting.policy.dataSubmissionPolicyNotifiedDate" + ); + Services.prefs.clearUserPref( + "datareporting.policy.dataSubmissionPolicyAcceptedVersion" + ); + + // Make _shouldShowTOU() return true + Services.prefs.setBoolPref("browser.preonboarding.enabled", true); + const unenroll = await NimbusTestUtils.enrollWithFeatureConfig( + { + featureId: NimbusFeatures.preonboarding.featureId, + value: { + enabled: true, + currentVersion: 4, + minimumVersion: 4, + screens: [{ id: "test" }], + }, + }, + { isRollout: false } + ); + + const result = TelemetryReportingPolicy.canUpload(); + + Assert.ok( + !result, + "When TOU flow should be shown, but is not yet accepted with legacy bypass true, (!shouldShowTOU && bypassLegacy) is false, so upload is blocked." + ); + } +);