@adguard/agtree 2.0.0-alpha.0 → 2.0.1
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/README.md +12 -0
- package/dist/agtree.cjs +2056 -1384
- package/dist/agtree.d.ts +80 -9
- package/dist/agtree.esm.js +2055 -1385
- package/dist/agtree.iife.min.js +5 -5
- package/dist/agtree.umd.min.js +5 -5
- package/dist/build.txt +1 -1
- package/dist/compatibility-tables.json +286 -98
- package/package.json +2 -2
package/dist/agtree.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* AGTree v2.0.
|
|
2
|
+
* AGTree v2.0.1 (build date: Fri, 06 Sep 2024 12:11:38 GMT)
|
|
3
3
|
* (c) 2024 Adguard Software Ltd.
|
|
4
4
|
* Released under the MIT license
|
|
5
5
|
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
|
|
@@ -212,6 +212,7 @@ const NUMBERS = new Set([NUMBER_0, NUMBER_1, NUMBER_2, NUMBER_3, NUMBER_4, NUMBE
|
|
|
212
212
|
const REGEX_MARKER = '/';
|
|
213
213
|
const ADG_SCRIPTLET_MASK = '//scriptlet';
|
|
214
214
|
const UBO_SCRIPTLET_MASK = '+js';
|
|
215
|
+
const UBO_SCRIPTLET_MASK_LEGACY = 'script:inject';
|
|
215
216
|
const UBO_HTML_MASK = '^';
|
|
216
217
|
// Modifiers are separated by ",". For example: "script,domain=example.com"
|
|
217
218
|
const MODIFIERS_SEPARATOR = ',';
|
|
@@ -763,6 +764,15 @@ class StringUtils {
|
|
|
763
764
|
}
|
|
764
765
|
}
|
|
765
766
|
|
|
767
|
+
/**
|
|
768
|
+
* Possible operators in the logical expression.
|
|
769
|
+
*/
|
|
770
|
+
var OperatorValue;
|
|
771
|
+
(function (OperatorValue) {
|
|
772
|
+
OperatorValue["Not"] = "!";
|
|
773
|
+
OperatorValue["And"] = "&&";
|
|
774
|
+
OperatorValue["Or"] = "||";
|
|
775
|
+
})(OperatorValue || (OperatorValue = {}));
|
|
766
776
|
/**
|
|
767
777
|
* Represents the different comment markers that can be used in an adblock rule.
|
|
768
778
|
*
|
|
@@ -1139,6 +1149,15 @@ const isUndefined = value => {
|
|
|
1139
1149
|
const isNull = value => {
|
|
1140
1150
|
return value === null;
|
|
1141
1151
|
};
|
|
1152
|
+
/**
|
|
1153
|
+
* Checks whether the given value is a string.
|
|
1154
|
+
*
|
|
1155
|
+
* @param value Value to check.
|
|
1156
|
+
* @returns `true` if the value is a string, `false` otherwise.
|
|
1157
|
+
*/
|
|
1158
|
+
const isString = value => {
|
|
1159
|
+
return typeof value === 'string';
|
|
1160
|
+
};
|
|
1142
1161
|
/**
|
|
1143
1162
|
* Checks whether the given value is an array of Uint8Arrays.
|
|
1144
1163
|
*
|
|
@@ -2958,15 +2977,6 @@ var ParenthesisNodeBinaryPropMap;
|
|
|
2958
2977
|
ParenthesisNodeBinaryPropMap[ParenthesisNodeBinaryPropMap["Start"] = 2] = "Start";
|
|
2959
2978
|
ParenthesisNodeBinaryPropMap[ParenthesisNodeBinaryPropMap["End"] = 3] = "End";
|
|
2960
2979
|
})(ParenthesisNodeBinaryPropMap || (ParenthesisNodeBinaryPropMap = {}));
|
|
2961
|
-
/**
|
|
2962
|
-
* Possible operators in the logical expression.
|
|
2963
|
-
*/
|
|
2964
|
-
var OperatorValue;
|
|
2965
|
-
(function (OperatorValue) {
|
|
2966
|
-
OperatorValue["Not"] = "!";
|
|
2967
|
-
OperatorValue["And"] = "&&";
|
|
2968
|
-
OperatorValue["Or"] = "||";
|
|
2969
|
-
})(OperatorValue || (OperatorValue = {}));
|
|
2970
2980
|
/**
|
|
2971
2981
|
* Possible token types in the logical expression.
|
|
2972
2982
|
*/
|
|
@@ -3012,7 +3022,9 @@ const getOperatorOrFail = binary => {
|
|
|
3012
3022
|
/**
|
|
3013
3023
|
* Serialization map for known variables.
|
|
3014
3024
|
*/
|
|
3015
|
-
const KNOWN_VARIABLES_MAP = new Map([['ext_abp', 0], ['ext_ublock', 1], ['ext_ubol', 2], ['ext_devbuild', 3], ['env_chromium', 4], ['env_edge', 5], ['env_firefox', 6], ['env_mobile', 7], ['env_safari', 8], ['env_mv3', 9], ['false', 10], ['cap_html_filtering', 11], ['cap_user_stylesheet', 12], ['adguard', 13], ['adguard_app_windows', 14], ['adguard_app_mac', 15], ['adguard_app_android', 16], ['adguard_app_ios', 17], ['adguard_ext_safari', 18], ['adguard_ext_chromium', 19], ['adguard_ext_firefox', 20], ['adguard_ext_edge', 21], ['adguard_ext_opera', 22], ['adguard_ext_android_cb', 23]
|
|
3025
|
+
const KNOWN_VARIABLES_MAP = new Map([['ext_abp', 0], ['ext_ublock', 1], ['ext_ubol', 2], ['ext_devbuild', 3], ['env_chromium', 4], ['env_edge', 5], ['env_firefox', 6], ['env_mobile', 7], ['env_safari', 8], ['env_mv3', 9], ['false', 10], ['cap_html_filtering', 11], ['cap_user_stylesheet', 12], ['adguard', 13], ['adguard_app_windows', 14], ['adguard_app_mac', 15], ['adguard_app_android', 16], ['adguard_app_ios', 17], ['adguard_ext_safari', 18], ['adguard_ext_chromium', 19], ['adguard_ext_firefox', 20], ['adguard_ext_edge', 21], ['adguard_ext_opera', 22], ['adguard_ext_android_cb', 23]
|
|
3026
|
+
// TODO: Add 'adguard_ext_chromium_mv3' to the list
|
|
3027
|
+
]);
|
|
3016
3028
|
/**
|
|
3017
3029
|
* Deserialization map for known variables.
|
|
3018
3030
|
*/
|
|
@@ -5716,7 +5728,7 @@ class CssTokenStream {
|
|
|
5716
5728
|
* @returns An array containing the number of tokens skipped and the number of tokens skipped without leading and
|
|
5717
5729
|
* trailing whitespace tokens.
|
|
5718
5730
|
*/
|
|
5719
|
-
|
|
5731
|
+
skipUntilExt(type, balance) {
|
|
5720
5732
|
let i = this.index;
|
|
5721
5733
|
let firstNonWsToken = -1; // -1 means no non-whitespace token found yet
|
|
5722
5734
|
let lastNonWsToken = -1; // -1 means no non-whitespace token found yet
|
|
@@ -5944,7 +5956,7 @@ class AdgCssInjectionParser extends ParserBase {
|
|
|
5944
5956
|
// └ this one
|
|
5945
5957
|
const {
|
|
5946
5958
|
skippedTrimmed: selectorTokensLength
|
|
5947
|
-
} = stream.
|
|
5959
|
+
} = stream.skipUntilExt(cssTokenizer.TokenType.OpenCurlyBracket, balanceShift + 1);
|
|
5948
5960
|
stream.expect(cssTokenizer.TokenType.OpenCurlyBracket);
|
|
5949
5961
|
// If the skipped tokens count is 0 without leading and trailing whitespace characters, then the selector list
|
|
5950
5962
|
// is empty
|
|
@@ -6184,6 +6196,7 @@ class AbpSnippetInjectionBodyParser extends ParserBase {
|
|
|
6184
6196
|
*
|
|
6185
6197
|
* @note Only 256 values can be represented this way.
|
|
6186
6198
|
*/
|
|
6199
|
+
// TODO: Update this map with the actual values
|
|
6187
6200
|
static FREQUENT_ARGS_SERIALIZATION_MAP = new Map([['abort-current-inline-script', 0], ['abort-on-property-read', 1], ['abort-on-property-write', 2], ['json-prune', 3], ['log', 4], ['prevent-listener', 5], ['cookie-remover', 6], ['override-property-read', 7], ['abort-on-iframe-property-read', 8], ['abort-on-iframe-property-write', 9], ['freeze-element', 10], ['json-override', 11], ['simulate-mouse-event', 12], ['strip-fetch-query-parameter', 13], ['hide-if-contains', 14], ['hide-if-contains-image', 15], ['hide-if-contains-image-hash', 16], ['hide-if-contains-similar-text', 17], ['hide-if-contains-visible-text', 18], ['hide-if-contains-and-matches-style', 19], ['hide-if-graph-matches', 20], ['hide-if-has-and-matches-style', 21], ['hide-if-labelled-by', 22], ['hide-if-matches-xpath', 23], ['hide-if-matches-computed-xpath', 24], ['hide-if-shadow-contains', 25], ['debug', 26], ['trace', 27], ['race', 28]]);
|
|
6188
6201
|
/**
|
|
6189
6202
|
* Value map for binary deserialization. This helps to reduce the size of the serialized data,
|
|
@@ -6311,6 +6324,7 @@ class UboScriptletInjectionBodyParser extends ParserBase {
|
|
|
6311
6324
|
*
|
|
6312
6325
|
* @note Only 256 values can be represented this way.
|
|
6313
6326
|
*/
|
|
6327
|
+
// TODO: Update this map with the actual values
|
|
6314
6328
|
static FREQUENT_ARGS_SERIALIZATION_MAP = new Map([['abort-current-script.js', 0], ['acs.js', 1], ['abort-current-inline-script.js', 2], ['acis.js', 3], ['abort-on-property-read.js', 4], ['aopr.js', 5], ['abort-on-property-write.js', 6], ['aopw.js', 7], ['abort-on-stack-trace.js', 8], ['aost.js', 9], ['adjust-setInterval.js', 10], ['nano-setInterval-booster.js', 11], ['nano-sib.js', 12], ['adjust-setTimeout.js', 13], ['nano-setTimeout-booster.js', 14], ['nano-stb.js', 15], ['close-window.js', 16], ['window-close-if.js', 17], ['disable-newtab-links.js', 18], ['evaldata-prune.js', 19], ['json-prune.js', 20], ['addEventListener-logger.js', 21], ['aell.js', 22], ['m3u-prune.js', 23], ['nowebrtc.js', 24], ['addEventListener-defuser.js', 25], ['aeld.js', 26], ['prevent-addEventListener.js', 27], ['adfly-defuser.js', 28], ['noeval-if.js', 29], ['prevent-eval-if.js', 30], ['no-fetch-if.js', 31], ['prevent-fetch.js', 32], ['no-xhr-if.js', 33], ['prevent-xhr.js', 34], ['prevent-refresh.js', 35], ['refresh-defuser.js', 36], ['no-requestAnimationFrame-if.js', 37], ['norafif.js', 38], ['prevent-requestAnimationFrame.js', 39], ['no-setInterval-if.js', 40], ['nosiif.js', 41], ['prevent-setInterval.js', 42], ['setInterval-defuser.js', 43], ['no-setTimeout-if.js', 44], ['nostif.js', 45], ['prevent-setTimeout.js', 46], ['setTimeout-defuser.js', 47], ['no-window-open-if.js', 48], ['nowoif.js', 49], ['prevent-window-open.js', 50], ['window.open-defuser.js', 51], ['remove-attr.js', 52], ['ra.js', 53], ['remove-class.js', 54], ['rc.js', 55], ['remove-cookie.js', 56], ['cookie-remover.js', 57], ['remove-node-text.js', 58], ['rmnt.js', 59], ['set-attr.js', 60], ['set-constant.js', 61], ['set.js', 62], ['set-cookie.js', 63], ['set-local-storage-item.js', 64], ['set-session-storage-item.js', 65], ['xml-prune.js', 66], ['webrtc-if.js', 67], ['overlay-buster.js', 68], ['alert-buster.js', 69], ['golem.de.js', 70], ['href-sanitizer.js', 71], ['call-nothrow.js', 72], ['window.name-defuser.js', 73], ['spoof-css.js', 74], ['trusted-set-constant.js', 75], ['trusted-set.js', 76], ['trusted-set-cookie.js', 77], ['trusted-set-local-storage-item.js', 78], ['trusted-replace-fetch-response.js', 79], ['json-prune-fetch-response.js', 80], ['json-prune-xhr-response.js', 81], ['trusted-replace-xhr-response.js', 82], ['multiup.js', 83], ['prevent-canvas.js', 84], ['set-cookie-reload.js', 85], ['trusted-set-cookie-reload.js', 86], ['trusted-click-element.js', 87], ['trusted-prune-inbound-object.js', 88], ['trusted-prune-outbound-object.js', 89], ['trusted-set-session-storage-item.js', 90], ['trusted-replace-node-text.js', 91], ['trusted-rpnt.js', 92], ['replace-node-text.js', 93], ['rpnt.js', 94]]);
|
|
6315
6329
|
/**
|
|
6316
6330
|
* Value map for binary deserialization. This helps to reduce the size of the serialized data,
|
|
@@ -6334,11 +6348,17 @@ class UboScriptletInjectionBodyParser extends ParserBase {
|
|
|
6334
6348
|
let offset = 0;
|
|
6335
6349
|
// Skip leading spaces
|
|
6336
6350
|
offset = StringUtils.skipWS(raw, offset);
|
|
6351
|
+
let scriptletMaskLength = 0;
|
|
6352
|
+
if (raw.startsWith(UBO_SCRIPTLET_MASK, offset)) {
|
|
6353
|
+
scriptletMaskLength = UBO_SCRIPTLET_MASK.length;
|
|
6354
|
+
} else if (raw.startsWith(UBO_SCRIPTLET_MASK_LEGACY, offset)) {
|
|
6355
|
+
scriptletMaskLength = UBO_SCRIPTLET_MASK_LEGACY.length;
|
|
6356
|
+
}
|
|
6337
6357
|
// Scriptlet call should start with "+js"
|
|
6338
|
-
if (!
|
|
6358
|
+
if (!scriptletMaskLength) {
|
|
6339
6359
|
throw new AdblockSyntaxError(this.ERROR_MESSAGES.NO_SCRIPTLET_MASK, baseOffset + offset, baseOffset + raw.length);
|
|
6340
6360
|
}
|
|
6341
|
-
offset +=
|
|
6361
|
+
offset += scriptletMaskLength;
|
|
6342
6362
|
// Whitespace is not allowed after the mask
|
|
6343
6363
|
if (raw[offset] === SPACE) {
|
|
6344
6364
|
throw new AdblockSyntaxError(this.ERROR_MESSAGES.WHITESPACE_AFTER_MASK, baseOffset + offset, baseOffset + raw.length);
|
|
@@ -6383,6 +6403,7 @@ class UboScriptletInjectionBodyParser extends ParserBase {
|
|
|
6383
6403
|
if (node.children.length > 1) {
|
|
6384
6404
|
throw new Error(this.ERROR_MESSAGES.NO_MULTIPLE_SCRIPTLET_CALLS);
|
|
6385
6405
|
}
|
|
6406
|
+
// During generation, we only support the modern scriptlet mask
|
|
6386
6407
|
result.push(UBO_SCRIPTLET_MASK);
|
|
6387
6408
|
result.push(OPEN_PARENTHESIS);
|
|
6388
6409
|
if (node.children.length > 0) {
|
|
@@ -6448,6 +6469,7 @@ class AdgScriptletInjectionBodyParser extends ParserBase {
|
|
|
6448
6469
|
*
|
|
6449
6470
|
* @note Only 256 values can be represented this way.
|
|
6450
6471
|
*/
|
|
6472
|
+
// TODO: Update this map with the actual values
|
|
6451
6473
|
static FREQUENT_ARGS_SERIALIZATION_MAP = new Map([['abort-current-inline-script', 0], ['abort-on-property-read', 1], ['abort-on-property-write', 2], ['abort-on-stack-trace', 3], ['adjust-setInterval', 4], ['adjust-setTimeout', 5], ['close-window', 6], ['debug-current-inline-script', 7], ['debug-on-property-read', 8], ['debug-on-property-write', 9], ['dir-string', 10], ['disable-newtab-links', 11], ['evaldata-prune', 12], ['json-prune', 13], ['log', 14], ['log-addEventListener', 15], ['log-eval', 16], ['log-on-stack-trace', 17], ['m3u-prune', 18], ['noeval', 19], ['nowebrtc', 20], ['no-topics', 21], ['prevent-addEventListener', 22], ['prevent-adfly', 23], ['prevent-bab', 24], ['prevent-eval-if', 25], ['prevent-fab-3.2.0', 26], ['prevent-fetch', 27], ['prevent-xhr', 28], ['prevent-popads-net', 29], ['prevent-refresh', 30], ['prevent-requestAnimationFrame', 31], ['prevent-setInterval', 32], ['prevent-setTimeout', 33], ['prevent-window-open', 34], ['remove-attr', 35], ['remove-class', 36], ['remove-cookie', 37], ['remove-node-text', 38], ['set-attr', 39], ['set-constant', 40], ['set-cookie', 41], ['set-cookie-reload', 42], ['set-local-storage-item', 43], ['set-popads-dummy', 44], ['set-session-storage-item', 45], ['xml-prune', 46]]);
|
|
6452
6474
|
/**
|
|
6453
6475
|
* Value map for binary deserialization. This helps to reduce the size of the serialized data,
|
|
@@ -6919,7 +6941,7 @@ class CosmeticRuleParser extends ParserBase {
|
|
|
6919
6941
|
};
|
|
6920
6942
|
};
|
|
6921
6943
|
const parseUboScriptletInjection = () => {
|
|
6922
|
-
if (!rawBody.startsWith(UBO_SCRIPTLET_MASK)) {
|
|
6944
|
+
if (!rawBody.startsWith(UBO_SCRIPTLET_MASK) && !rawBody.startsWith(UBO_SCRIPTLET_MASK_LEGACY)) {
|
|
6923
6945
|
return null;
|
|
6924
6946
|
}
|
|
6925
6947
|
if (!options.parseUboSpecificRules) {
|
|
@@ -13874,7 +13896,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13874
13896
|
deprecationMessage: null,
|
|
13875
13897
|
removed: false,
|
|
13876
13898
|
removalMessage: null,
|
|
13877
|
-
isBlocking: false
|
|
13899
|
+
isBlocking: false,
|
|
13900
|
+
resourceTypes: []
|
|
13878
13901
|
}, {
|
|
13879
13902
|
name: "1x1.gif",
|
|
13880
13903
|
aliases: null,
|
|
@@ -13886,7 +13909,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13886
13909
|
deprecationMessage: null,
|
|
13887
13910
|
removed: false,
|
|
13888
13911
|
removalMessage: null,
|
|
13889
|
-
isBlocking: false
|
|
13912
|
+
isBlocking: false,
|
|
13913
|
+
resourceTypes: ["image"]
|
|
13890
13914
|
}, {
|
|
13891
13915
|
name: "1x1-transparent-gif",
|
|
13892
13916
|
aliases: null,
|
|
@@ -13898,7 +13922,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13898
13922
|
deprecationMessage: null,
|
|
13899
13923
|
removed: false,
|
|
13900
13924
|
removalMessage: null,
|
|
13901
|
-
isBlocking: false
|
|
13925
|
+
isBlocking: false,
|
|
13926
|
+
resourceTypes: []
|
|
13902
13927
|
}],
|
|
13903
13928
|
map: {
|
|
13904
13929
|
"1": 0,
|
|
@@ -13929,7 +13954,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13929
13954
|
deprecationMessage: null,
|
|
13930
13955
|
removed: false,
|
|
13931
13956
|
removalMessage: null,
|
|
13932
|
-
isBlocking: false
|
|
13957
|
+
isBlocking: false,
|
|
13958
|
+
resourceTypes: []
|
|
13933
13959
|
}, {
|
|
13934
13960
|
name: "2x2.png",
|
|
13935
13961
|
aliases: null,
|
|
@@ -13941,7 +13967,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13941
13967
|
deprecationMessage: null,
|
|
13942
13968
|
removed: false,
|
|
13943
13969
|
removalMessage: null,
|
|
13944
|
-
isBlocking: false
|
|
13970
|
+
isBlocking: false,
|
|
13971
|
+
resourceTypes: ["image"]
|
|
13945
13972
|
}, {
|
|
13946
13973
|
name: "2x2-transparent-png",
|
|
13947
13974
|
aliases: null,
|
|
@@ -13953,7 +13980,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13953
13980
|
deprecationMessage: null,
|
|
13954
13981
|
removed: false,
|
|
13955
13982
|
removalMessage: null,
|
|
13956
|
-
isBlocking: false
|
|
13983
|
+
isBlocking: false,
|
|
13984
|
+
resourceTypes: []
|
|
13957
13985
|
}],
|
|
13958
13986
|
map: {
|
|
13959
13987
|
"1": 0,
|
|
@@ -13984,7 +14012,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13984
14012
|
deprecationMessage: null,
|
|
13985
14013
|
removed: false,
|
|
13986
14014
|
removalMessage: null,
|
|
13987
|
-
isBlocking: false
|
|
14015
|
+
isBlocking: false,
|
|
14016
|
+
resourceTypes: []
|
|
13988
14017
|
}, {
|
|
13989
14018
|
name: "32x32.png",
|
|
13990
14019
|
aliases: null,
|
|
@@ -13996,7 +14025,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13996
14025
|
deprecationMessage: null,
|
|
13997
14026
|
removed: false,
|
|
13998
14027
|
removalMessage: null,
|
|
13999
|
-
isBlocking: false
|
|
14028
|
+
isBlocking: false,
|
|
14029
|
+
resourceTypes: ["image"]
|
|
14000
14030
|
}, {
|
|
14001
14031
|
name: "32x32-transparent-png",
|
|
14002
14032
|
aliases: null,
|
|
@@ -14008,7 +14038,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14008
14038
|
deprecationMessage: null,
|
|
14009
14039
|
removed: false,
|
|
14010
14040
|
removalMessage: null,
|
|
14011
|
-
isBlocking: false
|
|
14041
|
+
isBlocking: false,
|
|
14042
|
+
resourceTypes: []
|
|
14012
14043
|
}],
|
|
14013
14044
|
map: {
|
|
14014
14045
|
"1": 0,
|
|
@@ -14039,7 +14070,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14039
14070
|
deprecationMessage: null,
|
|
14040
14071
|
removed: false,
|
|
14041
14072
|
removalMessage: null,
|
|
14042
|
-
isBlocking: false
|
|
14073
|
+
isBlocking: false,
|
|
14074
|
+
resourceTypes: []
|
|
14043
14075
|
}, {
|
|
14044
14076
|
name: "3x2.png",
|
|
14045
14077
|
aliases: null,
|
|
@@ -14051,7 +14083,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14051
14083
|
deprecationMessage: null,
|
|
14052
14084
|
removed: false,
|
|
14053
14085
|
removalMessage: null,
|
|
14054
|
-
isBlocking: false
|
|
14086
|
+
isBlocking: false,
|
|
14087
|
+
resourceTypes: ["image"]
|
|
14055
14088
|
}, {
|
|
14056
14089
|
name: "3x2-transparent-png",
|
|
14057
14090
|
aliases: null,
|
|
@@ -14063,7 +14096,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14063
14096
|
deprecationMessage: null,
|
|
14064
14097
|
removed: false,
|
|
14065
14098
|
removalMessage: null,
|
|
14066
|
-
isBlocking: false
|
|
14099
|
+
isBlocking: false,
|
|
14100
|
+
resourceTypes: []
|
|
14067
14101
|
}],
|
|
14068
14102
|
map: {
|
|
14069
14103
|
"1": 0,
|
|
@@ -14094,7 +14128,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14094
14128
|
deprecationMessage: null,
|
|
14095
14129
|
removed: false,
|
|
14096
14130
|
removalMessage: null,
|
|
14097
|
-
isBlocking: false
|
|
14131
|
+
isBlocking: false,
|
|
14132
|
+
resourceTypes: []
|
|
14098
14133
|
}, {
|
|
14099
14134
|
name: "amazon_apstag.js",
|
|
14100
14135
|
aliases: null,
|
|
@@ -14106,7 +14141,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14106
14141
|
deprecationMessage: null,
|
|
14107
14142
|
removed: false,
|
|
14108
14143
|
removalMessage: null,
|
|
14109
|
-
isBlocking: false
|
|
14144
|
+
isBlocking: false,
|
|
14145
|
+
resourceTypes: ["script"]
|
|
14110
14146
|
}],
|
|
14111
14147
|
map: {
|
|
14112
14148
|
"1": 0,
|
|
@@ -14133,7 +14169,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14133
14169
|
deprecationMessage: null,
|
|
14134
14170
|
removed: false,
|
|
14135
14171
|
removalMessage: null,
|
|
14136
|
-
isBlocking: false
|
|
14172
|
+
isBlocking: false,
|
|
14173
|
+
resourceTypes: ["script"]
|
|
14137
14174
|
}],
|
|
14138
14175
|
map: {
|
|
14139
14176
|
"1024": 0,
|
|
@@ -14153,7 +14190,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14153
14190
|
deprecationMessage: null,
|
|
14154
14191
|
removed: false,
|
|
14155
14192
|
removalMessage: null,
|
|
14156
|
-
isBlocking: false
|
|
14193
|
+
isBlocking: false,
|
|
14194
|
+
resourceTypes: ["script"]
|
|
14157
14195
|
}],
|
|
14158
14196
|
map: {
|
|
14159
14197
|
"1024": 0,
|
|
@@ -14173,7 +14211,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14173
14211
|
deprecationMessage: null,
|
|
14174
14212
|
removed: false,
|
|
14175
14213
|
removalMessage: null,
|
|
14176
|
-
isBlocking: false
|
|
14214
|
+
isBlocking: false,
|
|
14215
|
+
resourceTypes: []
|
|
14177
14216
|
}],
|
|
14178
14217
|
map: {
|
|
14179
14218
|
"1": 0,
|
|
@@ -14196,7 +14235,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14196
14235
|
deprecationMessage: null,
|
|
14197
14236
|
removed: false,
|
|
14198
14237
|
removalMessage: null,
|
|
14199
|
-
isBlocking: false
|
|
14238
|
+
isBlocking: false,
|
|
14239
|
+
resourceTypes: ["script"]
|
|
14200
14240
|
}],
|
|
14201
14241
|
map: {
|
|
14202
14242
|
"1024": 0,
|
|
@@ -14216,7 +14256,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14216
14256
|
deprecationMessage: null,
|
|
14217
14257
|
removed: false,
|
|
14218
14258
|
removalMessage: null,
|
|
14219
|
-
isBlocking: true
|
|
14259
|
+
isBlocking: true,
|
|
14260
|
+
resourceTypes: []
|
|
14220
14261
|
}, {
|
|
14221
14262
|
name: "click2load.html",
|
|
14222
14263
|
aliases: null,
|
|
@@ -14228,7 +14269,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14228
14269
|
deprecationMessage: null,
|
|
14229
14270
|
removed: false,
|
|
14230
14271
|
removalMessage: null,
|
|
14231
|
-
isBlocking: true
|
|
14272
|
+
isBlocking: true,
|
|
14273
|
+
resourceTypes: []
|
|
14232
14274
|
}],
|
|
14233
14275
|
map: {
|
|
14234
14276
|
"1": 0,
|
|
@@ -14255,7 +14297,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14255
14297
|
deprecationMessage: null,
|
|
14256
14298
|
removed: false,
|
|
14257
14299
|
removalMessage: null,
|
|
14258
|
-
isBlocking: false
|
|
14300
|
+
isBlocking: false,
|
|
14301
|
+
resourceTypes: []
|
|
14259
14302
|
}],
|
|
14260
14303
|
map: {
|
|
14261
14304
|
"1": 0,
|
|
@@ -14278,7 +14321,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14278
14321
|
deprecationMessage: null,
|
|
14279
14322
|
removed: false,
|
|
14280
14323
|
removalMessage: null,
|
|
14281
|
-
isBlocking: false
|
|
14324
|
+
isBlocking: false,
|
|
14325
|
+
resourceTypes: []
|
|
14282
14326
|
}, {
|
|
14283
14327
|
name: "empty",
|
|
14284
14328
|
aliases: null,
|
|
@@ -14290,7 +14334,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14290
14334
|
deprecationMessage: null,
|
|
14291
14335
|
removed: false,
|
|
14292
14336
|
removalMessage: null,
|
|
14293
|
-
isBlocking: false
|
|
14337
|
+
isBlocking: false,
|
|
14338
|
+
resourceTypes: []
|
|
14294
14339
|
}],
|
|
14295
14340
|
map: {
|
|
14296
14341
|
"1": 0,
|
|
@@ -14317,7 +14362,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14317
14362
|
deprecationMessage: null,
|
|
14318
14363
|
removed: false,
|
|
14319
14364
|
removalMessage: null,
|
|
14320
|
-
isBlocking: false
|
|
14365
|
+
isBlocking: false,
|
|
14366
|
+
resourceTypes: []
|
|
14321
14367
|
}, {
|
|
14322
14368
|
name: "fingerprint2.js",
|
|
14323
14369
|
aliases: null,
|
|
@@ -14329,7 +14375,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14329
14375
|
deprecationMessage: null,
|
|
14330
14376
|
removed: false,
|
|
14331
14377
|
removalMessage: null,
|
|
14332
|
-
isBlocking: false
|
|
14378
|
+
isBlocking: false,
|
|
14379
|
+
resourceTypes: ["script"]
|
|
14333
14380
|
}],
|
|
14334
14381
|
map: {
|
|
14335
14382
|
"1": 0,
|
|
@@ -14356,7 +14403,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14356
14403
|
deprecationMessage: null,
|
|
14357
14404
|
removed: false,
|
|
14358
14405
|
removalMessage: null,
|
|
14359
|
-
isBlocking: false
|
|
14406
|
+
isBlocking: false,
|
|
14407
|
+
resourceTypes: []
|
|
14360
14408
|
}, {
|
|
14361
14409
|
name: "fingerprint3.js",
|
|
14362
14410
|
aliases: null,
|
|
@@ -14368,7 +14416,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14368
14416
|
deprecationMessage: null,
|
|
14369
14417
|
removed: false,
|
|
14370
14418
|
removalMessage: null,
|
|
14371
|
-
isBlocking: false
|
|
14419
|
+
isBlocking: false,
|
|
14420
|
+
resourceTypes: ["script"]
|
|
14372
14421
|
}],
|
|
14373
14422
|
map: {
|
|
14374
14423
|
"1": 0,
|
|
@@ -14395,7 +14444,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14395
14444
|
deprecationMessage: null,
|
|
14396
14445
|
removed: false,
|
|
14397
14446
|
removalMessage: null,
|
|
14398
|
-
isBlocking: false
|
|
14447
|
+
isBlocking: false,
|
|
14448
|
+
resourceTypes: []
|
|
14399
14449
|
}],
|
|
14400
14450
|
map: {
|
|
14401
14451
|
"1": 0,
|
|
@@ -14418,7 +14468,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14418
14468
|
deprecationMessage: null,
|
|
14419
14469
|
removed: false,
|
|
14420
14470
|
removalMessage: null,
|
|
14421
|
-
isBlocking: false
|
|
14471
|
+
isBlocking: false,
|
|
14472
|
+
resourceTypes: []
|
|
14422
14473
|
}, {
|
|
14423
14474
|
name: "google-analytics_ga.js",
|
|
14424
14475
|
aliases: null,
|
|
@@ -14430,7 +14481,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14430
14481
|
deprecationMessage: null,
|
|
14431
14482
|
removed: false,
|
|
14432
14483
|
removalMessage: null,
|
|
14433
|
-
isBlocking: false
|
|
14484
|
+
isBlocking: false,
|
|
14485
|
+
resourceTypes: ["script"]
|
|
14434
14486
|
}],
|
|
14435
14487
|
map: {
|
|
14436
14488
|
"1": 0,
|
|
@@ -14457,7 +14509,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14457
14509
|
deprecationMessage: null,
|
|
14458
14510
|
removed: false,
|
|
14459
14511
|
removalMessage: null,
|
|
14460
|
-
isBlocking: false
|
|
14512
|
+
isBlocking: false,
|
|
14513
|
+
resourceTypes: []
|
|
14461
14514
|
}, {
|
|
14462
14515
|
name: "google-analytics_analytics.js",
|
|
14463
14516
|
aliases: null,
|
|
@@ -14469,7 +14522,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14469
14522
|
deprecationMessage: null,
|
|
14470
14523
|
removed: false,
|
|
14471
14524
|
removalMessage: null,
|
|
14472
|
-
isBlocking: false
|
|
14525
|
+
isBlocking: false,
|
|
14526
|
+
resourceTypes: ["script"]
|
|
14473
14527
|
}],
|
|
14474
14528
|
map: {
|
|
14475
14529
|
"1": 0,
|
|
@@ -14496,7 +14550,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14496
14550
|
deprecationMessage: null,
|
|
14497
14551
|
removed: false,
|
|
14498
14552
|
removalMessage: null,
|
|
14499
|
-
isBlocking: false
|
|
14553
|
+
isBlocking: false,
|
|
14554
|
+
resourceTypes: ["script"]
|
|
14500
14555
|
}],
|
|
14501
14556
|
map: {
|
|
14502
14557
|
"1024": 0,
|
|
@@ -14516,7 +14571,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14516
14571
|
deprecationMessage: null,
|
|
14517
14572
|
removed: false,
|
|
14518
14573
|
removalMessage: null,
|
|
14519
|
-
isBlocking: false
|
|
14574
|
+
isBlocking: false,
|
|
14575
|
+
resourceTypes: ["script"]
|
|
14520
14576
|
}],
|
|
14521
14577
|
map: {
|
|
14522
14578
|
"1024": 0,
|
|
@@ -14536,7 +14592,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14536
14592
|
deprecationMessage: null,
|
|
14537
14593
|
removed: false,
|
|
14538
14594
|
removalMessage: null,
|
|
14539
|
-
isBlocking: false
|
|
14595
|
+
isBlocking: false,
|
|
14596
|
+
resourceTypes: []
|
|
14540
14597
|
}, {
|
|
14541
14598
|
name: "google-ima.js",
|
|
14542
14599
|
aliases: null,
|
|
@@ -14548,7 +14605,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14548
14605
|
deprecationMessage: null,
|
|
14549
14606
|
removed: false,
|
|
14550
14607
|
removalMessage: null,
|
|
14551
|
-
isBlocking: false
|
|
14608
|
+
isBlocking: false,
|
|
14609
|
+
resourceTypes: ["script"]
|
|
14552
14610
|
}],
|
|
14553
14611
|
map: {
|
|
14554
14612
|
"1": 0,
|
|
@@ -14566,7 +14624,7 @@ const redirectsCompatibilityTableData = {
|
|
|
14566
14624
|
}, {
|
|
14567
14625
|
shared: [{
|
|
14568
14626
|
name: "googlesyndication-adsbygoogle",
|
|
14569
|
-
aliases: ["ubo-googlesyndication_adsbygoogle.js", "googlesyndication_adsbygoogle.js"],
|
|
14627
|
+
aliases: ["ubo-googlesyndication_adsbygoogle.js", "ubo-googlesyndication.com/adsbygoogle.js", "googlesyndication_adsbygoogle.js"],
|
|
14570
14628
|
description: "Mocks Google AdSense API.",
|
|
14571
14629
|
docs: null,
|
|
14572
14630
|
versionAdded: null,
|
|
@@ -14575,10 +14633,11 @@ const redirectsCompatibilityTableData = {
|
|
|
14575
14633
|
deprecationMessage: null,
|
|
14576
14634
|
removed: false,
|
|
14577
14635
|
removalMessage: null,
|
|
14578
|
-
isBlocking: false
|
|
14636
|
+
isBlocking: false,
|
|
14637
|
+
resourceTypes: []
|
|
14579
14638
|
}, {
|
|
14580
14639
|
name: "googlesyndication_adsbygoogle.js",
|
|
14581
|
-
aliases:
|
|
14640
|
+
aliases: ["googlesyndication.com/adsbygoogle.js"],
|
|
14582
14641
|
description: "Mocks Google AdSense API.",
|
|
14583
14642
|
docs: null,
|
|
14584
14643
|
versionAdded: null,
|
|
@@ -14587,7 +14646,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14587
14646
|
deprecationMessage: null,
|
|
14588
14647
|
removed: false,
|
|
14589
14648
|
removalMessage: null,
|
|
14590
|
-
isBlocking: false
|
|
14649
|
+
isBlocking: false,
|
|
14650
|
+
resourceTypes: ["script"]
|
|
14591
14651
|
}],
|
|
14592
14652
|
map: {
|
|
14593
14653
|
"1": 0,
|
|
@@ -14614,7 +14674,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14614
14674
|
deprecationMessage: null,
|
|
14615
14675
|
removed: false,
|
|
14616
14676
|
removalMessage: null,
|
|
14617
|
-
isBlocking: false
|
|
14677
|
+
isBlocking: false,
|
|
14678
|
+
resourceTypes: []
|
|
14618
14679
|
}, {
|
|
14619
14680
|
name: "googletagservices_gpt.js",
|
|
14620
14681
|
aliases: null,
|
|
@@ -14626,7 +14687,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14626
14687
|
deprecationMessage: null,
|
|
14627
14688
|
removed: false,
|
|
14628
14689
|
removalMessage: null,
|
|
14629
|
-
isBlocking: false
|
|
14690
|
+
isBlocking: false,
|
|
14691
|
+
resourceTypes: ["script"]
|
|
14630
14692
|
}],
|
|
14631
14693
|
map: {
|
|
14632
14694
|
"1": 0,
|
|
@@ -14653,7 +14715,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14653
14715
|
deprecationMessage: null,
|
|
14654
14716
|
removed: false,
|
|
14655
14717
|
removalMessage: null,
|
|
14656
|
-
isBlocking: false
|
|
14718
|
+
isBlocking: false,
|
|
14719
|
+
resourceTypes: ["script"]
|
|
14657
14720
|
}],
|
|
14658
14721
|
map: {
|
|
14659
14722
|
"1024": 0,
|
|
@@ -14673,7 +14736,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14673
14736
|
deprecationMessage: null,
|
|
14674
14737
|
removed: false,
|
|
14675
14738
|
removalMessage: null,
|
|
14676
|
-
isBlocking: false
|
|
14739
|
+
isBlocking: false,
|
|
14740
|
+
resourceTypes: []
|
|
14677
14741
|
}],
|
|
14678
14742
|
map: {
|
|
14679
14743
|
"1": 0,
|
|
@@ -14696,7 +14760,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14696
14760
|
deprecationMessage: null,
|
|
14697
14761
|
removed: false,
|
|
14698
14762
|
removalMessage: null,
|
|
14699
|
-
isBlocking: false
|
|
14763
|
+
isBlocking: false,
|
|
14764
|
+
resourceTypes: []
|
|
14700
14765
|
}],
|
|
14701
14766
|
map: {
|
|
14702
14767
|
"1": 0,
|
|
@@ -14719,7 +14784,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14719
14784
|
deprecationMessage: null,
|
|
14720
14785
|
removed: false,
|
|
14721
14786
|
removalMessage: null,
|
|
14722
|
-
isBlocking: false
|
|
14787
|
+
isBlocking: false,
|
|
14788
|
+
resourceTypes: []
|
|
14723
14789
|
}],
|
|
14724
14790
|
map: {
|
|
14725
14791
|
"1": 0,
|
|
@@ -14742,7 +14808,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14742
14808
|
deprecationMessage: null,
|
|
14743
14809
|
removed: false,
|
|
14744
14810
|
removalMessage: null,
|
|
14745
|
-
isBlocking: false
|
|
14811
|
+
isBlocking: false,
|
|
14812
|
+
resourceTypes: []
|
|
14746
14813
|
}],
|
|
14747
14814
|
map: {
|
|
14748
14815
|
"1": 0,
|
|
@@ -14765,7 +14832,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14765
14832
|
deprecationMessage: null,
|
|
14766
14833
|
removed: false,
|
|
14767
14834
|
removalMessage: null,
|
|
14768
|
-
isBlocking: false
|
|
14835
|
+
isBlocking: false,
|
|
14836
|
+
resourceTypes: []
|
|
14769
14837
|
}],
|
|
14770
14838
|
map: {
|
|
14771
14839
|
"1": 0,
|
|
@@ -14788,7 +14856,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14788
14856
|
deprecationMessage: null,
|
|
14789
14857
|
removed: false,
|
|
14790
14858
|
removalMessage: null,
|
|
14791
|
-
isBlocking: false
|
|
14859
|
+
isBlocking: false,
|
|
14860
|
+
resourceTypes: []
|
|
14792
14861
|
}],
|
|
14793
14862
|
map: {
|
|
14794
14863
|
"1": 0,
|
|
@@ -14811,7 +14880,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14811
14880
|
deprecationMessage: null,
|
|
14812
14881
|
removed: false,
|
|
14813
14882
|
removalMessage: null,
|
|
14814
|
-
isBlocking: false
|
|
14883
|
+
isBlocking: false,
|
|
14884
|
+
resourceTypes: []
|
|
14815
14885
|
}, {
|
|
14816
14886
|
name: "noeval.js",
|
|
14817
14887
|
aliases: null,
|
|
@@ -14823,7 +14893,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14823
14893
|
deprecationMessage: null,
|
|
14824
14894
|
removed: false,
|
|
14825
14895
|
removalMessage: null,
|
|
14826
|
-
isBlocking: false
|
|
14896
|
+
isBlocking: false,
|
|
14897
|
+
resourceTypes: ["script"]
|
|
14827
14898
|
}],
|
|
14828
14899
|
map: {
|
|
14829
14900
|
"1": 0,
|
|
@@ -14850,7 +14921,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14850
14921
|
deprecationMessage: null,
|
|
14851
14922
|
removed: false,
|
|
14852
14923
|
removalMessage: null,
|
|
14853
|
-
isBlocking: false
|
|
14924
|
+
isBlocking: false,
|
|
14925
|
+
resourceTypes: ["media"]
|
|
14854
14926
|
}],
|
|
14855
14927
|
map: {
|
|
14856
14928
|
"1024": 0,
|
|
@@ -14870,7 +14942,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14870
14942
|
deprecationMessage: null,
|
|
14871
14943
|
removed: false,
|
|
14872
14944
|
removalMessage: null,
|
|
14873
|
-
isBlocking: false
|
|
14945
|
+
isBlocking: false,
|
|
14946
|
+
resourceTypes: []
|
|
14874
14947
|
}, {
|
|
14875
14948
|
name: "noop.css",
|
|
14876
14949
|
aliases: null,
|
|
@@ -14882,7 +14955,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14882
14955
|
deprecationMessage: null,
|
|
14883
14956
|
removed: false,
|
|
14884
14957
|
removalMessage: null,
|
|
14885
|
-
isBlocking: false
|
|
14958
|
+
isBlocking: false,
|
|
14959
|
+
resourceTypes: ["stylesheet"]
|
|
14886
14960
|
}, {
|
|
14887
14961
|
name: "blank-css",
|
|
14888
14962
|
aliases: null,
|
|
@@ -14894,7 +14968,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14894
14968
|
deprecationMessage: null,
|
|
14895
14969
|
removed: false,
|
|
14896
14970
|
removalMessage: null,
|
|
14897
|
-
isBlocking: false
|
|
14971
|
+
isBlocking: false,
|
|
14972
|
+
resourceTypes: []
|
|
14898
14973
|
}],
|
|
14899
14974
|
map: {
|
|
14900
14975
|
"1": 0,
|
|
@@ -14925,7 +15000,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14925
15000
|
deprecationMessage: null,
|
|
14926
15001
|
removed: false,
|
|
14927
15002
|
removalMessage: null,
|
|
14928
|
-
isBlocking: false
|
|
15003
|
+
isBlocking: false,
|
|
15004
|
+
resourceTypes: []
|
|
14929
15005
|
}, {
|
|
14930
15006
|
name: "noop.html",
|
|
14931
15007
|
aliases: null,
|
|
@@ -14937,7 +15013,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14937
15013
|
deprecationMessage: null,
|
|
14938
15014
|
removed: false,
|
|
14939
15015
|
removalMessage: null,
|
|
14940
|
-
isBlocking: false
|
|
15016
|
+
isBlocking: false,
|
|
15017
|
+
resourceTypes: ["sub_frame"]
|
|
14941
15018
|
}, {
|
|
14942
15019
|
name: "blank-html",
|
|
14943
15020
|
aliases: null,
|
|
@@ -14949,7 +15026,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14949
15026
|
deprecationMessage: null,
|
|
14950
15027
|
removed: false,
|
|
14951
15028
|
removalMessage: null,
|
|
14952
|
-
isBlocking: false
|
|
15029
|
+
isBlocking: false,
|
|
15030
|
+
resourceTypes: []
|
|
14953
15031
|
}],
|
|
14954
15032
|
map: {
|
|
14955
15033
|
"1": 0,
|
|
@@ -14980,7 +15058,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14980
15058
|
deprecationMessage: null,
|
|
14981
15059
|
removed: false,
|
|
14982
15060
|
removalMessage: null,
|
|
14983
|
-
isBlocking: false
|
|
15061
|
+
isBlocking: false,
|
|
15062
|
+
resourceTypes: []
|
|
14984
15063
|
}, {
|
|
14985
15064
|
name: "noop.js",
|
|
14986
15065
|
aliases: null,
|
|
@@ -14992,7 +15071,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14992
15071
|
deprecationMessage: null,
|
|
14993
15072
|
removed: false,
|
|
14994
15073
|
removalMessage: null,
|
|
14995
|
-
isBlocking: false
|
|
15074
|
+
isBlocking: false,
|
|
15075
|
+
resourceTypes: ["script"]
|
|
14996
15076
|
}, {
|
|
14997
15077
|
name: "blank-js",
|
|
14998
15078
|
aliases: null,
|
|
@@ -15004,7 +15084,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15004
15084
|
deprecationMessage: null,
|
|
15005
15085
|
removed: false,
|
|
15006
15086
|
removalMessage: null,
|
|
15007
|
-
isBlocking: false
|
|
15087
|
+
isBlocking: false,
|
|
15088
|
+
resourceTypes: []
|
|
15008
15089
|
}],
|
|
15009
15090
|
map: {
|
|
15010
15091
|
"1": 0,
|
|
@@ -15035,7 +15116,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15035
15116
|
deprecationMessage: null,
|
|
15036
15117
|
removed: false,
|
|
15037
15118
|
removalMessage: null,
|
|
15038
|
-
isBlocking: false
|
|
15119
|
+
isBlocking: false,
|
|
15120
|
+
resourceTypes: []
|
|
15039
15121
|
}, {
|
|
15040
15122
|
name: "noop.json",
|
|
15041
15123
|
aliases: null,
|
|
@@ -15047,7 +15129,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15047
15129
|
deprecationMessage: null,
|
|
15048
15130
|
removed: false,
|
|
15049
15131
|
removalMessage: null,
|
|
15050
|
-
isBlocking: false
|
|
15132
|
+
isBlocking: false,
|
|
15133
|
+
resourceTypes: []
|
|
15051
15134
|
}],
|
|
15052
15135
|
map: {
|
|
15053
15136
|
"1": 0,
|
|
@@ -15074,7 +15157,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15074
15157
|
deprecationMessage: null,
|
|
15075
15158
|
removed: false,
|
|
15076
15159
|
removalMessage: null,
|
|
15077
|
-
isBlocking: false
|
|
15160
|
+
isBlocking: false,
|
|
15161
|
+
resourceTypes: []
|
|
15078
15162
|
}, {
|
|
15079
15163
|
name: "noop-0.1s.mp3",
|
|
15080
15164
|
aliases: null,
|
|
@@ -15086,7 +15170,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15086
15170
|
deprecationMessage: null,
|
|
15087
15171
|
removed: false,
|
|
15088
15172
|
removalMessage: null,
|
|
15089
|
-
isBlocking: false
|
|
15173
|
+
isBlocking: false,
|
|
15174
|
+
resourceTypes: ["media"]
|
|
15090
15175
|
}, {
|
|
15091
15176
|
name: "blank-mp3",
|
|
15092
15177
|
aliases: null,
|
|
@@ -15098,7 +15183,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15098
15183
|
deprecationMessage: null,
|
|
15099
15184
|
removed: false,
|
|
15100
15185
|
removalMessage: null,
|
|
15101
|
-
isBlocking: false
|
|
15186
|
+
isBlocking: false,
|
|
15187
|
+
resourceTypes: []
|
|
15102
15188
|
}],
|
|
15103
15189
|
map: {
|
|
15104
15190
|
"1": 0,
|
|
@@ -15129,7 +15215,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15129
15215
|
deprecationMessage: null,
|
|
15130
15216
|
removed: false,
|
|
15131
15217
|
removalMessage: null,
|
|
15132
|
-
isBlocking: false
|
|
15218
|
+
isBlocking: false,
|
|
15219
|
+
resourceTypes: []
|
|
15133
15220
|
}, {
|
|
15134
15221
|
name: "noop-1s.mp4",
|
|
15135
15222
|
aliases: null,
|
|
@@ -15141,7 +15228,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15141
15228
|
deprecationMessage: null,
|
|
15142
15229
|
removed: false,
|
|
15143
15230
|
removalMessage: null,
|
|
15144
|
-
isBlocking: false
|
|
15231
|
+
isBlocking: false,
|
|
15232
|
+
resourceTypes: ["media"]
|
|
15145
15233
|
}, {
|
|
15146
15234
|
name: "blank-mp4",
|
|
15147
15235
|
aliases: null,
|
|
@@ -15153,7 +15241,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15153
15241
|
deprecationMessage: null,
|
|
15154
15242
|
removed: false,
|
|
15155
15243
|
removalMessage: null,
|
|
15156
|
-
isBlocking: false
|
|
15244
|
+
isBlocking: false,
|
|
15245
|
+
resourceTypes: []
|
|
15157
15246
|
}],
|
|
15158
15247
|
map: {
|
|
15159
15248
|
"1": 0,
|
|
@@ -15184,7 +15273,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15184
15273
|
deprecationMessage: null,
|
|
15185
15274
|
removed: false,
|
|
15186
15275
|
removalMessage: null,
|
|
15187
|
-
isBlocking: false
|
|
15276
|
+
isBlocking: false,
|
|
15277
|
+
resourceTypes: []
|
|
15188
15278
|
}, {
|
|
15189
15279
|
name: "noop.txt",
|
|
15190
15280
|
aliases: null,
|
|
@@ -15196,7 +15286,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15196
15286
|
deprecationMessage: null,
|
|
15197
15287
|
removed: false,
|
|
15198
15288
|
removalMessage: null,
|
|
15199
|
-
isBlocking: false
|
|
15289
|
+
isBlocking: false,
|
|
15290
|
+
resourceTypes: ["image", "media", "sub_frame", "stylesheet", "script", "xmlhttprequest", "other"]
|
|
15200
15291
|
}, {
|
|
15201
15292
|
name: "blank-text",
|
|
15202
15293
|
aliases: null,
|
|
@@ -15208,7 +15299,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15208
15299
|
deprecationMessage: null,
|
|
15209
15300
|
removed: false,
|
|
15210
15301
|
removalMessage: null,
|
|
15211
|
-
isBlocking: false
|
|
15302
|
+
isBlocking: false,
|
|
15303
|
+
resourceTypes: []
|
|
15212
15304
|
}],
|
|
15213
15305
|
map: {
|
|
15214
15306
|
"1": 0,
|
|
@@ -15239,7 +15331,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15239
15331
|
deprecationMessage: null,
|
|
15240
15332
|
removed: false,
|
|
15241
15333
|
removalMessage: null,
|
|
15242
|
-
isBlocking: false
|
|
15334
|
+
isBlocking: false,
|
|
15335
|
+
resourceTypes: []
|
|
15243
15336
|
}],
|
|
15244
15337
|
map: {
|
|
15245
15338
|
"1": 0,
|
|
@@ -15262,7 +15355,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15262
15355
|
deprecationMessage: null,
|
|
15263
15356
|
removed: false,
|
|
15264
15357
|
removalMessage: null,
|
|
15265
|
-
isBlocking: false
|
|
15358
|
+
isBlocking: false,
|
|
15359
|
+
resourceTypes: []
|
|
15266
15360
|
}],
|
|
15267
15361
|
map: {
|
|
15268
15362
|
"1": 0,
|
|
@@ -15285,7 +15379,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15285
15379
|
deprecationMessage: null,
|
|
15286
15380
|
removed: false,
|
|
15287
15381
|
removalMessage: null,
|
|
15288
|
-
isBlocking: false
|
|
15382
|
+
isBlocking: false,
|
|
15383
|
+
resourceTypes: []
|
|
15289
15384
|
}],
|
|
15290
15385
|
map: {
|
|
15291
15386
|
"1": 0,
|
|
@@ -15308,7 +15403,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15308
15403
|
deprecationMessage: null,
|
|
15309
15404
|
removed: false,
|
|
15310
15405
|
removalMessage: null,
|
|
15311
|
-
isBlocking: false
|
|
15406
|
+
isBlocking: false,
|
|
15407
|
+
resourceTypes: []
|
|
15312
15408
|
}, {
|
|
15313
15409
|
name: "noop-vmap1.0.xml",
|
|
15314
15410
|
aliases: null,
|
|
@@ -15320,7 +15416,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15320
15416
|
deprecationMessage: null,
|
|
15321
15417
|
removed: false,
|
|
15322
15418
|
removalMessage: null,
|
|
15323
|
-
isBlocking: false
|
|
15419
|
+
isBlocking: false,
|
|
15420
|
+
resourceTypes: ["media"]
|
|
15324
15421
|
}],
|
|
15325
15422
|
map: {
|
|
15326
15423
|
"1": 0,
|
|
@@ -15347,7 +15444,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15347
15444
|
deprecationMessage: null,
|
|
15348
15445
|
removed: false,
|
|
15349
15446
|
removalMessage: null,
|
|
15350
|
-
isBlocking: false
|
|
15447
|
+
isBlocking: false,
|
|
15448
|
+
resourceTypes: []
|
|
15351
15449
|
}, {
|
|
15352
15450
|
name: "nowebrtc.js",
|
|
15353
15451
|
aliases: null,
|
|
@@ -15359,7 +15457,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15359
15457
|
deprecationMessage: null,
|
|
15360
15458
|
removed: false,
|
|
15361
15459
|
removalMessage: null,
|
|
15362
|
-
isBlocking: false
|
|
15460
|
+
isBlocking: false,
|
|
15461
|
+
resourceTypes: ["other"]
|
|
15363
15462
|
}],
|
|
15364
15463
|
map: {
|
|
15365
15464
|
"1": 0,
|
|
@@ -15386,7 +15485,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15386
15485
|
deprecationMessage: null,
|
|
15387
15486
|
removed: false,
|
|
15388
15487
|
removalMessage: null,
|
|
15389
|
-
isBlocking: false
|
|
15488
|
+
isBlocking: false,
|
|
15489
|
+
resourceTypes: ["script"]
|
|
15390
15490
|
}],
|
|
15391
15491
|
map: {
|
|
15392
15492
|
"1024": 0,
|
|
@@ -15406,7 +15506,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15406
15506
|
deprecationMessage: null,
|
|
15407
15507
|
removed: false,
|
|
15408
15508
|
removalMessage: null,
|
|
15409
|
-
isBlocking: false
|
|
15509
|
+
isBlocking: false,
|
|
15510
|
+
resourceTypes: []
|
|
15410
15511
|
}],
|
|
15411
15512
|
map: {
|
|
15412
15513
|
"1": 0,
|
|
@@ -15429,7 +15530,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15429
15530
|
deprecationMessage: null,
|
|
15430
15531
|
removed: false,
|
|
15431
15532
|
removalMessage: null,
|
|
15432
|
-
isBlocking: false
|
|
15533
|
+
isBlocking: false,
|
|
15534
|
+
resourceTypes: []
|
|
15433
15535
|
}, {
|
|
15434
15536
|
name: "prebid-ads.js",
|
|
15435
15537
|
aliases: null,
|
|
@@ -15441,7 +15543,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15441
15543
|
deprecationMessage: null,
|
|
15442
15544
|
removed: false,
|
|
15443
15545
|
removalMessage: null,
|
|
15444
|
-
isBlocking: false
|
|
15546
|
+
isBlocking: false,
|
|
15547
|
+
resourceTypes: ["script"]
|
|
15445
15548
|
}],
|
|
15446
15549
|
map: {
|
|
15447
15550
|
"1": 0,
|
|
@@ -15468,7 +15571,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15468
15571
|
deprecationMessage: null,
|
|
15469
15572
|
removed: false,
|
|
15470
15573
|
removalMessage: null,
|
|
15471
|
-
isBlocking: false
|
|
15574
|
+
isBlocking: false,
|
|
15575
|
+
resourceTypes: []
|
|
15472
15576
|
}],
|
|
15473
15577
|
map: {
|
|
15474
15578
|
"1": 0,
|
|
@@ -15491,7 +15595,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15491
15595
|
deprecationMessage: null,
|
|
15492
15596
|
removed: false,
|
|
15493
15597
|
removalMessage: null,
|
|
15494
|
-
isBlocking: false
|
|
15598
|
+
isBlocking: false,
|
|
15599
|
+
resourceTypes: []
|
|
15495
15600
|
}, {
|
|
15496
15601
|
name: "nobab.js",
|
|
15497
15602
|
aliases: null,
|
|
@@ -15503,7 +15608,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15503
15608
|
deprecationMessage: null,
|
|
15504
15609
|
removed: false,
|
|
15505
15610
|
removalMessage: null,
|
|
15506
|
-
isBlocking: false
|
|
15611
|
+
isBlocking: false,
|
|
15612
|
+
resourceTypes: ["script"]
|
|
15507
15613
|
}],
|
|
15508
15614
|
map: {
|
|
15509
15615
|
"1": 0,
|
|
@@ -15530,7 +15636,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15530
15636
|
deprecationMessage: null,
|
|
15531
15637
|
removed: false,
|
|
15532
15638
|
removalMessage: null,
|
|
15533
|
-
isBlocking: false
|
|
15639
|
+
isBlocking: false,
|
|
15640
|
+
resourceTypes: []
|
|
15534
15641
|
}, {
|
|
15535
15642
|
name: "nobab2.js",
|
|
15536
15643
|
aliases: null,
|
|
@@ -15542,7 +15649,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15542
15649
|
deprecationMessage: null,
|
|
15543
15650
|
removed: false,
|
|
15544
15651
|
removalMessage: null,
|
|
15545
|
-
isBlocking: false
|
|
15652
|
+
isBlocking: false,
|
|
15653
|
+
resourceTypes: ["script"]
|
|
15546
15654
|
}],
|
|
15547
15655
|
map: {
|
|
15548
15656
|
"1": 0,
|
|
@@ -15569,10 +15677,11 @@ const redirectsCompatibilityTableData = {
|
|
|
15569
15677
|
deprecationMessage: null,
|
|
15570
15678
|
removed: false,
|
|
15571
15679
|
removalMessage: null,
|
|
15572
|
-
isBlocking: false
|
|
15680
|
+
isBlocking: false,
|
|
15681
|
+
resourceTypes: []
|
|
15573
15682
|
}, {
|
|
15574
15683
|
name: "nofab.js",
|
|
15575
|
-
aliases:
|
|
15684
|
+
aliases: ["fuckadblock.js-3.2.0", "fuckadblock.js-3.2.0.js"],
|
|
15576
15685
|
description: "Mocks FAB script v3.2.0.",
|
|
15577
15686
|
docs: null,
|
|
15578
15687
|
versionAdded: null,
|
|
@@ -15581,7 +15690,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15581
15690
|
deprecationMessage: null,
|
|
15582
15691
|
removed: false,
|
|
15583
15692
|
removalMessage: null,
|
|
15584
|
-
isBlocking: false
|
|
15693
|
+
isBlocking: false,
|
|
15694
|
+
resourceTypes: ["script"]
|
|
15585
15695
|
}],
|
|
15586
15696
|
map: {
|
|
15587
15697
|
"1": 0,
|
|
@@ -15608,7 +15718,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15608
15718
|
deprecationMessage: null,
|
|
15609
15719
|
removed: false,
|
|
15610
15720
|
removalMessage: null,
|
|
15611
|
-
isBlocking: false
|
|
15721
|
+
isBlocking: false,
|
|
15722
|
+
resourceTypes: []
|
|
15612
15723
|
}, {
|
|
15613
15724
|
name: "popads.js",
|
|
15614
15725
|
aliases: null,
|
|
@@ -15620,7 +15731,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15620
15731
|
deprecationMessage: null,
|
|
15621
15732
|
removed: false,
|
|
15622
15733
|
removalMessage: null,
|
|
15623
|
-
isBlocking: false
|
|
15734
|
+
isBlocking: false,
|
|
15735
|
+
resourceTypes: ["script"]
|
|
15624
15736
|
}],
|
|
15625
15737
|
map: {
|
|
15626
15738
|
"1": 0,
|
|
@@ -15647,7 +15759,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15647
15759
|
deprecationMessage: null,
|
|
15648
15760
|
removed: false,
|
|
15649
15761
|
removalMessage: null,
|
|
15650
|
-
isBlocking: false
|
|
15762
|
+
isBlocking: false,
|
|
15763
|
+
resourceTypes: []
|
|
15651
15764
|
}, {
|
|
15652
15765
|
name: "scorecardresearch_beacon.js",
|
|
15653
15766
|
aliases: null,
|
|
@@ -15659,7 +15772,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15659
15772
|
deprecationMessage: null,
|
|
15660
15773
|
removed: false,
|
|
15661
15774
|
removalMessage: null,
|
|
15662
|
-
isBlocking: false
|
|
15775
|
+
isBlocking: false,
|
|
15776
|
+
resourceTypes: ["script"]
|
|
15663
15777
|
}],
|
|
15664
15778
|
map: {
|
|
15665
15779
|
"1": 0,
|
|
@@ -15686,7 +15800,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15686
15800
|
deprecationMessage: null,
|
|
15687
15801
|
removed: false,
|
|
15688
15802
|
removalMessage: null,
|
|
15689
|
-
isBlocking: false
|
|
15803
|
+
isBlocking: false,
|
|
15804
|
+
resourceTypes: []
|
|
15690
15805
|
}, {
|
|
15691
15806
|
name: "popads-dummy.js",
|
|
15692
15807
|
aliases: null,
|
|
@@ -15698,7 +15813,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15698
15813
|
deprecationMessage: null,
|
|
15699
15814
|
removed: false,
|
|
15700
15815
|
removalMessage: null,
|
|
15701
|
-
isBlocking: false
|
|
15816
|
+
isBlocking: false,
|
|
15817
|
+
resourceTypes: ["script"]
|
|
15702
15818
|
}],
|
|
15703
15819
|
map: {
|
|
15704
15820
|
"1": 0,
|
|
@@ -15759,7 +15875,9 @@ const redirectsCompatibilityTableData = {
|
|
|
15759
15875
|
"google-ima.js": 19,
|
|
15760
15876
|
"googlesyndication-adsbygoogle": 20,
|
|
15761
15877
|
"ubo-googlesyndication_adsbygoogle.js": 20,
|
|
15878
|
+
"ubo-googlesyndication.com/adsbygoogle.js": 20,
|
|
15762
15879
|
"googlesyndication_adsbygoogle.js": 20,
|
|
15880
|
+
"googlesyndication.com/adsbygoogle.js": 20,
|
|
15763
15881
|
"googletagservices-gpt": 21,
|
|
15764
15882
|
"ubo-googletagservices_gpt.js": 21,
|
|
15765
15883
|
"googletagservices_gpt.js": 21,
|
|
@@ -15818,6 +15936,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15818
15936
|
"nobab2.js": 48,
|
|
15819
15937
|
"prevent-fab-3.2.0": 49,
|
|
15820
15938
|
"nofab.js": 49,
|
|
15939
|
+
"fuckadblock.js-3.2.0": 49,
|
|
15940
|
+
"fuckadblock.js-3.2.0.js": 49,
|
|
15821
15941
|
"prevent-popads-net": 50,
|
|
15822
15942
|
"popads.js": 50,
|
|
15823
15943
|
"scorecardresearch-beacon": 51,
|
|
@@ -18525,7 +18645,7 @@ const scriptletsCompatibilityTableData = {
|
|
|
18525
18645
|
}]
|
|
18526
18646
|
}, {
|
|
18527
18647
|
name: "remove-attr.js",
|
|
18528
|
-
aliases: ["ra.js"],
|
|
18648
|
+
aliases: ["ra.js", "ra", "remove-attr"],
|
|
18529
18649
|
description: null,
|
|
18530
18650
|
docs: "https://github.com/gorhill/uBlock/wiki/Resources-Library#remove-attrjs-",
|
|
18531
18651
|
versionAdded: null,
|
|
@@ -18606,7 +18726,7 @@ const scriptletsCompatibilityTableData = {
|
|
|
18606
18726
|
}]
|
|
18607
18727
|
}, {
|
|
18608
18728
|
name: "remove-class.js",
|
|
18609
|
-
aliases: ["rc.js"],
|
|
18729
|
+
aliases: ["rc.js", "rc", "remove-class"],
|
|
18610
18730
|
description: null,
|
|
18611
18731
|
docs: "https://github.com/gorhill/uBlock/wiki/Resources-Library#remove-classjs-",
|
|
18612
18732
|
versionAdded: null,
|
|
@@ -20463,6 +20583,7 @@ const scriptletsCompatibilityTableData = {
|
|
|
20463
20583
|
"ubo-ra.js": 59,
|
|
20464
20584
|
"ubo-remove-attr": 59,
|
|
20465
20585
|
"ubo-ra": 59,
|
|
20586
|
+
ra: 59,
|
|
20466
20587
|
"remove-class": 60,
|
|
20467
20588
|
"remove-class.js": 60,
|
|
20468
20589
|
"ubo-remove-class.js": 60,
|
|
@@ -20470,6 +20591,7 @@ const scriptletsCompatibilityTableData = {
|
|
|
20470
20591
|
"ubo-rc.js": 60,
|
|
20471
20592
|
"ubo-remove-class": 60,
|
|
20472
20593
|
"ubo-rc": 60,
|
|
20594
|
+
rc: 60,
|
|
20473
20595
|
"remove-cookie": 61,
|
|
20474
20596
|
"cookie-remover.js": 61,
|
|
20475
20597
|
"ubo-cookie-remover.js": 61,
|
|
@@ -21527,6 +21649,44 @@ function getScriptletName(scriptletNode) {
|
|
|
21527
21649
|
}
|
|
21528
21650
|
return scriptletNode.children[0]?.value ?? EMPTY;
|
|
21529
21651
|
}
|
|
21652
|
+
/**
|
|
21653
|
+
* Transform the nth argument of the scriptlet node
|
|
21654
|
+
*
|
|
21655
|
+
* @param scriptletNode Scriptlet node to transform argument of
|
|
21656
|
+
* @param index Index of the argument to transform (index 0 is the scriptlet name)
|
|
21657
|
+
* @param transform Function to transform the argument
|
|
21658
|
+
*/
|
|
21659
|
+
function transformNthScriptletArgument(scriptletNode, index, transform) {
|
|
21660
|
+
const child = scriptletNode.children[index];
|
|
21661
|
+
if (!isUndefined(child)) {
|
|
21662
|
+
const transformed = transform(child?.value ?? null);
|
|
21663
|
+
if (isNull(transformed)) {
|
|
21664
|
+
// eslint-disable-next-line no-param-reassign
|
|
21665
|
+
scriptletNode.children[index] = null;
|
|
21666
|
+
return;
|
|
21667
|
+
}
|
|
21668
|
+
if (isNull(child)) {
|
|
21669
|
+
// eslint-disable-next-line no-param-reassign
|
|
21670
|
+
scriptletNode.children[index] = {
|
|
21671
|
+
type: 'Value',
|
|
21672
|
+
value: transformed
|
|
21673
|
+
};
|
|
21674
|
+
return;
|
|
21675
|
+
}
|
|
21676
|
+
child.value = transformed;
|
|
21677
|
+
}
|
|
21678
|
+
}
|
|
21679
|
+
/**
|
|
21680
|
+
* Transform all arguments of the scriptlet node
|
|
21681
|
+
*
|
|
21682
|
+
* @param scriptletNode Scriptlet node to transform arguments of
|
|
21683
|
+
* @param transform Function to transform the arguments
|
|
21684
|
+
*/
|
|
21685
|
+
function transformAllScriptletArguments(scriptletNode, transform) {
|
|
21686
|
+
for (let i = 0; i < scriptletNode.children.length; i += 1) {
|
|
21687
|
+
transformNthScriptletArgument(scriptletNode, i, transform);
|
|
21688
|
+
}
|
|
21689
|
+
}
|
|
21530
21690
|
/**
|
|
21531
21691
|
* Set name of the scriptlet.
|
|
21532
21692
|
* Modifies input `scriptletNode` if needed.
|
|
@@ -21535,10 +21695,7 @@ function getScriptletName(scriptletNode) {
|
|
|
21535
21695
|
* @param name Name to set
|
|
21536
21696
|
*/
|
|
21537
21697
|
function setScriptletName(scriptletNode, name) {
|
|
21538
|
-
|
|
21539
|
-
// eslint-disable-next-line no-param-reassign
|
|
21540
|
-
scriptletNode.children[0].value = name;
|
|
21541
|
-
}
|
|
21698
|
+
transformNthScriptletArgument(scriptletNode, 0, () => name);
|
|
21542
21699
|
}
|
|
21543
21700
|
/**
|
|
21544
21701
|
* Set quote type of the scriptlet parameters
|
|
@@ -21547,1422 +21704,1761 @@ function setScriptletName(scriptletNode, name) {
|
|
|
21547
21704
|
* @param quoteType Preferred quote type
|
|
21548
21705
|
*/
|
|
21549
21706
|
function setScriptletQuoteType(scriptletNode, quoteType) {
|
|
21550
|
-
|
|
21551
|
-
|
|
21552
|
-
|
|
21553
|
-
if (isNull(child)) {
|
|
21554
|
-
continue;
|
|
21555
|
-
}
|
|
21556
|
-
// eslint-disable-next-line no-param-reassign
|
|
21557
|
-
child.value = QuoteUtils.setStringQuoteType(child.value, quoteType);
|
|
21558
|
-
}
|
|
21559
|
-
}
|
|
21707
|
+
// null is a special value that means "no value", but we can't change its quote type,
|
|
21708
|
+
// so we need to convert it to empty string
|
|
21709
|
+
transformAllScriptletArguments(scriptletNode, value => QuoteUtils.setStringQuoteType(value ?? EMPTY, quoteType));
|
|
21560
21710
|
}
|
|
21561
21711
|
|
|
21562
21712
|
/**
|
|
21563
|
-
* @file
|
|
21713
|
+
* @file Resource type schema.
|
|
21564
21714
|
*/
|
|
21565
|
-
const ABP_SCRIPTLET_PREFIX = 'abp-';
|
|
21566
|
-
const UBO_SCRIPTLET_PREFIX = 'ubo-';
|
|
21567
21715
|
/**
|
|
21568
|
-
*
|
|
21716
|
+
* Resource type.
|
|
21569
21717
|
*
|
|
21570
|
-
* @
|
|
21718
|
+
* @see {@link https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#type-ResourceType}
|
|
21571
21719
|
*/
|
|
21572
|
-
|
|
21573
|
-
|
|
21574
|
-
|
|
21575
|
-
|
|
21576
|
-
|
|
21577
|
-
|
|
21578
|
-
|
|
21579
|
-
|
|
21580
|
-
|
|
21581
|
-
|
|
21582
|
-
|
|
21583
|
-
|
|
21584
|
-
|
|
21585
|
-
|
|
21586
|
-
|
|
21587
|
-
const separator = rule.separator.value;
|
|
21588
|
-
let convertedSeparator = separator;
|
|
21589
|
-
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.AdgJsInjectionException : exports.CosmeticRuleSeparator.AdgJsInjection;
|
|
21590
|
-
const convertedScriptlets = [];
|
|
21591
|
-
for (const scriptlet of rule.body.children) {
|
|
21592
|
-
// Clone the node to avoid any side effects
|
|
21593
|
-
const scriptletClone = cloneScriptletRuleNode(scriptlet);
|
|
21594
|
-
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
21595
|
-
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletClone), exports.QuoteType.None);
|
|
21596
|
-
// Add prefix if it's not already there
|
|
21597
|
-
let prefix;
|
|
21598
|
-
switch (rule.syntax) {
|
|
21599
|
-
case exports.AdblockSyntax.Abp:
|
|
21600
|
-
prefix = ABP_SCRIPTLET_PREFIX;
|
|
21601
|
-
break;
|
|
21602
|
-
case exports.AdblockSyntax.Ubo:
|
|
21603
|
-
prefix = UBO_SCRIPTLET_PREFIX;
|
|
21604
|
-
break;
|
|
21605
|
-
default:
|
|
21606
|
-
prefix = EMPTY;
|
|
21607
|
-
}
|
|
21608
|
-
if (!scriptletName.startsWith(prefix)) {
|
|
21609
|
-
setScriptletName(scriptletClone, `${prefix}${scriptletName}`);
|
|
21610
|
-
}
|
|
21611
|
-
// ADG scriptlet parameters should be quoted, and single quoted are preferred
|
|
21612
|
-
setScriptletQuoteType(scriptletClone, exports.QuoteType.Single);
|
|
21613
|
-
convertedScriptlets.push(scriptletClone);
|
|
21614
|
-
}
|
|
21615
|
-
return createNodeConversionResult(convertedScriptlets.map(scriptlet => {
|
|
21616
|
-
const res = {
|
|
21617
|
-
category: rule.category,
|
|
21618
|
-
type: rule.type,
|
|
21619
|
-
syntax: exports.AdblockSyntax.Adg,
|
|
21620
|
-
exception: rule.exception,
|
|
21621
|
-
domains: cloneDomainListNode(rule.domains),
|
|
21622
|
-
separator: {
|
|
21623
|
-
type: 'Value',
|
|
21624
|
-
value: convertedSeparator
|
|
21625
|
-
},
|
|
21626
|
-
body: {
|
|
21627
|
-
type: rule.body.type,
|
|
21628
|
-
children: [scriptlet]
|
|
21629
|
-
}
|
|
21630
|
-
};
|
|
21631
|
-
if (rule.modifiers) {
|
|
21632
|
-
res.modifiers = cloneModifierListNode(rule.modifiers);
|
|
21633
|
-
}
|
|
21634
|
-
return res;
|
|
21635
|
-
}), true);
|
|
21636
|
-
}
|
|
21637
|
-
}
|
|
21638
|
-
|
|
21720
|
+
exports.ResourceType = void 0;
|
|
21721
|
+
(function (ResourceType) {
|
|
21722
|
+
ResourceType["MainFrame"] = "main_frame";
|
|
21723
|
+
ResourceType["SubFrame"] = "sub_frame";
|
|
21724
|
+
ResourceType["Stylesheet"] = "stylesheet";
|
|
21725
|
+
ResourceType["Script"] = "script";
|
|
21726
|
+
ResourceType["Image"] = "image";
|
|
21727
|
+
ResourceType["Font"] = "font";
|
|
21728
|
+
ResourceType["Object"] = "object";
|
|
21729
|
+
ResourceType["XmlHttpRequest"] = "xmlhttprequest";
|
|
21730
|
+
ResourceType["Ping"] = "ping";
|
|
21731
|
+
ResourceType["Media"] = "media";
|
|
21732
|
+
ResourceType["WebSocket"] = "websocket";
|
|
21733
|
+
ResourceType["Other"] = "other";
|
|
21734
|
+
})(exports.ResourceType || (exports.ResourceType = {}));
|
|
21639
21735
|
/**
|
|
21640
|
-
*
|
|
21736
|
+
* Resource type schema.
|
|
21641
21737
|
*/
|
|
21738
|
+
const resourceTypeSchema = zod.nativeEnum(exports.ResourceType);
|
|
21739
|
+
|
|
21642
21740
|
/**
|
|
21643
|
-
*
|
|
21741
|
+
* Map of resource types to their corresponding adblock modifier names.
|
|
21742
|
+
*
|
|
21743
|
+
* @note Record type is used to ensure that all resource types are present in the map.
|
|
21744
|
+
*/
|
|
21745
|
+
const RESOURCE_TYPE_MODIFIER_MAP = Object.freeze({
|
|
21746
|
+
[exports.ResourceType.MainFrame]: 'document',
|
|
21747
|
+
[exports.ResourceType.SubFrame]: 'subdocument',
|
|
21748
|
+
[exports.ResourceType.Stylesheet]: 'stylesheet',
|
|
21749
|
+
[exports.ResourceType.Script]: 'script',
|
|
21750
|
+
[exports.ResourceType.Image]: 'image',
|
|
21751
|
+
[exports.ResourceType.Font]: 'font',
|
|
21752
|
+
[exports.ResourceType.Object]: 'object',
|
|
21753
|
+
[exports.ResourceType.XmlHttpRequest]: 'xmlhttprequest',
|
|
21754
|
+
[exports.ResourceType.Ping]: 'ping',
|
|
21755
|
+
[exports.ResourceType.Media]: 'media',
|
|
21756
|
+
[exports.ResourceType.WebSocket]: 'websocket',
|
|
21757
|
+
[exports.ResourceType.Other]: 'other'
|
|
21758
|
+
});
|
|
21759
|
+
/**
|
|
21760
|
+
* Gets the adblock modifier name for the given resource type.
|
|
21644
21761
|
*
|
|
21645
|
-
* @param
|
|
21646
|
-
* @param
|
|
21647
|
-
*
|
|
21648
|
-
* @returns
|
|
21762
|
+
* @param resourceType Resource type to get the modifier name for.
|
|
21763
|
+
* @param platform Platform to get the modifier for.
|
|
21764
|
+
*
|
|
21765
|
+
* @returns A string containing the adblock modifier name for the given resource type
|
|
21766
|
+
* or `null` if the modifier could not be found.
|
|
21649
21767
|
*/
|
|
21650
|
-
|
|
21651
|
-
const
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
name: {
|
|
21655
|
-
type: 'Value',
|
|
21656
|
-
value: name
|
|
21657
|
-
}
|
|
21658
|
-
};
|
|
21659
|
-
if (!isUndefined(value)) {
|
|
21660
|
-
result.value = {
|
|
21661
|
-
type: 'Value',
|
|
21662
|
-
value
|
|
21663
|
-
};
|
|
21768
|
+
const getResourceTypeModifier = (resourceType, platform) => {
|
|
21769
|
+
const modifierName = RESOURCE_TYPE_MODIFIER_MAP[resourceType];
|
|
21770
|
+
if (!modifierName) {
|
|
21771
|
+
return null;
|
|
21664
21772
|
}
|
|
21665
|
-
|
|
21666
|
-
|
|
21773
|
+
const modifierData = modifiersCompatibilityTable.getFirst(modifierName, platform);
|
|
21774
|
+
if (isNull(modifierData)) {
|
|
21775
|
+
return null;
|
|
21776
|
+
}
|
|
21777
|
+
return modifierData.name;
|
|
21778
|
+
};
|
|
21667
21779
|
/**
|
|
21668
|
-
*
|
|
21780
|
+
* Checks if the given resource type is valid.
|
|
21669
21781
|
*
|
|
21670
|
-
* @param
|
|
21671
|
-
*
|
|
21782
|
+
* @param resourceType Resource type to check.
|
|
21783
|
+
*
|
|
21784
|
+
* @returns `true` if the resource type is valid, `false` otherwise.
|
|
21672
21785
|
*/
|
|
21673
|
-
|
|
21674
|
-
|
|
21675
|
-
|
|
21676
|
-
// We need to clone the modifiers to avoid side effects
|
|
21677
|
-
children: modifiers.length ? clone(modifiers) : []
|
|
21678
|
-
};
|
|
21679
|
-
return result;
|
|
21680
|
-
}
|
|
21786
|
+
const isValidResourceType = resourceType => {
|
|
21787
|
+
return Object.values(exports.ResourceType).includes(resourceType);
|
|
21788
|
+
};
|
|
21681
21789
|
|
|
21682
21790
|
/**
|
|
21683
|
-
*
|
|
21684
|
-
|
|
21791
|
+
* @file Compatibility tables for redirects.
|
|
21792
|
+
*/
|
|
21793
|
+
/**
|
|
21794
|
+
* Prefix for resource redirection names.
|
|
21795
|
+
*/
|
|
21796
|
+
const ABP_RESOURCE_PREFIX = 'abp-resource:';
|
|
21797
|
+
const ABP_RESOURCE_PREFIX_LENGTH = ABP_RESOURCE_PREFIX.length;
|
|
21798
|
+
/**
|
|
21799
|
+
* Normalizes the redirect name.
|
|
21685
21800
|
*
|
|
21686
|
-
* @
|
|
21801
|
+
* @param name Redirect name to normalize.
|
|
21802
|
+
*
|
|
21803
|
+
* @returns Normalized redirect name.
|
|
21804
|
+
*
|
|
21805
|
+
* @example
|
|
21806
|
+
* redirectNameNormalizer('abp-resource:my-resource') // => 'my-resource'
|
|
21807
|
+
* redirectNameNormalizer('noop.js:99') // => 'noop.js'
|
|
21687
21808
|
*/
|
|
21688
|
-
|
|
21809
|
+
const redirectNameNormalizer = name => {
|
|
21810
|
+
// Remove ABP resource prefix, if present
|
|
21811
|
+
if (name.startsWith(ABP_RESOURCE_PREFIX)) {
|
|
21812
|
+
return name.slice(ABP_RESOURCE_PREFIX_LENGTH);
|
|
21813
|
+
}
|
|
21814
|
+
// Remove :[integer] priority suffix from the name, if present
|
|
21815
|
+
// See:
|
|
21816
|
+
// - https://github.com/AdguardTeam/tsurlfilter/issues/59
|
|
21817
|
+
// - https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#redirect
|
|
21818
|
+
const colonIndex = name.lastIndexOf(COLON);
|
|
21819
|
+
if (colonIndex !== -1 && /^\d+$/.test(name.slice(colonIndex + 1))) {
|
|
21820
|
+
return name.slice(0, colonIndex);
|
|
21821
|
+
}
|
|
21822
|
+
return name;
|
|
21823
|
+
};
|
|
21824
|
+
/**
|
|
21825
|
+
* Compatibility table for redirects.
|
|
21826
|
+
*/
|
|
21827
|
+
class RedirectsCompatibilityTable extends CompatibilityTableBase {
|
|
21689
21828
|
/**
|
|
21690
|
-
*
|
|
21691
|
-
* otherwise a new array will be created for the key.
|
|
21829
|
+
* Creates a new instance of the compatibility table for redirects.
|
|
21692
21830
|
*
|
|
21693
|
-
* @param
|
|
21694
|
-
* @param values Value(s) to add
|
|
21831
|
+
* @param data Compatibility table data.
|
|
21695
21832
|
*/
|
|
21696
|
-
|
|
21697
|
-
|
|
21698
|
-
|
|
21699
|
-
|
|
21700
|
-
|
|
21833
|
+
constructor(data) {
|
|
21834
|
+
super(data, redirectNameNormalizer);
|
|
21835
|
+
}
|
|
21836
|
+
/**
|
|
21837
|
+
* Gets the resource type adblock modifiers for the redirect for the given platform
|
|
21838
|
+
* based on the `resourceTypes` field.
|
|
21839
|
+
*
|
|
21840
|
+
* @param redirect Redirect name or redirect data.
|
|
21841
|
+
* @param platform Platform to get the modifiers for.
|
|
21842
|
+
*
|
|
21843
|
+
* @returns Set of resource type modifiers or an empty set if the redirect is not found or has no resource types.
|
|
21844
|
+
*/
|
|
21845
|
+
getResourceTypeModifiers(redirect, platform) {
|
|
21846
|
+
let redirectData = null;
|
|
21847
|
+
if (isString(redirect)) {
|
|
21848
|
+
redirectData = this.getFirst(redirect, platform);
|
|
21849
|
+
} else {
|
|
21850
|
+
redirectData = redirect;
|
|
21701
21851
|
}
|
|
21702
|
-
|
|
21852
|
+
const modifierNames = new Set();
|
|
21853
|
+
if (isNull(redirectData) || isUndefined(redirectData.resourceTypes)) {
|
|
21854
|
+
return modifierNames;
|
|
21855
|
+
}
|
|
21856
|
+
for (const resourceType of redirectData.resourceTypes) {
|
|
21857
|
+
const modifierName = getResourceTypeModifier(resourceType, platform);
|
|
21858
|
+
if (isNull(modifierName)) {
|
|
21859
|
+
continue;
|
|
21860
|
+
}
|
|
21861
|
+
modifierNames.add(modifierName);
|
|
21862
|
+
}
|
|
21863
|
+
return modifierNames;
|
|
21703
21864
|
}
|
|
21704
21865
|
}
|
|
21866
|
+
/**
|
|
21867
|
+
* Deep freeze the compatibility table data to avoid accidental modifications.
|
|
21868
|
+
*/
|
|
21869
|
+
deepFreeze(redirectsCompatibilityTableData);
|
|
21870
|
+
/**
|
|
21871
|
+
* Compatibility table instance for redirects.
|
|
21872
|
+
*/
|
|
21873
|
+
const redirectsCompatibilityTable = new RedirectsCompatibilityTable(redirectsCompatibilityTableData);
|
|
21705
21874
|
|
|
21706
21875
|
/**
|
|
21707
|
-
* @file
|
|
21876
|
+
* @file Compatibility tables for scriptlets.
|
|
21708
21877
|
*/
|
|
21709
|
-
const UBO_MATCHES_PATH_OPERATOR = 'matches-path';
|
|
21710
|
-
const ADG_PATH_MODIFIER = 'path';
|
|
21711
21878
|
/**
|
|
21712
|
-
*
|
|
21879
|
+
* Compatibility table for scriptlets.
|
|
21713
21880
|
*/
|
|
21714
|
-
|
|
21881
|
+
class ScriptletsCompatibilityTable extends CompatibilityTableBase {}
|
|
21715
21882
|
/**
|
|
21716
|
-
*
|
|
21883
|
+
* Deep freeze the compatibility table data to avoid accidental modifications.
|
|
21717
21884
|
*/
|
|
21718
|
-
|
|
21719
|
-
/**
|
|
21720
|
-
* Converts a uBO cosmetic rule modifier list to ADG, if possible.
|
|
21721
|
-
*
|
|
21722
|
-
* @param modifierList Cosmetic rule modifier list node to convert
|
|
21723
|
-
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
21724
|
-
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
21725
|
-
* If the node was not converted, the result will contain the original node with the same object reference
|
|
21726
|
-
* @throws If the modifier list cannot be converted
|
|
21727
|
-
* @see {@link https://github.com/gorhill/uBlock/wiki/Procedural-cosmetic-filters#cosmetic-filter-operators}
|
|
21728
|
-
*/
|
|
21729
|
-
static convertFromUbo(modifierList) {
|
|
21730
|
-
const conversionMap = new MultiValueMap();
|
|
21731
|
-
modifierList.children.forEach((modifier, index) => {
|
|
21732
|
-
// :matches-path
|
|
21733
|
-
if (modifier.name.value === UBO_MATCHES_PATH_OPERATOR) {
|
|
21734
|
-
if (!modifier.value) {
|
|
21735
|
-
throw new RuleConversionError(`'${UBO_MATCHES_PATH_OPERATOR}' operator requires a value`);
|
|
21736
|
-
}
|
|
21737
|
-
const value = RegExpUtils.isRegexPattern(modifier.value.value) ? StringUtils.escapeCharacters(modifier.value.value, SPECIAL_MODIFIER_REGEX_CHARS) : modifier.value.value;
|
|
21738
|
-
// Convert uBO's `:matches-path(...)` operator to ADG's `$path=...` modifier
|
|
21739
|
-
conversionMap.add(index, createModifierNode(ADG_PATH_MODIFIER,
|
|
21740
|
-
// We should negate the regexp if the modifier is an exception
|
|
21741
|
-
modifier.exception
|
|
21742
|
-
// eslint-disable-next-line max-len
|
|
21743
|
-
? `${REGEX_MARKER}${RegExpUtils.negateRegexPattern(RegExpUtils.patternToRegexp(value))}${REGEX_MARKER}` : value));
|
|
21744
|
-
}
|
|
21745
|
-
});
|
|
21746
|
-
// Check if we have any converted modifiers
|
|
21747
|
-
if (conversionMap.size) {
|
|
21748
|
-
const modifierListClone = clone(modifierList);
|
|
21749
|
-
// Replace the original modifiers with the converted ones
|
|
21750
|
-
modifierListClone.children = modifierListClone.children.map((modifier, index) => {
|
|
21751
|
-
const convertedModifier = conversionMap.get(index);
|
|
21752
|
-
return convertedModifier ?? modifier;
|
|
21753
|
-
}).flat();
|
|
21754
|
-
return createConversionResult(modifierListClone, true);
|
|
21755
|
-
}
|
|
21756
|
-
// Otherwise, just return the original modifier list
|
|
21757
|
-
return createConversionResult(modifierList, false);
|
|
21758
|
-
}
|
|
21759
|
-
}
|
|
21760
|
-
const ERROR_MESSAGES$1 = {
|
|
21761
|
-
// eslint-disable-next-line max-len
|
|
21762
|
-
INVALID_ATTRIBUTE_VALUE: `Expected '${cssTokenizer.getFormattedTokenName(cssTokenizer.TokenType.Ident)}' or '${cssTokenizer.getFormattedTokenName(cssTokenizer.TokenType.String)}' as attribute value, but got '%s' with value '%s`
|
|
21763
|
-
};
|
|
21764
|
-
var PseudoClasses;
|
|
21765
|
-
(function (PseudoClasses) {
|
|
21766
|
-
PseudoClasses["AbpContains"] = "-abp-contains";
|
|
21767
|
-
PseudoClasses["AbpHas"] = "-abp-has";
|
|
21768
|
-
PseudoClasses["Contains"] = "contains";
|
|
21769
|
-
PseudoClasses["Has"] = "has";
|
|
21770
|
-
PseudoClasses["HasText"] = "has-text";
|
|
21771
|
-
PseudoClasses["MatchesCss"] = "matches-css";
|
|
21772
|
-
PseudoClasses["MatchesCssAfter"] = "matches-css-after";
|
|
21773
|
-
PseudoClasses["MatchesCssBefore"] = "matches-css-before";
|
|
21774
|
-
PseudoClasses["Not"] = "not";
|
|
21775
|
-
})(PseudoClasses || (PseudoClasses = {}));
|
|
21776
|
-
var PseudoElements;
|
|
21777
|
-
(function (PseudoElements) {
|
|
21778
|
-
PseudoElements["After"] = "after";
|
|
21779
|
-
PseudoElements["Before"] = "before";
|
|
21780
|
-
})(PseudoElements || (PseudoElements = {}));
|
|
21781
|
-
const PSEUDO_ELEMENT_NAMES = new Set([PseudoElements.After, PseudoElements.Before]);
|
|
21885
|
+
deepFreeze(scriptletsCompatibilityTableData);
|
|
21782
21886
|
/**
|
|
21783
|
-
*
|
|
21784
|
-
*
|
|
21785
|
-
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
21887
|
+
* Compatibility table instance for scriptlets.
|
|
21786
21888
|
*/
|
|
21787
|
-
|
|
21788
|
-
/**
|
|
21789
|
-
* Converts Extended CSS elements to AdGuard-compatible ones
|
|
21790
|
-
*
|
|
21791
|
-
* @param selectorList Selector list to convert
|
|
21792
|
-
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
21793
|
-
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
21794
|
-
* If the node was not converted, the result will contain the original node with the same object reference
|
|
21795
|
-
* @throws If the rule is invalid or incompatible
|
|
21796
|
-
*/
|
|
21797
|
-
static convertToAdg(selectorList) {
|
|
21798
|
-
const stream = selectorList instanceof CssTokenStream ? selectorList : new CssTokenStream(selectorList);
|
|
21799
|
-
const converted = [];
|
|
21800
|
-
const convertAndPushPseudo = pseudo => {
|
|
21801
|
-
switch (pseudo) {
|
|
21802
|
-
case PseudoClasses.AbpContains:
|
|
21803
|
-
case PseudoClasses.HasText:
|
|
21804
|
-
converted.push(PseudoClasses.Contains);
|
|
21805
|
-
converted.push(OPEN_PARENTHESIS);
|
|
21806
|
-
break;
|
|
21807
|
-
case PseudoClasses.AbpHas:
|
|
21808
|
-
converted.push(PseudoClasses.Has);
|
|
21809
|
-
converted.push(OPEN_PARENTHESIS);
|
|
21810
|
-
break;
|
|
21811
|
-
// a bit special case:
|
|
21812
|
-
// - `:matches-css-before(...)` → `:matches-css(before, ...)`
|
|
21813
|
-
// - `:matches-css-after(...)` → `:matches-css(after, ...)`
|
|
21814
|
-
case PseudoClasses.MatchesCssBefore:
|
|
21815
|
-
case PseudoClasses.MatchesCssAfter:
|
|
21816
|
-
converted.push(PseudoClasses.MatchesCss);
|
|
21817
|
-
converted.push(OPEN_PARENTHESIS);
|
|
21818
|
-
converted.push(pseudo.substring(PseudoClasses.MatchesCss.length + 1));
|
|
21819
|
-
converted.push(COMMA);
|
|
21820
|
-
break;
|
|
21821
|
-
default:
|
|
21822
|
-
converted.push(pseudo);
|
|
21823
|
-
converted.push(OPEN_PARENTHESIS);
|
|
21824
|
-
break;
|
|
21825
|
-
}
|
|
21826
|
-
};
|
|
21827
|
-
while (!stream.isEof()) {
|
|
21828
|
-
const token = stream.getOrFail();
|
|
21829
|
-
if (token.type === cssTokenizer.TokenType.Colon) {
|
|
21830
|
-
// Advance colon
|
|
21831
|
-
stream.advance();
|
|
21832
|
-
converted.push(COLON);
|
|
21833
|
-
const tempToken = stream.getOrFail();
|
|
21834
|
-
// Double colon is a pseudo-element
|
|
21835
|
-
if (tempToken.type === cssTokenizer.TokenType.Colon) {
|
|
21836
|
-
stream.advance();
|
|
21837
|
-
converted.push(COLON);
|
|
21838
|
-
continue;
|
|
21839
|
-
}
|
|
21840
|
-
if (tempToken.type === cssTokenizer.TokenType.Ident) {
|
|
21841
|
-
const name = stream.source.slice(tempToken.start, tempToken.end);
|
|
21842
|
-
if (PSEUDO_ELEMENT_NAMES.has(name)) {
|
|
21843
|
-
// Add an extra colon to the name
|
|
21844
|
-
converted.push(COLON);
|
|
21845
|
-
converted.push(name);
|
|
21846
|
-
} else {
|
|
21847
|
-
// Add the name as is
|
|
21848
|
-
converted.push(name);
|
|
21849
|
-
}
|
|
21850
|
-
// Advance the names
|
|
21851
|
-
stream.advance();
|
|
21852
|
-
} else if (tempToken.type === cssTokenizer.TokenType.Function) {
|
|
21853
|
-
const name = stream.source.slice(tempToken.start, tempToken.end - 1); // omit the last parenthesis
|
|
21854
|
-
// :-abp-contains(...) → :contains(...)
|
|
21855
|
-
// :has-text(...) → :contains(...)
|
|
21856
|
-
// :-abp-has(...) → :has(...)
|
|
21857
|
-
convertAndPushPseudo(name);
|
|
21858
|
-
// Advance the function name
|
|
21859
|
-
stream.advance();
|
|
21860
|
-
}
|
|
21861
|
-
} else if (token.type === cssTokenizer.TokenType.OpenSquareBracket) {
|
|
21862
|
-
let tempToken;
|
|
21863
|
-
const {
|
|
21864
|
-
start
|
|
21865
|
-
} = token;
|
|
21866
|
-
stream.advance();
|
|
21867
|
-
// Converts legacy Extended CSS selectors to the modern Extended CSS syntax.
|
|
21868
|
-
// For example:
|
|
21869
|
-
// - `[-ext-has=...]` → `:has(...)`
|
|
21870
|
-
// - `[-ext-contains=...]` → `:contains(...)`
|
|
21871
|
-
// - `[-ext-matches-css-before=...]` → `:matches-css(before, ...)`
|
|
21872
|
-
stream.skipWhitespace();
|
|
21873
|
-
stream.expect(cssTokenizer.TokenType.Ident);
|
|
21874
|
-
tempToken = stream.getOrFail();
|
|
21875
|
-
let attr = stream.source.slice(tempToken.start, tempToken.end);
|
|
21876
|
-
// Skip if the attribute name is not a legacy Extended CSS one
|
|
21877
|
-
if (!(attr.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX) || attr.startsWith(ABP_EXT_CSS_PREFIX))) {
|
|
21878
|
-
converted.push(stream.source.slice(start, tempToken.end));
|
|
21879
|
-
stream.advance();
|
|
21880
|
-
continue;
|
|
21881
|
-
}
|
|
21882
|
-
if (attr.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX)) {
|
|
21883
|
-
attr = attr.slice(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX.length);
|
|
21884
|
-
}
|
|
21885
|
-
stream.advance();
|
|
21886
|
-
stream.skipWhitespace();
|
|
21887
|
-
// Next token should be an equality operator (=), because Extended CSS attribute selectors
|
|
21888
|
-
// do not support other operators
|
|
21889
|
-
stream.expect(cssTokenizer.TokenType.Delim, {
|
|
21890
|
-
value: EQUALS
|
|
21891
|
-
});
|
|
21892
|
-
stream.advance();
|
|
21893
|
-
// Skip optional whitespace after the operator
|
|
21894
|
-
stream.skipWhitespace();
|
|
21895
|
-
// Parse attribute value
|
|
21896
|
-
tempToken = stream.getOrFail();
|
|
21897
|
-
// According to the spec, attribute value should be an identifier or a string
|
|
21898
|
-
if (tempToken.type !== cssTokenizer.TokenType.Ident && tempToken.type !== cssTokenizer.TokenType.String) {
|
|
21899
|
-
throw new Error(sprintfJs.sprintf(ERROR_MESSAGES$1.INVALID_ATTRIBUTE_VALUE, cssTokenizer.getFormattedTokenName(tempToken.type), stream.source.slice(tempToken.start, tempToken.end)));
|
|
21900
|
-
}
|
|
21901
|
-
const value = stream.source.slice(tempToken.start, tempToken.end);
|
|
21902
|
-
// Advance the attribute value
|
|
21903
|
-
stream.advance();
|
|
21904
|
-
// Skip optional whitespace after the attribute value
|
|
21905
|
-
stream.skipWhitespace();
|
|
21906
|
-
// Next character should be a closing square bracket
|
|
21907
|
-
// We don't allow flags for Extended CSS attribute selectors
|
|
21908
|
-
stream.expect(cssTokenizer.TokenType.CloseSquareBracket);
|
|
21909
|
-
stream.advance();
|
|
21910
|
-
converted.push(COLON);
|
|
21911
|
-
convertAndPushPseudo(attr);
|
|
21912
|
-
let processedValue = value.slice(1, -1); // omit the quotes
|
|
21913
|
-
if (attr === PseudoClasses.Has) {
|
|
21914
|
-
// TODO: Optimize this to avoid double tokenization
|
|
21915
|
-
processedValue = CssSelectorConverter.convertToAdg(processedValue).result;
|
|
21916
|
-
}
|
|
21917
|
-
converted.push(processedValue);
|
|
21918
|
-
converted.push(CLOSE_PARENTHESIS);
|
|
21919
|
-
} else {
|
|
21920
|
-
converted.push(stream.source.slice(token.start, token.end));
|
|
21921
|
-
// Advance the token
|
|
21922
|
-
stream.advance();
|
|
21923
|
-
}
|
|
21924
|
-
}
|
|
21925
|
-
const convertedSelectorList = converted.join(EMPTY);
|
|
21926
|
-
return createConversionResult(convertedSelectorList, stream.source !== convertedSelectorList);
|
|
21927
|
-
}
|
|
21928
|
-
}
|
|
21889
|
+
const scriptletsCompatibilityTable = new ScriptletsCompatibilityTable(scriptletsCompatibilityTableData);
|
|
21929
21890
|
|
|
21891
|
+
/* eslint-disable no-bitwise */
|
|
21930
21892
|
/**
|
|
21931
|
-
* @file
|
|
21893
|
+
* @file Platform schema.
|
|
21932
21894
|
*/
|
|
21933
21895
|
/**
|
|
21934
|
-
*
|
|
21896
|
+
* Platform separator, e.g. 'adg_os_any|adg_safari_any' means any AdGuard OS platform and
|
|
21897
|
+
* any AdGuard Safari content blocker platform.
|
|
21898
|
+
*/
|
|
21899
|
+
const PLATFORM_SEPARATOR = '|';
|
|
21900
|
+
/**
|
|
21901
|
+
* Platform negation character, e.g. 'adg_any|~adg_safari_any' means any AdGuard product except
|
|
21902
|
+
* Safari content blockers.
|
|
21903
|
+
*/
|
|
21904
|
+
const PLATFORM_NEGATION = '~';
|
|
21905
|
+
/**
|
|
21906
|
+
* Parses a raw platform string into a platform bitmask.
|
|
21935
21907
|
*
|
|
21936
|
-
* @
|
|
21908
|
+
* @param rawPlatforms Raw platform string, e.g. 'adg_safari_any|adg_os_any'.
|
|
21909
|
+
*
|
|
21910
|
+
* @returns Platform bitmask.
|
|
21937
21911
|
*/
|
|
21938
|
-
|
|
21939
|
-
|
|
21940
|
-
|
|
21941
|
-
|
|
21942
|
-
|
|
21943
|
-
|
|
21944
|
-
|
|
21945
|
-
|
|
21946
|
-
|
|
21947
|
-
|
|
21948
|
-
static convertToAdg(rule) {
|
|
21949
|
-
const separator = rule.separator.value;
|
|
21950
|
-
let convertedSeparator = separator;
|
|
21951
|
-
const stream = new CssTokenStream(rule.body.selectorList.value);
|
|
21952
|
-
const convertedSelectorList = CssSelectorConverter.convertToAdg(stream);
|
|
21953
|
-
// Change the separator if the rule contains ExtendedCSS elements,
|
|
21954
|
-
// but do not force non-extended CSS separator if the rule does not contain any ExtendedCSS selectors,
|
|
21955
|
-
// because sometimes we use it to force executing ExtendedCSS library.
|
|
21956
|
-
if (stream.hasAnySelectorExtendedCssNodeStrict() || rule.body.remove) {
|
|
21957
|
-
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.AdgExtendedCssInjectionException : exports.CosmeticRuleSeparator.AdgExtendedCssInjection;
|
|
21958
|
-
} else if (rule.syntax !== exports.AdblockSyntax.Adg) {
|
|
21959
|
-
// If the original rule syntax is not AdGuard, use the default separator
|
|
21960
|
-
// e.g. if the input rule is from uBO, we need to convert ## to #$#.
|
|
21961
|
-
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.AdgCssInjectionException : exports.CosmeticRuleSeparator.AdgCssInjection;
|
|
21912
|
+
const parseRawPlatforms = rawPlatforms => {
|
|
21913
|
+
// e.g. 'adg_safari_any|adg_os_any'
|
|
21914
|
+
const rawPlatformList = rawPlatforms.split(PLATFORM_SEPARATOR).map(rawPlatform => rawPlatform.trim());
|
|
21915
|
+
let result = 0;
|
|
21916
|
+
for (let rawPlatform of rawPlatformList) {
|
|
21917
|
+
// negation, e.g. 'adg_any|~adg_safari_any' means any AdGuard product except Safari content blockers
|
|
21918
|
+
let negated = false;
|
|
21919
|
+
if (rawPlatform.startsWith(PLATFORM_NEGATION)) {
|
|
21920
|
+
negated = true;
|
|
21921
|
+
rawPlatform = rawPlatform.slice(1).trim();
|
|
21962
21922
|
}
|
|
21963
|
-
|
|
21964
|
-
if (
|
|
21965
|
-
|
|
21966
|
-
|
|
21967
|
-
|
|
21968
|
-
|
|
21969
|
-
|
|
21970
|
-
|
|
21923
|
+
const platform = SPECIFIC_PLATFORM_MAP.get(rawPlatform) ?? GENERIC_PLATFORM_MAP.get(rawPlatform);
|
|
21924
|
+
if (isUndefined(platform)) {
|
|
21925
|
+
throw new Error(`Unknown platform: ${rawPlatform}`);
|
|
21926
|
+
}
|
|
21927
|
+
if (negated) {
|
|
21928
|
+
result &= ~platform;
|
|
21929
|
+
} else {
|
|
21930
|
+
result |= platform;
|
|
21971
21931
|
}
|
|
21972
|
-
// Otherwise, return the original rule
|
|
21973
|
-
return createNodeConversionResult([rule], false);
|
|
21974
21932
|
}
|
|
21975
|
-
|
|
21976
|
-
|
|
21933
|
+
if (result === 0) {
|
|
21934
|
+
throw new Error('No platforms specified');
|
|
21935
|
+
}
|
|
21936
|
+
return result;
|
|
21937
|
+
};
|
|
21977
21938
|
/**
|
|
21978
|
-
*
|
|
21939
|
+
* Platform schema.
|
|
21979
21940
|
*/
|
|
21980
|
-
|
|
21981
|
-
|
|
21982
|
-
|
|
21983
|
-
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
21984
|
-
*/
|
|
21985
|
-
class ElementHidingRuleConverter extends RuleConverterBase {
|
|
21986
|
-
/**
|
|
21987
|
-
* Converts an element hiding rule to AdGuard format, if possible.
|
|
21988
|
-
*
|
|
21989
|
-
* @param rule Rule node to convert
|
|
21990
|
-
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
21991
|
-
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
21992
|
-
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
21993
|
-
* @throws If the rule is invalid or cannot be converted
|
|
21994
|
-
*/
|
|
21995
|
-
static convertToAdg(rule) {
|
|
21996
|
-
const separator = rule.separator.value;
|
|
21997
|
-
let convertedSeparator = separator;
|
|
21998
|
-
const stream = new CssTokenStream(rule.body.selectorList.value);
|
|
21999
|
-
const convertedSelectorList = CssSelectorConverter.convertToAdg(stream);
|
|
22000
|
-
// Change the separator if the rule contains ExtendedCSS elements,
|
|
22001
|
-
// but do not force non-extended CSS separator if the rule does not contain any ExtendedCSS selectors,
|
|
22002
|
-
// because sometimes we use it to force executing ExtendedCSS library.
|
|
22003
|
-
if (stream.hasAnySelectorExtendedCssNodeStrict()) {
|
|
22004
|
-
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.ExtendedElementHidingException : exports.CosmeticRuleSeparator.ExtendedElementHiding;
|
|
22005
|
-
}
|
|
22006
|
-
// Check if the rule needs to be converted
|
|
22007
|
-
if (!(rule.syntax === exports.AdblockSyntax.Common || rule.syntax === exports.AdblockSyntax.Adg) || separator !== convertedSeparator || convertedSelectorList.isConverted) {
|
|
22008
|
-
// TODO: Replace with custom clone method
|
|
22009
|
-
const ruleClone = clone(rule);
|
|
22010
|
-
ruleClone.syntax = exports.AdblockSyntax.Adg;
|
|
22011
|
-
ruleClone.separator.value = convertedSeparator;
|
|
22012
|
-
ruleClone.body.selectorList.value = convertedSelectorList.result;
|
|
22013
|
-
return createNodeConversionResult([ruleClone], true);
|
|
22014
|
-
}
|
|
22015
|
-
// Otherwise, return the original rule
|
|
22016
|
-
return createNodeConversionResult([rule], false);
|
|
22017
|
-
}
|
|
21941
|
+
zod.string().min(1).transform(value => parseRawPlatforms(value));
|
|
21942
|
+
function getDefaultExportFromCjs(x) {
|
|
21943
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
22018
21944
|
}
|
|
21945
|
+
var mapObj$1 = {
|
|
21946
|
+
exports: {}
|
|
21947
|
+
};
|
|
21948
|
+
const isObject$1 = value => typeof value === 'object' && value !== null;
|
|
21949
|
+
const mapObjectSkip = Symbol('skip');
|
|
22019
21950
|
|
|
22020
|
-
|
|
22021
|
-
|
|
22022
|
-
|
|
22023
|
-
|
|
22024
|
-
|
|
22025
|
-
|
|
22026
|
-
|
|
22027
|
-
* @param modifiers Rule modifiers (optional, default: undefined)
|
|
22028
|
-
* @param exception Exception rule flag (optional, default: false)
|
|
22029
|
-
* @param syntax Adblock syntax (optional, default: Common)
|
|
22030
|
-
* @returns Network rule node
|
|
22031
|
-
*/
|
|
22032
|
-
function createNetworkRuleNode(pattern, modifiers = undefined, exception = false, syntax = exports.AdblockSyntax.Common) {
|
|
22033
|
-
const result = {
|
|
22034
|
-
category: exports.RuleCategory.Network,
|
|
22035
|
-
type: exports.NetworkRuleType.NetworkRule,
|
|
22036
|
-
syntax,
|
|
22037
|
-
exception,
|
|
22038
|
-
pattern: {
|
|
22039
|
-
type: 'Value',
|
|
22040
|
-
value: pattern
|
|
22041
|
-
}
|
|
21951
|
+
// Customized for this use-case
|
|
21952
|
+
const isObjectCustom = value => isObject$1(value) && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
|
|
21953
|
+
const mapObject = (object, mapper, options, isSeen = new WeakMap()) => {
|
|
21954
|
+
options = {
|
|
21955
|
+
deep: false,
|
|
21956
|
+
target: {},
|
|
21957
|
+
...options
|
|
22042
21958
|
};
|
|
22043
|
-
if (
|
|
22044
|
-
|
|
21959
|
+
if (isSeen.has(object)) {
|
|
21960
|
+
return isSeen.get(object);
|
|
22045
21961
|
}
|
|
22046
|
-
|
|
22047
|
-
|
|
22048
|
-
|
|
22049
|
-
|
|
22050
|
-
|
|
22051
|
-
|
|
22052
|
-
|
|
22053
|
-
|
|
22054
|
-
|
|
22055
|
-
|
|
22056
|
-
|
|
22057
|
-
|
|
22058
|
-
|
|
22059
|
-
/**
|
|
22060
|
-
* Converter for request header removal rules
|
|
22061
|
-
*
|
|
22062
|
-
* @todo Implement `convertToUbo` (ABP currently doesn't support header removal rules)
|
|
22063
|
-
*/
|
|
22064
|
-
class HeaderRemovalRuleConverter extends RuleConverterBase {
|
|
22065
|
-
/**
|
|
22066
|
-
* Converts a header removal rule to AdGuard syntax, if possible.
|
|
22067
|
-
*
|
|
22068
|
-
* @param rule Rule node to convert
|
|
22069
|
-
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
22070
|
-
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
22071
|
-
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
22072
|
-
* @throws If the rule is invalid or cannot be converted
|
|
22073
|
-
* @example
|
|
22074
|
-
* If the input rule is:
|
|
22075
|
-
* ```adblock
|
|
22076
|
-
* example.com##^responseheader(header-name)
|
|
22077
|
-
* ```
|
|
22078
|
-
* The output will be:
|
|
22079
|
-
* ```adblock
|
|
22080
|
-
* ||example.com^$removeheader=header-name
|
|
22081
|
-
* ```
|
|
22082
|
-
*/
|
|
22083
|
-
static convertToAdg(rule) {
|
|
22084
|
-
// TODO: Add support for ABP syntax once it starts supporting header removal rules
|
|
22085
|
-
// Leave the rule as is if it's not a header removal rule
|
|
22086
|
-
if (rule.category !== exports.RuleCategory.Cosmetic || rule.type !== exports.CosmeticRuleType.HtmlFilteringRule) {
|
|
22087
|
-
return createNodeConversionResult([rule], false);
|
|
22088
|
-
}
|
|
22089
|
-
const stream = new CssTokenStream(rule.body.value);
|
|
22090
|
-
let token;
|
|
22091
|
-
// Skip leading whitespace
|
|
22092
|
-
stream.skipWhitespace();
|
|
22093
|
-
// Next token should be the `^` followed by a `responseheader` function
|
|
22094
|
-
token = stream.get();
|
|
22095
|
-
if (!token || token.type !== cssTokenizer.TokenType.Delim || rule.body.value[token.start] !== UBO_HTML_MASK) {
|
|
22096
|
-
return createNodeConversionResult([rule], false);
|
|
22097
|
-
}
|
|
22098
|
-
stream.advance();
|
|
22099
|
-
token = stream.get();
|
|
22100
|
-
if (!token) {
|
|
22101
|
-
return createNodeConversionResult([rule], false);
|
|
22102
|
-
}
|
|
22103
|
-
const functionName = rule.body.value.slice(token.start, token.end - 1);
|
|
22104
|
-
if (functionName !== UBO_RESPONSEHEADER_FN) {
|
|
22105
|
-
return createNodeConversionResult([rule], false);
|
|
21962
|
+
isSeen.set(object, options.target);
|
|
21963
|
+
const {
|
|
21964
|
+
target
|
|
21965
|
+
} = options;
|
|
21966
|
+
delete options.target;
|
|
21967
|
+
const mapArray = array => array.map(element => isObjectCustom(element) ? mapObject(element, mapper, options, isSeen) : element);
|
|
21968
|
+
if (Array.isArray(object)) {
|
|
21969
|
+
return mapArray(object);
|
|
21970
|
+
}
|
|
21971
|
+
for (const [key, value] of Object.entries(object)) {
|
|
21972
|
+
const mapResult = mapper(key, value, object);
|
|
21973
|
+
if (mapResult === mapObjectSkip) {
|
|
21974
|
+
continue;
|
|
22106
21975
|
}
|
|
22107
|
-
|
|
22108
|
-
|
|
22109
|
-
|
|
22110
|
-
|
|
22111
|
-
|
|
22112
|
-
|
|
22113
|
-
|
|
22114
|
-
throw new RuleConversionError(ERROR_MESSAGES.EMPTY_PARAMETER);
|
|
21976
|
+
let [newKey, newValue, {
|
|
21977
|
+
shouldRecurse = true
|
|
21978
|
+
} = {}] = mapResult;
|
|
21979
|
+
|
|
21980
|
+
// Drop `__proto__` keys.
|
|
21981
|
+
if (newKey === '__proto__') {
|
|
21982
|
+
continue;
|
|
22115
21983
|
}
|
|
22116
|
-
|
|
22117
|
-
|
|
22118
|
-
// Skip trailing whitespace after the function call
|
|
22119
|
-
stream.skipWhitespace();
|
|
22120
|
-
// Expect the end of the rule - so nothing should be left in the stream
|
|
22121
|
-
if (!stream.isEof()) {
|
|
22122
|
-
token = stream.getOrFail();
|
|
22123
|
-
throw new RuleConversionError(sprintfJs.sprintf(ERROR_MESSAGES.EXPECTED_END_OF_RULE, cssTokenizer.getFormattedTokenName(token.type)));
|
|
21984
|
+
if (options.deep && shouldRecurse && isObjectCustom(newValue)) {
|
|
21985
|
+
newValue = Array.isArray(newValue) ? mapArray(newValue) : mapObject(newValue, mapper, options, isSeen);
|
|
22124
21986
|
}
|
|
22125
|
-
|
|
22126
|
-
|
|
22127
|
-
|
|
22128
|
-
|
|
22129
|
-
|
|
22130
|
-
|
|
22131
|
-
|
|
22132
|
-
|
|
22133
|
-
|
|
22134
|
-
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
21987
|
+
target[newKey] = newValue;
|
|
21988
|
+
}
|
|
21989
|
+
return target;
|
|
21990
|
+
};
|
|
21991
|
+
mapObj$1.exports = (object, mapper, options) => {
|
|
21992
|
+
if (!isObject$1(object)) {
|
|
21993
|
+
throw new TypeError(`Expected an object, got \`${object}\` (${typeof object})`);
|
|
21994
|
+
}
|
|
21995
|
+
return mapObject(object, mapper, options);
|
|
21996
|
+
};
|
|
21997
|
+
mapObj$1.exports.mapObjectSkip = mapObjectSkip;
|
|
21998
|
+
var mapObjExports = mapObj$1.exports;
|
|
21999
|
+
var camelcase = {
|
|
22000
|
+
exports: {}
|
|
22001
|
+
};
|
|
22002
|
+
const UPPERCASE = /[\p{Lu}]/u;
|
|
22003
|
+
const LOWERCASE = /[\p{Ll}]/u;
|
|
22004
|
+
const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
|
|
22005
|
+
const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
|
|
22006
|
+
const SEPARATORS = /[_.\- ]+/;
|
|
22007
|
+
const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
|
|
22008
|
+
const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
|
|
22009
|
+
const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');
|
|
22010
|
+
const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
|
|
22011
|
+
let isLastCharLower = false;
|
|
22012
|
+
let isLastCharUpper = false;
|
|
22013
|
+
let isLastLastCharUpper = false;
|
|
22014
|
+
for (let i = 0; i < string.length; i++) {
|
|
22015
|
+
const character = string[i];
|
|
22016
|
+
if (isLastCharLower && UPPERCASE.test(character)) {
|
|
22017
|
+
string = string.slice(0, i) + '-' + string.slice(i);
|
|
22018
|
+
isLastCharLower = false;
|
|
22019
|
+
isLastLastCharUpper = isLastCharUpper;
|
|
22020
|
+
isLastCharUpper = true;
|
|
22021
|
+
i++;
|
|
22022
|
+
} else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) {
|
|
22023
|
+
string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
|
|
22024
|
+
isLastLastCharUpper = isLastCharUpper;
|
|
22025
|
+
isLastCharUpper = false;
|
|
22026
|
+
isLastCharLower = true;
|
|
22027
|
+
} else {
|
|
22028
|
+
isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
|
|
22029
|
+
isLastLastCharUpper = isLastCharUpper;
|
|
22030
|
+
isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
|
|
22138
22031
|
}
|
|
22139
|
-
// Prepare network rule modifiers
|
|
22140
|
-
const modifiers = createModifierListNode();
|
|
22141
|
-
modifiers.children.push(createModifierNode(ADG_REMOVEHEADER_MODIFIER, param));
|
|
22142
|
-
// Construct the network rule
|
|
22143
|
-
return createNodeConversionResult([createNetworkRuleNode(pattern.join(EMPTY), modifiers,
|
|
22144
|
-
// Copy the exception flag
|
|
22145
|
-
rule.exception, exports.AdblockSyntax.Adg)], true);
|
|
22146
22032
|
}
|
|
22147
|
-
|
|
22148
|
-
|
|
22149
|
-
|
|
22150
|
-
|
|
22151
|
-
|
|
22152
|
-
|
|
22153
|
-
|
|
22154
|
-
|
|
22155
|
-
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
|
|
22159
|
-
|
|
22160
|
-
|
|
22161
|
-
|
|
22162
|
-
|
|
22163
|
-
|
|
22164
|
-
|
|
22165
|
-
|
|
22166
|
-
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22170
|
-
|
|
22171
|
-
|
|
22172
|
-
|
|
22173
|
-
|
|
22174
|
-
|
|
22175
|
-
|
|
22176
|
-
|
|
22177
|
-
|
|
22178
|
-
|
|
22179
|
-
|
|
22180
|
-
|
|
22181
|
-
|
|
22182
|
-
|
|
22183
|
-
|
|
22184
|
-
|
|
22185
|
-
|
|
22186
|
-
|
|
22187
|
-
|
|
22188
|
-
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22194
|
-
|
|
22033
|
+
return string;
|
|
22034
|
+
};
|
|
22035
|
+
const preserveConsecutiveUppercase = (input, toLowerCase) => {
|
|
22036
|
+
LEADING_CAPITAL.lastIndex = 0;
|
|
22037
|
+
return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1));
|
|
22038
|
+
};
|
|
22039
|
+
const postProcess = (input, toUpperCase) => {
|
|
22040
|
+
SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
|
|
22041
|
+
NUMBERS_AND_IDENTIFIER.lastIndex = 0;
|
|
22042
|
+
return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier)).replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m));
|
|
22043
|
+
};
|
|
22044
|
+
const camelCase$1 = (input, options) => {
|
|
22045
|
+
if (!(typeof input === 'string' || Array.isArray(input))) {
|
|
22046
|
+
throw new TypeError('Expected the input to be `string | string[]`');
|
|
22047
|
+
}
|
|
22048
|
+
options = {
|
|
22049
|
+
pascalCase: false,
|
|
22050
|
+
preserveConsecutiveUppercase: false,
|
|
22051
|
+
...options
|
|
22052
|
+
};
|
|
22053
|
+
if (Array.isArray(input)) {
|
|
22054
|
+
input = input.map(x => x.trim()).filter(x => x.length).join('-');
|
|
22055
|
+
} else {
|
|
22056
|
+
input = input.trim();
|
|
22057
|
+
}
|
|
22058
|
+
if (input.length === 0) {
|
|
22059
|
+
return '';
|
|
22060
|
+
}
|
|
22061
|
+
const toLowerCase = options.locale === false ? string => string.toLowerCase() : string => string.toLocaleLowerCase(options.locale);
|
|
22062
|
+
const toUpperCase = options.locale === false ? string => string.toUpperCase() : string => string.toLocaleUpperCase(options.locale);
|
|
22063
|
+
if (input.length === 1) {
|
|
22064
|
+
return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
|
|
22065
|
+
}
|
|
22066
|
+
const hasUpperCase = input !== toLowerCase(input);
|
|
22067
|
+
if (hasUpperCase) {
|
|
22068
|
+
input = preserveCamelCase(input, toLowerCase, toUpperCase);
|
|
22069
|
+
}
|
|
22070
|
+
input = input.replace(LEADING_SEPARATORS, '');
|
|
22071
|
+
if (options.preserveConsecutiveUppercase) {
|
|
22072
|
+
input = preserveConsecutiveUppercase(input, toLowerCase);
|
|
22073
|
+
} else {
|
|
22074
|
+
input = toLowerCase(input);
|
|
22075
|
+
}
|
|
22076
|
+
if (options.pascalCase) {
|
|
22077
|
+
input = toUpperCase(input.charAt(0)) + input.slice(1);
|
|
22078
|
+
}
|
|
22079
|
+
return postProcess(input, toUpperCase);
|
|
22080
|
+
};
|
|
22081
|
+
camelcase.exports = camelCase$1;
|
|
22082
|
+
// TODO: Remove this for the next major release
|
|
22083
|
+
camelcase.exports.default = camelCase$1;
|
|
22084
|
+
var camelcaseExports = camelcase.exports;
|
|
22085
|
+
class QuickLRU {
|
|
22086
|
+
constructor(options = {}) {
|
|
22087
|
+
if (!(options.maxSize && options.maxSize > 0)) {
|
|
22088
|
+
throw new TypeError('`maxSize` must be a number greater than 0');
|
|
22195
22089
|
}
|
|
22196
|
-
|
|
22197
|
-
|
|
22198
|
-
|
|
22199
|
-
|
|
22200
|
-
|
|
22201
|
-
|
|
22202
|
-
|
|
22203
|
-
|
|
22090
|
+
this.maxSize = options.maxSize;
|
|
22091
|
+
this.onEviction = options.onEviction;
|
|
22092
|
+
this.cache = new Map();
|
|
22093
|
+
this.oldCache = new Map();
|
|
22094
|
+
this._size = 0;
|
|
22095
|
+
}
|
|
22096
|
+
_set(key, value) {
|
|
22097
|
+
this.cache.set(key, value);
|
|
22098
|
+
this._size++;
|
|
22099
|
+
if (this._size >= this.maxSize) {
|
|
22100
|
+
this._size = 0;
|
|
22101
|
+
if (typeof this.onEviction === 'function') {
|
|
22102
|
+
for (const [key, value] of this.oldCache.entries()) {
|
|
22103
|
+
this.onEviction(key, value);
|
|
22204
22104
|
}
|
|
22205
|
-
convertedModifiers = AdgCosmeticRuleModifierConverter.convertFromUbo(rule.modifiers);
|
|
22206
|
-
} else if (rule.syntax === exports.AdblockSyntax.Abp) {
|
|
22207
|
-
// TODO: Implement once ABP starts supporting cosmetic rule modifiers
|
|
22208
|
-
throw new RuleConversionError('ABP don\'t support cosmetic rule modifiers');
|
|
22209
22105
|
}
|
|
22106
|
+
this.oldCache = this.cache;
|
|
22107
|
+
this.cache = new Map();
|
|
22210
22108
|
}
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
|
|
22214
|
-
|
|
22215
|
-
|
|
22216
|
-
|
|
22217
|
-
|
|
22218
|
-
|
|
22219
|
-
|
|
22109
|
+
}
|
|
22110
|
+
get(key) {
|
|
22111
|
+
if (this.cache.has(key)) {
|
|
22112
|
+
return this.cache.get(key);
|
|
22113
|
+
}
|
|
22114
|
+
if (this.oldCache.has(key)) {
|
|
22115
|
+
const value = this.oldCache.get(key);
|
|
22116
|
+
this.oldCache.delete(key);
|
|
22117
|
+
this._set(key, value);
|
|
22118
|
+
return value;
|
|
22220
22119
|
}
|
|
22221
|
-
return createNodeConversionResult([rule], false);
|
|
22222
22120
|
}
|
|
22223
|
-
|
|
22224
|
-
|
|
22225
|
-
|
|
22226
|
-
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
*/
|
|
22231
|
-
const ABP_RESOURCE_PREFIX = 'abp-resource:';
|
|
22232
|
-
const ABP_RESOURCE_PREFIX_LENGTH = ABP_RESOURCE_PREFIX.length;
|
|
22233
|
-
/**
|
|
22234
|
-
* Transforms the name of an ABP redirect to a normalized form.
|
|
22235
|
-
*
|
|
22236
|
-
* @param name Redirect name to normalize.
|
|
22237
|
-
*
|
|
22238
|
-
* @returns Normalized redirect name.
|
|
22239
|
-
*
|
|
22240
|
-
* @example
|
|
22241
|
-
* abpRedirectNameNormalizer('abp-resource:my-resource') // => 'my-resource'
|
|
22242
|
-
*/
|
|
22243
|
-
const abpRedirectNameNormalizer = name => {
|
|
22244
|
-
if (name.startsWith(ABP_RESOURCE_PREFIX)) {
|
|
22245
|
-
return name.slice(ABP_RESOURCE_PREFIX_LENGTH);
|
|
22121
|
+
set(key, value) {
|
|
22122
|
+
if (this.cache.has(key)) {
|
|
22123
|
+
this.cache.set(key, value);
|
|
22124
|
+
} else {
|
|
22125
|
+
this._set(key, value);
|
|
22126
|
+
}
|
|
22127
|
+
return this;
|
|
22246
22128
|
}
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
/**
|
|
22250
|
-
* Compatibility table for redirects.
|
|
22251
|
-
*/
|
|
22252
|
-
class RedirectsCompatibilityTable extends CompatibilityTableBase {
|
|
22253
|
-
/**
|
|
22254
|
-
* Creates a new instance of the compatibility table for redirects.
|
|
22255
|
-
*
|
|
22256
|
-
* @param data Compatibility table data.
|
|
22257
|
-
*/
|
|
22258
|
-
constructor(data) {
|
|
22259
|
-
super(data, abpRedirectNameNormalizer);
|
|
22129
|
+
has(key) {
|
|
22130
|
+
return this.cache.has(key) || this.oldCache.has(key);
|
|
22260
22131
|
}
|
|
22261
|
-
|
|
22262
|
-
|
|
22263
|
-
|
|
22264
|
-
*/
|
|
22265
|
-
deepFreeze(redirectsCompatibilityTableData);
|
|
22266
|
-
/**
|
|
22267
|
-
* Compatibility table instance for redirects.
|
|
22268
|
-
*/
|
|
22269
|
-
const redirectsCompatibilityTable = new RedirectsCompatibilityTable(redirectsCompatibilityTableData);
|
|
22270
|
-
|
|
22271
|
-
/**
|
|
22272
|
-
* @file Compatibility tables for scriptlets.
|
|
22273
|
-
*/
|
|
22274
|
-
/**
|
|
22275
|
-
* Compatibility table for scriptlets.
|
|
22276
|
-
*/
|
|
22277
|
-
class ScriptletsCompatibilityTable extends CompatibilityTableBase {}
|
|
22278
|
-
/**
|
|
22279
|
-
* Deep freeze the compatibility table data to avoid accidental modifications.
|
|
22280
|
-
*/
|
|
22281
|
-
deepFreeze(scriptletsCompatibilityTableData);
|
|
22282
|
-
/**
|
|
22283
|
-
* Compatibility table instance for scriptlets.
|
|
22284
|
-
*/
|
|
22285
|
-
const scriptletsCompatibilityTable = new ScriptletsCompatibilityTable(scriptletsCompatibilityTableData);
|
|
22286
|
-
|
|
22287
|
-
/* eslint-disable no-bitwise */
|
|
22288
|
-
/**
|
|
22289
|
-
* @file Platform schema.
|
|
22290
|
-
*/
|
|
22291
|
-
/**
|
|
22292
|
-
* Platform separator, e.g. 'adg_os_any|adg_safari_any' means any AdGuard OS platform and
|
|
22293
|
-
* any AdGuard Safari content blocker platform.
|
|
22294
|
-
*/
|
|
22295
|
-
const PLATFORM_SEPARATOR = '|';
|
|
22296
|
-
/**
|
|
22297
|
-
* Platform negation character, e.g. 'adg_any|~adg_safari_any' means any AdGuard product except
|
|
22298
|
-
* Safari content blockers.
|
|
22299
|
-
*/
|
|
22300
|
-
const PLATFORM_NEGATION = '~';
|
|
22301
|
-
/**
|
|
22302
|
-
* Parses a raw platform string into a platform bitmask.
|
|
22303
|
-
*
|
|
22304
|
-
* @param rawPlatforms Raw platform string, e.g. 'adg_safari_any|adg_os_any'.
|
|
22305
|
-
*
|
|
22306
|
-
* @returns Platform bitmask.
|
|
22307
|
-
*/
|
|
22308
|
-
const parseRawPlatforms = rawPlatforms => {
|
|
22309
|
-
// e.g. 'adg_safari_any|adg_os_any'
|
|
22310
|
-
const rawPlatformList = rawPlatforms.split(PLATFORM_SEPARATOR).map(rawPlatform => rawPlatform.trim());
|
|
22311
|
-
let result = 0;
|
|
22312
|
-
for (let rawPlatform of rawPlatformList) {
|
|
22313
|
-
// negation, e.g. 'adg_any|~adg_safari_any' means any AdGuard product except Safari content blockers
|
|
22314
|
-
let negated = false;
|
|
22315
|
-
if (rawPlatform.startsWith(PLATFORM_NEGATION)) {
|
|
22316
|
-
negated = true;
|
|
22317
|
-
rawPlatform = rawPlatform.slice(1).trim();
|
|
22318
|
-
}
|
|
22319
|
-
const platform = SPECIFIC_PLATFORM_MAP.get(rawPlatform) ?? GENERIC_PLATFORM_MAP.get(rawPlatform);
|
|
22320
|
-
if (isUndefined(platform)) {
|
|
22321
|
-
throw new Error(`Unknown platform: ${rawPlatform}`);
|
|
22132
|
+
peek(key) {
|
|
22133
|
+
if (this.cache.has(key)) {
|
|
22134
|
+
return this.cache.get(key);
|
|
22322
22135
|
}
|
|
22323
|
-
if (
|
|
22324
|
-
|
|
22325
|
-
} else {
|
|
22326
|
-
result |= platform;
|
|
22136
|
+
if (this.oldCache.has(key)) {
|
|
22137
|
+
return this.oldCache.get(key);
|
|
22327
22138
|
}
|
|
22328
22139
|
}
|
|
22329
|
-
|
|
22330
|
-
|
|
22140
|
+
delete(key) {
|
|
22141
|
+
const deleted = this.cache.delete(key);
|
|
22142
|
+
if (deleted) {
|
|
22143
|
+
this._size--;
|
|
22144
|
+
}
|
|
22145
|
+
return this.oldCache.delete(key) || deleted;
|
|
22331
22146
|
}
|
|
22332
|
-
|
|
22333
|
-
|
|
22334
|
-
|
|
22335
|
-
|
|
22336
|
-
*/
|
|
22337
|
-
zod.string().min(1).transform(value => parseRawPlatforms(value));
|
|
22338
|
-
function getDefaultExportFromCjs(x) {
|
|
22339
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
22340
|
-
}
|
|
22341
|
-
var mapObj$1 = {
|
|
22342
|
-
exports: {}
|
|
22343
|
-
};
|
|
22344
|
-
const isObject$1 = value => typeof value === 'object' && value !== null;
|
|
22345
|
-
const mapObjectSkip = Symbol('skip');
|
|
22346
|
-
|
|
22347
|
-
// Customized for this use-case
|
|
22348
|
-
const isObjectCustom = value => isObject$1(value) && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
|
|
22349
|
-
const mapObject = (object, mapper, options, isSeen = new WeakMap()) => {
|
|
22350
|
-
options = {
|
|
22351
|
-
deep: false,
|
|
22352
|
-
target: {},
|
|
22353
|
-
...options
|
|
22354
|
-
};
|
|
22355
|
-
if (isSeen.has(object)) {
|
|
22356
|
-
return isSeen.get(object);
|
|
22147
|
+
clear() {
|
|
22148
|
+
this.cache.clear();
|
|
22149
|
+
this.oldCache.clear();
|
|
22150
|
+
this._size = 0;
|
|
22357
22151
|
}
|
|
22358
|
-
|
|
22359
|
-
|
|
22360
|
-
|
|
22361
|
-
|
|
22362
|
-
delete options.target;
|
|
22363
|
-
const mapArray = array => array.map(element => isObjectCustom(element) ? mapObject(element, mapper, options, isSeen) : element);
|
|
22364
|
-
if (Array.isArray(object)) {
|
|
22365
|
-
return mapArray(object);
|
|
22152
|
+
*keys() {
|
|
22153
|
+
for (const [key] of this) {
|
|
22154
|
+
yield key;
|
|
22155
|
+
}
|
|
22366
22156
|
}
|
|
22367
|
-
|
|
22368
|
-
const
|
|
22369
|
-
|
|
22370
|
-
continue;
|
|
22157
|
+
*values() {
|
|
22158
|
+
for (const [, value] of this) {
|
|
22159
|
+
yield value;
|
|
22371
22160
|
}
|
|
22372
|
-
|
|
22373
|
-
|
|
22374
|
-
|
|
22375
|
-
|
|
22376
|
-
// Drop `__proto__` keys.
|
|
22377
|
-
if (newKey === '__proto__') {
|
|
22378
|
-
continue;
|
|
22161
|
+
}
|
|
22162
|
+
*[Symbol.iterator]() {
|
|
22163
|
+
for (const item of this.cache) {
|
|
22164
|
+
yield item;
|
|
22379
22165
|
}
|
|
22380
|
-
|
|
22381
|
-
|
|
22166
|
+
for (const item of this.oldCache) {
|
|
22167
|
+
const [key] = item;
|
|
22168
|
+
if (!this.cache.has(key)) {
|
|
22169
|
+
yield item;
|
|
22170
|
+
}
|
|
22382
22171
|
}
|
|
22383
|
-
target[newKey] = newValue;
|
|
22384
|
-
}
|
|
22385
|
-
return target;
|
|
22386
|
-
};
|
|
22387
|
-
mapObj$1.exports = (object, mapper, options) => {
|
|
22388
|
-
if (!isObject$1(object)) {
|
|
22389
|
-
throw new TypeError(`Expected an object, got \`${object}\` (${typeof object})`);
|
|
22390
22172
|
}
|
|
22391
|
-
|
|
22392
|
-
|
|
22393
|
-
|
|
22394
|
-
|
|
22395
|
-
|
|
22396
|
-
|
|
22397
|
-
};
|
|
22398
|
-
const UPPERCASE = /[\p{Lu}]/u;
|
|
22399
|
-
const LOWERCASE = /[\p{Ll}]/u;
|
|
22400
|
-
const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
|
|
22401
|
-
const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
|
|
22402
|
-
const SEPARATORS = /[_.\- ]+/;
|
|
22403
|
-
const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
|
|
22404
|
-
const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
|
|
22405
|
-
const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');
|
|
22406
|
-
const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
|
|
22407
|
-
let isLastCharLower = false;
|
|
22408
|
-
let isLastCharUpper = false;
|
|
22409
|
-
let isLastLastCharUpper = false;
|
|
22410
|
-
for (let i = 0; i < string.length; i++) {
|
|
22411
|
-
const character = string[i];
|
|
22412
|
-
if (isLastCharLower && UPPERCASE.test(character)) {
|
|
22413
|
-
string = string.slice(0, i) + '-' + string.slice(i);
|
|
22414
|
-
isLastCharLower = false;
|
|
22415
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
22416
|
-
isLastCharUpper = true;
|
|
22417
|
-
i++;
|
|
22418
|
-
} else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) {
|
|
22419
|
-
string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
|
|
22420
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
22421
|
-
isLastCharUpper = false;
|
|
22422
|
-
isLastCharLower = true;
|
|
22423
|
-
} else {
|
|
22424
|
-
isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
|
|
22425
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
22426
|
-
isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
|
|
22173
|
+
get size() {
|
|
22174
|
+
let oldCacheSize = 0;
|
|
22175
|
+
for (const key of this.oldCache.keys()) {
|
|
22176
|
+
if (!this.cache.has(key)) {
|
|
22177
|
+
oldCacheSize++;
|
|
22178
|
+
}
|
|
22427
22179
|
}
|
|
22180
|
+
return Math.min(this._size + oldCacheSize, this.maxSize);
|
|
22428
22181
|
}
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
const
|
|
22432
|
-
|
|
22433
|
-
|
|
22434
|
-
|
|
22435
|
-
|
|
22436
|
-
|
|
22437
|
-
|
|
22438
|
-
|
|
22439
|
-
|
|
22440
|
-
|
|
22441
|
-
|
|
22442
|
-
|
|
22182
|
+
}
|
|
22183
|
+
var quickLru = QuickLRU;
|
|
22184
|
+
const mapObj = mapObjExports;
|
|
22185
|
+
const camelCase = camelcaseExports;
|
|
22186
|
+
const QuickLru = quickLru;
|
|
22187
|
+
const has = (array, key) => array.some(x => {
|
|
22188
|
+
if (typeof x === 'string') {
|
|
22189
|
+
return x === key;
|
|
22190
|
+
}
|
|
22191
|
+
x.lastIndex = 0;
|
|
22192
|
+
return x.test(key);
|
|
22193
|
+
});
|
|
22194
|
+
const cache = new QuickLru({
|
|
22195
|
+
maxSize: 100000
|
|
22196
|
+
});
|
|
22197
|
+
|
|
22198
|
+
// Reproduces behavior from `map-obj`
|
|
22199
|
+
const isObject = value => typeof value === 'object' && value !== null && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
|
|
22200
|
+
const camelCaseConvert = (input, options) => {
|
|
22201
|
+
if (!isObject(input)) {
|
|
22202
|
+
return input;
|
|
22443
22203
|
}
|
|
22444
22204
|
options = {
|
|
22205
|
+
deep: false,
|
|
22445
22206
|
pascalCase: false,
|
|
22446
|
-
preserveConsecutiveUppercase: false,
|
|
22447
22207
|
...options
|
|
22448
22208
|
};
|
|
22209
|
+
const {
|
|
22210
|
+
exclude,
|
|
22211
|
+
pascalCase,
|
|
22212
|
+
stopPaths,
|
|
22213
|
+
deep
|
|
22214
|
+
} = options;
|
|
22215
|
+
const stopPathsSet = new Set(stopPaths);
|
|
22216
|
+
const makeMapper = parentPath => (key, value) => {
|
|
22217
|
+
if (deep && isObject(value)) {
|
|
22218
|
+
const path = parentPath === undefined ? key : `${parentPath}.${key}`;
|
|
22219
|
+
if (!stopPathsSet.has(path)) {
|
|
22220
|
+
value = mapObj(value, makeMapper(path));
|
|
22221
|
+
}
|
|
22222
|
+
}
|
|
22223
|
+
if (!(exclude && has(exclude, key))) {
|
|
22224
|
+
const cacheKey = pascalCase ? `${key}_` : key;
|
|
22225
|
+
if (cache.has(cacheKey)) {
|
|
22226
|
+
key = cache.get(cacheKey);
|
|
22227
|
+
} else {
|
|
22228
|
+
const returnValue = camelCase(key, {
|
|
22229
|
+
pascalCase,
|
|
22230
|
+
locale: false
|
|
22231
|
+
});
|
|
22232
|
+
if (key.length < 100) {
|
|
22233
|
+
// Prevent abuse
|
|
22234
|
+
cache.set(cacheKey, returnValue);
|
|
22235
|
+
}
|
|
22236
|
+
key = returnValue;
|
|
22237
|
+
}
|
|
22238
|
+
}
|
|
22239
|
+
return [key, value];
|
|
22240
|
+
};
|
|
22241
|
+
return mapObj(input, makeMapper(undefined));
|
|
22242
|
+
};
|
|
22243
|
+
var camelcaseKeys = (input, options) => {
|
|
22449
22244
|
if (Array.isArray(input)) {
|
|
22450
|
-
|
|
22451
|
-
} else {
|
|
22452
|
-
input = input.trim();
|
|
22453
|
-
}
|
|
22454
|
-
if (input.length === 0) {
|
|
22455
|
-
return '';
|
|
22456
|
-
}
|
|
22457
|
-
const toLowerCase = options.locale === false ? string => string.toLowerCase() : string => string.toLocaleLowerCase(options.locale);
|
|
22458
|
-
const toUpperCase = options.locale === false ? string => string.toUpperCase() : string => string.toLocaleUpperCase(options.locale);
|
|
22459
|
-
if (input.length === 1) {
|
|
22460
|
-
return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
|
|
22245
|
+
return Object.keys(input).map(key => camelCaseConvert(input[key], options));
|
|
22461
22246
|
}
|
|
22462
|
-
|
|
22463
|
-
|
|
22464
|
-
|
|
22247
|
+
return camelCaseConvert(input, options);
|
|
22248
|
+
};
|
|
22249
|
+
var camelCaseKeys = /*@__PURE__*/getDefaultExportFromCjs(camelcaseKeys);
|
|
22250
|
+
|
|
22251
|
+
/**
|
|
22252
|
+
* @file Zod camelCase utility.
|
|
22253
|
+
*/
|
|
22254
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
22255
|
+
/**
|
|
22256
|
+
* Transforms Zod schema to camelCase.
|
|
22257
|
+
*
|
|
22258
|
+
* @param zod Zod schema.
|
|
22259
|
+
*
|
|
22260
|
+
* @returns Zod schema with camelCase properties.
|
|
22261
|
+
*
|
|
22262
|
+
* @see {@link https://github.com/colinhacks/zod/issues/486#issuecomment-1501097361}
|
|
22263
|
+
*/
|
|
22264
|
+
const zodToCamelCase = zod => {
|
|
22265
|
+
return zod.transform(val => camelCaseKeys(val));
|
|
22266
|
+
};
|
|
22267
|
+
|
|
22268
|
+
/**
|
|
22269
|
+
* @file Base compatibility data schema, which is commonly used in compatibility tables.
|
|
22270
|
+
*/
|
|
22271
|
+
/**
|
|
22272
|
+
* Zod schema for boolean values. Accepts both boolean and string values.
|
|
22273
|
+
*/
|
|
22274
|
+
const booleanSchema = zod.union([zod.string().transform(val => val.trim().toLowerCase() === 'true'), zod.boolean()]);
|
|
22275
|
+
/**
|
|
22276
|
+
* Zod schema for non-empty string values.
|
|
22277
|
+
*/
|
|
22278
|
+
const nonEmptyStringSchema = zod.string().transform(val => val.trim()).pipe(zod.string().min(1));
|
|
22279
|
+
/**
|
|
22280
|
+
* Zod schema for base compatibility data.
|
|
22281
|
+
* Here we use snake_case properties because the compatibility data is stored in YAML files.
|
|
22282
|
+
*/
|
|
22283
|
+
const baseCompatibilityDataSchema = zod.object({
|
|
22284
|
+
/**
|
|
22285
|
+
* Name of the actual entity.
|
|
22286
|
+
*/
|
|
22287
|
+
name: nonEmptyStringSchema,
|
|
22288
|
+
/**
|
|
22289
|
+
* List of aliases for the entity (if any).
|
|
22290
|
+
*/
|
|
22291
|
+
aliases: zod.array(nonEmptyStringSchema).nullable().default(null),
|
|
22292
|
+
/**
|
|
22293
|
+
* Short description of the actual entity.
|
|
22294
|
+
* If not specified or it's value is `null`, then the description is not available.
|
|
22295
|
+
*/
|
|
22296
|
+
description: nonEmptyStringSchema.nullable().default(null),
|
|
22297
|
+
/**
|
|
22298
|
+
* Link to the documentation. If not specified or it's value is `null`, then the documentation is not available.
|
|
22299
|
+
*/
|
|
22300
|
+
docs: nonEmptyStringSchema.nullable().default(null),
|
|
22301
|
+
/**
|
|
22302
|
+
* The version of the adblocker in which the entity was added.
|
|
22303
|
+
* For AdGuard resources, the version of the library is specified.
|
|
22304
|
+
*/
|
|
22305
|
+
version_added: nonEmptyStringSchema.nullable().default(null),
|
|
22306
|
+
/**
|
|
22307
|
+
* The version of the adblocker when the entity was removed.
|
|
22308
|
+
*/
|
|
22309
|
+
version_removed: nonEmptyStringSchema.nullable().default(null),
|
|
22310
|
+
/**
|
|
22311
|
+
* Describes whether the entity is deprecated.
|
|
22312
|
+
*/
|
|
22313
|
+
deprecated: booleanSchema.default(false),
|
|
22314
|
+
/**
|
|
22315
|
+
* Message that describes why the entity is deprecated.
|
|
22316
|
+
* If not specified or it's value is `null`, then the message is not available.
|
|
22317
|
+
* It's value is omitted if the entity is not marked as deprecated.
|
|
22318
|
+
*/
|
|
22319
|
+
deprecation_message: nonEmptyStringSchema.nullable().default(null),
|
|
22320
|
+
/**
|
|
22321
|
+
* Describes whether the entity is removed; for *already removed* features.
|
|
22322
|
+
*/
|
|
22323
|
+
removed: booleanSchema.default(false),
|
|
22324
|
+
/**
|
|
22325
|
+
* Message that describes why the entity is removed.
|
|
22326
|
+
* If not specified or it's value is `null`, then the message is not available.
|
|
22327
|
+
* It's value is omitted if the entity is not marked as deprecated.
|
|
22328
|
+
*/
|
|
22329
|
+
removal_message: nonEmptyStringSchema.nullable().default(null)
|
|
22330
|
+
});
|
|
22331
|
+
/**
|
|
22332
|
+
* Zod schema for base compatibility data with camelCase properties.
|
|
22333
|
+
*/
|
|
22334
|
+
zodToCamelCase(baseCompatibilityDataSchema);
|
|
22335
|
+
/**
|
|
22336
|
+
* Refinement logic for base compatibility data.
|
|
22337
|
+
*
|
|
22338
|
+
* @param data Base compatibility data.
|
|
22339
|
+
* @param ctx Refinement context.
|
|
22340
|
+
*/
|
|
22341
|
+
const baseRefineLogic = (data, ctx) => {
|
|
22342
|
+
if (data.deprecated && !data.deprecation_message) {
|
|
22343
|
+
ctx.addIssue({
|
|
22344
|
+
code: zod.ZodIssueCode.custom,
|
|
22345
|
+
message: 'deprecation_message is required for deprecated modifiers'
|
|
22346
|
+
});
|
|
22465
22347
|
}
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22348
|
+
if (!data.deprecated && data.deprecation_message) {
|
|
22349
|
+
ctx.addIssue({
|
|
22350
|
+
code: zod.ZodIssueCode.custom,
|
|
22351
|
+
message: 'deprecation_message is only allowed for deprecated modifiers'
|
|
22352
|
+
});
|
|
22471
22353
|
}
|
|
22472
|
-
if (
|
|
22473
|
-
|
|
22354
|
+
if (data.aliases && data.aliases.length !== new Set(data.aliases).size) {
|
|
22355
|
+
ctx.addIssue({
|
|
22356
|
+
code: zod.ZodIssueCode.custom,
|
|
22357
|
+
message: 'Aliases must be unique'
|
|
22358
|
+
});
|
|
22474
22359
|
}
|
|
22475
|
-
return postProcess(input, toUpperCase);
|
|
22476
22360
|
};
|
|
22477
|
-
|
|
22478
|
-
|
|
22479
|
-
|
|
22480
|
-
|
|
22481
|
-
|
|
22482
|
-
|
|
22483
|
-
|
|
22484
|
-
|
|
22485
|
-
|
|
22486
|
-
|
|
22487
|
-
|
|
22488
|
-
|
|
22489
|
-
|
|
22490
|
-
|
|
22361
|
+
|
|
22362
|
+
/**
|
|
22363
|
+
* Checks if error has message.
|
|
22364
|
+
*
|
|
22365
|
+
* @param error Error object.
|
|
22366
|
+
* @returns If param is error.
|
|
22367
|
+
*/
|
|
22368
|
+
function isErrorWithMessage(error) {
|
|
22369
|
+
return typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string';
|
|
22370
|
+
}
|
|
22371
|
+
/**
|
|
22372
|
+
* Converts error to the error with message.
|
|
22373
|
+
*
|
|
22374
|
+
* @param maybeError Possible error.
|
|
22375
|
+
* @returns Error with message.
|
|
22376
|
+
*/
|
|
22377
|
+
function toErrorWithMessage(maybeError) {
|
|
22378
|
+
if (isErrorWithMessage(maybeError)) {
|
|
22379
|
+
return maybeError;
|
|
22491
22380
|
}
|
|
22492
|
-
|
|
22493
|
-
|
|
22494
|
-
|
|
22495
|
-
|
|
22496
|
-
|
|
22497
|
-
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
|
|
22502
|
-
|
|
22503
|
-
|
|
22504
|
-
|
|
22381
|
+
try {
|
|
22382
|
+
return new Error(JSON.stringify(maybeError));
|
|
22383
|
+
} catch {
|
|
22384
|
+
// fallback in case there's an error stringifying the maybeError
|
|
22385
|
+
// like with circular references for example.
|
|
22386
|
+
return new Error(String(maybeError));
|
|
22387
|
+
}
|
|
22388
|
+
}
|
|
22389
|
+
/**
|
|
22390
|
+
* Converts error object to error with message. This method might be helpful to handle thrown errors.
|
|
22391
|
+
*
|
|
22392
|
+
* @param error Error object.
|
|
22393
|
+
*
|
|
22394
|
+
* @returns Message of the error.
|
|
22395
|
+
*/
|
|
22396
|
+
function getErrorMessage(error) {
|
|
22397
|
+
return toErrorWithMessage(error).message;
|
|
22398
|
+
}
|
|
22399
|
+
|
|
22400
|
+
/**
|
|
22401
|
+
* @file Schema for modifier data.
|
|
22402
|
+
*/
|
|
22403
|
+
/**
|
|
22404
|
+
* Known validators that don't need to be validated as regex.
|
|
22405
|
+
*/
|
|
22406
|
+
const KNOWN_VALIDATORS = new Set(['domain', 'pipe_separated_domains', 'regexp', 'url']);
|
|
22407
|
+
/**
|
|
22408
|
+
* Zod schema for modifier data.
|
|
22409
|
+
*/
|
|
22410
|
+
zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
22411
|
+
/**
|
|
22412
|
+
* List of modifiers that are incompatible with the actual one.
|
|
22413
|
+
*/
|
|
22414
|
+
conflicts: zod.array(nonEmptyStringSchema).nullable().default(null),
|
|
22415
|
+
/**
|
|
22416
|
+
* The actual modifier is incompatible with all other modifiers, except the ones listed in `conflicts`.
|
|
22417
|
+
*/
|
|
22418
|
+
inverse_conflicts: booleanSchema.default(false),
|
|
22419
|
+
/**
|
|
22420
|
+
* Describes whether the actual modifier supports value assignment. For example, `$domain` is assignable,
|
|
22421
|
+
* so it can be used like this: `$domain=domain.com\|~subdomain.domain.com`, where `=` is the assignment operator
|
|
22422
|
+
* and `domain.com\|~subdomain.domain.com` is the value.
|
|
22423
|
+
*/
|
|
22424
|
+
assignable: booleanSchema.default(false),
|
|
22425
|
+
/**
|
|
22426
|
+
* Describes whether the actual modifier can be negated. For example, `$third-party` is negatable,
|
|
22427
|
+
* so it can be used like this: `$~third-party`.
|
|
22428
|
+
*/
|
|
22429
|
+
negatable: booleanSchema.default(true),
|
|
22430
|
+
/**
|
|
22431
|
+
* The actual modifier can only be used in blocking rules, it cannot be used in exceptions.
|
|
22432
|
+
* If it's value is `true`, then the modifier can be used only in blocking rules.
|
|
22433
|
+
* `exception_only` and `block_only` cannot be used together (they are mutually exclusive).
|
|
22434
|
+
*/
|
|
22435
|
+
block_only: booleanSchema.default(false),
|
|
22436
|
+
/**
|
|
22437
|
+
* The actual modifier can only be used in exceptions, it cannot be used in blocking rules.
|
|
22438
|
+
* If it's value is `true`, then the modifier can be used only in exceptions.
|
|
22439
|
+
* `exception_only` and `block_only` cannot be used together (they are mutually exclusive).
|
|
22440
|
+
*/
|
|
22441
|
+
exception_only: booleanSchema.default(false),
|
|
22442
|
+
/**
|
|
22443
|
+
* Describes whether the *assignable* modifier value is required.
|
|
22444
|
+
* For example, `$cookie` is assignable but it can be used without a value in exception rules:
|
|
22445
|
+
* `@@\|\|example.com^$cookie`.
|
|
22446
|
+
* If `false`, the `value_format` is required, e.g. the value of `$app` should always be specified
|
|
22447
|
+
*/
|
|
22448
|
+
value_optional: booleanSchema.default(false),
|
|
22449
|
+
/**
|
|
22450
|
+
* Describes the format of the value for the *assignable* modifier.
|
|
22451
|
+
* Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
|
|
22452
|
+
*/
|
|
22453
|
+
value_format: nonEmptyStringSchema.nullable().default(null)
|
|
22454
|
+
}).superRefine((data, ctx) => {
|
|
22455
|
+
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
22456
|
+
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
22457
|
+
baseRefineLogic(data, ctx);
|
|
22458
|
+
if (data.block_only && data.exception_only) {
|
|
22459
|
+
ctx.addIssue({
|
|
22460
|
+
code: zod.ZodIssueCode.custom,
|
|
22461
|
+
message: 'block_only and exception_only are mutually exclusive'
|
|
22462
|
+
});
|
|
22505
22463
|
}
|
|
22506
|
-
|
|
22507
|
-
|
|
22508
|
-
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
const value = this.oldCache.get(key);
|
|
22512
|
-
this.oldCache.delete(key);
|
|
22513
|
-
this._set(key, value);
|
|
22514
|
-
return value;
|
|
22515
|
-
}
|
|
22464
|
+
if (data.assignable && !data.value_format) {
|
|
22465
|
+
ctx.addIssue({
|
|
22466
|
+
code: zod.ZodIssueCode.custom,
|
|
22467
|
+
message: 'value_format is required for assignable modifiers'
|
|
22468
|
+
});
|
|
22516
22469
|
}
|
|
22517
|
-
|
|
22518
|
-
|
|
22519
|
-
|
|
22520
|
-
|
|
22521
|
-
|
|
22470
|
+
if (data.value_format) {
|
|
22471
|
+
const valueFormat = data.value_format.trim();
|
|
22472
|
+
// if it is a known validator, we don't need to validate it further
|
|
22473
|
+
if (KNOWN_VALIDATORS.has(valueFormat)) {
|
|
22474
|
+
return;
|
|
22475
|
+
}
|
|
22476
|
+
// otherwise, we need to validate it as a regex
|
|
22477
|
+
try {
|
|
22478
|
+
XRegExp(valueFormat);
|
|
22479
|
+
} catch (error) {
|
|
22480
|
+
ctx.addIssue({
|
|
22481
|
+
code: zod.ZodIssueCode.custom,
|
|
22482
|
+
message: getErrorMessage(error)
|
|
22483
|
+
});
|
|
22522
22484
|
}
|
|
22523
|
-
return this;
|
|
22524
22485
|
}
|
|
22525
|
-
|
|
22526
|
-
|
|
22486
|
+
}));
|
|
22487
|
+
|
|
22488
|
+
/**
|
|
22489
|
+
* @file Schema for redirect data.
|
|
22490
|
+
*/
|
|
22491
|
+
/**
|
|
22492
|
+
* Zod schema for redirect data.
|
|
22493
|
+
*/
|
|
22494
|
+
zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
22495
|
+
/**
|
|
22496
|
+
* Whether the redirect is blocking.
|
|
22497
|
+
*/
|
|
22498
|
+
is_blocking: booleanSchema.default(false),
|
|
22499
|
+
/**
|
|
22500
|
+
* Resource type(s) belonging to the redirect.
|
|
22501
|
+
*
|
|
22502
|
+
* @see {@link https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#type-ResourceType}
|
|
22503
|
+
*/
|
|
22504
|
+
resource_types: zod.array(resourceTypeSchema).default([])
|
|
22505
|
+
}).superRefine(baseRefineLogic));
|
|
22506
|
+
|
|
22507
|
+
/**
|
|
22508
|
+
* @file Schema for scriptlet data.
|
|
22509
|
+
*/
|
|
22510
|
+
/**
|
|
22511
|
+
* Zod schema for scriptlet parameter data.
|
|
22512
|
+
*/
|
|
22513
|
+
const scriptletParameterSchema = zod.object({
|
|
22514
|
+
/**
|
|
22515
|
+
* Name of the actual parameter.
|
|
22516
|
+
*/
|
|
22517
|
+
name: nonEmptyStringSchema,
|
|
22518
|
+
/**
|
|
22519
|
+
* Describes whether the parameter is required. Empty parameters are not allowed.
|
|
22520
|
+
*/
|
|
22521
|
+
required: booleanSchema,
|
|
22522
|
+
/**
|
|
22523
|
+
* Short description of the parameter.
|
|
22524
|
+
* If not specified or it's value is `null`,then the description is not available.
|
|
22525
|
+
*/
|
|
22526
|
+
description: nonEmptyStringSchema.nullable().default(null),
|
|
22527
|
+
/**
|
|
22528
|
+
* Regular expression that matches the value of the parameter.
|
|
22529
|
+
* If it's value is `null`, then the parameter value is not checked.
|
|
22530
|
+
*/
|
|
22531
|
+
pattern: nonEmptyStringSchema.nullable().default(null),
|
|
22532
|
+
/**
|
|
22533
|
+
* Default value of the parameter (if any).
|
|
22534
|
+
*/
|
|
22535
|
+
default: nonEmptyStringSchema.nullable().default(null),
|
|
22536
|
+
/**
|
|
22537
|
+
* Describes whether the parameter is used only for debugging purposes.
|
|
22538
|
+
*/
|
|
22539
|
+
debug: booleanSchema.default(false)
|
|
22540
|
+
});
|
|
22541
|
+
/**
|
|
22542
|
+
* Zod schema for scriptlet parameters.
|
|
22543
|
+
*/
|
|
22544
|
+
const scriptletParametersSchema = zod.array(scriptletParameterSchema);
|
|
22545
|
+
/**
|
|
22546
|
+
* Zod schema for scriptlet data.
|
|
22547
|
+
*/
|
|
22548
|
+
zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
22549
|
+
/**
|
|
22550
|
+
* List of parameters that the scriptlet accepts.
|
|
22551
|
+
* **Every** parameter should be listed here, because we check that the scriptlet is used correctly
|
|
22552
|
+
* (e.g. that the number of parameters is correct).
|
|
22553
|
+
*/
|
|
22554
|
+
parameters: scriptletParametersSchema.optional()
|
|
22555
|
+
}).superRefine((data, ctx) => {
|
|
22556
|
+
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
22557
|
+
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
22558
|
+
baseRefineLogic(data, ctx);
|
|
22559
|
+
// we don't allow required parameters after optional ones
|
|
22560
|
+
if (!data.parameters) {
|
|
22561
|
+
return;
|
|
22527
22562
|
}
|
|
22528
|
-
|
|
22529
|
-
|
|
22530
|
-
|
|
22531
|
-
|
|
22532
|
-
|
|
22533
|
-
|
|
22563
|
+
let optionalFound = false;
|
|
22564
|
+
for (const parameter of data.parameters) {
|
|
22565
|
+
if (optionalFound && parameter.required) {
|
|
22566
|
+
ctx.addIssue({
|
|
22567
|
+
code: zod.ZodIssueCode.custom,
|
|
22568
|
+
message: 'Required parameters must be before optional ones'
|
|
22569
|
+
});
|
|
22534
22570
|
}
|
|
22535
|
-
|
|
22536
|
-
|
|
22537
|
-
const deleted = this.cache.delete(key);
|
|
22538
|
-
if (deleted) {
|
|
22539
|
-
this._size--;
|
|
22571
|
+
if (!parameter.required) {
|
|
22572
|
+
optionalFound = true;
|
|
22540
22573
|
}
|
|
22541
|
-
return this.oldCache.delete(key) || deleted;
|
|
22542
|
-
}
|
|
22543
|
-
clear() {
|
|
22544
|
-
this.cache.clear();
|
|
22545
|
-
this.oldCache.clear();
|
|
22546
|
-
this._size = 0;
|
|
22547
22574
|
}
|
|
22548
|
-
|
|
22549
|
-
|
|
22550
|
-
|
|
22575
|
+
}));
|
|
22576
|
+
|
|
22577
|
+
/**
|
|
22578
|
+
* @file Scriptlet injection rule converter
|
|
22579
|
+
*/
|
|
22580
|
+
const ABP_SCRIPTLET_PREFIX = 'abp-';
|
|
22581
|
+
const UBO_SCRIPTLET_PREFIX = 'ubo-';
|
|
22582
|
+
const UBO_SCRIPTLET_PREFIX_LENGTH = UBO_SCRIPTLET_PREFIX.length;
|
|
22583
|
+
const UBO_SCRIPTLET_JS_SUFFIX = '.js';
|
|
22584
|
+
const UBO_SCRIPTLET_JS_SUFFIX_LENGTH = UBO_SCRIPTLET_JS_SUFFIX.length;
|
|
22585
|
+
const COMMA_SEPARATOR = ',';
|
|
22586
|
+
const ADG_SET_CONSTANT_NAME = 'set-constant';
|
|
22587
|
+
const ADG_SET_CONSTANT_EMPTY_STRING = '';
|
|
22588
|
+
const ADG_SET_CONSTANT_EMPTY_ARRAY = 'emptyArr';
|
|
22589
|
+
const ADG_SET_CONSTANT_EMPTY_OBJECT = 'emptyObj';
|
|
22590
|
+
const UBO_SET_CONSTANT_EMPTY_STRING = '\'\'';
|
|
22591
|
+
const UBO_SET_CONSTANT_EMPTY_ARRAY = '[]';
|
|
22592
|
+
const UBO_SET_CONSTANT_EMPTY_OBJECT = '{}';
|
|
22593
|
+
const ADG_PREVENT_FETCH_NAME = 'prevent-fetch';
|
|
22594
|
+
const ADG_PREVENT_FETCH_EMPTY_STRING = '';
|
|
22595
|
+
const ADG_PREVENT_FETCH_WILDCARD = '*';
|
|
22596
|
+
const UBO_NO_FETCH_IF_WILDCARD = '/^/';
|
|
22597
|
+
const UBO_REMOVE_CLASS_NAME = 'remove-class.js';
|
|
22598
|
+
const UBO_REMOVE_ATTR_NAME = 'remove-attr.js';
|
|
22599
|
+
const setConstantAdgToUboMap = {
|
|
22600
|
+
[ADG_SET_CONSTANT_EMPTY_STRING]: UBO_SET_CONSTANT_EMPTY_STRING,
|
|
22601
|
+
[ADG_SET_CONSTANT_EMPTY_ARRAY]: UBO_SET_CONSTANT_EMPTY_ARRAY,
|
|
22602
|
+
[ADG_SET_CONSTANT_EMPTY_OBJECT]: UBO_SET_CONSTANT_EMPTY_OBJECT
|
|
22603
|
+
};
|
|
22604
|
+
const REMOVE_ATTR_CLASS_APPLYING = new Set(['asap', 'stay', 'complete']);
|
|
22605
|
+
/**
|
|
22606
|
+
* Scriptlet injection rule converter class
|
|
22607
|
+
*
|
|
22608
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22609
|
+
*/
|
|
22610
|
+
class ScriptletRuleConverter extends RuleConverterBase {
|
|
22611
|
+
/**
|
|
22612
|
+
* Converts a scriptlet injection rule to AdGuard format, if possible.
|
|
22613
|
+
*
|
|
22614
|
+
* @param rule Rule node to convert
|
|
22615
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
22616
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
22617
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
22618
|
+
* @throws If the rule is invalid or cannot be converted
|
|
22619
|
+
*/
|
|
22620
|
+
static convertToAdg(rule) {
|
|
22621
|
+
// Ignore AdGuard rules
|
|
22622
|
+
if (rule.syntax === exports.AdblockSyntax.Adg) {
|
|
22623
|
+
return createNodeConversionResult([rule], false);
|
|
22551
22624
|
}
|
|
22552
|
-
|
|
22553
|
-
|
|
22554
|
-
|
|
22555
|
-
|
|
22625
|
+
const separator = rule.separator.value;
|
|
22626
|
+
let convertedSeparator = separator;
|
|
22627
|
+
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.AdgJsInjectionException : exports.CosmeticRuleSeparator.AdgJsInjection;
|
|
22628
|
+
const convertedScriptlets = [];
|
|
22629
|
+
// Special case: empty uBO exception scriptlet, e.g. `example.com#@#+js()`
|
|
22630
|
+
if (rule.syntax === exports.AdblockSyntax.Ubo && rule.body.children.length === 1 && rule.body.children[0].children.length === 0) {
|
|
22631
|
+
convertedScriptlets.push(rule.body.children[0]);
|
|
22632
|
+
} else {
|
|
22633
|
+
for (const scriptlet of rule.body.children) {
|
|
22634
|
+
// Clone the node to avoid any side effects
|
|
22635
|
+
const scriptletClone = cloneScriptletRuleNode(scriptlet);
|
|
22636
|
+
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
22637
|
+
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletClone), exports.QuoteType.None);
|
|
22638
|
+
// Add prefix if it's not already there
|
|
22639
|
+
let prefix;
|
|
22640
|
+
// In uBO / ABP syntax, if a parameter contains the separator character, it should be escaped,
|
|
22641
|
+
// but during the conversion, we need to unescape them, because AdGuard syntax uses quotes to
|
|
22642
|
+
// distinguish between parameters.
|
|
22643
|
+
let charToUnescape;
|
|
22644
|
+
switch (rule.syntax) {
|
|
22645
|
+
case exports.AdblockSyntax.Abp:
|
|
22646
|
+
prefix = ABP_SCRIPTLET_PREFIX;
|
|
22647
|
+
charToUnescape = SPACE;
|
|
22648
|
+
break;
|
|
22649
|
+
case exports.AdblockSyntax.Ubo:
|
|
22650
|
+
prefix = UBO_SCRIPTLET_PREFIX;
|
|
22651
|
+
charToUnescape = COMMA_SEPARATOR;
|
|
22652
|
+
break;
|
|
22653
|
+
default:
|
|
22654
|
+
prefix = EMPTY;
|
|
22655
|
+
}
|
|
22656
|
+
if (!scriptletName.startsWith(prefix)) {
|
|
22657
|
+
setScriptletName(scriptletClone, `${prefix}${scriptletName}`);
|
|
22658
|
+
}
|
|
22659
|
+
if (!isUndefined(charToUnescape)) {
|
|
22660
|
+
transformAllScriptletArguments(scriptletClone, value => {
|
|
22661
|
+
if (!isNull(value)) {
|
|
22662
|
+
return QuoteUtils.unescapeSingleEscapedOccurrences(value, charToUnescape);
|
|
22663
|
+
}
|
|
22664
|
+
return value;
|
|
22665
|
+
});
|
|
22666
|
+
}
|
|
22667
|
+
if (rule.syntax === exports.AdblockSyntax.Ubo) {
|
|
22668
|
+
const scriptletData = scriptletsCompatibilityTable.getFirst(scriptletName, exports.GenericPlatform.UboAny);
|
|
22669
|
+
// Some scriptlets have special values that need to be converted
|
|
22670
|
+
if (scriptletData && (scriptletData.name === UBO_REMOVE_CLASS_NAME || scriptletData.name === UBO_REMOVE_ATTR_NAME) && scriptletClone.children.length > 2) {
|
|
22671
|
+
const selectors = [];
|
|
22672
|
+
let applying = null;
|
|
22673
|
+
let lastArg = scriptletClone.children.pop();
|
|
22674
|
+
// The very last argument might be the 'applying' parameter
|
|
22675
|
+
if (lastArg) {
|
|
22676
|
+
if (REMOVE_ATTR_CLASS_APPLYING.has(lastArg.value)) {
|
|
22677
|
+
applying = lastArg.value;
|
|
22678
|
+
} else {
|
|
22679
|
+
selectors.push(lastArg.value);
|
|
22680
|
+
}
|
|
22681
|
+
}
|
|
22682
|
+
while (scriptletClone.children.length > 2) {
|
|
22683
|
+
lastArg = scriptletClone.children.pop();
|
|
22684
|
+
if (lastArg) {
|
|
22685
|
+
selectors.push(lastArg.value.trim());
|
|
22686
|
+
}
|
|
22687
|
+
}
|
|
22688
|
+
// Set last arg to be the combined selectors (in reverse order, because we popped them)
|
|
22689
|
+
if (selectors.length > 0) {
|
|
22690
|
+
scriptletClone.children.push({
|
|
22691
|
+
type: 'Value',
|
|
22692
|
+
value: selectors.reverse().join(', ')
|
|
22693
|
+
});
|
|
22694
|
+
}
|
|
22695
|
+
// Push back the 'applying' parameter if it was found previously
|
|
22696
|
+
if (!isNull(applying)) {
|
|
22697
|
+
// If we don't have any selectors,
|
|
22698
|
+
// we need to add an empty parameter before the 'applying' one
|
|
22699
|
+
if (selectors.length === 0) {
|
|
22700
|
+
scriptletClone.children.push({
|
|
22701
|
+
type: 'Value',
|
|
22702
|
+
value: EMPTY
|
|
22703
|
+
});
|
|
22704
|
+
}
|
|
22705
|
+
scriptletClone.children.push({
|
|
22706
|
+
type: 'Value',
|
|
22707
|
+
value: applying
|
|
22708
|
+
});
|
|
22709
|
+
}
|
|
22710
|
+
}
|
|
22711
|
+
}
|
|
22712
|
+
// ADG scriptlet parameters should be quoted, and single quoted are preferred
|
|
22713
|
+
setScriptletQuoteType(scriptletClone, exports.QuoteType.Single);
|
|
22714
|
+
convertedScriptlets.push(scriptletClone);
|
|
22715
|
+
}
|
|
22556
22716
|
}
|
|
22717
|
+
return createNodeConversionResult(convertedScriptlets.map(scriptlet => {
|
|
22718
|
+
const res = {
|
|
22719
|
+
category: rule.category,
|
|
22720
|
+
type: rule.type,
|
|
22721
|
+
syntax: exports.AdblockSyntax.Adg,
|
|
22722
|
+
exception: rule.exception,
|
|
22723
|
+
domains: cloneDomainListNode(rule.domains),
|
|
22724
|
+
separator: {
|
|
22725
|
+
type: 'Value',
|
|
22726
|
+
value: convertedSeparator
|
|
22727
|
+
},
|
|
22728
|
+
body: {
|
|
22729
|
+
type: rule.body.type,
|
|
22730
|
+
children: [scriptlet]
|
|
22731
|
+
}
|
|
22732
|
+
};
|
|
22733
|
+
if (rule.modifiers) {
|
|
22734
|
+
res.modifiers = cloneModifierListNode(rule.modifiers);
|
|
22735
|
+
}
|
|
22736
|
+
return res;
|
|
22737
|
+
}), true);
|
|
22557
22738
|
}
|
|
22558
|
-
|
|
22559
|
-
|
|
22560
|
-
|
|
22739
|
+
/**
|
|
22740
|
+
* Converts a scriptlet injection rule to uBlock format, if possible.
|
|
22741
|
+
*
|
|
22742
|
+
* @param rule Rule node to convert
|
|
22743
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
22744
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
22745
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
22746
|
+
* @throws If the rule is invalid or cannot be converted
|
|
22747
|
+
*/
|
|
22748
|
+
static convertToUbo(rule) {
|
|
22749
|
+
// Ignore uBlock rules
|
|
22750
|
+
if (rule.syntax === exports.AdblockSyntax.Ubo) {
|
|
22751
|
+
return createNodeConversionResult([rule], false);
|
|
22561
22752
|
}
|
|
22562
|
-
|
|
22563
|
-
|
|
22564
|
-
|
|
22565
|
-
|
|
22753
|
+
const separator = rule.separator.value;
|
|
22754
|
+
let convertedSeparator = separator;
|
|
22755
|
+
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.ElementHidingException : exports.CosmeticRuleSeparator.ElementHiding;
|
|
22756
|
+
const convertedScriptlets = [];
|
|
22757
|
+
// Special case: empty AdGuard exception scriptlet, e.g. `example.com#%#//scriptlet()`
|
|
22758
|
+
if (rule.syntax === exports.AdblockSyntax.Adg && rule.body.children.length === 1 && rule.body.children[0].children.length === 0) {
|
|
22759
|
+
convertedScriptlets.push(rule.body.children[0]);
|
|
22760
|
+
} else {
|
|
22761
|
+
for (const scriptlet of rule.body.children) {
|
|
22762
|
+
// Clone the node to avoid any side effects
|
|
22763
|
+
const scriptletClone = cloneScriptletRuleNode(scriptlet);
|
|
22764
|
+
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
22765
|
+
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletClone), exports.QuoteType.None);
|
|
22766
|
+
let uboScriptletName;
|
|
22767
|
+
if (rule.syntax === exports.AdblockSyntax.Adg && scriptletName.startsWith(UBO_SCRIPTLET_PREFIX)) {
|
|
22768
|
+
// Special case: AdGuard syntax 'preserves' the original scriptlet name,
|
|
22769
|
+
// so we need to convert it back by removing the uBO prefix
|
|
22770
|
+
uboScriptletName = scriptletName.slice(UBO_SCRIPTLET_PREFIX_LENGTH);
|
|
22771
|
+
} else {
|
|
22772
|
+
// Otherwise, try to find the corresponding uBO scriptlet name, or use the original one if not found
|
|
22773
|
+
const uboScriptlet = scriptletsCompatibilityTable.getFirst(scriptletName, exports.GenericPlatform.UboAny);
|
|
22774
|
+
uboScriptletName = uboScriptlet?.name ?? scriptletName;
|
|
22775
|
+
}
|
|
22776
|
+
// Remove the '.js' suffix if it's there - its presence is not mandatory
|
|
22777
|
+
if (uboScriptletName.endsWith(UBO_SCRIPTLET_JS_SUFFIX)) {
|
|
22778
|
+
uboScriptletName = uboScriptletName.slice(0, -UBO_SCRIPTLET_JS_SUFFIX_LENGTH);
|
|
22779
|
+
}
|
|
22780
|
+
setScriptletName(scriptletClone, uboScriptletName);
|
|
22781
|
+
setScriptletQuoteType(scriptletClone, exports.QuoteType.None);
|
|
22782
|
+
// Escape unescaped commas in parameters, because uBlock Origin uses them as separators.
|
|
22783
|
+
// For example, the following AdGuard rule:
|
|
22784
|
+
//
|
|
22785
|
+
// example.com#%#//scriptlet('spoof-css', '.adsbygoogle, #ads', 'visibility', 'visible')
|
|
22786
|
+
//
|
|
22787
|
+
// ↓↓ should be converted to ↓↓
|
|
22788
|
+
//
|
|
22789
|
+
// example.com##+js(spoof-css.js, .adsbygoogle\, #ads, visibility, visible)
|
|
22790
|
+
// ------------ ------------------- ---------- -------
|
|
22791
|
+
// arg 0 arg 1 arg 2 arg 3
|
|
22792
|
+
//
|
|
22793
|
+
// and we need to escape the comma in the second argument to prevent it from being treated
|
|
22794
|
+
// as two separate arguments.
|
|
22795
|
+
transformAllScriptletArguments(scriptletClone, value => {
|
|
22796
|
+
if (!isNull(value)) {
|
|
22797
|
+
return QuoteUtils.escapeUnescapedOccurrences(value, COMMA_SEPARATOR);
|
|
22798
|
+
}
|
|
22799
|
+
return value;
|
|
22800
|
+
});
|
|
22801
|
+
// Unescape spaces in parameters, because uBlock Origin doesn't treat them as separators.
|
|
22802
|
+
if (rule.syntax === exports.AdblockSyntax.Abp) {
|
|
22803
|
+
transformAllScriptletArguments(scriptletClone, value => {
|
|
22804
|
+
if (!isNull(value)) {
|
|
22805
|
+
return QuoteUtils.unescapeSingleEscapedOccurrences(value, SPACE);
|
|
22806
|
+
}
|
|
22807
|
+
return value;
|
|
22808
|
+
});
|
|
22809
|
+
}
|
|
22810
|
+
// Some scriptlets have special values that need to be converted
|
|
22811
|
+
switch (scriptletName) {
|
|
22812
|
+
case ADG_SET_CONSTANT_NAME:
|
|
22813
|
+
transformNthScriptletArgument(scriptletClone, 2, value => {
|
|
22814
|
+
if (!isNull(value)) {
|
|
22815
|
+
return setConstantAdgToUboMap[value] ?? value;
|
|
22816
|
+
}
|
|
22817
|
+
return value;
|
|
22818
|
+
});
|
|
22819
|
+
break;
|
|
22820
|
+
case ADG_PREVENT_FETCH_NAME:
|
|
22821
|
+
transformNthScriptletArgument(scriptletClone, 1, value => {
|
|
22822
|
+
if (value === ADG_PREVENT_FETCH_EMPTY_STRING || value === ADG_PREVENT_FETCH_WILDCARD) {
|
|
22823
|
+
return UBO_NO_FETCH_IF_WILDCARD;
|
|
22824
|
+
}
|
|
22825
|
+
return value;
|
|
22826
|
+
});
|
|
22827
|
+
break;
|
|
22828
|
+
}
|
|
22829
|
+
convertedScriptlets.push(scriptletClone);
|
|
22566
22830
|
}
|
|
22567
22831
|
}
|
|
22568
|
-
|
|
22569
|
-
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
22573
|
-
|
|
22832
|
+
return createNodeConversionResult(convertedScriptlets.map(scriptlet => {
|
|
22833
|
+
const res = {
|
|
22834
|
+
category: rule.category,
|
|
22835
|
+
type: rule.type,
|
|
22836
|
+
syntax: exports.AdblockSyntax.Ubo,
|
|
22837
|
+
exception: rule.exception,
|
|
22838
|
+
domains: cloneDomainListNode(rule.domains),
|
|
22839
|
+
separator: {
|
|
22840
|
+
type: 'Value',
|
|
22841
|
+
value: convertedSeparator
|
|
22842
|
+
},
|
|
22843
|
+
body: {
|
|
22844
|
+
type: rule.body.type,
|
|
22845
|
+
children: [scriptlet]
|
|
22846
|
+
}
|
|
22847
|
+
};
|
|
22848
|
+
if (rule.modifiers) {
|
|
22849
|
+
res.modifiers = cloneModifierListNode(rule.modifiers);
|
|
22574
22850
|
}
|
|
22575
|
-
|
|
22576
|
-
|
|
22851
|
+
return res;
|
|
22852
|
+
}), true);
|
|
22577
22853
|
}
|
|
22578
22854
|
}
|
|
22579
|
-
var quickLru = QuickLRU;
|
|
22580
|
-
const mapObj = mapObjExports;
|
|
22581
|
-
const camelCase = camelcaseExports;
|
|
22582
|
-
const QuickLru = quickLru;
|
|
22583
|
-
const has = (array, key) => array.some(x => {
|
|
22584
|
-
if (typeof x === 'string') {
|
|
22585
|
-
return x === key;
|
|
22586
|
-
}
|
|
22587
|
-
x.lastIndex = 0;
|
|
22588
|
-
return x.test(key);
|
|
22589
|
-
});
|
|
22590
|
-
const cache = new QuickLru({
|
|
22591
|
-
maxSize: 100000
|
|
22592
|
-
});
|
|
22593
22855
|
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22856
|
+
/**
|
|
22857
|
+
* @file Utility functions for working with modifier nodes
|
|
22858
|
+
*/
|
|
22859
|
+
/**
|
|
22860
|
+
* Creates a modifier node
|
|
22861
|
+
*
|
|
22862
|
+
* @param name Name of the modifier
|
|
22863
|
+
* @param value Value of the modifier
|
|
22864
|
+
* @param exception Whether the modifier is an exception
|
|
22865
|
+
* @returns Modifier node
|
|
22866
|
+
*/
|
|
22867
|
+
function createModifierNode(name, value = undefined, exception = false) {
|
|
22868
|
+
const result = {
|
|
22869
|
+
type: 'Modifier',
|
|
22870
|
+
exception,
|
|
22871
|
+
name: {
|
|
22872
|
+
type: 'Value',
|
|
22873
|
+
value: name
|
|
22874
|
+
}
|
|
22875
|
+
};
|
|
22876
|
+
if (!isUndefined(value)) {
|
|
22877
|
+
result.value = {
|
|
22878
|
+
type: 'Value',
|
|
22879
|
+
value
|
|
22880
|
+
};
|
|
22599
22881
|
}
|
|
22600
|
-
|
|
22601
|
-
|
|
22602
|
-
|
|
22603
|
-
|
|
22882
|
+
return result;
|
|
22883
|
+
}
|
|
22884
|
+
/**
|
|
22885
|
+
* Creates a modifier list node
|
|
22886
|
+
*
|
|
22887
|
+
* @param modifiers Modifiers to put in the list (optional, defaults to an empty list)
|
|
22888
|
+
* @returns Modifier list node
|
|
22889
|
+
*/
|
|
22890
|
+
function createModifierListNode(modifiers = []) {
|
|
22891
|
+
const result = {
|
|
22892
|
+
type: 'ModifierList',
|
|
22893
|
+
// We need to clone the modifiers to avoid side effects
|
|
22894
|
+
children: modifiers.length ? clone(modifiers) : []
|
|
22604
22895
|
};
|
|
22605
|
-
|
|
22606
|
-
|
|
22607
|
-
|
|
22608
|
-
|
|
22609
|
-
|
|
22610
|
-
|
|
22611
|
-
|
|
22612
|
-
|
|
22613
|
-
|
|
22614
|
-
|
|
22615
|
-
|
|
22616
|
-
|
|
22896
|
+
return result;
|
|
22897
|
+
}
|
|
22898
|
+
|
|
22899
|
+
/**
|
|
22900
|
+
* A very simple map extension that allows to store multiple values for the same key
|
|
22901
|
+
* by storing them in an array.
|
|
22902
|
+
*
|
|
22903
|
+
* @todo Add more methods if needed
|
|
22904
|
+
*/
|
|
22905
|
+
class MultiValueMap extends Map {
|
|
22906
|
+
/**
|
|
22907
|
+
* Adds a value to the map. If the key already exists, the value will be appended to the existing array,
|
|
22908
|
+
* otherwise a new array will be created for the key.
|
|
22909
|
+
*
|
|
22910
|
+
* @param key Key to add
|
|
22911
|
+
* @param values Value(s) to add
|
|
22912
|
+
*/
|
|
22913
|
+
add(key, ...values) {
|
|
22914
|
+
let currentValues = super.get(key);
|
|
22915
|
+
if (isUndefined(currentValues)) {
|
|
22916
|
+
currentValues = [];
|
|
22917
|
+
super.set(key, values);
|
|
22918
|
+
}
|
|
22919
|
+
currentValues.push(...values);
|
|
22920
|
+
}
|
|
22921
|
+
}
|
|
22922
|
+
|
|
22923
|
+
/**
|
|
22924
|
+
* @file Cosmetic rule modifier converter from uBO to ADG
|
|
22925
|
+
*/
|
|
22926
|
+
const UBO_MATCHES_PATH_OPERATOR = 'matches-path';
|
|
22927
|
+
const ADG_PATH_MODIFIER = 'path';
|
|
22928
|
+
/**
|
|
22929
|
+
* Special characters in modifier regexps that should be escaped
|
|
22930
|
+
*/
|
|
22931
|
+
const SPECIAL_MODIFIER_REGEX_CHARS = new Set([OPEN_SQUARE_BRACKET, CLOSE_SQUARE_BRACKET, COMMA, ESCAPE_CHARACTER]);
|
|
22932
|
+
/**
|
|
22933
|
+
* Helper class for converting cosmetic rule modifiers from uBO to ADG
|
|
22934
|
+
*/
|
|
22935
|
+
class AdgCosmeticRuleModifierConverter {
|
|
22936
|
+
/**
|
|
22937
|
+
* Converts a uBO cosmetic rule modifier list to ADG, if possible.
|
|
22938
|
+
*
|
|
22939
|
+
* @param modifierList Cosmetic rule modifier list node to convert
|
|
22940
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
22941
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
22942
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
22943
|
+
* @throws If the modifier list cannot be converted
|
|
22944
|
+
* @see {@link https://github.com/gorhill/uBlock/wiki/Procedural-cosmetic-filters#cosmetic-filter-operators}
|
|
22945
|
+
*/
|
|
22946
|
+
static convertFromUbo(modifierList) {
|
|
22947
|
+
const conversionMap = new MultiValueMap();
|
|
22948
|
+
modifierList.children.forEach((modifier, index) => {
|
|
22949
|
+
// :matches-path
|
|
22950
|
+
if (modifier.name.value === UBO_MATCHES_PATH_OPERATOR) {
|
|
22951
|
+
if (!modifier.value) {
|
|
22952
|
+
throw new RuleConversionError(`'${UBO_MATCHES_PATH_OPERATOR}' operator requires a value`);
|
|
22953
|
+
}
|
|
22954
|
+
const value = RegExpUtils.isRegexPattern(modifier.value.value) ? StringUtils.escapeCharacters(modifier.value.value, SPECIAL_MODIFIER_REGEX_CHARS) : modifier.value.value;
|
|
22955
|
+
// Convert uBO's `:matches-path(...)` operator to ADG's `$path=...` modifier
|
|
22956
|
+
conversionMap.add(index, createModifierNode(ADG_PATH_MODIFIER,
|
|
22957
|
+
// We should negate the regexp if the modifier is an exception
|
|
22958
|
+
modifier.exception
|
|
22959
|
+
// eslint-disable-next-line max-len
|
|
22960
|
+
? `${REGEX_MARKER}${RegExpUtils.negateRegexPattern(RegExpUtils.patternToRegexp(value))}${REGEX_MARKER}` : value));
|
|
22617
22961
|
}
|
|
22962
|
+
});
|
|
22963
|
+
// Check if we have any converted modifiers
|
|
22964
|
+
if (conversionMap.size) {
|
|
22965
|
+
const modifierListClone = clone(modifierList);
|
|
22966
|
+
// Replace the original modifiers with the converted ones
|
|
22967
|
+
modifierListClone.children = modifierListClone.children.map((modifier, index) => {
|
|
22968
|
+
const convertedModifier = conversionMap.get(index);
|
|
22969
|
+
return convertedModifier ?? modifier;
|
|
22970
|
+
}).flat();
|
|
22971
|
+
return createConversionResult(modifierListClone, true);
|
|
22618
22972
|
}
|
|
22619
|
-
|
|
22620
|
-
|
|
22621
|
-
|
|
22622
|
-
|
|
22623
|
-
|
|
22624
|
-
|
|
22625
|
-
|
|
22626
|
-
|
|
22973
|
+
// Otherwise, just return the original modifier list
|
|
22974
|
+
return createConversionResult(modifierList, false);
|
|
22975
|
+
}
|
|
22976
|
+
}
|
|
22977
|
+
const ERROR_MESSAGES$1 = {
|
|
22978
|
+
// eslint-disable-next-line max-len
|
|
22979
|
+
INVALID_ATTRIBUTE_VALUE: `Expected '${cssTokenizer.getFormattedTokenName(cssTokenizer.TokenType.Ident)}' or '${cssTokenizer.getFormattedTokenName(cssTokenizer.TokenType.String)}' as attribute value, but got '%s' with value '%s`
|
|
22980
|
+
};
|
|
22981
|
+
var PseudoClasses;
|
|
22982
|
+
(function (PseudoClasses) {
|
|
22983
|
+
PseudoClasses["AbpContains"] = "-abp-contains";
|
|
22984
|
+
PseudoClasses["AbpHas"] = "-abp-has";
|
|
22985
|
+
PseudoClasses["Contains"] = "contains";
|
|
22986
|
+
PseudoClasses["Has"] = "has";
|
|
22987
|
+
PseudoClasses["HasText"] = "has-text";
|
|
22988
|
+
PseudoClasses["MatchesCss"] = "matches-css";
|
|
22989
|
+
PseudoClasses["MatchesCssAfter"] = "matches-css-after";
|
|
22990
|
+
PseudoClasses["MatchesCssBefore"] = "matches-css-before";
|
|
22991
|
+
PseudoClasses["Not"] = "not";
|
|
22992
|
+
})(PseudoClasses || (PseudoClasses = {}));
|
|
22993
|
+
var PseudoElements;
|
|
22994
|
+
(function (PseudoElements) {
|
|
22995
|
+
PseudoElements["After"] = "after";
|
|
22996
|
+
PseudoElements["Before"] = "before";
|
|
22997
|
+
})(PseudoElements || (PseudoElements = {}));
|
|
22998
|
+
const PSEUDO_ELEMENT_NAMES = new Set([PseudoElements.After, PseudoElements.Before]);
|
|
22999
|
+
/**
|
|
23000
|
+
* CSS selector converter
|
|
23001
|
+
*
|
|
23002
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
23003
|
+
*/
|
|
23004
|
+
class CssSelectorConverter extends ConverterBase {
|
|
23005
|
+
/**
|
|
23006
|
+
* Converts Extended CSS elements to AdGuard-compatible ones
|
|
23007
|
+
*
|
|
23008
|
+
* @param selectorList Selector list to convert
|
|
23009
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
23010
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
23011
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
23012
|
+
* @throws If the rule is invalid or incompatible
|
|
23013
|
+
*/
|
|
23014
|
+
static convertToAdg(selectorList) {
|
|
23015
|
+
const stream = selectorList instanceof CssTokenStream ? selectorList : new CssTokenStream(selectorList);
|
|
23016
|
+
const converted = [];
|
|
23017
|
+
const convertAndPushPseudo = pseudo => {
|
|
23018
|
+
switch (pseudo) {
|
|
23019
|
+
case PseudoClasses.AbpContains:
|
|
23020
|
+
case PseudoClasses.HasText:
|
|
23021
|
+
converted.push(PseudoClasses.Contains);
|
|
23022
|
+
converted.push(OPEN_PARENTHESIS);
|
|
23023
|
+
break;
|
|
23024
|
+
case PseudoClasses.AbpHas:
|
|
23025
|
+
converted.push(PseudoClasses.Has);
|
|
23026
|
+
converted.push(OPEN_PARENTHESIS);
|
|
23027
|
+
break;
|
|
23028
|
+
// a bit special case:
|
|
23029
|
+
// - `:matches-css-before(...)` → `:matches-css(before, ...)`
|
|
23030
|
+
// - `:matches-css-after(...)` → `:matches-css(after, ...)`
|
|
23031
|
+
case PseudoClasses.MatchesCssBefore:
|
|
23032
|
+
case PseudoClasses.MatchesCssAfter:
|
|
23033
|
+
converted.push(PseudoClasses.MatchesCss);
|
|
23034
|
+
converted.push(OPEN_PARENTHESIS);
|
|
23035
|
+
converted.push(pseudo.substring(PseudoClasses.MatchesCss.length + 1));
|
|
23036
|
+
converted.push(COMMA);
|
|
23037
|
+
break;
|
|
23038
|
+
default:
|
|
23039
|
+
converted.push(pseudo);
|
|
23040
|
+
converted.push(OPEN_PARENTHESIS);
|
|
23041
|
+
break;
|
|
23042
|
+
}
|
|
23043
|
+
};
|
|
23044
|
+
while (!stream.isEof()) {
|
|
23045
|
+
const token = stream.getOrFail();
|
|
23046
|
+
if (token.type === cssTokenizer.TokenType.Colon) {
|
|
23047
|
+
// Advance colon
|
|
23048
|
+
stream.advance();
|
|
23049
|
+
converted.push(COLON);
|
|
23050
|
+
const tempToken = stream.getOrFail();
|
|
23051
|
+
// Double colon is a pseudo-element
|
|
23052
|
+
if (tempToken.type === cssTokenizer.TokenType.Colon) {
|
|
23053
|
+
stream.advance();
|
|
23054
|
+
converted.push(COLON);
|
|
23055
|
+
continue;
|
|
23056
|
+
}
|
|
23057
|
+
if (tempToken.type === cssTokenizer.TokenType.Ident) {
|
|
23058
|
+
const name = stream.source.slice(tempToken.start, tempToken.end);
|
|
23059
|
+
if (PSEUDO_ELEMENT_NAMES.has(name)) {
|
|
23060
|
+
// Add an extra colon to the name
|
|
23061
|
+
converted.push(COLON);
|
|
23062
|
+
converted.push(name);
|
|
23063
|
+
} else {
|
|
23064
|
+
// Add the name as is
|
|
23065
|
+
converted.push(name);
|
|
23066
|
+
}
|
|
23067
|
+
// Advance the names
|
|
23068
|
+
stream.advance();
|
|
23069
|
+
} else if (tempToken.type === cssTokenizer.TokenType.Function) {
|
|
23070
|
+
const name = stream.source.slice(tempToken.start, tempToken.end - 1); // omit the last parenthesis
|
|
23071
|
+
// :-abp-contains(...) → :contains(...)
|
|
23072
|
+
// :has-text(...) → :contains(...)
|
|
23073
|
+
// :-abp-has(...) → :has(...)
|
|
23074
|
+
convertAndPushPseudo(name);
|
|
23075
|
+
// Advance the function name
|
|
23076
|
+
stream.advance();
|
|
23077
|
+
}
|
|
23078
|
+
} else if (token.type === cssTokenizer.TokenType.OpenSquareBracket) {
|
|
23079
|
+
let tempToken;
|
|
23080
|
+
const {
|
|
23081
|
+
start
|
|
23082
|
+
} = token;
|
|
23083
|
+
stream.advance();
|
|
23084
|
+
// Converts legacy Extended CSS selectors to the modern Extended CSS syntax.
|
|
23085
|
+
// For example:
|
|
23086
|
+
// - `[-ext-has=...]` → `:has(...)`
|
|
23087
|
+
// - `[-ext-contains=...]` → `:contains(...)`
|
|
23088
|
+
// - `[-ext-matches-css-before=...]` → `:matches-css(before, ...)`
|
|
23089
|
+
stream.skipWhitespace();
|
|
23090
|
+
stream.expect(cssTokenizer.TokenType.Ident);
|
|
23091
|
+
tempToken = stream.getOrFail();
|
|
23092
|
+
let attr = stream.source.slice(tempToken.start, tempToken.end);
|
|
23093
|
+
// Skip if the attribute name is not a legacy Extended CSS one
|
|
23094
|
+
if (!(attr.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX) || attr.startsWith(ABP_EXT_CSS_PREFIX))) {
|
|
23095
|
+
converted.push(stream.source.slice(start, tempToken.end));
|
|
23096
|
+
stream.advance();
|
|
23097
|
+
continue;
|
|
23098
|
+
}
|
|
23099
|
+
if (attr.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX)) {
|
|
23100
|
+
attr = attr.slice(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX.length);
|
|
23101
|
+
}
|
|
23102
|
+
stream.advance();
|
|
23103
|
+
stream.skipWhitespace();
|
|
23104
|
+
// Next token should be an equality operator (=), because Extended CSS attribute selectors
|
|
23105
|
+
// do not support other operators
|
|
23106
|
+
stream.expect(cssTokenizer.TokenType.Delim, {
|
|
23107
|
+
value: EQUALS
|
|
22627
23108
|
});
|
|
22628
|
-
|
|
22629
|
-
|
|
22630
|
-
|
|
23109
|
+
stream.advance();
|
|
23110
|
+
// Skip optional whitespace after the operator
|
|
23111
|
+
stream.skipWhitespace();
|
|
23112
|
+
// Parse attribute value
|
|
23113
|
+
tempToken = stream.getOrFail();
|
|
23114
|
+
// According to the spec, attribute value should be an identifier or a string
|
|
23115
|
+
if (tempToken.type !== cssTokenizer.TokenType.Ident && tempToken.type !== cssTokenizer.TokenType.String) {
|
|
23116
|
+
throw new Error(sprintfJs.sprintf(ERROR_MESSAGES$1.INVALID_ATTRIBUTE_VALUE, cssTokenizer.getFormattedTokenName(tempToken.type), stream.source.slice(tempToken.start, tempToken.end)));
|
|
22631
23117
|
}
|
|
22632
|
-
|
|
23118
|
+
const value = stream.source.slice(tempToken.start, tempToken.end);
|
|
23119
|
+
// Advance the attribute value
|
|
23120
|
+
stream.advance();
|
|
23121
|
+
// Skip optional whitespace after the attribute value
|
|
23122
|
+
stream.skipWhitespace();
|
|
23123
|
+
// Next character should be a closing square bracket
|
|
23124
|
+
// We don't allow flags for Extended CSS attribute selectors
|
|
23125
|
+
stream.expect(cssTokenizer.TokenType.CloseSquareBracket);
|
|
23126
|
+
stream.advance();
|
|
23127
|
+
converted.push(COLON);
|
|
23128
|
+
convertAndPushPseudo(attr);
|
|
23129
|
+
let processedValue = value.slice(1, -1); // omit the quotes
|
|
23130
|
+
if (attr === PseudoClasses.Has) {
|
|
23131
|
+
// TODO: Optimize this to avoid double tokenization
|
|
23132
|
+
processedValue = CssSelectorConverter.convertToAdg(processedValue).result;
|
|
23133
|
+
}
|
|
23134
|
+
converted.push(processedValue);
|
|
23135
|
+
converted.push(CLOSE_PARENTHESIS);
|
|
23136
|
+
} else {
|
|
23137
|
+
converted.push(stream.source.slice(token.start, token.end));
|
|
23138
|
+
// Advance the token
|
|
23139
|
+
stream.advance();
|
|
22633
23140
|
}
|
|
22634
23141
|
}
|
|
22635
|
-
|
|
22636
|
-
|
|
22637
|
-
return mapObj(input, makeMapper(undefined));
|
|
22638
|
-
};
|
|
22639
|
-
var camelcaseKeys = (input, options) => {
|
|
22640
|
-
if (Array.isArray(input)) {
|
|
22641
|
-
return Object.keys(input).map(key => camelCaseConvert(input[key], options));
|
|
23142
|
+
const convertedSelectorList = converted.join(EMPTY);
|
|
23143
|
+
return createConversionResult(convertedSelectorList, stream.source !== convertedSelectorList);
|
|
22642
23144
|
}
|
|
22643
|
-
|
|
22644
|
-
};
|
|
22645
|
-
var camelCaseKeys = /*@__PURE__*/getDefaultExportFromCjs(camelcaseKeys);
|
|
23145
|
+
}
|
|
22646
23146
|
|
|
22647
23147
|
/**
|
|
22648
|
-
* @file
|
|
23148
|
+
* @file CSS injection rule converter
|
|
22649
23149
|
*/
|
|
22650
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
22651
23150
|
/**
|
|
22652
|
-
*
|
|
22653
|
-
*
|
|
22654
|
-
* @param zod Zod schema.
|
|
22655
|
-
*
|
|
22656
|
-
* @returns Zod schema with camelCase properties.
|
|
23151
|
+
* CSS injection rule converter class
|
|
22657
23152
|
*
|
|
22658
|
-
* @
|
|
22659
|
-
*/
|
|
22660
|
-
const zodToCamelCase = zod => {
|
|
22661
|
-
return zod.transform(val => camelCaseKeys(val));
|
|
22662
|
-
};
|
|
22663
|
-
|
|
22664
|
-
/**
|
|
22665
|
-
* @file Base compatibility data schema, which is commonly used in compatibility tables.
|
|
22666
|
-
*/
|
|
22667
|
-
/**
|
|
22668
|
-
* Zod schema for boolean values. Accepts both boolean and string values.
|
|
22669
|
-
*/
|
|
22670
|
-
const booleanSchema = zod.union([zod.string().transform(val => val.trim().toLowerCase() === 'true'), zod.boolean()]);
|
|
22671
|
-
/**
|
|
22672
|
-
* Zod schema for non-empty string values.
|
|
22673
|
-
*/
|
|
22674
|
-
const nonEmptyStringSchema = zod.string().transform(val => val.trim()).pipe(zod.string().min(1));
|
|
22675
|
-
/**
|
|
22676
|
-
* Zod schema for base compatibility data.
|
|
22677
|
-
* Here we use snake_case properties because the compatibility data is stored in YAML files.
|
|
23153
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22678
23154
|
*/
|
|
22679
|
-
|
|
22680
|
-
/**
|
|
22681
|
-
* Name of the actual entity.
|
|
22682
|
-
*/
|
|
22683
|
-
name: nonEmptyStringSchema,
|
|
22684
|
-
/**
|
|
22685
|
-
* List of aliases for the entity (if any).
|
|
22686
|
-
*/
|
|
22687
|
-
aliases: zod.array(nonEmptyStringSchema).nullable().default(null),
|
|
22688
|
-
/**
|
|
22689
|
-
* Short description of the actual entity.
|
|
22690
|
-
* If not specified or it's value is `null`, then the description is not available.
|
|
22691
|
-
*/
|
|
22692
|
-
description: nonEmptyStringSchema.nullable().default(null),
|
|
22693
|
-
/**
|
|
22694
|
-
* Link to the documentation. If not specified or it's value is `null`, then the documentation is not available.
|
|
22695
|
-
*/
|
|
22696
|
-
docs: nonEmptyStringSchema.nullable().default(null),
|
|
22697
|
-
/**
|
|
22698
|
-
* The version of the adblocker in which the entity was added.
|
|
22699
|
-
* For AdGuard resources, the version of the library is specified.
|
|
22700
|
-
*/
|
|
22701
|
-
version_added: nonEmptyStringSchema.nullable().default(null),
|
|
22702
|
-
/**
|
|
22703
|
-
* The version of the adblocker when the entity was removed.
|
|
22704
|
-
*/
|
|
22705
|
-
version_removed: nonEmptyStringSchema.nullable().default(null),
|
|
22706
|
-
/**
|
|
22707
|
-
* Describes whether the entity is deprecated.
|
|
22708
|
-
*/
|
|
22709
|
-
deprecated: booleanSchema.default(false),
|
|
22710
|
-
/**
|
|
22711
|
-
* Message that describes why the entity is deprecated.
|
|
22712
|
-
* If not specified or it's value is `null`, then the message is not available.
|
|
22713
|
-
* It's value is omitted if the entity is not marked as deprecated.
|
|
22714
|
-
*/
|
|
22715
|
-
deprecation_message: nonEmptyStringSchema.nullable().default(null),
|
|
22716
|
-
/**
|
|
22717
|
-
* Describes whether the entity is removed; for *already removed* features.
|
|
22718
|
-
*/
|
|
22719
|
-
removed: booleanSchema.default(false),
|
|
23155
|
+
class CssInjectionRuleConverter extends RuleConverterBase {
|
|
22720
23156
|
/**
|
|
22721
|
-
*
|
|
22722
|
-
*
|
|
22723
|
-
*
|
|
23157
|
+
* Converts a CSS injection rule to AdGuard format, if possible.
|
|
23158
|
+
*
|
|
23159
|
+
* @param rule Rule node to convert
|
|
23160
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23161
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23162
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23163
|
+
* @throws If the rule is invalid or cannot be converted
|
|
22724
23164
|
*/
|
|
22725
|
-
|
|
22726
|
-
|
|
22727
|
-
|
|
22728
|
-
|
|
22729
|
-
|
|
22730
|
-
|
|
22731
|
-
|
|
22732
|
-
|
|
22733
|
-
|
|
22734
|
-
|
|
22735
|
-
|
|
22736
|
-
|
|
22737
|
-
|
|
22738
|
-
|
|
22739
|
-
|
|
22740
|
-
|
|
22741
|
-
|
|
22742
|
-
|
|
22743
|
-
|
|
22744
|
-
|
|
22745
|
-
|
|
22746
|
-
|
|
22747
|
-
|
|
22748
|
-
}
|
|
22749
|
-
|
|
22750
|
-
|
|
22751
|
-
ctx.addIssue({
|
|
22752
|
-
code: zod.ZodIssueCode.custom,
|
|
22753
|
-
message: 'Aliases must be unique'
|
|
22754
|
-
});
|
|
23165
|
+
static convertToAdg(rule) {
|
|
23166
|
+
const separator = rule.separator.value;
|
|
23167
|
+
let convertedSeparator = separator;
|
|
23168
|
+
const stream = new CssTokenStream(rule.body.selectorList.value);
|
|
23169
|
+
const convertedSelectorList = CssSelectorConverter.convertToAdg(stream);
|
|
23170
|
+
// Change the separator if the rule contains ExtendedCSS elements,
|
|
23171
|
+
// but do not force non-extended CSS separator if the rule does not contain any ExtendedCSS selectors,
|
|
23172
|
+
// because sometimes we use it to force executing ExtendedCSS library.
|
|
23173
|
+
if (stream.hasAnySelectorExtendedCssNodeStrict() || rule.body.remove) {
|
|
23174
|
+
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.AdgExtendedCssInjectionException : exports.CosmeticRuleSeparator.AdgExtendedCssInjection;
|
|
23175
|
+
} else if (rule.syntax !== exports.AdblockSyntax.Adg) {
|
|
23176
|
+
// If the original rule syntax is not AdGuard, use the default separator
|
|
23177
|
+
// e.g. if the input rule is from uBO, we need to convert ## to #$#.
|
|
23178
|
+
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.AdgCssInjectionException : exports.CosmeticRuleSeparator.AdgCssInjection;
|
|
23179
|
+
}
|
|
23180
|
+
// Check if the rule needs to be converted
|
|
23181
|
+
if (!(rule.syntax === exports.AdblockSyntax.Common || rule.syntax === exports.AdblockSyntax.Adg) || separator !== convertedSeparator || convertedSelectorList.isConverted) {
|
|
23182
|
+
// TODO: Replace with custom clone method
|
|
23183
|
+
const ruleClone = clone(rule);
|
|
23184
|
+
ruleClone.syntax = exports.AdblockSyntax.Adg;
|
|
23185
|
+
ruleClone.separator.value = convertedSeparator;
|
|
23186
|
+
ruleClone.body.selectorList.value = convertedSelectorList.result;
|
|
23187
|
+
return createNodeConversionResult([ruleClone], true);
|
|
23188
|
+
}
|
|
23189
|
+
// Otherwise, return the original rule
|
|
23190
|
+
return createNodeConversionResult([rule], false);
|
|
22755
23191
|
}
|
|
22756
|
-
}
|
|
23192
|
+
}
|
|
22757
23193
|
|
|
22758
23194
|
/**
|
|
22759
|
-
*
|
|
22760
|
-
*
|
|
22761
|
-
* @param error Error object.
|
|
22762
|
-
* @returns If param is error.
|
|
23195
|
+
* @file Element hiding rule converter
|
|
22763
23196
|
*/
|
|
22764
|
-
function isErrorWithMessage(error) {
|
|
22765
|
-
return typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string';
|
|
22766
|
-
}
|
|
22767
23197
|
/**
|
|
22768
|
-
*
|
|
23198
|
+
* Element hiding rule converter class
|
|
22769
23199
|
*
|
|
22770
|
-
* @
|
|
22771
|
-
* @returns Error with message.
|
|
23200
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22772
23201
|
*/
|
|
22773
|
-
|
|
22774
|
-
|
|
22775
|
-
|
|
22776
|
-
|
|
22777
|
-
|
|
22778
|
-
|
|
22779
|
-
|
|
22780
|
-
|
|
22781
|
-
|
|
22782
|
-
|
|
23202
|
+
class ElementHidingRuleConverter extends RuleConverterBase {
|
|
23203
|
+
/**
|
|
23204
|
+
* Converts an element hiding rule to AdGuard format, if possible.
|
|
23205
|
+
*
|
|
23206
|
+
* @param rule Rule node to convert
|
|
23207
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23208
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23209
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23210
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23211
|
+
*/
|
|
23212
|
+
static convertToAdg(rule) {
|
|
23213
|
+
const separator = rule.separator.value;
|
|
23214
|
+
let convertedSeparator = separator;
|
|
23215
|
+
const stream = new CssTokenStream(rule.body.selectorList.value);
|
|
23216
|
+
const convertedSelectorList = CssSelectorConverter.convertToAdg(stream);
|
|
23217
|
+
// Change the separator if the rule contains ExtendedCSS elements,
|
|
23218
|
+
// but do not force non-extended CSS separator if the rule does not contain any ExtendedCSS selectors,
|
|
23219
|
+
// because sometimes we use it to force executing ExtendedCSS library.
|
|
23220
|
+
if (stream.hasAnySelectorExtendedCssNodeStrict()) {
|
|
23221
|
+
convertedSeparator = rule.exception ? exports.CosmeticRuleSeparator.ExtendedElementHidingException : exports.CosmeticRuleSeparator.ExtendedElementHiding;
|
|
23222
|
+
}
|
|
23223
|
+
// Check if the rule needs to be converted
|
|
23224
|
+
if (!(rule.syntax === exports.AdblockSyntax.Common || rule.syntax === exports.AdblockSyntax.Adg) || separator !== convertedSeparator || convertedSelectorList.isConverted) {
|
|
23225
|
+
// TODO: Replace with custom clone method
|
|
23226
|
+
const ruleClone = clone(rule);
|
|
23227
|
+
ruleClone.syntax = exports.AdblockSyntax.Adg;
|
|
23228
|
+
ruleClone.separator.value = convertedSeparator;
|
|
23229
|
+
ruleClone.body.selectorList.value = convertedSelectorList.result;
|
|
23230
|
+
return createNodeConversionResult([ruleClone], true);
|
|
23231
|
+
}
|
|
23232
|
+
// Otherwise, return the original rule
|
|
23233
|
+
return createNodeConversionResult([rule], false);
|
|
22783
23234
|
}
|
|
22784
23235
|
}
|
|
22785
|
-
/**
|
|
22786
|
-
* Converts error object to error with message. This method might be helpful to handle thrown errors.
|
|
22787
|
-
*
|
|
22788
|
-
* @param error Error object.
|
|
22789
|
-
*
|
|
22790
|
-
* @returns Message of the error.
|
|
22791
|
-
*/
|
|
22792
|
-
function getErrorMessage(error) {
|
|
22793
|
-
return toErrorWithMessage(error).message;
|
|
22794
|
-
}
|
|
22795
23236
|
|
|
22796
23237
|
/**
|
|
22797
|
-
* @file
|
|
22798
|
-
*/
|
|
22799
|
-
/**
|
|
22800
|
-
* Known validators that don't need to be validated as regex.
|
|
23238
|
+
* @file Utility functions for working with network rule nodes
|
|
22801
23239
|
*/
|
|
22802
|
-
const KNOWN_VALIDATORS = new Set(['domain', 'pipe_separated_domains', 'regexp', 'url']);
|
|
22803
23240
|
/**
|
|
22804
|
-
*
|
|
23241
|
+
* Creates a network rule node
|
|
23242
|
+
*
|
|
23243
|
+
* @param pattern Rule pattern
|
|
23244
|
+
* @param modifiers Rule modifiers (optional, default: undefined)
|
|
23245
|
+
* @param exception Exception rule flag (optional, default: false)
|
|
23246
|
+
* @param syntax Adblock syntax (optional, default: Common)
|
|
23247
|
+
* @returns Network rule node
|
|
22805
23248
|
*/
|
|
22806
|
-
|
|
22807
|
-
|
|
22808
|
-
|
|
22809
|
-
|
|
22810
|
-
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
|
|
22814
|
-
|
|
22815
|
-
/**
|
|
22816
|
-
* Describes whether the actual modifier supports value assignment. For example, `$domain` is assignable,
|
|
22817
|
-
* so it can be used like this: `$domain=domain.com\|~subdomain.domain.com`, where `=` is the assignment operator
|
|
22818
|
-
* and `domain.com\|~subdomain.domain.com` is the value.
|
|
22819
|
-
*/
|
|
22820
|
-
assignable: booleanSchema.default(false),
|
|
22821
|
-
/**
|
|
22822
|
-
* Describes whether the actual modifier can be negated. For example, `$third-party` is negatable,
|
|
22823
|
-
* so it can be used like this: `$~third-party`.
|
|
22824
|
-
*/
|
|
22825
|
-
negatable: booleanSchema.default(true),
|
|
22826
|
-
/**
|
|
22827
|
-
* The actual modifier can only be used in blocking rules, it cannot be used in exceptions.
|
|
22828
|
-
* If it's value is `true`, then the modifier can be used only in blocking rules.
|
|
22829
|
-
* `exception_only` and `block_only` cannot be used together (they are mutually exclusive).
|
|
22830
|
-
*/
|
|
22831
|
-
block_only: booleanSchema.default(false),
|
|
22832
|
-
/**
|
|
22833
|
-
* The actual modifier can only be used in exceptions, it cannot be used in blocking rules.
|
|
22834
|
-
* If it's value is `true`, then the modifier can be used only in exceptions.
|
|
22835
|
-
* `exception_only` and `block_only` cannot be used together (they are mutually exclusive).
|
|
22836
|
-
*/
|
|
22837
|
-
exception_only: booleanSchema.default(false),
|
|
22838
|
-
/**
|
|
22839
|
-
* Describes whether the *assignable* modifier value is required.
|
|
22840
|
-
* For example, `$cookie` is assignable but it can be used without a value in exception rules:
|
|
22841
|
-
* `@@\|\|example.com^$cookie`.
|
|
22842
|
-
* If `false`, the `value_format` is required, e.g. the value of `$app` should always be specified
|
|
22843
|
-
*/
|
|
22844
|
-
value_optional: booleanSchema.default(false),
|
|
22845
|
-
/**
|
|
22846
|
-
* Describes the format of the value for the *assignable* modifier.
|
|
22847
|
-
* Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
|
|
22848
|
-
*/
|
|
22849
|
-
value_format: nonEmptyStringSchema.nullable().default(null)
|
|
22850
|
-
}).superRefine((data, ctx) => {
|
|
22851
|
-
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
22852
|
-
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
22853
|
-
baseRefineLogic(data, ctx);
|
|
22854
|
-
if (data.block_only && data.exception_only) {
|
|
22855
|
-
ctx.addIssue({
|
|
22856
|
-
code: zod.ZodIssueCode.custom,
|
|
22857
|
-
message: 'block_only and exception_only are mutually exclusive'
|
|
22858
|
-
});
|
|
22859
|
-
}
|
|
22860
|
-
if (data.assignable && !data.value_format) {
|
|
22861
|
-
ctx.addIssue({
|
|
22862
|
-
code: zod.ZodIssueCode.custom,
|
|
22863
|
-
message: 'value_format is required for assignable modifiers'
|
|
22864
|
-
});
|
|
22865
|
-
}
|
|
22866
|
-
if (data.value_format) {
|
|
22867
|
-
const valueFormat = data.value_format.trim();
|
|
22868
|
-
// if it is a known validator, we don't need to validate it further
|
|
22869
|
-
if (KNOWN_VALIDATORS.has(valueFormat)) {
|
|
22870
|
-
return;
|
|
22871
|
-
}
|
|
22872
|
-
// otherwise, we need to validate it as a regex
|
|
22873
|
-
try {
|
|
22874
|
-
XRegExp(valueFormat);
|
|
22875
|
-
} catch (error) {
|
|
22876
|
-
ctx.addIssue({
|
|
22877
|
-
code: zod.ZodIssueCode.custom,
|
|
22878
|
-
message: getErrorMessage(error)
|
|
22879
|
-
});
|
|
23249
|
+
function createNetworkRuleNode(pattern, modifiers = undefined, exception = false, syntax = exports.AdblockSyntax.Common) {
|
|
23250
|
+
const result = {
|
|
23251
|
+
category: exports.RuleCategory.Network,
|
|
23252
|
+
type: exports.NetworkRuleType.NetworkRule,
|
|
23253
|
+
syntax,
|
|
23254
|
+
exception,
|
|
23255
|
+
pattern: {
|
|
23256
|
+
type: 'Value',
|
|
23257
|
+
value: pattern
|
|
22880
23258
|
}
|
|
23259
|
+
};
|
|
23260
|
+
if (!isUndefined(modifiers)) {
|
|
23261
|
+
result.modifiers = clone(modifiers);
|
|
22881
23262
|
}
|
|
22882
|
-
|
|
23263
|
+
return result;
|
|
23264
|
+
}
|
|
22883
23265
|
|
|
22884
23266
|
/**
|
|
22885
|
-
* @file
|
|
23267
|
+
* @file Converter for request header removal rules
|
|
22886
23268
|
*/
|
|
23269
|
+
const UBO_RESPONSEHEADER_FN = 'responseheader';
|
|
23270
|
+
const ADG_REMOVEHEADER_MODIFIER = 'removeheader';
|
|
23271
|
+
const ERROR_MESSAGES = {
|
|
23272
|
+
EMPTY_PARAMETER: `Empty parameter for '${UBO_RESPONSEHEADER_FN}' function`,
|
|
23273
|
+
EXPECTED_END_OF_RULE: "Expected end of rule, but got '%s'",
|
|
23274
|
+
MULTIPLE_DOMAINS_NOT_SUPPORTED: 'Multiple domains are not supported yet'
|
|
23275
|
+
};
|
|
22887
23276
|
/**
|
|
22888
|
-
*
|
|
23277
|
+
* Converter for request header removal rules
|
|
23278
|
+
*
|
|
23279
|
+
* @todo Implement `convertToUbo` (ABP currently doesn't support header removal rules)
|
|
22889
23280
|
*/
|
|
22890
|
-
|
|
23281
|
+
class HeaderRemovalRuleConverter extends RuleConverterBase {
|
|
22891
23282
|
/**
|
|
22892
|
-
*
|
|
23283
|
+
* Converts a header removal rule to AdGuard syntax, if possible.
|
|
23284
|
+
*
|
|
23285
|
+
* @param rule Rule node to convert
|
|
23286
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23287
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23288
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23289
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23290
|
+
* @example
|
|
23291
|
+
* If the input rule is:
|
|
23292
|
+
* ```adblock
|
|
23293
|
+
* example.com##^responseheader(header-name)
|
|
23294
|
+
* ```
|
|
23295
|
+
* The output will be:
|
|
23296
|
+
* ```adblock
|
|
23297
|
+
* ||example.com^$removeheader=header-name
|
|
23298
|
+
* ```
|
|
22893
23299
|
*/
|
|
22894
|
-
|
|
22895
|
-
|
|
23300
|
+
static convertToAdg(rule) {
|
|
23301
|
+
// TODO: Add support for ABP syntax once it starts supporting header removal rules
|
|
23302
|
+
// Leave the rule as is if it's not a header removal rule
|
|
23303
|
+
if (rule.category !== exports.RuleCategory.Cosmetic || rule.type !== exports.CosmeticRuleType.HtmlFilteringRule) {
|
|
23304
|
+
return createNodeConversionResult([rule], false);
|
|
23305
|
+
}
|
|
23306
|
+
const stream = new CssTokenStream(rule.body.value);
|
|
23307
|
+
let token;
|
|
23308
|
+
// Skip leading whitespace
|
|
23309
|
+
stream.skipWhitespace();
|
|
23310
|
+
// Next token should be the `^` followed by a `responseheader` function
|
|
23311
|
+
token = stream.get();
|
|
23312
|
+
if (!token || token.type !== cssTokenizer.TokenType.Delim || rule.body.value[token.start] !== UBO_HTML_MASK) {
|
|
23313
|
+
return createNodeConversionResult([rule], false);
|
|
23314
|
+
}
|
|
23315
|
+
stream.advance();
|
|
23316
|
+
token = stream.get();
|
|
23317
|
+
if (!token) {
|
|
23318
|
+
return createNodeConversionResult([rule], false);
|
|
23319
|
+
}
|
|
23320
|
+
const functionName = rule.body.value.slice(token.start, token.end - 1);
|
|
23321
|
+
if (functionName !== UBO_RESPONSEHEADER_FN) {
|
|
23322
|
+
return createNodeConversionResult([rule], false);
|
|
23323
|
+
}
|
|
23324
|
+
// Parse the parameter
|
|
23325
|
+
const paramStart = token.end;
|
|
23326
|
+
stream.skipUntilBalanced();
|
|
23327
|
+
const paramEnd = stream.getOrFail().end;
|
|
23328
|
+
const param = rule.body.value.slice(paramStart, paramEnd - 1).trim();
|
|
23329
|
+
// Do not allow empty parameter
|
|
23330
|
+
if (param.length === 0) {
|
|
23331
|
+
throw new RuleConversionError(ERROR_MESSAGES.EMPTY_PARAMETER);
|
|
23332
|
+
}
|
|
23333
|
+
stream.expect(cssTokenizer.TokenType.CloseParenthesis);
|
|
23334
|
+
stream.advance();
|
|
23335
|
+
// Skip trailing whitespace after the function call
|
|
23336
|
+
stream.skipWhitespace();
|
|
23337
|
+
// Expect the end of the rule - so nothing should be left in the stream
|
|
23338
|
+
if (!stream.isEof()) {
|
|
23339
|
+
token = stream.getOrFail();
|
|
23340
|
+
throw new RuleConversionError(sprintfJs.sprintf(ERROR_MESSAGES.EXPECTED_END_OF_RULE, cssTokenizer.getFormattedTokenName(token.type)));
|
|
23341
|
+
}
|
|
23342
|
+
// Prepare network rule pattern
|
|
23343
|
+
const pattern = [];
|
|
23344
|
+
if (rule.domains.children.length === 1) {
|
|
23345
|
+
// If the rule has only one domain, we can use a simple network rule pattern:
|
|
23346
|
+
// ||single-domain-from-the-rule^
|
|
23347
|
+
pattern.push(ADBLOCK_URL_START, rule.domains.children[0].value, ADBLOCK_URL_SEPARATOR);
|
|
23348
|
+
} else if (rule.domains.children.length > 1) {
|
|
23349
|
+
// TODO: Add support for multiple domains, for example:
|
|
23350
|
+
// example.com,example.org,example.net##^responseheader(header-name)
|
|
23351
|
+
// We should consider allowing $domain with $removeheader modifier,
|
|
23352
|
+
// for example:
|
|
23353
|
+
// $removeheader=header-name,domain=example.com|example.org|example.net
|
|
23354
|
+
throw new RuleConversionError(ERROR_MESSAGES.MULTIPLE_DOMAINS_NOT_SUPPORTED);
|
|
23355
|
+
}
|
|
23356
|
+
// Prepare network rule modifiers
|
|
23357
|
+
const modifiers = createModifierListNode();
|
|
23358
|
+
modifiers.children.push(createModifierNode(ADG_REMOVEHEADER_MODIFIER, param));
|
|
23359
|
+
// Construct the network rule
|
|
23360
|
+
return createNodeConversionResult([createNetworkRuleNode(pattern.join(EMPTY), modifiers,
|
|
23361
|
+
// Copy the exception flag
|
|
23362
|
+
rule.exception, exports.AdblockSyntax.Adg)], true);
|
|
23363
|
+
}
|
|
23364
|
+
}
|
|
22896
23365
|
|
|
22897
23366
|
/**
|
|
22898
|
-
* @file
|
|
22899
|
-
*/
|
|
22900
|
-
/**
|
|
22901
|
-
* Zod schema for scriptlet parameter data.
|
|
22902
|
-
*/
|
|
22903
|
-
const scriptletParameterSchema = zod.object({
|
|
22904
|
-
/**
|
|
22905
|
-
* Name of the actual parameter.
|
|
22906
|
-
*/
|
|
22907
|
-
name: nonEmptyStringSchema,
|
|
22908
|
-
/**
|
|
22909
|
-
* Describes whether the parameter is required. Empty parameters are not allowed.
|
|
22910
|
-
*/
|
|
22911
|
-
required: booleanSchema,
|
|
22912
|
-
/**
|
|
22913
|
-
* Short description of the parameter.
|
|
22914
|
-
* If not specified or it's value is `null`,then the description is not available.
|
|
22915
|
-
*/
|
|
22916
|
-
description: nonEmptyStringSchema.nullable().default(null),
|
|
22917
|
-
/**
|
|
22918
|
-
* Regular expression that matches the value of the parameter.
|
|
22919
|
-
* If it's value is `null`, then the parameter value is not checked.
|
|
22920
|
-
*/
|
|
22921
|
-
pattern: nonEmptyStringSchema.nullable().default(null),
|
|
22922
|
-
/**
|
|
22923
|
-
* Default value of the parameter (if any).
|
|
22924
|
-
*/
|
|
22925
|
-
default: nonEmptyStringSchema.nullable().default(null),
|
|
22926
|
-
/**
|
|
22927
|
-
* Describes whether the parameter is used only for debugging purposes.
|
|
22928
|
-
*/
|
|
22929
|
-
debug: booleanSchema.default(false)
|
|
22930
|
-
});
|
|
22931
|
-
/**
|
|
22932
|
-
* Zod schema for scriptlet parameters.
|
|
23367
|
+
* @file Cosmetic rule converter
|
|
22933
23368
|
*/
|
|
22934
|
-
const scriptletParametersSchema = zod.array(scriptletParameterSchema);
|
|
22935
23369
|
/**
|
|
22936
|
-
*
|
|
23370
|
+
* Cosmetic rule converter class (also known as "non-basic rule converter")
|
|
23371
|
+
*
|
|
23372
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22937
23373
|
*/
|
|
22938
|
-
|
|
23374
|
+
class CosmeticRuleConverter extends RuleConverterBase {
|
|
22939
23375
|
/**
|
|
22940
|
-
*
|
|
22941
|
-
*
|
|
22942
|
-
*
|
|
23376
|
+
* Converts a cosmetic rule to AdGuard syntax, if possible.
|
|
23377
|
+
*
|
|
23378
|
+
* @param rule Rule node to convert
|
|
23379
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23380
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23381
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23382
|
+
* @throws If the rule is invalid or cannot be converted
|
|
22943
23383
|
*/
|
|
22944
|
-
|
|
22945
|
-
|
|
22946
|
-
|
|
22947
|
-
|
|
22948
|
-
|
|
22949
|
-
|
|
22950
|
-
|
|
22951
|
-
|
|
22952
|
-
|
|
22953
|
-
|
|
22954
|
-
|
|
22955
|
-
|
|
22956
|
-
|
|
22957
|
-
|
|
22958
|
-
|
|
23384
|
+
static convertToAdg(rule) {
|
|
23385
|
+
let subconverterResult;
|
|
23386
|
+
// Convert cosmetic rule based on its type
|
|
23387
|
+
switch (rule.type) {
|
|
23388
|
+
case exports.CosmeticRuleType.ElementHidingRule:
|
|
23389
|
+
subconverterResult = ElementHidingRuleConverter.convertToAdg(rule);
|
|
23390
|
+
break;
|
|
23391
|
+
case exports.CosmeticRuleType.ScriptletInjectionRule:
|
|
23392
|
+
subconverterResult = ScriptletRuleConverter.convertToAdg(rule);
|
|
23393
|
+
break;
|
|
23394
|
+
case exports.CosmeticRuleType.CssInjectionRule:
|
|
23395
|
+
subconverterResult = CssInjectionRuleConverter.convertToAdg(rule);
|
|
23396
|
+
break;
|
|
23397
|
+
case exports.CosmeticRuleType.HtmlFilteringRule:
|
|
23398
|
+
// Handle special case: uBO response header filtering rule
|
|
23399
|
+
// TODO: Optimize double CSS tokenization here
|
|
23400
|
+
subconverterResult = HeaderRemovalRuleConverter.convertToAdg(rule);
|
|
23401
|
+
if (subconverterResult.isConverted) {
|
|
23402
|
+
break;
|
|
23403
|
+
}
|
|
23404
|
+
subconverterResult = HtmlRuleConverter.convertToAdg(rule);
|
|
23405
|
+
break;
|
|
23406
|
+
// Note: Currently, only ADG supports JS injection rules, so we don't need to convert them
|
|
23407
|
+
case exports.CosmeticRuleType.JsInjectionRule:
|
|
23408
|
+
subconverterResult = createNodeConversionResult([rule], false);
|
|
23409
|
+
break;
|
|
23410
|
+
default:
|
|
23411
|
+
throw new RuleConversionError('Unsupported cosmetic rule type');
|
|
23412
|
+
}
|
|
23413
|
+
let convertedModifiers;
|
|
23414
|
+
// Convert cosmetic rule modifiers, if any
|
|
23415
|
+
if (rule.modifiers) {
|
|
23416
|
+
if (rule.syntax === exports.AdblockSyntax.Ubo) {
|
|
23417
|
+
// uBO doesn't support this rule:
|
|
23418
|
+
// example.com##+js(set-constant.js, foo, bar):matches-path(/baz)
|
|
23419
|
+
if (rule.type === exports.CosmeticRuleType.ScriptletInjectionRule) {
|
|
23420
|
+
throw new RuleConversionError('uBO scriptlet injection rules don\'t support cosmetic rule modifiers');
|
|
23421
|
+
}
|
|
23422
|
+
convertedModifiers = AdgCosmeticRuleModifierConverter.convertFromUbo(rule.modifiers);
|
|
23423
|
+
} else if (rule.syntax === exports.AdblockSyntax.Abp) {
|
|
23424
|
+
// TODO: Implement once ABP starts supporting cosmetic rule modifiers
|
|
23425
|
+
throw new RuleConversionError('ABP don\'t support cosmetic rule modifiers');
|
|
23426
|
+
}
|
|
23427
|
+
}
|
|
23428
|
+
if (subconverterResult.result.length > 1 || subconverterResult.isConverted || convertedModifiers && convertedModifiers.isConverted) {
|
|
23429
|
+
// Add modifier list to the subconverter result rules
|
|
23430
|
+
subconverterResult.result.forEach(subconverterRule => {
|
|
23431
|
+
if (convertedModifiers && subconverterRule.category === exports.RuleCategory.Cosmetic) {
|
|
23432
|
+
// eslint-disable-next-line no-param-reassign
|
|
23433
|
+
subconverterRule.modifiers = convertedModifiers.result;
|
|
23434
|
+
}
|
|
22959
23435
|
});
|
|
23436
|
+
return subconverterResult;
|
|
22960
23437
|
}
|
|
22961
|
-
|
|
22962
|
-
|
|
23438
|
+
return createNodeConversionResult([rule], false);
|
|
23439
|
+
}
|
|
23440
|
+
/**
|
|
23441
|
+
* Converts a cosmetic rule to uBlock Origin syntax, if possible.
|
|
23442
|
+
*
|
|
23443
|
+
* @param rule Rule node to convert
|
|
23444
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23445
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23446
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23447
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23448
|
+
*/
|
|
23449
|
+
// TODO: Add support for other cosmetic rule types
|
|
23450
|
+
static convertToUbo(rule) {
|
|
23451
|
+
// Convert cosmetic rule based on its type
|
|
23452
|
+
if (rule.type === exports.CosmeticRuleType.ScriptletInjectionRule) {
|
|
23453
|
+
if (rule.syntax === exports.AdblockSyntax.Adg && rule.modifiers?.children.length) {
|
|
23454
|
+
// e.g. example.com##+js(set-constant.js, foo, bar):matches-path(/baz)
|
|
23455
|
+
throw new RuleConversionError('uBO scriptlet injection rules do not support cosmetic rule modifiers');
|
|
23456
|
+
}
|
|
23457
|
+
return ScriptletRuleConverter.convertToUbo(rule);
|
|
22963
23458
|
}
|
|
23459
|
+
return createNodeConversionResult([rule], false);
|
|
22964
23460
|
}
|
|
22965
|
-
}
|
|
23461
|
+
}
|
|
22966
23462
|
|
|
22967
23463
|
/**
|
|
22968
23464
|
* @file Network rule modifier list converter.
|
|
@@ -22989,6 +23485,10 @@ const REDIRECT_MODIFIER = 'redirect';
|
|
|
22989
23485
|
* @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#redirect-rule-modifier}
|
|
22990
23486
|
*/
|
|
22991
23487
|
const REDIRECT_RULE_MODIFIER = 'redirect-rule';
|
|
23488
|
+
/**
|
|
23489
|
+
* @see {@link https://github.com/gorhill/uBlock/wiki/Resources-Library#empty-redirect-resources}
|
|
23490
|
+
*/
|
|
23491
|
+
const UBO_NOOP_TEXT_RESOURCE = 'noop.txt';
|
|
22992
23492
|
/**
|
|
22993
23493
|
* Redirect-related modifiers.
|
|
22994
23494
|
*/
|
|
@@ -23138,6 +23638,112 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
23138
23638
|
}
|
|
23139
23639
|
return createConversionResult(modifierList, false);
|
|
23140
23640
|
}
|
|
23641
|
+
/**
|
|
23642
|
+
* Converts a network rule modifier list to uBlock format, if possible.
|
|
23643
|
+
*
|
|
23644
|
+
* @param modifierList Network rule modifier list node to convert
|
|
23645
|
+
* @param isException If `true`, the rule is an exception rule
|
|
23646
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
23647
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
23648
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
23649
|
+
* @throws If the conversion is not possible
|
|
23650
|
+
*/
|
|
23651
|
+
// TODO: Optimize
|
|
23652
|
+
static convertToUbo(modifierList, isException = false) {
|
|
23653
|
+
const conversionMap = new MultiValueMap();
|
|
23654
|
+
const resourceTypeModifiersToAdd = new Set();
|
|
23655
|
+
modifierList.children.forEach((modifierNode, index) => {
|
|
23656
|
+
const originalModifierName = modifierNode.name.value;
|
|
23657
|
+
const modifierData = modifiersCompatibilityTable.getFirst(originalModifierName, exports.GenericPlatform.UboAny);
|
|
23658
|
+
// Handle special case: resource redirection modifiers
|
|
23659
|
+
if (REDIRECT_MODIFIERS.has(originalModifierName)) {
|
|
23660
|
+
// Redirect modifiers cannot be negated
|
|
23661
|
+
if (modifierNode.exception === true) {
|
|
23662
|
+
throw new RuleConversionError(`Modifier '${modifierNode.name.value}' cannot be negated`);
|
|
23663
|
+
}
|
|
23664
|
+
// Convert the redirect resource name to uBO format
|
|
23665
|
+
const redirectResourceName = modifierNode.value?.value;
|
|
23666
|
+
// Special case: for exception rules, $redirect without value is allowed,
|
|
23667
|
+
// and in this case it means an exception for all redirects
|
|
23668
|
+
if (!redirectResourceName && !isException) {
|
|
23669
|
+
throw new RuleConversionError(`No redirect resource specified for '${modifierNode.name.value}' modifier`);
|
|
23670
|
+
}
|
|
23671
|
+
if (!redirectResourceName) {
|
|
23672
|
+
// Jump to the next modifier if the redirect resource is not specified
|
|
23673
|
+
return;
|
|
23674
|
+
}
|
|
23675
|
+
// Leave $redirect and $redirect-rule modifiers as is, but convert $rewrite to $redirect
|
|
23676
|
+
const modifierName = modifierNode.name.value === ABP_REWRITE_MODIFIER ? REDIRECT_MODIFIER : modifierNode.name.value;
|
|
23677
|
+
const convertedRedirectResourceData = redirectsCompatibilityTable.getFirst(redirectResourceName, exports.GenericPlatform.UboAny);
|
|
23678
|
+
const convertedRedirectResourceName = convertedRedirectResourceData?.name ?? redirectResourceName;
|
|
23679
|
+
// uBlock requires the $redirect modifier to have a resource type
|
|
23680
|
+
// https://github.com/AdguardTeam/Scriptlets/issues/101
|
|
23681
|
+
if (convertedRedirectResourceData?.resourceTypes?.length) {
|
|
23682
|
+
// Convert the resource types to uBO modifiers
|
|
23683
|
+
const uboResourceTypeModifiers = redirectsCompatibilityTable.getResourceTypeModifiers(convertedRedirectResourceData, exports.GenericPlatform.UboAny);
|
|
23684
|
+
// Special case: noop text resource
|
|
23685
|
+
// If any of resource type is already present, we don't need to add other resource types,
|
|
23686
|
+
// otherwise, add all resource types
|
|
23687
|
+
// TODO: Optimize this logic
|
|
23688
|
+
// Check if the current resource is the noop text resource
|
|
23689
|
+
const isNoopTextResource = convertedRedirectResourceName === UBO_NOOP_TEXT_RESOURCE;
|
|
23690
|
+
// Determine if there are any valid resource types already present
|
|
23691
|
+
const hasValidResourceType = modifierList.children.some(modifier => {
|
|
23692
|
+
const name = modifier.name.value;
|
|
23693
|
+
if (!isValidResourceType(name)) {
|
|
23694
|
+
return false;
|
|
23695
|
+
}
|
|
23696
|
+
const convertedModifierData = modifiersCompatibilityTable.getFirst(name, exports.GenericPlatform.UboAny);
|
|
23697
|
+
return uboResourceTypeModifiers.has(convertedModifierData?.name ?? name);
|
|
23698
|
+
});
|
|
23699
|
+
// If it's not the noop text resource or if no valid resource types are present
|
|
23700
|
+
if (!isNoopTextResource || !hasValidResourceType) {
|
|
23701
|
+
uboResourceTypeModifiers.forEach(resourceType => {
|
|
23702
|
+
resourceTypeModifiersToAdd.add(resourceType);
|
|
23703
|
+
});
|
|
23704
|
+
}
|
|
23705
|
+
}
|
|
23706
|
+
// Check if the modifier name or the redirect resource name is different from the original modifier.
|
|
23707
|
+
// If so, add the converted modifier to the list
|
|
23708
|
+
if (modifierName !== originalModifierName || !isUndefined(convertedRedirectResourceName) && convertedRedirectResourceName !== redirectResourceName) {
|
|
23709
|
+
conversionMap.add(index, createModifierNode(modifierName,
|
|
23710
|
+
// If the redirect resource name is unknown, fall back to the original one
|
|
23711
|
+
// Later, the validator will throw an error if the resource name is invalid
|
|
23712
|
+
convertedRedirectResourceName || redirectResourceName, modifierNode.exception));
|
|
23713
|
+
}
|
|
23714
|
+
return;
|
|
23715
|
+
}
|
|
23716
|
+
// Generic modifier conversion
|
|
23717
|
+
if (modifierData && modifierData.name !== originalModifierName) {
|
|
23718
|
+
conversionMap.add(index, createModifierNode(modifierData.name, modifierNode.value?.value, modifierNode.exception));
|
|
23719
|
+
}
|
|
23720
|
+
});
|
|
23721
|
+
// Prepare the result if there are any converted modifiers or $csp modifiers
|
|
23722
|
+
if (conversionMap.size || resourceTypeModifiersToAdd.size) {
|
|
23723
|
+
const modifierListClone = cloneModifierListNode(modifierList);
|
|
23724
|
+
// Replace the original modifiers with the converted ones
|
|
23725
|
+
// One modifier may be replaced with multiple modifiers, so we need to flatten the array
|
|
23726
|
+
modifierListClone.children = modifierListClone.children.map((modifierNode, index) => {
|
|
23727
|
+
const conversionRecord = conversionMap.get(index);
|
|
23728
|
+
if (conversionRecord) {
|
|
23729
|
+
return conversionRecord;
|
|
23730
|
+
}
|
|
23731
|
+
return modifierNode;
|
|
23732
|
+
}).flat();
|
|
23733
|
+
// Before returning the result, remove duplicated modifiers
|
|
23734
|
+
modifierListClone.children = modifierListClone.children.filter((modifierNode, index, self) => self.findIndex(m => m.name.value === modifierNode.name.value && m.exception === modifierNode.exception && m.value?.value === modifierNode.value?.value) === index);
|
|
23735
|
+
if (resourceTypeModifiersToAdd.size) {
|
|
23736
|
+
const modifierNameSet = new Set(modifierList.children.map(m => m.name.value));
|
|
23737
|
+
resourceTypeModifiersToAdd.forEach(resourceType => {
|
|
23738
|
+
if (!modifierNameSet.has(resourceType)) {
|
|
23739
|
+
modifierListClone.children.push(createModifierNode(resourceType));
|
|
23740
|
+
}
|
|
23741
|
+
});
|
|
23742
|
+
}
|
|
23743
|
+
return createConversionResult(modifierListClone, true);
|
|
23744
|
+
}
|
|
23745
|
+
return createConversionResult(modifierList, false);
|
|
23746
|
+
}
|
|
23141
23747
|
}
|
|
23142
23748
|
|
|
23143
23749
|
/**
|
|
@@ -23187,6 +23793,44 @@ class NetworkRuleConverter extends RuleConverterBase {
|
|
|
23187
23793
|
// If the modifiers were not converted, return the original rule
|
|
23188
23794
|
return createNodeConversionResult([rule], false);
|
|
23189
23795
|
}
|
|
23796
|
+
/**
|
|
23797
|
+
* Converts a network rule to uBlock format, if possible.
|
|
23798
|
+
*
|
|
23799
|
+
* @param rule Rule node to convert
|
|
23800
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23801
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23802
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23803
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23804
|
+
*/
|
|
23805
|
+
static convertToUbo(rule) {
|
|
23806
|
+
// TODO: add support for host rules
|
|
23807
|
+
if (rule.type !== exports.NetworkRuleType.NetworkRule) {
|
|
23808
|
+
throw new Error(`Invalid rule type: ${rule.type}`);
|
|
23809
|
+
}
|
|
23810
|
+
if (rule.modifiers) {
|
|
23811
|
+
const modifiers = NetworkRuleModifierListConverter.convertToUbo(rule.modifiers, rule.exception);
|
|
23812
|
+
// If the object reference is different, it means that the modifiers were converted
|
|
23813
|
+
// In this case, we should clone the entire rule and replace the modifiers with the converted ones
|
|
23814
|
+
if (modifiers.isConverted) {
|
|
23815
|
+
return {
|
|
23816
|
+
result: [{
|
|
23817
|
+
category: exports.RuleCategory.Network,
|
|
23818
|
+
type: exports.NetworkRuleType.NetworkRule,
|
|
23819
|
+
syntax: rule.syntax,
|
|
23820
|
+
exception: rule.exception,
|
|
23821
|
+
pattern: {
|
|
23822
|
+
type: 'Value',
|
|
23823
|
+
value: rule.pattern.value
|
|
23824
|
+
},
|
|
23825
|
+
modifiers: modifiers.result
|
|
23826
|
+
}],
|
|
23827
|
+
isConverted: true
|
|
23828
|
+
};
|
|
23829
|
+
}
|
|
23830
|
+
}
|
|
23831
|
+
// If the modifiers were not converted, return the original rule
|
|
23832
|
+
return createNodeConversionResult([rule], false);
|
|
23833
|
+
}
|
|
23190
23834
|
}
|
|
23191
23835
|
|
|
23192
23836
|
/**
|
|
@@ -23234,6 +23878,25 @@ class RuleConverter extends RuleConverterBase {
|
|
|
23234
23878
|
throw new RuleConversionError('Unknown rule category');
|
|
23235
23879
|
}
|
|
23236
23880
|
}
|
|
23881
|
+
/**
|
|
23882
|
+
* Converts an adblock filtering rule to uBlock Origin format, if possible.
|
|
23883
|
+
*
|
|
23884
|
+
* @param rule Rule node to convert
|
|
23885
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23886
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23887
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23888
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23889
|
+
*/
|
|
23890
|
+
// TODO: Add support for other rule types
|
|
23891
|
+
static convertToUbo(rule) {
|
|
23892
|
+
if (rule.category === exports.RuleCategory.Cosmetic) {
|
|
23893
|
+
return CosmeticRuleConverter.convertToUbo(rule);
|
|
23894
|
+
}
|
|
23895
|
+
if (rule.category === exports.RuleCategory.Network) {
|
|
23896
|
+
return NetworkRuleConverter.convertToUbo(rule);
|
|
23897
|
+
}
|
|
23898
|
+
return createConversionResult([rule], false);
|
|
23899
|
+
}
|
|
23237
23900
|
}
|
|
23238
23901
|
|
|
23239
23902
|
/**
|
|
@@ -23627,7 +24290,7 @@ class ByteBuffer {
|
|
|
23627
24290
|
* @see {@link https://stackoverflow.com/a/62797156}
|
|
23628
24291
|
*/
|
|
23629
24292
|
const isChromium = () => {
|
|
23630
|
-
return typeof window !== 'undefined' && (Object.prototype.hasOwnProperty.call(window, 'chrome') || typeof window.navigator !== 'undefined' && /chrome/i.test(window.navigator.userAgent
|
|
24293
|
+
return typeof window !== 'undefined' && (Object.prototype.hasOwnProperty.call(window, 'chrome') || typeof window.navigator !== 'undefined' && /chrome/i.test(window.navigator.userAgent));
|
|
23631
24294
|
};
|
|
23632
24295
|
|
|
23633
24296
|
/* eslint-disable no-param-reassign */
|
|
@@ -23718,16 +24381,23 @@ class OutputByteBuffer extends ByteBuffer {
|
|
|
23718
24381
|
*/
|
|
23719
24382
|
offset;
|
|
23720
24383
|
/**
|
|
23721
|
-
* Size of the shared buffer for encoding strings.
|
|
24384
|
+
* Size of the shared buffer for encoding strings in bytes.
|
|
24385
|
+
* This is a divisor of ByteBuffer.CHUNK_SIZE and experience shows that this value works optimally.
|
|
24386
|
+
* This is sufficient for most strings that occur in filter lists (we checked average string length in popular
|
|
24387
|
+
* filter lists).
|
|
23722
24388
|
*/
|
|
23723
24389
|
static ENCODER_BUFFER_SIZE = 8192;
|
|
23724
24390
|
/**
|
|
23725
|
-
*
|
|
24391
|
+
* Length threshold for using a shared buffer for encoding strings.
|
|
24392
|
+
* This temp buffer is needed because we write the short strings in it
|
|
24393
|
+
* (so there is no need to constantly allocate a new buffer).
|
|
24394
|
+
* The reason for dividing ENCODER_BUFFER_SIZE by 4 is to ensure that the encoded string fits in the buffer,
|
|
24395
|
+
* if we also take into account the worst possible case (each character is encoded with 4 bytes).
|
|
23726
24396
|
*/
|
|
23727
24397
|
static SHORT_STRING_THRESHOLD = 2048; // 8192 / 4
|
|
23728
24398
|
/**
|
|
23729
24399
|
* Represents the maximum value that can be written as a 'storage optimized' unsigned integer.
|
|
23730
|
-
* 0x1FFFFFFF means 32 bits minus 3 bits
|
|
24400
|
+
* 0x1FFFFFFF means 29 bits — 32 bits minus 3 bits — because the last bit in each byte is a flag indicating
|
|
23731
24401
|
* if there are more bytes (except for the last byte).
|
|
23732
24402
|
*/
|
|
23733
24403
|
static MAX_OPTIMIZED_UINT = 0x1FFFFFFF;
|
|
@@ -24274,7 +24944,7 @@ class RuleCategorizer {
|
|
|
24274
24944
|
}
|
|
24275
24945
|
}
|
|
24276
24946
|
}
|
|
24277
|
-
const version = "2.0.
|
|
24947
|
+
const version = "2.0.1";
|
|
24278
24948
|
|
|
24279
24949
|
/**
|
|
24280
24950
|
* @file AGTree version
|
|
@@ -24355,8 +25025,10 @@ exports.decodeTextPolyfill = decodeTextPolyfill;
|
|
|
24355
25025
|
exports.defaultParserOptions = defaultParserOptions;
|
|
24356
25026
|
exports.encodeIntoPolyfill = encodeIntoPolyfill;
|
|
24357
25027
|
exports.getPlatformId = getPlatformId;
|
|
25028
|
+
exports.getResourceTypeModifier = getResourceTypeModifier;
|
|
24358
25029
|
exports.getSpecificPlatformName = getSpecificPlatformName;
|
|
24359
25030
|
exports.isGenericPlatform = isGenericPlatform;
|
|
25031
|
+
exports.isValidResourceType = isValidResourceType;
|
|
24360
25032
|
exports.modifierValidator = modifierValidator;
|
|
24361
25033
|
exports.modifiersCompatibilityTable = modifiersCompatibilityTable;
|
|
24362
25034
|
exports.parseRawPlatforms = parseRawPlatforms;
|