From bc8d8b235cc1e986c2ce2a3e7ebc57eaf557b3f2 Mon Sep 17 00:00:00 2001 From: tophf Date: Sun, 8 Nov 2020 13:27:42 +0300 Subject: [PATCH] fix equalOrEmpty for empty strings --- background/update.js | 21 ++++++--- content/install-hook-userstyles.js | 53 ++++++++++------------- js/sections-util.js | 69 +++++++++--------------------- 3 files changed, 59 insertions(+), 84 deletions(-) diff --git a/background/update.js b/background/update.js index 33c2022c..c458426e 100644 --- a/background/update.js +++ b/background/update.js @@ -1,7 +1,18 @@ -/* global styleSectionsEqual prefs download tryJSONparse ignoreChromeError - calcStyleDigest getStyleWithNoCode debounce chromeLocal - usercss semverCompare styleJSONseemsValid - API_METHODS styleManager */ +/* global + API_METHODS + calcStyleDigest + chromeLocal + debounce + download + getStyleWithNoCode + ignoreChromeError + prefs + semverCompare + styleJSONseemsValid + styleManager + tryJSONparse + usercss +*/ 'use strict'; (() => { @@ -208,7 +219,7 @@ delete json.enabled; 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. return styleManager.installStyle(newStyle) .then(saved => { diff --git a/content/install-hook-userstyles.js b/content/install-hook-userstyles.js index 1adc4e89..f01f1a8f 100644 --- a/content/install-hook-userstyles.js +++ b/content/install-hook-userstyles.js @@ -264,40 +264,31 @@ .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}) { - if (!a || !b) { - return undefined; + const targets = ['urls', 'urlPrefixes', 'domains', 'regexps']; + 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) { - return false; + function equalOrEmpty(a, b, type, comparator) { + 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 - // 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 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)) - ); + function arrayMirrors(a, b) { + return a.length === b.length && + a.every(el => b.includes(el)) && + b.every(el => a.includes(el)); } } diff --git a/js/sections-util.js b/js/sections-util.js index 68c08bbc..aa0d01c7 100644 --- a/js/sections-util.js +++ b/js/sections-util.js @@ -23,57 +23,30 @@ function styleSectionGlobal(section) { } /** - * @param {Style} a - first style object - * @param {Style} b - second style object - * @param {Object} options - * @param {Boolean=} options.ignoreCode - - * true used by invalidateCache to determine if cached filters should be cleared - * @param {Boolean=} options.checkSource - - * true used by update check to compare the server response - * instead of sections that depend on @preprocessor - * @returns {Boolean|undefined} + * 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(a, b, {ignoreCode, checkSource} = {}) { - if (checkSource && - typeof a.sourceCode === 'string' && - typeof b.sourceCode === 'string') { - return a.sourceCode === b.sourceCode; +function styleSectionsEqual({sections: a}, {sections: b}) { + const targets = ['urls', 'urlPrefixes', 'domains', 'regexps']; + 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)); } - a = a.sections; - b = b.sections; - if (!a || !b) { - return undefined; + function equalOrEmpty(a, b, type, comparator) { + 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); } - if (a.length !== b.length) { - return false; - } - // order of sections should be identical to account for the case of multiple - // 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)) - ); + function arrayMirrors(a, b) { + return a.length === b.length && + a.every(el => b.includes(el)) && + b.every(el => a.includes(el)); } }