tor-browser

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

commit 40facf4946eaa56583da9905a7aa657dd381fb61
parent d4dd1f103a41296033efa972388fb8534878c75a
Author: Rob Wu <rob@robwu.nl>
Date:   Thu,  9 Oct 2025 01:59:35 +0000

Bug 1778684 - Add info in empty panel when there is a disabled extension r=rpl,fluent-reviewers,bolsson

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

Diffstat:
Mbrowser/base/content/browser-addons.js | 23++++++++++++++++++++++-
Mbrowser/components/extensions/test/browser/browser_unified_extensions_empty_panel.js | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/locales/en-US/browser/unifiedExtensions.ftl | 1+
3 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js @@ -2298,6 +2298,11 @@ var gUnifiedExtensions = { return policies.some(p => !p.privateBrowsingAllowed); }, + async isAtLeastOneExtensionDisabled() { + const addons = await AddonManager.getAddonsByTypes(["extension"]); + return addons.some(a => !a.hidden && !a.isActive); + }, + handleEvent(event) { switch (event.type) { case "ViewShowing": @@ -2389,6 +2394,21 @@ var gUnifiedExtensions = { "unified-extensions-empty-content-explain-enable" ); emptyStateBox.hidden = false; + } else { + emptyStateBox.hidden = true; + this.isAtLeastOneExtensionDisabled().then(result => { + if (result) { + document.l10n.setAttributes( + emptyStateBox.querySelector("h2"), + "unified-extensions-empty-reason-extension-not-enabled" + ); + document.l10n.setAttributes( + emptyStateBox.querySelector("description"), + "unified-extensions-empty-content-explain-enable" + ); + emptyStateBox.hidden = false; + } + }); } const container = panelview.querySelector( @@ -2609,7 +2629,8 @@ var gUnifiedExtensions = { // and no alternative content is available for display in the panel. if ( !this.hasExtensionsInPanel() && - !this.isPrivateWindowMissingExtensionsWithoutPBMAccess() + !this.isPrivateWindowMissingExtensionsWithoutPBMAccess() && + !(await this.isAtLeastOneExtensionDisabled()) ) { let viewID; if ( diff --git a/browser/components/extensions/test/browser/browser_unified_extensions_empty_panel.js b/browser/components/extensions/test/browser/browser_unified_extensions_empty_panel.js @@ -3,12 +3,25 @@ "use strict"; +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); +AddonTestUtils.initMochitest(this); + const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" ); loadTestSubscript("head_unified_extensions.js"); +// The createExtensions helper (using ExtensionTestUtils.loadExtension) does +// not support disabled add-ons. This helper uses AOM directly instead. +async function promiseInstallWebExtension(extensionData) { + let addonFile = AddonTestUtils.createTempWebExtensionFile(extensionData); + let { addon } = await AddonTestUtils.promiseInstallFile(addonFile); + return addon; +} + add_setup(async function () { // Make sure extension buttons added to the navbar will not overflow in the // panel, which could happen when a previous test file resizes the current @@ -271,3 +284,76 @@ add_task(async function test_button_click_in_pbm_pinned_and_no_access() { await Promise.all(extensions.map(extension => extension.unload())); }); + +add_task(async function test_empty_state_with_disabled_addon() { + const [extension] = createExtensions([{ name: "The Only Extension" }]); + await extension.startup(); + const addon = await AddonManager.getAddonByID(extension.id); + await addon.disable(); + + const win = await BrowserTestUtils.openNewBrowserWindow(); + + // This clicks on gUnifiedExtensions.button and waits for panel to show. + await openExtensionsPanel(win); + + let emptyStateBox = getEmptyStateContainer(win); + ok(BrowserTestUtils.isVisible(emptyStateBox), "Empty state is visible"); + is( + emptyStateBox.querySelector("h2").getAttribute("data-l10n-id"), + "unified-extensions-empty-reason-extension-not-enabled", + "Has header 'You have extensions installed, but not enabled'" + ); + is( + emptyStateBox.querySelector("description").getAttribute("data-l10n-id"), + "unified-extensions-empty-content-explain-enable", + "Has description pointing to Manage extensions button." + ); + + await BrowserTestUtils.closeWindow(win); + + await extension.unload(); +}); + +// This test shows that non-extension add-ons are ignored in evaluating whether +// the empty panel should be shown, even if there is another reason that could +// potentially match for extension types (e.g. add-on being disabled). +add_task(async function test_no_empty_state_with_disabled_non_extension() { + const disabledDictAddon = await promiseInstallWebExtension({ + manifest: { + name: "This is a dictionary (definitely not type 'extension') (disabled)", + dictionaries: {}, + browser_specific_settings: { gecko: { id: "@dict-disabled" } }, + }, + }); + const dictAddon = await promiseInstallWebExtension({ + manifest: { + name: "This is a dictionary (definitely not type 'extension') (enabled)", + dictionaries: {}, + browser_specific_settings: { gecko: { id: "@dict-not-disabled" } }, + }, + }); + await disabledDictAddon.disable(); + is(disabledDictAddon.isActive, false, "One of the dict add-ons was disabled"); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:robots" }, + async () => { + // Primary click should open about:addons. Notably, the extensions panel + // and "You have extensions installed, but not enabled" is not shown. + const tabPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + "about:addons", + true + ); + + gUnifiedExtensions.button.click(); + + const tab = await tabPromise; + ok(true, "about:addons opened instead of panel about disabled add-ons"); + BrowserTestUtils.removeTab(tab); + } + ); + + await disabledDictAddon.uninstall(); + await dictAddon.uninstall(); +}); diff --git a/browser/locales/en-US/browser/unifiedExtensions.ftl b/browser/locales/en-US/browser/unifiedExtensions.ftl @@ -10,6 +10,7 @@ unified-extensions-header-title = Extensions unified-extensions-manage-extensions = .label = Manage extensions unified-extensions-empty-reason-private-browsing-not-allowed = You have extensions installed, but not enabled in private windows +unified-extensions-empty-reason-extension-not-enabled = You have extensions installed, but not enabled unified-extensions-empty-content-explain-enable = Select “{ unified-extensions-item-message-manage }” to enable them in settings. ## An extension in the main list