tor-browser

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

commit 9ba705aa632e333700c3a0663e5ea7c07895826a
parent 98b6d6251634dbb404c11f130de2d173f780a4d1
Author: Stephen Thompson <sthompson@mozilla.com>
Date:   Tue, 14 Oct 2025 21:00:36 +0000

Bug 1988855 Part 1 - telemetry for pref toggle "Open links from apps next to your active tab" r=mstriemer,tabbrowser-reviewers,Gijs

When a user toggles the "Open links from apps next to your active tab" checkbox in about:preferences, record that interaction and record the new value. This will help us understand whether people are using this feature, whether they are keeping it, etc.

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

Diffstat:
Mbrowser/components/preferences/main.js | 7++++++-
Mbrowser/components/preferences/tests/browser_tabs_open_external_next_to_active_tab.js | 86++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mbrowser/components/tabbrowser/metrics.yaml | 40++++++++++++++++++++++++++++++++++++++++
Mbrowser/modules/BrowserUsageTelemetry.sys.mjs | 31+++++++++++++++++++++++++++++++
4 files changed, 158 insertions(+), 6 deletions(-)

diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js @@ -2985,7 +2985,12 @@ var gMainPane = { const externalLinkOpenOverride = Preferences.get( "browser.link.open_newwindow.override.external" ); - + Glean.linkHandling.openNextToActiveTabSettingsEnabled.set( + inputElement.checked + ); + Glean.linkHandling.openNextToActiveTabSettingsChange.record({ + checked: inputElement.checked, + }); return inputElement.checked ? Ci.nsIBrowserDOMWindow.OPEN_NEWTAB_AFTER_CURRENT : externalLinkOpenOverride.defaultValue; diff --git a/browser/components/preferences/tests/browser_tabs_open_external_next_to_active_tab.js b/browser/components/preferences/tests/browser_tabs_open_external_next_to_active_tab.js @@ -7,14 +7,86 @@ const PREF_NAME = "browser.link.open_newwindow.override.external"; const PREF_VALUE_FEATURE_OFF = Ci.nsIBrowserDOMWindow.OPEN_NEWTAB_BACKGROUND; const PREF_VALUE_FEATURE_ON = Ci.nsIBrowserDOMWindow.OPEN_NEWTAB_AFTER_CURRENT; +let resetTelemetry = async () => { + await Services.fog.testFlushAllChildren(); + Services.fog.testResetFOG(); +}; + +registerCleanupFunction(async () => { + await resetTelemetry(); +}); + +/** + * @param {boolean} shouldBeEnabled + */ +async function assertOpenNextToActiveTabSettingsEnabled(shouldBeEnabled) { + await TestUtils.waitForCondition( + () => + Glean.linkHandling.openNextToActiveTabSettingsEnabled.testGetValue() === + shouldBeEnabled, + "wait for metric to be recorded" + ); + Assert.equal( + Glean.linkHandling.openNextToActiveTabSettingsEnabled.testGetValue(), + shouldBeEnabled, + `metric should have recorded that the pref is ${shouldBeEnabled ? "on" : "off"}` + ); +} + +/** + * @param {number} nthEvent + * @param {boolean} shouldBeChecked + */ +async function assertOpenNextToActiveTabSettingsChange( + nthEvent, + shouldBeChecked +) { + await TestUtils.waitForCondition( + () => + Glean.linkHandling.openNextToActiveTabSettingsChange.testGetValue() + ?.length == nthEvent, + "wait for event to be recorded" + ); + let settingsChangeEvents = + Glean.linkHandling.openNextToActiveTabSettingsChange.testGetValue(); + Assert.ok(settingsChangeEvents, "an event should have been recorded"); + Assert.equal( + settingsChangeEvents.length, + nthEvent, + `${nthEvent} event(s) should have been recorded so far` + ); + Assert.equal( + settingsChangeEvents[nthEvent - 1].extra.checked, + shouldBeChecked.toString(), + `event #${nthEvent} should record that the checkbox was ${shouldBeChecked ? "checked" : "not checked"}` + ); +} + +/** + * @param {number} expectedCounterValue + */ +async function assertBrowserUiInteraction(expectedCounterValue) { + await TestUtils.waitForCondition( + () => + Glean.browserUiInteraction.preferencesPaneGeneral?.openAppLinksNextToActiveTab?.testGetValue() == + expectedCounterValue, + "wait for metric to be recorded" + ); + Assert.equal( + Glean.browserUiInteraction.preferencesPaneGeneral.openAppLinksNextToActiveTab.testGetValue(), + expectedCounterValue, + "click on the pref checkbox should have been counted" + ); +} + add_task(async function test_open_external_link_next_to_active_tab_pref() { await SpecialPowers.pushPrefEnv({ set: [[PREF_NAME, PREF_VALUE_FEATURE_OFF]], }); - await openPreferencesViaOpenPreferencesAPI("paneGeneral", { leaveOpen: true, }); + await resetTelemetry(); const doc = gBrowser.contentDocument; const checkbox = doc.getElementById("openAppLinksNextToActiveTab"); @@ -23,12 +95,10 @@ add_task(async function test_open_external_link_next_to_active_tab_pref() { Assert.ok(checkbox, "pref should have a checkbox"); Assert.ok( !checkbox.checked, - "pref checkbox should not be checked by default" + "pref checkbox should not be checked when the feature is off" ); - info( - "validate checking and unchecking the pref checkbox under default conditions" - ); + info("validate checking and unchecking the pref checkbox"); let becameChecked = BrowserTestUtils.waitForAttribute("checked", checkbox); checkbox.click(); await becameChecked; @@ -38,6 +108,9 @@ add_task(async function test_open_external_link_next_to_active_tab_pref() { PREF_VALUE_FEATURE_ON, "pref should be set to open external links after the active tab" ); + await assertOpenNextToActiveTabSettingsEnabled(true); + await assertOpenNextToActiveTabSettingsChange(1, true); + await assertBrowserUiInteraction(1); let becameUnchecked = BrowserTestUtils.waitForAttributeRemoval( "checked", @@ -50,6 +123,9 @@ add_task(async function test_open_external_link_next_to_active_tab_pref() { !Services.prefs.prefHasUserValue(PREF_NAME), "pref should have been reverted to its default value" ); + await assertOpenNextToActiveTabSettingsEnabled(false); + await assertOpenNextToActiveTabSettingsChange(2, false); + await assertBrowserUiInteraction(2); await SpecialPowers.popPrefEnv(); BrowserTestUtils.removeTab(gBrowser.selectedTab, { animate: false }); diff --git a/browser/components/tabbrowser/metrics.yaml b/browser/components/tabbrowser/metrics.yaml @@ -770,3 +770,43 @@ browser.tabclose: - mconley@mozilla.com expires: never telemetry_mirror: FX_TAB_CLOSE_PERMIT_UNLOAD_TIME_MS + +link.handling: + open_next_to_active_tab_settings_enabled: + type: boolean + lifetime: application + description: > + Records if the user has the "Open links from apps next to your active tab" + feature enabled. + notification_emails: + - sthompson@mozilla.com + bugs: + - https://bugzil.la/1988855 + data_reviews: + - https://bugzil.la/1988855 + data_sensitivity: + - interaction + expires: never + no_lint: + - COMMON_PREFIX + open_next_to_active_tab_settings_change: + type: event + description: > + This is recorded when the "Open links from apps next to your active tab" + checkbox in the General > Tabs section of about:preferences is toggled. + bugs: + - https://bugzil.la/1988855 + data_reviews: + - https://bugzil.la/1988855 + data_sensitivity: + - interaction + notification_emails: + - sthompson@mozilla.com + expires: never + extra_keys: + checked: + description: > + Whether the checkbox was toggled on or off. + type: boolean + no_lint: + - COMMON_PREFIX diff --git a/browser/modules/BrowserUsageTelemetry.sys.mjs b/browser/modules/BrowserUsageTelemetry.sys.mjs @@ -522,8 +522,10 @@ export let BrowserUsageTelemetry = { "media.videocontrols.picture-in-picture.enable-when-switching-tabs.enabled", this ); + Services.prefs.addObserver("idle-daily", this); this._recordUITelemetry(); + this._recordInitialPrefValues(); this.recordPinnedTabsCount(); this._onTabsOpenedTask = new lazy.DeferredTask( @@ -637,6 +639,10 @@ export let BrowserUsageTelemetry = { Glean.pictureinpictureSettings.enableAutotriggerSettings.record(); } break; + + case "idle-daily": + this._recordInitialPrefValues(); + break; } break; } @@ -1212,6 +1218,31 @@ export let BrowserUsageTelemetry = { }, /** + * Records the startup values of prefs that govern important browser behavior + * options. + */ + _recordInitialPrefValues() { + this._recordOpenNextToActiveTabSettingValue(); + }, + + /** + * @param {number} prefValue + * pref `browser.link.open_newwindow.override.external` + */ + _isOpenNextToActiveTabSettingEnabled(prefValue) { + return prefValue == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB_AFTER_CURRENT; + }, + + _recordOpenNextToActiveTabSettingValue() { + const value = Services.prefs.getIntPref( + "browser.link.open_newwindow.override.external" + ); + Glean.linkHandling.openNextToActiveTabSettingsEnabled.set( + this._isOpenNextToActiveTabSettingEnabled(value) + ); + }, + + /** * Adds listeners to a single chrome window. * @param {Window} win */