tor-browser

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

commit fb0d97f9ef6adfcb656439d5594f09d340144c63
parent a4680f6b5f497e6f59014c5294cfdfa11d9ae5eb
Author: Duncan McIntosh <dmcintosh@mozilla.com>
Date:   Wed,  1 Oct 2025 13:51:38 +0000

Bug 1989093 - Add ASRoutingTargeting attribute to check whether the current tab is installed as a web app. r=nrishel,emcminn,omc-reviewers

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

Diffstat:
Mbrowser/components/asrouter/docs/targeting-attributes.md | 12++++++++++++
Mbrowser/components/asrouter/modules/ASRouterTargeting.sys.mjs | 22++++++++++++++++++++++
Mbrowser/components/asrouter/tests/browser/browser_asrouter_targeting.js | 48++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+), 0 deletions(-)

diff --git a/browser/components/asrouter/docs/targeting-attributes.md b/browser/components/asrouter/docs/targeting-attributes.md @@ -25,6 +25,7 @@ Please note that some targeting attributes require stricter controls on the tele * [creditCardsSaved](#creditcardssaved) * [currentDate](#currentdate) * [currentTabGroups](#currenttabgroups) +* [currentTabInstalledAsWebApp](#currenttabinstalledaswebapp) * [currentProfileId](#currentprofileid) * [defaultPDFHandler](#defaultpdfhandler) * [devToolsOpenedCount](#devtoolsopenedcount) @@ -666,6 +667,17 @@ Pref used by system administrators to disallow add-ons from installed altogether ```ts declare const xpinstallEnabled: boolean; ``` + +### `currentTabInstalledAsWebApp` + +Returns whether the current tab has a matching Web App (Taskbar Tab) installed. + +#### Definition + +```ts +declare const currentTabInstalledAsWebApp: Promise<boolean>; +``` + ### `currentTabGroups` Returns the number of currently open tab groups. diff --git a/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs b/browser/components/asrouter/modules/ASRouterTargeting.sys.mjs @@ -71,6 +71,7 @@ ChromeUtils.defineESModuleGetters(lazy, { "resource:///modules/profiles/SelectableProfileService.sys.mjs", SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs", TargetingContext: "resource://messaging-system/targeting/Targeting.sys.mjs", + TaskbarTabs: "resource:///modules/taskbartabs/TaskbarTabs.sys.mjs", TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.sys.mjs", TelemetrySession: "resource://gre/modules/TelemetrySession.sys.mjs", WindowsLaunchOnLogin: "resource://gre/modules/WindowsLaunchOnLogin.sys.mjs", @@ -829,6 +830,27 @@ const TargetingGetters = { let totalTabGroups = win.gBrowser.getAllTabGroups().length; return totalTabGroups; }, + get currentTabInstalledAsWebApp() { + let win = lazy.BrowserWindowTracker.getTopWindow({ + allowFromInactiveWorkspace: true, + }); + if (!win) { + // There is no active tab, so it isn't a web app. + return false; + } + + // Note: this is a promise! + return ( + lazy.TaskbarTabs.findTaskbarTab( + win.gBrowser.selectedBrowser.currentURI, + win.gBrowser.selectedTab.userContextId + ) + .then(aTaskbarTab => aTaskbarTab !== null) + // If this is not an nsIURL (e.g. if it's about:blank), then this will + // throw; in that case there isn't a matching web app. + .catch(() => false) + ); + }, get hasPinnedTabs() { for (let win of Services.wm.getEnumerator("navigator:browser")) { if (win.closed || !win.ownerGlobal.gBrowser) { diff --git a/browser/components/asrouter/tests/browser/browser_asrouter_targeting.js b/browser/components/asrouter/tests/browser/browser_asrouter_targeting.js @@ -25,8 +25,11 @@ ChromeUtils.defineESModuleGetters(this, { QueryCache: "resource:///modules/asrouter/ASRouterTargeting.sys.mjs", Region: "resource://gre/modules/Region.sys.mjs", ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs", + sinon: "resource://testing-common/Sinon.sys.mjs", Spotlight: "resource:///modules/asrouter/Spotlight.sys.mjs", TargetingContext: "resource://messaging-system/targeting/Targeting.sys.mjs", + TaskbarTabs: "resource:///modules/taskbartabs/TaskbarTabs.sys.mjs", + TaskbarTabsPin: "resource:///modules/taskbartabs/TaskbarTabsPin.sys.mjs", TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.sys.mjs", TelemetrySession: "resource://gre/modules/TelemetrySession.sys.mjs", }); @@ -966,6 +969,51 @@ add_task(async function check_xpinstall_enabled() { is(await ASRouterTargeting.Environment.xpinstallEnabled, true); }); +add_task(async function check_current_tab_installed_as_web_app() { + // By default, Taskbar Tabs will try and pin this to the taskbar, but we + // don't want to do that in this test. + const sandbox = sinon.createSandbox(); + sandbox.stub(TaskbarTabsPin, "pinTaskbarTab"); + sandbox.stub(TaskbarTabsPin, "unpinTaskbarTab"); + + const kUri = "https://example.com"; + + await BrowserTestUtils.withNewTab(kUri, async () => { + is( + await ASRouterTargeting.Environment.currentTabInstalledAsWebApp, + false, + "no taskbar tab exists yet" + ); + + const tt = await TaskbarTabs.findOrCreateTaskbarTab( + Services.io.newURI(kUri), + 0 + ); + is( + await ASRouterTargeting.Environment.currentTabInstalledAsWebApp, + true, + "should be true when a Taskbar Tab exists for this tab" + ); + + await BrowserTestUtils.withNewTab("mochi.test:8888", async () => { + is( + await ASRouterTargeting.Environment.currentTabInstalledAsWebApp, + false, + "should be false even if a Taskbar Tab exists for a different tab" + ); + }); + + await TaskbarTabs.removeTaskbarTab(tt.id); + is( + await ASRouterTargeting.Environment.currentTabInstalledAsWebApp, + false, + "should be false after removing the Taskbar Tab" + ); + }); + + sandbox.restore(); +}); + add_task(async function check_pinned_tabs() { await BrowserTestUtils.withNewTab( { gBrowser, url: "about:blank" },