commit 6e07030e5f86a3f1e512c72b55967f06c5bcf479
parent 805fffea427c8d3be92e8465828bfd361043a33b
Author: Carlos Jeurissen <carlos-mozilla@jeurissen.co>
Date: Tue, 6 Jan 2026 15:42:15 +0000
Bug 1994948 - prefer theme_icons.dark for default themes r=robwu,desktop-theme-reviewers,jsudiaman,emilio
Swaps the internal "dark" and "light" property names to match the conventions of dark/light scheme, and now uses theme_icons.dark instead of default_icon for default light themes.
Differential Revision: https://phabricator.services.mozilla.com/D270302
Diffstat:
6 files changed, 128 insertions(+), 131 deletions(-)
diff --git a/browser/components/extensions/parent/ext-browserAction.js b/browser/components/extensions/parent/ext-browserAction.js
@@ -913,37 +913,41 @@ this.browserAction = class extends ExtensionAPIPersistent {
}
getIconData(icons) {
- let getIcon = (icon, theme) => {
+ const getIcon = (icon, theme) => {
if (typeof icon === "object") {
return IconDetails.escapeUrl(icon[theme]);
}
return IconDetails.escapeUrl(icon);
};
- let getStyle = (name, icon1x, icon2x) => {
- return `
- --webextension-${name}: image-set(
- url("${getIcon(icon1x, "default")}"),
- url("${getIcon(icon2x, "default")}") 2x
- );
- --webextension-${name}-light: image-set(
- url("${getIcon(icon1x, "light")}"),
- url("${getIcon(icon2x, "light")}") 2x
- );
- --webextension-${name}-dark: image-set(
- url("${getIcon(icon1x, "dark")}"),
- url("${getIcon(icon2x, "dark")}") 2x
- );
- `;
+ const getBackgroundImage = (icon1x, icon2x = icon1x) => {
+ const image1x = `url("${icon1x}")`;
+ if (icon2x === icon1x) {
+ return image1x;
+ }
+
+ const image2x = `url("${icon2x}")`;
+ return `image-set(${image1x} 1dppx, ${image2x} 2dppx);`;
+ };
+
+ const getStyle = (cssVarName, icon1x, icon2x) => {
+ return `${cssVarName}: ${getBackgroundImage(
+ getIcon(icon1x, "light"),
+ getIcon(icon2x, "light")
+ )};
+ ${cssVarName}-dark: ${getBackgroundImage(
+ getIcon(icon1x, "dark"),
+ getIcon(icon2x, "dark")
+ )};`;
};
- let icon16 = IconDetails.getPreferredIcon(icons, this.extension, 16).icon;
- let icon32 = IconDetails.getPreferredIcon(icons, this.extension, 32).icon;
- let icon64 = IconDetails.getPreferredIcon(icons, this.extension, 64).icon;
+ const icon16 = IconDetails.getPreferredIcon(icons, this.extension, 16).icon;
+ const icon32 = IconDetails.getPreferredIcon(icons, this.extension, 32).icon;
+ const icon64 = IconDetails.getPreferredIcon(icons, this.extension, 64).icon;
return `
- ${getStyle("menupanel-image", icon32, icon64)}
- ${getStyle("toolbar-image", icon16, icon32)}
+ ${getStyle("--webextension-menupanel-image", icon32, icon64)}
+ ${getStyle("--webextension-toolbar-image", icon16, icon32)}
`;
}
diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js b/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
@@ -3,16 +3,17 @@
"use strict";
function testHiDpiImage(button, images1x, images2x, prop) {
- let image = getRawListStyleImage(button);
+ const image = getRawListStyleImage(button);
info(image);
info(button.outerHTML);
- let image1x = images1x[prop];
- let image2x = images2x[prop];
- is(
- image,
- `image-set(url("${image1x}") 1dppx, url("${image2x}") 2dppx)`,
- prop
- );
+ const image1x = images1x[prop];
+ const image2x = images2x[prop];
+ const backgroundImage =
+ image1x === image2x && prop === "browserActionImageURL"
+ ? `url("${image1x}")`
+ : `image-set(url("${image1x}") 1dppx, url("${image2x}") 2dppx)`;
+
+ is(image, backgroundImage, prop);
}
// Test that various combinations of icon details specs, for both paths
diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_theme_icons.js b/browser/components/extensions/test/browser/browser_ext_browserAction_theme_icons.js
@@ -17,21 +17,25 @@ const TOOLBAR_MAPPING = {
tabstrip: "TabsToolbar",
};
+const DEFAULT_ICON = "default.png";
+const LIGHT_THEME_ICON = "black.png";
+const DARK_THEME_ICON = "white.png";
+
async function testBrowserAction(extension, expectedIcon) {
- let browserActionWidget = getBrowserActionWidget(extension);
+ const browserActionWidget = getBrowserActionWidget(extension);
await promiseAnimationFrame();
- let browserActionButton = browserActionWidget
+ const browserActionButton = browserActionWidget
.forWindow(window)
.node.querySelector(".unified-extensions-item-action-button");
- let image = getListStyleImage(browserActionButton);
+ const image = getListStyleImage(browserActionButton);
ok(
- image.includes(expectedIcon),
+ image?.includes(expectedIcon),
`Expected browser action icon (${image}) to be ${expectedIcon}`
);
}
async function testStaticTheme(options) {
- let {
+ const {
themeData,
themeIcons,
withDefaultIcon,
@@ -39,7 +43,7 @@ async function testStaticTheme(options) {
defaultArea = "navbar",
} = options;
- let manifest = {
+ const manifest = {
browser_action: {
theme_icons: themeIcons,
default_area: defaultArea,
@@ -47,17 +51,17 @@ async function testStaticTheme(options) {
};
if (withDefaultIcon) {
- manifest.browser_action.default_icon = "default.png";
+ manifest.browser_action.default_icon = DEFAULT_ICON;
}
- let extension = ExtensionTestUtils.loadExtension({ manifest });
+ const extension = ExtensionTestUtils.loadExtension({ manifest });
await extension.startup();
// Ensure we show the menupanel at least once. This makes sure that the
// elements we're going to query the style of are in the flat tree.
if (defaultArea == "menupanel") {
- let shown = BrowserTestUtils.waitForPopupEvent(
+ const shown = BrowserTestUtils.waitForPopupEvent(
window.gUnifiedExtensions.panel,
"shown"
);
@@ -66,17 +70,15 @@ async function testStaticTheme(options) {
}
// Confirm that the browser action has the correct default icon before a theme is loaded.
- let toolbarId = TOOLBAR_MAPPING[defaultArea];
- let expectedDefaultIcon;
+ const toolbarId = TOOLBAR_MAPPING[defaultArea];
+
// Some platforms have dark toolbars by default, take it in account when picking the default icon.
- if (
- toolbarId &&
- document.getElementById(toolbarId).hasAttribute("brighttext")
- ) {
- expectedDefaultIcon = "light.png";
- } else {
- expectedDefaultIcon = withDefaultIcon ? "default.png" : "dark.png";
- }
+ const hasDarkToolbar =
+ toolbarId && document.getElementById(toolbarId).hasAttribute("brighttext");
+ const expectedDefaultIcon = hasDarkToolbar
+ ? DARK_THEME_ICON
+ : LIGHT_THEME_ICON;
+
if (Services.appinfo.nativeMenubar) {
ok(
!document.getElementById("toolbar-menubar").hasAttribute("brighttext"),
@@ -85,7 +87,7 @@ async function testStaticTheme(options) {
}
await testBrowserAction(extension, expectedDefaultIcon);
- let theme = ExtensionTestUtils.loadExtension({
+ const theme = ExtensionTestUtils.loadExtension({
manifest: {
theme: {
colors: themeData,
@@ -96,13 +98,7 @@ async function testStaticTheme(options) {
await theme.startup();
// Confirm that the correct icon is used when the theme is loaded.
- if (expectedIcon == "dark") {
- // The dark icon should be used if the area is light.
- await testBrowserAction(extension, "dark.png");
- } else {
- // The light icon should be used if the area is dark.
- await testBrowserAction(extension, "light.png");
- }
+ await testBrowserAction(extension, expectedIcon);
await theme.unload();
@@ -115,11 +111,11 @@ async function testStaticTheme(options) {
add_task(async function browseraction_theme_icons_light_theme() {
await testStaticTheme({
themeData: LIGHT_THEME_COLORS,
- expectedIcon: "dark",
+ expectedIcon: LIGHT_THEME_ICON,
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 19,
},
],
@@ -127,16 +123,16 @@ add_task(async function browseraction_theme_icons_light_theme() {
});
await testStaticTheme({
themeData: LIGHT_THEME_COLORS,
- expectedIcon: "dark",
+ expectedIcon: LIGHT_THEME_ICON,
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 16,
},
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 32,
},
],
@@ -147,11 +143,11 @@ add_task(async function browseraction_theme_icons_light_theme() {
add_task(async function browseraction_theme_icons_dark_theme() {
await testStaticTheme({
themeData: DARK_THEME_COLORS,
- expectedIcon: "light",
+ expectedIcon: DARK_THEME_ICON,
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 19,
},
],
@@ -159,16 +155,16 @@ add_task(async function browseraction_theme_icons_dark_theme() {
});
await testStaticTheme({
themeData: DARK_THEME_COLORS,
- expectedIcon: "light",
+ expectedIcon: DARK_THEME_ICON,
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 16,
},
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 32,
},
],
@@ -177,7 +173,7 @@ add_task(async function browseraction_theme_icons_dark_theme() {
});
add_task(async function browseraction_theme_icons_different_toolbars() {
- let themeData = {
+ const themeData = {
frame: "#000",
tab_background_text: "#fff",
toolbar: "#fff",
@@ -185,11 +181,11 @@ add_task(async function browseraction_theme_icons_different_toolbars() {
};
await testStaticTheme({
themeData,
- expectedIcon: "dark",
+ expectedIcon: LIGHT_THEME_ICON,
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 19,
},
],
@@ -197,28 +193,28 @@ add_task(async function browseraction_theme_icons_different_toolbars() {
});
await testStaticTheme({
themeData,
- expectedIcon: "dark",
+ expectedIcon: LIGHT_THEME_ICON,
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 16,
},
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 32,
},
],
});
await testStaticTheme({
themeData,
- expectedIcon: "light",
+ expectedIcon: DARK_THEME_ICON,
defaultArea: "tabstrip",
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 19,
},
],
@@ -226,17 +222,17 @@ add_task(async function browseraction_theme_icons_different_toolbars() {
});
await testStaticTheme({
themeData,
- expectedIcon: "light",
+ expectedIcon: DARK_THEME_ICON,
defaultArea: "tabstrip",
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 16,
},
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 32,
},
],
@@ -244,17 +240,17 @@ add_task(async function browseraction_theme_icons_different_toolbars() {
});
add_task(async function browseraction_theme_icons_overflow_panel() {
- let themeData = {
+ const themeData = {
popup: "#000",
popup_text: "#fff",
};
await testStaticTheme({
themeData,
- expectedIcon: "dark",
+ expectedIcon: LIGHT_THEME_ICON,
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 19,
},
],
@@ -262,16 +258,16 @@ add_task(async function browseraction_theme_icons_overflow_panel() {
});
await testStaticTheme({
themeData,
- expectedIcon: "dark",
+ expectedIcon: LIGHT_THEME_ICON,
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 16,
},
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 32,
},
],
@@ -279,12 +275,12 @@ add_task(async function browseraction_theme_icons_overflow_panel() {
await testStaticTheme({
themeData,
- expectedIcon: "light",
+ expectedIcon: DARK_THEME_ICON,
defaultArea: "menupanel",
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 19,
},
],
@@ -292,17 +288,17 @@ add_task(async function browseraction_theme_icons_overflow_panel() {
});
await testStaticTheme({
themeData,
- expectedIcon: "light",
+ expectedIcon: DARK_THEME_ICON,
defaultArea: "menupanel",
themeIcons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 16,
},
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 32,
},
],
@@ -310,7 +306,7 @@ add_task(async function browseraction_theme_icons_overflow_panel() {
});
add_task(async function browseraction_theme_icons_dynamic_theme() {
- let themeExtension = ExtensionTestUtils.loadExtension({
+ const themeExtension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["theme"],
},
@@ -328,20 +324,20 @@ add_task(async function browseraction_theme_icons_dynamic_theme() {
await themeExtension.startup();
- let extension = ExtensionTestUtils.loadExtension({
+ const extension = ExtensionTestUtils.loadExtension({
manifest: {
browser_action: {
- default_icon: "default.png",
+ default_icon: DEFAULT_ICON,
default_area: "navbar",
theme_icons: [
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 16,
},
{
- light: "light.png",
- dark: "dark.png",
+ dark: LIGHT_THEME_ICON,
+ light: DARK_THEME_ICON,
size: 32,
},
],
@@ -352,27 +348,27 @@ add_task(async function browseraction_theme_icons_dynamic_theme() {
await extension.startup();
// Confirm that the browser action has the default icon before a theme is set.
- await testBrowserAction(extension, "default.png");
+ await testBrowserAction(extension, LIGHT_THEME_ICON);
// Update the theme to a light theme.
themeExtension.sendMessage("update-theme", LIGHT_THEME_COLORS);
await themeExtension.awaitMessage("theme-updated");
// Confirm that the dark icon is used for the light theme.
- await testBrowserAction(extension, "dark.png");
+ await testBrowserAction(extension, LIGHT_THEME_ICON);
// Update the theme to a dark theme.
themeExtension.sendMessage("update-theme", DARK_THEME_COLORS);
await themeExtension.awaitMessage("theme-updated");
// Confirm that the light icon is used for the dark theme.
- await testBrowserAction(extension, "light.png");
+ await testBrowserAction(extension, DARK_THEME_ICON);
// Unload the theme.
await themeExtension.unload();
- // Confirm that the default icon is used when the theme is unloaded.
- await testBrowserAction(extension, "default.png");
+ // Confirm that the light icon is used when the theme is unloaded.
+ await testBrowserAction(extension, LIGHT_THEME_ICON);
await extension.unload();
});
diff --git a/browser/components/sidebar/browser-sidebar.js b/browser/components/sidebar/browser-sidebar.js
@@ -1764,6 +1764,8 @@ var SidebarController = {
sidebar.label = label;
const updateAttributes = el => {
+ // TODO Bug 1996762 - Add support for dark-theme sidebar icons
+ // --webextension-menuitem-image-dark is used in dark themes
el.style.setProperty("--webextension-menuitem-image", sidebar.icon);
el.setAttribute("label", sidebar.label);
};
diff --git a/browser/themes/shared/addons/unified-extensions.css b/browser/themes/shared/addons/unified-extensions.css
@@ -119,20 +119,13 @@ unified-extensions-item {
list-style-image: var(--webextension-toolbar-image, inherit);
toolbar[brighttext] & {
- list-style-image: var(--webextension-toolbar-image-light, inherit);
- }
- :root[lwtheme] toolbar:not([brighttext]) & {
+ /* separate image used for dark toolbars */
list-style-image: var(--webextension-toolbar-image-dark, inherit);
}
toolbaritem:is([overflowedItem="true"], [cui-areatype="panel"]) > .unified-extensions-item-row-wrapper > & {
list-style-image: var(--webextension-menupanel-image, inherit);
- /* TODO: This feels a bit odd, why do we have three images? It feels we
- * should probably have only two (light/dark), and choose based on
- * prefers-color-scheme + lwt-popup */
:root[lwt-popup="dark"] & {
- list-style-image: var(--webextension-menupanel-image-light, inherit);
- }
- :root[lwt-popup="light"] & {
+ /* separate image used for dark toolbars */
list-style-image: var(--webextension-menupanel-image-dark, inherit);
}
}
diff --git a/toolkit/components/extensions/ExtensionParent.sys.mjs b/toolkit/components/extensions/ExtensionParent.sys.mjs
@@ -2151,15 +2151,16 @@ let IconDetails = {
if (themeIcons) {
themeIcons.forEach(({ size, light, dark }) => {
- let lightURL = baseURI.resolve(light);
- let darkURL = baseURI.resolve(dark);
+ // light and dark are reversed. theme_icons specifies
+ // the color of the icon instead of the toolbar color
+ const lightURL = baseURI.resolve(dark);
+ const darkURL = baseURI.resolve(light);
this._checkURL(lightURL, extension);
this._checkURL(darkURL, extension);
- let defaultURL = result[size] || result[19]; // always fallback to default first
result[size] = {
- default: defaultURL || darkURL, // Fallback to the dark url if no default is specified.
+ default: lightURL, // TODO bug 2008737: Remove default property.
light: lightURL,
dark: darkURL,
};