diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index c782150beffd..04bd65442ad4 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1650,6 +1650,7 @@ pref("browser.contentblocking.report.cryptominer.url", "https://support.mozilla. pref("browser.contentblocking.cfr-milestone.enabled", true); pref("browser.contentblocking.cfr-milestone.milestone-achieved", 0); +// Milestones should always be in increasing order pref("browser.contentblocking.cfr-milestone.milestones", "[1000, 5000, 10000, 25000, 50000, 100000, 500000]"); // Enables the new Protections Panel. diff --git a/browser/base/content/test/siteProtections/browser_protections_UI_milestones.js b/browser/base/content/test/siteProtections/browser_protections_UI_milestones.js index af4f8845c7f7..28bed817cef5 100644 --- a/browser/base/content/test/siteProtections/browser_protections_UI_milestones.js +++ b/browser/base/content/test/siteProtections/browser_protections_UI_milestones.js @@ -23,7 +23,6 @@ add_task(async function doTest() { "browser.contentblocking.cfr-milestone.milestones" ) ); - let totalTrackerCount = 0; let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, @@ -31,12 +30,11 @@ add_task(async function doTest() { ); for (let milestone of milestones) { - let trackerCount = milestone - totalTrackerCount; - await addTrackerDataIntoDB(trackerCount); - totalTrackerCount += trackerCount; - // Trigger the milestone feature. - await TrackingDBService.saveEvents("{}"); + Services.prefs.setIntPref( + "browser.contentblocking.cfr-milestone.milestone-achieved", + milestone + ); await TestUtils.waitForCondition( () => gProtectionsHandler._milestoneTextSet diff --git a/browser/components/newtab/lib/CFRPageActions.jsm b/browser/components/newtab/lib/CFRPageActions.jsm index ab72d913b2ac..ff7212e14281 100644 --- a/browser/components/newtab/lib/CFRPageActions.jsm +++ b/browser/components/newtab/lib/CFRPageActions.jsm @@ -31,6 +31,14 @@ XPCOMUtils.defineLazyServiceGetter( "@mozilla.org/tracking-db-service;1", "nsITrackingDBService" ); +XPCOMUtils.defineLazyPreferenceGetter( + this, + "milestones", + "browser.contentblocking.cfr-milestone.milestones", + "[]", + null, + JSON.parse +); const POPUP_NOTIFICATION_ID = "contextual-feature-recommendation"; const ANIMATION_BUTTON_ID = "cfr-notification-footer-animation-button"; @@ -538,7 +546,7 @@ class PageAction { } } - async _renderMilestonePopup(message, browser, cfrMilestonePref) { + async _renderMilestonePopup(message, browser) { let { content } = message; let { primary } = content.buttons; @@ -556,13 +564,20 @@ class PageAction { let headerLabel = this.window.document.getElementById( "cfr-notification-header-label" ); + let reachedMilestone = null; + let totalSaved = await TrackingDBService.sumAllEvents(); + for (let milestone of milestones) { + if (totalSaved >= milestone) { + reachedMilestone = milestone; + } + } if (typeof message.content.heading_text === "string") { // This is a test environment. panelTitle = message.content.heading_text; headerLabel.value = panelTitle; } else { this._l10n.setAttributes(headerLabel, content.heading_text.string_id, { - blockedCount: cfrMilestonePref, + blockedCount: reachedMilestone, date: monthName, }); await this._l10n.translateElements([headerLabel]); @@ -629,7 +644,10 @@ class PageAction { eventCallback: manageClass, } ); - + Services.prefs.setIntPref( + "browser.contentblocking.cfr-milestone.milestone-achieved", + reachedMilestone + ); Services.prefs.setStringPref( "browser.contentblocking.cfr-milestone.milestone-shown-time", Date.now().toString() @@ -903,7 +921,7 @@ class PageAction { await this._renderPopup(message, browser); } - async showMilestonePopup(cfrMilestonePref) { + async showMilestonePopup() { const browser = this.window.gBrowser.selectedBrowser; const message = RecommendationMap.get(browser); const { content } = message; @@ -913,7 +931,7 @@ class PageAction { browser.cfrpopupnotificationanchor = this.window.document.getElementById(content.anchor_id) || this.container; - await this._renderMilestonePopup(message, browser, cfrMilestonePref); + await this._renderMilestonePopup(message, browser); return true; } } @@ -1002,10 +1020,6 @@ const CFRPageActions = { async showMilestone(browser, message, dispatchToASRouter, options = {}) { let win = null; const { id, content } = message; - let cfrMilestonePref = Services.prefs.getIntPref( - "browser.contentblocking.cfr-milestone.milestone-achieved", - 0 - ); // If we are forcing via the Admin page, the browser comes in a different format if (options.force) { @@ -1014,23 +1028,16 @@ const CFRPageActions = { } else { win = browser.ownerGlobal; RecommendationMap.set(browser, { id, retain: true, content }); - if (!cfrMilestonePref) { - return false; - } } if (!PageActionMap.has(win)) { PageActionMap.set(win, new PageAction(win, dispatchToASRouter)); } - let successfullyShown = await PageActionMap.get(win).showMilestonePopup( - cfrMilestonePref - ); - if (successfullyShown) { - PageActionMap.get(win).addImpression(message); - } + await PageActionMap.get(win).showMilestonePopup(); + PageActionMap.get(win).addImpression(message); - return successfullyShown; + return true; }, /** diff --git a/toolkit/components/antitracking/TrackingDBService.jsm b/toolkit/components/antitracking/TrackingDBService.jsm index 676006cfe074..94a0be667a96 100644 --- a/toolkit/components/antitracking/TrackingDBService.jsm +++ b/toolkit/components/antitracking/TrackingDBService.jsm @@ -309,8 +309,6 @@ TrackingDBService.prototype = { if (totalSaved >= milestone) { reachedMilestone = milestone; nextMilestone = milestones[index + 1]; - } else { - break; } } @@ -321,10 +319,6 @@ TrackingDBService.prototype = { (!nextMilestone || nextMilestone - totalSaved > 3000) && (!oldMilestone || oldMilestone < reachedMilestone) ) { - Services.prefs.setIntPref( - "browser.contentblocking.cfr-milestone.milestone-achieved", - reachedMilestone - ); Services.obs.notifyObservers( { wrappedJSObject: { diff --git a/toolkit/components/antitracking/test/xpcshell/test_tracking_db_service.js b/toolkit/components/antitracking/test/xpcshell/test_tracking_db_service.js index 9d3f8ce0ae85..2b8011657767 100644 --- a/toolkit/components/antitracking/test/xpcshell/test_tracking_db_service.js +++ b/toolkit/components/antitracking/test/xpcshell/test_tracking_db_service.js @@ -9,6 +9,9 @@ const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); +const { TestUtils } = ChromeUtils.import( + "resource://testing-common/TestUtils.jsm" +); const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm"); const { Sqlite } = ChromeUtils.import("resource://gre/modules/Sqlite.jsm"); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); @@ -97,9 +100,29 @@ Services.prefs.setBoolPref( "privacy.socialtracking.block_cookies.enabled", true ); +Services.prefs.setBoolPref( + "browser.contentblocking.cfr-milestone.enabled", + true +); +Services.prefs.setIntPref( + "browser.contentblocking.cfr-milestone.update-interval", + 0 +); +Services.prefs.setStringPref( + "browser.contentblocking.cfr-milestone.milestones", + "[1000, 5000, 10000, 25000, 100000, 500000]" +); + registerCleanupFunction(() => { Services.prefs.clearUserPref("browser.contentblocking.database.enabled"); Services.prefs.clearUserPref("privacy.socialtracking.block_cookies.enabled"); + Services.prefs.clearUserPref("browser.contentblocking.cfr-milestone.enabled"); + Services.prefs.clearUserPref( + "browser.contentblocking.cfr-milestone.update-interval" + ); + Services.prefs.clearUserPref( + "browser.contentblocking.cfr-milestone.milestones" + ); }); // This tests that data is added successfully, different types of events should get @@ -423,3 +446,38 @@ add_task(async function test_getEarliestRecordedDate() { await TrackingDBService.clearAll(); await db.close(); }); + +// This tests that a message to CFR is sent when the amount of saved trackers meets a milestone +add_task(async function test_sendMilestoneNotification() { + let milestones = JSON.parse( + Services.prefs.getStringPref( + "browser.contentblocking.cfr-milestone.milestones" + ) + ); + // This creates the schema. + await TrackingDBService.saveEvents(JSON.stringify({})); + let db = await Sqlite.openConnection({ path: DB_PATH }); + // save number of trackers equal to the first milestone + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.CRYPTOMINERS_ID, + count: milestones[0], + timestamp: new Date().toISOString(), + }); + + let awaitNotification = TestUtils.topicObserved( + "SiteProtection:ContentBlockingMilestone" + ); + + // trigger a "save" event to compare the trackers with the milestone. + await TrackingDBService.saveEvents( + JSON.stringify({ + "https://1.example.com": [ + [Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT, true, 1], + ], + }) + ); + await awaitNotification; + + await TrackingDBService.clearAll(); + await db.close(); +});