202 lines
8.8 KiB
JavaScript
202 lines
8.8 KiB
JavaScript
/*
|
|
# 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/.
|
|
*/
|
|
'use strict';
|
|
|
|
import {
|
|
configs
|
|
} from '/common/common.js';
|
|
import * as Color from './color.js';
|
|
import * as Constants from './constants.js';
|
|
|
|
export function generateThemeRules(theme) {
|
|
const rules = [];
|
|
const generateCustomRule = (theme, prefix = '') => {
|
|
for (const key of Object.keys(theme)) {
|
|
if (!theme[key])
|
|
continue;
|
|
const propertyKey = prefix ? `${prefix}-${key}` : key;
|
|
let value = theme[key];
|
|
switch (typeof theme[key]) {
|
|
case 'object':
|
|
generateCustomRule(value, propertyKey);
|
|
break;
|
|
case 'string':
|
|
if (/^[^:]+:\/\//.test(value))
|
|
value = `url(${JSON.stringify(value)})`;
|
|
rules.push(`--theme-${propertyKey}: ${value};`);
|
|
for (let alpha = 10; alpha < 100; alpha += 10) {
|
|
rules.push(`--theme-${propertyKey}-${alpha}: ${Color.overrideCSSAlpha(value, alpha / 100)};`);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
generateCustomRule(theme);
|
|
return rules.join('\n');
|
|
}
|
|
|
|
export async function generateThemeDeclarations(theme) {
|
|
if (!theme ||
|
|
!theme.colors) {
|
|
return `
|
|
:root {
|
|
/* https://searchfox.org/mozilla-central/rev/0c7c41109902cb8967ec3ef2c0ddb326701cfbee/browser/themes/windows/browser.css#15 */
|
|
/* https://searchfox.org/mozilla-central/rev/0c7c41109902cb8967ec3ef2c0ddb326701cfbee/browser/themes/linux/browser.css#20 */
|
|
--non-lwt-selected-tab-background-color-proton: rgba(255, 255, 255, 0.15);
|
|
}`;
|
|
}
|
|
|
|
const extraColors = [];
|
|
const themeFrameColor = theme.colors.frame || theme.colors.accentcolor /* old name */;
|
|
const inactiveTextColor = theme.colors.tab_background_text || theme.colors.textcolor /* old name */;
|
|
const activeTextColor = theme.colors.tab_text || theme.colors.bookmark_text || theme.colors.toolbar_text /* old name */ || inactiveTextColor;
|
|
let bgAlpha = 1;
|
|
let hasImage = false;
|
|
if (theme.images) {
|
|
const isRightside = configs.sidebarPosition == Constants.kTABBAR_POSITION_RIGHT;
|
|
const images = [];
|
|
const frameImage = theme.images.theme_frame || theme.images.headerURL /* old name */;
|
|
if (frameImage) {
|
|
hasImage = true;
|
|
// https://searchfox.org/mozilla-central/rev/532e4b94b9e807d157ba8e55034aef05c1196dc9/browser/themes/shared/tabs.inc.css#537
|
|
extraColors.push('--browser-bg-hover-for-header-image: rgba(0, 0, 0, 0.1);');
|
|
// https://searchfox.org/mozilla-central/rev/532e4b94b9e807d157ba8e55034aef05c1196dc9/browser/base/content/browser.css#20
|
|
extraColors.push('--browser-bg-active-for-header-image: rgba(255, 255, 255, 0.4)');
|
|
// https://searchfox.org/mozilla-central/rev/532e4b94b9e807d157ba8e55034aef05c1196dc9/toolkit/themes/windows/global/global.css#138
|
|
if (Color.isBrightColor(inactiveTextColor)) {
|
|
// for bright text
|
|
extraColors.push('--browser-textshadow-for-header-image: 1px 1px 1.5px black');
|
|
}
|
|
else {
|
|
// for dark text
|
|
extraColors.push('--browser-textshadow-for-header-image: 0 0 1.5px white');
|
|
}
|
|
images.push({
|
|
url: frameImage,
|
|
position: isRightside ? 'top right' : 'top left',
|
|
repeat: 'no-repeat',
|
|
});
|
|
}
|
|
|
|
const positions = theme.properties?.additional_backgrounds_alignment || [];
|
|
const repeats = theme.properties?.additional_backgrounds_tiling || [];
|
|
if (Array.isArray(theme.images.additional_backgrounds) &&
|
|
theme.images.additional_backgrounds.length > 0) {
|
|
const leftImageCount = positions.filter(position => position.includes('left')).length;
|
|
const rightImageCount = positions.filter(position => position.includes('right')).length;
|
|
const repeatableImageCount = repeats.filter(repeat => repeat != 'no-repeat').length;
|
|
for (let i = 0, maxi = theme.images.additional_backgrounds.length; i < maxi; i++) {
|
|
const image = theme.images.additional_backgrounds[i];
|
|
const position = positions.length > 0 && positions[Math.min(i, positions.length - 1)] || 'default';
|
|
const repeat = repeats.length > 0 && repeats[Math.min(i, repeats.length - 1)] || 'default';
|
|
if (repeatableImageCount > 0 &&
|
|
repeat.includes('no-repeat'))
|
|
continue;
|
|
if (position &&
|
|
position.includes('right') != isRightside &&
|
|
repeat == 'no-repeat' &&
|
|
leftImageCount > 0 &&
|
|
rightImageCount > 0)
|
|
continue;
|
|
images.push({
|
|
url: image,
|
|
position,
|
|
repeat,
|
|
size: repeat == 'reepat-y' ? 'auto' : 'auto 100%',
|
|
});
|
|
}
|
|
bgAlpha = 0.75;
|
|
hasImage = true;
|
|
}
|
|
|
|
await Promise.all(images.map(async image => {
|
|
if (image.size)
|
|
return;
|
|
|
|
const loader = new Image();
|
|
try {
|
|
const shouldRepeat = (
|
|
theme.properties &&
|
|
Array.isArray(theme.properties.additional_backgrounds_tiling) &&
|
|
theme.properties.additional_backgrounds_tiling.some(value => value == 'repeat' || value == 'repeat-y')
|
|
);
|
|
const shouldNoRepeat = (
|
|
!theme.properties ||
|
|
!Array.isArray(theme.properties.additional_backgrounds_tiling) ||
|
|
theme.properties.additional_backgrounds_tiling.some(value => value == 'no-repeat')
|
|
);
|
|
let maybeRepeatable = false;
|
|
if (!shouldRepeat && !shouldNoRepeat) {
|
|
await new Promise((resolve, reject) => {
|
|
loader.addEventListener('load', resolve);
|
|
loader.addEventListener('error', reject);
|
|
loader.src = image;
|
|
});
|
|
maybeRepeatable = (loader.width / Math.max(1, loader.height)) <= configs.unrepeatableBGImageAspectRatio;
|
|
}
|
|
if (shouldNoRepeat)
|
|
image.size = 'cover';
|
|
else if (shouldRepeat || maybeRepeatable)
|
|
image.size = 'auto';
|
|
}
|
|
catch(error) {
|
|
console.error(error);
|
|
}
|
|
}));
|
|
|
|
if (hasImage) {
|
|
extraColors.push('--browser-bg-images: ' + images.map(image => `url(${JSON.stringify(image.url)})`).join(','));
|
|
extraColors.push('--browser-bg-position: ' + images.map(image => image.position).join(','));
|
|
extraColors.push('--browser-bg-repeat: ' + images.map(image => image.repeat).join(','));
|
|
extraColors.push('--browser-bg-size: ' + images.map(image => image.size).join(','));
|
|
}
|
|
}
|
|
const themeBaseColor = Color.overrideCSSAlpha(themeFrameColor, bgAlpha);
|
|
let toolbarColor = Color.mixCSSColors(themeBaseColor, 'rgba(255, 255, 255, 0.4)', bgAlpha);
|
|
if (theme.colors.toolbar) {
|
|
if (hasImage) {
|
|
extraColors.push(`--browser-bg-for-header-image: ${theme.colors.toolbar};`);
|
|
toolbarColor = theme.colors.toolbar;
|
|
}
|
|
else {
|
|
toolbarColor = Color.mixCSSColors(themeBaseColor, theme.colors.toolbar);
|
|
}
|
|
extraColors.push(`--browser-toolbar: ${theme.colors.toolbar}`);
|
|
if (Color.isParsable(theme.colors.toolbar) &&
|
|
Color.isParsable(theme.colors.toolbar_text)) {
|
|
const halfTransparentTextColor = Color.mixCSSColors(theme.colors.toolbar_text, theme.colors.toolbar_text, 0.5);
|
|
extraColors.push(`--browser-toolbar_text-darker: ${Color.mixCSSColors(theme.colors.toolbar, halfTransparentTextColor)}`);
|
|
}
|
|
}
|
|
else if (hasImage) {
|
|
extraColors.push('--browser-bg-for-header-image: rgba(255, 255, 255, 0.25);');
|
|
}
|
|
if (theme.colors.tab_line)
|
|
extraColors.push(`--browser-tab-highlighter: ${theme.colors.tab_line}`);
|
|
if (theme.colors.tab_loading)
|
|
extraColors.push(`--browser-loading-indicator: ${theme.colors.tab_loading}`);
|
|
if (theme.colors.tab_selected)
|
|
extraColors.push(`--browser-selected-tab-bg: ${theme.colors.tab_selected}`);
|
|
extraColors.push(generateThemeRules(theme));
|
|
return `
|
|
:root {
|
|
--browser-background: ${themeFrameColor};
|
|
--browser-bg-base: ${toolbarColor};
|
|
--browser-bg-less-lighter: ${Color.mixCSSColors(toolbarColor, 'rgba(255, 255, 255, 0.05)', bgAlpha)};
|
|
--browser-bg-lighter: ${Color.mixCSSColors(toolbarColor, 'rgba(255, 255, 255, 0.1)', bgAlpha)};
|
|
--browser-bg-more-lighter: ${Color.mixCSSColors(toolbarColor, 'rgba(255, 255, 255, 0.25)', bgAlpha)};
|
|
--browser-bg-lightest: ${Color.mixCSSColors(toolbarColor, 'rgba(255, 255, 255, 0.4)', bgAlpha)};
|
|
--browser-bg-less-darker: ${Color.mixCSSColors(toolbarColor, 'rgba(0, 0, 0, 0.1)', bgAlpha)};
|
|
--browser-bg-darker: ${Color.mixCSSColors(toolbarColor, 'rgba(0, 0, 0, 0.25)', bgAlpha)};
|
|
--browser-bg-more-darker: ${Color.mixCSSColors(toolbarColor, 'rgba(0, 0, 0, 0.5)', bgAlpha)};
|
|
--browser-fg: ${inactiveTextColor};
|
|
--browser-fg-active: ${activeTextColor};
|
|
--browser-border: ${Color.overrideCSSAlpha(inactiveTextColor, 0.4)};
|
|
${extraColors.join(';\n')}
|
|
}
|
|
`;
|
|
}
|