From ec2865c8d716c82c322401daaf2e1d9fef1ebe18 Mon Sep 17 00:00:00 2001 From: Nihanth Subramanya Date: Wed, 22 Jan 2020 22:16:44 +0000 Subject: [PATCH] Bug 1608320 - DoH Rollout Extension: Don't show the doorhanger if the user has seen the post-DoH privacy statement. r=dragana Differential Revision: https://phabricator.services.mozilla.com/D59830 --HG-- extra : moz-landing-system : lando --- browser/app/profile/firefox.js | 6 +++ browser/extensions/doh-rollout/background.js | 42 +++++++++++++++---- .../doh-rollout/experiments/doorhanger/api.js | 25 +++++++++++ .../doh-rollout/test/browser/browser.ini | 1 + .../browser/browser_doorhanger_newProfile.js | 34 +++++++++++++++ .../doh-rollout/test/browser/head.js | 10 +++++ 6 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 browser/extensions/doh-rollout/test/browser/browser_doorhanger_newProfile.js diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 9481c4784cda..66dded2aa5cc 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1820,6 +1820,12 @@ pref("extensions.screenshots.disabled", false); // disable uploading to the server. pref("extensions.screenshots.upload-disabled", false); +// DoH Rollout: the earliest date of profile creation for which we don't need +// to show the doorhanger. This is when the version of the privacy statement +// that includes DoH went live - Oct 31, 2019. This has to be a string because +// the number is outside the signed 32-bit integer range. +pref("doh-rollout.profileCreationThreshold", "1572476400000"); + // URL for Learn More link for browser error logging in preferences pref("browser.chrome.errorReporter.infoURL", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/nightly-error-collection"); diff --git a/browser/extensions/doh-rollout/background.js b/browser/extensions/doh-rollout/background.js index f034b7281688..eb0659299e26 100644 --- a/browser/extensions/doh-rollout/background.js +++ b/browser/extensions/doh-rollout/background.js @@ -190,15 +190,9 @@ const stateManager = { }, async showDoorhanger() { - browser.experiments.doorhanger.onDoorhangerAccept.addListener( - rollout.doorhangerAcceptListener - ); + rollout.addDoorhangerListeners(); - browser.experiments.doorhanger.onDoorhangerDecline.addListener( - rollout.doorhangerDeclineListener - ); - - await browser.experiments.doorhanger.show({ + let doorhangerShown = await browser.experiments.doorhanger.show({ name: browser.i18n.getMessage("doorhangerName"), text: "<> " + browser.i18n.getMessage("doorhangerBody"), okLabel: browser.i18n.getMessage("doorhangerButtonOk"), @@ -208,6 +202,16 @@ const stateManager = { "doorhangerButtonCancelAccessKey" ), }); + + if (!doorhangerShown) { + // The profile was created after the go-live date of the privacy statement + // that included DoH. Treat it as accepted. + log("Profile is new, doorhanger not shown."); + await stateManager.setState("UIOk"); + await stateManager.rememberDoorhangerDecision("NewProfile"); + await stateManager.rememberDoorhangerShown(); + rollout.removeDoorhangerListeners(); + } }, }; @@ -223,11 +227,32 @@ const rollout = { return this._isTesting; }, + addDoorhangerListeners() { + browser.experiments.doorhanger.onDoorhangerAccept.addListener( + rollout.doorhangerAcceptListener + ); + + browser.experiments.doorhanger.onDoorhangerDecline.addListener( + rollout.doorhangerDeclineListener + ); + }, + + removeDoorhangerListeners() { + browser.experiments.doorhanger.onDoorhangerAccept.removeListener( + rollout.doorhangerAcceptListener + ); + + browser.experiments.doorhanger.onDoorhangerDecline.removeListener( + rollout.doorhangerDeclineListener + ); + }, + async doorhangerAcceptListener(tabId) { log("Doorhanger accepted on tab", tabId); await stateManager.setState("UIOk"); await stateManager.rememberDoorhangerDecision("UIOk"); await stateManager.rememberDoorhangerShown(); + rollout.removeDoorhangerListeners(); }, async doorhangerDeclineListener(tabId) { @@ -239,6 +264,7 @@ const rollout = { browser.experiments.heuristics.sendHeuristicsPing("disable_doh", results); await stateManager.rememberDisableHeuristics(); await stateManager.rememberDoorhangerShown(); + rollout.removeDoorhangerListeners(); }, async heuristics(evaluateReason) { diff --git a/browser/extensions/doh-rollout/experiments/doorhanger/api.js b/browser/extensions/doh-rollout/experiments/doorhanger/api.js index efdcb05a9b28..6027ecd30f97 100644 --- a/browser/extensions/doh-rollout/experiments/doorhanger/api.js +++ b/browser/extensions/doh-rollout/experiments/doorhanger/api.js @@ -7,6 +7,7 @@ /* global BrowserWindowTracker, ExtensionCommon, ExtensionAPI */ ChromeUtils.import("resource://gre/modules/Services.jsm", this); +ChromeUtils.import("resource://gre/modules/Preferences.jsm", this); var { EventManager, EventEmitter } = ExtensionCommon; const { @@ -97,7 +98,31 @@ this.doorhanger = class doorhanger extends ExtensionAPI { experiments: { doorhanger: { async show(properties) { + let profileAge = await ChromeUtils.import( + "resource://gre/modules/ProfileAge.jsm", + {} + ).ProfileAge(); + + let creationDate = await profileAge.created; + let firstUse = await profileAge.firstUse; + let resetDate = await profileAge.reset; + let profileDate = resetDate || firstUse || creationDate; + + // We only need to show the doorhanger to users who have not been + // shown a version of the privacy statement that includes DoH at + // first-run. + let profileCreationThreshold = parseInt( + Preferences.get("doh-rollout.profileCreationThreshold") + ); + if ( + !isNaN(profileCreationThreshold) && + profileDate > profileCreationThreshold + ) { + return false; + } + await doorhangerEventEmitter.emitShow(properties); + return true; }, onDoorhangerAccept: new EventManager({ context, diff --git a/browser/extensions/doh-rollout/test/browser/browser.ini b/browser/extensions/doh-rollout/test/browser/browser.ini index 35af1889d00c..4a407a991922 100644 --- a/browser/extensions/doh-rollout/test/browser/browser.ini +++ b/browser/extensions/doh-rollout/test/browser/browser.ini @@ -5,6 +5,7 @@ skip-if = debug # Bug 1548006 - reloading an addon upsets the leak checker. [browser_cleanFlow.js] [browser_dirtyEnable.js] [browser_doorhangerUserReject.js] +[browser_doorhanger_newProfile.js] [browser_policyOverride.js] skip-if = (!debug && bits == 64) #Bug 1605297 [browser_userInterference.js] diff --git a/browser/extensions/doh-rollout/test/browser/browser_doorhanger_newProfile.js b/browser/extensions/doh-rollout/test/browser/browser_doorhanger_newProfile.js new file mode 100644 index 000000000000..a2b3ba29b49c --- /dev/null +++ b/browser/extensions/doh-rollout/test/browser/browser_doorhanger_newProfile.js @@ -0,0 +1,34 @@ +"use strict"; + +add_task(setup); + +add_task(async function testDoorhanger() { + Preferences.reset(prefs.PROFILE_CREATION_THRESHOLD_PREF); + // Set up a passing environment and enable DoH. + setPassingHeuristics(); + Preferences.set(prefs.DOH_ENABLED_PREF, true); + + await BrowserTestUtils.waitForCondition(() => { + return Preferences.get(prefs.DOH_SELF_ENABLED_PREF); + }); + is(Preferences.get(prefs.DOH_SELF_ENABLED_PREF), true, "Breadcrumb saved."); + + await BrowserTestUtils.waitForCondition(() => { + return Preferences.get(prefs.DOH_DOORHANGER_SHOWN_PREF); + }); + is( + Preferences.get(prefs.DOH_DOORHANGER_SHOWN_PREF), + true, + "Doorhanger shown pref saved." + ); + is( + Preferences.get(prefs.DOH_DOORHANGER_USER_DECISION_PREF), + "NewProfile", + "Doorhanger decision saved." + ); + is( + Preferences.get(prefs.DOH_SELF_ENABLED_PREF), + true, + "Breadcrumb not cleared." + ); +}); diff --git a/browser/extensions/doh-rollout/test/browser/head.js b/browser/extensions/doh-rollout/test/browser/head.js index 1fb9f073452e..c054b8bf6d37 100644 --- a/browser/extensions/doh-rollout/test/browser/head.js +++ b/browser/extensions/doh-rollout/test/browser/head.js @@ -12,6 +12,10 @@ ChromeUtils.defineModuleGetter( "resource://gre/modules/Preferences.jsm" ); +const { CommonUtils } = ChromeUtils.import( + "resource://services-common/utils.js" +); + const ADDON_ID = "doh-rollout@mozilla.org"; const prefs = { @@ -27,6 +31,7 @@ const prefs = { DOH_BALROG_MIGRATION_PREF: "doh-rollout.balrog-migration-done", DOH_DEBUG_PREF: "doh-rollout.debug", MOCK_HEURISTICS_PREF: "doh-rollout.heuristics.mockValues", + PROFILE_CREATION_THRESHOLD_PREF: "doh-rollout.profileCreationThreshold", }; const fakePassingHeuristics = JSON.stringify({ @@ -59,6 +64,11 @@ async function setup() { Services.telemetry.canRecordExtended = true; Services.telemetry.clearEvents(); + // Set the profile creation threshold to very far in the future by defualt, + // so that we can test the doorhanger. browser_doorhanger_newProfile.js + // overrides this. + Preferences.set(prefs.PROFILE_CREATION_THRESHOLD_PREF, "99999999999999"); + registerCleanupFunction(async () => { Services.telemetry.canRecordExtended = oldCanRecord; Services.telemetry.clearEvents();