tor-browser

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

commit 099ce56a644fe786ea963787ea23258f4dc67f64
parent 62ab9e5d620a2ecb6f83fda060198916817bf6ce
Author: Duncan McIntosh <dmcintosh@mozilla.com>
Date:   Tue,  7 Oct 2025 20:53:42 +0000

Bug 1990892 - Delete files when the Taskbar Tab is removed even if unpinning fails. r=nrishel

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

Diffstat:
Mbrowser/components/taskbartabs/TaskbarTabsPin.sys.mjs | 25++++++++++++++++++++-----
Mbrowser/components/taskbartabs/metrics.yaml | 10+++++++++-
Mbrowser/components/taskbartabs/test/browser/browser_taskbarTabs_telemetry.js | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
3 files changed, 97 insertions(+), 32 deletions(-)

diff --git a/browser/components/taskbartabs/TaskbarTabsPin.sys.mjs b/browser/components/taskbartabs/TaskbarTabsPin.sys.mjs @@ -56,11 +56,20 @@ export const TaskbarTabsPin = { * @returns {Promise} Resolves once finished. */ async unpinTaskbarTab(aTaskbarTab, aRegistry) { + let unpinError = null; + let removalError = null; + try { lazy.logConsole.info("Unpinning Taskbar Tab from the taskbar."); let { relativePath } = await generateShortcutInfo(aTaskbarTab); - lazy.ShellService.unpinShortcutFromTaskbar("Programs", relativePath); + + try { + lazy.ShellService.unpinShortcutFromTaskbar("Programs", relativePath); + } catch (e) { + lazy.logConsole.error(`Failed to unpin shortcut: ${e.message}`); + unpinError = e; + } let iconFile = getIconFile(aTaskbarTab); @@ -76,12 +85,18 @@ export const TaskbarTabsPin = { }), IOUtils.remove(iconFile.path), ]); - - Glean.webApp.unpin.record({ result: "Success" }); } catch (e) { - lazy.logConsole.error(`An error occurred while unpinning: ${e.message}`); - Glean.webApp.unpin.record({ result: e.name ?? "Unknown exception" }); + lazy.logConsole.error( + `An error occurred while uninstalling: ${e.message}` + ); + removalError = e; } + + const message = e => (e ? (e.name ?? "Unknown exception") : "Success"); + Glean.webApp.unpin.record({ + result: message(unpinError), + removal_result: message(removalError), + }); }, /** diff --git a/browser/components/taskbartabs/metrics.yaml b/browser/components/taskbartabs/metrics.yaml @@ -92,13 +92,21 @@ web_app: description: > The user unpinned the web app from their taskbar using Firefox. extra_keys: + removal_result: + description: > + Error message from the cleanup process, i.e. deleting the shortcut + and icon, or 'Success' if it succeeds. + type: string result: - description: Error code from the unpinning process. + description: > + Error message from the unpinning process, or 'Success' if it succeeds. type: string bugs: - https://bugzilla.mozilla.org/1966476 + - https://bugzilla.mozilla.org/1990892 data_reviews: - https://phabricator.services.mozilla.com/D254733 + - https://phabricator.services.mozilla.com/D266221 notification_emails: - nrishel@mozilla.com expires: never diff --git a/browser/components/taskbartabs/test/browser/browser_taskbarTabs_telemetry.js b/browser/components/taskbartabs/test/browser/browser_taskbarTabs_telemetry.js @@ -25,11 +25,22 @@ const exposePinResult = () => { } }; +// Similarly, we want to fake an error while deleting to check that telemetry +// reports it. +let gShortcutDeleteResult = null; +const exposeDeleteResult = async () => { + if (gShortcutDeleteResult !== null) { + const error = new Error(); + error.name = gShortcutDeleteResult; + throw error; + } +}; + const proxyNativeShellService = { ...ShellService.shellService, createWindowsIcon: sinon.stub().resolves(), createShortcut: sinon.stub().resolves("dummy_path"), - deleteShortcut: sinon.stub().resolves("dummy_path"), + deleteShortcut: sinon.stub().callsFake(exposeDeleteResult), pinShortcutToTaskbar: sinon.stub().callsFake(exposePinResult), unpinShortcutFromTaskbar: sinon.stub().callsFake(exposePinResult), }; @@ -62,62 +73,93 @@ add_task(async function testInstallAndUninstallMetric() { is(snapshot.length, 1, "Should have recorded an 'uninstall' event"); }); -add_task(async function testPinAndUnpinMetricSuccess() { +async function testPinMetricCustom(aPinResult, aPinMessage = null) { let snapshot; const taskbarTab = gRegistry.findOrCreateTaskbarTab(PARSED_URL, 0); Services.fog.testResetFOG(); - gShortcutPinResult = null; + gShortcutPinResult = aPinResult; await TaskbarTabsPin.pinTaskbarTab(taskbarTab, gRegistry); snapshot = Glean.webApp.pin.testGetValue(); is(snapshot.length, 1, "A single pin event was recorded"); Assert.strictEqual( snapshot[0].extra.result, - "Success", - "The pin event should be successful" - ); - - await TaskbarTabsPin.unpinTaskbarTab(taskbarTab, gRegistry); - snapshot = Glean.webApp.unpin.testGetValue(); - is(snapshot.length, 1, "A single unpin event was recorded"); - Assert.strictEqual( - snapshot[0].extra.result, - "Success", - "The unpin event should be successful" + aPinMessage ?? aPinResult ?? "Success", + `Should record the pin ${aPinResult ? "exception" : "success"}` ); gRegistry.removeTaskbarTab(taskbarTab.id); +} + +add_task(async function testPinMetricSuccess() { + await testPinMetricCustom(null); }); -add_task(async function testPinAndUnpinMetricError() { +add_task(async function testPinMetricFail() { + await testPinMetricCustom("Pin fail!"); +}); + +add_task(async function testPinMetricInvalid() { + await testPinMetricCustom(undefined, "Unknown exception"); +}); + +async function testUnpinMetricCustom( + aUnpinResult, + aDeleteResult, + aUnpinMessage = null, + aDeleteMessage = null +) { let snapshot; const taskbarTab = gRegistry.findOrCreateTaskbarTab(PARSED_URL, 0); Services.fog.testResetFOG(); - gShortcutPinResult = "This test failed!"; + gShortcutPinResult = aUnpinResult; + gShortcutDeleteResult = aDeleteResult; - await TaskbarTabsPin.pinTaskbarTab(taskbarTab, gRegistry); - snapshot = Glean.webApp.pin.testGetValue(); - is(snapshot.length, 1, "A single pin event was recorded"); - Assert.strictEqual( - snapshot[0].extra.result, - "This test failed!", - "The pin event shows failure" - ); + // We've mocked out so much that calling pinTaskbarTab should be irrelevant. await TaskbarTabsPin.unpinTaskbarTab(taskbarTab, gRegistry); snapshot = Glean.webApp.unpin.testGetValue(); is(snapshot.length, 1, "A single unpin event was recorded"); Assert.strictEqual( snapshot[0].extra.result, - "This test failed!", - "The unpin event shows failure" + aUnpinMessage ?? aUnpinResult ?? "Success", + `Should record the unpin ${aUnpinResult ? "exception" : "success"}` + ); + Assert.strictEqual( + snapshot[0].extra.removal_result, + aDeleteMessage ?? aDeleteResult ?? "Success", + `Should record the deletion ${aDeleteResult ? "exception" : "success"}` ); gRegistry.removeTaskbarTab(taskbarTab.id); +} + +add_task(async function testPinAndUnpinMetric_UnpinSuccessDeleteSuccess() { + await testUnpinMetricCustom(null, null); +}); + +add_task(async function testPinAndUnpinMetric_UnpinFailDeleteSuccess() { + await testUnpinMetricCustom("Unpin fail!", null); +}); + +add_task(async function testPinAndUnpinMetric_UnpinSuccessDeleteFail() { + await testUnpinMetricCustom(null, "Deletion fail!"); +}); + +add_task(async function testPinAndUnpinMetric_UnpinSuccessDeleteFail() { + await testUnpinMetricCustom("Unpin fail!", "Deletion fail!"); +}); + +add_task(async function testPinAndUnpinMetric_UnpinInvalid() { + await testUnpinMetricCustom(undefined, null, "Unknown exception", null); +}); + +add_task(async function testPinAndUnpinMetric_DeleteInvalid() { + await testUnpinMetricCustom(null, undefined, null, "Unknown exception"); }); add_task(async function testActivateWhenWindowOpened() {