tor-browser

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

commit 2a1f1919071ce5dc161fc07db132a8b6de9c7912
parent 49988b4b9887b39adabb5a698cef009d909bce12
Author: Nathan Barrett <nbarrett@mozilla.com>
Date:   Thu,  4 Dec 2025 02:32:01 +0000

Bug 2003048 - Add frecency telemetry for sponsored TopSites r=home-newtab-reviewers,reemhamz

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

Diffstat:
Mbrowser/components/newtab/metrics.yaml | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/extensions/newtab/content-src/components/TopSites/TopSite.jsx | 2++
Mbrowser/extensions/newtab/data/content/activity-stream.bundle.js | 6++++--
Mbrowser/extensions/newtab/lib/ActivityStream.sys.mjs | 8++++++++
Mbrowser/extensions/newtab/lib/TelemetryFeed.sys.mjs | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mbrowser/extensions/newtab/lib/TopSitesFeed.sys.mjs | 27+++++++++++++++++++++++++--
6 files changed, 242 insertions(+), 42 deletions(-)

diff --git a/browser/components/newtab/metrics.yaml b/browser/components/newtab/metrics.yaml @@ -2750,6 +2750,107 @@ newtab_content: no_lint: - HIGHER_DATA_SENSITIVITY_REQUIRED + top_sites_click: + type: event + description: > + Recorded when a top sites tile is clicked. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2003048 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2003048 + data_sensitivity: + - interaction + notification_emails: + - nbarrett@mozilla.com + - sdowne@mozilla.com + expires: never + extra_keys: + advertiser_name: *advertiser_name + tile_id: *tile_id + is_sponsored: *is_sponsored + position: *topsite_position + visible_topsites: *visible_topsites + frecency_boosted: &frecency_boosted + description: > + Whether the top site was frecency boosted + type: boolean + frecency_boosted_has_exposure: &frecency_boosted_has_exposure + description: > + Whether the user had the option to see frecency boosted shortcuts + type: boolean + send_in_pings: + - newtab-content + + top_sites_impression: + type: event + description: > + Recorded when a top sites tile is visible to the user. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2003048 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2003048 + data_sensitivity: + - interaction + notification_emails: + - nbarrett@mozilla.com + - sdowne@mozilla.com + expires: never + extra_keys: + advertiser_name: *advertiser_name + tile_id: *tile_id + is_sponsored: *is_sponsored + position: *topsite_position + visible_topsites: *visible_topsites + frecency_boosted: *frecency_boosted + frecency_boosted_has_exposure: *frecency_boosted_has_exposure + send_in_pings: + - newtab-content + + top_sites_dismiss: + type: event + description: > + Recorded when the "Dismiss" menu item in the three-dots menu of a topsite + is clicked. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2003048 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2003048 + data_sensitivity: + - interaction + notification_emails: + - nbarrett@mozilla.com + - sdowne@mozilla.com + expires: never + extra_keys: + advertiser_name: *advertiser_name + tile_id: *tile_id + is_sponsored: *is_sponsored + position: *topsite_position + send_in_pings: + - newtab-content + + top_sites_show_privacy_click: + type: event + description: > + Recorded when the "Our sponsors & your privacy" menu item in the three-dots menu of a topsite + is clicked. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2003048 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=2003048 + data_sensitivity: + - interaction + notification_emails: + - nbarrett@mozilla.com + - sdowne@mozilla.com + expires: never + extra_keys: + advertiser_name: *advertiser_name + tile_id: *tile_id + position: *topsite_position + send_in_pings: + - newtab-content + top_sites: # Replacement for PingCentre "topsites-impression|click" pings. ping_type: type: string diff --git a/browser/extensions/newtab/content-src/components/TopSites/TopSite.jsx b/browser/extensions/newtab/content-src/components/TopSites/TopSite.jsx @@ -337,6 +337,7 @@ export class TopSiteLink extends React.PureComponent { advertiser: title.toLocaleLowerCase(), source: NEWTAB_SOURCE, visible_topsites: visibleTopSites, + frecency_boosted: link.type === "frecency-boost", }} // For testing. IntersectionObserver={this.props.IntersectionObserver} @@ -585,6 +586,7 @@ export class TopSite extends React.PureComponent { advertiser: title.toLocaleLowerCase(), source: NEWTAB_SOURCE, visible_topsites: this.props.visibleTopSites, + frecency_boosted: this.props.link.type === "frecency-boost", }, }) ); diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js @@ -8544,7 +8544,8 @@ class TopSiteLink extends (external_React_default()).PureComponent { reporting_url: link.sponsored_impression_url, advertiser: title.toLocaleLowerCase(), source: NEWTAB_SOURCE, - visible_topsites: visibleTopSites + visible_topsites: visibleTopSites, + frecency_boosted: link.type === "frecency-boost" } // For testing. , @@ -8764,7 +8765,8 @@ class TopSite extends (external_React_default()).PureComponent { reporting_url: this.props.link.sponsored_click_url, advertiser: title.toLocaleLowerCase(), source: NEWTAB_SOURCE, - visible_topsites: this.props.visibleTopSites + visible_topsites: this.props.visibleTopSites, + frecency_boosted: this.props.link.type === "frecency-boost" } })); } else { diff --git a/browser/extensions/newtab/lib/ActivityStream.sys.mjs b/browser/extensions/newtab/lib/ActivityStream.sys.mjs @@ -982,6 +982,14 @@ export const PREFS_CONFIG = new Map([ }, ], [ + "sov.frecency.exposure", + { + title: + "Is or was the user eligible for frecency ranked sponsored shortcuts", + value: false, + }, + ], + [ "sov.amp.allocation", { title: "How many positions can be filled from amp", diff --git a/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs b/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs @@ -78,6 +78,7 @@ const PREF_SYSTEM_INFERRED_PERSONALIZATION = "discoverystream.sections.personalization.inferred.enabled"; const PREF_SECTIONS_PERSONALIZATION_ENABLED = "discoverystream.sections.personalization.enabled"; +const PREF_SOV_FRECENCY_EXPOSURE = "sov.frecency.exposure"; const TOP_STORIES_SECTION_NAME = "top_stories_section"; @@ -606,6 +607,17 @@ export class TelemetryFeed { } } + sovEnabled() { + const { values } = this.store?.getState()?.Prefs || {}; + const trainhopSovEnabled = values?.trainhopConfig?.sov?.enabled; + return trainhopSovEnabled; + } + + frecencyBoostedHasExposure() { + const { values } = this.store?.getState()?.Prefs || {}; + return values?.[PREF_SOV_FRECENCY_EXPOSURE]; + } + async handleTopSitesSponsoredImpressionStats(action) { const { data } = action; const { @@ -615,6 +627,7 @@ export class TelemetryFeed { advertiser: advertiser_name, tile_id, visible_topsites, + frecency_boosted = false, } = data; // Legacy telemetry expects 1-based tile positions. const legacyTelemetryPosition = position + 1; @@ -632,14 +645,28 @@ export class TelemetryFeed { `${source}_${legacyTelemetryPosition}` ].add(1); if (session) { - Glean.topsites.impression.record({ - advertiser_name, - tile_id, - newtab_visit_id: session.session_id, - is_sponsored: true, - position, - visible_topsites, - }); + if (this.sovEnabled()) { + if (this.privatePingEnabled) { + this.newtabContentPing.recordEvent("topSitesImpression", { + advertiser_name, + tile_id, + is_sponsored: true, + position, + visible_topsites, + frecency_boosted, + frecency_boosted_has_exposure: this.frecencyBoostedHasExposure(), + }); + } + } else { + Glean.topsites.impression.record({ + advertiser_name, + tile_id, + newtab_visit_id: session.session_id, + is_sponsored: true, + position, + visible_topsites, + }); + } } } else if (type === "click") { pingType = "topsites-click"; @@ -647,30 +674,46 @@ export class TelemetryFeed { `${source}_${legacyTelemetryPosition}` ].add(1); if (session) { - Glean.topsites.click.record({ - advertiser_name, - tile_id, - newtab_visit_id: session.session_id, - is_sponsored: true, - position, - visible_topsites, - }); + if (this.sovEnabled()) { + if (this.privatePingEnabled) { + this.newtabContentPing.recordEvent("topSitesClick", { + advertiser_name, + tile_id, + is_sponsored: true, + position, + visible_topsites, + frecency_boosted, + frecency_boosted_has_exposure: this.frecencyBoostedHasExposure(), + }); + } + } else { + Glean.topsites.click.record({ + advertiser_name, + tile_id, + newtab_visit_id: session.session_id, + is_sponsored: true, + position, + visible_topsites, + }); + } } } else { console.error("Unknown ping type for sponsored TopSites impression"); return; } - Glean.topSites.pingType.set(pingType); - Glean.topSites.position.set(legacyTelemetryPosition); - Glean.topSites.source.set(source); - Glean.topSites.tileId.set(tile_id); - if (data.reporting_url && !unifiedAdsTilesEnabled) { - Glean.topSites.reportingUrl.set(data.reporting_url); + if (this.sovEnabled()) { + Glean.topSites.pingType.set(pingType); + Glean.topSites.position.set(legacyTelemetryPosition); + Glean.topSites.source.set(source); + Glean.topSites.tileId.set(tile_id); + if (data.reporting_url && !unifiedAdsTilesEnabled) { + Glean.topSites.reportingUrl.set(data.reporting_url); + } + Glean.topSites.advertiser.set(advertiser_name); + Glean.topSites.contextId.set(await lazy.ContextId.request()); + GleanPings.topSites.submit(); } - Glean.topSites.advertiser.set(advertiser_name); - Glean.topSites.contextId.set(await lazy.ContextId.request()); - GleanPings.topSites.submit(); if (data.reporting_url && this.canSendUnifiedAdsTilesCallbacks) { // Send callback events to MARS unified ads api @@ -1878,13 +1921,24 @@ export class TelemetryFeed { if (action.source === "TOP_SITES") { const { position, advertiser_name, tile_id, isSponsoredTopSite } = datum; - Glean.topsites.dismiss.record({ - advertiser_name, - tile_id, - newtab_visit_id: session.session_id, - is_sponsored: !!isSponsoredTopSite, - position, - }); + if (this.sovEnabled()) { + if (this.privatePingEnabled) { + this.newtabContentPing.recordEvent("topSitesDismiss", { + advertiser_name, + tile_id, + is_sponsored: !!isSponsoredTopSite, + position, + }); + } + } else { + Glean.topsites.dismiss.record({ + advertiser_name, + tile_id, + newtab_visit_id: session.session_id, + is_sponsored: !!isSponsoredTopSite, + position, + }); + } } } } @@ -1895,12 +1949,22 @@ export class TelemetryFeed { const { position, advertiser_name, tile_id } = data; if (session) { - Glean.topsites.showPrivacyClick.record({ - advertiser_name, - tile_id, - newtab_visit_id: session.session_id, - position, - }); + if (this.sovEnabled()) { + if (this.privatePingEnabled) { + this.newtabContentPing.recordEvent("topSitesShowPrivacyClick", { + advertiser_name, + tile_id, + position, + }); + } + } else { + Glean.topsites.showPrivacyClick.record({ + advertiser_name, + tile_id, + newtab_visit_id: session.session_id, + position, + }); + } } } diff --git a/browser/extensions/newtab/lib/TopSitesFeed.sys.mjs b/browser/extensions/newtab/lib/TopSitesFeed.sys.mjs @@ -107,6 +107,7 @@ const PREF_UNIFIED_ADS_BLOCKED_LIST = "unifiedAds.blockedAds"; const PREF_UNIFIED_ADS_ADSFEED_ENABLED = "unifiedAds.adsFeed.enabled"; const PREF_SOV_ENABLED = "sov.enabled"; +const PREF_SOV_FRECENCY_EXPOSURE = "sov.frecency.exposure"; const PREF_SOV_NAME = "sov.name"; const PREF_SOV_AMP_ALLOCATION = "sov.amp.allocation"; const PREF_SOV_FRECENCY_ALLOCATION = "sov.frecency.allocation"; @@ -526,7 +527,7 @@ export class ContileIntegration { sovEnabled() { const { values } = this._topSitesFeed.store.getState().Prefs; const trainhopSovEnabled = values?.trainhopConfig?.sov?.enabled; - return trainhopSovEnabled || values[PREF_SOV_ENABLED]; + return trainhopSovEnabled || values?.[PREF_SOV_ENABLED]; } csvToInts(val) { @@ -1364,7 +1365,7 @@ export class TopSitesFeed { let pagesMap; try { pagesMap = await lazy.PlacesUtils.history.fetchMany( - sponsorsToCheck.map(({ domain }) => `https://${domain}`) + sponsorsToCheck.map(({ domain }) => `https://${domain}/`) ); } catch (error) { lazy.log.warn(`Failed to fetch history data: ${error.message}`); @@ -1392,6 +1393,12 @@ export class TopSitesFeed { }); } + // If we have a matched set of candidates, + // we can check if it's an exposure event. + if (candidates.length) { + this.frecencyBoostedSpocsExposureEvent(); + } + candidates.sort((a, b) => b.frecency - a.frecency); return candidates; } @@ -1405,6 +1412,7 @@ export class TopSitesFeed { if (!this._contile.sovEnabled() || !this._linksWithDefaults?.length) { return []; } + const domainData = {}; // Get domains for current region const userRegion = lazy.Region.home || ""; @@ -1415,6 +1423,21 @@ export class TopSitesFeed { } /** + * Flip exposure event pref, + * if the user is in a SOV experiment, + * for both control and treatment, + * and had frecency boosted spocs because of it. + */ + frecencyBoostedSpocsExposureEvent() { + const { values } = this.store.getState().Prefs; + const trainhopSovEnabled = values?.trainhopConfig?.sov?.enabled; + + if (trainhopSovEnabled) { + this.store.dispatch(ac.SetPref(PREF_SOV_FRECENCY_EXPOSURE, true)); + } + } + + /** * Fetch topsites spocs from the DiscoveryStream feed. * * @returns {Array} An array of sponsored tile objects.