forked from ZenBrowserMirrors/zen-desktop
338 lines
9.7 KiB
JavaScript
338 lines
9.7 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/.
|
|
const kZenStylesheetThemeHeader = '/* Zen Themes - Generated by ZenThemesImporter.';
|
|
const kZenStylesheetThemeHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
|
|
* Your changes will be overwritten.
|
|
* Instead, go to the preferences and edit the themes there.
|
|
*/
|
|
`;
|
|
const kenStylesheetFooter = `
|
|
/* End of Zen Themes */
|
|
`;
|
|
const getCurrentDateTime = () =>
|
|
new Intl.DateTimeFormat('en-US', {
|
|
dateStyle: 'full',
|
|
timeStyle: 'full',
|
|
}).format(new Date().getTime());
|
|
|
|
var gZenStylesheetManager = {
|
|
async writeStylesheet(path, themes) {
|
|
let content = kZenStylesheetThemeHeader;
|
|
content += `\n* FILE GENERATED AT: ${getCurrentDateTime()}\n`;
|
|
content += kZenStylesheetThemeHeaderBody;
|
|
|
|
for (let theme of themes) {
|
|
if (theme.enabled !== undefined && !theme.enabled) {
|
|
continue;
|
|
}
|
|
|
|
content += this.getThemeCSS(theme);
|
|
}
|
|
|
|
content += kenStylesheetFooter;
|
|
|
|
const buffer = new TextEncoder().encode(content);
|
|
|
|
await IOUtils.write(path, buffer);
|
|
},
|
|
|
|
getThemeCSS(theme) {
|
|
let css = '\n';
|
|
|
|
css += `/* Name: ${theme.name} */\n`;
|
|
css += `/* Description: ${theme.description} */\n`;
|
|
css += `/* Author: @${theme.author} */\n`;
|
|
|
|
if (theme._readmeURL) {
|
|
css += `/* Readme: ${theme.readme} */\n`;
|
|
}
|
|
|
|
css += `@import url("${theme._chromeURL}");\n`;
|
|
|
|
return css;
|
|
},
|
|
};
|
|
|
|
var gZenThemesImporter = new (class {
|
|
constructor() {
|
|
try {
|
|
window.SessionStore.promiseInitialized.then(async () => {
|
|
if (
|
|
Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
|
|
Services.appinfo.inSafeMode
|
|
) {
|
|
console.log('[ZenThemesImporter]: Disabling all themes.');
|
|
return;
|
|
}
|
|
|
|
await ZenThemesCommon.getThemes(); // Check for any errors in the themes data file
|
|
const themes = await this.getEnabledThemes();
|
|
|
|
const themesWithPreferences = await Promise.all(
|
|
themes.map(async (theme) => {
|
|
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
|
|
|
return {
|
|
name: theme.name,
|
|
enabled: theme.enabled,
|
|
preferences,
|
|
};
|
|
})
|
|
);
|
|
|
|
this.writeToDom(themesWithPreferences);
|
|
|
|
await this.insertStylesheet();
|
|
});
|
|
console.info('[ZenThemesImporter]: Zen Themes imported');
|
|
} catch (e) {
|
|
console.error('[ZenThemesImporter]: Error importing Zen Themes: ', e);
|
|
}
|
|
|
|
Services.prefs.addObserver(
|
|
'zen.themes.updated-value-observer',
|
|
this.rebuildThemeStylesheet.bind(this),
|
|
false
|
|
);
|
|
Services.prefs.addObserver(
|
|
'zen.themes.disable-all',
|
|
this.handleDisableThemes.bind(this),
|
|
false
|
|
);
|
|
}
|
|
|
|
get sss() {
|
|
if (!this._sss) {
|
|
this._sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(
|
|
Ci.nsIStyleSheetService
|
|
);
|
|
}
|
|
return this._sss;
|
|
}
|
|
|
|
get styleSheetPath() {
|
|
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
|
|
}
|
|
|
|
async handleDisableThemes() {
|
|
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
|
|
console.log('[ZenThemesImporter]: Disabling themes module.');
|
|
|
|
await this.removeStylesheet();
|
|
} else {
|
|
console.log('[ZenThemesImporter]: Enabling themes module.');
|
|
|
|
await this.rebuildThemeStylesheet();
|
|
}
|
|
}
|
|
|
|
get styleSheetURI() {
|
|
if (!this._styleSheetURI) {
|
|
this._styleSheetURI = Services.io.newFileURI(new FileUtils.File(this.styleSheetPath));
|
|
}
|
|
return this._styleSheetURI;
|
|
}
|
|
|
|
getStylesheetURIForTheme(theme) {
|
|
return Services.io.newFileURI(
|
|
new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css'))
|
|
);
|
|
}
|
|
|
|
async insertStylesheet() {
|
|
if (await IOUtils.exists(this.styleSheetPath)) {
|
|
await this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
|
|
}
|
|
|
|
if (this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET)) {
|
|
console.debug('[ZenThemesImporter]: Sheet successfully registered');
|
|
}
|
|
}
|
|
|
|
async removeStylesheet() {
|
|
await this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
|
|
const rv = this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET);
|
|
await IOUtils.remove(this.styleSheetPath, { ignoreAbsent: true });
|
|
|
|
if (!rv && !(await IOUtils.exists(this.styleSheetPath))) {
|
|
console.debug('[ZenThemesImporter]: Sheet successfully unregistered');
|
|
}
|
|
}
|
|
|
|
async rebuildThemeStylesheet() {
|
|
await this.removeStylesheet();
|
|
|
|
const themes = await this.getEnabledThemes();
|
|
|
|
await this.writeStylesheet(themes);
|
|
|
|
const themesWithPreferences = await Promise.all(
|
|
themes.map(async (theme) => {
|
|
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
|
|
|
return {
|
|
name: theme.name,
|
|
enabled: theme.enabled,
|
|
preferences,
|
|
};
|
|
})
|
|
);
|
|
|
|
this.setDefaults(themesWithPreferences);
|
|
this.writeToDom(themesWithPreferences);
|
|
|
|
await this.insertStylesheet();
|
|
}
|
|
|
|
async getEnabledThemes() {
|
|
const themeObject = await ZenThemesCommon.getThemes();
|
|
const themes = Object.values(themeObject).filter(
|
|
(theme) => theme.enabled === undefined || theme.enabled
|
|
);
|
|
|
|
const themeList = themes.map(({ name }) => name).join(', ');
|
|
|
|
const message =
|
|
themeList !== ''
|
|
? `[ZenThemesImporter]: Loading enabled Zen themes: ${themeList}.`
|
|
: '[ZenThemesImporter]: No enabled Zen themes.';
|
|
|
|
console.log(message);
|
|
|
|
return themes;
|
|
}
|
|
|
|
setDefaults(themesWithPreferences) {
|
|
for (const { preferences, enabled } of themesWithPreferences) {
|
|
if (enabled !== undefined && !enabled) {
|
|
continue;
|
|
}
|
|
|
|
for (const { type, property, defaultValue } of preferences) {
|
|
if (defaultValue === undefined) {
|
|
continue;
|
|
}
|
|
|
|
if (type === 'checkbox') {
|
|
const value = Services.prefs.getBoolPref(property, false);
|
|
if (typeof defaultValue !== 'boolean') {
|
|
console.log(
|
|
`[ZenThemesImporter]: Warning, invalid data type received for expected type boolean, skipping.`
|
|
);
|
|
continue;
|
|
}
|
|
|
|
if (!value) {
|
|
Services.prefs.setBoolPref(property, defaultValue);
|
|
}
|
|
} else {
|
|
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
|
|
|
|
if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
|
|
console.log(
|
|
`[ZenThemesImporter]: Warning, invalid data type received (${typeof defaultValue}), skipping.`
|
|
);
|
|
continue;
|
|
}
|
|
|
|
if (value === 'zen-property-no-saved') {
|
|
Services.prefs.setStringPref(property, defaultValue.toString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
writeToDom(themesWithPreferences) {
|
|
for (const browser of ZenMultiWindowFeature.browsers) {
|
|
for (const { enabled, preferences, name } of themesWithPreferences) {
|
|
const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
|
|
|
if (enabled !== undefined && !enabled) {
|
|
const element = browser.document.getElementById(sanitizedName);
|
|
|
|
if (element) {
|
|
element.remove();
|
|
}
|
|
|
|
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
|
|
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
|
|
|
browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
for (const { property, type } of preferences) {
|
|
const value = Services.prefs.getStringPref(property, '');
|
|
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
|
|
|
switch (type) {
|
|
case 'dropdown': {
|
|
if (value !== '') {
|
|
let element = browser.document.getElementById(sanitizedName);
|
|
|
|
if (!element) {
|
|
element = browser.document.createElement('div');
|
|
|
|
element.style.display = 'none';
|
|
element.setAttribute('id', sanitizedName);
|
|
|
|
browser.document.body.appendChild(element);
|
|
}
|
|
|
|
element.setAttribute(sanitizedProperty, value);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'string': {
|
|
if (value === '') {
|
|
browser.document
|
|
.querySelector(':root')
|
|
.style.removeProperty(`--${sanitizedProperty}`);
|
|
} else {
|
|
browser.document
|
|
.querySelector(':root')
|
|
.style.setProperty(`--${sanitizedProperty}`, value);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async writeStylesheet(themeList = []) {
|
|
const themes = [];
|
|
|
|
for (let theme of themeList) {
|
|
theme._chromeURL = this.getStylesheetURIForTheme(theme).spec;
|
|
themes.push(theme);
|
|
}
|
|
|
|
await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes);
|
|
}
|
|
})();
|
|
|
|
gZenActorsManager.addJSWindowActor('ZenThemeMarketplace', {
|
|
parent: {
|
|
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs',
|
|
},
|
|
child: {
|
|
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs',
|
|
events: {
|
|
DOMContentLoaded: {},
|
|
},
|
|
},
|
|
matches: [
|
|
...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
|
|
'about:preferences',
|
|
],
|
|
});
|