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:
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