aboutsummaryrefslogtreecommitdiff
path: root/ext/js/background/profile-conditions-util.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js/background/profile-conditions-util.js')
-rw-r--r--ext/js/background/profile-conditions-util.js662
1 files changed, 326 insertions, 336 deletions
diff --git a/ext/js/background/profile-conditions-util.js b/ext/js/background/profile-conditions-util.js
index f3be226d..e2d58725 100644
--- a/ext/js/background/profile-conditions-util.js
+++ b/ext/js/background/profile-conditions-util.js
@@ -18,379 +18,369 @@
import {JsonSchema} from '../data/json-schema.js';
+/** @type {RegExp} */
+const splitPattern = /[,;\s]+/;
+/** @type {Map<string, {operators: Map<string, import('profile-conditions-util').CreateSchemaFunction>}>} */
+const descriptors = new Map([
+ [
+ 'popupLevel',
+ {
+ operators: new Map(/** @type {import('profile-conditions-util').OperatorMapArray} */ ([
+ ['equal', createSchemaPopupLevelEqual.bind(this)],
+ ['notEqual', createSchemaPopupLevelNotEqual.bind(this)],
+ ['lessThan', createSchemaPopupLevelLessThan.bind(this)],
+ ['greaterThan', createSchemaPopupLevelGreaterThan.bind(this)],
+ ['lessThanOrEqual', createSchemaPopupLevelLessThanOrEqual.bind(this)],
+ ['greaterThanOrEqual', createSchemaPopupLevelGreaterThanOrEqual.bind(this)]
+ ]))
+ }
+ ],
+ [
+ 'url',
+ {
+ operators: new Map(/** @type {import('profile-conditions-util').OperatorMapArray} */ ([
+ ['matchDomain', createSchemaUrlMatchDomain.bind(this)],
+ ['matchRegExp', createSchemaUrlMatchRegExp.bind(this)]
+ ]))
+ }
+ ],
+ [
+ 'modifierKeys',
+ {
+ operators: new Map(/** @type {import('profile-conditions-util').OperatorMapArray} */ ([
+ ['are', createSchemaModifierKeysAre.bind(this)],
+ ['areNot', createSchemaModifierKeysAreNot.bind(this)],
+ ['include', createSchemaModifierKeysInclude.bind(this)],
+ ['notInclude', createSchemaModifierKeysNotInclude.bind(this)]
+ ]))
+ }
+ ],
+ [
+ 'flags',
+ {
+ operators: new Map(/** @type {import('profile-conditions-util').OperatorMapArray} */ ([
+ ['are', createSchemaFlagsAre.bind(this)],
+ ['areNot', createSchemaFlagsAreNot.bind(this)],
+ ['include', createSchemaFlagsInclude.bind(this)],
+ ['notInclude', createSchemaFlagsNotInclude.bind(this)]
+ ]))
+ }
+ ]
+]);
+
/**
- * Utility class to help processing profile conditions.
+ * Creates a new JSON schema descriptor for the given set of condition groups.
+ * @param {import('settings').ProfileConditionGroup[]} conditionGroups An array of condition groups.
+ * For a profile match, all of the items must return successfully in at least one of the groups.
+ * @returns {JsonSchema} A new `JsonSchema` object.
*/
-export class ProfileConditionsUtil {
- /**
- * Creates a new instance.
- */
- constructor() {
- /** @type {RegExp} */
- this._splitPattern = /[,;\s]+/;
- /** @type {Map<string, {operators: Map<string, import('profile-conditions-util').CreateSchemaFunction>}>} */
- this._descriptors = new Map([
- [
- 'popupLevel',
- {
- operators: new Map(/** @type {import('profile-conditions-util').OperatorMapArray} */ ([
- ['equal', this._createSchemaPopupLevelEqual.bind(this)],
- ['notEqual', this._createSchemaPopupLevelNotEqual.bind(this)],
- ['lessThan', this._createSchemaPopupLevelLessThan.bind(this)],
- ['greaterThan', this._createSchemaPopupLevelGreaterThan.bind(this)],
- ['lessThanOrEqual', this._createSchemaPopupLevelLessThanOrEqual.bind(this)],
- ['greaterThanOrEqual', this._createSchemaPopupLevelGreaterThanOrEqual.bind(this)]
- ]))
- }
- ],
- [
- 'url',
- {
- operators: new Map(/** @type {import('profile-conditions-util').OperatorMapArray} */ ([
- ['matchDomain', this._createSchemaUrlMatchDomain.bind(this)],
- ['matchRegExp', this._createSchemaUrlMatchRegExp.bind(this)]
- ]))
- }
- ],
- [
- 'modifierKeys',
- {
- operators: new Map(/** @type {import('profile-conditions-util').OperatorMapArray} */ ([
- ['are', this._createSchemaModifierKeysAre.bind(this)],
- ['areNot', this._createSchemaModifierKeysAreNot.bind(this)],
- ['include', this._createSchemaModifierKeysInclude.bind(this)],
- ['notInclude', this._createSchemaModifierKeysNotInclude.bind(this)]
- ]))
- }
- ],
- [
- 'flags',
- {
- operators: new Map(/** @type {import('profile-conditions-util').OperatorMapArray} */ ([
- ['are', this._createSchemaFlagsAre.bind(this)],
- ['areNot', this._createSchemaFlagsAreNot.bind(this)],
- ['include', this._createSchemaFlagsInclude.bind(this)],
- ['notInclude', this._createSchemaFlagsNotInclude.bind(this)]
- ]))
- }
- ]
- ]);
- }
+export function createSchema(conditionGroups) {
+ const anyOf = [];
+ for (const {conditions} of conditionGroups) {
+ const allOf = [];
+ for (const {type, operator, value} of conditions) {
+ const conditionDescriptor = descriptors.get(type);
+ if (typeof conditionDescriptor === 'undefined') { continue; }
- /**
- * Creates a new JSON schema descriptor for the given set of condition groups.
- * @param {import('settings').ProfileConditionGroup[]} conditionGroups An array of condition groups.
- * For a profile match, all of the items must return successfully in at least one of the groups.
- * @returns {JsonSchema} A new `JsonSchema` object.
- */
- createSchema(conditionGroups) {
- const anyOf = [];
- for (const {conditions} of conditionGroups) {
- const allOf = [];
- for (const {type, operator, value} of conditions) {
- const conditionDescriptor = this._descriptors.get(type);
- if (typeof conditionDescriptor === 'undefined') { continue; }
+ const createSchema2 = conditionDescriptor.operators.get(operator);
+ if (typeof createSchema2 === 'undefined') { continue; }
- const createSchema = conditionDescriptor.operators.get(operator);
- if (typeof createSchema === 'undefined') { continue; }
-
- const schema = createSchema(value);
- allOf.push(schema);
- }
- switch (allOf.length) {
- case 0: break;
- case 1: anyOf.push(allOf[0]); break;
- default: anyOf.push({allOf}); break;
- }
+ const schema = createSchema2(value);
+ allOf.push(schema);
}
- let schema;
- switch (anyOf.length) {
- case 0: schema = {}; break;
- case 1: schema = anyOf[0]; break;
- default: schema = {anyOf}; break;
+ switch (allOf.length) {
+ case 0: break;
+ case 1: anyOf.push(allOf[0]); break;
+ default: anyOf.push({allOf}); break;
}
- return new JsonSchema(schema);
}
+ let schema;
+ switch (anyOf.length) {
+ case 0: schema = {}; break;
+ case 1: schema = anyOf[0]; break;
+ default: schema = {anyOf}; break;
+ }
+ return new JsonSchema(schema);
+}
- /**
- * Creates a normalized version of the context object to test,
- * assigning dependent fields as needed.
- * @param {import('settings').OptionsContext} context A context object which is used during schema validation.
- * @returns {import('profile-conditions-util').NormalizedOptionsContext} A normalized context object.
- */
- normalizeContext(context) {
- const normalizedContext = /** @type {import('profile-conditions-util').NormalizedOptionsContext} */ (Object.assign({}, context));
- const {url} = normalizedContext;
- if (typeof url === 'string') {
- try {
- normalizedContext.domain = new URL(url).hostname;
- } catch (e) {
- // NOP
- }
- }
- const {flags} = normalizedContext;
- if (!Array.isArray(flags)) {
- normalizedContext.flags = [];
+/**
+ * Creates a normalized version of the context object to test,
+ * assigning dependent fields as needed.
+ * @param {import('settings').OptionsContext} context A context object which is used during schema validation.
+ * @returns {import('profile-conditions-util').NormalizedOptionsContext} A normalized context object.
+ */
+export function normalizeContext(context) {
+ const normalizedContext = /** @type {import('profile-conditions-util').NormalizedOptionsContext} */ (Object.assign({}, context));
+ const {url} = normalizedContext;
+ if (typeof url === 'string') {
+ try {
+ normalizedContext.domain = new URL(url).hostname;
+ } catch (e) {
+ // NOP
}
- return normalizedContext;
}
-
- // Private
-
- /**
- * @param {string} value
- * @returns {string[]}
- */
- _split(value) {
- return value.split(this._splitPattern);
+ const {flags} = normalizedContext;
+ if (!Array.isArray(flags)) {
+ normalizedContext.flags = [];
}
+ return normalizedContext;
+}
- /**
- * @param {string} value
- * @returns {number}
- */
- _stringToNumber(value) {
- const number = Number.parseFloat(value);
- return Number.isFinite(number) ? number : 0;
- }
+// Private
- // popupLevel schema creation functions
+/**
+ * @param {string} value
+ * @returns {string[]}
+ */
+function split(value) {
+ return value.split(splitPattern);
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaPopupLevelEqual(value) {
- const number = this._stringToNumber(value);
- return {
- required: ['depth'],
- properties: {
- depth: {const: number}
- }
- };
- }
+/**
+ * @param {string} value
+ * @returns {number}
+ */
+function stringToNumber(value) {
+ const number = Number.parseFloat(value);
+ return Number.isFinite(number) ? number : 0;
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaPopupLevelNotEqual(value) {
- return {
- not: {
- anyOf: [this._createSchemaPopupLevelEqual(value)]
- }
- };
- }
+// popupLevel schema creation functions
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaPopupLevelLessThan(value) {
- const number = this._stringToNumber(value);
- return {
- required: ['depth'],
- properties: {
- depth: {type: 'number', exclusiveMaximum: number}
- }
- };
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaPopupLevelEqual(value) {
+ const number = stringToNumber(value);
+ return {
+ required: ['depth'],
+ properties: {
+ depth: {const: number}
+ }
+ };
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaPopupLevelGreaterThan(value) {
- const number = this._stringToNumber(value);
- return {
- required: ['depth'],
- properties: {
- depth: {type: 'number', exclusiveMinimum: number}
- }
- };
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaPopupLevelNotEqual(value) {
+ return {
+ not: {
+ anyOf: [createSchemaPopupLevelEqual(value)]
+ }
+ };
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaPopupLevelLessThanOrEqual(value) {
- const number = this._stringToNumber(value);
- return {
- required: ['depth'],
- properties: {
- depth: {type: 'number', maximum: number}
- }
- };
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaPopupLevelLessThan(value) {
+ const number = stringToNumber(value);
+ return {
+ required: ['depth'],
+ properties: {
+ depth: {type: 'number', exclusiveMaximum: number}
+ }
+ };
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaPopupLevelGreaterThanOrEqual(value) {
- const number = this._stringToNumber(value);
- return {
- required: ['depth'],
- properties: {
- depth: {type: 'number', minimum: number}
- }
- };
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaPopupLevelGreaterThan(value) {
+ const number = stringToNumber(value);
+ return {
+ required: ['depth'],
+ properties: {
+ depth: {type: 'number', exclusiveMinimum: number}
+ }
+ };
+}
- // url schema creation functions
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaPopupLevelLessThanOrEqual(value) {
+ const number = stringToNumber(value);
+ return {
+ required: ['depth'],
+ properties: {
+ depth: {type: 'number', maximum: number}
+ }
+ };
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaUrlMatchDomain(value) {
- const oneOf = [];
- for (let domain of this._split(value)) {
- if (domain.length === 0) { continue; }
- domain = domain.toLowerCase();
- oneOf.push({const: domain});
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaPopupLevelGreaterThanOrEqual(value) {
+ const number = stringToNumber(value);
+ return {
+ required: ['depth'],
+ properties: {
+ depth: {type: 'number', minimum: number}
}
- return {
- required: ['domain'],
- properties: {
- domain: {oneOf}
- }
- };
- }
+ };
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaUrlMatchRegExp(value) {
- return {
- required: ['url'],
- properties: {
- url: {type: 'string', pattern: value, patternFlags: 'i'}
- }
- };
+// url schema creation functions
+
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaUrlMatchDomain(value) {
+ const oneOf = [];
+ for (let domain of split(value)) {
+ if (domain.length === 0) { continue; }
+ domain = domain.toLowerCase();
+ oneOf.push({const: domain});
}
+ return {
+ required: ['domain'],
+ properties: {
+ domain: {oneOf}
+ }
+ };
+}
- // modifierKeys schema creation functions
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaUrlMatchRegExp(value) {
+ return {
+ required: ['url'],
+ properties: {
+ url: {type: 'string', pattern: value, patternFlags: 'i'}
+ }
+ };
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaModifierKeysAre(value) {
- return this._createSchemaArrayCheck('modifierKeys', value, true, false);
- }
+// modifierKeys schema creation functions
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaModifierKeysAreNot(value) {
- return {
- not: {
- anyOf: [this._createSchemaArrayCheck('modifierKeys', value, true, false)]
- }
- };
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaModifierKeysAre(value) {
+ return createSchemaArrayCheck('modifierKeys', value, true, false);
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaModifierKeysInclude(value) {
- return this._createSchemaArrayCheck('modifierKeys', value, false, false);
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaModifierKeysAreNot(value) {
+ return {
+ not: {
+ anyOf: [createSchemaArrayCheck('modifierKeys', value, true, false)]
+ }
+ };
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaModifierKeysNotInclude(value) {
- return this._createSchemaArrayCheck('modifierKeys', value, false, true);
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaModifierKeysInclude(value) {
+ return createSchemaArrayCheck('modifierKeys', value, false, false);
+}
- // modifierKeys schema creation functions
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaModifierKeysNotInclude(value) {
+ return createSchemaArrayCheck('modifierKeys', value, false, true);
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaFlagsAre(value) {
- return this._createSchemaArrayCheck('flags', value, true, false);
- }
+// modifierKeys schema creation functions
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaFlagsAreNot(value) {
- return {
- not: {
- anyOf: [this._createSchemaArrayCheck('flags', value, true, false)]
- }
- };
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaFlagsAre(value) {
+ return createSchemaArrayCheck('flags', value, true, false);
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaFlagsInclude(value) {
- return this._createSchemaArrayCheck('flags', value, false, false);
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaFlagsAreNot(value) {
+ return {
+ not: {
+ anyOf: [createSchemaArrayCheck('flags', value, true, false)]
+ }
+ };
+}
- /**
- * @param {string} value
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaFlagsNotInclude(value) {
- return this._createSchemaArrayCheck('flags', value, false, true);
- }
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaFlagsInclude(value) {
+ return createSchemaArrayCheck('flags', value, false, false);
+}
- // Generic
+/**
+ * @param {string} value
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaFlagsNotInclude(value) {
+ return createSchemaArrayCheck('flags', value, false, true);
+}
- /**
- * @param {string} key
- * @param {string} value
- * @param {boolean} exact
- * @param {boolean} none
- * @returns {import('ext/json-schema').Schema}
- */
- _createSchemaArrayCheck(key, value, exact, none) {
- /** @type {import('ext/json-schema').Schema[]} */
- const containsList = [];
- for (const item of this._split(value)) {
- if (item.length === 0) { continue; }
- containsList.push({
- contains: {
- const: item
- }
- });
- }
- const containsListCount = containsList.length;
- /** @type {import('ext/json-schema').Schema} */
- const schema = {
- type: 'array'
- };
- if (exact) {
- schema.maxItems = containsListCount;
- }
- if (none) {
- if (containsListCount > 0) {
- schema.not = {anyOf: containsList};
- }
- } else {
- schema.minItems = containsListCount;
- if (containsListCount > 0) {
- schema.allOf = containsList;
+// Generic
+
+/**
+ * @param {string} key
+ * @param {string} value
+ * @param {boolean} exact
+ * @param {boolean} none
+ * @returns {import('ext/json-schema').Schema}
+ */
+function createSchemaArrayCheck(key, value, exact, none) {
+ /** @type {import('ext/json-schema').Schema[]} */
+ const containsList = [];
+ for (const item of split(value)) {
+ if (item.length === 0) { continue; }
+ containsList.push({
+ contains: {
+ const: item
}
+ });
+ }
+ const containsListCount = containsList.length;
+ /** @type {import('ext/json-schema').Schema} */
+ const schema = {
+ type: 'array'
+ };
+ if (exact) {
+ schema.maxItems = containsListCount;
+ }
+ if (none) {
+ if (containsListCount > 0) {
+ schema.not = {anyOf: containsList};
+ }
+ } else {
+ schema.minItems = containsListCount;
+ if (containsListCount > 0) {
+ schema.allOf = containsList;
}
- return {
- required: [key],
- properties: {
- [key]: schema
- }
- };
}
+ return {
+ required: [key],
+ properties: {
+ [key]: schema
+ }
+ };
}