Merge pull request #8384 from JosueGalRe/bugfix/mods-page-empty-on-invalid-preferences

bugfix(mods): add file download retry and try catchs to fallback on f…
This commit is contained in:
mr. m
2025-05-18 15:19:05 +02:00
committed by GitHub
4 changed files with 121 additions and 81 deletions

View File

@@ -47,9 +47,10 @@ var ZenThemesCommon = {
if (themes === null || typeof themes !== 'object') {
throw new Error('Themes data file is null');
}
} catch (e) {
} catch {
// If we have a corrupted file, reset it
await IOUtils.writeJSON(this.themesDataFile, {});
Services.wm
.getMostRecentWindow('navigator:browser')
.gZenUIManager.showToast('zen-themes-corrupted', {
@@ -61,46 +62,54 @@ var ZenThemesCommon = {
async getThemePreferences(theme) {
const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json');
if (!(await IOUtils.exists(themePath)) || !theme.preferences) {
return [];
}
const preferences = await IOUtils.readJSON(themePath);
try {
const preferences = await IOUtils.readJSON(themePath);
// compat mode for old preferences, all of them can only be checkboxes
if (typeof preferences === 'object' && !Array.isArray(preferences)) {
console.warn(
`[ZenThemes]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences`
);
const newThemePreferences = [];
// compat mode for old preferences, all of them can only be checkboxes
if (typeof preferences === 'object' && !Array.isArray(preferences)) {
console.warn(
`[ZenThemes]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences`
);
const newThemePreferences = [];
for (let [entry, label] of Object.entries(preferences)) {
const [_, negation = '', os = '', property] =
/(!?)(?:(macos|windows|linux):)?([A-Za-z0-9-_.]+)/g.exec(entry);
const isNegation = negation === '!';
for (let [entry, label] of Object.entries(preferences)) {
const [, negation = '', os = '', property] =
/(!?)(?:(macos|windows|linux):)?([A-Za-z0-9-_.]+)/g.exec(entry);
const isNegation = negation === '!';
if (
(isNegation && os === gZenOperatingSystemCommonUtils.currentOperatingSystem) ||
(os !== '' && os !== gZenOperatingSystemCommonUtils.currentOperatingSystem && !isNegation)
) {
continue;
if (
(isNegation && os === gZenOperatingSystemCommonUtils.currentOperatingSystem) ||
(os !== '' &&
os !== gZenOperatingSystemCommonUtils.currentOperatingSystem &&
!isNegation)
) {
continue;
}
newThemePreferences.push({
property,
label,
type: 'checkbox',
disabledOn: os !== '' ? [os] : [],
});
}
newThemePreferences.push({
property,
label,
type: 'checkbox',
disabledOn: os !== '' ? [os] : [],
});
return newThemePreferences;
}
return newThemePreferences;
return preferences.filter(
({ disabledOn = [] }) =>
!disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem)
);
} catch (e) {
console.error(`[ZenThemes]: Error reading preferences for ${theme.name}:`, e);
return [];
}
return preferences.filter(
({ disabledOn = [] }) =>
!disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem)
);
},
throttle(mainFunction, delay) {

View File

@@ -211,35 +211,30 @@ var gZenThemesImporter = new (class {
continue;
}
switch (type) {
case '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);
}
break;
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;
}
default: {
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
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 (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());
}
if (value === 'zen-property-no-saved') {
Services.prefs.setStringPref(property, defaultValue.toString());
}
}
}

View File

@@ -162,9 +162,12 @@ export class ZenThemeMarketplaceChild extends JSWindowActorChild {
const obj = await data.json();
return obj;
} catch (e) {
console.error('ZTM: Error parsing theme info: ', e);
console.error('ZenThemeMarketplace: Error parsing theme info: ', e);
}
} else console.log(data.status);
} else {
console.error('ZenThemeMarketplace: Error fetching theme info: ', data.status);
}
return null;
}
@@ -185,11 +188,11 @@ export class ZenThemeMarketplaceChild extends JSWindowActorChild {
} else {
themeId = object.themeId;
}
console.info('ZTM: Installing theme with id: ', themeId);
console.info('ZenThemeMarketplace: Installing theme with id: ', themeId);
const theme = await this.getThemeInfo(themeId);
if (!theme) {
console.error('ZTM: Error fetching theme info');
console.error('ZenThemeMarketplace: Error fetching theme info');
return;
}
this.addTheme(theme);

View File

@@ -28,7 +28,8 @@ export class ZenThemeMarketplaceParent extends JSWindowActorParent {
case 'ZenThemeMarketplace:IsThemeInstalled': {
const themeId = message.data.themeId;
const themes = await this.getThemes();
return themes[themeId] ? true : false;
return Boolean(themes?.[themeId]);
}
case 'ZenThemeMarketplace:CheckForUpdates': {
this.checkForThemeUpdates();
@@ -46,14 +47,17 @@ export class ZenThemeMarketplaceParent extends JSWindowActorParent {
}
compareVersions(version1, version2) {
var result = false;
let result = false;
if (typeof version1 !== 'object') {
version1 = version1.toString().split('.');
}
if (typeof version2 !== 'object') {
version2 = version2.toString().split('.');
}
for (var i = 0; i < Math.max(version1.length, version2.length); i++) {
for (let i = 0; i < Math.max(version1.length, version2.length); i++) {
if (version1[i] == undefined) {
version1[i] = 0;
}
@@ -76,7 +80,7 @@ export class ZenThemeMarketplaceParent extends JSWindowActorParent {
let updates = [];
const themes = await this.getThemes();
for (const theme of Object.values(await this.getThemes())) {
for (const theme of Object.values(themes)) {
try {
const themeInfo = await this.sendQuery('ZenThemeMarketplace:GetThemeInfo', {
themeId: theme.id,
@@ -141,29 +145,58 @@ export class ZenThemeMarketplaceParent extends JSWindowActorParent {
return stylesheet;
}
async downloadUrlToFile(url, path, isStyleSheet = false) {
try {
const response = await fetch(url);
const data = await response.text();
const content = isStyleSheet ? this.getStyleSheetFullContent(data) : data;
// convert the data into a Uint8Array
let buffer = new TextEncoder().encode(content);
await IOUtils.write(path, buffer);
} catch (e) {
console.error('ZenThemeMarketplaceParent: Error downloading file', url, e);
async downloadUrlToFile(url, path, isStyleSheet = false, maxRetries = 3, retryDelayMs = 500) {
let attempt = 0;
while (attempt < maxRetries) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`ZenThemeMarketplaceParent: HTTP error! status: ${response.status} for url: ${url}`
);
}
const data = await response.text();
const content = isStyleSheet ? this.getStyleSheetFullContent(data) : data;
// convert the data into a Uint8Array
const buffer = new TextEncoder().encode(content);
await IOUtils.write(path, buffer);
return;
} catch (e) {
attempt++;
if (attempt >= maxRetries) {
console.error('ZenThemeMarketplaceParent: Error downloading file after retries', url, e);
} else {
console.warn(
`ZenThemeMarketplaceParent: Download failed (attempt ${attempt} of ${maxRetries}), retrying in ${retryDelayMs}ms...`,
url,
e
);
await new Promise((res) => setTimeout(res, retryDelayMs));
}
}
}
}
async downloadThemeFileContents(theme) {
const themePath = PathUtils.join(this.themesRootPath, theme.id);
await IOUtils.makeDirectory(themePath, { ignoreExisting: true });
await this.downloadUrlToFile(theme.style, PathUtils.join(themePath, 'chrome.css'), true);
await this.downloadUrlToFile(theme.readme, PathUtils.join(themePath, 'readme.md'));
if (theme.preferences) {
await this.downloadUrlToFile(
theme.preferences,
PathUtils.join(themePath, 'preferences.json')
);
try {
const themePath = PathUtils.join(this.themesRootPath, theme.id);
await IOUtils.makeDirectory(themePath, { ignoreExisting: true });
await this.downloadUrlToFile(theme.style, PathUtils.join(themePath, 'chrome.css'), true);
await this.downloadUrlToFile(theme.readme, PathUtils.join(themePath, 'readme.md'));
if (theme.preferences) {
await this.downloadUrlToFile(
theme.preferences,
PathUtils.join(themePath, 'preferences.json')
);
}
} catch (e) {
console.log('ZenThemeMarketplaceParent: Error downloading theme file contents', theme.id, e);
}
}