@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.cjs
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
|
|
@@ -281,7 +281,7 @@ const NEGATION_MARKER = '~';
|
|
|
281
281
|
/**
|
|
282
282
|
* The wildcard symbol — `*`.
|
|
283
283
|
*/
|
|
284
|
-
const WILDCARD
|
|
284
|
+
const WILDCARD = ASTERISK;
|
|
285
285
|
/**
|
|
286
286
|
* Classic domain separator.
|
|
287
287
|
*
|
|
@@ -2880,7 +2880,7 @@ class ModifierParser {
|
|
|
2880
2880
|
const modifierEnd = Math.max(StringUtils.skipWSBack(raw) + 1, modifierNameStart);
|
|
2881
2881
|
// Modifier name can't be empty
|
|
2882
2882
|
if (modifierNameStart === modifierEnd) {
|
|
2883
|
-
throw new AdblockSyntaxError('Modifier name
|
|
2883
|
+
throw new AdblockSyntaxError('Modifier name cannot be empty', locRange(loc, 0, raw.length));
|
|
2884
2884
|
}
|
|
2885
2885
|
let modifier;
|
|
2886
2886
|
let value;
|
|
@@ -2904,7 +2904,7 @@ class ModifierParser {
|
|
|
2904
2904
|
};
|
|
2905
2905
|
// Value can't be empty
|
|
2906
2906
|
if (assignmentIndex + 1 === modifierEnd) {
|
|
2907
|
-
throw new AdblockSyntaxError('Modifier value
|
|
2907
|
+
throw new AdblockSyntaxError('Modifier value cannot be empty', locRange(loc, 0, raw.length));
|
|
2908
2908
|
}
|
|
2909
2909
|
// Skip whitespace after the assignment operator
|
|
2910
2910
|
const valueStart = StringUtils.skipWS(raw, assignmentIndex + MODIFIER_ASSIGN_OPERATOR.length);
|
|
@@ -3202,8 +3202,29 @@ const FORBIDDEN_CSS_FUNCTIONS = new Set([
|
|
|
3202
3202
|
'url',
|
|
3203
3203
|
]);
|
|
3204
3204
|
|
|
3205
|
+
/**
|
|
3206
|
+
* @file Clone related utilities
|
|
3207
|
+
*
|
|
3208
|
+
* We should keep clone related functions in this file. Thus, we just provide
|
|
3209
|
+
* a simple interface for cloning values, we use it across the AGTree project,
|
|
3210
|
+
* and the implementation "under the hood" can be improved later, if needed.
|
|
3211
|
+
*/
|
|
3212
|
+
/**
|
|
3213
|
+
* Clones an input value to avoid side effects. Use it only in justified cases,
|
|
3214
|
+
* because it can impact performance negatively.
|
|
3215
|
+
*
|
|
3216
|
+
* @param value Value to clone
|
|
3217
|
+
* @returns Cloned value
|
|
3218
|
+
*/
|
|
3219
|
+
function clone(value) {
|
|
3220
|
+
// TODO: Replace cloneDeep with a more efficient implementation
|
|
3221
|
+
return cloneDeep(value);
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3205
3224
|
/**
|
|
3206
3225
|
* @file Additional / helper functions for ECSSTree / CSSTree.
|
|
3226
|
+
*
|
|
3227
|
+
* @note There are no tests for some functions, but during the AGTree optimization we remove them anyway.
|
|
3207
3228
|
*/
|
|
3208
3229
|
/**
|
|
3209
3230
|
* Common CSSTree parsing options.
|
|
@@ -3339,10 +3360,10 @@ class CssTree {
|
|
|
3339
3360
|
ast = CssTree.parse(selectorList, exports.CssTreeParserContext.selectorList);
|
|
3340
3361
|
}
|
|
3341
3362
|
else {
|
|
3342
|
-
ast =
|
|
3363
|
+
ast = clone(selectorList);
|
|
3343
3364
|
}
|
|
3344
3365
|
const nodes = [];
|
|
3345
|
-
// TODO:
|
|
3366
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3346
3367
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3347
3368
|
ecssTree.walk(ast, (node) => {
|
|
3348
3369
|
if (CssTree.isExtendedCssNode(node, pseudoClasses, attributeSelectors)) {
|
|
@@ -3371,9 +3392,9 @@ class CssTree {
|
|
|
3371
3392
|
ast = CssTree.parse(selectorList, exports.CssTreeParserContext.selectorList);
|
|
3372
3393
|
}
|
|
3373
3394
|
else {
|
|
3374
|
-
ast =
|
|
3395
|
+
ast = selectorList;
|
|
3375
3396
|
}
|
|
3376
|
-
// TODO:
|
|
3397
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3377
3398
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3378
3399
|
return ecssTree.find(ast, (node) => CssTree.isExtendedCssNode(node, pseudoClasses, attributeSelectors)) !== null;
|
|
3379
3400
|
}
|
|
@@ -3410,14 +3431,14 @@ class CssTree {
|
|
|
3410
3431
|
ast = CssTree.parse(declarationList, exports.CssTreeParserContext.declarationList);
|
|
3411
3432
|
}
|
|
3412
3433
|
else {
|
|
3413
|
-
ast =
|
|
3434
|
+
ast = clone(declarationList);
|
|
3414
3435
|
}
|
|
3415
3436
|
const nodes = [];
|
|
3416
3437
|
// While walking the AST we should skip the nested functions,
|
|
3417
3438
|
// for example skip url()s in cross-fade(url(), url()), since
|
|
3418
3439
|
// cross-fade() itself is already a forbidden function
|
|
3419
3440
|
let inForbiddenFunction = false;
|
|
3420
|
-
// TODO:
|
|
3441
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3421
3442
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3422
3443
|
ecssTree.walk(ast, {
|
|
3423
3444
|
enter: (node) => {
|
|
@@ -3455,9 +3476,9 @@ class CssTree {
|
|
|
3455
3476
|
ast = CssTree.parse(declarationList, exports.CssTreeParserContext.declarationList);
|
|
3456
3477
|
}
|
|
3457
3478
|
else {
|
|
3458
|
-
ast =
|
|
3479
|
+
ast = clone(declarationList);
|
|
3459
3480
|
}
|
|
3460
|
-
// TODO:
|
|
3481
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3461
3482
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3462
3483
|
return ecssTree.find(ast, (node) => CssTree.isForbiddenFunction(node, forbiddenFunctions)) !== null;
|
|
3463
3484
|
}
|
|
@@ -3689,6 +3710,180 @@ class CssTree {
|
|
|
3689
3710
|
});
|
|
3690
3711
|
return result.trim();
|
|
3691
3712
|
}
|
|
3713
|
+
/**
|
|
3714
|
+
* Generates string representation of the selector list.
|
|
3715
|
+
*
|
|
3716
|
+
* @param ast SelectorList AST
|
|
3717
|
+
* @returns String representation of the selector list
|
|
3718
|
+
*/
|
|
3719
|
+
static generateSelectorListPlain(ast) {
|
|
3720
|
+
const result = [];
|
|
3721
|
+
if (!ast.children || ast.children.length === 0) {
|
|
3722
|
+
throw new Error('Selector list cannot be empty');
|
|
3723
|
+
}
|
|
3724
|
+
ast.children.forEach((selector, index, nodeList) => {
|
|
3725
|
+
if (selector.type !== exports.CssTreeNodeType.Selector) {
|
|
3726
|
+
throw new Error(`Unexpected node type: ${selector.type}`);
|
|
3727
|
+
}
|
|
3728
|
+
result.push(this.generateSelectorPlain(selector));
|
|
3729
|
+
// If there is a next node, add a comma and a space after the selector
|
|
3730
|
+
if (nodeList[index + 1]) {
|
|
3731
|
+
result.push(COMMA, SPACE);
|
|
3732
|
+
}
|
|
3733
|
+
});
|
|
3734
|
+
return result.join(EMPTY);
|
|
3735
|
+
}
|
|
3736
|
+
/**
|
|
3737
|
+
* Selector generation based on CSSTree's AST. This is necessary because CSSTree
|
|
3738
|
+
* only adds spaces in some edge cases.
|
|
3739
|
+
*
|
|
3740
|
+
* @param ast CSS Tree AST
|
|
3741
|
+
* @returns CSS selector as string
|
|
3742
|
+
*/
|
|
3743
|
+
static generateSelectorPlain(ast) {
|
|
3744
|
+
let result = EMPTY;
|
|
3745
|
+
let inAttributeSelector = false;
|
|
3746
|
+
let depth = 0;
|
|
3747
|
+
let selectorListDepth = -1;
|
|
3748
|
+
let prevNode = ast;
|
|
3749
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3750
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3751
|
+
ecssTree.walk(ast, {
|
|
3752
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
3753
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3754
|
+
enter: (node) => {
|
|
3755
|
+
depth += 1;
|
|
3756
|
+
// Skip attribute selector / selector list children
|
|
3757
|
+
if (inAttributeSelector || selectorListDepth > -1) {
|
|
3758
|
+
return;
|
|
3759
|
+
}
|
|
3760
|
+
switch (node.type) {
|
|
3761
|
+
// "Trivial" nodes
|
|
3762
|
+
case exports.CssTreeNodeType.TypeSelector:
|
|
3763
|
+
result += node.name;
|
|
3764
|
+
break;
|
|
3765
|
+
case exports.CssTreeNodeType.ClassSelector:
|
|
3766
|
+
result += DOT;
|
|
3767
|
+
result += node.name;
|
|
3768
|
+
break;
|
|
3769
|
+
case exports.CssTreeNodeType.IdSelector:
|
|
3770
|
+
result += HASHMARK;
|
|
3771
|
+
result += node.name;
|
|
3772
|
+
break;
|
|
3773
|
+
case exports.CssTreeNodeType.Identifier:
|
|
3774
|
+
result += node.name;
|
|
3775
|
+
break;
|
|
3776
|
+
case exports.CssTreeNodeType.Raw:
|
|
3777
|
+
result += node.value;
|
|
3778
|
+
break;
|
|
3779
|
+
// "Advanced" nodes
|
|
3780
|
+
case exports.CssTreeNodeType.Nth:
|
|
3781
|
+
// Default generation enough
|
|
3782
|
+
result += ecssTree.generate(node);
|
|
3783
|
+
break;
|
|
3784
|
+
// For example :not([id], [name])
|
|
3785
|
+
case exports.CssTreeNodeType.SelectorList:
|
|
3786
|
+
// eslint-disable-next-line no-case-declarations
|
|
3787
|
+
const selectors = [];
|
|
3788
|
+
node.children.forEach((selector) => {
|
|
3789
|
+
if (selector.type === exports.CssTreeNodeType.Selector) {
|
|
3790
|
+
selectors.push(CssTree.generateSelectorPlain(selector));
|
|
3791
|
+
}
|
|
3792
|
+
else if (selector.type === exports.CssTreeNodeType.Raw) {
|
|
3793
|
+
selectors.push(selector.value);
|
|
3794
|
+
}
|
|
3795
|
+
});
|
|
3796
|
+
// Join selector lists
|
|
3797
|
+
result += selectors.join(COMMA + SPACE);
|
|
3798
|
+
// Skip nodes here
|
|
3799
|
+
selectorListDepth = depth;
|
|
3800
|
+
break;
|
|
3801
|
+
case exports.CssTreeNodeType.Combinator:
|
|
3802
|
+
if (node.name === SPACE) {
|
|
3803
|
+
result += node.name;
|
|
3804
|
+
break;
|
|
3805
|
+
}
|
|
3806
|
+
// Prevent this case (unnecessary space): has( > .something)
|
|
3807
|
+
if (prevNode.type !== exports.CssTreeNodeType.Selector) {
|
|
3808
|
+
result += SPACE;
|
|
3809
|
+
}
|
|
3810
|
+
result += node.name;
|
|
3811
|
+
result += SPACE;
|
|
3812
|
+
break;
|
|
3813
|
+
case exports.CssTreeNodeType.AttributeSelector:
|
|
3814
|
+
result += OPEN_SQUARE_BRACKET;
|
|
3815
|
+
// Identifier name
|
|
3816
|
+
if (node.name) {
|
|
3817
|
+
result += node.name.name;
|
|
3818
|
+
}
|
|
3819
|
+
// Matcher operator, eg =
|
|
3820
|
+
if (node.matcher) {
|
|
3821
|
+
result += node.matcher;
|
|
3822
|
+
// Value can be String, Identifier or null
|
|
3823
|
+
if (node.value !== null) {
|
|
3824
|
+
// String node
|
|
3825
|
+
if (node.value.type === exports.CssTreeNodeType.String) {
|
|
3826
|
+
result += ecssTree.generate(node.value);
|
|
3827
|
+
}
|
|
3828
|
+
else if (node.value.type === exports.CssTreeNodeType.Identifier) {
|
|
3829
|
+
// Identifier node
|
|
3830
|
+
result += node.value.name;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
// Flags
|
|
3835
|
+
if (node.flags) {
|
|
3836
|
+
// Space before flags
|
|
3837
|
+
result += SPACE;
|
|
3838
|
+
result += node.flags;
|
|
3839
|
+
}
|
|
3840
|
+
result += CLOSE_SQUARE_BRACKET;
|
|
3841
|
+
inAttributeSelector = true;
|
|
3842
|
+
break;
|
|
3843
|
+
case exports.CssTreeNodeType.PseudoElementSelector:
|
|
3844
|
+
result += COLON;
|
|
3845
|
+
result += COLON;
|
|
3846
|
+
result += node.name;
|
|
3847
|
+
if (node.children !== null) {
|
|
3848
|
+
result += OPEN_PARENTHESIS;
|
|
3849
|
+
}
|
|
3850
|
+
break;
|
|
3851
|
+
case exports.CssTreeNodeType.PseudoClassSelector:
|
|
3852
|
+
result += COLON;
|
|
3853
|
+
result += node.name;
|
|
3854
|
+
if (node.children !== null) {
|
|
3855
|
+
result += OPEN_PARENTHESIS;
|
|
3856
|
+
}
|
|
3857
|
+
break;
|
|
3858
|
+
}
|
|
3859
|
+
prevNode = node;
|
|
3860
|
+
},
|
|
3861
|
+
leave: (node) => {
|
|
3862
|
+
depth -= 1;
|
|
3863
|
+
if (node.type === exports.CssTreeNodeType.SelectorList && depth + 1 === selectorListDepth) {
|
|
3864
|
+
selectorListDepth = -1;
|
|
3865
|
+
}
|
|
3866
|
+
if (selectorListDepth > -1) {
|
|
3867
|
+
return;
|
|
3868
|
+
}
|
|
3869
|
+
if (node.type === exports.CssTreeNodeType.AttributeSelector) {
|
|
3870
|
+
inAttributeSelector = false;
|
|
3871
|
+
}
|
|
3872
|
+
if (inAttributeSelector) {
|
|
3873
|
+
return;
|
|
3874
|
+
}
|
|
3875
|
+
switch (node.type) {
|
|
3876
|
+
case exports.CssTreeNodeType.PseudoElementSelector:
|
|
3877
|
+
case exports.CssTreeNodeType.PseudoClassSelector:
|
|
3878
|
+
if (node.children) {
|
|
3879
|
+
result += CLOSE_PARENTHESIS;
|
|
3880
|
+
}
|
|
3881
|
+
break;
|
|
3882
|
+
}
|
|
3883
|
+
},
|
|
3884
|
+
});
|
|
3885
|
+
return result.trim();
|
|
3886
|
+
}
|
|
3692
3887
|
/**
|
|
3693
3888
|
* Block generation based on CSSTree's AST. This is necessary because CSSTree only adds spaces in some edge cases.
|
|
3694
3889
|
*
|
|
@@ -3872,6 +4067,29 @@ class CssTree {
|
|
|
3872
4067
|
});
|
|
3873
4068
|
return result;
|
|
3874
4069
|
}
|
|
4070
|
+
/**
|
|
4071
|
+
* Helper function to generate a raw string from a function selector's children
|
|
4072
|
+
*
|
|
4073
|
+
* @param node Function node
|
|
4074
|
+
* @returns Generated function value
|
|
4075
|
+
* @example `responseheader(name)` -> `name`
|
|
4076
|
+
*/
|
|
4077
|
+
static generateFunctionPlainValue(node) {
|
|
4078
|
+
const result = [];
|
|
4079
|
+
node.children?.forEach((child) => {
|
|
4080
|
+
switch (child.type) {
|
|
4081
|
+
case exports.CssTreeNodeType.Raw:
|
|
4082
|
+
result.push(child.value);
|
|
4083
|
+
break;
|
|
4084
|
+
default:
|
|
4085
|
+
// Fallback to CSSTree's default generate function
|
|
4086
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
4087
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4088
|
+
result.push(ecssTree.generate(child));
|
|
4089
|
+
}
|
|
4090
|
+
});
|
|
4091
|
+
return result.join(EMPTY);
|
|
4092
|
+
}
|
|
3875
4093
|
}
|
|
3876
4094
|
|
|
3877
4095
|
/**
|
|
@@ -3919,7 +4137,7 @@ class ElementHidingBodyParser {
|
|
|
3919
4137
|
* @throws If the AST is invalid
|
|
3920
4138
|
*/
|
|
3921
4139
|
static generate(ast) {
|
|
3922
|
-
return CssTree.
|
|
4140
|
+
return CssTree.generateSelectorListPlain(ast.selectorList);
|
|
3923
4141
|
}
|
|
3924
4142
|
}
|
|
3925
4143
|
|
|
@@ -4163,7 +4381,7 @@ class CssInjectionBodyParser {
|
|
|
4163
4381
|
if (mediaQueryList || declarationList || remove) {
|
|
4164
4382
|
throw new AdblockSyntaxError(
|
|
4165
4383
|
// eslint-disable-next-line max-len
|
|
4166
|
-
'Invalid selector, regular selector elements
|
|
4384
|
+
'Invalid selector, regular selector elements cannot be used after special pseudo-classes', {
|
|
4167
4385
|
start: node.loc?.start ?? loc,
|
|
4168
4386
|
end: shiftLoc(loc, raw.length),
|
|
4169
4387
|
});
|
|
@@ -4852,7 +5070,7 @@ function createModifierListNode(modifiers = []) {
|
|
|
4852
5070
|
const result = {
|
|
4853
5071
|
type: 'ModifierList',
|
|
4854
5072
|
// We need to clone the modifiers to avoid side effects
|
|
4855
|
-
children:
|
|
5073
|
+
children: modifiers.length ? clone(modifiers) : [],
|
|
4856
5074
|
};
|
|
4857
5075
|
return result;
|
|
4858
5076
|
}
|
|
@@ -4892,8 +5110,9 @@ function hasUboModifierIndicator(rawSelectorList) {
|
|
|
4892
5110
|
* @returns Linked list based selector
|
|
4893
5111
|
*/
|
|
4894
5112
|
function convertSelectorToLinkedList(selector) {
|
|
5113
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
4895
5114
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4896
|
-
return ecssTree.fromPlainObject(
|
|
5115
|
+
return ecssTree.fromPlainObject(clone(selector));
|
|
4897
5116
|
}
|
|
4898
5117
|
/**
|
|
4899
5118
|
* Helper function that always returns the linked list version of the
|
|
@@ -4903,8 +5122,9 @@ function convertSelectorToLinkedList(selector) {
|
|
|
4903
5122
|
* @returns Linked list based selector list
|
|
4904
5123
|
*/
|
|
4905
5124
|
function convertSelectorListToLinkedList(selectorList) {
|
|
5125
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
4906
5126
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4907
|
-
return ecssTree.fromPlainObject(
|
|
5127
|
+
return ecssTree.fromPlainObject(clone(selectorList));
|
|
4908
5128
|
}
|
|
4909
5129
|
/**
|
|
4910
5130
|
* Helper function for checking and removing bounding combinators
|
|
@@ -5989,7 +6209,8 @@ class FilterListParser {
|
|
|
5989
6209
|
*/
|
|
5990
6210
|
static generate(ast, preferRaw = false) {
|
|
5991
6211
|
let result = EMPTY;
|
|
5992
|
-
for (
|
|
6212
|
+
for (let i = 0; i < ast.children.length; i += 1) {
|
|
6213
|
+
const rule = ast.children[i];
|
|
5993
6214
|
if (preferRaw && rule.raws?.text) {
|
|
5994
6215
|
result += rule.raws.text;
|
|
5995
6216
|
}
|
|
@@ -6006,6 +6227,11 @@ class FilterListParser {
|
|
|
6006
6227
|
case 'lf':
|
|
6007
6228
|
result += LF;
|
|
6008
6229
|
break;
|
|
6230
|
+
default:
|
|
6231
|
+
if (i !== ast.children.length - 1) {
|
|
6232
|
+
result += LF;
|
|
6233
|
+
}
|
|
6234
|
+
break;
|
|
6009
6235
|
}
|
|
6010
6236
|
}
|
|
6011
6237
|
return result;
|
|
@@ -6055,7 +6281,7 @@ class RuleConversionError extends Error {
|
|
|
6055
6281
|
}
|
|
6056
6282
|
}
|
|
6057
6283
|
|
|
6058
|
-
var data$
|
|
6284
|
+
var data$U = { adg_os_any:{ name:"all",
|
|
6059
6285
|
description:"$all modifier is made of $document, $popup, and all content-type modifiers combined.",
|
|
6060
6286
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#all-modifier",
|
|
6061
6287
|
negatable:false,
|
|
@@ -6081,14 +6307,14 @@ var data$T = { adg_os_any:{ name:"all",
|
|
|
6081
6307
|
negatable:false,
|
|
6082
6308
|
block_only:false } };
|
|
6083
6309
|
|
|
6084
|
-
var data$
|
|
6310
|
+
var data$T = { adg_os_any:{ name:"app",
|
|
6085
6311
|
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.",
|
|
6086
6312
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier",
|
|
6087
6313
|
assignable:true,
|
|
6088
6314
|
negatable:false,
|
|
6089
6315
|
value_format:"pipe_separated_apps" } };
|
|
6090
6316
|
|
|
6091
|
-
var data$
|
|
6317
|
+
var data$S = { adg_os_any:{ name:"badfilter",
|
|
6092
6318
|
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).",
|
|
6093
6319
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#badfilter-modifier",
|
|
6094
6320
|
negatable:false },
|
|
@@ -6109,19 +6335,19 @@ var data$R = { adg_os_any:{ name:"badfilter",
|
|
|
6109
6335
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#badfilter",
|
|
6110
6336
|
negatable:false } };
|
|
6111
6337
|
|
|
6112
|
-
var data$
|
|
6338
|
+
var data$R = { ubo_ext_any:{ name:"cname",
|
|
6113
6339
|
description:"When used in an exception filter,\nit will bypass blocking CNAME uncloaked requests for the current (specified) document.",
|
|
6114
6340
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#cname",
|
|
6115
6341
|
negatable:false,
|
|
6116
6342
|
exception_only:true } };
|
|
6117
6343
|
|
|
6118
|
-
var data$
|
|
6344
|
+
var data$Q = { adg_os_any:{ name:"content",
|
|
6119
6345
|
description:"Disables HTML filtering and `$replace` rules on the pages that match the rule.",
|
|
6120
6346
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#content-modifier",
|
|
6121
6347
|
negatable:false,
|
|
6122
6348
|
exception_only:true } };
|
|
6123
6349
|
|
|
6124
|
-
var data$
|
|
6350
|
+
var data$P = { adg_os_any:{ name:"cookie",
|
|
6125
6351
|
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.",
|
|
6126
6352
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#cookie-modifier",
|
|
6127
6353
|
assignable:true,
|
|
@@ -6136,7 +6362,7 @@ var data$O = { adg_os_any:{ name:"cookie",
|
|
|
6136
6362
|
value_optional:true,
|
|
6137
6363
|
value_format:"^([^;=\\s]*?)((?:;(maxAge=\\d+;?)?|(sameSite=(lax|none|strict);?)?){1,3})(?<!;)$" } };
|
|
6138
6364
|
|
|
6139
|
-
var data$
|
|
6365
|
+
var data$O = { adg_os_any:{ name:"csp",
|
|
6140
6366
|
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.",
|
|
6141
6367
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#csp-modifier",
|
|
6142
6368
|
conflicts:[ "domain",
|
|
@@ -6183,7 +6409,7 @@ var data$N = { adg_os_any:{ name:"csp",
|
|
|
6183
6409
|
value_optional:true,
|
|
6184
6410
|
value_format:"csp_value" } };
|
|
6185
6411
|
|
|
6186
|
-
var data$
|
|
6412
|
+
var data$N = { adg_os_any:{ name:"denyallow",
|
|
6187
6413
|
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.",
|
|
6188
6414
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier",
|
|
6189
6415
|
conflicts:[ "to" ],
|
|
@@ -6219,7 +6445,7 @@ var data$M = { adg_os_any:{ name:"denyallow",
|
|
|
6219
6445
|
negatable:false,
|
|
6220
6446
|
value_format:"pipe_separated_denyallow_domains" } };
|
|
6221
6447
|
|
|
6222
|
-
var data$
|
|
6448
|
+
var data$M = { adg_os_any:{ name:"document",
|
|
6223
6449
|
description:"The rule corresponds to the main frame document requests,\ni.e. HTML documents that are loaded in the browser tab.",
|
|
6224
6450
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#document-modifier",
|
|
6225
6451
|
negatable:false },
|
|
@@ -6245,7 +6471,7 @@ var data$L = { adg_os_any:{ name:"document",
|
|
|
6245
6471
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#document",
|
|
6246
6472
|
negatable:false } };
|
|
6247
6473
|
|
|
6248
|
-
var data$
|
|
6474
|
+
var data$L = { adg_any:{ name:"domain",
|
|
6249
6475
|
aliases:[ "from" ],
|
|
6250
6476
|
description:"The `$domain` modifier limits the rule application area to a list of domains and their subdomains.",
|
|
6251
6477
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#domain-modifier",
|
|
@@ -6266,7 +6492,7 @@ var data$K = { adg_any:{ name:"domain",
|
|
|
6266
6492
|
negatable:false,
|
|
6267
6493
|
value_format:"pipe_separated_domains" } };
|
|
6268
6494
|
|
|
6269
|
-
var data$
|
|
6495
|
+
var data$K = { adg_any:{ name:"elemhide",
|
|
6270
6496
|
aliases:[ "ehide" ],
|
|
6271
6497
|
description:"Disables any cosmetic rules on the pages matching the rule.",
|
|
6272
6498
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#elemhide-modifier",
|
|
@@ -6285,7 +6511,7 @@ var data$J = { adg_any:{ name:"elemhide",
|
|
|
6285
6511
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#elemhide-1",
|
|
6286
6512
|
description:"Disables any cosmetic rules on the pages matching the rule." } };
|
|
6287
6513
|
|
|
6288
|
-
var data$
|
|
6514
|
+
var data$J = { adg_os_any:{ name:"empty",
|
|
6289
6515
|
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.",
|
|
6290
6516
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#empty-modifier",
|
|
6291
6517
|
deprecated:true,
|
|
@@ -6302,7 +6528,7 @@ var data$I = { adg_os_any:{ name:"empty",
|
|
|
6302
6528
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#empty",
|
|
6303
6529
|
negatable:false } };
|
|
6304
6530
|
|
|
6305
|
-
var data$
|
|
6531
|
+
var data$I = { adg_any:{ name:"first-party",
|
|
6306
6532
|
aliases:[ "1p",
|
|
6307
6533
|
"~third-party" ],
|
|
6308
6534
|
description:"A restriction of first-party requests. Equal to `~third-party`.",
|
|
@@ -6315,7 +6541,7 @@ var data$H = { adg_any:{ name:"first-party",
|
|
|
6315
6541
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#1p",
|
|
6316
6542
|
negatable:false } };
|
|
6317
6543
|
|
|
6318
|
-
var data$
|
|
6544
|
+
var data$H = { adg_os_any:{ name:"extension",
|
|
6319
6545
|
description:"Disables all userscripts on the pages matching this rule.",
|
|
6320
6546
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#extension-modifier",
|
|
6321
6547
|
conflicts:[ "domain",
|
|
@@ -6331,7 +6557,7 @@ var data$G = { adg_os_any:{ name:"extension",
|
|
|
6331
6557
|
inverse_conflicts:true,
|
|
6332
6558
|
exception_only:true } };
|
|
6333
6559
|
|
|
6334
|
-
var data$
|
|
6560
|
+
var data$G = { adg_any:{ name:"font",
|
|
6335
6561
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#font-modifier",
|
|
6336
6562
|
description:"The rule corresponds to requests for fonts, e.g. `.woff` filename extension." },
|
|
6337
6563
|
abp_any:{ name:"font",
|
|
@@ -6341,7 +6567,7 @@ var data$F = { adg_any:{ name:"font",
|
|
|
6341
6567
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293#options",
|
|
6342
6568
|
description:"The rule corresponds to requests for fonts, e.g. `.woff` filename extension." } };
|
|
6343
6569
|
|
|
6344
|
-
var data$
|
|
6570
|
+
var data$F = { adg_os_any:{ name:"genericblock",
|
|
6345
6571
|
description:"Disables generic basic rules on pages that correspond to exception rule.",
|
|
6346
6572
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#genericblock-modifier",
|
|
6347
6573
|
conflicts:[ "domain",
|
|
@@ -6397,7 +6623,7 @@ var data$E = { adg_os_any:{ name:"genericblock",
|
|
|
6397
6623
|
negatable:false,
|
|
6398
6624
|
exception_only:true } };
|
|
6399
6625
|
|
|
6400
|
-
var data$
|
|
6626
|
+
var data$E = { adg_any:{ name:"generichide",
|
|
6401
6627
|
aliases:[ "ghide" ],
|
|
6402
6628
|
description:"Disables all generic cosmetic rules.",
|
|
6403
6629
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#generichide-modifier",
|
|
@@ -6429,7 +6655,7 @@ var data$D = { adg_any:{ name:"generichide",
|
|
|
6429
6655
|
negatable:false,
|
|
6430
6656
|
exception_only:true } };
|
|
6431
6657
|
|
|
6432
|
-
var data$
|
|
6658
|
+
var data$D = { adg_os_any:{ name:"header",
|
|
6433
6659
|
description:"The `$header` modifier allows matching the HTTP response\nhaving a specific header with (optionally) a specific value.",
|
|
6434
6660
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#header-modifier",
|
|
6435
6661
|
assignable:true,
|
|
@@ -6445,7 +6671,7 @@ var data$C = { adg_os_any:{ name:"header",
|
|
|
6445
6671
|
assignable:true,
|
|
6446
6672
|
value_format:"(?xi)\n ^\n # header name\n [\\w-]+\n (\n :\n # header value: string or regexp\n (\\w+|\\/.+\\/)\n )?" } };
|
|
6447
6673
|
|
|
6448
|
-
var data$
|
|
6674
|
+
var data$C = { adg_os_any:{ name:"hls",
|
|
6449
6675
|
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).",
|
|
6450
6676
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#hls-modifier",
|
|
6451
6677
|
version_added:"CoreLibs 1.10",
|
|
@@ -6461,7 +6687,7 @@ var data$B = { adg_os_any:{ name:"hls",
|
|
|
6461
6687
|
value_optional:true,
|
|
6462
6688
|
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 )" } };
|
|
6463
6689
|
|
|
6464
|
-
var data$
|
|
6690
|
+
var data$B = { adg_any:{ name:"image",
|
|
6465
6691
|
description:"The rule corresponds to images requests.",
|
|
6466
6692
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#image-modifier" },
|
|
6467
6693
|
abp_any:{ name:"image",
|
|
@@ -6471,7 +6697,7 @@ var data$A = { adg_any:{ name:"image",
|
|
|
6471
6697
|
description:"The rule corresponds to images requests.",
|
|
6472
6698
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293#options" } };
|
|
6473
6699
|
|
|
6474
|
-
var data$
|
|
6700
|
+
var data$A = { adg_any:{ name:"important",
|
|
6475
6701
|
description:"The `$important` modifier applied to a rule increases its priority\nover any other rule without `$important` modifier. Even over basic exception rules.",
|
|
6476
6702
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#important-modifier",
|
|
6477
6703
|
negatable:false },
|
|
@@ -6480,7 +6706,7 @@ var data$z = { adg_any:{ name:"important",
|
|
|
6480
6706
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#important",
|
|
6481
6707
|
negatable:false } };
|
|
6482
6708
|
|
|
6483
|
-
var data$
|
|
6709
|
+
var data$z = { adg_os_any:{ name:"inline-font",
|
|
6484
6710
|
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```",
|
|
6485
6711
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#inline-font-modifier" },
|
|
6486
6712
|
adg_ext_any:{ name:"inline-font",
|
|
@@ -6490,7 +6716,7 @@ var data$y = { adg_os_any:{ name:"inline-font",
|
|
|
6490
6716
|
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```",
|
|
6491
6717
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#inline-font" } };
|
|
6492
6718
|
|
|
6493
|
-
var data$
|
|
6719
|
+
var data$y = { adg_os_any:{ name:"inline-script",
|
|
6494
6720
|
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```",
|
|
6495
6721
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#inline-script-modifier" },
|
|
6496
6722
|
adg_ext_any:{ name:"inline-script",
|
|
@@ -6500,7 +6726,7 @@ var data$x = { adg_os_any:{ name:"inline-script",
|
|
|
6500
6726
|
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```",
|
|
6501
6727
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#inline-script" } };
|
|
6502
6728
|
|
|
6503
|
-
var data$
|
|
6729
|
+
var data$x = { adg_os_any:{ name:"jsinject",
|
|
6504
6730
|
description:"Forbids adding of javascript code to the page.",
|
|
6505
6731
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#jsinject-modifier",
|
|
6506
6732
|
conflicts:[ "domain",
|
|
@@ -6559,7 +6785,7 @@ var data$w = { adg_os_any:{ name:"jsinject",
|
|
|
6559
6785
|
negatable:false,
|
|
6560
6786
|
exception_only:true } };
|
|
6561
6787
|
|
|
6562
|
-
var data$
|
|
6788
|
+
var data$w = { adg_os_any:{ name:"jsonprune",
|
|
6563
6789
|
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.",
|
|
6564
6790
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#jsonprune-modifier",
|
|
6565
6791
|
assignable:true,
|
|
@@ -6567,7 +6793,7 @@ var data$v = { adg_os_any:{ name:"jsonprune",
|
|
|
6567
6793
|
value_optional:true,
|
|
6568
6794
|
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 $" } };
|
|
6569
6795
|
|
|
6570
|
-
var data$
|
|
6796
|
+
var data$v = { adg_any:{ name:"match-case",
|
|
6571
6797
|
description:"This modifier defines a rule which applies only to addresses that match the case.\nDefault rules are case-insensitive.",
|
|
6572
6798
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#match-case-modifier" },
|
|
6573
6799
|
abp_any:{ name:"match-case",
|
|
@@ -6577,7 +6803,7 @@ var data$u = { adg_any:{ name:"match-case",
|
|
|
6577
6803
|
description:"This modifier defines a rule which applies only to addresses that match the case.\nDefault rules are case-insensitive.",
|
|
6578
6804
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#match-case" } };
|
|
6579
6805
|
|
|
6580
|
-
var data$
|
|
6806
|
+
var data$u = { adg_any:{ name:"media",
|
|
6581
6807
|
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.",
|
|
6582
6808
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#media-modifier" },
|
|
6583
6809
|
abp_any:{ name:"media",
|
|
@@ -6587,7 +6813,7 @@ var data$t = { adg_any:{ name:"media",
|
|
|
6587
6813
|
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.",
|
|
6588
6814
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293#options" } };
|
|
6589
6815
|
|
|
6590
|
-
var data$
|
|
6816
|
+
var data$t = { adg_os_any:{ name:"method",
|
|
6591
6817
|
description:"This modifier limits the rule scope to requests that use the specified set of HTTP methods.\nNegated methods are allowed.",
|
|
6592
6818
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#method-modifier",
|
|
6593
6819
|
negatable:false,
|
|
@@ -6606,7 +6832,7 @@ var data$s = { adg_os_any:{ name:"method",
|
|
|
6606
6832
|
assignable:true,
|
|
6607
6833
|
value_format:"pipe_separated_methods" } };
|
|
6608
6834
|
|
|
6609
|
-
var data$
|
|
6835
|
+
var data$s = { adg_os_any:{ name:"mp4",
|
|
6610
6836
|
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.",
|
|
6611
6837
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#mp4-modifier",
|
|
6612
6838
|
deprecated:true,
|
|
@@ -6623,7 +6849,7 @@ var data$r = { adg_os_any:{ name:"mp4",
|
|
|
6623
6849
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#mp4",
|
|
6624
6850
|
negatable:false } };
|
|
6625
6851
|
|
|
6626
|
-
var data$
|
|
6852
|
+
var data$r = { adg_os_any:{ name:"network",
|
|
6627
6853
|
description:"This is basically a Firewall-kind of rules allowing to fully block\nor unblock access to a specified remote address.",
|
|
6628
6854
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#network-modifier",
|
|
6629
6855
|
conflicts:[ "app",
|
|
@@ -6631,7 +6857,7 @@ var data$q = { adg_os_any:{ name:"network",
|
|
|
6631
6857
|
inverse_conflicts:true,
|
|
6632
6858
|
negatable:false } };
|
|
6633
6859
|
|
|
6634
|
-
var data$
|
|
6860
|
+
var data$q = { adg_os_any:{ name:"_",
|
|
6635
6861
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#noop-modifier",
|
|
6636
6862
|
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.",
|
|
6637
6863
|
negatable:false },
|
|
@@ -6652,13 +6878,13 @@ var data$p = { adg_os_any:{ name:"_",
|
|
|
6652
6878
|
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.",
|
|
6653
6879
|
negatable:false } };
|
|
6654
6880
|
|
|
6655
|
-
var data$
|
|
6881
|
+
var data$p = { adg_any:{ name:"object-subrequest",
|
|
6656
6882
|
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).",
|
|
6657
6883
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#object-subrequest-modifier",
|
|
6658
6884
|
removed:true,
|
|
6659
6885
|
removal_message:"The `$object-subrequest` modifier is removed and is no longer supported.\nRules with it are considered as invalid." } };
|
|
6660
6886
|
|
|
6661
|
-
var data$
|
|
6887
|
+
var data$o = { adg_any:{ name:"object",
|
|
6662
6888
|
description:"The rule corresponds to browser plugins resources, e.g. Java or Flash",
|
|
6663
6889
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#object-modifier" },
|
|
6664
6890
|
abp_any:{ name:"object",
|
|
@@ -6668,7 +6894,7 @@ var data$n = { adg_any:{ name:"object",
|
|
|
6668
6894
|
description:"The rule corresponds to browser plugins resources, e.g. Java or Flash.",
|
|
6669
6895
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#type-options" } };
|
|
6670
6896
|
|
|
6671
|
-
var data$
|
|
6897
|
+
var data$n = { adg_any:{ name:"other",
|
|
6672
6898
|
description:"The rule applies to requests for which the type has not been determined\nor does not match the types listed above.",
|
|
6673
6899
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#other-modifier" },
|
|
6674
6900
|
abp_any:{ name:"other",
|
|
@@ -6678,7 +6904,7 @@ var data$m = { adg_any:{ name:"other",
|
|
|
6678
6904
|
description:"The rule applies to requests for which the type has not been determined\nor does not match the types listed above.",
|
|
6679
6905
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#type-options" } };
|
|
6680
6906
|
|
|
6681
|
-
var data$
|
|
6907
|
+
var data$m = { adg_os_any:{ name:"permissions",
|
|
6682
6908
|
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.",
|
|
6683
6909
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#permissions-modifier",
|
|
6684
6910
|
version_added:"CoreLibs 1.11",
|
|
@@ -6691,7 +6917,7 @@ var data$l = { adg_os_any:{ name:"permissions",
|
|
|
6691
6917
|
value_optional:true,
|
|
6692
6918
|
value_format:"permissions_value" } };
|
|
6693
6919
|
|
|
6694
|
-
var data$
|
|
6920
|
+
var data$l = { adg_any:{ name:"ping",
|
|
6695
6921
|
description:"The rule corresponds to requests caused by either navigator.sendBeacon() or the ping attribute on links.",
|
|
6696
6922
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#ping-modifier" },
|
|
6697
6923
|
abp_any:{ name:"ping",
|
|
@@ -6701,13 +6927,13 @@ var data$k = { adg_any:{ name:"ping",
|
|
|
6701
6927
|
description:"The rule corresponds to requests caused by either navigator.sendBeacon() or the ping attribute on links.",
|
|
6702
6928
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#type-options" } };
|
|
6703
6929
|
|
|
6704
|
-
var data$
|
|
6930
|
+
var data$k = { ubo_ext_any:{ name:"popunder",
|
|
6705
6931
|
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.",
|
|
6706
6932
|
docs:"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#popunder",
|
|
6707
6933
|
negatable:false,
|
|
6708
6934
|
block_only:true } };
|
|
6709
6935
|
|
|
6710
|
-
var data$
|
|
6936
|
+
var data$j = { adg_any:{ name:"popup",
|
|
6711
6937
|
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.",
|
|
6712
6938
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#popup-modifier",
|
|
6713
6939
|
negatable:false },
|
|
@@ -6720,7 +6946,7 @@ var data$i = { adg_any:{ name:"popup",
|
|
|
6720
6946
|
docs:"https://help.adblockplus.org/hc/en-us/articles/360062733293-How-to-write-filters#type-options",
|
|
6721
6947
|
negatable:false } };
|
|
6722
6948
|
|
|
6723
|
-
var data$
|
|
6949
|
+
var data$i = { adg_os_any:{ name:"redirect-rule",
|
|
6724
6950
|
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.",
|
|
6725
6951
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#redirect-rule-modifier",
|
|
6726
6952
|
conflicts:[ "domain",
|
|
@@ -6811,7 +7037,7 @@ var data$h = { adg_os_any:{ name:"redirect-rule",
|
|
|
6811
7037
|
negatable:false,
|
|
6812
7038
|
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]+)?$" } };
|
|
6813
7039
|
|
|
6814
|
-
var data$
|
|
7040
|
+
var data$h = { adg_os_any:{ name:"redirect",
|
|
6815
7041
|
description:"Used to redirect web requests to a local \"resource\".",
|
|
6816
7042
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#redirect-modifier",
|
|
6817
7043
|
assignable:true,
|
|
@@ -6839,6 +7065,17 @@ var data$g = { adg_os_any:{ name:"redirect",
|
|
|
6839
7065
|
negatable:false,
|
|
6840
7066
|
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 )$" } };
|
|
6841
7067
|
|
|
7068
|
+
var data$g = { adg_os_any:{ name:"referrerpolicy",
|
|
7069
|
+
description:"This modifier allows overriding of a page's referrer policy.",
|
|
7070
|
+
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#referrerpolicy-modifier",
|
|
7071
|
+
conflicts:[ "document",
|
|
7072
|
+
"subdocument" ],
|
|
7073
|
+
inverse_conflicts:true,
|
|
7074
|
+
assignable:true,
|
|
7075
|
+
negatable:false,
|
|
7076
|
+
value_optional:true,
|
|
7077
|
+
value_format:"referrerpolicy_value" } };
|
|
7078
|
+
|
|
6842
7079
|
var data$f = { adg_os_any:{ name:"removeheader",
|
|
6843
7080
|
description:"Rules with the `$removeheader` modifier are intended to remove headers from HTTP requests and responses.",
|
|
6844
7081
|
docs:"https://adguard.app/kb/general/ad-filtering/create-own-filters/#removeheader-modifier",
|
|
@@ -7148,46 +7385,47 @@ var data = { adg_any:{ name:"xmlhttprequest",
|
|
|
7148
7385
|
// @ts-nocheck
|
|
7149
7386
|
// Please keep imports and exports in alphabetical order
|
|
7150
7387
|
const rawModifiersData = {
|
|
7151
|
-
all: data$
|
|
7152
|
-
app: data$
|
|
7153
|
-
badfilter: data$
|
|
7154
|
-
cname: data$
|
|
7155
|
-
content: data$
|
|
7156
|
-
cookie: data$
|
|
7157
|
-
csp: data$
|
|
7158
|
-
denyallow: data$
|
|
7159
|
-
document: data$
|
|
7160
|
-
domain: data$
|
|
7161
|
-
elemhide: data$
|
|
7162
|
-
empty: data$
|
|
7163
|
-
firstParty: data$
|
|
7164
|
-
extension: data$
|
|
7165
|
-
font: data$
|
|
7166
|
-
genericblock: data$
|
|
7167
|
-
generichide: data$
|
|
7168
|
-
header: data$
|
|
7169
|
-
hls: data$
|
|
7170
|
-
image: data$
|
|
7171
|
-
important: data$
|
|
7172
|
-
inlineFont: data$
|
|
7173
|
-
inlineScript: data$
|
|
7174
|
-
jsinject: data$
|
|
7175
|
-
jsonprune: data$
|
|
7176
|
-
matchCase: data$
|
|
7177
|
-
media: data$
|
|
7178
|
-
method: data$
|
|
7179
|
-
mp4: data$
|
|
7180
|
-
network: data$
|
|
7181
|
-
noop: data$
|
|
7182
|
-
objectSubrequest: data$
|
|
7183
|
-
object: data$
|
|
7184
|
-
other: data$
|
|
7185
|
-
permissions: data$
|
|
7186
|
-
ping: data$
|
|
7187
|
-
popunder: data$
|
|
7188
|
-
popup: data$
|
|
7189
|
-
redirectRule: data$
|
|
7190
|
-
redirect: data$
|
|
7388
|
+
all: data$U,
|
|
7389
|
+
app: data$T,
|
|
7390
|
+
badfilter: data$S,
|
|
7391
|
+
cname: data$R,
|
|
7392
|
+
content: data$Q,
|
|
7393
|
+
cookie: data$P,
|
|
7394
|
+
csp: data$O,
|
|
7395
|
+
denyallow: data$N,
|
|
7396
|
+
document: data$M,
|
|
7397
|
+
domain: data$L,
|
|
7398
|
+
elemhide: data$K,
|
|
7399
|
+
empty: data$J,
|
|
7400
|
+
firstParty: data$I,
|
|
7401
|
+
extension: data$H,
|
|
7402
|
+
font: data$G,
|
|
7403
|
+
genericblock: data$F,
|
|
7404
|
+
generichide: data$E,
|
|
7405
|
+
header: data$D,
|
|
7406
|
+
hls: data$C,
|
|
7407
|
+
image: data$B,
|
|
7408
|
+
important: data$A,
|
|
7409
|
+
inlineFont: data$z,
|
|
7410
|
+
inlineScript: data$y,
|
|
7411
|
+
jsinject: data$x,
|
|
7412
|
+
jsonprune: data$w,
|
|
7413
|
+
matchCase: data$v,
|
|
7414
|
+
media: data$u,
|
|
7415
|
+
method: data$t,
|
|
7416
|
+
mp4: data$s,
|
|
7417
|
+
network: data$r,
|
|
7418
|
+
noop: data$q,
|
|
7419
|
+
objectSubrequest: data$p,
|
|
7420
|
+
object: data$o,
|
|
7421
|
+
other: data$n,
|
|
7422
|
+
permissions: data$m,
|
|
7423
|
+
ping: data$l,
|
|
7424
|
+
popunder: data$k,
|
|
7425
|
+
popup: data$j,
|
|
7426
|
+
redirectRule: data$i,
|
|
7427
|
+
redirect: data$h,
|
|
7428
|
+
referrerpolicy: data$g,
|
|
7191
7429
|
removeheader: data$f,
|
|
7192
7430
|
removeparam: data$e,
|
|
7193
7431
|
replace: data$d,
|
|
@@ -7380,7 +7618,7 @@ const ALLOWED_CSP_DIRECTIVES = new Set([
|
|
|
7380
7618
|
'worker-src',
|
|
7381
7619
|
]);
|
|
7382
7620
|
/**
|
|
7383
|
-
* Allowed
|
|
7621
|
+
* Allowed directives for $permissions modifier.
|
|
7384
7622
|
*
|
|
7385
7623
|
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#permissions-modifier}
|
|
7386
7624
|
*/
|
|
@@ -7430,6 +7668,21 @@ const PERMISSIONS_TOKEN_SELF = 'self';
|
|
|
7430
7668
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy#allowlists}
|
|
7431
7669
|
*/
|
|
7432
7670
|
const EMPTY_PERMISSIONS_ALLOWLIST = `${OPEN_PARENTHESIS}${CLOSE_PARENTHESIS}`;
|
|
7671
|
+
/**
|
|
7672
|
+
* Allowed directives for $referrerpolicy modifier.
|
|
7673
|
+
*
|
|
7674
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy}
|
|
7675
|
+
*/
|
|
7676
|
+
const REFERRER_POLICY_DIRECTIVES = new Set([
|
|
7677
|
+
'no-referrer',
|
|
7678
|
+
'no-referrer-when-downgrade',
|
|
7679
|
+
'origin',
|
|
7680
|
+
'origin-when-cross-origin',
|
|
7681
|
+
'same-origin',
|
|
7682
|
+
'strict-origin',
|
|
7683
|
+
'strict-origin-when-cross-origin',
|
|
7684
|
+
'unsafe-url',
|
|
7685
|
+
]);
|
|
7433
7686
|
/**
|
|
7434
7687
|
* Prefixes for error messages used in modifier validation.
|
|
7435
7688
|
*/
|
|
@@ -7442,6 +7695,7 @@ const VALIDATION_ERROR_PREFIX = {
|
|
|
7442
7695
|
INVALID_PERMISSION_DIRECTIVE: 'Invalid Permissions-Policy directive for the modifier',
|
|
7443
7696
|
INVALID_PERMISSION_ORIGINS: 'Origins in the value is invalid for the modifier and the directive',
|
|
7444
7697
|
INVALID_PERMISSION_ORIGIN_QUOTES: 'Double quotes should be used for origins in the value of the modifier',
|
|
7698
|
+
INVALID_REFERRER_POLICY_DIRECTIVE: 'Invalid Referrer-Policy directive for the modifier',
|
|
7445
7699
|
MIXED_NEGATIONS: 'Simultaneous usage of negated and not negated values is forbidden for the modifier',
|
|
7446
7700
|
NO_CSP_VALUE: 'No CSP value for the modifier and the directive',
|
|
7447
7701
|
NO_CSP_DIRECTIVE_QUOTE: 'CSP directives should no be quoted for the modifier',
|
|
@@ -7560,14 +7814,14 @@ const getSpecificBlockerData = (modifiersData, blockerPrefix, modifierName) => {
|
|
|
7560
7814
|
* @example
|
|
7561
7815
|
* `example.*` — matches with any TLD, e.g. `example.org`, `example.com`, etc.
|
|
7562
7816
|
*/
|
|
7563
|
-
const WILDCARD_TLD = DOT + WILDCARD
|
|
7817
|
+
const WILDCARD_TLD = DOT + WILDCARD;
|
|
7564
7818
|
/**
|
|
7565
7819
|
* Marker for a wildcard subdomain — `*.`.
|
|
7566
7820
|
*
|
|
7567
7821
|
* @example
|
|
7568
7822
|
* `*.example.org` — matches with any subdomain, e.g. `foo.example.org` or `bar.example.org`
|
|
7569
7823
|
*/
|
|
7570
|
-
const WILDCARD_SUBDOMAIN = WILDCARD
|
|
7824
|
+
const WILDCARD_SUBDOMAIN = WILDCARD + DOT;
|
|
7571
7825
|
class DomainUtils {
|
|
7572
7826
|
/**
|
|
7573
7827
|
* Check if the input is a valid domain or hostname.
|
|
@@ -7578,7 +7832,7 @@ class DomainUtils {
|
|
|
7578
7832
|
static isValidDomainOrHostname(domain) {
|
|
7579
7833
|
let domainToCheck = domain;
|
|
7580
7834
|
// Wildcard-only domain, typically a generic rule
|
|
7581
|
-
if (domainToCheck === WILDCARD
|
|
7835
|
+
if (domainToCheck === WILDCARD) {
|
|
7582
7836
|
return true;
|
|
7583
7837
|
}
|
|
7584
7838
|
// https://adguard.com/kb/general/ad-filtering/create-own-filters/#wildcard-for-tld
|
|
@@ -7765,6 +8019,7 @@ var CustomValueFormatValidatorName;
|
|
|
7765
8019
|
CustomValueFormatValidatorName["Domain"] = "pipe_separated_domains";
|
|
7766
8020
|
CustomValueFormatValidatorName["Method"] = "pipe_separated_methods";
|
|
7767
8021
|
CustomValueFormatValidatorName["Permissions"] = "permissions_value";
|
|
8022
|
+
CustomValueFormatValidatorName["ReferrerPolicy"] = "referrerpolicy_value";
|
|
7768
8023
|
CustomValueFormatValidatorName["StealthOption"] = "pipe_separated_stealth_options";
|
|
7769
8024
|
})(CustomValueFormatValidatorName || (CustomValueFormatValidatorName = {}));
|
|
7770
8025
|
/**
|
|
@@ -7798,7 +8053,7 @@ const isValidAppNameChunk = (chunk) => {
|
|
|
7798
8053
|
const isValidAppModifierValue = (value) => {
|
|
7799
8054
|
// $app modifier does not support wildcard tld
|
|
7800
8055
|
// https://adguard.app/kb/general/ad-filtering/create-own-filters/#app-modifier
|
|
7801
|
-
if (value.includes(WILDCARD
|
|
8056
|
+
if (value.includes(WILDCARD)) {
|
|
7802
8057
|
return false;
|
|
7803
8058
|
}
|
|
7804
8059
|
return value
|
|
@@ -7863,7 +8118,7 @@ const isValidDenyAllowModifierValue = (value) => {
|
|
|
7863
8118
|
// $denyallow modifier does not support wildcard tld
|
|
7864
8119
|
// https://adguard.app/kb/general/ad-filtering/create-own-filters/#denyallow-modifier
|
|
7865
8120
|
// but here we are simply checking whether the value contains wildcard `*`, not ends with `.*`
|
|
7866
|
-
if (value.includes(WILDCARD
|
|
8121
|
+
if (value.includes(WILDCARD)) {
|
|
7867
8122
|
return false;
|
|
7868
8123
|
}
|
|
7869
8124
|
// TODO: add cache for domains validation
|
|
@@ -8160,7 +8415,7 @@ const validatePermissionAllowlist = (allowlist, directive, modifierName) => {
|
|
|
8160
8415
|
// `*` is one of available permissions tokens
|
|
8161
8416
|
// e.g. 'fullscreen=*'
|
|
8162
8417
|
// https://w3c.github.io/webappsec-permissions-policy/#structured-header-serialization
|
|
8163
|
-
if (allowlist === WILDCARD
|
|
8418
|
+
if (allowlist === WILDCARD
|
|
8164
8419
|
// e.g. 'autoplay=()'
|
|
8165
8420
|
|| allowlist === EMPTY_PERMISSIONS_ALLOWLIST) {
|
|
8166
8421
|
return { valid: true };
|
|
@@ -8221,6 +8476,26 @@ const validatePermissions = (modifier) => {
|
|
|
8221
8476
|
}
|
|
8222
8477
|
return { valid: true };
|
|
8223
8478
|
};
|
|
8479
|
+
/**
|
|
8480
|
+
* Validates `referrerpolicy_value` custom value format.
|
|
8481
|
+
* Used for $referrerpolicy modifier.
|
|
8482
|
+
*
|
|
8483
|
+
* @param modifier Modifier AST node.
|
|
8484
|
+
*
|
|
8485
|
+
* @returns Validation result.
|
|
8486
|
+
*/
|
|
8487
|
+
const validateReferrerPolicy = (modifier) => {
|
|
8488
|
+
if (!modifier.value?.value) {
|
|
8489
|
+
return getValueRequiredValidationResult(modifier.modifier.value);
|
|
8490
|
+
}
|
|
8491
|
+
const modifierName = modifier.modifier.value;
|
|
8492
|
+
const modifierValue = modifier.value.value;
|
|
8493
|
+
if (!REFERRER_POLICY_DIRECTIVES.has(modifierValue)) {
|
|
8494
|
+
// eslint-disable-next-line max-len
|
|
8495
|
+
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_REFERRER_POLICY_DIRECTIVE}: '${modifierName}': '${modifierValue}'`);
|
|
8496
|
+
}
|
|
8497
|
+
return { valid: true };
|
|
8498
|
+
};
|
|
8224
8499
|
/**
|
|
8225
8500
|
* Map of all available pre-defined validators for modifiers with custom `value_format`.
|
|
8226
8501
|
*/
|
|
@@ -8231,6 +8506,7 @@ const CUSTOM_VALUE_FORMAT_MAP = {
|
|
|
8231
8506
|
[CustomValueFormatValidatorName.Domain]: validatePipeSeparatedDomains,
|
|
8232
8507
|
[CustomValueFormatValidatorName.Method]: validatePipeSeparatedMethods,
|
|
8233
8508
|
[CustomValueFormatValidatorName.Permissions]: validatePermissions,
|
|
8509
|
+
[CustomValueFormatValidatorName.ReferrerPolicy]: validateReferrerPolicy,
|
|
8234
8510
|
[CustomValueFormatValidatorName.StealthOption]: validatePipeSeparatedStealthOptions,
|
|
8235
8511
|
};
|
|
8236
8512
|
/**
|
|
@@ -8424,7 +8700,7 @@ class ModifierValidator {
|
|
|
8424
8700
|
* @returns Result of modifier validation.
|
|
8425
8701
|
*/
|
|
8426
8702
|
validate = (syntax, rawModifier, isException = false) => {
|
|
8427
|
-
const modifier =
|
|
8703
|
+
const modifier = clone(rawModifier);
|
|
8428
8704
|
// special case: handle noop modifier which may be used as multiple underscores (not just one)
|
|
8429
8705
|
// https://adguard.com/kb/general/ad-filtering/create-own-filters/#noop-modifier
|
|
8430
8706
|
if (modifier.modifier.value.startsWith(UNDERSCORE)) {
|
|
@@ -8503,7 +8779,9 @@ class ConverterBase {
|
|
|
8503
8779
|
* Converts some data to AdGuard format
|
|
8504
8780
|
*
|
|
8505
8781
|
* @param data Data to convert
|
|
8506
|
-
* @returns
|
|
8782
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
8783
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
8784
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
8507
8785
|
* @throws If the data is invalid or incompatible
|
|
8508
8786
|
*/
|
|
8509
8787
|
static convertToAdg(data) {
|
|
@@ -8513,7 +8791,9 @@ class ConverterBase {
|
|
|
8513
8791
|
* Converts some data to Adblock Plus format
|
|
8514
8792
|
*
|
|
8515
8793
|
* @param data Data to convert
|
|
8516
|
-
* @returns
|
|
8794
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
8795
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
8796
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
8517
8797
|
* @throws If the data is invalid or incompatible
|
|
8518
8798
|
*/
|
|
8519
8799
|
static convertToAbp(data) {
|
|
@@ -8523,7 +8803,9 @@ class ConverterBase {
|
|
|
8523
8803
|
* Converts some data to uBlock Origin format
|
|
8524
8804
|
*
|
|
8525
8805
|
* @param data Data to convert
|
|
8526
|
-
* @returns
|
|
8806
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
8807
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
8808
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
8527
8809
|
* @throws If the data is invalid or incompatible
|
|
8528
8810
|
*/
|
|
8529
8811
|
static convertToUbo(data) {
|
|
@@ -8547,7 +8829,9 @@ class RuleConverterBase extends ConverterBase {
|
|
|
8547
8829
|
* Converts an adblock filtering rule to AdGuard format, if possible.
|
|
8548
8830
|
*
|
|
8549
8831
|
* @param rule Rule node to convert
|
|
8550
|
-
* @returns
|
|
8832
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
8833
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
8834
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8551
8835
|
* @throws If the rule is invalid or cannot be converted
|
|
8552
8836
|
*/
|
|
8553
8837
|
static convertToAdg(rule) {
|
|
@@ -8557,7 +8841,9 @@ class RuleConverterBase extends ConverterBase {
|
|
|
8557
8841
|
* Converts an adblock filtering rule to Adblock Plus format, if possible.
|
|
8558
8842
|
*
|
|
8559
8843
|
* @param rule Rule node to convert
|
|
8560
|
-
* @returns
|
|
8844
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
8845
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
8846
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8561
8847
|
* @throws If the rule is invalid or cannot be converted
|
|
8562
8848
|
*/
|
|
8563
8849
|
static convertToAbp(rule) {
|
|
@@ -8567,7 +8853,9 @@ class RuleConverterBase extends ConverterBase {
|
|
|
8567
8853
|
* Converts an adblock filtering rule to uBlock Origin format, if possible.
|
|
8568
8854
|
*
|
|
8569
8855
|
* @param rule Rule node to convert
|
|
8570
|
-
* @returns
|
|
8856
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
8857
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
8858
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8571
8859
|
* @throws If the rule is invalid or cannot be converted
|
|
8572
8860
|
*/
|
|
8573
8861
|
static convertToUbo(rule) {
|
|
@@ -8575,6 +8863,37 @@ class RuleConverterBase extends ConverterBase {
|
|
|
8575
8863
|
}
|
|
8576
8864
|
}
|
|
8577
8865
|
|
|
8866
|
+
/**
|
|
8867
|
+
* @file Conversion result interface and helper functions
|
|
8868
|
+
*/
|
|
8869
|
+
/**
|
|
8870
|
+
* Helper function to create a generic conversion result.
|
|
8871
|
+
*
|
|
8872
|
+
* @param result Conversion result
|
|
8873
|
+
* @param isConverted Indicates whether the input item was converted
|
|
8874
|
+
* @template T Type of the item to convert
|
|
8875
|
+
* @template U Type of the conversion result (defaults to `T`, but can be `T[]` as well)
|
|
8876
|
+
* @returns Generic conversion result
|
|
8877
|
+
*/
|
|
8878
|
+
// eslint-disable-next-line max-len
|
|
8879
|
+
function createConversionResult(result, isConverted) {
|
|
8880
|
+
return {
|
|
8881
|
+
result,
|
|
8882
|
+
isConverted,
|
|
8883
|
+
};
|
|
8884
|
+
}
|
|
8885
|
+
/**
|
|
8886
|
+
* Helper function to create a node conversion result.
|
|
8887
|
+
*
|
|
8888
|
+
* @param nodes Array of nodes
|
|
8889
|
+
* @param isConverted Indicates whether the input item was converted
|
|
8890
|
+
* @template T Type of the node (extends `Node`)
|
|
8891
|
+
* @returns Node conversion result
|
|
8892
|
+
*/
|
|
8893
|
+
function createNodeConversionResult(nodes, isConverted) {
|
|
8894
|
+
return createConversionResult(nodes, isConverted);
|
|
8895
|
+
}
|
|
8896
|
+
|
|
8578
8897
|
/**
|
|
8579
8898
|
* @file Comment rule converter
|
|
8580
8899
|
*/
|
|
@@ -8588,27 +8907,30 @@ class CommentRuleConverter extends RuleConverterBase {
|
|
|
8588
8907
|
* Converts a comment rule to AdGuard format, if possible.
|
|
8589
8908
|
*
|
|
8590
8909
|
* @param rule Rule node to convert
|
|
8591
|
-
* @returns
|
|
8910
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
8911
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
8912
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8592
8913
|
* @throws If the rule is invalid or cannot be converted
|
|
8593
8914
|
*/
|
|
8594
8915
|
static convertToAdg(rule) {
|
|
8595
|
-
// Clone the provided AST node to avoid side effects
|
|
8596
|
-
const ruleNode = cloneDeep(rule);
|
|
8597
8916
|
// TODO: Add support for other comment types, if needed
|
|
8598
8917
|
// Main task is # -> ! conversion
|
|
8599
|
-
switch (
|
|
8918
|
+
switch (rule.type) {
|
|
8600
8919
|
case exports.CommentRuleType.CommentRule:
|
|
8601
|
-
//
|
|
8602
|
-
if (
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8920
|
+
// Check if the rule needs to be converted
|
|
8921
|
+
if (rule.type === exports.CommentRuleType.CommentRule && rule.marker.value === exports.CommentMarker.Hashmark) {
|
|
8922
|
+
// Add a ! to the beginning of the comment
|
|
8923
|
+
// TODO: Replace with custom clone method
|
|
8924
|
+
const ruleClone = clone(rule);
|
|
8925
|
+
ruleClone.marker.value = exports.CommentMarker.Regular;
|
|
8926
|
+
// Add the hashmark to the beginning of the comment text
|
|
8927
|
+
ruleClone.text.value = `${SPACE}${exports.CommentMarker.Hashmark}${ruleClone.text.value}`;
|
|
8928
|
+
return createNodeConversionResult([ruleClone], true);
|
|
8607
8929
|
}
|
|
8608
|
-
return [
|
|
8930
|
+
return createNodeConversionResult([rule], false);
|
|
8609
8931
|
// Leave any other comment rule as is
|
|
8610
8932
|
default:
|
|
8611
|
-
return [
|
|
8933
|
+
return createNodeConversionResult([rule], false);
|
|
8612
8934
|
}
|
|
8613
8935
|
}
|
|
8614
8936
|
}
|
|
@@ -8778,6 +9100,58 @@ class RegExpUtils {
|
|
|
8778
9100
|
}
|
|
8779
9101
|
}
|
|
8780
9102
|
|
|
9103
|
+
/**
|
|
9104
|
+
* @file Custom clone functions for AST nodes, this is probably the most efficient way to clone AST nodes.
|
|
9105
|
+
* @todo Maybe move them to parser classes as 'clone' methods
|
|
9106
|
+
*/
|
|
9107
|
+
/**
|
|
9108
|
+
* Clones a scriptlet rule node.
|
|
9109
|
+
*
|
|
9110
|
+
* @param node Node to clone
|
|
9111
|
+
* @returns Cloned node
|
|
9112
|
+
*/
|
|
9113
|
+
function cloneScriptletRuleNode(node) {
|
|
9114
|
+
return {
|
|
9115
|
+
type: node.type,
|
|
9116
|
+
children: node.children.map((child) => ({ ...child })),
|
|
9117
|
+
};
|
|
9118
|
+
}
|
|
9119
|
+
/**
|
|
9120
|
+
* Clones a domain list node.
|
|
9121
|
+
*
|
|
9122
|
+
* @param node Node to clone
|
|
9123
|
+
* @returns Cloned node
|
|
9124
|
+
*/
|
|
9125
|
+
function cloneDomainListNode(node) {
|
|
9126
|
+
return {
|
|
9127
|
+
type: node.type,
|
|
9128
|
+
separator: node.separator,
|
|
9129
|
+
children: node.children.map((domain) => ({ ...domain })),
|
|
9130
|
+
};
|
|
9131
|
+
}
|
|
9132
|
+
/**
|
|
9133
|
+
* Clones a modifier list node.
|
|
9134
|
+
*
|
|
9135
|
+
* @param node Node to clone
|
|
9136
|
+
* @returns Cloned node
|
|
9137
|
+
*/
|
|
9138
|
+
function cloneModifierListNode(node) {
|
|
9139
|
+
return {
|
|
9140
|
+
type: node.type,
|
|
9141
|
+
children: node.children.map((modifier) => {
|
|
9142
|
+
const res = {
|
|
9143
|
+
type: modifier.type,
|
|
9144
|
+
exception: modifier.exception,
|
|
9145
|
+
modifier: { ...modifier.modifier },
|
|
9146
|
+
};
|
|
9147
|
+
if (modifier.value) {
|
|
9148
|
+
res.value = { ...modifier.value };
|
|
9149
|
+
}
|
|
9150
|
+
return res;
|
|
9151
|
+
}),
|
|
9152
|
+
};
|
|
9153
|
+
}
|
|
9154
|
+
|
|
8781
9155
|
/**
|
|
8782
9156
|
* @file HTML filtering rule converter
|
|
8783
9157
|
*/
|
|
@@ -8790,16 +9164,22 @@ class RegExpUtils {
|
|
|
8790
9164
|
*
|
|
8791
9165
|
* @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#html-filtering-rules}
|
|
8792
9166
|
*/
|
|
8793
|
-
const
|
|
8794
|
-
const
|
|
9167
|
+
const ADG_HTML_DEFAULT_MAX_LENGTH = 8192;
|
|
9168
|
+
const ADG_HTML_CONVERSION_MAX_LENGTH = ADG_HTML_DEFAULT_MAX_LENGTH * 32;
|
|
8795
9169
|
const NOT_SPECIFIED = -1;
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
9170
|
+
var PseudoClasses$1;
|
|
9171
|
+
(function (PseudoClasses) {
|
|
9172
|
+
PseudoClasses["Contains"] = "contains";
|
|
9173
|
+
PseudoClasses["HasText"] = "has-text";
|
|
9174
|
+
PseudoClasses["MinTextLength"] = "min-text-length";
|
|
9175
|
+
})(PseudoClasses$1 || (PseudoClasses$1 = {}));
|
|
9176
|
+
var AttributeSelectors;
|
|
9177
|
+
(function (AttributeSelectors) {
|
|
9178
|
+
AttributeSelectors["MaxLength"] = "max-length";
|
|
9179
|
+
AttributeSelectors["MinLength"] = "min-length";
|
|
9180
|
+
AttributeSelectors["TagContent"] = "tag-content";
|
|
9181
|
+
AttributeSelectors["Wildcard"] = "wildcard";
|
|
9182
|
+
})(AttributeSelectors || (AttributeSelectors = {}));
|
|
8803
9183
|
/**
|
|
8804
9184
|
* HTML filtering rule converter class
|
|
8805
9185
|
*
|
|
@@ -8822,16 +9202,23 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8822
9202
|
* ```
|
|
8823
9203
|
*
|
|
8824
9204
|
* @param rule Rule node to convert
|
|
8825
|
-
* @returns
|
|
9205
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9206
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9207
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
8826
9208
|
* @throws If the rule is invalid or cannot be converted
|
|
8827
9209
|
*/
|
|
8828
9210
|
static convertToAdg(rule) {
|
|
8829
|
-
//
|
|
8830
|
-
|
|
9211
|
+
// Ignore AdGuard rules
|
|
9212
|
+
if (rule.syntax === exports.AdblockSyntax.Adg) {
|
|
9213
|
+
return createNodeConversionResult([rule], false);
|
|
9214
|
+
}
|
|
9215
|
+
if (rule.syntax === exports.AdblockSyntax.Abp) {
|
|
9216
|
+
throw new RuleConversionError('Invalid rule, ABP does not support HTML filtering rules');
|
|
9217
|
+
}
|
|
8831
9218
|
// Prepare the conversion result
|
|
8832
9219
|
const conversionResult = [];
|
|
8833
9220
|
// Iterate over selector list
|
|
8834
|
-
for (const selector of
|
|
9221
|
+
for (const selector of rule.body.body.children) {
|
|
8835
9222
|
// Check selector, just in case
|
|
8836
9223
|
if (selector.type !== exports.CssTreeNodeType.Selector) {
|
|
8837
9224
|
throw new RuleConversionError(`Expected selector, got '${selector.type}'`);
|
|
@@ -8858,24 +9245,24 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8858
9245
|
throw new RuleConversionError('Tag selector should be the first child, if present');
|
|
8859
9246
|
}
|
|
8860
9247
|
// Simply store the tag selector
|
|
8861
|
-
convertedSelector.children.push(
|
|
9248
|
+
convertedSelector.children.push(clone(node));
|
|
8862
9249
|
break;
|
|
8863
9250
|
case exports.CssTreeNodeType.AttributeSelector:
|
|
8864
9251
|
// Check if the attribute selector is a special AdGuard attribute
|
|
8865
9252
|
switch (node.name.name) {
|
|
8866
|
-
case
|
|
9253
|
+
case AttributeSelectors.MinLength:
|
|
8867
9254
|
minLength = CssTree.parseAttributeSelectorValueAsNumber(node);
|
|
8868
9255
|
break;
|
|
8869
|
-
case
|
|
9256
|
+
case AttributeSelectors.MaxLength:
|
|
8870
9257
|
maxLength = CssTree.parseAttributeSelectorValueAsNumber(node);
|
|
8871
9258
|
break;
|
|
8872
|
-
case
|
|
8873
|
-
case
|
|
9259
|
+
case AttributeSelectors.TagContent:
|
|
9260
|
+
case AttributeSelectors.Wildcard:
|
|
8874
9261
|
CssTree.assertAttributeSelectorHasStringValue(node);
|
|
8875
|
-
convertedSelector.children.push(
|
|
9262
|
+
convertedSelector.children.push(clone(node));
|
|
8876
9263
|
break;
|
|
8877
9264
|
default:
|
|
8878
|
-
convertedSelector.children.push(
|
|
9265
|
+
convertedSelector.children.push(clone(node));
|
|
8879
9266
|
}
|
|
8880
9267
|
break;
|
|
8881
9268
|
case exports.CssTreeNodeType.PseudoClassSelector:
|
|
@@ -8889,18 +9276,18 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8889
9276
|
}
|
|
8890
9277
|
// Process the pseudo class based on its name
|
|
8891
9278
|
switch (node.name) {
|
|
8892
|
-
case
|
|
8893
|
-
case
|
|
9279
|
+
case PseudoClasses$1.HasText:
|
|
9280
|
+
case PseudoClasses$1.Contains:
|
|
8894
9281
|
// Check if the argument is a RegExp
|
|
8895
9282
|
if (RegExpUtils.isRegexPattern(arg.value)) {
|
|
8896
9283
|
// TODO: Add some support for RegExp patterns later
|
|
8897
9284
|
// Need to find a way to convert some RegExp patterns to glob patterns
|
|
8898
9285
|
throw new RuleConversionError('Conversion of RegExp patterns is not yet supported');
|
|
8899
9286
|
}
|
|
8900
|
-
convertedSelector.children.push(CssTree.createAttributeSelectorNode(
|
|
9287
|
+
convertedSelector.children.push(CssTree.createAttributeSelectorNode(AttributeSelectors.TagContent, arg.value));
|
|
8901
9288
|
break;
|
|
8902
9289
|
// https://github.com/gorhill/uBlock/wiki/Procedural-cosmetic-filters#subjectmin-text-lengthn
|
|
8903
|
-
case
|
|
9290
|
+
case PseudoClasses$1.MinTextLength:
|
|
8904
9291
|
minLength = CssTree.parsePseudoClassArgumentAsNumber(node);
|
|
8905
9292
|
break;
|
|
8906
9293
|
default:
|
|
@@ -8912,10 +9299,10 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8912
9299
|
}
|
|
8913
9300
|
}
|
|
8914
9301
|
if (minLength !== NOT_SPECIFIED) {
|
|
8915
|
-
convertedSelector.children.push(CssTree.createAttributeSelectorNode(
|
|
9302
|
+
convertedSelector.children.push(CssTree.createAttributeSelectorNode(AttributeSelectors.MinLength, String(minLength)));
|
|
8916
9303
|
}
|
|
8917
|
-
convertedSelector.children.push(CssTree.createAttributeSelectorNode(
|
|
8918
|
-
?
|
|
9304
|
+
convertedSelector.children.push(CssTree.createAttributeSelectorNode(AttributeSelectors.MaxLength, String(maxLength === NOT_SPECIFIED
|
|
9305
|
+
? ADG_HTML_CONVERSION_MAX_LENGTH
|
|
8919
9306
|
: maxLength)));
|
|
8920
9307
|
// Create the converted rule
|
|
8921
9308
|
conversionResult.push({
|
|
@@ -8925,7 +9312,7 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8925
9312
|
// Convert the separator based on the exception status
|
|
8926
9313
|
separator: {
|
|
8927
9314
|
type: 'Value',
|
|
8928
|
-
value:
|
|
9315
|
+
value: rule.exception
|
|
8929
9316
|
? exports.CosmeticRuleSeparator.AdgHtmlFilteringException
|
|
8930
9317
|
: exports.CosmeticRuleSeparator.AdgHtmlFiltering,
|
|
8931
9318
|
},
|
|
@@ -8940,11 +9327,11 @@ class HtmlRuleConverter extends RuleConverterBase {
|
|
|
8940
9327
|
}],
|
|
8941
9328
|
},
|
|
8942
9329
|
},
|
|
8943
|
-
exception:
|
|
8944
|
-
domains:
|
|
9330
|
+
exception: rule.exception,
|
|
9331
|
+
domains: cloneDomainListNode(rule.domains),
|
|
8945
9332
|
});
|
|
8946
9333
|
}
|
|
8947
|
-
return conversionResult;
|
|
9334
|
+
return createNodeConversionResult(conversionResult, true);
|
|
8948
9335
|
}
|
|
8949
9336
|
}
|
|
8950
9337
|
|
|
@@ -8965,96 +9352,38 @@ function getScriptletName(scriptletNode) {
|
|
|
8965
9352
|
return scriptletNode.children[0].value;
|
|
8966
9353
|
}
|
|
8967
9354
|
/**
|
|
8968
|
-
* Set name of the scriptlet
|
|
9355
|
+
* Set name of the scriptlet.
|
|
9356
|
+
* Modifies input `scriptletNode` if needed.
|
|
8969
9357
|
*
|
|
8970
9358
|
* @param scriptletNode Scriptlet node to set name of
|
|
8971
9359
|
* @param name Name to set
|
|
8972
|
-
* @returns Scriptlet node with the specified name
|
|
8973
|
-
* @throws If the scriptlet is empty
|
|
8974
9360
|
*/
|
|
8975
9361
|
function setScriptletName(scriptletNode, name) {
|
|
8976
|
-
if (scriptletNode.children.length
|
|
8977
|
-
|
|
9362
|
+
if (scriptletNode.children.length > 0) {
|
|
9363
|
+
// eslint-disable-next-line no-param-reassign
|
|
9364
|
+
scriptletNode.children[0].value = name;
|
|
8978
9365
|
}
|
|
8979
|
-
const scriptletNodeClone = cloneDeep(scriptletNode);
|
|
8980
|
-
scriptletNodeClone.children[0].value = name;
|
|
8981
|
-
return scriptletNodeClone;
|
|
8982
9366
|
}
|
|
8983
9367
|
/**
|
|
8984
9368
|
* Set quote type of the scriptlet parameters
|
|
8985
9369
|
*
|
|
8986
9370
|
* @param scriptletNode Scriptlet node to set quote type of
|
|
8987
9371
|
* @param quoteType Preferred quote type
|
|
8988
|
-
* @returns Scriptlet node with the specified quote type
|
|
8989
9372
|
*/
|
|
8990
9373
|
function setScriptletQuoteType(scriptletNode, quoteType) {
|
|
8991
|
-
if (scriptletNode.children.length
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
for (let i = 0; i < scriptletNodeClone.children.length; i += 1) {
|
|
8996
|
-
scriptletNodeClone.children[i].value = QuoteUtils.setStringQuoteType(scriptletNodeClone.children[i].value, quoteType);
|
|
8997
|
-
}
|
|
8998
|
-
return scriptletNodeClone;
|
|
8999
|
-
}
|
|
9000
|
-
|
|
9001
|
-
/**
|
|
9002
|
-
* @file Scriptlet conversions from ABP and uBO to ADG
|
|
9003
|
-
*/
|
|
9004
|
-
const ABP_SCRIPTLET_PREFIX = 'abp-';
|
|
9005
|
-
const UBO_SCRIPTLET_PREFIX = 'ubo-';
|
|
9006
|
-
/**
|
|
9007
|
-
* Helper class for converting scriptlets from ABP and uBO to ADG
|
|
9008
|
-
*/
|
|
9009
|
-
class AdgScriptletConverter {
|
|
9010
|
-
/**
|
|
9011
|
-
* Helper function to convert scriptlets to ADG. We implement the core
|
|
9012
|
-
* logic here to avoid code duplication.
|
|
9013
|
-
*
|
|
9014
|
-
* @param scriptletNode Scriptlet parameter list node to convert
|
|
9015
|
-
* @param prefix Prefix to add to the scriptlet name
|
|
9016
|
-
* @returns Converted scriptlet parameter list node
|
|
9017
|
-
*/
|
|
9018
|
-
static convertToAdg(scriptletNode, prefix) {
|
|
9019
|
-
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
9020
|
-
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletNode), exports.QuoteType.None);
|
|
9021
|
-
// Clone the node to avoid any side effects
|
|
9022
|
-
let result = cloneDeep(scriptletNode);
|
|
9023
|
-
// Only add prefix if it's not already there
|
|
9024
|
-
if (!scriptletName.startsWith(prefix)) {
|
|
9025
|
-
result = setScriptletName(scriptletNode, `${prefix}${scriptletName}`);
|
|
9374
|
+
if (scriptletNode.children.length > 0) {
|
|
9375
|
+
for (let i = 0; i < scriptletNode.children.length; i += 1) {
|
|
9376
|
+
// eslint-disable-next-line no-param-reassign
|
|
9377
|
+
scriptletNode.children[i].value = QuoteUtils.setStringQuoteType(scriptletNode.children[i].value, quoteType);
|
|
9026
9378
|
}
|
|
9027
|
-
// ADG scriptlet parameters should be quoted, and single quoted are preferred
|
|
9028
|
-
result = setScriptletQuoteType(result, exports.QuoteType.Single);
|
|
9029
|
-
return result;
|
|
9030
9379
|
}
|
|
9031
|
-
/**
|
|
9032
|
-
* Converts an ABP snippet node to ADG scriptlet node, if possible.
|
|
9033
|
-
*
|
|
9034
|
-
* @param scriptletNode Scriptlet node to convert
|
|
9035
|
-
* @returns Converted scriptlet node
|
|
9036
|
-
* @throws If the scriptlet isn't supported by ADG or is invalid
|
|
9037
|
-
* @see {@link https://help.adblockplus.org/hc/en-us/articles/1500002338501#snippets-ref}
|
|
9038
|
-
*/
|
|
9039
|
-
static convertFromAbp = (scriptletNode) => {
|
|
9040
|
-
return AdgScriptletConverter.convertToAdg(scriptletNode, ABP_SCRIPTLET_PREFIX);
|
|
9041
|
-
};
|
|
9042
|
-
/**
|
|
9043
|
-
* Convert a uBO scriptlet node to ADG scriptlet node, if possible.
|
|
9044
|
-
*
|
|
9045
|
-
* @param scriptletNode Scriptlet node to convert
|
|
9046
|
-
* @returns Converted scriptlet node
|
|
9047
|
-
* @throws If the scriptlet isn't supported by ADG or is invalid
|
|
9048
|
-
* @see {@link https://github.com/gorhill/uBlock/wiki/Resources-Library#available-general-purpose-scriptlets}
|
|
9049
|
-
*/
|
|
9050
|
-
static convertFromUbo = (scriptletNode) => {
|
|
9051
|
-
return AdgScriptletConverter.convertToAdg(scriptletNode, UBO_SCRIPTLET_PREFIX);
|
|
9052
|
-
};
|
|
9053
9380
|
}
|
|
9054
9381
|
|
|
9055
9382
|
/**
|
|
9056
9383
|
* @file Scriptlet injection rule converter
|
|
9057
9384
|
*/
|
|
9385
|
+
const ABP_SCRIPTLET_PREFIX = 'abp-';
|
|
9386
|
+
const UBO_SCRIPTLET_PREFIX = 'ubo-';
|
|
9058
9387
|
/**
|
|
9059
9388
|
* Scriptlet injection rule converter class
|
|
9060
9389
|
*
|
|
@@ -9065,38 +9394,91 @@ class ScriptletRuleConverter extends RuleConverterBase {
|
|
|
9065
9394
|
* Converts a scriptlet injection rule to AdGuard format, if possible.
|
|
9066
9395
|
*
|
|
9067
9396
|
* @param rule Rule node to convert
|
|
9068
|
-
* @returns
|
|
9397
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9398
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9399
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9069
9400
|
* @throws If the rule is invalid or cannot be converted
|
|
9070
9401
|
*/
|
|
9071
9402
|
static convertToAdg(rule) {
|
|
9072
|
-
//
|
|
9073
|
-
|
|
9403
|
+
// Ignore AdGuard rules
|
|
9404
|
+
if (rule.syntax === exports.AdblockSyntax.Adg) {
|
|
9405
|
+
return createNodeConversionResult([rule], false);
|
|
9406
|
+
}
|
|
9407
|
+
const separator = rule.separator.value;
|
|
9408
|
+
let convertedSeparator = separator;
|
|
9409
|
+
convertedSeparator = rule.exception
|
|
9410
|
+
? exports.CosmeticRuleSeparator.AdgJsInjectionException
|
|
9411
|
+
: exports.CosmeticRuleSeparator.AdgJsInjection;
|
|
9074
9412
|
const convertedScriptlets = [];
|
|
9075
|
-
for (const scriptlet of
|
|
9076
|
-
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9413
|
+
for (const scriptlet of rule.body.children) {
|
|
9414
|
+
// Clone the node to avoid any side effects
|
|
9415
|
+
const scriptletClone = cloneScriptletRuleNode(scriptlet);
|
|
9416
|
+
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
9417
|
+
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletClone), exports.QuoteType.None);
|
|
9418
|
+
// Add prefix if it's not already there
|
|
9419
|
+
let prefix;
|
|
9420
|
+
switch (rule.syntax) {
|
|
9421
|
+
case exports.AdblockSyntax.Abp:
|
|
9422
|
+
prefix = ABP_SCRIPTLET_PREFIX;
|
|
9423
|
+
break;
|
|
9424
|
+
case exports.AdblockSyntax.Ubo:
|
|
9425
|
+
prefix = UBO_SCRIPTLET_PREFIX;
|
|
9426
|
+
break;
|
|
9427
|
+
default:
|
|
9428
|
+
prefix = EMPTY;
|
|
9081
9429
|
}
|
|
9082
|
-
|
|
9083
|
-
|
|
9430
|
+
if (!scriptletName.startsWith(prefix)) {
|
|
9431
|
+
setScriptletName(scriptletClone, `${prefix}${scriptletName}`);
|
|
9084
9432
|
}
|
|
9433
|
+
// ADG scriptlet parameters should be quoted, and single quoted are preferred
|
|
9434
|
+
setScriptletQuoteType(scriptletClone, exports.QuoteType.Single);
|
|
9435
|
+
convertedScriptlets.push(scriptletClone);
|
|
9085
9436
|
}
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
return convertedScriptlets.map((scriptlet) => {
|
|
9091
|
-
return {
|
|
9092
|
-
...ruleNode,
|
|
9437
|
+
return createNodeConversionResult(convertedScriptlets.map((scriptlet) => {
|
|
9438
|
+
const res = {
|
|
9439
|
+
category: rule.category,
|
|
9440
|
+
type: rule.type,
|
|
9093
9441
|
syntax: exports.AdblockSyntax.Adg,
|
|
9442
|
+
exception: rule.exception,
|
|
9443
|
+
domains: cloneDomainListNode(rule.domains),
|
|
9444
|
+
separator: {
|
|
9445
|
+
type: 'Value',
|
|
9446
|
+
value: convertedSeparator,
|
|
9447
|
+
},
|
|
9094
9448
|
body: {
|
|
9095
|
-
|
|
9449
|
+
type: rule.body.type,
|
|
9096
9450
|
children: [scriptlet],
|
|
9097
9451
|
},
|
|
9098
9452
|
};
|
|
9099
|
-
|
|
9453
|
+
if (rule.modifiers) {
|
|
9454
|
+
res.modifiers = cloneModifierListNode(rule.modifiers);
|
|
9455
|
+
}
|
|
9456
|
+
return res;
|
|
9457
|
+
}), true);
|
|
9458
|
+
}
|
|
9459
|
+
}
|
|
9460
|
+
|
|
9461
|
+
/**
|
|
9462
|
+
* A very simple map extension that allows to store multiple values for the same key
|
|
9463
|
+
* by storing them in an array.
|
|
9464
|
+
*
|
|
9465
|
+
* @todo Add more methods if needed
|
|
9466
|
+
*/
|
|
9467
|
+
class MultiValueMap extends Map {
|
|
9468
|
+
/**
|
|
9469
|
+
* Adds a value to the map. If the key already exists, the value will be appended to the existing array,
|
|
9470
|
+
* otherwise a new array will be created for the key.
|
|
9471
|
+
*
|
|
9472
|
+
* @param key Key to add
|
|
9473
|
+
* @param values Value(s) to add
|
|
9474
|
+
*/
|
|
9475
|
+
add(key, ...values) {
|
|
9476
|
+
let currentValues = super.get(key);
|
|
9477
|
+
if (isUndefined(currentValues)) {
|
|
9478
|
+
currentValues = [];
|
|
9479
|
+
super.set(key, values);
|
|
9480
|
+
}
|
|
9481
|
+
currentValues.push(...values);
|
|
9100
9482
|
}
|
|
9101
9483
|
}
|
|
9102
9484
|
|
|
@@ -9122,69 +9504,115 @@ class AdgCosmeticRuleModifierConverter {
|
|
|
9122
9504
|
* Converts a uBO cosmetic rule modifier list to ADG, if possible.
|
|
9123
9505
|
*
|
|
9124
9506
|
* @param modifierList Cosmetic rule modifier list node to convert
|
|
9125
|
-
* @returns
|
|
9507
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9508
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9509
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9126
9510
|
* @throws If the modifier list cannot be converted
|
|
9127
9511
|
* @see {@link https://github.com/gorhill/uBlock/wiki/Procedural-cosmetic-filters#cosmetic-filter-operators}
|
|
9128
9512
|
*/
|
|
9129
|
-
static convertFromUbo
|
|
9130
|
-
const
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
//
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
? `${REGEX_MARKER}${RegExpUtils.negateRegexPattern(RegExpUtils.patternToRegexp(modifierValue))}${REGEX_MARKER}`
|
|
9148
|
-
: modifierValue));
|
|
9149
|
-
break;
|
|
9150
|
-
default:
|
|
9151
|
-
// Leave the modifier as-is
|
|
9152
|
-
convertedModifierList.children.push(modifier);
|
|
9513
|
+
static convertFromUbo(modifierList) {
|
|
9514
|
+
const conversionMap = new MultiValueMap();
|
|
9515
|
+
modifierList.children.forEach((modifier, index) => {
|
|
9516
|
+
// :matches-path
|
|
9517
|
+
if (modifier.modifier.value === UBO_MATCHES_PATH_OPERATOR) {
|
|
9518
|
+
if (!modifier.value) {
|
|
9519
|
+
throw new RuleConversionError(`'${UBO_MATCHES_PATH_OPERATOR}' operator requires a value`);
|
|
9520
|
+
}
|
|
9521
|
+
const value = RegExpUtils.isRegexPattern(modifier.value.value)
|
|
9522
|
+
? StringUtils.escapeCharacters(modifier.value.value, SPECIAL_MODIFIER_REGEX_CHARS)
|
|
9523
|
+
: modifier.value.value;
|
|
9524
|
+
// Convert uBO's `:matches-path(...)` operator to ADG's `$path=...` modifier
|
|
9525
|
+
conversionMap.add(index, createModifierNode(ADG_PATH_MODIFIER,
|
|
9526
|
+
// We should negate the regexp if the modifier is an exception
|
|
9527
|
+
modifier.exception
|
|
9528
|
+
// eslint-disable-next-line max-len
|
|
9529
|
+
? `${REGEX_MARKER}${RegExpUtils.negateRegexPattern(RegExpUtils.patternToRegexp(value))}${REGEX_MARKER}`
|
|
9530
|
+
: value));
|
|
9153
9531
|
}
|
|
9154
|
-
}
|
|
9155
|
-
|
|
9156
|
-
|
|
9532
|
+
});
|
|
9533
|
+
// Check if we have any converted modifiers
|
|
9534
|
+
if (conversionMap.size) {
|
|
9535
|
+
const modifierListClone = clone(modifierList);
|
|
9536
|
+
// Replace the original modifiers with the converted ones
|
|
9537
|
+
modifierListClone.children = modifierListClone.children.map((modifier, index) => {
|
|
9538
|
+
const convertedModifier = conversionMap.get(index);
|
|
9539
|
+
return convertedModifier ?? modifier;
|
|
9540
|
+
}).flat();
|
|
9541
|
+
return createConversionResult(modifierListClone, true);
|
|
9542
|
+
}
|
|
9543
|
+
// Otherwise, just return the original modifier list
|
|
9544
|
+
return createConversionResult(modifierList, false);
|
|
9545
|
+
}
|
|
9157
9546
|
}
|
|
9158
9547
|
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9548
|
+
var PseudoClasses;
|
|
9549
|
+
(function (PseudoClasses) {
|
|
9550
|
+
PseudoClasses["AbpContains"] = "-abp-contains";
|
|
9551
|
+
PseudoClasses["AbpHas"] = "-abp-has";
|
|
9552
|
+
PseudoClasses["Contains"] = "contains";
|
|
9553
|
+
PseudoClasses["Has"] = "has";
|
|
9554
|
+
PseudoClasses["HasText"] = "has-text";
|
|
9555
|
+
PseudoClasses["MatchesCss"] = "matches-css";
|
|
9556
|
+
PseudoClasses["MatchesCssAfter"] = "matches-css-after";
|
|
9557
|
+
PseudoClasses["MatchesCssBefore"] = "matches-css-before";
|
|
9558
|
+
PseudoClasses["Not"] = "not";
|
|
9559
|
+
})(PseudoClasses || (PseudoClasses = {}));
|
|
9560
|
+
var PseudoElements;
|
|
9561
|
+
(function (PseudoElements) {
|
|
9562
|
+
PseudoElements["After"] = "after";
|
|
9563
|
+
PseudoElements["Before"] = "before";
|
|
9564
|
+
})(PseudoElements || (PseudoElements = {}));
|
|
9565
|
+
const PSEUDO_ELEMENT_NAMES = new Set([
|
|
9566
|
+
PseudoElements.After,
|
|
9567
|
+
PseudoElements.Before,
|
|
9568
|
+
]);
|
|
9569
|
+
const LEGACY_MATCHES_CSS_NAMES = new Set([
|
|
9570
|
+
PseudoClasses.MatchesCssAfter,
|
|
9571
|
+
PseudoClasses.MatchesCssBefore,
|
|
9572
|
+
]);
|
|
9573
|
+
const LEGACY_EXT_CSS_INDICATOR_PSEUDO_NAMES = new Set([
|
|
9574
|
+
PseudoClasses.Not,
|
|
9575
|
+
PseudoClasses.MatchesCssBefore,
|
|
9576
|
+
PseudoClasses.MatchesCssAfter,
|
|
9577
|
+
]);
|
|
9578
|
+
const CSS_CONVERSION_INDICATOR_PSEUDO_NAMES = new Set([
|
|
9579
|
+
PseudoClasses.AbpContains,
|
|
9580
|
+
PseudoClasses.AbpHas,
|
|
9581
|
+
PseudoClasses.HasText,
|
|
9582
|
+
]);
|
|
9172
9583
|
/**
|
|
9173
9584
|
* Converts some pseudo-classes to pseudo-elements. For example:
|
|
9174
9585
|
* - `:before` → `::before`
|
|
9175
9586
|
*
|
|
9176
9587
|
* @param selectorList Selector list to convert
|
|
9177
|
-
* @returns
|
|
9588
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9589
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9590
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9178
9591
|
*/
|
|
9179
9592
|
function convertToPseudoElements(selectorList) {
|
|
9180
|
-
//
|
|
9181
|
-
const
|
|
9593
|
+
// Check conversion indications before doing any heavy work
|
|
9594
|
+
const hasIndicator = ecssTree.find(
|
|
9595
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9596
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9597
|
+
selectorList, (node) => node.type === exports.CssTreeNodeType.PseudoClassSelector && PSEUDO_ELEMENT_NAMES.has(node.name));
|
|
9598
|
+
if (!hasIndicator) {
|
|
9599
|
+
return createConversionResult(selectorList, false);
|
|
9600
|
+
}
|
|
9601
|
+
// Make a clone of the selector list to avoid modifying the original one,
|
|
9602
|
+
// then convert & return the cloned version
|
|
9603
|
+
const selectorListClone = clone(selectorList);
|
|
9604
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9605
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9182
9606
|
ecssTree.walk(selectorListClone, {
|
|
9607
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9608
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9183
9609
|
leave: (node) => {
|
|
9184
9610
|
if (node.type === exports.CssTreeNodeType.PseudoClassSelector) {
|
|
9185
|
-
//
|
|
9186
|
-
//
|
|
9187
|
-
|
|
9611
|
+
// If the pseudo-class is `:before` or `:after`, then we should
|
|
9612
|
+
// convert the node type to pseudo-element:
|
|
9613
|
+
// :after → ::after
|
|
9614
|
+
// :before → ::before
|
|
9615
|
+
if (PSEUDO_ELEMENT_NAMES.has(node.name)) {
|
|
9188
9616
|
Object.assign(node, {
|
|
9189
9617
|
...node,
|
|
9190
9618
|
type: exports.CssTreeNodeType.PseudoElementSelector,
|
|
@@ -9193,7 +9621,7 @@ function convertToPseudoElements(selectorList) {
|
|
|
9193
9621
|
}
|
|
9194
9622
|
},
|
|
9195
9623
|
});
|
|
9196
|
-
return selectorListClone;
|
|
9624
|
+
return createConversionResult(selectorListClone, true);
|
|
9197
9625
|
}
|
|
9198
9626
|
/**
|
|
9199
9627
|
* Converts legacy Extended CSS `matches-css-before` and `matches-css-after`
|
|
@@ -9202,33 +9630,36 @@ function convertToPseudoElements(selectorList) {
|
|
|
9202
9630
|
* - `:matches-css-after(...)` → `:matches-css(after, ...)`
|
|
9203
9631
|
*
|
|
9204
9632
|
* @param node Node to convert
|
|
9633
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9634
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9635
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9205
9636
|
* @throws If the node is invalid
|
|
9206
9637
|
*/
|
|
9207
9638
|
function convertLegacyMatchesCss(node) {
|
|
9208
|
-
|
|
9209
|
-
if (
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9639
|
+
// Check conversion indications before doing any heavy work
|
|
9640
|
+
if (node.type !== exports.CssTreeNodeType.PseudoClassSelector || !LEGACY_MATCHES_CSS_NAMES.has(node.name)) {
|
|
9641
|
+
return createConversionResult(node, false);
|
|
9642
|
+
}
|
|
9643
|
+
const nodeClone = clone(node);
|
|
9644
|
+
if (!nodeClone.children || nodeClone.children.length < 1) {
|
|
9645
|
+
throw new Error(`Invalid ${node.name} pseudo-class: missing argument`);
|
|
9646
|
+
}
|
|
9647
|
+
// Rename the pseudo-class
|
|
9648
|
+
nodeClone.name = PseudoClasses.MatchesCss;
|
|
9649
|
+
// Remove the 'matches-css-' prefix to get the direction
|
|
9650
|
+
const direction = node.name.substring(PseudoClasses.MatchesCss.length + 1);
|
|
9651
|
+
// Add the direction to the first raw argument
|
|
9652
|
+
const arg = nodeClone.children[0];
|
|
9653
|
+
// Check argument
|
|
9654
|
+
if (!arg) {
|
|
9655
|
+
throw new Error(`Invalid ${node.name} pseudo-class: argument shouldn't be null`);
|
|
9656
|
+
}
|
|
9657
|
+
if (arg.type !== exports.CssTreeNodeType.Raw) {
|
|
9658
|
+
throw new Error(`Invalid ${node.name} pseudo-class: unexpected argument type`);
|
|
9659
|
+
}
|
|
9660
|
+
// Add the direction as the first argument
|
|
9661
|
+
arg.value = `${direction},${arg.value}`;
|
|
9662
|
+
return createConversionResult(nodeClone, true);
|
|
9232
9663
|
}
|
|
9233
9664
|
/**
|
|
9234
9665
|
* Converts legacy Extended CSS selectors to the modern Extended CSS syntax.
|
|
@@ -9238,16 +9669,40 @@ function convertLegacyMatchesCss(node) {
|
|
|
9238
9669
|
* - `[-ext-matches-css-before=...]` → `:matches-css(before, ...)`
|
|
9239
9670
|
*
|
|
9240
9671
|
* @param selectorList Selector list AST to convert
|
|
9241
|
-
* @returns
|
|
9672
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9673
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9674
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9242
9675
|
*/
|
|
9243
9676
|
function convertFromLegacyExtendedCss(selectorList) {
|
|
9244
|
-
//
|
|
9245
|
-
const
|
|
9677
|
+
// Check conversion indications before doing any heavy work
|
|
9678
|
+
const hasIndicator = ecssTree.find(
|
|
9679
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9680
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9681
|
+
selectorList, (node) => {
|
|
9682
|
+
if (node.type === exports.CssTreeNodeType.PseudoClassSelector) {
|
|
9683
|
+
return LEGACY_EXT_CSS_INDICATOR_PSEUDO_NAMES.has(node.name);
|
|
9684
|
+
}
|
|
9685
|
+
if (node.type === exports.CssTreeNodeType.AttributeSelector) {
|
|
9686
|
+
return node.name.name.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX);
|
|
9687
|
+
}
|
|
9688
|
+
return false;
|
|
9689
|
+
});
|
|
9690
|
+
if (!hasIndicator) {
|
|
9691
|
+
return createConversionResult(selectorList, false);
|
|
9692
|
+
}
|
|
9693
|
+
const selectorListClone = clone(selectorList);
|
|
9694
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9695
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9246
9696
|
ecssTree.walk(selectorListClone, {
|
|
9697
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9698
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9247
9699
|
leave: (node) => {
|
|
9248
9700
|
// :matches-css-before(arg) → :matches-css(before,arg)
|
|
9249
9701
|
// :matches-css-after(arg) → :matches-css(after,arg)
|
|
9250
|
-
convertLegacyMatchesCss(node);
|
|
9702
|
+
const convertedLegacyExtCss = convertLegacyMatchesCss(node);
|
|
9703
|
+
if (convertedLegacyExtCss.isConverted) {
|
|
9704
|
+
Object.assign(node, convertedLegacyExtCss.result);
|
|
9705
|
+
}
|
|
9251
9706
|
// [-ext-name=...] → :name(...)
|
|
9252
9707
|
// [-ext-name='...'] → :name(...)
|
|
9253
9708
|
// [-ext-name="..."] → :name(...)
|
|
@@ -9261,7 +9716,7 @@ function convertFromLegacyExtendedCss(selectorList) {
|
|
|
9261
9716
|
// Remove the '-ext-' prefix to get the pseudo-class name
|
|
9262
9717
|
const name = node.name.name.substring(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX.length);
|
|
9263
9718
|
// Prepare the children list for the pseudo-class node
|
|
9264
|
-
const children =
|
|
9719
|
+
const children = [];
|
|
9265
9720
|
// TODO: Change String node to Raw node to drop the quotes.
|
|
9266
9721
|
// The structure of the node is the same, just the type
|
|
9267
9722
|
// is different and generate() will generate the quotes
|
|
@@ -9274,7 +9729,7 @@ function convertFromLegacyExtendedCss(selectorList) {
|
|
|
9274
9729
|
// For example, if the input is [-ext-has="> .selector"], then
|
|
9275
9730
|
// we need to parse "> .selector" as a selector instead of string
|
|
9276
9731
|
// it as a raw value
|
|
9277
|
-
if ([
|
|
9732
|
+
if ([PseudoClasses.Has, PseudoClasses.Not].includes(name)) {
|
|
9278
9733
|
// Get the value of the attribute selector
|
|
9279
9734
|
const { value } = node;
|
|
9280
9735
|
// If the value is an identifier, then simply push it to the
|
|
@@ -9284,10 +9739,12 @@ function convertFromLegacyExtendedCss(selectorList) {
|
|
|
9284
9739
|
}
|
|
9285
9740
|
else if (value.type === exports.CssTreeNodeType.String) {
|
|
9286
9741
|
// Parse the value as a selector
|
|
9287
|
-
const parsedChildren = CssTree.
|
|
9742
|
+
const parsedChildren = CssTree.parsePlain(value.value, exports.CssTreeParserContext.selectorList);
|
|
9288
9743
|
// Don't forget convert the parsed AST again, because
|
|
9289
9744
|
// it was a raw string before
|
|
9290
|
-
|
|
9745
|
+
const convertedChildren = convertFromLegacyExtendedCss(parsedChildren);
|
|
9746
|
+
// Push the converted children to the list
|
|
9747
|
+
children.push(convertedChildren.result);
|
|
9291
9748
|
}
|
|
9292
9749
|
}
|
|
9293
9750
|
else {
|
|
@@ -9314,14 +9771,12 @@ function convertFromLegacyExtendedCss(selectorList) {
|
|
|
9314
9771
|
children,
|
|
9315
9772
|
};
|
|
9316
9773
|
// Handle this case: [-ext-matches-css-before=...] → :matches-css(before,...)
|
|
9317
|
-
convertLegacyMatchesCss(pseudoNode);
|
|
9318
|
-
|
|
9319
|
-
// keep the reference to the original node
|
|
9320
|
-
Object.assign(node, pseudoNode);
|
|
9774
|
+
const convertedPseudoNode = convertLegacyMatchesCss(pseudoNode);
|
|
9775
|
+
Object.assign(node, convertedPseudoNode.isConverted ? convertedPseudoNode.result : pseudoNode);
|
|
9321
9776
|
}
|
|
9322
9777
|
},
|
|
9323
9778
|
});
|
|
9324
|
-
return selectorListClone;
|
|
9779
|
+
return createConversionResult(selectorListClone, true);
|
|
9325
9780
|
}
|
|
9326
9781
|
/**
|
|
9327
9782
|
* CSS selector converter
|
|
@@ -9333,32 +9788,51 @@ class CssSelectorConverter extends ConverterBase {
|
|
|
9333
9788
|
* Converts Extended CSS elements to AdGuard-compatible ones
|
|
9334
9789
|
*
|
|
9335
9790
|
* @param selectorList Selector list to convert
|
|
9336
|
-
* @returns
|
|
9791
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
9792
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
9793
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9337
9794
|
* @throws If the rule is invalid or incompatible
|
|
9338
9795
|
*/
|
|
9339
9796
|
static convertToAdg(selectorList) {
|
|
9340
9797
|
// First, convert
|
|
9341
9798
|
// - legacy Extended CSS selectors to the modern Extended CSS syntax and
|
|
9342
9799
|
// - some pseudo-classes to pseudo-elements
|
|
9343
|
-
const
|
|
9800
|
+
const legacyExtCssConverted = convertFromLegacyExtendedCss(selectorList);
|
|
9801
|
+
const pseudoElementsConverted = convertToPseudoElements(legacyExtCssConverted.result);
|
|
9802
|
+
const hasIndicator = legacyExtCssConverted.isConverted || pseudoElementsConverted.isConverted || ecssTree.find(
|
|
9803
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9804
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9805
|
+
selectorList,
|
|
9806
|
+
// eslint-disable-next-line max-len
|
|
9807
|
+
(node) => node.type === exports.CssTreeNodeType.PseudoClassSelector && CSS_CONVERSION_INDICATOR_PSEUDO_NAMES.has(node.name));
|
|
9808
|
+
if (!hasIndicator) {
|
|
9809
|
+
return createConversionResult(selectorList, false);
|
|
9810
|
+
}
|
|
9811
|
+
const selectorListClone = legacyExtCssConverted.isConverted || pseudoElementsConverted.isConverted
|
|
9812
|
+
? pseudoElementsConverted.result
|
|
9813
|
+
: clone(selectorList);
|
|
9344
9814
|
// Then, convert some Extended CSS pseudo-classes to AdGuard-compatible ones
|
|
9815
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9816
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9345
9817
|
ecssTree.walk(selectorListClone, {
|
|
9818
|
+
// TODO: Need to improve CSSTree types, until then we need to use any type here
|
|
9819
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9346
9820
|
leave: (node) => {
|
|
9347
9821
|
if (node.type === exports.CssTreeNodeType.PseudoClassSelector) {
|
|
9348
9822
|
// :-abp-contains(...) → :contains(...)
|
|
9349
9823
|
// :has-text(...) → :contains(...)
|
|
9350
|
-
if (node.name ===
|
|
9351
|
-
CssTree.renamePseudoClass(node,
|
|
9824
|
+
if (node.name === PseudoClasses.AbpContains || node.name === PseudoClasses.HasText) {
|
|
9825
|
+
CssTree.renamePseudoClass(node, PseudoClasses.Contains);
|
|
9352
9826
|
}
|
|
9353
9827
|
// :-abp-has(...) → :has(...)
|
|
9354
|
-
if (node.name ===
|
|
9355
|
-
CssTree.renamePseudoClass(node,
|
|
9828
|
+
if (node.name === PseudoClasses.AbpHas) {
|
|
9829
|
+
CssTree.renamePseudoClass(node, PseudoClasses.Has);
|
|
9356
9830
|
}
|
|
9357
9831
|
// TODO: check uBO's `:others()` and `:watch-attr()` pseudo-classes
|
|
9358
9832
|
}
|
|
9359
9833
|
},
|
|
9360
9834
|
});
|
|
9361
|
-
return selectorListClone;
|
|
9835
|
+
return createConversionResult(selectorListClone, true);
|
|
9362
9836
|
}
|
|
9363
9837
|
}
|
|
9364
9838
|
|
|
@@ -9375,27 +9849,39 @@ class CssInjectionRuleConverter extends RuleConverterBase {
|
|
|
9375
9849
|
* Converts a CSS injection rule to AdGuard format, if possible.
|
|
9376
9850
|
*
|
|
9377
9851
|
* @param rule Rule node to convert
|
|
9378
|
-
* @returns
|
|
9852
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9853
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9854
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9379
9855
|
* @throws If the rule is invalid or cannot be converted
|
|
9380
9856
|
*/
|
|
9381
9857
|
static convertToAdg(rule) {
|
|
9382
|
-
|
|
9383
|
-
|
|
9858
|
+
const separator = rule.separator.value;
|
|
9859
|
+
let convertedSeparator = separator;
|
|
9384
9860
|
// Change the separator if the rule contains ExtendedCSS selectors
|
|
9385
|
-
if (CssTree.hasAnySelectorExtendedCssNode(
|
|
9386
|
-
|
|
9861
|
+
if (CssTree.hasAnySelectorExtendedCssNode(rule.body.selectorList) || rule.body.remove) {
|
|
9862
|
+
convertedSeparator = rule.exception
|
|
9387
9863
|
? exports.CosmeticRuleSeparator.AdgExtendedCssInjectionException
|
|
9388
9864
|
: exports.CosmeticRuleSeparator.AdgExtendedCssInjection;
|
|
9389
9865
|
}
|
|
9390
9866
|
else {
|
|
9391
|
-
|
|
9867
|
+
convertedSeparator = rule.exception
|
|
9392
9868
|
? exports.CosmeticRuleSeparator.AdgCssInjectionException
|
|
9393
9869
|
: exports.CosmeticRuleSeparator.AdgCssInjection;
|
|
9394
9870
|
}
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9871
|
+
const convertedSelectorList = CssSelectorConverter.convertToAdg(rule.body.selectorList);
|
|
9872
|
+
// Check if the rule needs to be converted
|
|
9873
|
+
if (!(rule.syntax === exports.AdblockSyntax.Common || rule.syntax === exports.AdblockSyntax.Adg)
|
|
9874
|
+
|| separator !== convertedSeparator
|
|
9875
|
+
|| convertedSelectorList.isConverted) {
|
|
9876
|
+
// TODO: Replace with custom clone method
|
|
9877
|
+
const ruleClone = clone(rule);
|
|
9878
|
+
ruleClone.syntax = exports.AdblockSyntax.Adg;
|
|
9879
|
+
ruleClone.separator.value = convertedSeparator;
|
|
9880
|
+
ruleClone.body.selectorList = convertedSelectorList.result;
|
|
9881
|
+
return createNodeConversionResult([ruleClone], true);
|
|
9882
|
+
}
|
|
9883
|
+
// Otherwise, return the original rule
|
|
9884
|
+
return createNodeConversionResult([rule], false);
|
|
9399
9885
|
}
|
|
9400
9886
|
}
|
|
9401
9887
|
|
|
@@ -9412,27 +9898,39 @@ class ElementHidingRuleConverter extends RuleConverterBase {
|
|
|
9412
9898
|
* Converts an element hiding rule to AdGuard format, if possible.
|
|
9413
9899
|
*
|
|
9414
9900
|
* @param rule Rule node to convert
|
|
9415
|
-
* @returns
|
|
9901
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9902
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9903
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9416
9904
|
* @throws If the rule is invalid or cannot be converted
|
|
9417
9905
|
*/
|
|
9418
9906
|
static convertToAdg(rule) {
|
|
9419
|
-
|
|
9420
|
-
|
|
9907
|
+
const separator = rule.separator.value;
|
|
9908
|
+
let convertedSeparator = separator;
|
|
9421
9909
|
// Change the separator if the rule contains ExtendedCSS selectors
|
|
9422
|
-
if (CssTree.hasAnySelectorExtendedCssNode(
|
|
9423
|
-
|
|
9910
|
+
if (CssTree.hasAnySelectorExtendedCssNode(rule.body.selectorList)) {
|
|
9911
|
+
convertedSeparator = rule.exception
|
|
9424
9912
|
? exports.CosmeticRuleSeparator.ExtendedElementHidingException
|
|
9425
9913
|
: exports.CosmeticRuleSeparator.ExtendedElementHiding;
|
|
9426
9914
|
}
|
|
9427
9915
|
else {
|
|
9428
|
-
|
|
9916
|
+
convertedSeparator = rule.exception
|
|
9429
9917
|
? exports.CosmeticRuleSeparator.ElementHidingException
|
|
9430
9918
|
: exports.CosmeticRuleSeparator.ElementHiding;
|
|
9431
9919
|
}
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9920
|
+
const convertedSelectorList = CssSelectorConverter.convertToAdg(rule.body.selectorList);
|
|
9921
|
+
// Check if the rule needs to be converted
|
|
9922
|
+
if (!(rule.syntax === exports.AdblockSyntax.Common || rule.syntax === exports.AdblockSyntax.Adg)
|
|
9923
|
+
|| separator !== convertedSeparator
|
|
9924
|
+
|| convertedSelectorList.isConverted) {
|
|
9925
|
+
// TODO: Replace with custom clone method
|
|
9926
|
+
const ruleClone = clone(rule);
|
|
9927
|
+
ruleClone.syntax = exports.AdblockSyntax.Adg;
|
|
9928
|
+
ruleClone.separator.value = convertedSeparator;
|
|
9929
|
+
ruleClone.body.selectorList = convertedSelectorList.result;
|
|
9930
|
+
return createNodeConversionResult([ruleClone], true);
|
|
9931
|
+
}
|
|
9932
|
+
// Otherwise, return the original rule
|
|
9933
|
+
return createNodeConversionResult([rule], false);
|
|
9436
9934
|
}
|
|
9437
9935
|
}
|
|
9438
9936
|
|
|
@@ -9460,7 +9958,7 @@ function createNetworkRuleNode(pattern, modifiers = undefined, exception = false
|
|
|
9460
9958
|
},
|
|
9461
9959
|
};
|
|
9462
9960
|
if (!isUndefined(modifiers)) {
|
|
9463
|
-
result.modifiers =
|
|
9961
|
+
result.modifiers = clone(modifiers);
|
|
9464
9962
|
}
|
|
9465
9963
|
return result;
|
|
9466
9964
|
}
|
|
@@ -9480,32 +9978,37 @@ class HeaderRemovalRuleConverter extends RuleConverterBase {
|
|
|
9480
9978
|
* Converts a header removal rule to AdGuard syntax, if possible.
|
|
9481
9979
|
*
|
|
9482
9980
|
* @param rule Rule node to convert
|
|
9483
|
-
* @returns
|
|
9981
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
9982
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
9983
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9484
9984
|
* @throws If the rule is invalid or cannot be converted
|
|
9985
|
+
* @example
|
|
9986
|
+
* If the input rule is:
|
|
9987
|
+
* ```adblock
|
|
9988
|
+
* example.com##^responseheader(header-name)
|
|
9989
|
+
* ```
|
|
9990
|
+
* The output will be:
|
|
9991
|
+
* ```adblock
|
|
9992
|
+
* ||example.com^$removeheader=header-name
|
|
9993
|
+
* ```
|
|
9485
9994
|
*/
|
|
9486
9995
|
static convertToAdg(rule) {
|
|
9487
|
-
// Clone the provided AST node to avoid side effects
|
|
9488
|
-
const ruleNode = cloneDeep(rule);
|
|
9489
9996
|
// TODO: Add support for ABP syntax once it starts supporting header removal rules
|
|
9490
|
-
//
|
|
9491
|
-
if (
|
|
9492
|
-
||
|
|
9493
|
-
||
|
|
9494
|
-
||
|
|
9495
|
-
|
|
9997
|
+
// Leave the rule as is if it's not a header removal rule
|
|
9998
|
+
if (rule.category !== exports.RuleCategory.Cosmetic
|
|
9999
|
+
|| rule.type !== exports.CosmeticRuleType.HtmlFilteringRule
|
|
10000
|
+
|| rule.body.body.type !== exports.CssTreeNodeType.Function
|
|
10001
|
+
|| rule.body.body.name !== UBO_RESPONSEHEADER_MARKER) {
|
|
10002
|
+
return createNodeConversionResult([rule], false);
|
|
9496
10003
|
}
|
|
9497
10004
|
// Prepare network rule pattern
|
|
9498
|
-
|
|
9499
|
-
if (
|
|
10005
|
+
const pattern = [];
|
|
10006
|
+
if (rule.domains.children.length === 1) {
|
|
9500
10007
|
// If the rule has only one domain, we can use a simple network rule pattern:
|
|
9501
10008
|
// ||single-domain-from-the-rule^
|
|
9502
|
-
pattern
|
|
9503
|
-
ADBLOCK_URL_START,
|
|
9504
|
-
ruleNode.domains.children[0].value,
|
|
9505
|
-
ADBLOCK_URL_SEPARATOR,
|
|
9506
|
-
].join(EMPTY);
|
|
10009
|
+
pattern.push(ADBLOCK_URL_START, rule.domains.children[0].value, ADBLOCK_URL_SEPARATOR);
|
|
9507
10010
|
}
|
|
9508
|
-
else if (
|
|
10011
|
+
else if (rule.domains.children.length > 1) {
|
|
9509
10012
|
// TODO: Add support for multiple domains, for example:
|
|
9510
10013
|
// example.com,example.org,example.net##^responseheader(header-name)
|
|
9511
10014
|
// We should consider allowing $domain with $removeheader modifier,
|
|
@@ -9515,13 +10018,13 @@ class HeaderRemovalRuleConverter extends RuleConverterBase {
|
|
|
9515
10018
|
}
|
|
9516
10019
|
// Prepare network rule modifiers
|
|
9517
10020
|
const modifiers = createModifierListNode();
|
|
9518
|
-
modifiers.children.push(createModifierNode(ADG_REMOVEHEADER_MODIFIER, CssTree.
|
|
10021
|
+
modifiers.children.push(createModifierNode(ADG_REMOVEHEADER_MODIFIER, CssTree.generateFunctionPlainValue(rule.body.body)));
|
|
9519
10022
|
// Construct the network rule
|
|
9520
|
-
return [
|
|
9521
|
-
createNetworkRuleNode(pattern, modifiers,
|
|
10023
|
+
return createNodeConversionResult([
|
|
10024
|
+
createNetworkRuleNode(pattern.join(EMPTY), modifiers,
|
|
9522
10025
|
// Copy the exception flag
|
|
9523
|
-
|
|
9524
|
-
];
|
|
10026
|
+
rule.exception, exports.AdblockSyntax.Adg),
|
|
10027
|
+
], true);
|
|
9525
10028
|
}
|
|
9526
10029
|
}
|
|
9527
10030
|
|
|
@@ -9538,48 +10041,69 @@ class CosmeticRuleConverter extends RuleConverterBase {
|
|
|
9538
10041
|
* Converts a cosmetic rule to AdGuard syntax, if possible.
|
|
9539
10042
|
*
|
|
9540
10043
|
* @param rule Rule node to convert
|
|
9541
|
-
* @returns
|
|
10044
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
10045
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
10046
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9542
10047
|
* @throws If the rule is invalid or cannot be converted
|
|
9543
10048
|
*/
|
|
9544
10049
|
static convertToAdg(rule) {
|
|
9545
|
-
|
|
9546
|
-
const ruleNode = cloneDeep(rule);
|
|
9547
|
-
// Convert cosmetic rule modifiers
|
|
9548
|
-
if (ruleNode.modifiers) {
|
|
9549
|
-
if (ruleNode.syntax === exports.AdblockSyntax.Ubo) {
|
|
9550
|
-
// uBO doesn't support this rule:
|
|
9551
|
-
// example.com##+js(set-constant.js, foo, bar):matches-path(/baz)
|
|
9552
|
-
if (ruleNode.type === exports.CosmeticRuleType.ScriptletInjectionRule) {
|
|
9553
|
-
throw new RuleConversionError('uBO scriptlet injection rules don\'t support cosmetic rule modifiers');
|
|
9554
|
-
}
|
|
9555
|
-
ruleNode.modifiers = AdgCosmeticRuleModifierConverter.convertFromUbo(ruleNode.modifiers);
|
|
9556
|
-
}
|
|
9557
|
-
else if (ruleNode.syntax === exports.AdblockSyntax.Abp) {
|
|
9558
|
-
// TODO: Implement once ABP starts supporting cosmetic rule modifiers
|
|
9559
|
-
throw new RuleConversionError('ABP don\'t support cosmetic rule modifiers');
|
|
9560
|
-
}
|
|
9561
|
-
}
|
|
10050
|
+
let subconverterResult;
|
|
9562
10051
|
// Convert cosmetic rule based on its type
|
|
9563
|
-
switch (
|
|
10052
|
+
switch (rule.type) {
|
|
9564
10053
|
case exports.CosmeticRuleType.ElementHidingRule:
|
|
9565
|
-
|
|
10054
|
+
subconverterResult = ElementHidingRuleConverter.convertToAdg(rule);
|
|
10055
|
+
break;
|
|
9566
10056
|
case exports.CosmeticRuleType.ScriptletInjectionRule:
|
|
9567
|
-
|
|
10057
|
+
subconverterResult = ScriptletRuleConverter.convertToAdg(rule);
|
|
10058
|
+
break;
|
|
9568
10059
|
case exports.CosmeticRuleType.CssInjectionRule:
|
|
9569
|
-
|
|
10060
|
+
subconverterResult = CssInjectionRuleConverter.convertToAdg(rule);
|
|
10061
|
+
break;
|
|
9570
10062
|
case exports.CosmeticRuleType.HtmlFilteringRule:
|
|
9571
10063
|
// Handle special case: uBO response header filtering rule
|
|
9572
|
-
if (
|
|
9573
|
-
&&
|
|
9574
|
-
|
|
10064
|
+
if (rule.body.body.type === exports.CssTreeNodeType.Function
|
|
10065
|
+
&& rule.body.body.name === UBO_RESPONSEHEADER_MARKER) {
|
|
10066
|
+
subconverterResult = HeaderRemovalRuleConverter.convertToAdg(rule);
|
|
10067
|
+
}
|
|
10068
|
+
else {
|
|
10069
|
+
subconverterResult = HtmlRuleConverter.convertToAdg(rule);
|
|
9575
10070
|
}
|
|
9576
|
-
|
|
9577
|
-
// Note: Currently, only ADG supports JS injection rules
|
|
10071
|
+
break;
|
|
10072
|
+
// Note: Currently, only ADG supports JS injection rules, so we don't need to convert them
|
|
9578
10073
|
case exports.CosmeticRuleType.JsInjectionRule:
|
|
9579
|
-
|
|
10074
|
+
subconverterResult = createNodeConversionResult([rule], false);
|
|
10075
|
+
break;
|
|
9580
10076
|
default:
|
|
9581
10077
|
throw new RuleConversionError('Unsupported cosmetic rule type');
|
|
9582
10078
|
}
|
|
10079
|
+
let convertedModifiers;
|
|
10080
|
+
// Convert cosmetic rule modifiers, if any
|
|
10081
|
+
if (rule.modifiers) {
|
|
10082
|
+
if (rule.syntax === exports.AdblockSyntax.Ubo) {
|
|
10083
|
+
// uBO doesn't support this rule:
|
|
10084
|
+
// example.com##+js(set-constant.js, foo, bar):matches-path(/baz)
|
|
10085
|
+
if (rule.type === exports.CosmeticRuleType.ScriptletInjectionRule) {
|
|
10086
|
+
throw new RuleConversionError('uBO scriptlet injection rules don\'t support cosmetic rule modifiers');
|
|
10087
|
+
}
|
|
10088
|
+
convertedModifiers = AdgCosmeticRuleModifierConverter.convertFromUbo(rule.modifiers);
|
|
10089
|
+
}
|
|
10090
|
+
else if (rule.syntax === exports.AdblockSyntax.Abp) {
|
|
10091
|
+
// TODO: Implement once ABP starts supporting cosmetic rule modifiers
|
|
10092
|
+
throw new RuleConversionError('ABP don\'t support cosmetic rule modifiers');
|
|
10093
|
+
}
|
|
10094
|
+
}
|
|
10095
|
+
if ((subconverterResult.result.length > 1 || subconverterResult.isConverted)
|
|
10096
|
+
|| (convertedModifiers && convertedModifiers.isConverted)) {
|
|
10097
|
+
// Add modifier list to the subconverter result rules
|
|
10098
|
+
subconverterResult.result.forEach((subconverterRule) => {
|
|
10099
|
+
if (convertedModifiers && subconverterRule.category === exports.RuleCategory.Cosmetic) {
|
|
10100
|
+
// eslint-disable-next-line no-param-reassign
|
|
10101
|
+
subconverterRule.modifiers = convertedModifiers.result;
|
|
10102
|
+
}
|
|
10103
|
+
});
|
|
10104
|
+
return subconverterResult;
|
|
10105
|
+
}
|
|
10106
|
+
return createNodeConversionResult([rule], false);
|
|
9583
10107
|
}
|
|
9584
10108
|
}
|
|
9585
10109
|
|
|
@@ -9651,17 +10175,16 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
9651
10175
|
* Converts a network rule modifier list to AdGuard format, if possible.
|
|
9652
10176
|
*
|
|
9653
10177
|
* @param modifierList Network rule modifier list node to convert
|
|
9654
|
-
* @returns
|
|
10178
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
10179
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
10180
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
9655
10181
|
* @throws If the conversion is not possible
|
|
9656
10182
|
*/
|
|
9657
10183
|
static convertToAdg(modifierList) {
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
const cspValues = [];
|
|
9663
|
-
modifierListNode.children.forEach((modifierNode) => {
|
|
9664
|
-
// Handle regular modifiers conversion and $csp modifiers collection
|
|
10184
|
+
const conversionMap = new MultiValueMap();
|
|
10185
|
+
// Special case: $csp modifier
|
|
10186
|
+
let cspCount = 0;
|
|
10187
|
+
modifierList.children.forEach((modifierNode, index) => {
|
|
9665
10188
|
const modifierConversions = ADG_CONVERSION_MAP.get(modifierNode.modifier.value);
|
|
9666
10189
|
if (modifierConversions) {
|
|
9667
10190
|
for (const modifierConversion of modifierConversions) {
|
|
@@ -9674,17 +10197,14 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
9674
10197
|
const value = modifierConversion.value
|
|
9675
10198
|
? modifierConversion.value(modifierNode.value?.value)
|
|
9676
10199
|
: modifierNode.value?.value;
|
|
9677
|
-
if
|
|
9678
|
-
|
|
9679
|
-
|
|
10200
|
+
// Check if the name or the value is different from the original modifier
|
|
10201
|
+
// If so, add the converted modifier to the list
|
|
10202
|
+
if (name !== modifierNode.modifier.value || value !== modifierNode.value?.value) {
|
|
10203
|
+
conversionMap.add(index, createModifierNode(name, value, exception));
|
|
9680
10204
|
}
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
const existingModifier = convertedModifierList.children.find((m) => m.modifier.value === name && m.exception === exception && m.value?.value === value);
|
|
9685
|
-
if (!existingModifier) {
|
|
9686
|
-
convertedModifierList.children.push(createModifierNode(name, value, exception));
|
|
9687
|
-
}
|
|
10205
|
+
// Special case: $csp modifier
|
|
10206
|
+
if (name === CSP_MODIFIER) {
|
|
10207
|
+
cspCount += 1;
|
|
9688
10208
|
}
|
|
9689
10209
|
}
|
|
9690
10210
|
return;
|
|
@@ -9707,26 +10227,52 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
9707
10227
|
// Try to convert the redirect resource name to ADG format
|
|
9708
10228
|
// This function returns undefined if the resource name is unknown
|
|
9709
10229
|
const convertedRedirectResource = redirects.convertRedirectNameToAdg(redirectResource);
|
|
9710
|
-
|
|
9711
|
-
// If
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
&& m.exception === modifierNode.exception
|
|
9720
|
-
&& m.value?.value === modifierNode.value?.value);
|
|
9721
|
-
if (!existingModifier) {
|
|
9722
|
-
convertedModifierList.children.push(modifierNode);
|
|
10230
|
+
// Check if the modifier name or the redirect resource name is different from the original modifier
|
|
10231
|
+
// If so, add the converted modifier to the list
|
|
10232
|
+
if (modifierName !== modifierNode.modifier.value
|
|
10233
|
+
|| (convertedRedirectResource !== undefined && convertedRedirectResource !== redirectResource)) {
|
|
10234
|
+
conversionMap.add(index, createModifierNode(modifierName,
|
|
10235
|
+
// If the redirect resource name is unknown, fall back to the original one
|
|
10236
|
+
// Later, the validator will throw an error if the resource name is invalid
|
|
10237
|
+
convertedRedirectResource || redirectResource, modifierNode.exception));
|
|
10238
|
+
}
|
|
9723
10239
|
}
|
|
9724
10240
|
});
|
|
9725
|
-
//
|
|
9726
|
-
if (
|
|
9727
|
-
|
|
10241
|
+
// Prepare the result if there are any converted modifiers or $csp modifiers
|
|
10242
|
+
if (conversionMap.size || cspCount) {
|
|
10243
|
+
const modifierListClone = cloneModifierListNode(modifierList);
|
|
10244
|
+
// Replace the original modifiers with the converted ones
|
|
10245
|
+
// One modifier may be replaced with multiple modifiers, so we need to flatten the array
|
|
10246
|
+
modifierListClone.children = modifierListClone.children.map((modifierNode, index) => {
|
|
10247
|
+
const conversionRecord = conversionMap.get(index);
|
|
10248
|
+
if (conversionRecord) {
|
|
10249
|
+
return conversionRecord;
|
|
10250
|
+
}
|
|
10251
|
+
return modifierNode;
|
|
10252
|
+
}).flat();
|
|
10253
|
+
// Special case: $csp modifier: merge multiple $csp modifiers into one
|
|
10254
|
+
// and put it at the end of the modifier list
|
|
10255
|
+
if (cspCount) {
|
|
10256
|
+
const cspValues = [];
|
|
10257
|
+
modifierListClone.children = modifierListClone.children.filter((modifierNode) => {
|
|
10258
|
+
if (modifierNode.modifier.value === CSP_MODIFIER) {
|
|
10259
|
+
if (!modifierNode.value?.value) {
|
|
10260
|
+
throw new RuleConversionError('$csp modifier value is missing');
|
|
10261
|
+
}
|
|
10262
|
+
cspValues.push(modifierNode.value?.value);
|
|
10263
|
+
return false;
|
|
10264
|
+
}
|
|
10265
|
+
return true;
|
|
10266
|
+
});
|
|
10267
|
+
modifierListClone.children.push(createModifierNode(CSP_MODIFIER, cspValues.join(CSP_SEPARATOR)));
|
|
10268
|
+
}
|
|
10269
|
+
// Before returning the result, remove duplicated modifiers
|
|
10270
|
+
modifierListClone.children = modifierListClone.children.filter((modifierNode, index, self) => self.findIndex((m) => m.modifier.value === modifierNode.modifier.value
|
|
10271
|
+
&& m.exception === modifierNode.exception
|
|
10272
|
+
&& m.value?.value === modifierNode.value?.value) === index);
|
|
10273
|
+
return createConversionResult(modifierListClone, true);
|
|
9728
10274
|
}
|
|
9729
|
-
return
|
|
10275
|
+
return createConversionResult(modifierList, false);
|
|
9730
10276
|
}
|
|
9731
10277
|
}
|
|
9732
10278
|
|
|
@@ -9743,17 +10289,35 @@ class NetworkRuleConverter extends RuleConverterBase {
|
|
|
9743
10289
|
* Converts a network rule to AdGuard format, if possible.
|
|
9744
10290
|
*
|
|
9745
10291
|
* @param rule Rule node to convert
|
|
9746
|
-
* @returns
|
|
10292
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
10293
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
10294
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9747
10295
|
* @throws If the rule is invalid or cannot be converted
|
|
9748
10296
|
*/
|
|
9749
10297
|
static convertToAdg(rule) {
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
10298
|
+
if (rule.modifiers) {
|
|
10299
|
+
const modifiers = NetworkRuleModifierListConverter.convertToAdg(rule.modifiers);
|
|
10300
|
+
// If the object reference is different, it means that the modifiers were converted
|
|
10301
|
+
// In this case, we should clone the entire rule and replace the modifiers with the converted ones
|
|
10302
|
+
if (modifiers.isConverted) {
|
|
10303
|
+
return {
|
|
10304
|
+
result: [{
|
|
10305
|
+
category: exports.RuleCategory.Network,
|
|
10306
|
+
type: 'NetworkRule',
|
|
10307
|
+
syntax: rule.syntax,
|
|
10308
|
+
exception: rule.exception,
|
|
10309
|
+
pattern: {
|
|
10310
|
+
type: 'Value',
|
|
10311
|
+
value: rule.pattern.value,
|
|
10312
|
+
},
|
|
10313
|
+
modifiers: modifiers.result,
|
|
10314
|
+
}],
|
|
10315
|
+
isConverted: true,
|
|
10316
|
+
};
|
|
10317
|
+
}
|
|
9755
10318
|
}
|
|
9756
|
-
return
|
|
10319
|
+
// If the modifiers were not converted, return the original rule
|
|
10320
|
+
return createNodeConversionResult([rule], false);
|
|
9757
10321
|
}
|
|
9758
10322
|
}
|
|
9759
10323
|
|
|
@@ -9774,48 +10338,27 @@ class RuleConverter extends RuleConverterBase {
|
|
|
9774
10338
|
* Converts an adblock filtering rule to AdGuard format, if possible.
|
|
9775
10339
|
*
|
|
9776
10340
|
* @param rule Rule node to convert
|
|
9777
|
-
* @returns
|
|
10341
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
10342
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
10343
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
9778
10344
|
* @throws If the rule is invalid or cannot be converted
|
|
9779
10345
|
*/
|
|
9780
10346
|
static convertToAdg(rule) {
|
|
9781
|
-
// Clone the provided AST node to avoid side effects
|
|
9782
|
-
const ruleNode = cloneDeep(rule);
|
|
9783
10347
|
// Delegate conversion to the corresponding sub-converter
|
|
9784
10348
|
// based on the rule category
|
|
9785
|
-
switch (
|
|
10349
|
+
switch (rule.category) {
|
|
9786
10350
|
case exports.RuleCategory.Comment:
|
|
9787
|
-
return CommentRuleConverter.convertToAdg(
|
|
10351
|
+
return CommentRuleConverter.convertToAdg(rule);
|
|
9788
10352
|
case exports.RuleCategory.Cosmetic:
|
|
9789
|
-
return CosmeticRuleConverter.convertToAdg(
|
|
10353
|
+
return CosmeticRuleConverter.convertToAdg(rule);
|
|
9790
10354
|
case exports.RuleCategory.Network:
|
|
9791
|
-
return NetworkRuleConverter.convertToAdg(
|
|
10355
|
+
return NetworkRuleConverter.convertToAdg(rule);
|
|
9792
10356
|
default:
|
|
9793
|
-
throw new RuleConversionError(`Unknown rule category: ${
|
|
10357
|
+
throw new RuleConversionError(`Unknown rule category: ${rule.category}`);
|
|
9794
10358
|
}
|
|
9795
10359
|
}
|
|
9796
10360
|
}
|
|
9797
10361
|
|
|
9798
|
-
/**
|
|
9799
|
-
* @file Utility functions for working with filter list nodes
|
|
9800
|
-
*/
|
|
9801
|
-
/**
|
|
9802
|
-
* Creates a filter list node
|
|
9803
|
-
*
|
|
9804
|
-
* @param rules Rules to put in the list (optional, defaults to an empty list)
|
|
9805
|
-
* @returns Filter list node
|
|
9806
|
-
*/
|
|
9807
|
-
function createFilterListNode(rules = []) {
|
|
9808
|
-
const result = {
|
|
9809
|
-
type: 'FilterList',
|
|
9810
|
-
children: [],
|
|
9811
|
-
};
|
|
9812
|
-
// We need to clone the rules to avoid side effects
|
|
9813
|
-
if (rules.length > 0) {
|
|
9814
|
-
result.children = cloneDeep(rules);
|
|
9815
|
-
}
|
|
9816
|
-
return result;
|
|
9817
|
-
}
|
|
9818
|
-
|
|
9819
10362
|
/**
|
|
9820
10363
|
* @file Adblock filter list converter
|
|
9821
10364
|
*/
|
|
@@ -9834,18 +10377,133 @@ class FilterListConverter extends ConverterBase {
|
|
|
9834
10377
|
* Converts an adblock filter list to AdGuard format, if possible.
|
|
9835
10378
|
*
|
|
9836
10379
|
* @param filterListNode Filter list node to convert
|
|
9837
|
-
* @
|
|
9838
|
-
*
|
|
10380
|
+
* @param tolerant Indicates whether the converter should be tolerant to invalid rules. If enabled and a rule is
|
|
10381
|
+
* invalid, it will be left as is. If disabled and a rule is invalid, the whole filter list will be failed.
|
|
10382
|
+
* Defaults to `true`.
|
|
10383
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
10384
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
10385
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
10386
|
+
* @throws If the filter list is invalid or cannot be converted (if the tolerant mode is disabled)
|
|
10387
|
+
*/
|
|
10388
|
+
static convertToAdg(filterListNode, tolerant = true) {
|
|
10389
|
+
// Prepare a map to store the converted rules by their index in the filter list
|
|
10390
|
+
const conversionMap = new MultiValueMap();
|
|
10391
|
+
// Iterate over the filtering rules and convert them one by one, then add them to the result (one conversion may
|
|
10392
|
+
// result in multiple rules)
|
|
10393
|
+
for (let i = 0; i < filterListNode.children.length; i += 1) {
|
|
10394
|
+
try {
|
|
10395
|
+
const convertedRules = RuleConverter.convertToAdg(filterListNode.children[i]);
|
|
10396
|
+
// Add the converted rules to the map if they were converted
|
|
10397
|
+
if (convertedRules.isConverted) {
|
|
10398
|
+
conversionMap.add(i, ...convertedRules.result);
|
|
10399
|
+
}
|
|
10400
|
+
}
|
|
10401
|
+
catch (error) {
|
|
10402
|
+
// If the tolerant mode is disabled, we should throw an error, this will fail the whole filter list
|
|
10403
|
+
// conversion.
|
|
10404
|
+
// Otherwise, we just ignore the error and leave the rule as is
|
|
10405
|
+
if (!tolerant) {
|
|
10406
|
+
throw error;
|
|
10407
|
+
}
|
|
10408
|
+
}
|
|
10409
|
+
}
|
|
10410
|
+
// If the conversion map is empty, it means that no rules were converted, so we can return the original filter
|
|
10411
|
+
// list
|
|
10412
|
+
if (conversionMap.size === 0) {
|
|
10413
|
+
return createConversionResult(filterListNode, false);
|
|
10414
|
+
}
|
|
10415
|
+
// Otherwise, create a new filter list node with the converted rules
|
|
10416
|
+
const convertedFilterList = {
|
|
10417
|
+
type: 'FilterList',
|
|
10418
|
+
children: [],
|
|
10419
|
+
};
|
|
10420
|
+
// Iterate over the original rules again and add them to the converted filter list, replacing the converted
|
|
10421
|
+
// rules with the new ones at the specified indexes
|
|
10422
|
+
for (let i = 0; i < filterListNode.children.length; i += 1) {
|
|
10423
|
+
const rules = conversionMap.get(i);
|
|
10424
|
+
if (rules) {
|
|
10425
|
+
convertedFilterList.children.push(...rules);
|
|
10426
|
+
}
|
|
10427
|
+
else {
|
|
10428
|
+
// We clone the unconverted rules to avoid mutating the original filter list if we return the converted
|
|
10429
|
+
// one
|
|
10430
|
+
convertedFilterList.children.push(clone(filterListNode.children[i]));
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10433
|
+
return createConversionResult(convertedFilterList, true);
|
|
10434
|
+
}
|
|
10435
|
+
}
|
|
10436
|
+
|
|
10437
|
+
/**
|
|
10438
|
+
* @file Filter list converter for raw filter lists
|
|
10439
|
+
*
|
|
10440
|
+
* Technically, this is a wrapper around `FilterListConverter` that works with nodes instead of strings.
|
|
10441
|
+
*/
|
|
10442
|
+
/**
|
|
10443
|
+
* Adblock filter list converter class.
|
|
10444
|
+
*
|
|
10445
|
+
* You can use this class to convert string-based filter lists, since most of the converters work with nodes.
|
|
10446
|
+
* This class just provides an extra layer on top of the {@link FilterListConverter} and calls the parser/serializer
|
|
10447
|
+
* before/after the conversion internally.
|
|
10448
|
+
*
|
|
10449
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
10450
|
+
*/
|
|
10451
|
+
class RawFilterListConverter extends ConverterBase {
|
|
10452
|
+
/**
|
|
10453
|
+
* Converts an adblock filter list text to AdGuard format, if possible.
|
|
10454
|
+
*
|
|
10455
|
+
* @param rawFilterList Raw filter list text to convert
|
|
10456
|
+
* @param tolerant Indicates whether the converter should be tolerant to invalid rules. If enabled and a rule is
|
|
10457
|
+
* invalid, it will be left as is. If disabled and a rule is invalid, the whole filter list will be failed.
|
|
10458
|
+
* Defaults to `true`.
|
|
10459
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
10460
|
+
* the array of converted filter list text, and its `isConverted` flag indicates whether the original rule was
|
|
10461
|
+
* converted. If the rule was not converted, the original filter list text will be returned
|
|
10462
|
+
* @throws If the filter list is invalid or cannot be converted (if the tolerant mode is disabled)
|
|
9839
10463
|
*/
|
|
9840
|
-
static convertToAdg(
|
|
9841
|
-
const
|
|
9842
|
-
//
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
const convertedRules = RuleConverter.convertToAdg(ruleNode);
|
|
9846
|
-
result.children.push(...convertedRules);
|
|
10464
|
+
static convertToAdg(rawFilterList, tolerant = true) {
|
|
10465
|
+
const conversionResult = FilterListConverter.convertToAdg(FilterListParser.parse(rawFilterList, tolerant), tolerant);
|
|
10466
|
+
// If the filter list was not converted, return the original text
|
|
10467
|
+
if (!conversionResult.isConverted) {
|
|
10468
|
+
return createConversionResult(rawFilterList, false);
|
|
9847
10469
|
}
|
|
9848
|
-
return result
|
|
10470
|
+
// Otherwise, serialize the filter list and return the result
|
|
10471
|
+
return createConversionResult(FilterListParser.generate(conversionResult.result), true);
|
|
10472
|
+
}
|
|
10473
|
+
}
|
|
10474
|
+
|
|
10475
|
+
/**
|
|
10476
|
+
* @file Rule converter for raw rules
|
|
10477
|
+
*
|
|
10478
|
+
* Technically, this is a wrapper around `RuleConverter` that works with nodes instead of strings.
|
|
10479
|
+
*/
|
|
10480
|
+
/**
|
|
10481
|
+
* Adblock filtering rule converter class.
|
|
10482
|
+
*
|
|
10483
|
+
* You can use this class to convert string-based adblock rules, since most of the converters work with nodes.
|
|
10484
|
+
* This class just provides an extra layer on top of the {@link RuleConverter} and calls the parser/serializer
|
|
10485
|
+
* before/after the conversion internally.
|
|
10486
|
+
*
|
|
10487
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
10488
|
+
*/
|
|
10489
|
+
class RawRuleConverter extends ConverterBase {
|
|
10490
|
+
/**
|
|
10491
|
+
* Converts an adblock filtering rule to AdGuard format, if possible.
|
|
10492
|
+
*
|
|
10493
|
+
* @param rawRule Raw rule text to convert
|
|
10494
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
10495
|
+
* the array of converted rule texts, and its `isConverted` flag indicates whether the original rule was converted.
|
|
10496
|
+
* If the rule was not converted, the original rule text will be returned
|
|
10497
|
+
* @throws If the rule is invalid or cannot be converted
|
|
10498
|
+
*/
|
|
10499
|
+
static convertToAdg(rawRule) {
|
|
10500
|
+
const conversionResult = RuleConverter.convertToAdg(RuleParser.parse(rawRule));
|
|
10501
|
+
// If the rule was not converted, return the original rule text
|
|
10502
|
+
if (!conversionResult.isConverted) {
|
|
10503
|
+
return createConversionResult([rawRule], false);
|
|
10504
|
+
}
|
|
10505
|
+
// Otherwise, serialize the converted rule nodes
|
|
10506
|
+
return createConversionResult(conversionResult.result.map(RuleParser.generate), true);
|
|
9849
10507
|
}
|
|
9850
10508
|
}
|
|
9851
10509
|
|
|
@@ -9927,7 +10585,7 @@ class LogicalExpressionUtils {
|
|
|
9927
10585
|
}
|
|
9928
10586
|
}
|
|
9929
10587
|
|
|
9930
|
-
const version$1 = "1.1.
|
|
10588
|
+
const version$1 = "1.1.7";
|
|
9931
10589
|
|
|
9932
10590
|
/**
|
|
9933
10591
|
* @file AGTree version
|
|
@@ -9988,6 +10646,8 @@ exports.PREPROCESSOR_MARKER = PREPROCESSOR_MARKER;
|
|
|
9988
10646
|
exports.ParameterListParser = ParameterListParser;
|
|
9989
10647
|
exports.PreProcessorCommentRuleParser = PreProcessorCommentRuleParser;
|
|
9990
10648
|
exports.QuoteUtils = QuoteUtils;
|
|
10649
|
+
exports.RawFilterListConverter = RawFilterListConverter;
|
|
10650
|
+
exports.RawRuleConverter = RawRuleConverter;
|
|
9991
10651
|
exports.RegExpUtils = RegExpUtils;
|
|
9992
10652
|
exports.RuleConversionError = RuleConversionError;
|
|
9993
10653
|
exports.RuleConverter = RuleConverter;
|