author | Nihanth Subramanya <nhnt11@gmail.com> |
Wed, 16 Oct 2019 09:00:20 +0000 | |
changeset 497836 | b649a4a3bb7e1f7ed173fd1f0237c0d896e84432 |
parent 497835 | a13a70f7bb6e5618724dd3baff03b8166d73d249 |
child 497837 | fe2312ad6c4fd03510106cfdbe51480ffcff031d |
push id | 36699 |
push user | shindli@mozilla.com |
push date | Wed, 16 Oct 2019 21:29:18 +0000 |
treeherder | mozilla-central@cdcbac306662 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | johannh, flod |
bugs | 1584312 |
milestone | 71.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/base/content/browser-siteProtections.js +++ b/browser/base/content/browser-siteProtections.js @@ -1259,16 +1259,23 @@ var gProtectionsHandler = { get noTrackersDetectedDescription() { delete this.noTrackersDetectedDescription; return (this.noTrackersDetectedDescription = document.getElementById( "protections-popup-no-trackers-found-description" )); }, + get _protectionsPopupMilestonesText() { + delete this._protectionsPopupMilestonesText; + return (this._protectionsPopupMilestonesText = document.getElementById( + "protections-popup-milestones-text" + )); + }, + get _notBlockingWhyLink() { delete this._notBlockingWhyLink; return (this._notBlockingWhyLink = document.getElementById( "protections-popup-not-blocking-section-why" )); }, get hasException() { @@ -1332,16 +1339,52 @@ var gProtectionsHandler = { XPCOMUtils.defineLazyPreferenceGetter( this, "_protectionsPopupToastTimeout", "browser.protections_panel.toast.timeout", 3000 ); + XPCOMUtils.defineLazyPreferenceGetter( + this, + "milestoneListPref", + "browser.contentblocking.cfr-milestone.milestones", + [], + () => this.maybeSetMilestoneCounterText(), + val => JSON.parse(val) + ); + + XPCOMUtils.defineLazyPreferenceGetter( + this, + "milestonePref", + "browser.contentblocking.cfr-milestone.milestone-achieved", + 0, + () => this.maybeSetMilestoneCounterText() + ); + + XPCOMUtils.defineLazyPreferenceGetter( + this, + "milestoneTimestampPref", + "browser.contentblocking.cfr-milestone.milestone-shown-time", + 0, + null, + val => parseInt(val) + ); + + XPCOMUtils.defineLazyPreferenceGetter( + this, + "milestonesEnabledPref", + "browser.contentblocking.cfr-milestone.enabled", + false, + () => this.maybeSetMilestoneCounterText() + ); + + this.maybeSetMilestoneCounterText(); + for (let blocker of this.blockers) { if (blocker.init) { blocker.init(); } } this.updateAnimationsEnabled(); @@ -1378,16 +1421,21 @@ var gProtectionsHandler = { }, openProtections(relatedToCurrent = false) { switchToTabHavingURI("about:protections", true, { replaceQueryString: true, relatedToCurrent, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }); + + // Don't show the milestones section anymore. + Services.prefs.clearUserPref( + "browser.contentblocking.cfr-milestone.milestone-shown-time" + ); }, async showTrackersSubview(event) { await TrackingProtection.updateSubView(); this._protectionsPopupMultiView.showSubView( "protections-popup-trackersView" ); }, @@ -1783,16 +1831,26 @@ var gProtectionsHandler = { gNavigatorBundle.getFormattedString("protections.enableAriaLabel", [ host, ]) ); } // Update the tooltip of the blocked tracker counter. this.maybeUpdateEarliestRecordedDateTooltip(); + + let today = Date.now(); + let threeDaysMillis = 72 * 60 * 60 * 1000; + let expired = today - this.milestoneTimestampPref > threeDaysMillis; + + if (this._milestoneTextSet && !expired) { + this._protectionsPopup.setAttribute("milestone", this.milestonePref); + } else { + this._protectionsPopup.removeAttribute("milestone"); + } }, /* * This function sorts the category items into the Blocked/Allowed/None Detected * sections. It's called immediately in onContentBlockingEvent if the popup * is presently open. Otherwise, the next time the popup is shown. */ reorderCategoryItems() { @@ -1947,16 +2005,56 @@ var gProtectionsHandler = { // Show the counter if the number of tracker is not zero. this._protectionsPopupTrackersCounterBox.toggleAttribute( "showing", trackerCount != 0 ); }, + // Whenever one of the milestone prefs are changed, we attempt to update + // the milestone section string. This requires us to fetch the earliest + // recorded date from the Tracking DB, hence this process is async. + // When completed, we set _milestoneSetText to signal that the section + // is populated and ready to be shown - which happens next time we call + // refreshProtectionsPopup. + _milestoneTextSet: false, + async maybeSetMilestoneCounterText() { + let trackerCount = this.milestonePref; + if ( + !this.milestonesEnabledPref || + !trackerCount || + !this.milestoneListPref.includes(trackerCount) + ) { + this._milestoneTextSet = false; + return; + } + + let date = await TrackingDBService.getEarliestRecordedDate(); + let dateLocaleStr = new Date(date).toLocaleDateString("default", { + month: "short", + year: "numeric", + }); + + let desc = PluralForm.get( + trackerCount, + gNavigatorBundle.getString("protections.milestone.description") + ); + + this._protectionsPopupMilestonesText.textContent = desc + .replace("#1", gBrandBundle.GetStringFromName("brandShortName")) + .replace( + "#2", + trackerCount.toLocaleString(Services.locale.appLocalesAsBCP47) + ) + .replace("#3", dateLocaleStr); + + this._milestoneTextSet = true; + }, + showDisabledTooltipForTPIcon() { this._trackingProtectionIconTooltipLabel.textContent = this.strings.disabledTooltipText; gIdentityHandler._trackingProtectionIconContainer.setAttribute( "aria-label", this.strings.disabledTooltipText ); },
--- a/browser/base/content/test/siteProtections/browser.ini +++ b/browser/base/content/test/siteProtections/browser.ini @@ -1,5 +1,6 @@ [DEFAULT] support-files = head.js [browser_protections_UI.js] +[browser_protections_UI_milestones.js]
--- a/browser/base/content/test/siteProtections/browser_protections_UI.js +++ b/browser/base/content/test/siteProtections/browser_protections_UI.js @@ -4,60 +4,16 @@ /* Basic UI tests for the protections panel */ ChromeUtils.defineModuleGetter( this, "ContentBlockingAllowList", "resource://gre/modules/ContentBlockingAllowList.jsm" ); -function checkClickTelemetry(objectName, value) { - let events = Services.telemetry.snapshotEvents( - Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS - ).parent; - let buttonEvents = events.filter( - e => - e[1] == "security.ui.protectionspopup" && - e[2] == "click" && - e[3] == objectName && - (!value || e[4] == value) - ); - is(buttonEvents.length, 1, `recorded ${objectName} telemetry event`); -} - -XPCOMUtils.defineLazyServiceGetter( - this, - "TrackingDBService", - "@mozilla.org/tracking-db-service;1", - "nsITrackingDBService" -); - -XPCOMUtils.defineLazyGetter(this, "TRACK_DB_PATH", function() { - return OS.Path.join(OS.Constants.Path.profileDir, "protections.sqlite"); -}); - -const { Sqlite } = ChromeUtils.import("resource://gre/modules/Sqlite.jsm"); - -async function addTrackerDataIntoDB(count) { - const insertSQL = - "INSERT INTO events (type, count, timestamp)" + - "VALUES (:type, :count, date(:timestamp));"; - - let db = await Sqlite.openConnection({ path: TRACK_DB_PATH }); - let date = new Date().toISOString(); - - await db.execute(insertSQL, { - type: TrackingDBService.TRACKERS_ID, - count, - timestamp: date, - }); - - await db.close(); -} - add_task(async function setup() { await SpecialPowers.pushPrefEnv({ set: [ // Set the auto hide timing to 100ms for blocking the test less. ["browser.protections_panel.toast.timeout", 100], // Hide protections cards so as not to trigger more async messaging // when landing on the page. ["browser.contentblocking.report.monitor.enabled", false], @@ -217,42 +173,30 @@ add_task(async function testShowFullRepo "https://example.com" ); await openProtectionsPanel(); let popuphiddenPromise = BrowserTestUtils.waitForEvent( protectionsPopup, "popuphidden" ); - let newTabPromise = BrowserTestUtils.waitForNewTab( - gBrowser, - "about:protections" - ); + let newTabPromise = waitForAboutProtectionsTab(); let showFullReportButton = document.getElementById( "protections-popup-show-report-button" ); showFullReportButton.click(); // The protection popup should be hidden after clicking the link. await popuphiddenPromise; // Wait until the 'about:protections' has been opened correctly. let newTab = await newTabPromise; ok(true, "about:protections has been opened successfully"); - // When the graph is built it means the messaging has finished, - // we can close the tab. - await ContentTask.spawn(newTab.linkedBrowser, {}, async function() { - await ContentTaskUtils.waitForCondition(() => { - let bars = content.document.querySelectorAll(".graph-bar"); - return bars.length; - }, "The graph has been built"); - }); - checkClickTelemetry("full_report"); BrowserTestUtils.removeTab(newTab); BrowserTestUtils.removeTab(tab); }); /** * A test for ensuring the mini panel is working correctly
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/siteProtections/browser_protections_UI_milestones.js @@ -0,0 +1,96 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function setup() { + await SpecialPowers.pushPrefEnv({ + set: [ + // Hide protections cards so as not to trigger more async messaging + // when landing on the page. + ["browser.contentblocking.report.monitor.enabled", false], + ["browser.contentblocking.report.lockwise.enabled", false], + ["browser.contentblocking.report.proxy.enabled", false], + ], + }); +}); + +add_task(async function doTest() { + // This also ensures that the DB tables have been initialized. + await TrackingDBService.clearAll(); + + let milestones = JSON.parse( + Services.prefs.getStringPref( + "browser.contentblocking.cfr-milestone.milestones" + ) + ); + let totalTrackerCount = 0; + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "https://example.com" + ); + + for (let milestone of milestones) { + let trackerCount = milestone - totalTrackerCount; + await addTrackerDataIntoDB(trackerCount); + totalTrackerCount += trackerCount; + + // Trigger the milestone feature. + await TrackingDBService.saveEvents("{}"); + + await TestUtils.waitForCondition( + () => gProtectionsHandler._milestoneTextSet + ); + + // We set the shown-time pref to pretend that the CFR has been + // shown, so that we can test the panel. + // TODO: Full integration test for robustness. + Services.prefs.setStringPref( + "browser.contentblocking.cfr-milestone.milestone-shown-time", + Date.now().toString() + ); + + await openProtectionsPanel(); + + ok( + BrowserTestUtils.is_visible( + gProtectionsHandler._protectionsPopupMilestonesText + ), + "Milestones section should be visible in the panel." + ); + + await closeProtectionsPanel(); + await openProtectionsPanel(); + + ok( + BrowserTestUtils.is_visible( + gProtectionsHandler._protectionsPopupMilestonesText + ), + "Milestones section should still be visible in the panel." + ); + + let newTabPromise = waitForAboutProtectionsTab(); + await EventUtils.synthesizeMouseAtCenter( + document.getElementById("protections-popup-milestones-content"), + {} + ); + let protectionsTab = await newTabPromise; + + ok(true, "about:protections has been opened as expected."); + + BrowserTestUtils.removeTab(protectionsTab); + + await openProtectionsPanel(); + + ok( + !BrowserTestUtils.is_visible( + gProtectionsHandler._protectionsPopupMilestonesText + ), + "Milestones section should no longer be visible in the panel." + ); + + await closeProtectionsPanel(); + } + + BrowserTestUtils.removeTab(tab); + await TrackingDBService.clearAll(); +});
--- a/browser/base/content/test/siteProtections/head.js +++ b/browser/base/content/test/siteProtections/head.js @@ -1,11 +1,24 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +const { Sqlite } = ChromeUtils.import("resource://gre/modules/Sqlite.jsm"); + +XPCOMUtils.defineLazyServiceGetter( + this, + "TrackingDBService", + "@mozilla.org/tracking-db-service;1", + "nsITrackingDBService" +); + +XPCOMUtils.defineLazyGetter(this, "TRACK_DB_PATH", function() { + return OS.Path.join(OS.Constants.Path.profileDir, "protections.sqlite"); +}); + var protectionsPopup = document.getElementById("protections-popup"); var protectionsPopupMainView = document.getElementById( "protections-popup-mainView" ); var protectionsPopupHeader = document.getElementById( "protections-popup-mainView-panel-header" ); @@ -56,8 +69,54 @@ async function closeProtectionsPanel() { let popuphiddenPromise = BrowserTestUtils.waitForEvent( protectionsPopup, "popuphidden" ); PanelMultiView.hidePopup(protectionsPopup); await popuphiddenPromise; } + +function checkClickTelemetry(objectName, value) { + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + let buttonEvents = events.filter( + e => + e[1] == "security.ui.protectionspopup" && + e[2] == "click" && + e[3] == objectName && + (!value || e[4] == value) + ); + is(buttonEvents.length, 1, `recorded ${objectName} telemetry event`); +} + +async function addTrackerDataIntoDB(count) { + const insertSQL = + "INSERT INTO events (type, count, timestamp)" + + "VALUES (:type, :count, date(:timestamp));"; + + let db = await Sqlite.openConnection({ path: TRACK_DB_PATH }); + let date = new Date().toISOString(); + + await db.execute(insertSQL, { + type: TrackingDBService.TRACKERS_ID, + count, + timestamp: date, + }); + + await db.close(); +} + +async function waitForAboutProtectionsTab() { + let tab = await BrowserTestUtils.waitForNewTab(gBrowser, "about:protections"); + + // When the graph is built it means the messaging has finished, + // we can close the tab. + await ContentTask.spawn(tab.linkedBrowser, {}, async function() { + await ContentTaskUtils.waitForCondition(() => { + let bars = content.document.querySelectorAll(".graph-bar"); + return bars.length; + }, "The graph has been built"); + }); + + return tab; +}
--- a/browser/components/controlcenter/content/protectionsPanel.inc.xul +++ b/browser/components/controlcenter/content/protectionsPanel.inc.xul @@ -150,16 +150,28 @@ <hbox id="protections-popup-trackers-blocked-counter-box" align="center" end="0"> <description id="protections-popup-trackers-blocked-counter-description" onclick="gProtectionsHandler.openProtections(true);"/> </hbox> </stack> </vbox> + + <hbox id="protections-popup-milestones" class="protections-popup-section"> + <!-- wrap=true is needed for descriptionheightworkaround, see bug 1564077 --> + <toolbarbutton id="protections-popup-milestones-content" + flex="1" + wrap="true" + oncommand="gProtectionsHandler.openProtections(true);"> + <description id="protections-popup-milestones-text" flex="1" + role="heading" aria-level="2"/> + <image id="protections-popup-milestones-illustration"/> + </toolbarbutton> + </hbox> </panelview> <!-- Site Not Working? SubView --> <panelview id="protections-popup-siteNotWorkingView" role="document" title="&protections.siteNotWorkingView.title;" descriptionheightworkaround="true" flex="1">
--- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -570,16 +570,26 @@ protections.notBlocking.socialMediaTrack # Semicolon-separated list of plural forms. # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals # Replacement for #1 is a locale-string converted positive integer. protections.footer.blockedTrackerCounter.description=1 Blocked;#1 Blocked # LOCALIZATION NOTE (protections.footer.blockedTrackerCounter.tooltip): # %S is the date on which we started counting (e.g., July 17, 2019). protections.footer.blockedTrackerCounter.tooltip=Since %S +# Milestones section in the Protections Panel +# LOCALIZATION NOTE (protections.milestone.description): +# Semicolon-separated list of plural forms. +# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals +# #1 is replaced with brandShortName. +# #2 is replaced with the (locale-formatted) number of trackers blocked +# #3 is replaced by a locale-formatted date with short month and numeric year. +# In English this looks like "Firefox blocked over 10,000 trackers since Oct 2019" +protections.milestone.description=#1 blocked #2 tracker since #3;#1 blocked over #2 trackers since #3 + # Edit Bookmark UI editBookmarkPanel.newBookmarkTitle=New Bookmark editBookmarkPanel.editBookmarkTitle=Edit This Bookmark editBookmarkPanel.cancel.label=Cancel editBookmarkPanel.cancel.accesskey=C # LOCALIZATION NOTE (editBookmark.removeBookmarks.label): Semicolon-separated list of plural forms. # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
new file mode 100644 --- /dev/null +++ b/browser/themes/shared/controlcenter/etp-milestone.svg @@ -0,0 +1,4 @@ +<!-- 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/. --> +<svg width="32" height="40" xmlns="http://www.w3.org/2000/svg"><path d="M31.617 10.435V5.009a1.74 1.74 0 00-1.443-1.74L16.296.87a1.687 1.687 0 00-.592 0L1.87 3.288a1.74 1.74 0 00-1.444 1.74v5.425c-.043 2.87-.07 4.948.052 6.244.748 8.295 2.496 12.078 5.47 16.147a16.165 16.165 0 009.704 6.287c.113.013.227.013.34 0 .112.014.226.014.338 0a16.409 16.409 0 009.757-6.313c2.974-4.07 4.722-7.826 5.47-16.156.13-1.287.104-3.365.06-6.226z" fill="context-fill"/><path d="M15.704.87L1.87 3.287a1.74 1.74 0 00-1.444 1.74v5.425c-.043 2.87-.07 4.948.052 6.244.748 8.295 2.496 12.078 5.47 16.147a16.165 16.165 0 009.704 6.287c.113.013.227.013.34 0V.87a1.74 1.74 0 00-.288 0z" opacity=".5" fill="context-stroke"/><path d="M12.878 11.635c-1.4.174-2.295 2.191-2.052 4.122.122.982.713 1.452.87 3.217 0 .174 0 .322.043.47a16.66 16.66 0 003.044-.166 5.009 5.009 0 01-.053-.87c.219-1.13.347-2.275.383-3.425-.226-1.757-.983-3.505-2.235-3.348zm2.052 8.487c-.84.135-1.688.202-2.539.2h-.548c.025.398.083.794.174 1.182.14.566.618 1.557 1.687 1.418a1.452 1.452 0 001.374-1.809c-.035-.348-.095-.67-.148-.991zm4.2-5.879c-1.252-.156-2.008 1.592-2.234 3.34.036 1.15.164 2.296.382 3.426.008.29-.01.581-.052.87 1.002.146 2.015.202 3.026.164v-.47c.174-1.738.765-2.234.87-3.216.295-1.922-.6-3.94-1.992-4.114zm-2.208 9.47a1.452 1.452 0 001.339 1.809c1.07.139 1.548-.87 1.687-1.418a7.07 7.07 0 00.174-1.174H19.6c-.845 0-1.688-.066-2.522-.2-.06.322-.121.644-.156.983z" fill="#FBFBFE"/></svg>
--- a/browser/themes/shared/controlcenter/panel.inc.css +++ b/browser/themes/shared/controlcenter/panel.inc.css @@ -1102,8 +1102,79 @@ description#identity-popup-content-verif .protections-popup-description { border-bottom: 1px solid var(--panel-separator-color); } .protections-popup-description > description { margin: 10px 24px; } + +#protections-popup:not([milestone]) #protections-popup-milestones { + display: none; +} + +#protections-popup-milestones { + margin: 0 calc(var(--horizontal-padding) * 0.5) var(--vertical-section-padding); + /* Override .protections-popup-section */ + border-top: none; + background-color: var(--arrowpanel-dimmed); +} + +#protections-popup-milestones-content { + padding: var(--vertical-section-padding) calc(var(--horizontal-padding) * 0.5); + margin: 0; +} + +#protections-popup-milestones-content:hover { + background-color: var(--arrowpanel-dimmed); +} + +#protections-popup-milestones-content:hover:active { + background-color: var(--arrowpanel-dimmed-further); +} + +#protections-popup-milestones-text { + font-size: 1.23em; + margin: 0; +} + +#protections-popup-milestones-illustration { + list-style-image: url(chrome://browser/skin/controlcenter/etp-milestone.svg); + -moz-context-properties: fill, stroke; + margin-inline-start: var(--horizontal-padding); + margin-inline-end: 4px; +} + +#protections-popup[milestone] #protections-popup-milestones-illustration { + fill: #45278d; + stroke: #321c64; +} + +#protections-popup[milestone="5000"] #protections-popup-milestones-illustration { + fill: #5a29cb; + stroke: #45278d; +} + +#protections-popup[milestone="10000"] #protections-popup-milestones-illustration { + fill: #7641e5; + stroke: #5a29cb; +} + +#protections-popup[milestone="25000"] #protections-popup-milestones-illustration { + fill: #e31587; + stroke: #c60084; +} + +#protections-popup[milestone="50000"] #protections-popup-milestones-illustration { + fill: #ff298a; + stroke: #e31587; +} + +#protections-popup[milestone="100000"] #protections-popup-milestones-illustration { + fill: #ffa436; + stroke: #e27f2e; +} + +#protections-popup[milestone="500000"] #protections-popup-milestones-illustration { + fill: #ffd567; + stroke: #ffbd4f; +}
--- a/browser/themes/shared/jar.inc.mn +++ b/browser/themes/shared/jar.inc.mn @@ -26,16 +26,17 @@ skin/classic/browser/addons/addon-private-browsing.svg (../shared/addons/addon-private-browsing.svg) skin/classic/browser/controlcenter/3rdpartycookies.svg (../shared/controlcenter/3rdpartycookies.svg) skin/classic/browser/controlcenter/3rdpartycookies-disabled.svg (../shared/controlcenter/3rdpartycookies-disabled.svg) skin/classic/browser/controlcenter/cryptominers.svg (../shared/controlcenter/cryptominers.svg) skin/classic/browser/controlcenter/dashboard.svg (../shared/controlcenter/dashboard.svg) skin/classic/browser/controlcenter/cryptominers-disabled.svg (../shared/controlcenter/cryptominers-disabled.svg) skin/classic/browser/controlcenter/mcb-disabled.svg (../shared/controlcenter/mcb-disabled.svg) skin/classic/browser/controlcenter/extension.svg (../shared/controlcenter/extension.svg) + skin/classic/browser/controlcenter/etp-milestone.svg (../shared/controlcenter/etp-milestone.svg) skin/classic/browser/controlcenter/fingerprinters.svg (../shared/controlcenter/fingerprinters.svg) skin/classic/browser/controlcenter/fingerprinters-disabled.svg (../shared/controlcenter/fingerprinters-disabled.svg) skin/classic/browser/controlcenter/info.svg (../shared/controlcenter/info.svg) skin/classic/browser/controlcenter/socialblock.svg (../shared/controlcenter/socialblock.svg) skin/classic/browser/controlcenter/socialblock-disabled.svg (../shared/controlcenter/socialblock-disabled.svg) skin/classic/browser/controlcenter/tracker-image.svg (../shared/controlcenter/tracker-image.svg) skin/classic/browser/controlcenter/tracker-image-disabled.svg (../shared/controlcenter/tracker-image-disabled.svg) skin/classic/browser/controlcenter/trackers.svg (../shared/controlcenter/trackers.svg)