tor-browser

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

commit 7f7988f472ba2fc94968b79413bdafe44f454f6d
parent ec1f4eddbcab61ba1c352aac135379a3168f385c
Author: Mike Conley <mconley@mozilla.com>
Date:   Mon, 20 Oct 2025 16:27:30 +0000

Bug 1992486 - Add a probe to determine if the Recent Activity section is enabled. r=home-newtab-reviewers,nbarrett

There's a race condition with runtime ping and metric registration, where the TelemetryFeed
initialization script might execute (and attempt to use the highlightsEnabled metric) before
it has had a chance to be registered in Firefox version 144 and 145.

We work around this by adding a Promise that resolves when NewTabGleanUtils has completed
registration, which TelemetryFeed will wait for.

That workaround is fine for now, but the plan is to fix this more systemically for early-use
metrics and pings in bug 1992522.

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

Diffstat:
Mbrowser/components/newtab/metrics.yaml | 20++++++++++++++++++++
Mbrowser/extensions/newtab/lib/NewTabGleanUtils.sys.mjs | 24++++++++++++++++++++++++
Mbrowser/extensions/newtab/lib/TelemetryFeed.sys.mjs | 16+++++++++++++++-
Mbrowser/extensions/newtab/test/browser/browser_newtab_ping.js | 44++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/browser/components/newtab/metrics.yaml b/browser/components/newtab/metrics.yaml @@ -1257,6 +1257,26 @@ newtab: - mconley@mozilla.com expires: never + highlights_enabled: + lifetime: application + type: boolean + description: > + Whether "highlights" is enabled on the newtab. + AKA the "Recent Activity" section. + Corresponds to the value of the + `browser.newtabpage.activity-stream.feeds.section.highlights` pref. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1992486 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1992486 + data_sensitivity: + - technical + notification_emails: + - mconley@Mozilla.com + expires: never + send_in_pings: + - newtab + newtab.search: enabled: lifetime: application diff --git a/browser/extensions/newtab/lib/NewTabGleanUtils.sys.mjs b/browser/extensions/newtab/lib/NewTabGleanUtils.sys.mjs @@ -28,6 +28,28 @@ const EXTRA_ARGS_TYPES_ALLOWLIST = ["event", "memory_distribution"]; */ export const NewTabGleanUtils = { /** + * Internal Promise.withResolvers() object for tracking metrics and pings + * registration completion. Contains resolve, reject functions and a promise + * that resolves when registerMetricsAndPings completes. + * + * @type {{promise: Promise<void>, resolve: Function, reject: Function}} + * @private + */ + _registrationDone: Promise.withResolvers(), + + /** + * Gets the promise that resolves when metrics and pings registration is + * complete. This allows external code to wait for registration to finish + * before using registered metrics. + * + * @returns {Promise<void>} A promise that resolves when + * registerMetricsAndPings completes + */ + get registrationDone() { + return this._registrationDone.promise; + }, + + /** * Fetches and parses a JSON file from a given resource URI. * * @param {string} resourceURI - The URI of the JSON file to fetch and parse @@ -84,12 +106,14 @@ export const NewTabGleanUtils = { lazy.logConsole.debug( "Successfully registered metrics and pings found in the JSON file" ); + this._registrationDone.resolve(); return true; } catch (e) { lazy.logConsole.error( "Failed to complete registration of metrics and pings found in runtime metrics JSON:", e ); + this._registrationDone.resolve(); return false; } }, diff --git a/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs b/browser/extensions/newtab/lib/TelemetryFeed.sys.mjs @@ -43,6 +43,7 @@ ChromeUtils.defineESModuleGetters(lazy, { TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.sys.mjs", UTEventReporting: "resource://newtab/lib/UTEventReporting.sys.mjs", NewTabContentPing: "resource://newtab/lib/NewTabContentPing.sys.mjs", + NewTabGleanUtils: "resource://newtab/lib/NewTabGleanUtils.sys.mjs", NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs", NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs", }); @@ -134,15 +135,18 @@ const PREF_SURFACE_ID = "telemetry.surfaceId"; const CONTENT_PING_VERSION = 2; const ACTIVITY_STREAM_PREF_BRANCH = "browser.newtabpage.activity-stream."; + const NEWTAB_PING_PREFS = { showSearch: Glean.newtabSearch.enabled, "feeds.topsites": Glean.topsites.enabled, [PREF_SHOW_SPONSORED_TOPSITES]: Glean.topsites.sponsoredEnabled, + "feeds.section.highlights": Glean.newtab.highlightsEnabled, "feeds.section.topstories": Glean.pocket.enabled, [PREF_SHOW_SPONSORED_STORIES]: Glean.pocket.sponsoredStoriesEnabled, topSitesRows: Glean.topsites.rows, showWeather: Glean.newtab.weatherEnabled, }; + const TOP_SITES_BLOCKED_SPONSORS_PREF = "browser.topsites.blockedSponsors"; const TOPIC_SELECTION_SELECTED_TOPICS_PREF = "browser.newtabpage.activity-stream.discoverystream.topicSelection.selectedTopics"; @@ -2149,7 +2153,17 @@ export class TelemetryFeed { } } - _setNewtabPrefMetrics(fullPrefName, isChanged) { + async _setNewtabPrefMetrics(fullPrefName, isChanged) { + // @backward-compat { version 146 } This newtab train-hop compatibility + // shim can be removed once Firefox 146 makes it to the release channel. + const is146AndUp = + Services.vc.compare(AppConstants.MOZ_APP_VERSION, "146.0a1") >= 0; + if (!is146AndUp) { + await lazy.NewTabGleanUtils.registrationDone; + NEWTAB_PING_PREFS["feeds.section.highlights"] = + Glean.newtab.highlightsEnabled; + } + const pref = fullPrefName.slice(ACTIVITY_STREAM_PREF_BRANCH.length); if (!Object.hasOwn(NEWTAB_PING_PREFS, pref)) { return; diff --git a/browser/extensions/newtab/test/browser/browser_newtab_ping.js b/browser/extensions/newtab/test/browser/browser_newtab_ping.js @@ -63,6 +63,8 @@ add_task(async function test_newtab_tab_close_sends_ping() { ); Assert.ok(Glean.newtabSearch.enabled.testGetValue()); Assert.ok(Glean.topsites.enabled.testGetValue()); + // Highlights ("Recent Activity") is off by default. + Assert.ok(!Glean.newtab.highlightsEnabled.testGetValue()); // Sponsored topsites are turned off in tests to avoid making remote requests. Assert.ok(!Glean.topsites.sponsoredEnabled.testGetValue()); Assert.ok(Glean.pocket.enabled.testGetValue()); @@ -121,6 +123,8 @@ add_task(async function test_newtab_tab_nav_sends_ping() { ); Assert.ok(Glean.newtabSearch.enabled.testGetValue()); Assert.ok(Glean.topsites.enabled.testGetValue()); + // Highlights ("Recent Activity") is off by default. + Assert.ok(!Glean.newtab.highlightsEnabled.testGetValue()); // Sponsored topsites are turned off in tests to avoid making remote requests. Assert.ok(!Glean.topsites.sponsoredEnabled.testGetValue()); Assert.ok(Glean.pocket.enabled.testGetValue()); @@ -206,3 +210,43 @@ add_task(async function test_newtab_categorization_sends_ping() { await SpecialPowers.popPrefEnv(); }); + +/** + * Tests that we set Glean.newtab.highlightsEnabled to true if the highlights + * section is enabled. + */ +add_task(async function test_newtab_highlights_enabled_pref() { + Services.fog.testResetFOG(); + sendTriggerMessageSpy.resetHistory(); + await GleanPings.newtab.testSubmission( + () => { + Assert.ok( + Glean.newtab.highlightsEnabled.testGetValue(), + "Highlights are reported as being enabled." + ); + }, + async () => { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.newtabpage.activity-stream.feeds.section.highlights", true], + ["browser.newtabpage.activity-stream.telemetry", true], + ], + }); + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:newtab", + false // waitForLoad; about:newtab is cached so this would never resolve + ); + + await BrowserTestUtils.waitForCondition( + () => sendTriggerMessageSpy.called, + "After about:newtab finishes loading" + ); + sendTriggerMessageSpy.resetHistory(); + BrowserTestUtils.removeTab(tab); + }, + 5000 /* timeout to send the ping */ + ); + + await SpecialPowers.popPrefEnv(); +});