@adguard/agtree 1.1.5 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/agtree.cjs +1149 -489
- package/dist/agtree.d.ts +169 -42
- package/dist/agtree.esm.js +1149 -491
- package/dist/agtree.iife.min.js +5 -5
- package/dist/agtree.umd.min.js +5 -5
- package/dist/build.txt +1 -1
- package/package.json +3 -2
package/dist/agtree.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v1.1.
|
|
2
|
+
* AGTree v1.1.7 (build date: Tue, 07 Nov 2023 14:17:46 GMT)
|
|
3
3
|
* (c) 2023 AdGuard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import valid from 'semver/functions/valid.js';
|
|
8
8
|
import coerce from 'semver/functions/coerce.js';
|
|
9
9
|
import JSON5 from 'json5';
|
|
10
|
-
import { walk, parse, toPlainObject, find, generate,
|
|
10
|
+
import { walk, parse, toPlainObject, find, generate, List, fromPlainObject } from '@adguard/ecss-tree';
|
|
11
11
|
import * as ecssTree from '@adguard/ecss-tree';
|
|
12
12
|
export { ecssTree as ECSSTree };
|
|
13
13
|
import cloneDeep from 'clone-deep';
|
|
@@ -262,7 +262,7 @@ const NEGATION_MARKER = '~';
|
|
|
262
262
|
/**
|
|
263
263
|
* The wildcard symbol — `*`.
|
|
264
264
|
*/
|
|
265
|
-
const WILDCARD
|
|
265
|
+
const WILDCARD = ASTERISK;
|
|
266
266
|
/**
|
|
267
267
|
* Classic domain separator.
|
|
268
268
|
*
|
|
@@ -2861,7 +2861,7 @@ class ModifierParser {
|
|
|
2861
2861
|
const modifierEnd = Math.max(StringUtils.skipWSBack(raw) + 1, modifierNameStart);
|
|
2862
2862
|
// Modifier name can't be empty
|
|
2863
2863
|
if (modifierNameStart === modifierEnd) {
|
|
2864
|
-
throw new AdblockSyntaxError('Modifier name
|
|
2864
|
+
throw new AdblockSyntaxError('Modifier name cannot be empty', locRange(loc, 0, raw.length));
|
|
2865
2865
|
}
|
|
2866
2866
|
let modifier;
|
|
2867
2867
|
let value;
|
|
@@ -2885,7 +2885,7 @@ class ModifierParser {
|
|
|
2885
2885
|
};
|
|
2886
2886
|
// Value can't be empty
|
|
2887
2887
|
if (assignmentIndex + 1 === modifierEnd) {
|
|
2888
|
-
throw new AdblockSyntaxError('Modifier value
|
|
2888
|
+
throw new AdblockSyntaxError('Modifier value cannot be empty', locRange(loc, 0, raw.length));
|
|
2889
2889
|
}
|
|
2890
2890
|
// Skip whitespace after the assignment operator
|
|
2891
2891
|
const valueStart = StringUtils.skipWS(raw, assignmentIndex + MODIFIER_ASSIGN_OPERATOR.length);
|
|
@@ -3183,8 +3183,29 @@ const FORBIDDEN_CSS_FUNCTIONS = new Set([
|
|
|
3183
3183
|
'url',
|
|
3184
3184
|
]);
|
|
3185
3185
|
|
|
3186
|
+
/**
|
|
3187
|
+
* @file Clone related utilities
|
|
3188
|
+
*
|
|
3189
|
+
* We should keep clone related functions in this file. Thus, we just provide
|
|
3190
|
+
* a simple interface for cloning values, we use it across the AGTree project,
|
|
3191
|
+
* and the implementation "under the hood" can be improved later, if needed.
|
|
3192
|
+
*/
|
|
3193
|
+
/**
|
|
3194
|
+
* Clones an input value to avoid side effects. Use it only in justified cases,
|
|
3195
|
+
* because it can impact performance negatively.
|
|
3196
|
+
*
|
|
3197
|
+
* @param value Value to clone
|
|
3198
|
+
* @returns Cloned value
|
|
3199
|
+
*/
|
|
3200
|
+
function clone(value) {
|
|
3201
|
+
// TODO: Replace cloneDeep with a more efficient implementation
|
|
3202
|
+
return cloneDeep(value);
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3186
3205
|
/**
|
|
3187
3206
|
* @file Additional / helper functions for ECSSTree / CSSTree.
|
|
3207
|
+
*
|
|
3208
|
+
* @note There are no tests for some functions, but during the AGTree optimization we remove them anyway.
|
|
3188
3209
|
*/
|
|
3189
3210
|
/**
|
|
3190
3211
|
* Common CSSTree parsing options.
|
|
@@ -3320,10 +3341,10 @@ class CssTree {
|
|
|
3320
3341
|
ast = CssTree.parse(selectorList, CssTreeParserContext.selectorList);
|
|
3321
3342
|
}
|
|
3322
3343
|
else {
|
|
3323
|
-
ast =
|
|
3344
|
+
ast = clone(selectorList);
|
|
3324
3345
|
}
|
|
3325
3346
|
const nodes = [];
|
|
3326
|
-
// TODO:
|
|
3347
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3327
3348
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3328
3349
|
walk(ast, (node) => {
|
|
3329
3350
|
if (CssTree.isExtendedCssNode(node, pseudoClasses, attributeSelectors)) {
|
|
@@ -3352,9 +3373,9 @@ class CssTree {
|
|
|
3352
3373
|
ast = CssTree.parse(selectorList, CssTreeParserContext.selectorList);
|
|
3353
3374
|
}
|
|
3354
3375
|
else {
|
|
3355
|
-
ast =
|
|
3376
|
+
ast = selectorList;
|
|
3356
3377
|
}
|
|
3357
|
-
// TODO:
|
|
3378
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3358
3379
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3359
3380
|
return find(ast, (node) => CssTree.isExtendedCssNode(node, pseudoClasses, attributeSelectors)) !== null;
|
|
3360
3381
|
}
|
|
@@ -3391,14 +3412,14 @@ class CssTree {
|
|
|
3391
3412
|
ast = CssTree.parse(declarationList, CssTreeParserContext.declarationList);
|
|
3392
3413
|
}
|
|
3393
3414
|
else {
|
|
3394
|
-
ast =
|
|
3415
|
+
ast = clone(declarationList);
|
|
3395
3416
|
}
|
|
3396
3417
|
const nodes = [];
|
|
3397
3418
|
// While walking the AST we should skip the nested functions,
|
|
3398
3419
|
// for example skip url()s in cross-fade(url(), url()), since
|
|
3399
3420
|
// cross-fade() itself is already a forbidden function
|
|
3400
3421
|
let inForbiddenFunction = false;
|
|
3401
|
-
// TODO:
|
|
3422
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3402
3423
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3403
3424
|
walk(ast, {
|
|
3404
3425
|
enter: (node) => {
|
|
@@ -3436,9 +3457,9 @@ class CssTree {
|
|
|
3436
3457
|
ast = CssTree.parse(declarationList, CssTreeParserContext.declarationList);
|
|
3437
3458
|
}
|
|
3438
3459
|
else {
|
|
3439
|
-
ast =
|
|
3460
|
+
ast = clone(declarationList);
|
|
3440
3461
|
}
|
|
3441
|
-
// TODO:
|
|
3462
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3442
3463
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3443
3464
|
return find(ast, (node) => CssTree.isForbiddenFunction(node, forbiddenFunctions)) !== null;
|
|
3444
3465
|
}
|
|
@@ -3670,6 +3691,180 @@ class CssTree {
|
|
|
3670
3691
|
});
|
|
3671
3692
|
return result.trim();
|
|
3672
3693
|
}
|
|
3694
|
+
/**
|
|
3695
|
+
* Generates string representation of the selector list.
|
|
3696
|
+
*
|
|
3697
|
+
* @param ast SelectorList AST
|
|
3698
|
+
* @returns String representation of the selector list
|
|
3699
|
+
*/
|
|
3700
|
+
static generateSelectorListPlain(ast) {
|
|
3701
|
+
const result = [];
|
|
3702
|
+
if (!ast.children || ast.children.length === 0) {
|
|
3703
|
+
throw new Error('Selector list cannot be empty');
|
|
3704
|
+
}
|
|
3705
|
+
ast.children.forEach((selector, index, nodeList) => {
|
|
3706
|
+
if (selector.type !== CssTreeNodeType.Selector) {
|
|
3707
|
+
throw new Error(`Unexpected node type: ${selector.type}`);
|
|
3708
|
+
}
|
|
3709
|
+
result.push(this.generateSelectorPlain(selector));
|
|
3710
|
+
// If there is a next node, add a comma and a space after the selector
|
|
3711
|
+
if (nodeList[index + 1]) {
|
|
3712
|
+
result.push(COMMA, SPACE);
|
|
3713
|
+
}
|
|
3714
|
+
});
|
|
3715
|
+
return result.join(EMPTY);
|
|
3716
|
+
}
|
|
3717
|
+
/**
|
|
3718
|
+
* Selector generation based on CSSTree's AST. This is necessary because CSSTree
|
|
3719
|
+
* only adds spaces in some edge cases.
|
|
3720
|
+
*
|
|
3721
|
+
* @param ast CSS Tree AST
|
|
3722
|
+
* @returns CSS selector as string
|
|
3723
|
+
*/
|
|
3724
|
+
static generateSelectorPlain(ast) {
|
|
3725
|
+
let result = EMPTY;
|
|
3726
|
+
let inAttributeSelector = false;
|
|
3727
|
+
let depth = 0;
|
|
3728
|
+
let selectorListDepth = -1;
|
|
3729
|
+
let prevNode = ast;
|
|
3730
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3731
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3732
|
+
walk(ast, {
|
|
3733
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3734
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3735
|
+
enter: (node) => {
|
|
3736
|
+
depth += 1;
|
|
3737
|
+
// Skip attribute selector / selector list children
|
|
3738
|
+
if (inAttributeSelector || selectorListDepth > -1) {
|
|
3739
|
+
return;
|
|
3740
|
+
}
|
|
3741
|
+
switch (node.type) {
|
|
3742
|
+
// "Trivial" nodes
|
|
3743
|
+
case CssTreeNodeType.TypeSelector:
|
|
3744
|
+
result += node.name;
|
|
3745
|
+
break;
|
|
3746
|
+
case CssTreeNodeType.ClassSelector:
|
|
3747
|
+
result += DOT;
|
|
3748
|
+
result += node.name;
|
|
3749
|
+
break;
|
|
3750
|
+
case CssTreeNodeType.IdSelector:
|
|
3751
|
+
result += HASHMARK;
|
|
3752
|
+
result += node.name;
|
|
3753
|
+
break;
|
|
3754
|
+
case CssTreeNodeType.Identifier:
|
|
3755
|
+
result += node.name;
|
|
3756
|
+
break;
|
|
3757
|
+
case CssTreeNodeType.Raw:
|
|
3758
|
+
result += node.value;
|
|
3759
|
+
break;
|
|
3760
|
+
// "Advanced" nodes
|
|
3761
|
+
case CssTreeNodeType.Nth:
|
|
3762
|
+
// Default generation enough
|
|
3763
|
+
result += generate(node);
|
|
3764
|
+
break;
|
|
3765
|
+
// For example :not([id], [name])
|
|
3766
|
+
case CssTreeNodeType.SelectorList:
|
|
3767
|
+
// eslint-disable-next-line no-case-declarations
|
|
3768
|
+
const selectors = [];
|
|
3769
|
+
node.children.forEach((selector) => {
|
|
3770
|
+
if (selector.type === CssTreeNodeType.Selector) {
|
|
3771
|
+
selectors.push(CssTree.generateSelectorPlain(selector));
|
|
3772
|
+
}
|
|
3773
|
+
else if (selector.type === CssTreeNodeType.Raw) {
|
|
3774
|
+
selectors.push(selector.value);
|
|
3775
|
+
}
|
|
3776
|
+
});
|
|
3777
|
+
// Join selector lists
|
|
3778
|
+
result += selectors.join(COMMA + SPACE);
|
|
3779
|
+
// Skip nodes here
|
|
3780
|
+
selectorListDepth = depth;
|
|
3781
|
+
break;
|
|
3782
|
+
case CssTreeNodeType.Combinator:
|
|
3783
|
+
if (node.name === SPACE) {
|
|
3784
|
+
result += node.name;
|
|
3785
|
+
break;
|
|
3786
|
+
}
|
|
3787
|
+
// Prevent this case (unnecessary space): has( > .something)
|
|
3788
|
+
if (prevNode.type !== CssTreeNodeType.Selector) {
|
|
3789
|
+
result += SPACE;
|
|
3790
|
+
}
|
|
3791
|
+
result += node.name;
|
|
3792
|
+
result += SPACE;
|
|
3793
|
+
break;
|
|
3794
|
+
case CssTreeNodeType.AttributeSelector:
|
|
3795
|
+
result += OPEN_SQUARE_BRACKET;
|
|
3796
|
+
// Identifier name
|
|
3797
|
+
if (node.name) {
|
|
3798
|
+
result += node.name.name;
|
|
3799
|
+
}
|
|
3800
|
+
// Matcher operator, eg =
|
|
3801
|
+
if (node.matcher) {
|
|
3802
|
+
result += node.matcher;
|
|
3803
|
+
// Value can be String, Identifier or null
|
|
3804
|
+
if (node.value !== null) {
|
|
3805
|
+
// String node
|
|
3806
|
+
if (node.value.type === CssTreeNodeType.String) {
|
|
3807
|
+
result += generate(node.value);
|
|
3808
|
+
}
|
|
3809
|
+
else if (node.value.type === CssTreeNodeType.Identifier) {
|
|
3810
|
+
// Identifier node
|
|
3811
|
+
result += node.value.name;
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
// Flags
|
|
3816
|
+
if (node.flags) {
|
|
3817
|
+
// Space before flags
|
|
3818
|
+
result += SPACE;
|
|
3819
|
+
result += node.flags;
|
|
3820
|
+
}
|
|
3821
|
+
result += CLOSE_SQUARE_BRACKET;
|
|
3822
|
+
inAttributeSelector = true;
|
|
3823
|
+
break;
|
|
3824
|
+
case CssTreeNodeType.PseudoElementSelector:
|
|
3825
|
+
result += COLON;
|
|
3826
|
+
result += COLON;
|
|
3827
|
+
result += node.name;
|
|
3828
|
+
if (node.children !== null) {
|
|
3829
|
+
result += OPEN_PARENTHESIS;
|
|
3830
|
+
}
|
|
3831
|
+
break;
|
|
3832
|
+
case CssTreeNodeType.PseudoClassSelector:
|
|
3833
|
+
result += COLON;
|
|
3834
|
+
result += node.name;
|
|
3835
|
+
if (node.children !== null) {
|
|
3836
|
+
result += OPEN_PARENTHESIS;
|
|
3837
|
+
}
|
|
3838
|
+
break;
|
|
3839
|
+
}
|
|
3840
|
+
prevNode = node;
|
|
3841
|
+
},
|
|
3842
|
+
leave: (node) => {
|
|
3843
|
+
depth -= 1;
|
|
3844
|
+
if (node.type === CssTreeNodeType.SelectorList && depth + 1 === selectorListDepth) {
|
|
3845
|
+
selectorListDepth = -1;
|
|
3846
|
+
}
|
|
3847
|
+
if (selectorListDepth > -1) {
|
|
3848
|
+
return;
|
|
3849
|
+
}
|
|
3850
|
+
if (node.type === CssTreeNodeType.AttributeSelector) {
|
|
3851
|
+
inAttributeSelector = false;
|
|
3852
|
+
}
|
|
3853
|
+
if (inAttributeSelector) {
|
|
3854
|
+
return;
|
|
3855
|
+
}
|
|
3856
|
+
switch (node.type) {
|
|
3857
|
+
case CssTreeNodeType.PseudoElementSelector:
|
|
3858
|
+
case CssTreeNodeType.PseudoClassSelector:
|
|
3859
|
+
if (node.children) {
|
|
3860
|
+
result += CLOSE_PARENTHESIS;
|
|
3861
|
+
}
|
|
3862
|
+
break;
|
|
3863
|
+
}
|
|
3864
|
+
},
|
|
3865
|
+
});
|
|
3866
|
+
return result.trim();
|
|
3867
|
+
}
|
|
3673
3868
|
/**
|
|
3674
3869
|
* Block generation based on CSSTree's AST. This is necessary because CSSTree only adds spaces in some edge cases.
|
|
3675
3870
|
*
|
|
@@ -3853,6 +4048,29 @@ class CssTree {
|
|
|
3853
4048
|
});
|
|
3854
4049
|
return result;
|
|
3855
4050
|
}
|
|
4051
|
+
/**
|
|
4052
|
+
* Helper function to generate a raw string from a function selector's children
|
|
4053
|
+
*
|
|
4054
|
+
* @param node Function node
|
|
4055
|
+
* @returns Generated function value
|
|
4056
|
+
* @example `responseheader(name)` -> `name`
|
|
4057
|
+
*/
|
|
4058
|
+
static generateFunctionPlainValue(node) {
|
|
4059
|
+
const result = [];
|
|
4060
|
+
node.children?.forEach((child) => {
|
|
4061
|
+
switch (child.type) {
|
|
4062
|
+
case CssTreeNodeType.Raw:
|
|
4063
|
+
result.push(child.value);
|
|
4064
|
+
break;
|
|
4065
|
+
default:
|
|
4066
|
+
// Fallback to CSSTree's default generate function
|
|
4067
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
4068
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4069
|
+
result.push(generate(child));
|
|
4070
|
+
}
|
|
4071
|
+
});
|
|
4072
|
+
return result.join(EMPTY);
|
|
4073
|
+
}
|
|
3856
4074
|
}
|
|
3857
4075
|
|
|
3858
4076
|
/**
|
|
@@ -3900,7 +4118,7 @@ class ElementHidingBodyParser {
|
|
|
3900
4118
|
* @throws If the AST is invalid
|
|
3901
4119
|
*/
|
|
3902
4120
|
static generate(ast) {
|
|
3903
|
-
return CssTree.
|
|
4121
|
+
return CssTree.generateSelectorListPlain(ast.selectorList);
|
|
3904
4122
|
}
|
|
3905
4123
|
}
|
|
3906
4124
|
|
|
@@ -4144,7 +4362,7 @@ class CssInjectionBodyParser {
|
|
|
4144
4362
|
if (mediaQueryList || declarationList || remove) {
|
|
4145
4363
|
throw new AdblockSyntaxError(
|
|
4146
4364
|
// eslint-disable-next-line max-len
|
|
4147
|
-
'Invalid selector, regular selector elements
|
|
4365
|
+
'Invalid selector, regular selector elements cannot be used after special pseudo-classes', {
|
|
4148
4366
|
start: node.loc?.start ?? loc,
|
|
4149
4367
|
end: shiftLoc(loc, raw.length),
|
|
4150
4368
|
});
|
|
@@ -4833,7 +5051,7 @@ function createModifierListNode(modifiers = []) {
|
|
|
4833
5051
|
const result = {
|
|
4834
5052
|
type: 'ModifierList',
|
|
4835
5053
|
// We need to clone the modifiers to avoid side effects
|
|
4836
|
-
children:
|
|
5054
|
+
children: modifiers.length ? clone(modifiers) : [],
|
|
4837
5055
|
};
|
|
4838
5056
|
return result;
|
|
4839
5057
|
}
|
|
@@ -4873,8 +5091,9 @@ function hasUboModifierIndicator(rawSelectorList) {
|
|
|
4873
5091
|
* @returns Linked list based selector
|
|
4874
5092
|
*/
|
|
4875
5093
|
function convertSelectorToLinkedList(selector) {
|
|
5094
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
4876
5095
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4877
|
-
return fromPlainObject(
|
|
5096
|
+
return fromPlainObject(clone(selector));
|
|
4878
5097
|
}
|
|
4879
5098
|
/**
|
|
4880
5099
|
* Helper function that always returns the linked list version of the
|
|
@@ -4884,8 +5103,9 @@ function convertSelectorToLinkedList(selector) {
|
|
|
4884
5103
|
* @returns Linked list based selector list
|
|
4885
5104
|
*/
|
|
4886
5105
|
function convertSelectorListToLinkedList(selectorList) {
|
|
5106
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
4887
5107
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4888
|
-
return fromPlainObject(
|
|
5108
|
+
return fromPlainObject(clone(selectorList));
|
|
4889
5109
|
}
|
|
4890
5110
|
/**
|
|
4891
5111
|
* Helper function for checking and removing bounding combinators
|
|
@@ -5970,7 +6190,8 @@ class FilterListParser {
|
|
|
5970
6190
|
*/
|
|
5971
6191
|
static generate(ast, preferRaw = false) {
|
|
5972
6192
|
let result = EMPTY;
|
|
5973
|
-
for (
|
|
6193
|
+
for (let i = 0; i < ast.children.length; i += 1) {
|
|
6194
|
+
const rule = ast.children[i];
|
|
5974
6195
|
if (preferRaw && rule.raws?.text) {
|
|
5975
6196
|
result += rule.raws.text;
|
|
5976
6197
|
}
|
|
@@ -5987,6 +6208,11 @@ class FilterListParser {
|
|
|
5987
6208
|
case 'lf':
|
|
5988
6209
|
result += LF;
|
|
5989
6210
|
break;
|
|
6211
|
+
default:
|
|
6212
|
+
if (i !== ast.children.length - 1) {
|
|
6213
|
+
result += LF;
|
|
6214
|
+
}
|
|
6215
|
+
break;
|
|
5990
6216
|
}
|
|
5991
6217
|
}
|
|
5992
6218
|
return result;
|
|
@@ -6036,7 +6262,7 @@ class RuleConversionError extends Error {
|
|
|
6036
6262
|
}
|
|
6037
6263
|
}
|
|
6038
6264
|
|
|
6039
|
-
var data$
|
|
6265
|
+
var data$U = { adg_os_any:{ name:"all",
|
|
6040
6266
|
description:"$all modifier is made of $document, $popup, and all content-type modifiers combined.",
|
|
6041
6267
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#all-modifier",
|
|
6042
6268
|
negatable:false,
|
|
@@ -6062,14 +6288,14 @@ var data$T = { adg_os_any:{ name:"all",
|
|
|
6062
6288
|
negatable:false,
|
|
6063
6289
|
block_only:false } };
|
|
6064
6290
|
|
|
6065
|
-
var data$
|
|
6291
|
+
var data$T = { adg_os_any:{ name:"app",
|
|
6066
6292
|
description:"The `$app` modifier lets you narrow the rule coverage down to a specific application or a list of applications.\nThe modifier's behavior and syntax perfectly match the corresponding basic rules `$app` modifier.",
|
|
6067
6293
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier",
|
|
6068
6294
|
assignable:true,
|
|
6069
6295
|
negatable:false,
|
|
6070
6296
|
value_format:"pipe_separated_apps" } };
|
|
6071
6297
|
|
|
6072
|
-
var data$
|
|
6298
|
+
var data$S = { adg_os_any:{ name:"badfilter",
|
|
6073
6299
|
description:"The rules with the `$badfilter` modifier disable other basic rules to which they refer. It means that\nthe text of the disabled rule should match the text of the `$badfilter` rule (without the `$badfilter` modifier).",
|
|
6074
6300
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#badfilter-modifier",
|
|
6075
6301
|
negatable:false },
|
|
@@ -6090,19 +6316,19 @@ var data$R = { adg_os_any:{ name:"badfilter",
|
|
|
6090
6316
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#badfilter",
|
|
6091
6317
|
negatable:false } };
|
|
6092
6318
|
|
|
6093
|
-
var data$
|
|
6319
|
+
var data$R = { ubo_ext_any:{ name:"cname",
|
|
6094
6320
|
description:"When used in an exception filter,\nit will bypass blocking CNAME uncloaked requests for the current (specified) document.",
|
|
6095
6321
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#cname",
|
|
6096
6322
|
negatable:false,
|
|
6097
6323
|
exception_only:true } };
|
|
6098
6324
|
|
|
6099
|
-
var data$
|
|
6325
|
+
var data$Q = { adg_os_any:{ name:"content",
|
|
6100
6326
|
description:"Disables HTML filtering and `$replace` rules on the pages that match the rule.",
|
|
6101
6327
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#content-modifier",
|
|
6102
6328
|
negatable:false,
|
|
6103
6329
|
exception_only:true } };
|
|
6104
6330
|
|
|
6105
|
-
var data$
|
|
6331
|
+
var data$P = { adg_os_any:{ name:"cookie",
|
|
6106
6332
|
description:"The `$cookie` modifier completely changes rule behavior.\nInstead of blocking a request, this modifier makes us suppress or modify the Cookie and Set-Cookie headers.",
|
|
6107
6333
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#cookie-modifier",
|
|
6108
6334
|
assignable:true,
|
|
@@ -6117,7 +6343,7 @@ var data$O = { adg_os_any:{ name:"cookie",
|
|
|
6117
6343
|
value_optional:true,
|
|
6118
6344
|
value_format:"^([^;=\\s]*?)((?:;(maxAge=\\d+;?)?|(sameSite=(lax|none|strict);?)?){1,3})(?<!;)$" } };
|
|
6119
6345
|
|
|
6120
|
-
var data$
|
|
6346
|
+
var data$O = { adg_os_any:{ name:"csp",
|
|
6121
6347
|
description:"This modifier completely changes the rule behavior.\nIf it is applied to a rule, it will not block the matching request.\nThe response headers are going to be modified instead.",
|
|
6122
6348
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#csp-modifier",
|
|
6123
6349
|
conflicts:[ "domain",
|
|
@@ -6164,7 +6390,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6164
6390
|
value_optional:true,
|
|
6165
6391
|
value_format:"csp_value" } };
|
|
6166
6392
|
|
|
6167
|
-
var data$
|
|
6393
|
+
var data$N = { adg_os_any:{ name:"denyallow",
|
|
6168
6394
|
description:"The `$denyallow` modifier allows to avoid creating additional rules\nwhen it is needed to disable a certain rule for specific domains.\n`$denyallow` matches only target domains and not referrer domains.",
|
|
6169
6395
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier",
|
|
6170
6396
|
conflicts:[ "to" ],
|
|
@@ -6200,7 +6426,7 @@ var data$M = { adg_os_any:{ name:"denyallow",
|
|
|
6200
6426
|
negatable:false,
|
|
6201
6427
|
value_format:"pipe_separated_denyallow_domains" } };
|
|
6202
6428
|
|
|
6203
|
-
var data$
|
|
6429
|
+
var data$M = { adg_os_any:{ name:"document",
|
|
6204
6430
|
description:"The rule corresponds to the main frame document requests,\ni.e. HTML documents that are loaded in the browser tab.",
|
|
6205
6431
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#document-modifier",
|
|
6206
6432
|
negatable:false },
|
|
@@ -6226,7 +6452,7 @@ var data$L = { adg_os_any:{ name:"document",
|
|
|
6226
6452
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#document",
|
|
6227
6453
|
negatable:false } };
|
|
6228
6454
|
|
|
6229
|
-
var data$
|
|
6455
|
+
var data$L = { adg_any:{ name:"domain",
|
|
6230
6456
|
aliases:[ "from" ],
|
|
6231
6457
|
description:"The `$domain` modifier limits the rule application area to a list of domains and their subdomains.",
|
|
6232
6458
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#domain-modifier",
|
|
@@ -6247,7 +6473,7 @@ var data$K = { adg_any:{ name:"domain",
|
|
|
6247
6473
|
negatable:false,
|
|
6248
6474
|
value_format:"pipe_separated_domains" } };
|
|
6249
6475
|
|
|
6250
|
-
var data$
|
|
6476
|
+
var data$K = { adg_any:{ name:"elemhide",
|
|
6251
6477
|
aliases:[ "ehide" ],
|
|
6252
6478
|
description:"Disables any cosmetic rules on the pages matching the rule.",
|
|
6253
6479
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#elemhide-modifier",
|
|
@@ -6266,7 +6492,7 @@ var data$J = { adg_any:{ name:"elemhide",
|
|
|
6266
6492
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#elemhide-1",
|
|
6267
6493
|
description:"Disables any cosmetic rules on the pages matching the rule." } };
|
|
6268
6494
|
|
|
6269
|
-
var data$
|
|
6495
|
+
var data$J = { adg_os_any:{ name:"empty",
|
|
6270
6496
|
description:"This modifier is deprecated in favor of the $redirect modifier.\nRules with `$empty` are still supported and being converted into `$redirect=nooptext` now\nbut the support shall be removed in the future.",
|
|
6271
6497
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#empty-modifier",
|
|
6272
6498
|
deprecated:true,
|
|
@@ -6283,7 +6509,7 @@ var data$I = { adg_os_any:{ name:"empty",
|
|
|
6283
6509
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#empty",
|
|
6284
6510
|
negatable:false } };
|
|
6285
6511
|
|
|
6286
|
-
var data$
|
|
6512
|
+
var data$I = { adg_any:{ name:"first-party",
|
|
6287
6513
|
aliases:[ "1p",
|
|
6288
6514
|
"~third-party" ],
|
|
6289
6515
|
description:"A restriction of first-party requests. Equal to `~third-party`.",
|
|
@@ -6296,7 +6522,7 @@ var data$H = { adg_any:{ name:"first-party",
|
|
|
6296
6522
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#1p",
|
|
6297
6523
|
negatable:false } };
|
|
6298
6524
|
|
|
6299
|
-
var data$
|
|
6525
|
+
var data$H = { adg_os_any:{ name:"extension",
|
|
6300
6526
|
description:"Disables all userscripts on the pages matching this rule.",
|
|
6301
6527
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#extension-modifier",
|
|
6302
6528
|
conflicts:[ "domain",
|
|
@@ -6312,7 +6538,7 @@ var data$G = { adg_os_any:{ name:"extension",
|
|
|
6312
6538
|
inverse_conflicts:true,
|
|
6313
6539
|
exception_only:true } };
|
|
6314
6540
|
|
|
6315
|
-
var data$
|
|
6541
|
+
var data$G = { adg_any:{ name:"font",
|
|
6316
6542
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#font-modifier",
|
|
6317
6543
|
description:"The rule corresponds to requests for fonts, e.g. `.woff` filename extension." },
|
|
6318
6544
|
abp_any:{ name:"font",
|
|
@@ -6322,7 +6548,7 @@ var data$F = { adg_any:{ name:"font",
|
|
|
6322
6548
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293#options",
|
|
6323
6549
|
description:"The rule corresponds to requests for fonts, e.g. `.woff` filename extension." } };
|
|
6324
6550
|
|
|
6325
|
-
var data$
|
|
6551
|
+
var data$F = { adg_os_any:{ name:"genericblock",
|
|
6326
6552
|
description:"Disables generic basic rules on pages that correspond to exception rule.",
|
|
6327
6553
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#genericblock-modifier",
|
|
6328
6554
|
conflicts:[ "domain",
|
|
@@ -6378,7 +6604,7 @@ var data$E = { adg_os_any:{ name:"genericblock",
|
|
|
6378
6604
|
negatable:false,
|
|
6379
6605
|
exception_only:true } };
|
|
6380
6606
|
|
|
6381
|
-
var data$
|
|
6607
|
+
var data$E = { adg_any:{ name:"generichide",
|
|
6382
6608
|
aliases:[ "ghide" ],
|
|
6383
6609
|
description:"Disables all generic cosmetic rules.",
|
|
6384
6610
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#generichide-modifier",
|
|
@@ -6410,7 +6636,7 @@ var data$D = { adg_any:{ name:"generichide",
|
|
|
6410
6636
|
negatable:false,
|
|
6411
6637
|
exception_only:true } };
|
|
6412
6638
|
|
|
6413
|
-
var data$
|
|
6639
|
+
var data$D = { adg_os_any:{ name:"header",
|
|
6414
6640
|
description:"The `$header` modifier allows matching the HTTP response\nhaving a specific header with (optionally) a specific value.",
|
|
6415
6641
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#header-modifier",
|
|
6416
6642
|
assignable:true,
|
|
@@ -6426,7 +6652,7 @@ var data$C = { adg_os_any:{ name:"header",
|
|
|
6426
6652
|
assignable:true,
|
|
6427
6653
|
value_format:"(?xi)\n ^\n # header name\n [\\w-]+\n (\n :\n # header value: string or regexp\n (\\w+|\\/.+\\/)\n )?" } };
|
|
6428
6654
|
|
|
6429
|
-
var data$
|
|
6655
|
+
var data$C = { adg_os_any:{ name:"hls",
|
|
6430
6656
|
description:"The `$hls` rules modify the response of a matching request.\nThey are intended as a convenient way to remove segments from HLS playlists (RFC 8216).",
|
|
6431
6657
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#hls-modifier",
|
|
6432
6658
|
version_added:"CoreLibs 1.10",
|
|
@@ -6442,7 +6668,7 @@ var data$B = { adg_os_any:{ name:"hls",
|
|
|
6442
6668
|
value_optional:true,
|
|
6443
6669
|
value_format:"(?xi)\n (\n # string pattern\n \\w+\n # or regexp pattern\n |\n # TODO: improve regexp pattern to invalidate unescaped `/`, `$`, and `,`\n \\/.+\\/\n # options\n ([ti]*)?\n )" } };
|
|
6444
6670
|
|
|
6445
|
-
var data$
|
|
6671
|
+
var data$B = { adg_any:{ name:"image",
|
|
6446
6672
|
description:"The rule corresponds to images requests.",
|
|
6447
6673
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#image-modifier" },
|
|
6448
6674
|
abp_any:{ name:"image",
|
|
@@ -6452,7 +6678,7 @@ var data$A = { adg_any:{ name:"image",
|
|
|
6452
6678
|
description:"The rule corresponds to images requests.",
|
|
6453
6679
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293#options" } };
|
|
6454
6680
|
|
|
6455
|
-
var data$
|
|
6681
|
+
var data$A = { adg_any:{ name:"important",
|
|
6456
6682
|
description:"The `$important` modifier applied to a rule increases its priority\nover any other rule without `$important` modifier. Even over basic exception rules.",
|
|
6457
6683
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#important-modifier",
|
|
6458
6684
|
negatable:false },
|
|
@@ -6461,7 +6687,7 @@ var data$z = { adg_any:{ name:"important",
|
|
|
6461
6687
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#important",
|
|
6462
6688
|
negatable:false } };
|
|
6463
6689
|
|
|
6464
|
-
var data$
|
|
6690
|
+
var data$z = { adg_os_any:{ name:"inline-font",
|
|
6465
6691
|
description:"The `$inline-font` modifier is a sort of a shortcut for $csp modifier with specific value.\nE.g. `||example.org^$inline-font` is converting into:\n```adblock\n||example.org^$csp=font-src 'self' 'unsafe-eval' http: https: data: blob: mediastream: filesystem:\n```",
|
|
6466
6692
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#inline-font-modifier" },
|
|
6467
6693
|
adg_ext_any:{ name:"inline-font",
|
|
@@ -6471,7 +6697,7 @@ var data$y = { adg_os_any:{ name:"inline-font",
|
|
|
6471
6697
|
description:"The `$inline-font` modifier is a sort of a shortcut for $csp modifier with specific value.\nE.g. `||example.org^$inline-font` is converting into:\n```adblock\n||example.org^$csp=font-src 'self' 'unsafe-eval' http: https: data: blob: mediastream: filesystem:\n```",
|
|
6472
6698
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#inline-font" } };
|
|
6473
6699
|
|
|
6474
|
-
var data$
|
|
6700
|
+
var data$y = { adg_os_any:{ name:"inline-script",
|
|
6475
6701
|
description:"The `$inline-script` modifier is a sort of a shortcut for $csp modifier with specific value.\nE.g. `||example.org^$inline-script` is converting into:\n```adblock\n||example.org^$csp=script-src 'self' 'unsafe-eval' http: https: data: blob: mediastream: filesystem:\n```",
|
|
6476
6702
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#inline-script-modifier" },
|
|
6477
6703
|
adg_ext_any:{ name:"inline-script",
|
|
@@ -6481,7 +6707,7 @@ var data$x = { adg_os_any:{ name:"inline-script",
|
|
|
6481
6707
|
description:"The `$inline-script` modifier is a sort of a shortcut for $csp modifier with specific value.\nE.g. `||example.org^$inline-script` is converting into:\n```adblock\n||example.org^$csp=script-src 'self' 'unsafe-eval' http: https: data: blob: mediastream: filesystem:\n```",
|
|
6482
6708
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#inline-script" } };
|
|
6483
6709
|
|
|
6484
|
-
var data$
|
|
6710
|
+
var data$x = { adg_os_any:{ name:"jsinject",
|
|
6485
6711
|
description:"Forbids adding of javascript code to the page.",
|
|
6486
6712
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#jsinject-modifier",
|
|
6487
6713
|
conflicts:[ "domain",
|
|
@@ -6540,7 +6766,7 @@ var data$w = { adg_os_any:{ name:"jsinject",
|
|
|
6540
6766
|
negatable:false,
|
|
6541
6767
|
exception_only:true } };
|
|
6542
6768
|
|
|
6543
|
-
var data$
|
|
6769
|
+
var data$w = { adg_os_any:{ name:"jsonprune",
|
|
6544
6770
|
description:"The `$jsonprune` rules modify the response to a matching request\nby removing JSON items that match a modified JSONPath expression.\nThey do not modify responses which are not valid JSON documents.",
|
|
6545
6771
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#jsonprune-modifier",
|
|
6546
6772
|
assignable:true,
|
|
@@ -6548,7 +6774,7 @@ var data$v = { adg_os_any:{ name:"jsonprune",
|
|
|
6548
6774
|
value_optional:true,
|
|
6549
6775
|
value_format:"(?xi)\n ^\n # the expression always starts with a dollar sign (for root)\n # which should be escaped\n \\\\\n \\$\n \\.?\n # TODO: improve the expression to invalidate unescaped `$` and `,`\n .+\n $" } };
|
|
6550
6776
|
|
|
6551
|
-
var data$
|
|
6777
|
+
var data$v = { adg_any:{ name:"match-case",
|
|
6552
6778
|
description:"This modifier defines a rule which applies only to addresses that match the case.\nDefault rules are case-insensitive.",
|
|
6553
6779
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#match-case-modifier" },
|
|
6554
6780
|
abp_any:{ name:"match-case",
|
|
@@ -6558,7 +6784,7 @@ var data$u = { adg_any:{ name:"match-case",
|
|
|
6558
6784
|
description:"This modifier defines a rule which applies only to addresses that match the case.\nDefault rules are case-insensitive.",
|
|
6559
6785
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#match-case" } };
|
|
6560
6786
|
|
|
6561
|
-
var data$
|
|
6787
|
+
var data$u = { adg_any:{ name:"media",
|
|
6562
6788
|
description:"A restriction of third-party and own requests.\nA third-party request is a request from a different domain.\nFor example, a request to `example.org` from `domain.com` is a third-party request.",
|
|
6563
6789
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#media-modifier" },
|
|
6564
6790
|
abp_any:{ name:"media",
|
|
@@ -6568,7 +6794,7 @@ var data$t = { adg_any:{ name:"media",
|
|
|
6568
6794
|
description:"A restriction of third-party and own requests.\nA third-party request is a request from a different domain.\nFor example, a request to `example.org` from `domain.com` is a third-party request.",
|
|
6569
6795
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293#options" } };
|
|
6570
6796
|
|
|
6571
|
-
var data$
|
|
6797
|
+
var data$t = { adg_os_any:{ name:"method",
|
|
6572
6798
|
description:"This modifier limits the rule scope to requests that use the specified set of HTTP methods.\nNegated methods are allowed.",
|
|
6573
6799
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier",
|
|
6574
6800
|
negatable:false,
|
|
@@ -6587,7 +6813,7 @@ var data$s = { adg_os_any:{ name:"method",
|
|
|
6587
6813
|
assignable:true,
|
|
6588
6814
|
value_format:"pipe_separated_methods" } };
|
|
6589
6815
|
|
|
6590
|
-
var data$
|
|
6816
|
+
var data$s = { adg_os_any:{ name:"mp4",
|
|
6591
6817
|
description:"As a response to blocked request AdGuard returns a short video placeholder.\nRules with `$mp4` are still supported and being converted into `$redirect=noopmp4-1s` now\nbut the support shall be removed in the future.",
|
|
6592
6818
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#mp4-modifier",
|
|
6593
6819
|
deprecated:true,
|
|
@@ -6604,7 +6830,7 @@ var data$r = { adg_os_any:{ name:"mp4",
|
|
|
6604
6830
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#mp4",
|
|
6605
6831
|
negatable:false } };
|
|
6606
6832
|
|
|
6607
|
-
var data$
|
|
6833
|
+
var data$r = { adg_os_any:{ name:"network",
|
|
6608
6834
|
description:"This is basically a Firewall-kind of rules allowing to fully block\nor unblock access to a specified remote address.",
|
|
6609
6835
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#network-modifier",
|
|
6610
6836
|
conflicts:[ "app",
|
|
@@ -6612,7 +6838,7 @@ var data$q = { adg_os_any:{ name:"network",
|
|
|
6612
6838
|
inverse_conflicts:true,
|
|
6613
6839
|
negatable:false } };
|
|
6614
6840
|
|
|
6615
|
-
var data$
|
|
6841
|
+
var data$q = { adg_os_any:{ name:"_",
|
|
6616
6842
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#noop-modifier",
|
|
6617
6843
|
description:"The noop modifier does nothing and can be used solely to increase rules' readability.\nIt consists of a sequence of underscore characters (_) of any length\nand can appear in a rule as many times as needed.",
|
|
6618
6844
|
negatable:false },
|
|
@@ -6633,13 +6859,13 @@ var data$p = { adg_os_any:{ name:"_",
|
|
|
6633
6859
|
description:"The noop modifier does nothing and can be used solely to increase rules' readability.\nIt consists of a sequence of underscore characters (_) of any length\nand can appear in a rule as many times as needed.",
|
|
6634
6860
|
negatable:false } };
|
|
6635
6861
|
|
|
6636
|
-
var data$
|
|
6862
|
+
var data$p = { adg_any:{ name:"object-subrequest",
|
|
6637
6863
|
description:"The `$object-subrequest` modifier is removed and is no longer supported.\nRules with it are considered as invalid.\nThe rule corresponds to requests by browser plugins (it is usually Flash).",
|
|
6638
6864
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#object-subrequest-modifier",
|
|
6639
6865
|
removed:true,
|
|
6640
6866
|
removal_message:"The `$object-subrequest` modifier is removed and is no longer supported.\nRules with it are considered as invalid." } };
|
|
6641
6867
|
|
|
6642
|
-
var data$
|
|
6868
|
+
var data$o = { adg_any:{ name:"object",
|
|
6643
6869
|
description:"The rule corresponds to browser plugins resources, e.g. Java or Flash",
|
|
6644
6870
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#object-modifier" },
|
|
6645
6871
|
abp_any:{ name:"object",
|
|
@@ -6649,7 +6875,7 @@ var data$n = { adg_any:{ name:"object",
|
|
|
6649
6875
|
description:"The rule corresponds to browser plugins resources, e.g. Java or Flash.",
|
|
6650
6876
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#type-options" } };
|
|
6651
6877
|
|
|
6652
|
-
var data$
|
|
6878
|
+
var data$n = { adg_any:{ name:"other",
|
|
6653
6879
|
description:"The rule applies to requests for which the type has not been determined\nor does not match the types listed above.",
|
|
6654
6880
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#other-modifier" },
|
|
6655
6881
|
abp_any:{ name:"other",
|
|
@@ -6659,7 +6885,7 @@ var data$m = { adg_any:{ name:"other",
|
|
|
6659
6885
|
description:"The rule applies to requests for which the type has not been determined\nor does not match the types listed above.",
|
|
6660
6886
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#type-options" } };
|
|
6661
6887
|
|
|
6662
|
-
var data$
|
|
6888
|
+
var data$m = { adg_os_any:{ name:"permissions",
|
|
6663
6889
|
description:"For the requests matching a `$permissions` rule, ad blocker strengthens response's feature policy\nby adding additional feature policy equal to the `$permissions` modifier contents.\n`$permissions` rules are applied independently from any other rule type.",
|
|
6664
6890
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#permissions-modifier",
|
|
6665
6891
|
version_added:"CoreLibs 1.11",
|
|
@@ -6672,7 +6898,7 @@ var data$l = { adg_os_any:{ name:"permissions",
|
|
|
6672
6898
|
value_optional:true,
|
|
6673
6899
|
value_format:"permissions_value" } };
|
|
6674
6900
|
|
|
6675
|
-
var data$
|
|
6901
|
+
var data$l = { adg_any:{ name:"ping",
|
|
6676
6902
|
description:"The rule corresponds to requests caused by either navigator.sendBeacon() or the ping attribute on links.",
|
|
6677
6903
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#ping-modifier" },
|
|
6678
6904
|
abp_any:{ name:"ping",
|
|
@@ -6682,13 +6908,13 @@ var data$k = { adg_any:{ name:"ping",
|
|
|
6682
6908
|
description:"The rule corresponds to requests caused by either navigator.sendBeacon() or the ping attribute on links.",
|
|
6683
6909
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#type-options" } };
|
|
6684
6910
|
|
|
6685
|
-
var data$
|
|
6911
|
+
var data$k = { ubo_ext_any:{ name:"popunder",
|
|
6686
6912
|
description:"To block \"popunders\" windows/tabs where the original page redirects to an advertisement\nand the desired content loads in the newly created one.\nTo be used in the same manner as the popup filter option, except that it will block popunders.",
|
|
6687
6913
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#popunder",
|
|
6688
6914
|
negatable:false,
|
|
6689
6915
|
block_only:true } };
|
|
6690
6916
|
|
|
6691
|
-
var data$
|
|
6917
|
+
var data$j = { adg_any:{ name:"popup",
|
|
6692
6918
|
description:"Pages opened in a new tab or window.\nNote: Filters will not block pop-ups by default, only if the `$popup` type option is specified.",
|
|
6693
6919
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#popup-modifier",
|
|
6694
6920
|
negatable:false },
|
|
@@ -6701,7 +6927,7 @@ var data$i = { adg_any:{ name:"popup",
|
|
|
6701
6927
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#type-options",
|
|
6702
6928
|
negatable:false } };
|
|
6703
6929
|
|
|
6704
|
-
var data$
|
|
6930
|
+
var data$i = { adg_os_any:{ name:"redirect-rule",
|
|
6705
6931
|
description:"This is basically an alias to `$redirect`\nsince it has the same \"redirection\" values and the logic is almost similar.\nThe difference is that `$redirect-rule` is applied only in the case\nwhen the target request is blocked by a different basic rule.",
|
|
6706
6932
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#redirect-rule-modifier",
|
|
6707
6933
|
conflicts:[ "domain",
|
|
@@ -6792,7 +7018,7 @@ var data$h = { adg_os_any:{ name:"redirect-rule",
|
|
|
6792
7018
|
negatable:false,
|
|
6793
7019
|
value_format:"(?x)\n ^(\n 1x1\\.gif|\n 2x2\\.png|\n 3x2\\.png|\n 32x32\\.png|\n noop\\.css|\n noop\\.html|\n noopframe|\n noop\\.js|\n noop\\.txt|\n noop-0\\.1s\\.mp3|\n noop-0\\.5s\\.mp3|\n noop-1s\\.mp4|\n none|\n click2load\\.html|\n addthis_widget\\.js|\n amazon_ads\\.js|\n amazon_apstag\\.js|\n monkeybroker\\.js|\n doubleclick_instream_ad_status|\n google-analytics_ga\\.js|\n google-analytics_analytics\\.js|\n google-analytics_inpage_linkid\\.js|\n google-analytics_cx_api\\.js|\n google-ima\\.js|\n googletagservices_gpt\\.js|\n googletagmanager_gtm\\.js|\n googlesyndication_adsbygoogle\\.js|\n scorecardresearch_beacon\\.js|\n outbrain-widget\\.js|\n hd-main\\.js\n )\n (:[0-9]+)?$" } };
|
|
6794
7020
|
|
|
6795
|
-
var data$
|
|
7021
|
+
var data$h = { adg_os_any:{ name:"redirect",
|
|
6796
7022
|
description:"Used to redirect web requests to a local \"resource\".",
|
|
6797
7023
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#redirect-modifier",
|
|
6798
7024
|
assignable:true,
|
|
@@ -6820,6 +7046,17 @@ var data$g = { adg_os_any:{ name:"redirect",
|
|
|
6820
7046
|
negatable:false,
|
|
6821
7047
|
value_format:"(?x)\n # ABP resources always starts with the `abp-resource:` prefix\n ^abp-resource:\n # Possible resource names\n (\n blank-text|\n blank-css|\n blank-js|\n blank-html|\n blank-mp3|\n 1x1-transparent-gif|\n 2x2-transparent-png|\n 3x2-transparent-png|\n 32x32-transparent-png\n )$" } };
|
|
6822
7048
|
|
|
7049
|
+
var data$g = { adg_os_any:{ name:"referrerpolicy",
|
|
7050
|
+
description:"This modifier allows overriding of a page's referrer policy.",
|
|
7051
|
+
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#referrerpolicy-modifier",
|
|
7052
|
+
conflicts:[ "document",
|
|
7053
|
+
"subdocument" ],
|
|
7054
|
+
inverse_conflicts:true,
|
|
7055
|
+
assignable:true,
|
|
7056
|
+
negatable:false,
|
|
7057
|
+
value_optional:true,
|
|
7058
|
+
value_format:"referrerpolicy_value" } };
|
|
7059
|
+
|
|
6823
7060
|
var data$f = { adg_os_any:{ name:"removeheader",
|
|
6824
7061
|
description:"Rules with the `$removeheader` modifier are intended to remove headers from HTTP requests and responses.",
|
|
6825
7062
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#removeheader-modifier",
|
|
@@ -7129,46 +7366,47 @@ var data = { adg_any:{ name:"xmlhttprequest",
|
|
|
7129
7366
|
// @ts-nocheck
|
|
7130
7367
|
// Please keep imports and exports in alphabetical order
|
|
7131
7368
|
const rawModifiersData = {
|
|
7132
|
-
all: data$
|
|
7133
|
-
app: data$
|
|
7134
|
-
badfilter: data$
|
|
7135
|
-
cname: data$
|
|
7136
|
-
content: data$
|
|
7137
|
-
cookie: data$
|
|
7138
|
-
csp: data$
|
|
7139
|
-
denyallow: data$
|
|
7140
|
-
document: data$
|
|
7141
|
-
domain: data$
|
|
7142
|
-
elemhide: data$
|
|
7143
|
-
empty: data$
|
|
7144
|
-
firstParty: data$
|
|
7145
|
-
extension: data$
|
|
7146
|
-
font: data$
|
|
7147
|
-
genericblock: data$
|
|
7148
|
-
generichide: data$
|
|
7149
|
-
header: data$
|
|
7150
|
-
hls: data$
|
|
7151
|
-
image: data$
|
|
7152
|
-
important: data$
|
|
7153
|
-
inlineFont: data$
|
|
7154
|
-
inlineScript: data$
|
|
7155
|
-
jsinject: data$
|
|
7156
|
-
jsonprune: data$
|
|
7157
|
-
matchCase: data$
|
|
7158
|
-
media: data$
|
|
7159
|
-
method: data$
|
|
7160
|
-
mp4: data$
|
|
7161
|
-
network: data$
|
|
7162
|
-
noop: data$
|
|
7163
|
-
objectSubrequest: data$
|
|
7164
|
-
object: data$
|
|
7165
|
-
other: data$
|
|
7166
|
-
permissions: data$
|
|
7167
|
-
ping: data$
|
|
7168
|
-
popunder: data$
|
|
7169
|
-
popup: data$
|
|
7170
|
-
redirectRule: data$
|
|
7171
|
-
redirect: data$
|
|
7369
|
+
all: data$U,
|
|
7370
|
+
app: data$T,
|
|
7371
|
+
badfilter: data$S,
|
|
7372
|
+
cname: data$R,
|
|
7373
|
+
content: data$Q,
|
|
7374
|
+
cookie: data$P,
|
|
7375
|
+
csp: data$O,
|
|
7376
|
+
denyallow: data$N,
|
|
7377
|
+
document: data$M,
|
|
7378
|
+
domain: data$L,
|
|
7379
|
+
elemhide: data$K,
|
|
7380
|
+
empty: data$J,
|
|
7381
|
+
firstParty: data$I,
|
|
7382
|
+
extension: data$H,
|
|
7383
|
+
font: data$G,
|
|
7384
|
+
genericblock: data$F,
|
|
7385
|
+
generichide: data$E,
|
|
7386
|
+
header: data$D,
|
|
7387
|
+
hls: data$C,
|
|
7388
|
+
image: data$B,
|
|
7389
|
+
important: data$A,
|
|
7390
|
+
inlineFont: data$z,
|
|
7391
|
+
inlineScript: data$y,
|
|
7392
|
+
jsinject: data$x,
|
|
7393
|
+
jsonprune: data$w,
|
|
7394
|
+
matchCase: data$v,
|
|
7395
|
+
media: data$u,
|
|
7396
|
+
method: data$t,
|
|
7397
|
+
mp4: data$s,
|
|
7398
|
+
network: data$r,
|
|
7399
|
+
noop: data$q,
|
|
7400
|
+
objectSubrequest: data$p,
|
|
7401
|
+
object: data$o,
|
|
7402
|
+
other: data$n,
|
|
7403
|
+
permissions: data$m,
|
|
7404
|
+
ping: data$l,
|
|
7405
|
+
popunder: data$k,
|
|
7406
|
+
popup: data$j,
|
|
7407
|
+
redirectRule: data$i,
|
|
7408
|
+
redirect: data$h,
|
|
7409
|
+
referrerpolicy: data$g,
|
|
7172
7410
|
removeheader: data$f,
|
|
7173
7411
|
removeparam: data$e,
|
|
7174
7412
|
replace: data$d,
|
|
@@ -7361,7 +7599,7 @@ const ALLOWED_CSP_DIRECTIVES = new Set([
|
|
|
7361
7599
|
'worker-src',
|
|
7362
7600
|
]);
|
|
7363
7601
|
/**
|
|
7364
|
-
* Allowed
|
|
7602
|
+
* Allowed directives for $permissions modifier.
|
|
7365
7603
|
*
|
|
7366
7604
|
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#permissions-modifier}
|
|
7367
7605
|
*/
|
|
@@ -7411,6 +7649,21 @@ const PERMISSIONS_TOKEN_SELF = 'self';
|
|
|
7411
7649
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy#allowlists}
|
|
7412
7650
|
*/
|
|
7413
7651
|
const EMPTY_PERMISSIONS_ALLOWLIST = `${OPEN_PARENTHESIS}${CLOSE_PARENTHESIS}`;
|
|
7652
|
+
/**
|
|
7653
|
+
* Allowed directives for $referrerpolicy modifier.
|
|
7654
|
+
*
|
|
7655
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy}
|
|
7656
|
+
*/
|
|
7657
|
+
const REFERRER_POLICY_DIRECTIVES = new Set([
|
|
7658
|
+
'no-referrer',
|
|
7659
|
+
'no-referrer-when-downgrade',
|
|
7660
|
+
'origin',
|
|
7661
|
+
'origin-when-cross-origin',
|
|
7662
|
+
'same-origin',
|
|
7663
|
+
'strict-origin',
|
|
7664
|
+
'strict-origin-when-cross-origin',
|
|
7665
|
+
'unsafe-url',
|
|
7666
|
+
]);
|
|
7414
7667
|
/**
|
|
7415
7668
|
* Prefixes for error messages used in modifier validation.
|
|
7416
7669
|
*/
|
|
@@ -7423,6 +7676,7 @@ const VALIDATION_ERROR_PREFIX = {
|
|
|
7423
7676
|
INVALID_PERMISSION_DIRECTIVE: 'Invalid Permissions-Policy directive for the modifier',
|
|
7424
7677
|
INVALID_PERMISSION_ORIGINS: 'Origins in the value is invalid for the modifier and the directive',
|
|
7425
7678
|
INVALID_PERMISSION_ORIGIN_QUOTES: 'Double quotes should be used for origins in the value of the modifier',
|
|
7679
|
+
INVALID_REFERRER_POLICY_DIRECTIVE: 'Invalid Referrer-Policy directive for the modifier',
|
|
7426
7680
|
MIXED_NEGATIONS: 'Simultaneous usage of negated and not negated values is forbidden for the modifier',
|
|
7427
7681
|
NO_CSP_VALUE: 'No CSP value for the modifier and the directive',
|
|
7428
7682
|
NO_CSP_DIRECTIVE_QUOTE: 'CSP directives should no be quoted for the modifier',
|
|
@@ -7541,14 +7795,14 @@ const getSpecificBlockerData = (modifiersData, blockerPrefix, modifierName) => {
|
|
|
7541
7795
|
* @example
|
|
7542
7796
|
* `example.*` — matches with any TLD, e.g. `example.org`, `example.com`, etc.
|
|
7543
7797
|
*/
|
|
7544
|
-
const WILDCARD_TLD = DOT + WILDCARD
|
|
7798
|
+
const WILDCARD_TLD = DOT + WILDCARD;
|
|
7545
7799
|
/**
|
|
7546
7800
|
* Marker for a wildcard subdomain — `*.`.
|
|
7547
7801
|
*
|
|
7548
7802
|
* @example
|
|
7549
7803
|
* `*.example.org` — matches with any subdomain, e.g. `foo.example.org` or `bar.example.org`
|
|
7550
7804
|
*/
|
|
7551
|
-
const WILDCARD_SUBDOMAIN = WILDCARD
|
|
7805
|
+
const WILDCARD_SUBDOMAIN = WILDCARD + DOT;
|
|
7552
7806
|
class DomainUtils {
|
|
7553
7807
|
/**
|
|
7554
7808
|
* Check if the input is a valid domain or hostname.
|
|
@@ -7559,7 +7813,7 @@ class DomainUtils {
|
|
|
7559
7813
|
static isValidDomainOrHostname(domain) {
|
|
7560
7814
|
let domainToCheck = domain;
|
|
7561
7815
|
// Wildcard-only domain, typically a generic rule
|
|
7562
|
-
if (domainToCheck === WILDCARD
|
|
7816
|
+
if (domainToCheck === WILDCARD) {
|
|
7563
7817
|
return true;
|
|
7564
7818
|
}
|
|
7565
7819
|
// https://adguard.com/kb/general/ad-filtering/create-own-filters/#wildcard-for-tld
|
|
@@ -7746,6 +8000,7 @@ var CustomValueFormatValidatorName;
|
|
|
7746
8000
|
CustomValueFormatValidatorName["Domain"] = "pipe_separated_domains";
|
|
7747
8001
|
CustomValueFormatValidatorName["Method"] = "pipe_separated_methods";
|
|
7748
8002
|
CustomValueFormatValidatorName["Permissions"] = "permissions_value";
|
|
8003
|
+
CustomValueFormatValidatorName["ReferrerPolicy"] = "referrerpolicy_value";
|
|
7749
8004
|
CustomValueFormatValidatorName["StealthOption"] = "pipe_separated_stealth_options";
|
|
7750
8005
|
})(CustomValueFormatValidatorName || (CustomValueFormatValidatorName = {}));
|
|
7751
8006
|
/**
|
|
@@ -7779,7 +8034,7 @@ const isValidAppNameChunk = (chunk) => {
|
|
|
7779
8034
|
const isValidAppModifierValue = (value) => {
|
|
7780
8035
|
// $app modifier does not support wildcard tld
|
|
7781
8036
|
// https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier
|
|
7782
|
-
if (value.includes(WILDCARD
|
|
8037
|
+
if (value.includes(WILDCARD)) {
|
|
7783
8038
|
return false;
|
|
7784
8039
|
}
|
|
7785
8040
|
return value
|
|
@@ -7844,7 +8099,7 @@ const isValidDenyAllowModifierValue = (value) => {
|
|
|
7844
8099
|
// $denyallow modifier does not support wildcard tld
|
|
7845
8100
|
// https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier
|
|
7846
8101
|
// but here we are simply checking whether the value contains wildcard `*`, not ends with `.*`
|
|
7847
|
-
if (value.includes(WILDCARD
|
|
8102
|
+
if (value.includes(WILDCARD)) {
|
|
7848
8103
|
return false;
|
|
7849
8104
|
}
|
|
7850
8105
|
// TODO: add cache for domains validation
|
|
@@ -8141,7 +8396,7 @@ const validatePermissionAllowlist = (allowlist, directive, modifierName) => {
|
|
|
8141
8396
|
// `*` is one of available permissions tokens
|
|
8142
8397
|
// e.g. 'fullscreen=*'
|
|
8143
8398
|
// https://w3c.github.io/webappsec-permissions-policy/#structured-header-serialization
|
|
8144
|
-
if (allowlist === WILDCARD
|
|
8399
|
+
if (allowlist === WILDCARD
|
|
8145
8400
|
// e.g. 'autoplay=()'
|
|
8146
8401
|
|| allowlist === EMPTY_PERMISSIONS_ALLOWLIST) {
|
|
8147
8402
|
return { valid: true };
|
|
@@ -8202,6 +8457,26 @@ const validatePermissions = (modifier) => {
|
|
|
8202
8457
|
}
|
|
8203
8458
|
return { valid: true };
|
|
8204
8459
|
};
|
|
8460
|
+
/**
|
|
8461
|
+
* Validates `referrerpolicy_value` custom value format.
|
|
8462
|
+
* Used for $referrerpolicy modifier.
|
|
8463
|
+
*
|
|
8464
|
+
* @param modifier Modifier AST node.
|
|
8465
|
+
*
|
|
8466
|
+
* @returns Validation result.
|
|
8467
|
+
*/
|
|
8468
|
+
const validateReferrerPolicy = (modifier) => {
|
|
8469
|
+
if (!modifier.value?.value) {
|
|
8470
|
+
return getValueRequiredValidationResult(modifier.modifier.value);
|
|
8471
|
+
}
|
|
8472
|
+
const modifierName = modifier.modifier.value;
|
|
8473
|
+
const modifierValue = modifier.value.value;
|
|
8474
|
+
if (!REFERRER_POLICY_DIRECTIVES.has(modifierValue)) {
|
|
8475
|
+
// eslint-disable-next-line max-len
|
|
8476
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_REFERRER_POLICY_DIRECTIVE}: '${modifierName}': '${modifierValue}'`);
|
|
8477
|
+
}
|
|
8478
|
+
return { valid: true };
|
|
8479
|
+
};
|
|
8205
8480
|
/**
|
|
8206
8481
|
* Map of all available pre-defined validators for modifiers with custom `value_format`.
|
|
8207
8482
|
*/
|
|
@@ -8212,6 +8487,7 @@ const CUSTOM_VALUE_FORMAT_MAP = {
|
|
|
8212
8487
|
[CustomValueFormatValidatorName.Domain]: validatePipeSeparatedDomains,
|
|
8213
8488
|
[CustomValueFormatValidatorName.Method]: validatePipeSeparatedMethods,
|
|
8214
8489
|
[CustomValueFormatValidatorName.Permissions]: validatePermissions,
|
|
8490
|
+
[CustomValueFormatValidatorName.ReferrerPolicy]: validateReferrerPolicy,
|
|
8215
8491
|
[CustomValueFormatValidatorName.StealthOption]: validatePipeSeparatedStealthOptions,
|
|
8216
8492
|
};
|
|
8217
8493
|
/**
|
|
@@ -8405,7 +8681,7 @@ class ModifierValidator {
|
|
|
8405
8681
|
* @returns Result of modifier validation.
|
|
8406
8682
|
*/
|
|
8407
8683
|
validate = (syntax, rawModifier, isException = false) => {
|
|
8408
|
-
const modifier =
|
|
8684
|
+
const modifier = clone(rawModifier);
|
|
8409
8685
|
// special case: handle noop modifier which may be used as multiple underscores (not just one)
|
|
8410
8686
|
// https://adguard.com/kb/general/ad-filtering/create-own-filters/#noop-modifier
|
|
8411
8687
|
if (modifier.modifier.value.startsWith(UNDERSCORE)) {
|
|
@@ -8484,7 +8760,9 @@ class ConverterBase {
|
|
|
8484
8760
|
* Converts some data to AdGuard format
|
|
8485
8761
|
*
|
|
8486
8762
|
* @param data Data to convert
|
|
8487
|
-
* @returns
|
|
8763
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
8764
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
8765
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
8488
8766
|
* @throws If the data is invalid or incompatible
|
|
8489
8767
|
*/
|
|
8490
8768
|
static convertToAdg(data) {
|
|
@@ -8494,7 +8772,9 @@ class ConverterBase {
|
|
|
8494
8772
|
* Converts some data to Adblock Plus format
|
|
8495
8773
|
*
|
|
8496
8774
|
* @param data Data to convert
|
|
8497
|
-
* @returns
|
|
8775
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
8776
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
8777
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
8498
8778
|
* @throws If the data is invalid or incompatible
|
|
8499
8779
|
*/
|
|
8500
8780
|
static convertToAbp(data) {
|
|
@@ -8504,7 +8784,9 @@ class ConverterBase {
|
|
|
8504
8784
|
* Converts some data to uBlock Origin format
|
|
8505
8785
|
*
|
|
8506
8786
|
* @param data Data to convert
|
|
8507
|
-
* @returns
|
|
8787
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
8788
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
8789
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
8508
8790
|
* @throws If the data is invalid or incompatible
|
|
8509
8791
|
*/
|
|
8510
8792
|
static convertToUbo(data) {
|
|
@@ -8528,7 +8810,9 @@ class RuleConverterBase extends ConverterBase {
|
|
|
8528
8810
|
* Converts an adblock filtering rule to AdGuard format, if possible.
|
|
8529
8811
|
*
|
|
8530
8812
|
* @param rule Rule node to convert
|
|
8531
|
-
* @returns
|
|
8813
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
8814
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
8815
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8532
8816
|
* @throws If the rule is invalid or cannot be converted
|
|
8533
8817
|
*/
|
|
8534
8818
|
static convertToAdg(rule) {
|
|
@@ -8538,7 +8822,9 @@ class RuleConverterBase extends ConverterBase {
|
|
|
8538
8822
|
* Converts an adblock filtering rule to Adblock Plus format, if possible.
|
|
8539
8823
|
*
|
|
8540
8824
|
* @param rule Rule node to convert
|
|
8541
|
-
* @returns
|
|
8825
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
8826
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
8827
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8542
8828
|
* @throws If the rule is invalid or cannot be converted
|
|
8543
8829
|
*/
|
|
8544
8830
|
static convertToAbp(rule) {
|
|
@@ -8548,7 +8834,9 @@ class RuleConverterBase extends ConverterBase {
|
|
|
8548
8834
|
* Converts an adblock filtering rule to uBlock Origin format, if possible.
|
|
8549
8835
|
*
|
|
8550
8836
|
* @param rule Rule node to convert
|
|
8551
|
-
* @returns
|
|
8837
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
8838
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
8839
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8552
8840
|
* @throws If the rule is invalid or cannot be converted
|
|
8553
8841
|
*/
|
|
8554
8842
|
static convertToUbo(rule) {
|
|
@@ -8556,6 +8844,37 @@ class RuleConverterBase extends ConverterBase {
|
|
|
8556
8844
|
}
|
|
8557
8845
|
}
|
|
8558
8846
|
|
|
8847
|
+
/**
|
|
8848
|
+
* @file Conversion result interface and helper functions
|
|
8849
|
+
*/
|
|
8850
|
+
/**
|
|
8851
|
+
* Helper function to create a generic conversion result.
|
|
8852
|
+
*
|
|
8853
|
+
* @param result Conversion result
|
|
8854
|
+
* @param isConverted Indicates whether the input item was converted
|
|
8855
|
+
* @template T Type of the item to convert
|
|
8856
|
+
* @template U Type of the conversion result (defaults to `T`, but can be `T[]` as well)
|
|
8857
|
+
* @returns Generic conversion result
|
|
8858
|
+
*/
|
|
8859
|
+
// eslint-disable-next-line max-len
|
|
8860
|
+
function createConversionResult(result, isConverted) {
|
|
8861
|
+
return {
|
|
8862
|
+
result,
|
|
8863
|
+
isConverted,
|
|
8864
|
+
};
|
|
8865
|
+
}
|
|
8866
|
+
/**
|
|
8867
|
+
* Helper function to create a node conversion result.
|
|
8868
|
+
*
|
|
8869
|
+
* @param nodes Array of nodes
|
|
8870
|
+
* @param isConverted Indicates whether the input item was converted
|
|
8871
|
+
* @template T Type of the node (extends `Node`)
|
|
8872
|
+
* @returns Node conversion result
|
|
8873
|
+
*/
|
|
8874
|
+
function createNodeConversionResult(nodes, isConverted) {
|
|
8875
|
+
return createConversionResult(nodes, isConverted);
|
|
8876
|
+
}
|
|
8877
|
+
|
|
8559
8878
|
/**
|
|
8560
8879
|
* @file Comment rule converter
|
|
8561
8880
|
*/
|
|
@@ -8569,27 +8888,30 @@ class CommentRuleConverter extends RuleConverterBase {
|
|
|
8569
8888
|
* Converts a comment rule to AdGuard format, if possible.
|
|
8570
8889
|
*
|
|
8571
8890
|
* @param rule Rule node to convert
|
|
8572
|
-
* @returns
|
|
8891
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
8892
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
8893
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8573
8894
|
* @throws If the rule is invalid or cannot be converted
|
|
8574
8895
|
*/
|
|
8575
8896
|
static convertToAdg(rule) {
|
|
8576
|
-
// Clone the provided AST node to avoid side effects
|
|
8577
|
-
const ruleNode = cloneDeep(rule);
|
|
8578
8897
|
// TODO: Add support for other comment types, if needed
|
|
8579
8898
|
// Main task is # -> ! conversion
|
|
8580
|
-
switch (
|
|
8899
|
+
switch (rule.type) {
|
|
8581
8900
|
case CommentRuleType.CommentRule:
|
|
8582
|
-
//
|
|
8583
|
-
if (
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8901
|
+
// Check if the rule needs to be converted
|
|
8902
|
+
if (rule.type === CommentRuleType.CommentRule && rule.marker.value === CommentMarker.Hashmark) {
|
|
8903
|
+
// Add a ! to the beginning of the comment
|
|
8904
|
+
// TODO: Replace with custom clone method
|
|
8905
|
+
const ruleClone = clone(rule);
|
|
8906
|
+
ruleClone.marker.value = CommentMarker.Regular;
|
|
8907
|
+
// Add the hashmark to the beginning of the comment text
|
|
8908
|
+
ruleClone.text.value = `${SPACE}${CommentMarker.Hashmark}${ruleClone.text.value}`;
|
|
8909
|
+
return createNodeConversionResult([ruleClone], true);
|
|
8588
8910
|
}
|
|
8589
|
-
return [
|
|
8911
|
+
return createNodeConversionResult([rule], false);
|
|
8590
8912
|
// Leave any other comment rule as is
|
|
8591
8913
|
default:
|
|
8592
|
-
return [
|
|
8914
|
+
return createNodeConversionResult([rule], false);
|
|
8593
8915
|
}
|
|
8594
8916
|
}
|
|
8595
8917
|
}
|
|
@@ -8759,6 +9081,58 @@ class RegExpUtils {
|
|
|
8759
9081
|
}
|
|
8760
9082
|
}
|
|
8761
9083
|
|
|
9084
|
+
/**
|
|
9085
|
+
* @file Custom clone functions for AST nodes, this is probably the most efficient way to clone AST nodes.
|
|
9086
|
+
* @todo Maybe move them to parser classes as 'clone' methods
|
|
9087
|
+
*/
|
|
9088
|
+
/**
|
|
9089
|
+
* Clones a scriptlet rule node.
|
|
9090
|
+
*
|
|
9091
|
+
* @param node Node to clone
|
|
9092
|
+
* @returns Cloned node
|
|
9093
|
+
*/
|
|
9094
|
+
function cloneScriptletRuleNode(node) {
|
|
9095
|
+
return {
|
|
9096
|
+
type: node.type,
|
|
9097
|
+
children: node.children.map((child) => ({ ...child })),
|
|
9098
|
+
};
|
|
9099
|
+
}
|
|
9100
|
+
/**
|
|
9101
|
+
* Clones a domain list node.
|
|
9102
|
+
*
|
|
9103
|
+
* @param node Node to clone
|
|
9104
|
+
* @returns Cloned node
|
|
9105
|
+
*/
|
|
9106
|
+
function cloneDomainListNode(node) {
|
|
9107
|
+
return {
|
|
9108
|
+
type: node.type,
|
|
9109
|
+
separator: node.separator,
|
|
9110
|
+
children: node.children.map((domain) => ({ ...domain })),
|
|
9111
|
+
};
|
|
9112
|
+
}
|
|
9113
|
+
/**
|
|
9114
|
+
* Clones a modifier list node.
|
|
9115
|
+
*
|
|
9116
|
+
* @param node Node to clone
|
|
9117
|
+
* @returns Cloned node
|
|
9118
|
+
*/
|
|
9119
|
+
function cloneModifierListNode(node) {
|
|
9120
|
+
return {
|
|
9121
|
+
type: node.type,
|
|
9122
|
+
children: node.children.map((modifier) => {
|
|
9123
|
+
const res = {
|
|
9124
|
+
type: modifier.type,
|
|
9125
|
+
exception: modifier.exception,
|
|
9126
|
+
modifier: { ...modifier.modifier },
|
|
9127
|
+
};
|
|
9128
|
+
if (modifier.value) {
|
|
9129
|
+
res.value = { ...modifier.value };
|
|
9130
|
+
}
|
|
9131
|
+
return res;
|
|
9132
|
+
}),
|
|
9133
|
+
};
|
|
9134
|
+
}
|
|
9135
|
+
|
|
8762
9136
|
/**
|
|
8763
9137
|
* @file HTML filtering rule converter
|
|
8764
9138
|
*/
|
|
@@ -8771,16 +9145,22 @@ class RegExpUtils {
|
|
|
8771
9145
|
*
|
|
8772
9146
|
* @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#html-filtering-rules}
|
|
8773
9147
|
*/
|
|
8774
|
-
const
|
|
8775
|
-
const
|
|
9148
|
+
const ADG_HTML_DEFAULT_MAX_LENGTH = 8192;
|
|
9149
|
+
const ADG_HTML_CONVERSION_MAX_LENGTH = ADG_HTML_DEFAULT_MAX_LENGTH * 32;
|
|
8776
9150
|
const NOT_SPECIFIED = -1;
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
9151
|
+
var PseudoClasses$1;
|
|
9152
|
+
(function (PseudoClasses) {
|
|
9153
|
+
PseudoClasses["Contains"] = "contains";
|
|
9154
|
+
PseudoClasses["HasText"] = "has-text";
|
|
9155
|
+
PseudoClasses["MinTextLength"] = "min-text-length";
|
|
9156
|
+
})(PseudoClasses$1 || (PseudoClasses$1 = {}));
|
|
9157
|
+
var AttributeSelectors;
|
|
9158
|
+
(function (AttributeSelectors) {
|
|
9159
|
+
AttributeSelectors["MaxLength"] = "max-length";
|
|
9160
|
+
AttributeSelectors["MinLength"] = "min-length";
|
|
9161
|
+
AttributeSelectors["TagContent"] = "tag-content";
|
|
9162
|
+
AttributeSelectors["Wildcard"] = "wildcard";
|
|
9163
|
+
})(AttributeSelectors || (AttributeSelectors = {}));
|
|
8784
9164
|
/**
|
|
8785
9165
|
* HTML filtering rule converter class
|
|
8786
9166
|
*
|
|
@@ -8803,16 +9183,23 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8803
9183
|
* ```
|
|
8804
9184
|
*
|
|
8805
9185
|
* @param rule Rule node to convert
|
|
8806
|
-
* @returns
|
|
9186
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9187
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9188
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8807
9189
|
* @throws If the rule is invalid or cannot be converted
|
|
8808
9190
|
*/
|
|
8809
9191
|
static convertToAdg(rule) {
|
|
8810
|
-
//
|
|
8811
|
-
|
|
9192
|
+
// Ignore AdGuard rules
|
|
9193
|
+
if (rule.syntax === AdblockSyntax.Adg) {
|
|
9194
|
+
return createNodeConversionResult([rule], false);
|
|
9195
|
+
}
|
|
9196
|
+
if (rule.syntax === AdblockSyntax.Abp) {
|
|
9197
|
+
throw new RuleConversionError('Invalid rule, ABP does not support HTML filtering rules');
|
|
9198
|
+
}
|
|
8812
9199
|
// Prepare the conversion result
|
|
8813
9200
|
const conversionResult = [];
|
|
8814
9201
|
// Iterate over selector list
|
|
8815
|
-
for (const selector of
|
|
9202
|
+
for (const selector of rule.body.body.children) {
|
|
8816
9203
|
// Check selector, just in case
|
|
8817
9204
|
if (selector.type !== CssTreeNodeType.Selector) {
|
|
8818
9205
|
throw new RuleConversionError(`Expected selector, got '${selector.type}'`);
|
|
@@ -8839,24 +9226,24 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8839
9226
|
throw new RuleConversionError('Tag selector should be the first child, if present');
|
|
8840
9227
|
}
|
|
8841
9228
|
// Simply store the tag selector
|
|
8842
|
-
convertedSelector.children.push(
|
|
9229
|
+
convertedSelector.children.push(clone(node));
|
|
8843
9230
|
break;
|
|
8844
9231
|
case CssTreeNodeType.AttributeSelector:
|
|
8845
9232
|
// Check if the attribute selector is a special AdGuard attribute
|
|
8846
9233
|
switch (node.name.name) {
|
|
8847
|
-
case
|
|
9234
|
+
case AttributeSelectors.MinLength:
|
|
8848
9235
|
minLength = CssTree.parseAttributeSelectorValueAsNumber(node);
|
|
8849
9236
|
break;
|
|
8850
|
-
case
|
|
9237
|
+
case AttributeSelectors.MaxLength:
|
|
8851
9238
|
maxLength = CssTree.parseAttributeSelectorValueAsNumber(node);
|
|
8852
9239
|
break;
|
|
8853
|
-
case
|
|
8854
|
-
case
|
|
9240
|
+
case AttributeSelectors.TagContent:
|
|
9241
|
+
case AttributeSelectors.Wildcard:
|
|
8855
9242
|
CssTree.assertAttributeSelectorHasStringValue(node);
|
|
8856
|
-
convertedSelector.children.push(
|
|
9243
|
+
convertedSelector.children.push(clone(node));
|
|
8857
9244
|
break;
|
|
8858
9245
|
default:
|
|
8859
|
-
convertedSelector.children.push(
|
|
9246
|
+
convertedSelector.children.push(clone(node));
|
|
8860
9247
|
}
|
|
8861
9248
|
break;
|
|
8862
9249
|
case CssTreeNodeType.PseudoClassSelector:
|
|
@@ -8870,18 +9257,18 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8870
9257
|
}
|
|
8871
9258
|
// Process the pseudo class based on its name
|
|
8872
9259
|
switch (node.name) {
|
|
8873
|
-
case
|
|
8874
|
-
case
|
|
9260
|
+
case PseudoClasses$1.HasText:
|
|
9261
|
+
case PseudoClasses$1.Contains:
|
|
8875
9262
|
// Check if the argument is a RegExp
|
|
8876
9263
|
if (RegExpUtils.isRegexPattern(arg.value)) {
|
|
8877
9264
|
// TODO: Add some support for RegExp patterns later
|
|
8878
9265
|
// Need to find a way to convert some RegExp patterns to glob patterns
|
|
8879
9266
|
throw new RuleConversionError('Conversion of RegExp patterns is not yet supported');
|
|
8880
9267
|
}
|
|
8881
|
-
convertedSelector.children.push(CssTree.createAttributeSelectorNode(
|
|
9268
|
+
convertedSelector.children.push(CssTree.createAttributeSelectorNode(AttributeSelectors.TagContent, arg.value));
|
|
8882
9269
|
break;
|
|
8883
9270
|
// https://github.com/gorhill/uBlock/wiki/Procedural-cosmetic-filters#subjectmin-text-lengthn
|
|
8884
|
-
case
|
|
9271
|
+
case PseudoClasses$1.MinTextLength:
|
|
8885
9272
|
minLength = CssTree.parsePseudoClassArgumentAsNumber(node);
|
|
8886
9273
|
break;
|
|
8887
9274
|
default:
|
|
@@ -8893,10 +9280,10 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8893
9280
|
}
|
|
8894
9281
|
}
|
|
8895
9282
|
if (minLength !== NOT_SPECIFIED) {
|
|
8896
|
-
convertedSelector.children.push(CssTree.createAttributeSelectorNode(
|
|
9283
|
+
convertedSelector.children.push(CssTree.createAttributeSelectorNode(AttributeSelectors.MinLength, String(minLength)));
|
|
8897
9284
|
}
|
|
8898
|
-
convertedSelector.children.push(CssTree.createAttributeSelectorNode(
|
|
8899
|
-
?
|
|
9285
|
+
convertedSelector.children.push(CssTree.createAttributeSelectorNode(AttributeSelectors.MaxLength, String(maxLength === NOT_SPECIFIED
|
|
9286
|
+
? ADG_HTML_CONVERSION_MAX_LENGTH
|
|
8900
9287
|
: maxLength)));
|
|
8901
9288
|
// Create the converted rule
|
|
8902
9289
|
conversionResult.push({
|
|
@@ -8906,7 +9293,7 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8906
9293
|
// Convert the separator based on the exception status
|
|
8907
9294
|
separator: {
|
|
8908
9295
|
type: 'Value',
|
|
8909
|
-
value:
|
|
9296
|
+
value: rule.exception
|
|
8910
9297
|
? CosmeticRuleSeparator.AdgHtmlFilteringException
|
|
8911
9298
|
: CosmeticRuleSeparator.AdgHtmlFiltering,
|
|
8912
9299
|
},
|
|
@@ -8921,11 +9308,11 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8921
9308
|
}],
|
|
8922
9309
|
},
|
|
8923
9310
|
},
|
|
8924
|
-
exception:
|
|
8925
|
-
domains:
|
|
9311
|
+
exception: rule.exception,
|
|
9312
|
+
domains: cloneDomainListNode(rule.domains),
|
|
8926
9313
|
});
|
|
8927
9314
|
}
|
|
8928
|
-
return conversionResult;
|
|
9315
|
+
return createNodeConversionResult(conversionResult, true);
|
|
8929
9316
|
}
|
|
8930
9317
|
}
|
|
8931
9318
|
|
|
@@ -8946,96 +9333,38 @@ function getScriptletName(scriptletNode) {
|
|
|
8946
9333
|
return scriptletNode.children[0].value;
|
|
8947
9334
|
}
|
|
8948
9335
|
/**
|
|
8949
|
-
* Set name of the scriptlet
|
|
9336
|
+
* Set name of the scriptlet.
|
|
9337
|
+
* Modifies input `scriptletNode` if needed.
|
|
8950
9338
|
*
|
|
8951
9339
|
* @param scriptletNode Scriptlet node to set name of
|
|
8952
9340
|
* @param name Name to set
|
|
8953
|
-
* @returns Scriptlet node with the specified name
|
|
8954
|
-
* @throws If the scriptlet is empty
|
|
8955
9341
|
*/
|
|
8956
9342
|
function setScriptletName(scriptletNode, name) {
|
|
8957
|
-
if (scriptletNode.children.length
|
|
8958
|
-
|
|
9343
|
+
if (scriptletNode.children.length > 0) {
|
|
9344
|
+
// eslint-disable-next-line no-param-reassign
|
|
9345
|
+
scriptletNode.children[0].value = name;
|
|
8959
9346
|
}
|
|
8960
|
-
const scriptletNodeClone = cloneDeep(scriptletNode);
|
|
8961
|
-
scriptletNodeClone.children[0].value = name;
|
|
8962
|
-
return scriptletNodeClone;
|
|
8963
9347
|
}
|
|
8964
9348
|
/**
|
|
8965
9349
|
* Set quote type of the scriptlet parameters
|
|
8966
9350
|
*
|
|
8967
9351
|
* @param scriptletNode Scriptlet node to set quote type of
|
|
8968
9352
|
* @param quoteType Preferred quote type
|
|
8969
|
-
* @returns Scriptlet node with the specified quote type
|
|
8970
9353
|
*/
|
|
8971
9354
|
function setScriptletQuoteType(scriptletNode, quoteType) {
|
|
8972
|
-
if (scriptletNode.children.length
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
for (let i = 0; i < scriptletNodeClone.children.length; i += 1) {
|
|
8977
|
-
scriptletNodeClone.children[i].value = QuoteUtils.setStringQuoteType(scriptletNodeClone.children[i].value, quoteType);
|
|
8978
|
-
}
|
|
8979
|
-
return scriptletNodeClone;
|
|
8980
|
-
}
|
|
8981
|
-
|
|
8982
|
-
/**
|
|
8983
|
-
* @file Scriptlet conversions from ABP and uBO to ADG
|
|
8984
|
-
*/
|
|
8985
|
-
const ABP_SCRIPTLET_PREFIX = 'abp-';
|
|
8986
|
-
const UBO_SCRIPTLET_PREFIX = 'ubo-';
|
|
8987
|
-
/**
|
|
8988
|
-
* Helper class for converting scriptlets from ABP and uBO to ADG
|
|
8989
|
-
*/
|
|
8990
|
-
class AdgScriptletConverter {
|
|
8991
|
-
/**
|
|
8992
|
-
* Helper function to convert scriptlets to ADG. We implement the core
|
|
8993
|
-
* logic here to avoid code duplication.
|
|
8994
|
-
*
|
|
8995
|
-
* @param scriptletNode Scriptlet parameter list node to convert
|
|
8996
|
-
* @param prefix Prefix to add to the scriptlet name
|
|
8997
|
-
* @returns Converted scriptlet parameter list node
|
|
8998
|
-
*/
|
|
8999
|
-
static convertToAdg(scriptletNode, prefix) {
|
|
9000
|
-
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
9001
|
-
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletNode), QuoteType.None);
|
|
9002
|
-
// Clone the node to avoid any side effects
|
|
9003
|
-
let result = cloneDeep(scriptletNode);
|
|
9004
|
-
// Only add prefix if it's not already there
|
|
9005
|
-
if (!scriptletName.startsWith(prefix)) {
|
|
9006
|
-
result = setScriptletName(scriptletNode, `${prefix}${scriptletName}`);
|
|
9355
|
+
if (scriptletNode.children.length > 0) {
|
|
9356
|
+
for (let i = 0; i < scriptletNode.children.length; i += 1) {
|
|
9357
|
+
// eslint-disable-next-line no-param-reassign
|
|
9358
|
+
scriptletNode.children[i].value = QuoteUtils.setStringQuoteType(scriptletNode.children[i].value, quoteType);
|
|
9007
9359
|
}
|
|
9008
|
-
// ADG scriptlet parameters should be quoted, and single quoted are preferred
|
|
9009
|
-
result = setScriptletQuoteType(result, QuoteType.Single);
|
|
9010
|
-
return result;
|
|
9011
9360
|
}
|
|
9012
|
-
/**
|
|
9013
|
-
* Converts an ABP snippet node to ADG scriptlet node, if possible.
|
|
9014
|
-
*
|
|
9015
|
-
* @param scriptletNode Scriptlet node to convert
|
|
9016
|
-
* @returns Converted scriptlet node
|
|
9017
|
-
* @throws If the scriptlet isn't supported by ADG or is invalid
|
|
9018
|
-
* @see {@link https://help.adblockplus.org/hc/en-us/articles/1500002338501#snippets-ref}
|
|
9019
|
-
*/
|
|
9020
|
-
static convertFromAbp = (scriptletNode) => {
|
|
9021
|
-
return AdgScriptletConverter.convertToAdg(scriptletNode, ABP_SCRIPTLET_PREFIX);
|
|
9022
|
-
};
|
|
9023
|
-
/**
|
|
9024
|
-
* Convert a uBO scriptlet node to ADG scriptlet node, if possible.
|
|
9025
|
-
*
|
|
9026
|
-
* @param scriptletNode Scriptlet node to convert
|
|
9027
|
-
* @returns Converted scriptlet node
|
|
9028
|
-
* @throws If the scriptlet isn't supported by ADG or is invalid
|
|
9029
|
-
* @see {@link https://github.com/gorhill/uBlock/wiki/Resources-Library#available-general-purpose-scriptlets}
|
|
9030
|
-
*/
|
|
9031
|
-
static convertFromUbo = (scriptletNode) => {
|
|
9032
|
-
return AdgScriptletConverter.convertToAdg(scriptletNode, UBO_SCRIPTLET_PREFIX);
|
|
9033
|
-
};
|
|
9034
9361
|
}
|
|
9035
9362
|
|
|
9036
9363
|
/**
|
|
9037
9364
|
* @file Scriptlet injection rule converter
|
|
9038
9365
|
*/
|
|
9366
|
+
const ABP_SCRIPTLET_PREFIX = 'abp-';
|
|
9367
|
+
const UBO_SCRIPTLET_PREFIX = 'ubo-';
|
|
9039
9368
|
/**
|
|
9040
9369
|
* Scriptlet injection rule converter class
|
|
9041
9370
|
*
|
|
@@ -9046,38 +9375,91 @@ class ScriptletRuleConverter extends RuleConverterBase {
|
|
|
9046
9375
|
* Converts a scriptlet injection rule to AdGuard format, if possible.
|
|
9047
9376
|
*
|
|
9048
9377
|
* @param rule Rule node to convert
|
|
9049
|
-
* @returns
|
|
9378
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9379
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9380
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9050
9381
|
* @throws If the rule is invalid or cannot be converted
|
|
9051
9382
|
*/
|
|
9052
9383
|
static convertToAdg(rule) {
|
|
9053
|
-
//
|
|
9054
|
-
|
|
9384
|
+
// Ignore AdGuard rules
|
|
9385
|
+
if (rule.syntax === AdblockSyntax.Adg) {
|
|
9386
|
+
return createNodeConversionResult([rule], false);
|
|
9387
|
+
}
|
|
9388
|
+
const separator = rule.separator.value;
|
|
9389
|
+
let convertedSeparator = separator;
|
|
9390
|
+
convertedSeparator = rule.exception
|
|
9391
|
+
? CosmeticRuleSeparator.AdgJsInjectionException
|
|
9392
|
+
: CosmeticRuleSeparator.AdgJsInjection;
|
|
9055
9393
|
const convertedScriptlets = [];
|
|
9056
|
-
for (const scriptlet of
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9394
|
+
for (const scriptlet of rule.body.children) {
|
|
9395
|
+
// Clone the node to avoid any side effects
|
|
9396
|
+
const scriptletClone = cloneScriptletRuleNode(scriptlet);
|
|
9397
|
+
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
9398
|
+
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletClone), QuoteType.None);
|
|
9399
|
+
// Add prefix if it's not already there
|
|
9400
|
+
let prefix;
|
|
9401
|
+
switch (rule.syntax) {
|
|
9402
|
+
case AdblockSyntax.Abp:
|
|
9403
|
+
prefix = ABP_SCRIPTLET_PREFIX;
|
|
9404
|
+
break;
|
|
9405
|
+
case AdblockSyntax.Ubo:
|
|
9406
|
+
prefix = UBO_SCRIPTLET_PREFIX;
|
|
9407
|
+
break;
|
|
9408
|
+
default:
|
|
9409
|
+
prefix = EMPTY;
|
|
9062
9410
|
}
|
|
9063
|
-
|
|
9064
|
-
|
|
9411
|
+
if (!scriptletName.startsWith(prefix)) {
|
|
9412
|
+
setScriptletName(scriptletClone, `${prefix}${scriptletName}`);
|
|
9065
9413
|
}
|
|
9414
|
+
// ADG scriptlet parameters should be quoted, and single quoted are preferred
|
|
9415
|
+
setScriptletQuoteType(scriptletClone, QuoteType.Single);
|
|
9416
|
+
convertedScriptlets.push(scriptletClone);
|
|
9066
9417
|
}
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
return convertedScriptlets.map((scriptlet) => {
|
|
9072
|
-
return {
|
|
9073
|
-
...ruleNode,
|
|
9418
|
+
return createNodeConversionResult(convertedScriptlets.map((scriptlet) => {
|
|
9419
|
+
const res = {
|
|
9420
|
+
category: rule.category,
|
|
9421
|
+
type: rule.type,
|
|
9074
9422
|
syntax: AdblockSyntax.Adg,
|
|
9423
|
+
exception: rule.exception,
|
|
9424
|
+
domains: cloneDomainListNode(rule.domains),
|
|
9425
|
+
separator: {
|
|
9426
|
+
type: 'Value',
|
|
9427
|
+
value: convertedSeparator,
|
|
9428
|
+
},
|
|
9075
9429
|
body: {
|
|
9076
|
-
|
|
9430
|
+
type: rule.body.type,
|
|
9077
9431
|
children: [scriptlet],
|
|
9078
9432
|
},
|
|
9079
9433
|
};
|
|
9080
|
-
|
|
9434
|
+
if (rule.modifiers) {
|
|
9435
|
+
res.modifiers = cloneModifierListNode(rule.modifiers);
|
|
9436
|
+
}
|
|
9437
|
+
return res;
|
|
9438
|
+
}), true);
|
|
9439
|
+
}
|
|
9440
|
+
}
|
|
9441
|
+
|
|
9442
|
+
/**
|
|
9443
|
+
* A very simple map extension that allows to store multiple values for the same key
|
|
9444
|
+
* by storing them in an array.
|
|
9445
|
+
*
|
|
9446
|
+
* @todo Add more methods if needed
|
|
9447
|
+
*/
|
|
9448
|
+
class MultiValueMap extends Map {
|
|
9449
|
+
/**
|
|
9450
|
+
* Adds a value to the map. If the key already exists, the value will be appended to the existing array,
|
|
9451
|
+
* otherwise a new array will be created for the key.
|
|
9452
|
+
*
|
|
9453
|
+
* @param key Key to add
|
|
9454
|
+
* @param values Value(s) to add
|
|
9455
|
+
*/
|
|
9456
|
+
add(key, ...values) {
|
|
9457
|
+
let currentValues = super.get(key);
|
|
9458
|
+
if (isUndefined(currentValues)) {
|
|
9459
|
+
currentValues = [];
|
|
9460
|
+
super.set(key, values);
|
|
9461
|
+
}
|
|
9462
|
+
currentValues.push(...values);
|
|
9081
9463
|
}
|
|
9082
9464
|
}
|
|
9083
9465
|
|
|
@@ -9103,69 +9485,115 @@ class AdgCosmeticRuleModifierConverter {
|
|
|
9103
9485
|
* Converts a uBO cosmetic rule modifier list to ADG, if possible.
|
|
9104
9486
|
*
|
|
9105
9487
|
* @param modifierList Cosmetic rule modifier list node to convert
|
|
9106
|
-
* @returns
|
|
9488
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9489
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9490
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9107
9491
|
* @throws If the modifier list cannot be converted
|
|
9108
9492
|
* @see {@link https://github.com/gorhill/uBlock/wiki/Procedural-cosmetic-filters#cosmetic-filter-operators}
|
|
9109
9493
|
*/
|
|
9110
|
-
static convertFromUbo
|
|
9111
|
-
const
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
//
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
? `${REGEX_MARKER}${RegExpUtils.negateRegexPattern(RegExpUtils.patternToRegexp(modifierValue))}${REGEX_MARKER}`
|
|
9129
|
-
: modifierValue));
|
|
9130
|
-
break;
|
|
9131
|
-
default:
|
|
9132
|
-
// Leave the modifier as-is
|
|
9133
|
-
convertedModifierList.children.push(modifier);
|
|
9494
|
+
static convertFromUbo(modifierList) {
|
|
9495
|
+
const conversionMap = new MultiValueMap();
|
|
9496
|
+
modifierList.children.forEach((modifier, index) => {
|
|
9497
|
+
// :matches-path
|
|
9498
|
+
if (modifier.modifier.value === UBO_MATCHES_PATH_OPERATOR) {
|
|
9499
|
+
if (!modifier.value) {
|
|
9500
|
+
throw new RuleConversionError(`'${UBO_MATCHES_PATH_OPERATOR}' operator requires a value`);
|
|
9501
|
+
}
|
|
9502
|
+
const value = RegExpUtils.isRegexPattern(modifier.value.value)
|
|
9503
|
+
? StringUtils.escapeCharacters(modifier.value.value, SPECIAL_MODIFIER_REGEX_CHARS)
|
|
9504
|
+
: modifier.value.value;
|
|
9505
|
+
// Convert uBO's `:matches-path(...)` operator to ADG's `$path=...` modifier
|
|
9506
|
+
conversionMap.add(index, createModifierNode(ADG_PATH_MODIFIER,
|
|
9507
|
+
// We should negate the regexp if the modifier is an exception
|
|
9508
|
+
modifier.exception
|
|
9509
|
+
// eslint-disable-next-line max-len
|
|
9510
|
+
? `${REGEX_MARKER}${RegExpUtils.negateRegexPattern(RegExpUtils.patternToRegexp(value))}${REGEX_MARKER}`
|
|
9511
|
+
: value));
|
|
9134
9512
|
}
|
|
9135
|
-
}
|
|
9136
|
-
|
|
9137
|
-
|
|
9513
|
+
});
|
|
9514
|
+
// Check if we have any converted modifiers
|
|
9515
|
+
if (conversionMap.size) {
|
|
9516
|
+
const modifierListClone = clone(modifierList);
|
|
9517
|
+
// Replace the original modifiers with the converted ones
|
|
9518
|
+
modifierListClone.children = modifierListClone.children.map((modifier, index) => {
|
|
9519
|
+
const convertedModifier = conversionMap.get(index);
|
|
9520
|
+
return convertedModifier ?? modifier;
|
|
9521
|
+
}).flat();
|
|
9522
|
+
return createConversionResult(modifierListClone, true);
|
|
9523
|
+
}
|
|
9524
|
+
// Otherwise, just return the original modifier list
|
|
9525
|
+
return createConversionResult(modifierList, false);
|
|
9526
|
+
}
|
|
9138
9527
|
}
|
|
9139
9528
|
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
9529
|
+
var PseudoClasses;
|
|
9530
|
+
(function (PseudoClasses) {
|
|
9531
|
+
PseudoClasses["AbpContains"] = "-abp-contains";
|
|
9532
|
+
PseudoClasses["AbpHas"] = "-abp-has";
|
|
9533
|
+
PseudoClasses["Contains"] = "contains";
|
|
9534
|
+
PseudoClasses["Has"] = "has";
|
|
9535
|
+
PseudoClasses["HasText"] = "has-text";
|
|
9536
|
+
PseudoClasses["MatchesCss"] = "matches-css";
|
|
9537
|
+
PseudoClasses["MatchesCssAfter"] = "matches-css-after";
|
|
9538
|
+
PseudoClasses["MatchesCssBefore"] = "matches-css-before";
|
|
9539
|
+
PseudoClasses["Not"] = "not";
|
|
9540
|
+
})(PseudoClasses || (PseudoClasses = {}));
|
|
9541
|
+
var PseudoElements;
|
|
9542
|
+
(function (PseudoElements) {
|
|
9543
|
+
PseudoElements["After"] = "after";
|
|
9544
|
+
PseudoElements["Before"] = "before";
|
|
9545
|
+
})(PseudoElements || (PseudoElements = {}));
|
|
9546
|
+
const PSEUDO_ELEMENT_NAMES = new Set([
|
|
9547
|
+
PseudoElements.After,
|
|
9548
|
+
PseudoElements.Before,
|
|
9549
|
+
]);
|
|
9550
|
+
const LEGACY_MATCHES_CSS_NAMES = new Set([
|
|
9551
|
+
PseudoClasses.MatchesCssAfter,
|
|
9552
|
+
PseudoClasses.MatchesCssBefore,
|
|
9553
|
+
]);
|
|
9554
|
+
const LEGACY_EXT_CSS_INDICATOR_PSEUDO_NAMES = new Set([
|
|
9555
|
+
PseudoClasses.Not,
|
|
9556
|
+
PseudoClasses.MatchesCssBefore,
|
|
9557
|
+
PseudoClasses.MatchesCssAfter,
|
|
9558
|
+
]);
|
|
9559
|
+
const CSS_CONVERSION_INDICATOR_PSEUDO_NAMES = new Set([
|
|
9560
|
+
PseudoClasses.AbpContains,
|
|
9561
|
+
PseudoClasses.AbpHas,
|
|
9562
|
+
PseudoClasses.HasText,
|
|
9563
|
+
]);
|
|
9153
9564
|
/**
|
|
9154
9565
|
* Converts some pseudo-classes to pseudo-elements. For example:
|
|
9155
9566
|
* - `:before` → `::before`
|
|
9156
9567
|
*
|
|
9157
9568
|
* @param selectorList Selector list to convert
|
|
9158
|
-
* @returns
|
|
9569
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9570
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9571
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9159
9572
|
*/
|
|
9160
9573
|
function convertToPseudoElements(selectorList) {
|
|
9161
|
-
//
|
|
9162
|
-
const
|
|
9574
|
+
// Check conversion indications before doing any heavy work
|
|
9575
|
+
const hasIndicator = find(
|
|
9576
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9577
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9578
|
+
selectorList, (node) => node.type === CssTreeNodeType.PseudoClassSelector && PSEUDO_ELEMENT_NAMES.has(node.name));
|
|
9579
|
+
if (!hasIndicator) {
|
|
9580
|
+
return createConversionResult(selectorList, false);
|
|
9581
|
+
}
|
|
9582
|
+
// Make a clone of the selector list to avoid modifying the original one,
|
|
9583
|
+
// then convert & return the cloned version
|
|
9584
|
+
const selectorListClone = clone(selectorList);
|
|
9585
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9586
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9163
9587
|
walk(selectorListClone, {
|
|
9588
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9589
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9164
9590
|
leave: (node) => {
|
|
9165
9591
|
if (node.type === CssTreeNodeType.PseudoClassSelector) {
|
|
9166
|
-
//
|
|
9167
|
-
//
|
|
9168
|
-
|
|
9592
|
+
// If the pseudo-class is `:before` or `:after`, then we should
|
|
9593
|
+
// convert the node type to pseudo-element:
|
|
9594
|
+
// :after → ::after
|
|
9595
|
+
// :before → ::before
|
|
9596
|
+
if (PSEUDO_ELEMENT_NAMES.has(node.name)) {
|
|
9169
9597
|
Object.assign(node, {
|
|
9170
9598
|
...node,
|
|
9171
9599
|
type: CssTreeNodeType.PseudoElementSelector,
|
|
@@ -9174,7 +9602,7 @@ function convertToPseudoElements(selectorList) {
|
|
|
9174
9602
|
}
|
|
9175
9603
|
},
|
|
9176
9604
|
});
|
|
9177
|
-
return selectorListClone;
|
|
9605
|
+
return createConversionResult(selectorListClone, true);
|
|
9178
9606
|
}
|
|
9179
9607
|
/**
|
|
9180
9608
|
* Converts legacy Extended CSS `matches-css-before` and `matches-css-after`
|
|
@@ -9183,33 +9611,36 @@ function convertToPseudoElements(selectorList) {
|
|
|
9183
9611
|
* - `:matches-css-after(...)` → `:matches-css(after, ...)`
|
|
9184
9612
|
*
|
|
9185
9613
|
* @param node Node to convert
|
|
9614
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9615
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9616
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9186
9617
|
* @throws If the node is invalid
|
|
9187
9618
|
*/
|
|
9188
9619
|
function convertLegacyMatchesCss(node) {
|
|
9189
|
-
|
|
9190
|
-
if (
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9620
|
+
// Check conversion indications before doing any heavy work
|
|
9621
|
+
if (node.type !== CssTreeNodeType.PseudoClassSelector || !LEGACY_MATCHES_CSS_NAMES.has(node.name)) {
|
|
9622
|
+
return createConversionResult(node, false);
|
|
9623
|
+
}
|
|
9624
|
+
const nodeClone = clone(node);
|
|
9625
|
+
if (!nodeClone.children || nodeClone.children.length < 1) {
|
|
9626
|
+
throw new Error(`Invalid ${node.name} pseudo-class: missing argument`);
|
|
9627
|
+
}
|
|
9628
|
+
// Rename the pseudo-class
|
|
9629
|
+
nodeClone.name = PseudoClasses.MatchesCss;
|
|
9630
|
+
// Remove the 'matches-css-' prefix to get the direction
|
|
9631
|
+
const direction = node.name.substring(PseudoClasses.MatchesCss.length + 1);
|
|
9632
|
+
// Add the direction to the first raw argument
|
|
9633
|
+
const arg = nodeClone.children[0];
|
|
9634
|
+
// Check argument
|
|
9635
|
+
if (!arg) {
|
|
9636
|
+
throw new Error(`Invalid ${node.name} pseudo-class: argument shouldn't be null`);
|
|
9637
|
+
}
|
|
9638
|
+
if (arg.type !== CssTreeNodeType.Raw) {
|
|
9639
|
+
throw new Error(`Invalid ${node.name} pseudo-class: unexpected argument type`);
|
|
9640
|
+
}
|
|
9641
|
+
// Add the direction as the first argument
|
|
9642
|
+
arg.value = `${direction},${arg.value}`;
|
|
9643
|
+
return createConversionResult(nodeClone, true);
|
|
9213
9644
|
}
|
|
9214
9645
|
/**
|
|
9215
9646
|
* Converts legacy Extended CSS selectors to the modern Extended CSS syntax.
|
|
@@ -9219,16 +9650,40 @@ function convertLegacyMatchesCss(node) {
|
|
|
9219
9650
|
* - `[-ext-matches-css-before=...]` → `:matches-css(before, ...)`
|
|
9220
9651
|
*
|
|
9221
9652
|
* @param selectorList Selector list AST to convert
|
|
9222
|
-
* @returns
|
|
9653
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9654
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9655
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9223
9656
|
*/
|
|
9224
9657
|
function convertFromLegacyExtendedCss(selectorList) {
|
|
9225
|
-
//
|
|
9226
|
-
const
|
|
9658
|
+
// Check conversion indications before doing any heavy work
|
|
9659
|
+
const hasIndicator = find(
|
|
9660
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9661
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9662
|
+
selectorList, (node) => {
|
|
9663
|
+
if (node.type === CssTreeNodeType.PseudoClassSelector) {
|
|
9664
|
+
return LEGACY_EXT_CSS_INDICATOR_PSEUDO_NAMES.has(node.name);
|
|
9665
|
+
}
|
|
9666
|
+
if (node.type === CssTreeNodeType.AttributeSelector) {
|
|
9667
|
+
return node.name.name.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX);
|
|
9668
|
+
}
|
|
9669
|
+
return false;
|
|
9670
|
+
});
|
|
9671
|
+
if (!hasIndicator) {
|
|
9672
|
+
return createConversionResult(selectorList, false);
|
|
9673
|
+
}
|
|
9674
|
+
const selectorListClone = clone(selectorList);
|
|
9675
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9676
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9227
9677
|
walk(selectorListClone, {
|
|
9678
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9679
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9228
9680
|
leave: (node) => {
|
|
9229
9681
|
// :matches-css-before(arg) → :matches-css(before,arg)
|
|
9230
9682
|
// :matches-css-after(arg) → :matches-css(after,arg)
|
|
9231
|
-
convertLegacyMatchesCss(node);
|
|
9683
|
+
const convertedLegacyExtCss = convertLegacyMatchesCss(node);
|
|
9684
|
+
if (convertedLegacyExtCss.isConverted) {
|
|
9685
|
+
Object.assign(node, convertedLegacyExtCss.result);
|
|
9686
|
+
}
|
|
9232
9687
|
// [-ext-name=...] → :name(...)
|
|
9233
9688
|
// [-ext-name='...'] → :name(...)
|
|
9234
9689
|
// [-ext-name="..."] → :name(...)
|
|
@@ -9242,7 +9697,7 @@ function convertFromLegacyExtendedCss(selectorList) {
|
|
|
9242
9697
|
// Remove the '-ext-' prefix to get the pseudo-class name
|
|
9243
9698
|
const name = node.name.name.substring(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX.length);
|
|
9244
9699
|
// Prepare the children list for the pseudo-class node
|
|
9245
|
-
const children =
|
|
9700
|
+
const children = [];
|
|
9246
9701
|
// TODO: Change String node to Raw node to drop the quotes.
|
|
9247
9702
|
// The structure of the node is the same, just the type
|
|
9248
9703
|
// is different and generate() will generate the quotes
|
|
@@ -9255,7 +9710,7 @@ function convertFromLegacyExtendedCss(selectorList) {
|
|
|
9255
9710
|
// For example, if the input is [-ext-has="> .selector"], then
|
|
9256
9711
|
// we need to parse "> .selector" as a selector instead of string
|
|
9257
9712
|
// it as a raw value
|
|
9258
|
-
if ([
|
|
9713
|
+
if ([PseudoClasses.Has, PseudoClasses.Not].includes(name)) {
|
|
9259
9714
|
// Get the value of the attribute selector
|
|
9260
9715
|
const { value } = node;
|
|
9261
9716
|
// If the value is an identifier, then simply push it to the
|
|
@@ -9265,10 +9720,12 @@ function convertFromLegacyExtendedCss(selectorList) {
|
|
|
9265
9720
|
}
|
|
9266
9721
|
else if (value.type === CssTreeNodeType.String) {
|
|
9267
9722
|
// Parse the value as a selector
|
|
9268
|
-
const parsedChildren = CssTree.
|
|
9723
|
+
const parsedChildren = CssTree.parsePlain(value.value, CssTreeParserContext.selectorList);
|
|
9269
9724
|
// Don't forget convert the parsed AST again, because
|
|
9270
9725
|
// it was a raw string before
|
|
9271
|
-
|
|
9726
|
+
const convertedChildren = convertFromLegacyExtendedCss(parsedChildren);
|
|
9727
|
+
// Push the converted children to the list
|
|
9728
|
+
children.push(convertedChildren.result);
|
|
9272
9729
|
}
|
|
9273
9730
|
}
|
|
9274
9731
|
else {
|
|
@@ -9295,14 +9752,12 @@ function convertFromLegacyExtendedCss(selectorList) {
|
|
|
9295
9752
|
children,
|
|
9296
9753
|
};
|
|
9297
9754
|
// Handle this case: [-ext-matches-css-before=...] → :matches-css(before,...)
|
|
9298
|
-
convertLegacyMatchesCss(pseudoNode);
|
|
9299
|
-
|
|
9300
|
-
// keep the reference to the original node
|
|
9301
|
-
Object.assign(node, pseudoNode);
|
|
9755
|
+
const convertedPseudoNode = convertLegacyMatchesCss(pseudoNode);
|
|
9756
|
+
Object.assign(node, convertedPseudoNode.isConverted ? convertedPseudoNode.result : pseudoNode);
|
|
9302
9757
|
}
|
|
9303
9758
|
},
|
|
9304
9759
|
});
|
|
9305
|
-
return selectorListClone;
|
|
9760
|
+
return createConversionResult(selectorListClone, true);
|
|
9306
9761
|
}
|
|
9307
9762
|
/**
|
|
9308
9763
|
* CSS selector converter
|
|
@@ -9314,32 +9769,51 @@ class CssSelectorConverter extends ConverterBase {
|
|
|
9314
9769
|
* Converts Extended CSS elements to AdGuard-compatible ones
|
|
9315
9770
|
*
|
|
9316
9771
|
* @param selectorList Selector list to convert
|
|
9317
|
-
* @returns
|
|
9772
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9773
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9774
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9318
9775
|
* @throws If the rule is invalid or incompatible
|
|
9319
9776
|
*/
|
|
9320
9777
|
static convertToAdg(selectorList) {
|
|
9321
9778
|
// First, convert
|
|
9322
9779
|
// - legacy Extended CSS selectors to the modern Extended CSS syntax and
|
|
9323
9780
|
// - some pseudo-classes to pseudo-elements
|
|
9324
|
-
const
|
|
9781
|
+
const legacyExtCssConverted = convertFromLegacyExtendedCss(selectorList);
|
|
9782
|
+
const pseudoElementsConverted = convertToPseudoElements(legacyExtCssConverted.result);
|
|
9783
|
+
const hasIndicator = legacyExtCssConverted.isConverted || pseudoElementsConverted.isConverted || find(
|
|
9784
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9785
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9786
|
+
selectorList,
|
|
9787
|
+
// eslint-disable-next-line max-len
|
|
9788
|
+
(node) => node.type === CssTreeNodeType.PseudoClassSelector && CSS_CONVERSION_INDICATOR_PSEUDO_NAMES.has(node.name));
|
|
9789
|
+
if (!hasIndicator) {
|
|
9790
|
+
return createConversionResult(selectorList, false);
|
|
9791
|
+
}
|
|
9792
|
+
const selectorListClone = legacyExtCssConverted.isConverted || pseudoElementsConverted.isConverted
|
|
9793
|
+
? pseudoElementsConverted.result
|
|
9794
|
+
: clone(selectorList);
|
|
9325
9795
|
// Then, convert some Extended CSS pseudo-classes to AdGuard-compatible ones
|
|
9796
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9797
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9326
9798
|
walk(selectorListClone, {
|
|
9799
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9800
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9327
9801
|
leave: (node) => {
|
|
9328
9802
|
if (node.type === CssTreeNodeType.PseudoClassSelector) {
|
|
9329
9803
|
// :-abp-contains(...) → :contains(...)
|
|
9330
9804
|
// :has-text(...) → :contains(...)
|
|
9331
|
-
if (node.name ===
|
|
9332
|
-
CssTree.renamePseudoClass(node,
|
|
9805
|
+
if (node.name === PseudoClasses.AbpContains || node.name === PseudoClasses.HasText) {
|
|
9806
|
+
CssTree.renamePseudoClass(node, PseudoClasses.Contains);
|
|
9333
9807
|
}
|
|
9334
9808
|
// :-abp-has(...) → :has(...)
|
|
9335
|
-
if (node.name ===
|
|
9336
|
-
CssTree.renamePseudoClass(node,
|
|
9809
|
+
if (node.name === PseudoClasses.AbpHas) {
|
|
9810
|
+
CssTree.renamePseudoClass(node, PseudoClasses.Has);
|
|
9337
9811
|
}
|
|
9338
9812
|
// TODO: check uBO's `:others()` and `:watch-attr()` pseudo-classes
|
|
9339
9813
|
}
|
|
9340
9814
|
},
|
|
9341
9815
|
});
|
|
9342
|
-
return selectorListClone;
|
|
9816
|
+
return createConversionResult(selectorListClone, true);
|
|
9343
9817
|
}
|
|
9344
9818
|
}
|
|
9345
9819
|
|
|
@@ -9356,27 +9830,39 @@ class CssInjectionRuleConverter extends RuleConverterBase {
|
|
|
9356
9830
|
* Converts a CSS injection rule to AdGuard format, if possible.
|
|
9357
9831
|
*
|
|
9358
9832
|
* @param rule Rule node to convert
|
|
9359
|
-
* @returns
|
|
9833
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9834
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9835
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9360
9836
|
* @throws If the rule is invalid or cannot be converted
|
|
9361
9837
|
*/
|
|
9362
9838
|
static convertToAdg(rule) {
|
|
9363
|
-
|
|
9364
|
-
|
|
9839
|
+
const separator = rule.separator.value;
|
|
9840
|
+
let convertedSeparator = separator;
|
|
9365
9841
|
// Change the separator if the rule contains ExtendedCSS selectors
|
|
9366
|
-
if (CssTree.hasAnySelectorExtendedCssNode(
|
|
9367
|
-
|
|
9842
|
+
if (CssTree.hasAnySelectorExtendedCssNode(rule.body.selectorList) || rule.body.remove) {
|
|
9843
|
+
convertedSeparator = rule.exception
|
|
9368
9844
|
? CosmeticRuleSeparator.AdgExtendedCssInjectionException
|
|
9369
9845
|
: CosmeticRuleSeparator.AdgExtendedCssInjection;
|
|
9370
9846
|
}
|
|
9371
9847
|
else {
|
|
9372
|
-
|
|
9848
|
+
convertedSeparator = rule.exception
|
|
9373
9849
|
? CosmeticRuleSeparator.AdgCssInjectionException
|
|
9374
9850
|
: CosmeticRuleSeparator.AdgCssInjection;
|
|
9375
9851
|
}
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9852
|
+
const convertedSelectorList = CssSelectorConverter.convertToAdg(rule.body.selectorList);
|
|
9853
|
+
// Check if the rule needs to be converted
|
|
9854
|
+
if (!(rule.syntax === AdblockSyntax.Common || rule.syntax === AdblockSyntax.Adg)
|
|
9855
|
+
|| separator !== convertedSeparator
|
|
9856
|
+
|| convertedSelectorList.isConverted) {
|
|
9857
|
+
// TODO: Replace with custom clone method
|
|
9858
|
+
const ruleClone = clone(rule);
|
|
9859
|
+
ruleClone.syntax = AdblockSyntax.Adg;
|
|
9860
|
+
ruleClone.separator.value = convertedSeparator;
|
|
9861
|
+
ruleClone.body.selectorList = convertedSelectorList.result;
|
|
9862
|
+
return createNodeConversionResult([ruleClone], true);
|
|
9863
|
+
}
|
|
9864
|
+
// Otherwise, return the original rule
|
|
9865
|
+
return createNodeConversionResult([rule], false);
|
|
9380
9866
|
}
|
|
9381
9867
|
}
|
|
9382
9868
|
|
|
@@ -9393,27 +9879,39 @@ class ElementHidingRuleConverter extends RuleConverterBase {
|
|
|
9393
9879
|
* Converts an element hiding rule to AdGuard format, if possible.
|
|
9394
9880
|
*
|
|
9395
9881
|
* @param rule Rule node to convert
|
|
9396
|
-
* @returns
|
|
9882
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9883
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9884
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9397
9885
|
* @throws If the rule is invalid or cannot be converted
|
|
9398
9886
|
*/
|
|
9399
9887
|
static convertToAdg(rule) {
|
|
9400
|
-
|
|
9401
|
-
|
|
9888
|
+
const separator = rule.separator.value;
|
|
9889
|
+
let convertedSeparator = separator;
|
|
9402
9890
|
// Change the separator if the rule contains ExtendedCSS selectors
|
|
9403
|
-
if (CssTree.hasAnySelectorExtendedCssNode(
|
|
9404
|
-
|
|
9891
|
+
if (CssTree.hasAnySelectorExtendedCssNode(rule.body.selectorList)) {
|
|
9892
|
+
convertedSeparator = rule.exception
|
|
9405
9893
|
? CosmeticRuleSeparator.ExtendedElementHidingException
|
|
9406
9894
|
: CosmeticRuleSeparator.ExtendedElementHiding;
|
|
9407
9895
|
}
|
|
9408
9896
|
else {
|
|
9409
|
-
|
|
9897
|
+
convertedSeparator = rule.exception
|
|
9410
9898
|
? CosmeticRuleSeparator.ElementHidingException
|
|
9411
9899
|
: CosmeticRuleSeparator.ElementHiding;
|
|
9412
9900
|
}
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9901
|
+
const convertedSelectorList = CssSelectorConverter.convertToAdg(rule.body.selectorList);
|
|
9902
|
+
// Check if the rule needs to be converted
|
|
9903
|
+
if (!(rule.syntax === AdblockSyntax.Common || rule.syntax === AdblockSyntax.Adg)
|
|
9904
|
+
|| separator !== convertedSeparator
|
|
9905
|
+
|| convertedSelectorList.isConverted) {
|
|
9906
|
+
// TODO: Replace with custom clone method
|
|
9907
|
+
const ruleClone = clone(rule);
|
|
9908
|
+
ruleClone.syntax = AdblockSyntax.Adg;
|
|
9909
|
+
ruleClone.separator.value = convertedSeparator;
|
|
9910
|
+
ruleClone.body.selectorList = convertedSelectorList.result;
|
|
9911
|
+
return createNodeConversionResult([ruleClone], true);
|
|
9912
|
+
}
|
|
9913
|
+
// Otherwise, return the original rule
|
|
9914
|
+
return createNodeConversionResult([rule], false);
|
|
9417
9915
|
}
|
|
9418
9916
|
}
|
|
9419
9917
|
|
|
@@ -9441,7 +9939,7 @@ function createNetworkRuleNode(pattern, modifiers = undefined, exception = false
|
|
|
9441
9939
|
},
|
|
9442
9940
|
};
|
|
9443
9941
|
if (!isUndefined(modifiers)) {
|
|
9444
|
-
result.modifiers =
|
|
9942
|
+
result.modifiers = clone(modifiers);
|
|
9445
9943
|
}
|
|
9446
9944
|
return result;
|
|
9447
9945
|
}
|
|
@@ -9461,32 +9959,37 @@ class HeaderRemovalRuleConverter extends RuleConverterBase {
|
|
|
9461
9959
|
* Converts a header removal rule to AdGuard syntax, if possible.
|
|
9462
9960
|
*
|
|
9463
9961
|
* @param rule Rule node to convert
|
|
9464
|
-
* @returns
|
|
9962
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9963
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9964
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9465
9965
|
* @throws If the rule is invalid or cannot be converted
|
|
9966
|
+
* @example
|
|
9967
|
+
* If the input rule is:
|
|
9968
|
+
* ```adblock
|
|
9969
|
+
* example.com##^responseheader(header-name)
|
|
9970
|
+
* ```
|
|
9971
|
+
* The output will be:
|
|
9972
|
+
* ```adblock
|
|
9973
|
+
* ||example.com^$removeheader=header-name
|
|
9974
|
+
* ```
|
|
9466
9975
|
*/
|
|
9467
9976
|
static convertToAdg(rule) {
|
|
9468
|
-
// Clone the provided AST node to avoid side effects
|
|
9469
|
-
const ruleNode = cloneDeep(rule);
|
|
9470
9977
|
// TODO: Add support for ABP syntax once it starts supporting header removal rules
|
|
9471
|
-
//
|
|
9472
|
-
if (
|
|
9473
|
-
||
|
|
9474
|
-
||
|
|
9475
|
-
||
|
|
9476
|
-
|
|
9978
|
+
// Leave the rule as is if it's not a header removal rule
|
|
9979
|
+
if (rule.category !== RuleCategory.Cosmetic
|
|
9980
|
+
|| rule.type !== CosmeticRuleType.HtmlFilteringRule
|
|
9981
|
+
|| rule.body.body.type !== CssTreeNodeType.Function
|
|
9982
|
+
|| rule.body.body.name !== UBO_RESPONSEHEADER_MARKER) {
|
|
9983
|
+
return createNodeConversionResult([rule], false);
|
|
9477
9984
|
}
|
|
9478
9985
|
// Prepare network rule pattern
|
|
9479
|
-
|
|
9480
|
-
if (
|
|
9986
|
+
const pattern = [];
|
|
9987
|
+
if (rule.domains.children.length === 1) {
|
|
9481
9988
|
// If the rule has only one domain, we can use a simple network rule pattern:
|
|
9482
9989
|
// ||single-domain-from-the-rule^
|
|
9483
|
-
pattern
|
|
9484
|
-
ADBLOCK_URL_START,
|
|
9485
|
-
ruleNode.domains.children[0].value,
|
|
9486
|
-
ADBLOCK_URL_SEPARATOR,
|
|
9487
|
-
].join(EMPTY);
|
|
9990
|
+
pattern.push(ADBLOCK_URL_START, rule.domains.children[0].value, ADBLOCK_URL_SEPARATOR);
|
|
9488
9991
|
}
|
|
9489
|
-
else if (
|
|
9992
|
+
else if (rule.domains.children.length > 1) {
|
|
9490
9993
|
// TODO: Add support for multiple domains, for example:
|
|
9491
9994
|
// example.com,example.org,example.net##^responseheader(header-name)
|
|
9492
9995
|
// We should consider allowing $domain with $removeheader modifier,
|
|
@@ -9496,13 +9999,13 @@ class HeaderRemovalRuleConverter extends RuleConverterBase {
|
|
|
9496
9999
|
}
|
|
9497
10000
|
// Prepare network rule modifiers
|
|
9498
10001
|
const modifiers = createModifierListNode();
|
|
9499
|
-
modifiers.children.push(createModifierNode(ADG_REMOVEHEADER_MODIFIER, CssTree.
|
|
10002
|
+
modifiers.children.push(createModifierNode(ADG_REMOVEHEADER_MODIFIER, CssTree.generateFunctionPlainValue(rule.body.body)));
|
|
9500
10003
|
// Construct the network rule
|
|
9501
|
-
return [
|
|
9502
|
-
createNetworkRuleNode(pattern, modifiers,
|
|
10004
|
+
return createNodeConversionResult([
|
|
10005
|
+
createNetworkRuleNode(pattern.join(EMPTY), modifiers,
|
|
9503
10006
|
// Copy the exception flag
|
|
9504
|
-
|
|
9505
|
-
];
|
|
10007
|
+
rule.exception, AdblockSyntax.Adg),
|
|
10008
|
+
], true);
|
|
9506
10009
|
}
|
|
9507
10010
|
}
|
|
9508
10011
|
|
|
@@ -9519,48 +10022,69 @@ class CosmeticRuleConverter extends RuleConverterBase {
|
|
|
9519
10022
|
* Converts a cosmetic rule to AdGuard syntax, if possible.
|
|
9520
10023
|
*
|
|
9521
10024
|
* @param rule Rule node to convert
|
|
9522
|
-
* @returns
|
|
10025
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
10026
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
10027
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9523
10028
|
* @throws If the rule is invalid or cannot be converted
|
|
9524
10029
|
*/
|
|
9525
10030
|
static convertToAdg(rule) {
|
|
9526
|
-
|
|
9527
|
-
const ruleNode = cloneDeep(rule);
|
|
9528
|
-
// Convert cosmetic rule modifiers
|
|
9529
|
-
if (ruleNode.modifiers) {
|
|
9530
|
-
if (ruleNode.syntax === AdblockSyntax.Ubo) {
|
|
9531
|
-
// uBO doesn't support this rule:
|
|
9532
|
-
// example.com##+js(set-constant.js, foo, bar):matches-path(/baz)
|
|
9533
|
-
if (ruleNode.type === CosmeticRuleType.ScriptletInjectionRule) {
|
|
9534
|
-
throw new RuleConversionError('uBO scriptlet injection rules don\'t support cosmetic rule modifiers');
|
|
9535
|
-
}
|
|
9536
|
-
ruleNode.modifiers = AdgCosmeticRuleModifierConverter.convertFromUbo(ruleNode.modifiers);
|
|
9537
|
-
}
|
|
9538
|
-
else if (ruleNode.syntax === AdblockSyntax.Abp) {
|
|
9539
|
-
// TODO: Implement once ABP starts supporting cosmetic rule modifiers
|
|
9540
|
-
throw new RuleConversionError('ABP don\'t support cosmetic rule modifiers');
|
|
9541
|
-
}
|
|
9542
|
-
}
|
|
10031
|
+
let subconverterResult;
|
|
9543
10032
|
// Convert cosmetic rule based on its type
|
|
9544
|
-
switch (
|
|
10033
|
+
switch (rule.type) {
|
|
9545
10034
|
case CosmeticRuleType.ElementHidingRule:
|
|
9546
|
-
|
|
10035
|
+
subconverterResult = ElementHidingRuleConverter.convertToAdg(rule);
|
|
10036
|
+
break;
|
|
9547
10037
|
case CosmeticRuleType.ScriptletInjectionRule:
|
|
9548
|
-
|
|
10038
|
+
subconverterResult = ScriptletRuleConverter.convertToAdg(rule);
|
|
10039
|
+
break;
|
|
9549
10040
|
case CosmeticRuleType.CssInjectionRule:
|
|
9550
|
-
|
|
10041
|
+
subconverterResult = CssInjectionRuleConverter.convertToAdg(rule);
|
|
10042
|
+
break;
|
|
9551
10043
|
case CosmeticRuleType.HtmlFilteringRule:
|
|
9552
10044
|
// Handle special case: uBO response header filtering rule
|
|
9553
|
-
if (
|
|
9554
|
-
&&
|
|
9555
|
-
|
|
10045
|
+
if (rule.body.body.type === CssTreeNodeType.Function
|
|
10046
|
+
&& rule.body.body.name === UBO_RESPONSEHEADER_MARKER) {
|
|
10047
|
+
subconverterResult = HeaderRemovalRuleConverter.convertToAdg(rule);
|
|
10048
|
+
}
|
|
10049
|
+
else {
|
|
10050
|
+
subconverterResult = HtmlRuleConverter.convertToAdg(rule);
|
|
9556
10051
|
}
|
|
9557
|
-
|
|
9558
|
-
// Note: Currently, only ADG supports JS injection rules
|
|
10052
|
+
break;
|
|
10053
|
+
// Note: Currently, only ADG supports JS injection rules, so we don't need to convert them
|
|
9559
10054
|
case CosmeticRuleType.JsInjectionRule:
|
|
9560
|
-
|
|
10055
|
+
subconverterResult = createNodeConversionResult([rule], false);
|
|
10056
|
+
break;
|
|
9561
10057
|
default:
|
|
9562
10058
|
throw new RuleConversionError('Unsupported cosmetic rule type');
|
|
9563
10059
|
}
|
|
10060
|
+
let convertedModifiers;
|
|
10061
|
+
// Convert cosmetic rule modifiers, if any
|
|
10062
|
+
if (rule.modifiers) {
|
|
10063
|
+
if (rule.syntax === AdblockSyntax.Ubo) {
|
|
10064
|
+
// uBO doesn't support this rule:
|
|
10065
|
+
// example.com##+js(set-constant.js, foo, bar):matches-path(/baz)
|
|
10066
|
+
if (rule.type === CosmeticRuleType.ScriptletInjectionRule) {
|
|
10067
|
+
throw new RuleConversionError('uBO scriptlet injection rules don\'t support cosmetic rule modifiers');
|
|
10068
|
+
}
|
|
10069
|
+
convertedModifiers = AdgCosmeticRuleModifierConverter.convertFromUbo(rule.modifiers);
|
|
10070
|
+
}
|
|
10071
|
+
else if (rule.syntax === AdblockSyntax.Abp) {
|
|
10072
|
+
// TODO: Implement once ABP starts supporting cosmetic rule modifiers
|
|
10073
|
+
throw new RuleConversionError('ABP don\'t support cosmetic rule modifiers');
|
|
10074
|
+
}
|
|
10075
|
+
}
|
|
10076
|
+
if ((subconverterResult.result.length > 1 || subconverterResult.isConverted)
|
|
10077
|
+
|| (convertedModifiers && convertedModifiers.isConverted)) {
|
|
10078
|
+
// Add modifier list to the subconverter result rules
|
|
10079
|
+
subconverterResult.result.forEach((subconverterRule) => {
|
|
10080
|
+
if (convertedModifiers && subconverterRule.category === RuleCategory.Cosmetic) {
|
|
10081
|
+
// eslint-disable-next-line no-param-reassign
|
|
10082
|
+
subconverterRule.modifiers = convertedModifiers.result;
|
|
10083
|
+
}
|
|
10084
|
+
});
|
|
10085
|
+
return subconverterResult;
|
|
10086
|
+
}
|
|
10087
|
+
return createNodeConversionResult([rule], false);
|
|
9564
10088
|
}
|
|
9565
10089
|
}
|
|
9566
10090
|
|
|
@@ -9632,17 +10156,16 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
9632
10156
|
* Converts a network rule modifier list to AdGuard format, if possible.
|
|
9633
10157
|
*
|
|
9634
10158
|
* @param modifierList Network rule modifier list node to convert
|
|
9635
|
-
* @returns
|
|
10159
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
10160
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
10161
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9636
10162
|
* @throws If the conversion is not possible
|
|
9637
10163
|
*/
|
|
9638
10164
|
static convertToAdg(modifierList) {
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
const cspValues = [];
|
|
9644
|
-
modifierListNode.children.forEach((modifierNode) => {
|
|
9645
|
-
// Handle regular modifiers conversion and $csp modifiers collection
|
|
10165
|
+
const conversionMap = new MultiValueMap();
|
|
10166
|
+
// Special case: $csp modifier
|
|
10167
|
+
let cspCount = 0;
|
|
10168
|
+
modifierList.children.forEach((modifierNode, index) => {
|
|
9646
10169
|
const modifierConversions = ADG_CONVERSION_MAP.get(modifierNode.modifier.value);
|
|
9647
10170
|
if (modifierConversions) {
|
|
9648
10171
|
for (const modifierConversion of modifierConversions) {
|
|
@@ -9655,17 +10178,14 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
9655
10178
|
const value = modifierConversion.value
|
|
9656
10179
|
? modifierConversion.value(modifierNode.value?.value)
|
|
9657
10180
|
: modifierNode.value?.value;
|
|
9658
|
-
if
|
|
9659
|
-
|
|
9660
|
-
|
|
10181
|
+
// Check if the name or the value is different from the original modifier
|
|
10182
|
+
// If so, add the converted modifier to the list
|
|
10183
|
+
if (name !== modifierNode.modifier.value || value !== modifierNode.value?.value) {
|
|
10184
|
+
conversionMap.add(index, createModifierNode(name, value, exception));
|
|
9661
10185
|
}
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
const existingModifier = convertedModifierList.children.find((m) => m.modifier.value === name && m.exception === exception && m.value?.value === value);
|
|
9666
|
-
if (!existingModifier) {
|
|
9667
|
-
convertedModifierList.children.push(createModifierNode(name, value, exception));
|
|
9668
|
-
}
|
|
10186
|
+
// Special case: $csp modifier
|
|
10187
|
+
if (name === CSP_MODIFIER) {
|
|
10188
|
+
cspCount += 1;
|
|
9669
10189
|
}
|
|
9670
10190
|
}
|
|
9671
10191
|
return;
|
|
@@ -9688,26 +10208,52 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
9688
10208
|
// Try to convert the redirect resource name to ADG format
|
|
9689
10209
|
// This function returns undefined if the resource name is unknown
|
|
9690
10210
|
const convertedRedirectResource = redirects.convertRedirectNameToAdg(redirectResource);
|
|
9691
|
-
|
|
9692
|
-
// If
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
&& m.exception === modifierNode.exception
|
|
9701
|
-
&& m.value?.value === modifierNode.value?.value);
|
|
9702
|
-
if (!existingModifier) {
|
|
9703
|
-
convertedModifierList.children.push(modifierNode);
|
|
10211
|
+
// Check if the modifier name or the redirect resource name is different from the original modifier
|
|
10212
|
+
// If so, add the converted modifier to the list
|
|
10213
|
+
if (modifierName !== modifierNode.modifier.value
|
|
10214
|
+
|| (convertedRedirectResource !== undefined && convertedRedirectResource !== redirectResource)) {
|
|
10215
|
+
conversionMap.add(index, createModifierNode(modifierName,
|
|
10216
|
+
// If the redirect resource name is unknown, fall back to the original one
|
|
10217
|
+
// Later, the validator will throw an error if the resource name is invalid
|
|
10218
|
+
convertedRedirectResource || redirectResource, modifierNode.exception));
|
|
10219
|
+
}
|
|
9704
10220
|
}
|
|
9705
10221
|
});
|
|
9706
|
-
//
|
|
9707
|
-
if (
|
|
9708
|
-
|
|
10222
|
+
// Prepare the result if there are any converted modifiers or $csp modifiers
|
|
10223
|
+
if (conversionMap.size || cspCount) {
|
|
10224
|
+
const modifierListClone = cloneModifierListNode(modifierList);
|
|
10225
|
+
// Replace the original modifiers with the converted ones
|
|
10226
|
+
// One modifier may be replaced with multiple modifiers, so we need to flatten the array
|
|
10227
|
+
modifierListClone.children = modifierListClone.children.map((modifierNode, index) => {
|
|
10228
|
+
const conversionRecord = conversionMap.get(index);
|
|
10229
|
+
if (conversionRecord) {
|
|
10230
|
+
return conversionRecord;
|
|
10231
|
+
}
|
|
10232
|
+
return modifierNode;
|
|
10233
|
+
}).flat();
|
|
10234
|
+
// Special case: $csp modifier: merge multiple $csp modifiers into one
|
|
10235
|
+
// and put it at the end of the modifier list
|
|
10236
|
+
if (cspCount) {
|
|
10237
|
+
const cspValues = [];
|
|
10238
|
+
modifierListClone.children = modifierListClone.children.filter((modifierNode) => {
|
|
10239
|
+
if (modifierNode.modifier.value === CSP_MODIFIER) {
|
|
10240
|
+
if (!modifierNode.value?.value) {
|
|
10241
|
+
throw new RuleConversionError('$csp modifier value is missing');
|
|
10242
|
+
}
|
|
10243
|
+
cspValues.push(modifierNode.value?.value);
|
|
10244
|
+
return false;
|
|
10245
|
+
}
|
|
10246
|
+
return true;
|
|
10247
|
+
});
|
|
10248
|
+
modifierListClone.children.push(createModifierNode(CSP_MODIFIER, cspValues.join(CSP_SEPARATOR)));
|
|
10249
|
+
}
|
|
10250
|
+
// Before returning the result, remove duplicated modifiers
|
|
10251
|
+
modifierListClone.children = modifierListClone.children.filter((modifierNode, index, self) => self.findIndex((m) => m.modifier.value === modifierNode.modifier.value
|
|
10252
|
+
&& m.exception === modifierNode.exception
|
|
10253
|
+
&& m.value?.value === modifierNode.value?.value) === index);
|
|
10254
|
+
return createConversionResult(modifierListClone, true);
|
|
9709
10255
|
}
|
|
9710
|
-
return
|
|
10256
|
+
return createConversionResult(modifierList, false);
|
|
9711
10257
|
}
|
|
9712
10258
|
}
|
|
9713
10259
|
|
|
@@ -9724,17 +10270,35 @@ class NetworkRuleConverter extends RuleConverterBase {
|
|
|
9724
10270
|
* Converts a network rule to AdGuard format, if possible.
|
|
9725
10271
|
*
|
|
9726
10272
|
* @param rule Rule node to convert
|
|
9727
|
-
* @returns
|
|
10273
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
10274
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
10275
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9728
10276
|
* @throws If the rule is invalid or cannot be converted
|
|
9729
10277
|
*/
|
|
9730
10278
|
static convertToAdg(rule) {
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
10279
|
+
if (rule.modifiers) {
|
|
10280
|
+
const modifiers = NetworkRuleModifierListConverter.convertToAdg(rule.modifiers);
|
|
10281
|
+
// If the object reference is different, it means that the modifiers were converted
|
|
10282
|
+
// In this case, we should clone the entire rule and replace the modifiers with the converted ones
|
|
10283
|
+
if (modifiers.isConverted) {
|
|
10284
|
+
return {
|
|
10285
|
+
result: [{
|
|
10286
|
+
category: RuleCategory.Network,
|
|
10287
|
+
type: 'NetworkRule',
|
|
10288
|
+
syntax: rule.syntax,
|
|
10289
|
+
exception: rule.exception,
|
|
10290
|
+
pattern: {
|
|
10291
|
+
type: 'Value',
|
|
10292
|
+
value: rule.pattern.value,
|
|
10293
|
+
},
|
|
10294
|
+
modifiers: modifiers.result,
|
|
10295
|
+
}],
|
|
10296
|
+
isConverted: true,
|
|
10297
|
+
};
|
|
10298
|
+
}
|
|
9736
10299
|
}
|
|
9737
|
-
return
|
|
10300
|
+
// If the modifiers were not converted, return the original rule
|
|
10301
|
+
return createNodeConversionResult([rule], false);
|
|
9738
10302
|
}
|
|
9739
10303
|
}
|
|
9740
10304
|
|
|
@@ -9755,48 +10319,27 @@ class RuleConverter extends RuleConverterBase {
|
|
|
9755
10319
|
* Converts an adblock filtering rule to AdGuard format, if possible.
|
|
9756
10320
|
*
|
|
9757
10321
|
* @param rule Rule node to convert
|
|
9758
|
-
* @returns
|
|
10322
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
10323
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
10324
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9759
10325
|
* @throws If the rule is invalid or cannot be converted
|
|
9760
10326
|
*/
|
|
9761
10327
|
static convertToAdg(rule) {
|
|
9762
|
-
// Clone the provided AST node to avoid side effects
|
|
9763
|
-
const ruleNode = cloneDeep(rule);
|
|
9764
10328
|
// Delegate conversion to the corresponding sub-converter
|
|
9765
10329
|
// based on the rule category
|
|
9766
|
-
switch (
|
|
10330
|
+
switch (rule.category) {
|
|
9767
10331
|
case RuleCategory.Comment:
|
|
9768
|
-
return CommentRuleConverter.convertToAdg(
|
|
10332
|
+
return CommentRuleConverter.convertToAdg(rule);
|
|
9769
10333
|
case RuleCategory.Cosmetic:
|
|
9770
|
-
return CosmeticRuleConverter.convertToAdg(
|
|
10334
|
+
return CosmeticRuleConverter.convertToAdg(rule);
|
|
9771
10335
|
case RuleCategory.Network:
|
|
9772
|
-
return NetworkRuleConverter.convertToAdg(
|
|
10336
|
+
return NetworkRuleConverter.convertToAdg(rule);
|
|
9773
10337
|
default:
|
|
9774
|
-
throw new RuleConversionError(`Unknown rule category: ${
|
|
10338
|
+
throw new RuleConversionError(`Unknown rule category: ${rule.category}`);
|
|
9775
10339
|
}
|
|
9776
10340
|
}
|
|
9777
10341
|
}
|
|
9778
10342
|
|
|
9779
|
-
/**
|
|
9780
|
-
* @file Utility functions for working with filter list nodes
|
|
9781
|
-
*/
|
|
9782
|
-
/**
|
|
9783
|
-
* Creates a filter list node
|
|
9784
|
-
*
|
|
9785
|
-
* @param rules Rules to put in the list (optional, defaults to an empty list)
|
|
9786
|
-
* @returns Filter list node
|
|
9787
|
-
*/
|
|
9788
|
-
function createFilterListNode(rules = []) {
|
|
9789
|
-
const result = {
|
|
9790
|
-
type: 'FilterList',
|
|
9791
|
-
children: [],
|
|
9792
|
-
};
|
|
9793
|
-
// We need to clone the rules to avoid side effects
|
|
9794
|
-
if (rules.length > 0) {
|
|
9795
|
-
result.children = cloneDeep(rules);
|
|
9796
|
-
}
|
|
9797
|
-
return result;
|
|
9798
|
-
}
|
|
9799
|
-
|
|
9800
10343
|
/**
|
|
9801
10344
|
* @file Adblock filter list converter
|
|
9802
10345
|
*/
|
|
@@ -9815,18 +10358,133 @@ class FilterListConverter extends ConverterBase {
|
|
|
9815
10358
|
* Converts an adblock filter list to AdGuard format, if possible.
|
|
9816
10359
|
*
|
|
9817
10360
|
* @param filterListNode Filter list node to convert
|
|
9818
|
-
* @
|
|
9819
|
-
*
|
|
10361
|
+
* @param tolerant Indicates whether the converter should be tolerant to invalid rules. If enabled and a rule is
|
|
10362
|
+
* invalid, it will be left as is. If disabled and a rule is invalid, the whole filter list will be failed.
|
|
10363
|
+
* Defaults to `true`.
|
|
10364
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
10365
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
10366
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
10367
|
+
* @throws If the filter list is invalid or cannot be converted (if the tolerant mode is disabled)
|
|
10368
|
+
*/
|
|
10369
|
+
static convertToAdg(filterListNode, tolerant = true) {
|
|
10370
|
+
// Prepare a map to store the converted rules by their index in the filter list
|
|
10371
|
+
const conversionMap = new MultiValueMap();
|
|
10372
|
+
// Iterate over the filtering rules and convert them one by one, then add them to the result (one conversion may
|
|
10373
|
+
// result in multiple rules)
|
|
10374
|
+
for (let i = 0; i < filterListNode.children.length; i += 1) {
|
|
10375
|
+
try {
|
|
10376
|
+
const convertedRules = RuleConverter.convertToAdg(filterListNode.children[i]);
|
|
10377
|
+
// Add the converted rules to the map if they were converted
|
|
10378
|
+
if (convertedRules.isConverted) {
|
|
10379
|
+
conversionMap.add(i, ...convertedRules.result);
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
catch (error) {
|
|
10383
|
+
// If the tolerant mode is disabled, we should throw an error, this will fail the whole filter list
|
|
10384
|
+
// conversion.
|
|
10385
|
+
// Otherwise, we just ignore the error and leave the rule as is
|
|
10386
|
+
if (!tolerant) {
|
|
10387
|
+
throw error;
|
|
10388
|
+
}
|
|
10389
|
+
}
|
|
10390
|
+
}
|
|
10391
|
+
// If the conversion map is empty, it means that no rules were converted, so we can return the original filter
|
|
10392
|
+
// list
|
|
10393
|
+
if (conversionMap.size === 0) {
|
|
10394
|
+
return createConversionResult(filterListNode, false);
|
|
10395
|
+
}
|
|
10396
|
+
// Otherwise, create a new filter list node with the converted rules
|
|
10397
|
+
const convertedFilterList = {
|
|
10398
|
+
type: 'FilterList',
|
|
10399
|
+
children: [],
|
|
10400
|
+
};
|
|
10401
|
+
// Iterate over the original rules again and add them to the converted filter list, replacing the converted
|
|
10402
|
+
// rules with the new ones at the specified indexes
|
|
10403
|
+
for (let i = 0; i < filterListNode.children.length; i += 1) {
|
|
10404
|
+
const rules = conversionMap.get(i);
|
|
10405
|
+
if (rules) {
|
|
10406
|
+
convertedFilterList.children.push(...rules);
|
|
10407
|
+
}
|
|
10408
|
+
else {
|
|
10409
|
+
// We clone the unconverted rules to avoid mutating the original filter list if we return the converted
|
|
10410
|
+
// one
|
|
10411
|
+
convertedFilterList.children.push(clone(filterListNode.children[i]));
|
|
10412
|
+
}
|
|
10413
|
+
}
|
|
10414
|
+
return createConversionResult(convertedFilterList, true);
|
|
10415
|
+
}
|
|
10416
|
+
}
|
|
10417
|
+
|
|
10418
|
+
/**
|
|
10419
|
+
* @file Filter list converter for raw filter lists
|
|
10420
|
+
*
|
|
10421
|
+
* Technically, this is a wrapper around `FilterListConverter` that works with nodes instead of strings.
|
|
10422
|
+
*/
|
|
10423
|
+
/**
|
|
10424
|
+
* Adblock filter list converter class.
|
|
10425
|
+
*
|
|
10426
|
+
* You can use this class to convert string-based filter lists, since most of the converters work with nodes.
|
|
10427
|
+
* This class just provides an extra layer on top of the {@link FilterListConverter} and calls the parser/serializer
|
|
10428
|
+
* before/after the conversion internally.
|
|
10429
|
+
*
|
|
10430
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
10431
|
+
*/
|
|
10432
|
+
class RawFilterListConverter extends ConverterBase {
|
|
10433
|
+
/**
|
|
10434
|
+
* Converts an adblock filter list text to AdGuard format, if possible.
|
|
10435
|
+
*
|
|
10436
|
+
* @param rawFilterList Raw filter list text to convert
|
|
10437
|
+
* @param tolerant Indicates whether the converter should be tolerant to invalid rules. If enabled and a rule is
|
|
10438
|
+
* invalid, it will be left as is. If disabled and a rule is invalid, the whole filter list will be failed.
|
|
10439
|
+
* Defaults to `true`.
|
|
10440
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
10441
|
+
* the array of converted filter list text, and its `isConverted` flag indicates whether the original rule was
|
|
10442
|
+
* converted. If the rule was not converted, the original filter list text will be returned
|
|
10443
|
+
* @throws If the filter list is invalid or cannot be converted (if the tolerant mode is disabled)
|
|
9820
10444
|
*/
|
|
9821
|
-
static convertToAdg(
|
|
9822
|
-
const
|
|
9823
|
-
//
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
const convertedRules = RuleConverter.convertToAdg(ruleNode);
|
|
9827
|
-
result.children.push(...convertedRules);
|
|
10445
|
+
static convertToAdg(rawFilterList, tolerant = true) {
|
|
10446
|
+
const conversionResult = FilterListConverter.convertToAdg(FilterListParser.parse(rawFilterList, tolerant), tolerant);
|
|
10447
|
+
// If the filter list was not converted, return the original text
|
|
10448
|
+
if (!conversionResult.isConverted) {
|
|
10449
|
+
return createConversionResult(rawFilterList, false);
|
|
9828
10450
|
}
|
|
9829
|
-
return result
|
|
10451
|
+
// Otherwise, serialize the filter list and return the result
|
|
10452
|
+
return createConversionResult(FilterListParser.generate(conversionResult.result), true);
|
|
10453
|
+
}
|
|
10454
|
+
}
|
|
10455
|
+
|
|
10456
|
+
/**
|
|
10457
|
+
* @file Rule converter for raw rules
|
|
10458
|
+
*
|
|
10459
|
+
* Technically, this is a wrapper around `RuleConverter` that works with nodes instead of strings.
|
|
10460
|
+
*/
|
|
10461
|
+
/**
|
|
10462
|
+
* Adblock filtering rule converter class.
|
|
10463
|
+
*
|
|
10464
|
+
* You can use this class to convert string-based adblock rules, since most of the converters work with nodes.
|
|
10465
|
+
* This class just provides an extra layer on top of the {@link RuleConverter} and calls the parser/serializer
|
|
10466
|
+
* before/after the conversion internally.
|
|
10467
|
+
*
|
|
10468
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
10469
|
+
*/
|
|
10470
|
+
class RawRuleConverter extends ConverterBase {
|
|
10471
|
+
/**
|
|
10472
|
+
* Converts an adblock filtering rule to AdGuard format, if possible.
|
|
10473
|
+
*
|
|
10474
|
+
* @param rawRule Raw rule text to convert
|
|
10475
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
10476
|
+
* the array of converted rule texts, and its `isConverted` flag indicates whether the original rule was converted.
|
|
10477
|
+
* If the rule was not converted, the original rule text will be returned
|
|
10478
|
+
* @throws If the rule is invalid or cannot be converted
|
|
10479
|
+
*/
|
|
10480
|
+
static convertToAdg(rawRule) {
|
|
10481
|
+
const conversionResult = RuleConverter.convertToAdg(RuleParser.parse(rawRule));
|
|
10482
|
+
// If the rule was not converted, return the original rule text
|
|
10483
|
+
if (!conversionResult.isConverted) {
|
|
10484
|
+
return createConversionResult([rawRule], false);
|
|
10485
|
+
}
|
|
10486
|
+
// Otherwise, serialize the converted rule nodes
|
|
10487
|
+
return createConversionResult(conversionResult.result.map(RuleParser.generate), true);
|
|
9830
10488
|
}
|
|
9831
10489
|
}
|
|
9832
10490
|
|
|
@@ -9908,7 +10566,7 @@ class LogicalExpressionUtils {
|
|
|
9908
10566
|
}
|
|
9909
10567
|
}
|
|
9910
10568
|
|
|
9911
|
-
const version$1 = "1.1.
|
|
10569
|
+
const version$1 = "1.1.7";
|
|
9912
10570
|
|
|
9913
10571
|
/**
|
|
9914
10572
|
* @file AGTree version
|
|
@@ -9919,4 +10577,4 @@ const version$1 = "1.1.5";
|
|
|
9919
10577
|
// with wrong relative path to `package.json`. So we need this little "hack"
|
|
9920
10578
|
const version = version$1;
|
|
9921
10579
|
|
|
9922
|
-
export { ADBLOCK_URL_SEPARATOR, ADBLOCK_URL_SEPARATOR_REGEX, ADBLOCK_URL_START, ADBLOCK_URL_START_REGEX, ADBLOCK_WILDCARD, ADBLOCK_WILDCARD_REGEX, ADG_SCRIPTLET_MASK, AGLINT_COMMAND_PREFIX, AdblockSyntax, AdblockSyntaxError, AgentCommentRuleParser, AgentParser, AppListParser, COMMA_DOMAIN_LIST_SEPARATOR, CommentMarker, CommentRuleParser, CommentRuleType, ConfigCommentRuleParser, CosmeticRuleParser, CosmeticRuleSeparator, CosmeticRuleSeparatorUtils, CosmeticRuleType, CssTree, CssTreeNodeType, CssTreeParserContext, DomainListParser, DomainUtils, EXT_CSS_LEGACY_ATTRIBUTES, EXT_CSS_PSEUDO_CLASSES, FORBIDDEN_CSS_FUNCTIONS, FilterListConverter, FilterListParser, HINT_MARKER, HintCommentRuleParser, HintParser, IF, INCLUDE, LogicalExpressionParser, LogicalExpressionUtils, METADATA_HEADERS, MODIFIERS_SEPARATOR, MODIFIER_ASSIGN_OPERATOR, MetadataCommentRuleParser, MethodListParser, ModifierListParser, ModifierParser, NEGATION_MARKER, NETWORK_RULE_EXCEPTION_MARKER, NETWORK_RULE_SEPARATOR, NetworkRuleParser, NotImplementedError, PIPE_MODIFIER_SEPARATOR, PREPROCESSOR_MARKER, ParameterListParser, PreProcessorCommentRuleParser, QuoteType, QuoteUtils, RegExpUtils, RuleCategory, RuleConversionError, RuleConverter, RuleParser, SAFARI_CB_AFFINITY, SPECIAL_REGEX_SYMBOLS, StealthOptionListParser, UBO_SCRIPTLET_MASK, locRange, modifierValidator, shiftLoc, version };
|
|
10580
|
+
export { ADBLOCK_URL_SEPARATOR, ADBLOCK_URL_SEPARATOR_REGEX, ADBLOCK_URL_START, ADBLOCK_URL_START_REGEX, ADBLOCK_WILDCARD, ADBLOCK_WILDCARD_REGEX, ADG_SCRIPTLET_MASK, AGLINT_COMMAND_PREFIX, AdblockSyntax, AdblockSyntaxError, AgentCommentRuleParser, AgentParser, AppListParser, COMMA_DOMAIN_LIST_SEPARATOR, CommentMarker, CommentRuleParser, CommentRuleType, ConfigCommentRuleParser, CosmeticRuleParser, CosmeticRuleSeparator, CosmeticRuleSeparatorUtils, CosmeticRuleType, CssTree, CssTreeNodeType, CssTreeParserContext, DomainListParser, DomainUtils, EXT_CSS_LEGACY_ATTRIBUTES, EXT_CSS_PSEUDO_CLASSES, FORBIDDEN_CSS_FUNCTIONS, FilterListConverter, FilterListParser, HINT_MARKER, HintCommentRuleParser, HintParser, IF, INCLUDE, LogicalExpressionParser, LogicalExpressionUtils, METADATA_HEADERS, MODIFIERS_SEPARATOR, MODIFIER_ASSIGN_OPERATOR, MetadataCommentRuleParser, MethodListParser, ModifierListParser, ModifierParser, NEGATION_MARKER, NETWORK_RULE_EXCEPTION_MARKER, NETWORK_RULE_SEPARATOR, NetworkRuleParser, NotImplementedError, PIPE_MODIFIER_SEPARATOR, PREPROCESSOR_MARKER, ParameterListParser, PreProcessorCommentRuleParser, QuoteType, QuoteUtils, RawFilterListConverter, RawRuleConverter, RegExpUtils, RuleCategory, RuleConversionError, RuleConverter, RuleParser, SAFARI_CB_AFFINITY, SPECIAL_REGEX_SYMBOLS, StealthOptionListParser, UBO_SCRIPTLET_MASK, locRange, modifierValidator, shiftLoc, version };
|