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:
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();
+});