Bug 1815898 - Inform users about Colorway theme removal - part 1 - notification bar, r=fluent-reviewers,bolsson,rpl

Differential Revision: https://phabricator.services.mozilla.com/D243441
This commit is contained in:
Andrea Marchesini
2025-04-23 15:17:29 +00:00
parent 9b09e5cf03
commit eaabe2e896
6 changed files with 381 additions and 0 deletions

View File

@@ -47,6 +47,7 @@ category browser-idle-startup resource:///modules/UrlbarSearchTermsPersistence.s
category browser-idle-startup resource:///modules/ShoppingUtils.sys.mjs ShoppingUtils.init
category browser-idle-startup moz-src:///browser/components/search/SERPCategorization.sys.mjs SERPCategorization.init
category browser-idle-startup resource://gre/modules/ContentRelevancyManager.sys.mjs ContentRelevancyManager.init
category browser-idle-startup resource://gre/modules/ColorwayThemeMigration.sys.mjs ColorwayThemeMigration.maybeWarn
#ifdef MOZ_UPDATER
category browser-idle-startup resource://gre/modules/UpdateListener.sys.mjs UpdateListener.maybeShowUnsupportedNotification
#endif

View File

@@ -108,3 +108,8 @@ webext-site-perms-header-unsigned-with-perms = Add { $extension }? This extensio
webext-site-perms-midi = Access MIDI devices
webext-site-perms-midi-sysex = Access MIDI devices with SysEx support
## Colorway theme migration
webext-colorway-theme-migration-notification-message = <b>Your colorway theme was removed.</b> { -brand-shorter-name } updated its colorways collection. You can find the latest versions on the add-ons site.
webext-colorway-theme-migration-notification-button = Get updated colorways

View File

@@ -0,0 +1,102 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
AddonSettings: "resource://gre/modules/addons/AddonSettings.sys.mjs",
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
});
const CLEANUP_UNKNOWN = 0;
const CLEANUP_COMPLETED = 1;
const CLEANUP_COMPLETED_WITH_BUILTIN = 2;
// Cleanup any colorway builtin theme that may still be installed.
async function uninstallAllColorwayBuiltinThemes(activeThemeID) {
const CLEANUP_PREF = "extensions.colorway-builtin-themes-cleanup";
if (
Services.prefs.getIntPref(CLEANUP_PREF, CLEANUP_UNKNOWN) != CLEANUP_UNKNOWN
) {
return false;
}
let builtinColorwayThemeFound = false;
let activeThemeUninstalling = false;
const themes = await lazy.AddonManager.getAddonsByTypes(["theme"]);
for (const theme of themes) {
if (theme.isBuiltinColorwayTheme) {
builtinColorwayThemeFound = true;
if (theme.id === activeThemeID) {
activeThemeUninstalling = true;
}
theme.uninstall();
}
}
Services.prefs.setIntPref(
CLEANUP_PREF,
builtinColorwayThemeFound
? CLEANUP_COMPLETED_WITH_BUILTIN
: CLEANUP_COMPLETED
);
return activeThemeUninstalling;
}
export const ColorwayThemeMigration = {
maybeWarn: async () => {
const activeThemeID = Services.prefs.getCharPref(
"extensions.activeThemeID",
""
);
// Let's remove all the existing colorwy builtin themes.
const activeThemeUninstalled = await uninstallAllColorwayBuiltinThemes(
activeThemeID
).catch(err => {
console.warn("Error on uninstalling all colorways builtin themes", err);
});
if (!activeThemeUninstalled) {
return;
}
// This can go async.
lazy.AddonManager.getAddonByID(lazy.AddonSettings.DEFAULT_THEME_ID).then(
addon => addon.enable()
);
const win = lazy.BrowserWindowTracker.getTopWindow();
win.MozXULElement.insertFTLIfNeeded("toolkit/global/extensions.ftl");
win.gNotificationBox.appendNotification(
"colorway-theme-migration",
{
label: {
"l10n-id": "webext-colorway-theme-migration-notification-message",
},
priority: win.gNotificationBox.PRIORITY_INFO_MEDIUM,
},
[
{
supportPage: "colorways",
},
{
"l10n-id": "webext-colorway-theme-migration-notification-button",
callback: () => {
win.openTrustedLinkIn(
"https://addons.mozilla.org/firefox/collections/4757633/colorways/",
"tab"
);
},
},
]
);
},
};

View File

@@ -71,6 +71,7 @@ EXTRA_JS_MODULES += [
"amManager.sys.mjs",
"amWebAPI.sys.mjs",
"Blocklist.sys.mjs",
"ColorwayThemeMigration.sys.mjs",
"LightweightThemeManager.sys.mjs",
]

View File

@@ -64,6 +64,9 @@ support-files = ["head_abuse_report.js"]
["browser_colorwaybuiltins_migration.js"]
run-if = ["appname == 'firefox'"]
["browser_colorwaybuiltins_notification.js"]
run-if = ["appname == 'firefox'"]
["browser_dragdrop.js"]
skip-if = ["true"] # Bug 1626824

View File

@@ -0,0 +1,269 @@
"use strict";
const { AddonTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/AddonTestUtils.sys.mjs"
);
const { ColorwayThemeMigration } = ChromeUtils.importESModule(
"resource://gre/modules/ColorwayThemeMigration.sys.mjs"
);
AddonTestUtils.initMochitest(this);
const CLEANUP_PREF = "extensions.colorway-builtin-themes-cleanup";
const NON_COLORWAY_THEME_ID = "test-non@colorway.org";
const COLORWAY_THEME_ID = "test-colorway@mozilla.org";
function mockAsyncUninstallMethod(mockProviderAddon) {
// Override the MockAddon uninstall method to mock the behavior
// of uninstalling an XPIProvider add-on, for which uninstalling
// is asynchonous and the add-on may not be gone right away
// (unlike the MockProvider uninstall method which synchonously remove
// the addon from the MockProvider).
const mockUninstall = mockProviderAddon.uninstall.bind(mockProviderAddon);
mockProviderAddon.uninstall = async () => {
//
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 1000));
return mockUninstall();
};
}
function hasNotification() {
return !!window.gNotificationBox.getNotificationWithValue(
"colorway-theme-migration"
);
}
function closeNotification() {
const notification = window.gNotificationBox.getNotificationWithValue(
"colorway-theme-migration"
);
if (notification) {
window.gNotificationBox.removeNotification(notification);
}
}
async function checkColorwayBuiltinTheme(colorwayThemeExists) {
const colorwayTheme = await AddonManager.getAddonByID(COLORWAY_THEME_ID);
is(!!colorwayTheme, colorwayThemeExists, "The colorway theme exists");
}
async function checkNonBuiltinTheme(nonColorwayThemeExists) {
const nonColorwayTheme = await AddonManager.getAddonByID(
NON_COLORWAY_THEME_ID
);
is(
!!nonColorwayTheme,
nonColorwayThemeExists,
"The non colorway theme exists"
);
}
let gProvider;
async function installThemes() {
if (!gProvider) {
gProvider = new MockProvider(["extension"]);
}
const [mockNonColorwayTheme, mockColorwayTheme] = gProvider.createAddons([
{
id: NON_COLORWAY_THEME_ID,
name: "Test Non Colorway theme",
creator: { name: "Artist", url: "https://example.com/artist" },
description: "A nice tree",
type: "theme",
isBuiltinColorwayTheme: false,
isBuiltin: true,
screenshots: [],
},
{
id: COLORWAY_THEME_ID,
name: "Test Colorway theme",
creator: { name: "Artist", url: "https://example.com/artist" },
description: "A nice tree",
type: "theme",
isBuiltinColorwayTheme: true,
isBuiltin: true,
screenshots: [],
},
]);
mockAsyncUninstallMethod(mockNonColorwayTheme);
mockAsyncUninstallMethod(mockColorwayTheme);
await checkColorwayBuiltinTheme(true);
await checkNonBuiltinTheme(true);
}
add_setup(() => {
// Make sure we do close the notificationbox when this test file has been fully run
// (prevents the notificationbox to stay open when other mochitests may run on the
// same application instance and trigger unexpected failures).
registerCleanupFunction(closeNotification);
});
add_task(async function no_colorway_themes() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:blank",
},
async function () {
// Before running the test, let's close existing notifications.
closeNotification();
ok(!hasNotification(), "No notification found when the test is starting");
await SpecialPowers.pushPrefEnv({
set: [[CLEANUP_PREF, 0]],
});
await ColorwayThemeMigration.maybeWarn();
ok(!hasNotification(), "No notification shown with the default theme");
is(SpecialPowers.getIntPref(CLEANUP_PREF), 1, "The cleanup pref is set");
await SpecialPowers.popPrefEnv();
}
);
});
add_task(async function default_theme_no_notification() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:blank",
},
async function () {
// Before running the test, let's close existing notifications.
closeNotification();
ok(!hasNotification(), "No notification found when the test is starting");
await installThemes();
await SpecialPowers.pushPrefEnv({
set: [[CLEANUP_PREF, 0]],
});
// Default theme should not trigger the notification.
const defaultTheme = await AddonManager.getAddonByID(
"default-theme@mozilla.org"
);
ok(!!defaultTheme, "The default theme exists");
await defaultTheme.enable();
const promiseUninstalled =
AddonTestUtils.promiseAddonEvent("onUninstalled");
await ColorwayThemeMigration.maybeWarn();
ok(!hasNotification(), "No notification shown with the default theme");
// No notification shown, but the colorway themes are gone.
await promiseUninstalled;
await checkColorwayBuiltinTheme(false);
await checkNonBuiltinTheme(true);
is(
SpecialPowers.getIntPref(CLEANUP_PREF),
2,
"The cleanup pref is set (builtin add-ons found)"
);
await SpecialPowers.popPrefEnv();
}
);
});
add_task(async function non_colorway_theme_no_notification() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:blank",
},
async function () {
// Before running the test, let's close existing notifications.
closeNotification();
ok(!hasNotification(), "No notification found when the test is starting");
await installThemes();
await SpecialPowers.pushPrefEnv({
set: [[CLEANUP_PREF, 0]],
});
// Let's force a non-colorway theme.
await (await AddonManager.getAddonByID(NON_COLORWAY_THEME_ID)).enable();
await SpecialPowers.pushPrefEnv({
set: [["extensions.activeThemeID", NON_COLORWAY_THEME_ID]],
});
const promiseUninstalled =
AddonTestUtils.promiseAddonEvent("onUninstalled");
await ColorwayThemeMigration.maybeWarn();
ok(
!hasNotification(),
"No notification shown with a non-existing theme != colorway"
);
// No notification shown, but the colorway themes are gone.
await promiseUninstalled;
await checkColorwayBuiltinTheme(false);
await checkNonBuiltinTheme(true);
is(
SpecialPowers.getIntPref(CLEANUP_PREF),
2,
"The cleanup pref is set (builtin add-ons found)"
);
await SpecialPowers.popPrefEnv();
}
);
});
add_task(async function colorway_theme_notification() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:blank",
},
async function () {
// Before running the test, let's close existing notifications.
closeNotification();
ok(!hasNotification(), "No notification found when the test is starting");
await installThemes();
await SpecialPowers.pushPrefEnv({
set: [[CLEANUP_PREF, 0]],
});
// Mock an active colorway builtin theme.
const mockColorwayTheme =
await AddonManager.getAddonByID(COLORWAY_THEME_ID);
await mockColorwayTheme.enable();
await SpecialPowers.pushPrefEnv({
set: [["extensions.activeThemeID", COLORWAY_THEME_ID]],
});
const promiseUninstalled =
AddonTestUtils.promiseAddonEvent("onUninstalled");
await ColorwayThemeMigration.maybeWarn();
ok(
hasNotification(),
"Notification shown with an active colorway builtin theme"
);
await promiseUninstalled;
await checkColorwayBuiltinTheme(false);
await checkNonBuiltinTheme(true);
is(
SpecialPowers.getIntPref(CLEANUP_PREF),
2,
"The cleanup pref is set (builtin add-ons found)"
);
await SpecialPowers.popPrefEnv();
}
);
});