fix equalOrEmpty for empty strings

This commit is contained in:
tophf 2020-11-08 13:27:42 +03:00
parent 5e5fecbcfe
commit bc8d8b235c
3 changed files with 59 additions and 84 deletions

View File

@ -1,7 +1,18 @@
/* global styleSectionsEqual prefs download tryJSONparse ignoreChromeError /* global
calcStyleDigest getStyleWithNoCode debounce chromeLocal API_METHODS
usercss semverCompare styleJSONseemsValid calcStyleDigest
API_METHODS styleManager */ chromeLocal
debounce
download
getStyleWithNoCode
ignoreChromeError
prefs
semverCompare
styleJSONseemsValid
styleManager
tryJSONparse
usercss
*/
'use strict'; 'use strict';
(() => { (() => {
@ -208,7 +219,7 @@
delete json.enabled; delete json.enabled;
const newStyle = Object.assign({}, style, json); const newStyle = Object.assign({}, style, json);
if (styleSectionsEqual(json, style, {checkSource: true})) { if (json.sourceCode === style.sourceCode) {
// update digest even if save === false as there might be just a space added etc. // update digest even if save === false as there might be just a space added etc.
return styleManager.installStyle(newStyle) return styleManager.installStyle(newStyle)
.then(saved => { .then(saved => {

View File

@ -264,40 +264,31 @@
.catch(() => null); .catch(() => null);
} }
/**
* The sections are checked in successive order because it matters when many sections
* match the same URL and they have rules with the same CSS specificity
* @param {Object} a - first style object
* @param {Object} b - second style object
* @returns {?boolean}
*/
function styleSectionsEqual({sections: a}, {sections: b}) { function styleSectionsEqual({sections: a}, {sections: b}) {
if (!a || !b) { const targets = ['urls', 'urlPrefixes', 'domains', 'regexps'];
return undefined; return a && b && a.length === b.length && a.every(sameSection);
function sameSection(secA, i) {
return equalOrEmpty(secA.code, b[i].code, 'string', (a, b) => a === b) &&
targets.every(target => equalOrEmpty(secA[target], b[i][target], 'array', arrayMirrors));
} }
if (a.length !== b.length) { function equalOrEmpty(a, b, type, comparator) {
return false; const typeA = type === 'array' ? Array.isArray(a) : typeof a === type;
const typeB = type === 'array' ? Array.isArray(b) : typeof b === type;
return typeA && typeB && comparator(a, b) ||
(a == null || typeA && !a.length) &&
(b == null || typeB && !b.length);
} }
// order of sections should be identical to account for the case of multiple function arrayMirrors(a, b) {
// sections matching the same URL because the order of rules is part of cascading return a.length === b.length &&
return a.every((sectionA, index) => propertiesEqual(sectionA, b[index])); a.every(el => b.includes(el)) &&
b.every(el => a.includes(el));
function propertiesEqual(secA, secB) {
for (const name of ['urlPrefixes', 'urls', 'domains', 'regexps']) {
if (!equalOrEmpty(secA[name], secB[name], 'every', arrayMirrors)) {
return false;
}
}
return equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a === b);
}
function equalOrEmpty(a, b, telltale, comparator) {
const typeA = a && typeof a[telltale] === 'function';
const typeB = b && typeof b[telltale] === 'function';
return (
(a === null || a === undefined || (typeA && !a.length)) &&
(b === null || b === undefined || (typeB && !b.length))
) || typeA && typeB && a.length === b.length && comparator(a, b);
}
function arrayMirrors(array1, array2) {
return (
array1.every(el => array2.includes(el)) &&
array2.every(el => array1.includes(el))
);
} }
} }

View File

@ -23,57 +23,30 @@ function styleSectionGlobal(section) {
} }
/** /**
* @param {Style} a - first style object * The sections are checked in successive order because it matters when many sections
* @param {Style} b - second style object * match the same URL and they have rules with the same CSS specificity
* @param {Object} options * @param {Object} a - first style object
* @param {Boolean=} options.ignoreCode - * @param {Object} b - second style object
* true used by invalidateCache to determine if cached filters should be cleared * @returns {?boolean}
* @param {Boolean=} options.checkSource -
* true used by update check to compare the server response
* instead of sections that depend on @preprocessor
* @returns {Boolean|undefined}
*/ */
function styleSectionsEqual(a, b, {ignoreCode, checkSource} = {}) { function styleSectionsEqual({sections: a}, {sections: b}) {
if (checkSource && const targets = ['urls', 'urlPrefixes', 'domains', 'regexps'];
typeof a.sourceCode === 'string' && return a && b && a.length === b.length && a.every(sameSection);
typeof b.sourceCode === 'string') { function sameSection(secA, i) {
return a.sourceCode === b.sourceCode; return equalOrEmpty(secA.code, b[i].code, 'string', (a, b) => a === b) &&
targets.every(target => equalOrEmpty(secA[target], b[i][target], 'array', arrayMirrors));
} }
a = a.sections; function equalOrEmpty(a, b, type, comparator) {
b = b.sections; const typeA = type === 'array' ? Array.isArray(a) : typeof a === type;
if (!a || !b) { const typeB = type === 'array' ? Array.isArray(b) : typeof b === type;
return undefined; return typeA && typeB && comparator(a, b) ||
(a == null || typeA && !a.length) &&
(b == null || typeB && !b.length);
} }
if (a.length !== b.length) { function arrayMirrors(a, b) {
return false; return a.length === b.length &&
} a.every(el => b.includes(el)) &&
// order of sections should be identical to account for the case of multiple b.every(el => a.includes(el));
// sections matching the same URL because the order of rules is part of cascading
return a.every((sectionA, index) => propertiesEqual(sectionA, b[index]));
function propertiesEqual(secA, secB) {
for (const name of ['urlPrefixes', 'urls', 'domains', 'regexps']) {
if (!equalOrEmpty(secA[name], secB[name], 'every', arrayMirrors)) {
return false;
}
}
return ignoreCode || equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a === b);
}
function equalOrEmpty(a, b, telltale, comparator) {
const typeA = a && typeof a[telltale] === 'function';
const typeB = b && typeof b[telltale] === 'function';
return (
(a === null || a === undefined || (typeA && !a.length)) &&
(b === null || b === undefined || (typeB && !b.length))
) || typeA && typeB && a.length === b.length && comparator(a, b);
}
function arrayMirrors(array1, array2) {
return (
array1.every(el => array2.includes(el)) &&
array2.every(el => array1.includes(el))
);
} }
} }