| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- 'use strict';
- /* exported
- calcStyleDigest
- MozDocMapper
- styleCodeEmpty
- styleJSONseemsValid
- styleSectionGlobal
- styleSectionsEqual
- */
- const MozDocMapper = {
- TO_CSS: {
- urls: 'url',
- urlPrefixes: 'url-prefix',
- domains: 'domain',
- regexps: 'regexp',
- },
- FROM_CSS: {
- 'url': 'urls',
- 'url-prefix': 'urlPrefixes',
- 'domain': 'domains',
- 'regexp': 'regexps',
- },
- /**
- * @param {Object} section
- * @param {function(func:string, value:string)} fn
- */
- forEachProp(section, fn) {
- for (const [propName, func] of Object.entries(MozDocMapper.TO_CSS)) {
- const props = section[propName];
- if (props) props.forEach(value => fn(func, value));
- }
- },
- /**
- * @param {Array<?[type,value]>} funcItems
- * @param {?Object} [section]
- * @returns {Object} section
- */
- toSection(funcItems, section = {}) {
- for (const item of funcItems) {
- const [func, value] = item || [];
- const propName = MozDocMapper.FROM_CSS[func];
- if (propName) {
- const props = section[propName] || (section[propName] = []);
- if (Array.isArray(value)) props.push(...value);
- else props.push(value);
- }
- }
- return section;
- },
- /**
- * @param {StyleObj} style
- * @returns {string}
- */
- styleToCss(style) {
- const res = [];
- for (const section of style.sections) {
- const funcs = [];
- MozDocMapper.forEachProp(section, (type, value) =>
- funcs.push(`${type}("${value.replace(/[\\"]/g, '\\$&')}")`));
- res.push(funcs.length
- ? `@-moz-document ${funcs.join(', ')} {\n${section.code}\n}`
- : section.code);
- }
- return res.join('\n\n');
- },
- };
- function styleCodeEmpty(code) {
- if (!code) {
- return true;
- }
- let lastIndex = 0;
- const rx = /\s+|\/\*([^*]|\*(?!\/))*(\*\/|$)|@namespace[^;]+;|@charset[^;]+;/giyu;
- while (rx.exec(code)) {
- lastIndex = rx.lastIndex;
- if (lastIndex === code.length) {
- return true;
- }
- }
- styleCodeEmpty.lastIndex = lastIndex;
- return false;
- }
- /** Checks if section is global i.e. has no targets at all */
- function styleSectionGlobal(section) {
- return (!section.regexps || !section.regexps.length) &&
- (!section.urlPrefixes || !section.urlPrefixes.length) &&
- (!section.urls || !section.urls.length) &&
- (!section.domains || !section.domains.length);
- }
- /**
- * 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}) {
- 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));
- }
- 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);
- }
- function arrayMirrors(a, b) {
- return a.length === b.length &&
- a.every(el => b.includes(el)) &&
- b.every(el => a.includes(el));
- }
- }
- async function calcStyleDigest(style) {
- // retain known properties in an arbitrarily predefined order
- const src = style.usercssData
- ? style.sourceCode
- // retain known properties in an arbitrarily predefined order
- : JSON.stringify((style.sections || []).map(section => /** @namespace StyleSection */({
- code: section.code || '',
- urls: section.urls || [],
- urlPrefixes: section.urlPrefixes || [],
- domains: section.domains || [],
- regexps: section.regexps || [],
- })));
- const srcBytes = new TextEncoder().encode(src);
- const res = await crypto.subtle.digest('SHA-1', srcBytes);
- return Array.from(new Uint8Array(res), b => (0x100 + b).toString(16).slice(1)).join('');
- }
- function styleJSONseemsValid(json) {
- return json
- && typeof json.name == 'string'
- && json.name.trim()
- && Array.isArray(json.sections)
- && typeof (json.sections[0] || {}).code === 'string';
- }
|