commit 8f39ccbc3c48ef0f2cadd9ec3320631c018cafb2
parent 898a38978a9b4f20082a5b1701aad864b92129b5
Author: Rob Wu <rob@robwu.nl>
Date: Mon, 20 Oct 2025 14:12:25 +0000
Bug 1992983 - Add notice to extensions panel if in safe mode r=rpl,fluent-reviewers,bolsson
Differential Revision: https://phabricator.services.mozilla.com/D268460
Diffstat:
3 files changed, 59 insertions(+), 0 deletions(-)
diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js
@@ -2485,6 +2485,14 @@ var gUnifiedExtensions = {
"#unified-extensions-messages-container"
);
+ if (Services.appinfo.inSafeMode) {
+ this._messageBarSafemode ??= this._makeMessageBar({
+ messageBarFluentId: "unified-extensions-notice-safe-mode",
+ type: "info",
+ });
+ container.prepend(this._messageBarSafemode);
+ } // No "else" case; inSafeMode flag is fixed at browser startup.
+
if (this.blocklistAttentionInfo?.shouldShow) {
this._messageBarBlocklist = this._createBlocklistMessageBar(container);
} else {
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
@@ -629,3 +629,51 @@ add_task(async function test_empty_state_with_blocklisted_addon_hardblock() {
add_task(async function test_empty_state_with_blocklisted_addon_softblock() {
await do_test_empty_state_with_blocklisted_addon(/* isSoftBlock */ true);
});
+
+add_task(async function test_safe_mode_notice() {
+ const sandbox = sinon.createSandbox();
+ registerCleanupFunction(() => sandbox.restore());
+
+ // Services.appinfo.inSafeMode is ordinarily a constant fixed at browser
+ // startup. We fake its implementation, and use a separate browser window in
+ // the test to make sure that any state derived from reading the inSafeMode
+ // flag is limited to this window.
+ const win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ // Services.appinfo.inSafeMode is a non-configurable property, so to spoof
+ // its value we stub Services.appinfo and let it fall back to the original
+ // implementation for every property, except for inSafeMode.
+ const appinfoStub = new Proxy(Services.appinfo, {
+ get(target, propertyKey) {
+ if (propertyKey === "inSafeMode") {
+ return true;
+ }
+ return Reflect.get(target, propertyKey, target);
+ },
+ });
+ sandbox.stub(Services, "appinfo").get(() => appinfoStub);
+ await openExtensionsPanel(win);
+
+ const messages = getMessageBars(win);
+ is(messages.length, 1, "Got one message bar");
+ const bar = messages[0];
+ is(bar.getAttribute("type"), "info", "Bar is informational notice");
+ ok(!bar.hasAttribute("dismissable"), "Bar is not dismissable");
+
+ // We don't exactly care which empty state is shown, as the notice is
+ // independent of the empty state. We just verify as a sanity check that the
+ // panel is indeed empty, which is most realistic when users enter safe mode.
+ let emptyStateBox = getEmptyStateContainer(win);
+ ok(BrowserTestUtils.isVisible(emptyStateBox), "Empty state is visible");
+
+ await closeExtensionsPanel(win);
+
+ // Closing and re-opening should show one bar.
+ await openExtensionsPanel(win);
+ is(getMessageBars(win).length, 1, "Still one bar");
+ await closeExtensionsPanel(win);
+
+ sandbox.restore();
+ is(Services.appinfo.inSafeMode, false, "Restored original inSafeMode");
+
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/locales/en-US/browser/unifiedExtensions.ftl b/browser/locales/en-US/browser/unifiedExtensions.ftl
@@ -56,6 +56,9 @@ unified-extensions-context-menu-move-widget-down =
## Notifications
+unified-extensions-notice-safe-mode =
+ .message = All extensions have been disabled by Troubleshoot Mode.
+
# .heading is processed by moz-message-bar to be used as a heading attribute
unified-extensions-mb-quarantined-domain-message-3 =
.heading = Some extensions are not allowed