@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.esm.js
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
|
|
@@ -192,6 +192,7 @@ const NUMBERS = new Set([NUMBER_0, NUMBER_1, NUMBER_2, NUMBER_3, NUMBER_4, NUMBE
|
|
|
192
192
|
const REGEX_MARKER = '/';
|
|
193
193
|
const ADG_SCRIPTLET_MASK = '//scriptlet';
|
|
194
194
|
const UBO_SCRIPTLET_MASK = '+js';
|
|
195
|
+
const UBO_SCRIPTLET_MASK_LEGACY = 'script:inject';
|
|
195
196
|
const UBO_HTML_MASK = '^';
|
|
196
197
|
// Modifiers are separated by ",". For example: "script,domain=example.com"
|
|
197
198
|
const MODIFIERS_SEPARATOR = ',';
|
|
@@ -743,6 +744,15 @@ class StringUtils {
|
|
|
743
744
|
}
|
|
744
745
|
}
|
|
745
746
|
|
|
747
|
+
/**
|
|
748
|
+
* Possible operators in the logical expression.
|
|
749
|
+
*/
|
|
750
|
+
var OperatorValue;
|
|
751
|
+
(function (OperatorValue) {
|
|
752
|
+
OperatorValue["Not"] = "!";
|
|
753
|
+
OperatorValue["And"] = "&&";
|
|
754
|
+
OperatorValue["Or"] = "||";
|
|
755
|
+
})(OperatorValue || (OperatorValue = {}));
|
|
746
756
|
/**
|
|
747
757
|
* Represents the different comment markers that can be used in an adblock rule.
|
|
748
758
|
*
|
|
@@ -1119,6 +1129,15 @@ const isUndefined = value => {
|
|
|
1119
1129
|
const isNull = value => {
|
|
1120
1130
|
return value === null;
|
|
1121
1131
|
};
|
|
1132
|
+
/**
|
|
1133
|
+
* Checks whether the given value is a string.
|
|
1134
|
+
*
|
|
1135
|
+
* @param value Value to check.
|
|
1136
|
+
* @returns `true` if the value is a string, `false` otherwise.
|
|
1137
|
+
*/
|
|
1138
|
+
const isString = value => {
|
|
1139
|
+
return typeof value === 'string';
|
|
1140
|
+
};
|
|
1122
1141
|
/**
|
|
1123
1142
|
* Checks whether the given value is an array of Uint8Arrays.
|
|
1124
1143
|
*
|
|
@@ -2938,15 +2957,6 @@ var ParenthesisNodeBinaryPropMap;
|
|
|
2938
2957
|
ParenthesisNodeBinaryPropMap[ParenthesisNodeBinaryPropMap["Start"] = 2] = "Start";
|
|
2939
2958
|
ParenthesisNodeBinaryPropMap[ParenthesisNodeBinaryPropMap["End"] = 3] = "End";
|
|
2940
2959
|
})(ParenthesisNodeBinaryPropMap || (ParenthesisNodeBinaryPropMap = {}));
|
|
2941
|
-
/**
|
|
2942
|
-
* Possible operators in the logical expression.
|
|
2943
|
-
*/
|
|
2944
|
-
var OperatorValue;
|
|
2945
|
-
(function (OperatorValue) {
|
|
2946
|
-
OperatorValue["Not"] = "!";
|
|
2947
|
-
OperatorValue["And"] = "&&";
|
|
2948
|
-
OperatorValue["Or"] = "||";
|
|
2949
|
-
})(OperatorValue || (OperatorValue = {}));
|
|
2950
2960
|
/**
|
|
2951
2961
|
* Possible token types in the logical expression.
|
|
2952
2962
|
*/
|
|
@@ -2992,7 +3002,9 @@ const getOperatorOrFail = binary => {
|
|
|
2992
3002
|
/**
|
|
2993
3003
|
* Serialization map for known variables.
|
|
2994
3004
|
*/
|
|
2995
|
-
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]
|
|
3005
|
+
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]
|
|
3006
|
+
// TODO: Add 'adguard_ext_chromium_mv3' to the list
|
|
3007
|
+
]);
|
|
2996
3008
|
/**
|
|
2997
3009
|
* Deserialization map for known variables.
|
|
2998
3010
|
*/
|
|
@@ -5696,7 +5708,7 @@ class CssTokenStream {
|
|
|
5696
5708
|
* @returns An array containing the number of tokens skipped and the number of tokens skipped without leading and
|
|
5697
5709
|
* trailing whitespace tokens.
|
|
5698
5710
|
*/
|
|
5699
|
-
|
|
5711
|
+
skipUntilExt(type, balance) {
|
|
5700
5712
|
let i = this.index;
|
|
5701
5713
|
let firstNonWsToken = -1; // -1 means no non-whitespace token found yet
|
|
5702
5714
|
let lastNonWsToken = -1; // -1 means no non-whitespace token found yet
|
|
@@ -5924,7 +5936,7 @@ class AdgCssInjectionParser extends ParserBase {
|
|
|
5924
5936
|
// └ this one
|
|
5925
5937
|
const {
|
|
5926
5938
|
skippedTrimmed: selectorTokensLength
|
|
5927
|
-
} = stream.
|
|
5939
|
+
} = stream.skipUntilExt(TokenType$1.OpenCurlyBracket, balanceShift + 1);
|
|
5928
5940
|
stream.expect(TokenType$1.OpenCurlyBracket);
|
|
5929
5941
|
// If the skipped tokens count is 0 without leading and trailing whitespace characters, then the selector list
|
|
5930
5942
|
// is empty
|
|
@@ -6164,6 +6176,7 @@ class AbpSnippetInjectionBodyParser extends ParserBase {
|
|
|
6164
6176
|
*
|
|
6165
6177
|
* @note Only 256 values can be represented this way.
|
|
6166
6178
|
*/
|
|
6179
|
+
// TODO: Update this map with the actual values
|
|
6167
6180
|
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]]);
|
|
6168
6181
|
/**
|
|
6169
6182
|
* Value map for binary deserialization. This helps to reduce the size of the serialized data,
|
|
@@ -6291,6 +6304,7 @@ class UboScriptletInjectionBodyParser extends ParserBase {
|
|
|
6291
6304
|
*
|
|
6292
6305
|
* @note Only 256 values can be represented this way.
|
|
6293
6306
|
*/
|
|
6307
|
+
// TODO: Update this map with the actual values
|
|
6294
6308
|
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]]);
|
|
6295
6309
|
/**
|
|
6296
6310
|
* Value map for binary deserialization. This helps to reduce the size of the serialized data,
|
|
@@ -6314,11 +6328,17 @@ class UboScriptletInjectionBodyParser extends ParserBase {
|
|
|
6314
6328
|
let offset = 0;
|
|
6315
6329
|
// Skip leading spaces
|
|
6316
6330
|
offset = StringUtils.skipWS(raw, offset);
|
|
6331
|
+
let scriptletMaskLength = 0;
|
|
6332
|
+
if (raw.startsWith(UBO_SCRIPTLET_MASK, offset)) {
|
|
6333
|
+
scriptletMaskLength = UBO_SCRIPTLET_MASK.length;
|
|
6334
|
+
} else if (raw.startsWith(UBO_SCRIPTLET_MASK_LEGACY, offset)) {
|
|
6335
|
+
scriptletMaskLength = UBO_SCRIPTLET_MASK_LEGACY.length;
|
|
6336
|
+
}
|
|
6317
6337
|
// Scriptlet call should start with "+js"
|
|
6318
|
-
if (!
|
|
6338
|
+
if (!scriptletMaskLength) {
|
|
6319
6339
|
throw new AdblockSyntaxError(this.ERROR_MESSAGES.NO_SCRIPTLET_MASK, baseOffset + offset, baseOffset + raw.length);
|
|
6320
6340
|
}
|
|
6321
|
-
offset +=
|
|
6341
|
+
offset += scriptletMaskLength;
|
|
6322
6342
|
// Whitespace is not allowed after the mask
|
|
6323
6343
|
if (raw[offset] === SPACE) {
|
|
6324
6344
|
throw new AdblockSyntaxError(this.ERROR_MESSAGES.WHITESPACE_AFTER_MASK, baseOffset + offset, baseOffset + raw.length);
|
|
@@ -6363,6 +6383,7 @@ class UboScriptletInjectionBodyParser extends ParserBase {
|
|
|
6363
6383
|
if (node.children.length > 1) {
|
|
6364
6384
|
throw new Error(this.ERROR_MESSAGES.NO_MULTIPLE_SCRIPTLET_CALLS);
|
|
6365
6385
|
}
|
|
6386
|
+
// During generation, we only support the modern scriptlet mask
|
|
6366
6387
|
result.push(UBO_SCRIPTLET_MASK);
|
|
6367
6388
|
result.push(OPEN_PARENTHESIS);
|
|
6368
6389
|
if (node.children.length > 0) {
|
|
@@ -6428,6 +6449,7 @@ class AdgScriptletInjectionBodyParser extends ParserBase {
|
|
|
6428
6449
|
*
|
|
6429
6450
|
* @note Only 256 values can be represented this way.
|
|
6430
6451
|
*/
|
|
6452
|
+
// TODO: Update this map with the actual values
|
|
6431
6453
|
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]]);
|
|
6432
6454
|
/**
|
|
6433
6455
|
* Value map for binary deserialization. This helps to reduce the size of the serialized data,
|
|
@@ -6899,7 +6921,7 @@ class CosmeticRuleParser extends ParserBase {
|
|
|
6899
6921
|
};
|
|
6900
6922
|
};
|
|
6901
6923
|
const parseUboScriptletInjection = () => {
|
|
6902
|
-
if (!rawBody.startsWith(UBO_SCRIPTLET_MASK)) {
|
|
6924
|
+
if (!rawBody.startsWith(UBO_SCRIPTLET_MASK) && !rawBody.startsWith(UBO_SCRIPTLET_MASK_LEGACY)) {
|
|
6903
6925
|
return null;
|
|
6904
6926
|
}
|
|
6905
6927
|
if (!options.parseUboSpecificRules) {
|
|
@@ -13854,7 +13876,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13854
13876
|
deprecationMessage: null,
|
|
13855
13877
|
removed: false,
|
|
13856
13878
|
removalMessage: null,
|
|
13857
|
-
isBlocking: false
|
|
13879
|
+
isBlocking: false,
|
|
13880
|
+
resourceTypes: []
|
|
13858
13881
|
}, {
|
|
13859
13882
|
name: "1x1.gif",
|
|
13860
13883
|
aliases: null,
|
|
@@ -13866,7 +13889,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13866
13889
|
deprecationMessage: null,
|
|
13867
13890
|
removed: false,
|
|
13868
13891
|
removalMessage: null,
|
|
13869
|
-
isBlocking: false
|
|
13892
|
+
isBlocking: false,
|
|
13893
|
+
resourceTypes: ["image"]
|
|
13870
13894
|
}, {
|
|
13871
13895
|
name: "1x1-transparent-gif",
|
|
13872
13896
|
aliases: null,
|
|
@@ -13878,7 +13902,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13878
13902
|
deprecationMessage: null,
|
|
13879
13903
|
removed: false,
|
|
13880
13904
|
removalMessage: null,
|
|
13881
|
-
isBlocking: false
|
|
13905
|
+
isBlocking: false,
|
|
13906
|
+
resourceTypes: []
|
|
13882
13907
|
}],
|
|
13883
13908
|
map: {
|
|
13884
13909
|
"1": 0,
|
|
@@ -13909,7 +13934,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13909
13934
|
deprecationMessage: null,
|
|
13910
13935
|
removed: false,
|
|
13911
13936
|
removalMessage: null,
|
|
13912
|
-
isBlocking: false
|
|
13937
|
+
isBlocking: false,
|
|
13938
|
+
resourceTypes: []
|
|
13913
13939
|
}, {
|
|
13914
13940
|
name: "2x2.png",
|
|
13915
13941
|
aliases: null,
|
|
@@ -13921,7 +13947,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13921
13947
|
deprecationMessage: null,
|
|
13922
13948
|
removed: false,
|
|
13923
13949
|
removalMessage: null,
|
|
13924
|
-
isBlocking: false
|
|
13950
|
+
isBlocking: false,
|
|
13951
|
+
resourceTypes: ["image"]
|
|
13925
13952
|
}, {
|
|
13926
13953
|
name: "2x2-transparent-png",
|
|
13927
13954
|
aliases: null,
|
|
@@ -13933,7 +13960,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13933
13960
|
deprecationMessage: null,
|
|
13934
13961
|
removed: false,
|
|
13935
13962
|
removalMessage: null,
|
|
13936
|
-
isBlocking: false
|
|
13963
|
+
isBlocking: false,
|
|
13964
|
+
resourceTypes: []
|
|
13937
13965
|
}],
|
|
13938
13966
|
map: {
|
|
13939
13967
|
"1": 0,
|
|
@@ -13964,7 +13992,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13964
13992
|
deprecationMessage: null,
|
|
13965
13993
|
removed: false,
|
|
13966
13994
|
removalMessage: null,
|
|
13967
|
-
isBlocking: false
|
|
13995
|
+
isBlocking: false,
|
|
13996
|
+
resourceTypes: []
|
|
13968
13997
|
}, {
|
|
13969
13998
|
name: "32x32.png",
|
|
13970
13999
|
aliases: null,
|
|
@@ -13976,7 +14005,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13976
14005
|
deprecationMessage: null,
|
|
13977
14006
|
removed: false,
|
|
13978
14007
|
removalMessage: null,
|
|
13979
|
-
isBlocking: false
|
|
14008
|
+
isBlocking: false,
|
|
14009
|
+
resourceTypes: ["image"]
|
|
13980
14010
|
}, {
|
|
13981
14011
|
name: "32x32-transparent-png",
|
|
13982
14012
|
aliases: null,
|
|
@@ -13988,7 +14018,8 @@ const redirectsCompatibilityTableData = {
|
|
|
13988
14018
|
deprecationMessage: null,
|
|
13989
14019
|
removed: false,
|
|
13990
14020
|
removalMessage: null,
|
|
13991
|
-
isBlocking: false
|
|
14021
|
+
isBlocking: false,
|
|
14022
|
+
resourceTypes: []
|
|
13992
14023
|
}],
|
|
13993
14024
|
map: {
|
|
13994
14025
|
"1": 0,
|
|
@@ -14019,7 +14050,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14019
14050
|
deprecationMessage: null,
|
|
14020
14051
|
removed: false,
|
|
14021
14052
|
removalMessage: null,
|
|
14022
|
-
isBlocking: false
|
|
14053
|
+
isBlocking: false,
|
|
14054
|
+
resourceTypes: []
|
|
14023
14055
|
}, {
|
|
14024
14056
|
name: "3x2.png",
|
|
14025
14057
|
aliases: null,
|
|
@@ -14031,7 +14063,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14031
14063
|
deprecationMessage: null,
|
|
14032
14064
|
removed: false,
|
|
14033
14065
|
removalMessage: null,
|
|
14034
|
-
isBlocking: false
|
|
14066
|
+
isBlocking: false,
|
|
14067
|
+
resourceTypes: ["image"]
|
|
14035
14068
|
}, {
|
|
14036
14069
|
name: "3x2-transparent-png",
|
|
14037
14070
|
aliases: null,
|
|
@@ -14043,7 +14076,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14043
14076
|
deprecationMessage: null,
|
|
14044
14077
|
removed: false,
|
|
14045
14078
|
removalMessage: null,
|
|
14046
|
-
isBlocking: false
|
|
14079
|
+
isBlocking: false,
|
|
14080
|
+
resourceTypes: []
|
|
14047
14081
|
}],
|
|
14048
14082
|
map: {
|
|
14049
14083
|
"1": 0,
|
|
@@ -14074,7 +14108,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14074
14108
|
deprecationMessage: null,
|
|
14075
14109
|
removed: false,
|
|
14076
14110
|
removalMessage: null,
|
|
14077
|
-
isBlocking: false
|
|
14111
|
+
isBlocking: false,
|
|
14112
|
+
resourceTypes: []
|
|
14078
14113
|
}, {
|
|
14079
14114
|
name: "amazon_apstag.js",
|
|
14080
14115
|
aliases: null,
|
|
@@ -14086,7 +14121,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14086
14121
|
deprecationMessage: null,
|
|
14087
14122
|
removed: false,
|
|
14088
14123
|
removalMessage: null,
|
|
14089
|
-
isBlocking: false
|
|
14124
|
+
isBlocking: false,
|
|
14125
|
+
resourceTypes: ["script"]
|
|
14090
14126
|
}],
|
|
14091
14127
|
map: {
|
|
14092
14128
|
"1": 0,
|
|
@@ -14113,7 +14149,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14113
14149
|
deprecationMessage: null,
|
|
14114
14150
|
removed: false,
|
|
14115
14151
|
removalMessage: null,
|
|
14116
|
-
isBlocking: false
|
|
14152
|
+
isBlocking: false,
|
|
14153
|
+
resourceTypes: ["script"]
|
|
14117
14154
|
}],
|
|
14118
14155
|
map: {
|
|
14119
14156
|
"1024": 0,
|
|
@@ -14133,7 +14170,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14133
14170
|
deprecationMessage: null,
|
|
14134
14171
|
removed: false,
|
|
14135
14172
|
removalMessage: null,
|
|
14136
|
-
isBlocking: false
|
|
14173
|
+
isBlocking: false,
|
|
14174
|
+
resourceTypes: ["script"]
|
|
14137
14175
|
}],
|
|
14138
14176
|
map: {
|
|
14139
14177
|
"1024": 0,
|
|
@@ -14153,7 +14191,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14153
14191
|
deprecationMessage: null,
|
|
14154
14192
|
removed: false,
|
|
14155
14193
|
removalMessage: null,
|
|
14156
|
-
isBlocking: false
|
|
14194
|
+
isBlocking: false,
|
|
14195
|
+
resourceTypes: []
|
|
14157
14196
|
}],
|
|
14158
14197
|
map: {
|
|
14159
14198
|
"1": 0,
|
|
@@ -14176,7 +14215,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14176
14215
|
deprecationMessage: null,
|
|
14177
14216
|
removed: false,
|
|
14178
14217
|
removalMessage: null,
|
|
14179
|
-
isBlocking: false
|
|
14218
|
+
isBlocking: false,
|
|
14219
|
+
resourceTypes: ["script"]
|
|
14180
14220
|
}],
|
|
14181
14221
|
map: {
|
|
14182
14222
|
"1024": 0,
|
|
@@ -14196,7 +14236,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14196
14236
|
deprecationMessage: null,
|
|
14197
14237
|
removed: false,
|
|
14198
14238
|
removalMessage: null,
|
|
14199
|
-
isBlocking: true
|
|
14239
|
+
isBlocking: true,
|
|
14240
|
+
resourceTypes: []
|
|
14200
14241
|
}, {
|
|
14201
14242
|
name: "click2load.html",
|
|
14202
14243
|
aliases: null,
|
|
@@ -14208,7 +14249,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14208
14249
|
deprecationMessage: null,
|
|
14209
14250
|
removed: false,
|
|
14210
14251
|
removalMessage: null,
|
|
14211
|
-
isBlocking: true
|
|
14252
|
+
isBlocking: true,
|
|
14253
|
+
resourceTypes: []
|
|
14212
14254
|
}],
|
|
14213
14255
|
map: {
|
|
14214
14256
|
"1": 0,
|
|
@@ -14235,7 +14277,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14235
14277
|
deprecationMessage: null,
|
|
14236
14278
|
removed: false,
|
|
14237
14279
|
removalMessage: null,
|
|
14238
|
-
isBlocking: false
|
|
14280
|
+
isBlocking: false,
|
|
14281
|
+
resourceTypes: []
|
|
14239
14282
|
}],
|
|
14240
14283
|
map: {
|
|
14241
14284
|
"1": 0,
|
|
@@ -14258,7 +14301,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14258
14301
|
deprecationMessage: null,
|
|
14259
14302
|
removed: false,
|
|
14260
14303
|
removalMessage: null,
|
|
14261
|
-
isBlocking: false
|
|
14304
|
+
isBlocking: false,
|
|
14305
|
+
resourceTypes: []
|
|
14262
14306
|
}, {
|
|
14263
14307
|
name: "empty",
|
|
14264
14308
|
aliases: null,
|
|
@@ -14270,7 +14314,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14270
14314
|
deprecationMessage: null,
|
|
14271
14315
|
removed: false,
|
|
14272
14316
|
removalMessage: null,
|
|
14273
|
-
isBlocking: false
|
|
14317
|
+
isBlocking: false,
|
|
14318
|
+
resourceTypes: []
|
|
14274
14319
|
}],
|
|
14275
14320
|
map: {
|
|
14276
14321
|
"1": 0,
|
|
@@ -14297,7 +14342,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14297
14342
|
deprecationMessage: null,
|
|
14298
14343
|
removed: false,
|
|
14299
14344
|
removalMessage: null,
|
|
14300
|
-
isBlocking: false
|
|
14345
|
+
isBlocking: false,
|
|
14346
|
+
resourceTypes: []
|
|
14301
14347
|
}, {
|
|
14302
14348
|
name: "fingerprint2.js",
|
|
14303
14349
|
aliases: null,
|
|
@@ -14309,7 +14355,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14309
14355
|
deprecationMessage: null,
|
|
14310
14356
|
removed: false,
|
|
14311
14357
|
removalMessage: null,
|
|
14312
|
-
isBlocking: false
|
|
14358
|
+
isBlocking: false,
|
|
14359
|
+
resourceTypes: ["script"]
|
|
14313
14360
|
}],
|
|
14314
14361
|
map: {
|
|
14315
14362
|
"1": 0,
|
|
@@ -14336,7 +14383,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14336
14383
|
deprecationMessage: null,
|
|
14337
14384
|
removed: false,
|
|
14338
14385
|
removalMessage: null,
|
|
14339
|
-
isBlocking: false
|
|
14386
|
+
isBlocking: false,
|
|
14387
|
+
resourceTypes: []
|
|
14340
14388
|
}, {
|
|
14341
14389
|
name: "fingerprint3.js",
|
|
14342
14390
|
aliases: null,
|
|
@@ -14348,7 +14396,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14348
14396
|
deprecationMessage: null,
|
|
14349
14397
|
removed: false,
|
|
14350
14398
|
removalMessage: null,
|
|
14351
|
-
isBlocking: false
|
|
14399
|
+
isBlocking: false,
|
|
14400
|
+
resourceTypes: ["script"]
|
|
14352
14401
|
}],
|
|
14353
14402
|
map: {
|
|
14354
14403
|
"1": 0,
|
|
@@ -14375,7 +14424,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14375
14424
|
deprecationMessage: null,
|
|
14376
14425
|
removed: false,
|
|
14377
14426
|
removalMessage: null,
|
|
14378
|
-
isBlocking: false
|
|
14427
|
+
isBlocking: false,
|
|
14428
|
+
resourceTypes: []
|
|
14379
14429
|
}],
|
|
14380
14430
|
map: {
|
|
14381
14431
|
"1": 0,
|
|
@@ -14398,7 +14448,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14398
14448
|
deprecationMessage: null,
|
|
14399
14449
|
removed: false,
|
|
14400
14450
|
removalMessage: null,
|
|
14401
|
-
isBlocking: false
|
|
14451
|
+
isBlocking: false,
|
|
14452
|
+
resourceTypes: []
|
|
14402
14453
|
}, {
|
|
14403
14454
|
name: "google-analytics_ga.js",
|
|
14404
14455
|
aliases: null,
|
|
@@ -14410,7 +14461,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14410
14461
|
deprecationMessage: null,
|
|
14411
14462
|
removed: false,
|
|
14412
14463
|
removalMessage: null,
|
|
14413
|
-
isBlocking: false
|
|
14464
|
+
isBlocking: false,
|
|
14465
|
+
resourceTypes: ["script"]
|
|
14414
14466
|
}],
|
|
14415
14467
|
map: {
|
|
14416
14468
|
"1": 0,
|
|
@@ -14437,7 +14489,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14437
14489
|
deprecationMessage: null,
|
|
14438
14490
|
removed: false,
|
|
14439
14491
|
removalMessage: null,
|
|
14440
|
-
isBlocking: false
|
|
14492
|
+
isBlocking: false,
|
|
14493
|
+
resourceTypes: []
|
|
14441
14494
|
}, {
|
|
14442
14495
|
name: "google-analytics_analytics.js",
|
|
14443
14496
|
aliases: null,
|
|
@@ -14449,7 +14502,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14449
14502
|
deprecationMessage: null,
|
|
14450
14503
|
removed: false,
|
|
14451
14504
|
removalMessage: null,
|
|
14452
|
-
isBlocking: false
|
|
14505
|
+
isBlocking: false,
|
|
14506
|
+
resourceTypes: ["script"]
|
|
14453
14507
|
}],
|
|
14454
14508
|
map: {
|
|
14455
14509
|
"1": 0,
|
|
@@ -14476,7 +14530,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14476
14530
|
deprecationMessage: null,
|
|
14477
14531
|
removed: false,
|
|
14478
14532
|
removalMessage: null,
|
|
14479
|
-
isBlocking: false
|
|
14533
|
+
isBlocking: false,
|
|
14534
|
+
resourceTypes: ["script"]
|
|
14480
14535
|
}],
|
|
14481
14536
|
map: {
|
|
14482
14537
|
"1024": 0,
|
|
@@ -14496,7 +14551,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14496
14551
|
deprecationMessage: null,
|
|
14497
14552
|
removed: false,
|
|
14498
14553
|
removalMessage: null,
|
|
14499
|
-
isBlocking: false
|
|
14554
|
+
isBlocking: false,
|
|
14555
|
+
resourceTypes: ["script"]
|
|
14500
14556
|
}],
|
|
14501
14557
|
map: {
|
|
14502
14558
|
"1024": 0,
|
|
@@ -14516,7 +14572,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14516
14572
|
deprecationMessage: null,
|
|
14517
14573
|
removed: false,
|
|
14518
14574
|
removalMessage: null,
|
|
14519
|
-
isBlocking: false
|
|
14575
|
+
isBlocking: false,
|
|
14576
|
+
resourceTypes: []
|
|
14520
14577
|
}, {
|
|
14521
14578
|
name: "google-ima.js",
|
|
14522
14579
|
aliases: null,
|
|
@@ -14528,7 +14585,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14528
14585
|
deprecationMessage: null,
|
|
14529
14586
|
removed: false,
|
|
14530
14587
|
removalMessage: null,
|
|
14531
|
-
isBlocking: false
|
|
14588
|
+
isBlocking: false,
|
|
14589
|
+
resourceTypes: ["script"]
|
|
14532
14590
|
}],
|
|
14533
14591
|
map: {
|
|
14534
14592
|
"1": 0,
|
|
@@ -14546,7 +14604,7 @@ const redirectsCompatibilityTableData = {
|
|
|
14546
14604
|
}, {
|
|
14547
14605
|
shared: [{
|
|
14548
14606
|
name: "googlesyndication-adsbygoogle",
|
|
14549
|
-
aliases: ["ubo-googlesyndication_adsbygoogle.js", "googlesyndication_adsbygoogle.js"],
|
|
14607
|
+
aliases: ["ubo-googlesyndication_adsbygoogle.js", "ubo-googlesyndication.com/adsbygoogle.js", "googlesyndication_adsbygoogle.js"],
|
|
14550
14608
|
description: "Mocks Google AdSense API.",
|
|
14551
14609
|
docs: null,
|
|
14552
14610
|
versionAdded: null,
|
|
@@ -14555,10 +14613,11 @@ const redirectsCompatibilityTableData = {
|
|
|
14555
14613
|
deprecationMessage: null,
|
|
14556
14614
|
removed: false,
|
|
14557
14615
|
removalMessage: null,
|
|
14558
|
-
isBlocking: false
|
|
14616
|
+
isBlocking: false,
|
|
14617
|
+
resourceTypes: []
|
|
14559
14618
|
}, {
|
|
14560
14619
|
name: "googlesyndication_adsbygoogle.js",
|
|
14561
|
-
aliases:
|
|
14620
|
+
aliases: ["googlesyndication.com/adsbygoogle.js"],
|
|
14562
14621
|
description: "Mocks Google AdSense API.",
|
|
14563
14622
|
docs: null,
|
|
14564
14623
|
versionAdded: null,
|
|
@@ -14567,7 +14626,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14567
14626
|
deprecationMessage: null,
|
|
14568
14627
|
removed: false,
|
|
14569
14628
|
removalMessage: null,
|
|
14570
|
-
isBlocking: false
|
|
14629
|
+
isBlocking: false,
|
|
14630
|
+
resourceTypes: ["script"]
|
|
14571
14631
|
}],
|
|
14572
14632
|
map: {
|
|
14573
14633
|
"1": 0,
|
|
@@ -14594,7 +14654,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14594
14654
|
deprecationMessage: null,
|
|
14595
14655
|
removed: false,
|
|
14596
14656
|
removalMessage: null,
|
|
14597
|
-
isBlocking: false
|
|
14657
|
+
isBlocking: false,
|
|
14658
|
+
resourceTypes: []
|
|
14598
14659
|
}, {
|
|
14599
14660
|
name: "googletagservices_gpt.js",
|
|
14600
14661
|
aliases: null,
|
|
@@ -14606,7 +14667,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14606
14667
|
deprecationMessage: null,
|
|
14607
14668
|
removed: false,
|
|
14608
14669
|
removalMessage: null,
|
|
14609
|
-
isBlocking: false
|
|
14670
|
+
isBlocking: false,
|
|
14671
|
+
resourceTypes: ["script"]
|
|
14610
14672
|
}],
|
|
14611
14673
|
map: {
|
|
14612
14674
|
"1": 0,
|
|
@@ -14633,7 +14695,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14633
14695
|
deprecationMessage: null,
|
|
14634
14696
|
removed: false,
|
|
14635
14697
|
removalMessage: null,
|
|
14636
|
-
isBlocking: false
|
|
14698
|
+
isBlocking: false,
|
|
14699
|
+
resourceTypes: ["script"]
|
|
14637
14700
|
}],
|
|
14638
14701
|
map: {
|
|
14639
14702
|
"1024": 0,
|
|
@@ -14653,7 +14716,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14653
14716
|
deprecationMessage: null,
|
|
14654
14717
|
removed: false,
|
|
14655
14718
|
removalMessage: null,
|
|
14656
|
-
isBlocking: false
|
|
14719
|
+
isBlocking: false,
|
|
14720
|
+
resourceTypes: []
|
|
14657
14721
|
}],
|
|
14658
14722
|
map: {
|
|
14659
14723
|
"1": 0,
|
|
@@ -14676,7 +14740,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14676
14740
|
deprecationMessage: null,
|
|
14677
14741
|
removed: false,
|
|
14678
14742
|
removalMessage: null,
|
|
14679
|
-
isBlocking: false
|
|
14743
|
+
isBlocking: false,
|
|
14744
|
+
resourceTypes: []
|
|
14680
14745
|
}],
|
|
14681
14746
|
map: {
|
|
14682
14747
|
"1": 0,
|
|
@@ -14699,7 +14764,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14699
14764
|
deprecationMessage: null,
|
|
14700
14765
|
removed: false,
|
|
14701
14766
|
removalMessage: null,
|
|
14702
|
-
isBlocking: false
|
|
14767
|
+
isBlocking: false,
|
|
14768
|
+
resourceTypes: []
|
|
14703
14769
|
}],
|
|
14704
14770
|
map: {
|
|
14705
14771
|
"1": 0,
|
|
@@ -14722,7 +14788,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14722
14788
|
deprecationMessage: null,
|
|
14723
14789
|
removed: false,
|
|
14724
14790
|
removalMessage: null,
|
|
14725
|
-
isBlocking: false
|
|
14791
|
+
isBlocking: false,
|
|
14792
|
+
resourceTypes: []
|
|
14726
14793
|
}],
|
|
14727
14794
|
map: {
|
|
14728
14795
|
"1": 0,
|
|
@@ -14745,7 +14812,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14745
14812
|
deprecationMessage: null,
|
|
14746
14813
|
removed: false,
|
|
14747
14814
|
removalMessage: null,
|
|
14748
|
-
isBlocking: false
|
|
14815
|
+
isBlocking: false,
|
|
14816
|
+
resourceTypes: []
|
|
14749
14817
|
}],
|
|
14750
14818
|
map: {
|
|
14751
14819
|
"1": 0,
|
|
@@ -14768,7 +14836,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14768
14836
|
deprecationMessage: null,
|
|
14769
14837
|
removed: false,
|
|
14770
14838
|
removalMessage: null,
|
|
14771
|
-
isBlocking: false
|
|
14839
|
+
isBlocking: false,
|
|
14840
|
+
resourceTypes: []
|
|
14772
14841
|
}],
|
|
14773
14842
|
map: {
|
|
14774
14843
|
"1": 0,
|
|
@@ -14791,7 +14860,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14791
14860
|
deprecationMessage: null,
|
|
14792
14861
|
removed: false,
|
|
14793
14862
|
removalMessage: null,
|
|
14794
|
-
isBlocking: false
|
|
14863
|
+
isBlocking: false,
|
|
14864
|
+
resourceTypes: []
|
|
14795
14865
|
}, {
|
|
14796
14866
|
name: "noeval.js",
|
|
14797
14867
|
aliases: null,
|
|
@@ -14803,7 +14873,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14803
14873
|
deprecationMessage: null,
|
|
14804
14874
|
removed: false,
|
|
14805
14875
|
removalMessage: null,
|
|
14806
|
-
isBlocking: false
|
|
14876
|
+
isBlocking: false,
|
|
14877
|
+
resourceTypes: ["script"]
|
|
14807
14878
|
}],
|
|
14808
14879
|
map: {
|
|
14809
14880
|
"1": 0,
|
|
@@ -14830,7 +14901,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14830
14901
|
deprecationMessage: null,
|
|
14831
14902
|
removed: false,
|
|
14832
14903
|
removalMessage: null,
|
|
14833
|
-
isBlocking: false
|
|
14904
|
+
isBlocking: false,
|
|
14905
|
+
resourceTypes: ["media"]
|
|
14834
14906
|
}],
|
|
14835
14907
|
map: {
|
|
14836
14908
|
"1024": 0,
|
|
@@ -14850,7 +14922,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14850
14922
|
deprecationMessage: null,
|
|
14851
14923
|
removed: false,
|
|
14852
14924
|
removalMessage: null,
|
|
14853
|
-
isBlocking: false
|
|
14925
|
+
isBlocking: false,
|
|
14926
|
+
resourceTypes: []
|
|
14854
14927
|
}, {
|
|
14855
14928
|
name: "noop.css",
|
|
14856
14929
|
aliases: null,
|
|
@@ -14862,7 +14935,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14862
14935
|
deprecationMessage: null,
|
|
14863
14936
|
removed: false,
|
|
14864
14937
|
removalMessage: null,
|
|
14865
|
-
isBlocking: false
|
|
14938
|
+
isBlocking: false,
|
|
14939
|
+
resourceTypes: ["stylesheet"]
|
|
14866
14940
|
}, {
|
|
14867
14941
|
name: "blank-css",
|
|
14868
14942
|
aliases: null,
|
|
@@ -14874,7 +14948,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14874
14948
|
deprecationMessage: null,
|
|
14875
14949
|
removed: false,
|
|
14876
14950
|
removalMessage: null,
|
|
14877
|
-
isBlocking: false
|
|
14951
|
+
isBlocking: false,
|
|
14952
|
+
resourceTypes: []
|
|
14878
14953
|
}],
|
|
14879
14954
|
map: {
|
|
14880
14955
|
"1": 0,
|
|
@@ -14905,7 +14980,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14905
14980
|
deprecationMessage: null,
|
|
14906
14981
|
removed: false,
|
|
14907
14982
|
removalMessage: null,
|
|
14908
|
-
isBlocking: false
|
|
14983
|
+
isBlocking: false,
|
|
14984
|
+
resourceTypes: []
|
|
14909
14985
|
}, {
|
|
14910
14986
|
name: "noop.html",
|
|
14911
14987
|
aliases: null,
|
|
@@ -14917,7 +14993,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14917
14993
|
deprecationMessage: null,
|
|
14918
14994
|
removed: false,
|
|
14919
14995
|
removalMessage: null,
|
|
14920
|
-
isBlocking: false
|
|
14996
|
+
isBlocking: false,
|
|
14997
|
+
resourceTypes: ["sub_frame"]
|
|
14921
14998
|
}, {
|
|
14922
14999
|
name: "blank-html",
|
|
14923
15000
|
aliases: null,
|
|
@@ -14929,7 +15006,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14929
15006
|
deprecationMessage: null,
|
|
14930
15007
|
removed: false,
|
|
14931
15008
|
removalMessage: null,
|
|
14932
|
-
isBlocking: false
|
|
15009
|
+
isBlocking: false,
|
|
15010
|
+
resourceTypes: []
|
|
14933
15011
|
}],
|
|
14934
15012
|
map: {
|
|
14935
15013
|
"1": 0,
|
|
@@ -14960,7 +15038,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14960
15038
|
deprecationMessage: null,
|
|
14961
15039
|
removed: false,
|
|
14962
15040
|
removalMessage: null,
|
|
14963
|
-
isBlocking: false
|
|
15041
|
+
isBlocking: false,
|
|
15042
|
+
resourceTypes: []
|
|
14964
15043
|
}, {
|
|
14965
15044
|
name: "noop.js",
|
|
14966
15045
|
aliases: null,
|
|
@@ -14972,7 +15051,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14972
15051
|
deprecationMessage: null,
|
|
14973
15052
|
removed: false,
|
|
14974
15053
|
removalMessage: null,
|
|
14975
|
-
isBlocking: false
|
|
15054
|
+
isBlocking: false,
|
|
15055
|
+
resourceTypes: ["script"]
|
|
14976
15056
|
}, {
|
|
14977
15057
|
name: "blank-js",
|
|
14978
15058
|
aliases: null,
|
|
@@ -14984,7 +15064,8 @@ const redirectsCompatibilityTableData = {
|
|
|
14984
15064
|
deprecationMessage: null,
|
|
14985
15065
|
removed: false,
|
|
14986
15066
|
removalMessage: null,
|
|
14987
|
-
isBlocking: false
|
|
15067
|
+
isBlocking: false,
|
|
15068
|
+
resourceTypes: []
|
|
14988
15069
|
}],
|
|
14989
15070
|
map: {
|
|
14990
15071
|
"1": 0,
|
|
@@ -15015,7 +15096,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15015
15096
|
deprecationMessage: null,
|
|
15016
15097
|
removed: false,
|
|
15017
15098
|
removalMessage: null,
|
|
15018
|
-
isBlocking: false
|
|
15099
|
+
isBlocking: false,
|
|
15100
|
+
resourceTypes: []
|
|
15019
15101
|
}, {
|
|
15020
15102
|
name: "noop.json",
|
|
15021
15103
|
aliases: null,
|
|
@@ -15027,7 +15109,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15027
15109
|
deprecationMessage: null,
|
|
15028
15110
|
removed: false,
|
|
15029
15111
|
removalMessage: null,
|
|
15030
|
-
isBlocking: false
|
|
15112
|
+
isBlocking: false,
|
|
15113
|
+
resourceTypes: []
|
|
15031
15114
|
}],
|
|
15032
15115
|
map: {
|
|
15033
15116
|
"1": 0,
|
|
@@ -15054,7 +15137,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15054
15137
|
deprecationMessage: null,
|
|
15055
15138
|
removed: false,
|
|
15056
15139
|
removalMessage: null,
|
|
15057
|
-
isBlocking: false
|
|
15140
|
+
isBlocking: false,
|
|
15141
|
+
resourceTypes: []
|
|
15058
15142
|
}, {
|
|
15059
15143
|
name: "noop-0.1s.mp3",
|
|
15060
15144
|
aliases: null,
|
|
@@ -15066,7 +15150,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15066
15150
|
deprecationMessage: null,
|
|
15067
15151
|
removed: false,
|
|
15068
15152
|
removalMessage: null,
|
|
15069
|
-
isBlocking: false
|
|
15153
|
+
isBlocking: false,
|
|
15154
|
+
resourceTypes: ["media"]
|
|
15070
15155
|
}, {
|
|
15071
15156
|
name: "blank-mp3",
|
|
15072
15157
|
aliases: null,
|
|
@@ -15078,7 +15163,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15078
15163
|
deprecationMessage: null,
|
|
15079
15164
|
removed: false,
|
|
15080
15165
|
removalMessage: null,
|
|
15081
|
-
isBlocking: false
|
|
15166
|
+
isBlocking: false,
|
|
15167
|
+
resourceTypes: []
|
|
15082
15168
|
}],
|
|
15083
15169
|
map: {
|
|
15084
15170
|
"1": 0,
|
|
@@ -15109,7 +15195,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15109
15195
|
deprecationMessage: null,
|
|
15110
15196
|
removed: false,
|
|
15111
15197
|
removalMessage: null,
|
|
15112
|
-
isBlocking: false
|
|
15198
|
+
isBlocking: false,
|
|
15199
|
+
resourceTypes: []
|
|
15113
15200
|
}, {
|
|
15114
15201
|
name: "noop-1s.mp4",
|
|
15115
15202
|
aliases: null,
|
|
@@ -15121,7 +15208,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15121
15208
|
deprecationMessage: null,
|
|
15122
15209
|
removed: false,
|
|
15123
15210
|
removalMessage: null,
|
|
15124
|
-
isBlocking: false
|
|
15211
|
+
isBlocking: false,
|
|
15212
|
+
resourceTypes: ["media"]
|
|
15125
15213
|
}, {
|
|
15126
15214
|
name: "blank-mp4",
|
|
15127
15215
|
aliases: null,
|
|
@@ -15133,7 +15221,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15133
15221
|
deprecationMessage: null,
|
|
15134
15222
|
removed: false,
|
|
15135
15223
|
removalMessage: null,
|
|
15136
|
-
isBlocking: false
|
|
15224
|
+
isBlocking: false,
|
|
15225
|
+
resourceTypes: []
|
|
15137
15226
|
}],
|
|
15138
15227
|
map: {
|
|
15139
15228
|
"1": 0,
|
|
@@ -15164,7 +15253,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15164
15253
|
deprecationMessage: null,
|
|
15165
15254
|
removed: false,
|
|
15166
15255
|
removalMessage: null,
|
|
15167
|
-
isBlocking: false
|
|
15256
|
+
isBlocking: false,
|
|
15257
|
+
resourceTypes: []
|
|
15168
15258
|
}, {
|
|
15169
15259
|
name: "noop.txt",
|
|
15170
15260
|
aliases: null,
|
|
@@ -15176,7 +15266,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15176
15266
|
deprecationMessage: null,
|
|
15177
15267
|
removed: false,
|
|
15178
15268
|
removalMessage: null,
|
|
15179
|
-
isBlocking: false
|
|
15269
|
+
isBlocking: false,
|
|
15270
|
+
resourceTypes: ["image", "media", "sub_frame", "stylesheet", "script", "xmlhttprequest", "other"]
|
|
15180
15271
|
}, {
|
|
15181
15272
|
name: "blank-text",
|
|
15182
15273
|
aliases: null,
|
|
@@ -15188,7 +15279,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15188
15279
|
deprecationMessage: null,
|
|
15189
15280
|
removed: false,
|
|
15190
15281
|
removalMessage: null,
|
|
15191
|
-
isBlocking: false
|
|
15282
|
+
isBlocking: false,
|
|
15283
|
+
resourceTypes: []
|
|
15192
15284
|
}],
|
|
15193
15285
|
map: {
|
|
15194
15286
|
"1": 0,
|
|
@@ -15219,7 +15311,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15219
15311
|
deprecationMessage: null,
|
|
15220
15312
|
removed: false,
|
|
15221
15313
|
removalMessage: null,
|
|
15222
|
-
isBlocking: false
|
|
15314
|
+
isBlocking: false,
|
|
15315
|
+
resourceTypes: []
|
|
15223
15316
|
}],
|
|
15224
15317
|
map: {
|
|
15225
15318
|
"1": 0,
|
|
@@ -15242,7 +15335,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15242
15335
|
deprecationMessage: null,
|
|
15243
15336
|
removed: false,
|
|
15244
15337
|
removalMessage: null,
|
|
15245
|
-
isBlocking: false
|
|
15338
|
+
isBlocking: false,
|
|
15339
|
+
resourceTypes: []
|
|
15246
15340
|
}],
|
|
15247
15341
|
map: {
|
|
15248
15342
|
"1": 0,
|
|
@@ -15265,7 +15359,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15265
15359
|
deprecationMessage: null,
|
|
15266
15360
|
removed: false,
|
|
15267
15361
|
removalMessage: null,
|
|
15268
|
-
isBlocking: false
|
|
15362
|
+
isBlocking: false,
|
|
15363
|
+
resourceTypes: []
|
|
15269
15364
|
}],
|
|
15270
15365
|
map: {
|
|
15271
15366
|
"1": 0,
|
|
@@ -15288,7 +15383,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15288
15383
|
deprecationMessage: null,
|
|
15289
15384
|
removed: false,
|
|
15290
15385
|
removalMessage: null,
|
|
15291
|
-
isBlocking: false
|
|
15386
|
+
isBlocking: false,
|
|
15387
|
+
resourceTypes: []
|
|
15292
15388
|
}, {
|
|
15293
15389
|
name: "noop-vmap1.0.xml",
|
|
15294
15390
|
aliases: null,
|
|
@@ -15300,7 +15396,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15300
15396
|
deprecationMessage: null,
|
|
15301
15397
|
removed: false,
|
|
15302
15398
|
removalMessage: null,
|
|
15303
|
-
isBlocking: false
|
|
15399
|
+
isBlocking: false,
|
|
15400
|
+
resourceTypes: ["media"]
|
|
15304
15401
|
}],
|
|
15305
15402
|
map: {
|
|
15306
15403
|
"1": 0,
|
|
@@ -15327,7 +15424,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15327
15424
|
deprecationMessage: null,
|
|
15328
15425
|
removed: false,
|
|
15329
15426
|
removalMessage: null,
|
|
15330
|
-
isBlocking: false
|
|
15427
|
+
isBlocking: false,
|
|
15428
|
+
resourceTypes: []
|
|
15331
15429
|
}, {
|
|
15332
15430
|
name: "nowebrtc.js",
|
|
15333
15431
|
aliases: null,
|
|
@@ -15339,7 +15437,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15339
15437
|
deprecationMessage: null,
|
|
15340
15438
|
removed: false,
|
|
15341
15439
|
removalMessage: null,
|
|
15342
|
-
isBlocking: false
|
|
15440
|
+
isBlocking: false,
|
|
15441
|
+
resourceTypes: ["other"]
|
|
15343
15442
|
}],
|
|
15344
15443
|
map: {
|
|
15345
15444
|
"1": 0,
|
|
@@ -15366,7 +15465,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15366
15465
|
deprecationMessage: null,
|
|
15367
15466
|
removed: false,
|
|
15368
15467
|
removalMessage: null,
|
|
15369
|
-
isBlocking: false
|
|
15468
|
+
isBlocking: false,
|
|
15469
|
+
resourceTypes: ["script"]
|
|
15370
15470
|
}],
|
|
15371
15471
|
map: {
|
|
15372
15472
|
"1024": 0,
|
|
@@ -15386,7 +15486,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15386
15486
|
deprecationMessage: null,
|
|
15387
15487
|
removed: false,
|
|
15388
15488
|
removalMessage: null,
|
|
15389
|
-
isBlocking: false
|
|
15489
|
+
isBlocking: false,
|
|
15490
|
+
resourceTypes: []
|
|
15390
15491
|
}],
|
|
15391
15492
|
map: {
|
|
15392
15493
|
"1": 0,
|
|
@@ -15409,7 +15510,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15409
15510
|
deprecationMessage: null,
|
|
15410
15511
|
removed: false,
|
|
15411
15512
|
removalMessage: null,
|
|
15412
|
-
isBlocking: false
|
|
15513
|
+
isBlocking: false,
|
|
15514
|
+
resourceTypes: []
|
|
15413
15515
|
}, {
|
|
15414
15516
|
name: "prebid-ads.js",
|
|
15415
15517
|
aliases: null,
|
|
@@ -15421,7 +15523,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15421
15523
|
deprecationMessage: null,
|
|
15422
15524
|
removed: false,
|
|
15423
15525
|
removalMessage: null,
|
|
15424
|
-
isBlocking: false
|
|
15526
|
+
isBlocking: false,
|
|
15527
|
+
resourceTypes: ["script"]
|
|
15425
15528
|
}],
|
|
15426
15529
|
map: {
|
|
15427
15530
|
"1": 0,
|
|
@@ -15448,7 +15551,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15448
15551
|
deprecationMessage: null,
|
|
15449
15552
|
removed: false,
|
|
15450
15553
|
removalMessage: null,
|
|
15451
|
-
isBlocking: false
|
|
15554
|
+
isBlocking: false,
|
|
15555
|
+
resourceTypes: []
|
|
15452
15556
|
}],
|
|
15453
15557
|
map: {
|
|
15454
15558
|
"1": 0,
|
|
@@ -15471,7 +15575,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15471
15575
|
deprecationMessage: null,
|
|
15472
15576
|
removed: false,
|
|
15473
15577
|
removalMessage: null,
|
|
15474
|
-
isBlocking: false
|
|
15578
|
+
isBlocking: false,
|
|
15579
|
+
resourceTypes: []
|
|
15475
15580
|
}, {
|
|
15476
15581
|
name: "nobab.js",
|
|
15477
15582
|
aliases: null,
|
|
@@ -15483,7 +15588,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15483
15588
|
deprecationMessage: null,
|
|
15484
15589
|
removed: false,
|
|
15485
15590
|
removalMessage: null,
|
|
15486
|
-
isBlocking: false
|
|
15591
|
+
isBlocking: false,
|
|
15592
|
+
resourceTypes: ["script"]
|
|
15487
15593
|
}],
|
|
15488
15594
|
map: {
|
|
15489
15595
|
"1": 0,
|
|
@@ -15510,7 +15616,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15510
15616
|
deprecationMessage: null,
|
|
15511
15617
|
removed: false,
|
|
15512
15618
|
removalMessage: null,
|
|
15513
|
-
isBlocking: false
|
|
15619
|
+
isBlocking: false,
|
|
15620
|
+
resourceTypes: []
|
|
15514
15621
|
}, {
|
|
15515
15622
|
name: "nobab2.js",
|
|
15516
15623
|
aliases: null,
|
|
@@ -15522,7 +15629,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15522
15629
|
deprecationMessage: null,
|
|
15523
15630
|
removed: false,
|
|
15524
15631
|
removalMessage: null,
|
|
15525
|
-
isBlocking: false
|
|
15632
|
+
isBlocking: false,
|
|
15633
|
+
resourceTypes: ["script"]
|
|
15526
15634
|
}],
|
|
15527
15635
|
map: {
|
|
15528
15636
|
"1": 0,
|
|
@@ -15549,10 +15657,11 @@ const redirectsCompatibilityTableData = {
|
|
|
15549
15657
|
deprecationMessage: null,
|
|
15550
15658
|
removed: false,
|
|
15551
15659
|
removalMessage: null,
|
|
15552
|
-
isBlocking: false
|
|
15660
|
+
isBlocking: false,
|
|
15661
|
+
resourceTypes: []
|
|
15553
15662
|
}, {
|
|
15554
15663
|
name: "nofab.js",
|
|
15555
|
-
aliases:
|
|
15664
|
+
aliases: ["fuckadblock.js-3.2.0", "fuckadblock.js-3.2.0.js"],
|
|
15556
15665
|
description: "Mocks FAB script v3.2.0.",
|
|
15557
15666
|
docs: null,
|
|
15558
15667
|
versionAdded: null,
|
|
@@ -15561,7 +15670,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15561
15670
|
deprecationMessage: null,
|
|
15562
15671
|
removed: false,
|
|
15563
15672
|
removalMessage: null,
|
|
15564
|
-
isBlocking: false
|
|
15673
|
+
isBlocking: false,
|
|
15674
|
+
resourceTypes: ["script"]
|
|
15565
15675
|
}],
|
|
15566
15676
|
map: {
|
|
15567
15677
|
"1": 0,
|
|
@@ -15588,7 +15698,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15588
15698
|
deprecationMessage: null,
|
|
15589
15699
|
removed: false,
|
|
15590
15700
|
removalMessage: null,
|
|
15591
|
-
isBlocking: false
|
|
15701
|
+
isBlocking: false,
|
|
15702
|
+
resourceTypes: []
|
|
15592
15703
|
}, {
|
|
15593
15704
|
name: "popads.js",
|
|
15594
15705
|
aliases: null,
|
|
@@ -15600,7 +15711,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15600
15711
|
deprecationMessage: null,
|
|
15601
15712
|
removed: false,
|
|
15602
15713
|
removalMessage: null,
|
|
15603
|
-
isBlocking: false
|
|
15714
|
+
isBlocking: false,
|
|
15715
|
+
resourceTypes: ["script"]
|
|
15604
15716
|
}],
|
|
15605
15717
|
map: {
|
|
15606
15718
|
"1": 0,
|
|
@@ -15627,7 +15739,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15627
15739
|
deprecationMessage: null,
|
|
15628
15740
|
removed: false,
|
|
15629
15741
|
removalMessage: null,
|
|
15630
|
-
isBlocking: false
|
|
15742
|
+
isBlocking: false,
|
|
15743
|
+
resourceTypes: []
|
|
15631
15744
|
}, {
|
|
15632
15745
|
name: "scorecardresearch_beacon.js",
|
|
15633
15746
|
aliases: null,
|
|
@@ -15639,7 +15752,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15639
15752
|
deprecationMessage: null,
|
|
15640
15753
|
removed: false,
|
|
15641
15754
|
removalMessage: null,
|
|
15642
|
-
isBlocking: false
|
|
15755
|
+
isBlocking: false,
|
|
15756
|
+
resourceTypes: ["script"]
|
|
15643
15757
|
}],
|
|
15644
15758
|
map: {
|
|
15645
15759
|
"1": 0,
|
|
@@ -15666,7 +15780,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15666
15780
|
deprecationMessage: null,
|
|
15667
15781
|
removed: false,
|
|
15668
15782
|
removalMessage: null,
|
|
15669
|
-
isBlocking: false
|
|
15783
|
+
isBlocking: false,
|
|
15784
|
+
resourceTypes: []
|
|
15670
15785
|
}, {
|
|
15671
15786
|
name: "popads-dummy.js",
|
|
15672
15787
|
aliases: null,
|
|
@@ -15678,7 +15793,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15678
15793
|
deprecationMessage: null,
|
|
15679
15794
|
removed: false,
|
|
15680
15795
|
removalMessage: null,
|
|
15681
|
-
isBlocking: false
|
|
15796
|
+
isBlocking: false,
|
|
15797
|
+
resourceTypes: ["script"]
|
|
15682
15798
|
}],
|
|
15683
15799
|
map: {
|
|
15684
15800
|
"1": 0,
|
|
@@ -15739,7 +15855,9 @@ const redirectsCompatibilityTableData = {
|
|
|
15739
15855
|
"google-ima.js": 19,
|
|
15740
15856
|
"googlesyndication-adsbygoogle": 20,
|
|
15741
15857
|
"ubo-googlesyndication_adsbygoogle.js": 20,
|
|
15858
|
+
"ubo-googlesyndication.com/adsbygoogle.js": 20,
|
|
15742
15859
|
"googlesyndication_adsbygoogle.js": 20,
|
|
15860
|
+
"googlesyndication.com/adsbygoogle.js": 20,
|
|
15743
15861
|
"googletagservices-gpt": 21,
|
|
15744
15862
|
"ubo-googletagservices_gpt.js": 21,
|
|
15745
15863
|
"googletagservices_gpt.js": 21,
|
|
@@ -15798,6 +15916,8 @@ const redirectsCompatibilityTableData = {
|
|
|
15798
15916
|
"nobab2.js": 48,
|
|
15799
15917
|
"prevent-fab-3.2.0": 49,
|
|
15800
15918
|
"nofab.js": 49,
|
|
15919
|
+
"fuckadblock.js-3.2.0": 49,
|
|
15920
|
+
"fuckadblock.js-3.2.0.js": 49,
|
|
15801
15921
|
"prevent-popads-net": 50,
|
|
15802
15922
|
"popads.js": 50,
|
|
15803
15923
|
"scorecardresearch-beacon": 51,
|
|
@@ -18505,7 +18625,7 @@ const scriptletsCompatibilityTableData = {
|
|
|
18505
18625
|
}]
|
|
18506
18626
|
}, {
|
|
18507
18627
|
name: "remove-attr.js",
|
|
18508
|
-
aliases: ["ra.js"],
|
|
18628
|
+
aliases: ["ra.js", "ra", "remove-attr"],
|
|
18509
18629
|
description: null,
|
|
18510
18630
|
docs: "https://github.com/gorhill/uBlock/wiki/Resources-Library#remove-attrjs-",
|
|
18511
18631
|
versionAdded: null,
|
|
@@ -18586,7 +18706,7 @@ const scriptletsCompatibilityTableData = {
|
|
|
18586
18706
|
}]
|
|
18587
18707
|
}, {
|
|
18588
18708
|
name: "remove-class.js",
|
|
18589
|
-
aliases: ["rc.js"],
|
|
18709
|
+
aliases: ["rc.js", "rc", "remove-class"],
|
|
18590
18710
|
description: null,
|
|
18591
18711
|
docs: "https://github.com/gorhill/uBlock/wiki/Resources-Library#remove-classjs-",
|
|
18592
18712
|
versionAdded: null,
|
|
@@ -20443,6 +20563,7 @@ const scriptletsCompatibilityTableData = {
|
|
|
20443
20563
|
"ubo-ra.js": 59,
|
|
20444
20564
|
"ubo-remove-attr": 59,
|
|
20445
20565
|
"ubo-ra": 59,
|
|
20566
|
+
ra: 59,
|
|
20446
20567
|
"remove-class": 60,
|
|
20447
20568
|
"remove-class.js": 60,
|
|
20448
20569
|
"ubo-remove-class.js": 60,
|
|
@@ -20450,6 +20571,7 @@ const scriptletsCompatibilityTableData = {
|
|
|
20450
20571
|
"ubo-rc.js": 60,
|
|
20451
20572
|
"ubo-remove-class": 60,
|
|
20452
20573
|
"ubo-rc": 60,
|
|
20574
|
+
rc: 60,
|
|
20453
20575
|
"remove-cookie": 61,
|
|
20454
20576
|
"cookie-remover.js": 61,
|
|
20455
20577
|
"ubo-cookie-remover.js": 61,
|
|
@@ -21507,6 +21629,44 @@ function getScriptletName(scriptletNode) {
|
|
|
21507
21629
|
}
|
|
21508
21630
|
return scriptletNode.children[0]?.value ?? EMPTY;
|
|
21509
21631
|
}
|
|
21632
|
+
/**
|
|
21633
|
+
* Transform the nth argument of the scriptlet node
|
|
21634
|
+
*
|
|
21635
|
+
* @param scriptletNode Scriptlet node to transform argument of
|
|
21636
|
+
* @param index Index of the argument to transform (index 0 is the scriptlet name)
|
|
21637
|
+
* @param transform Function to transform the argument
|
|
21638
|
+
*/
|
|
21639
|
+
function transformNthScriptletArgument(scriptletNode, index, transform) {
|
|
21640
|
+
const child = scriptletNode.children[index];
|
|
21641
|
+
if (!isUndefined(child)) {
|
|
21642
|
+
const transformed = transform(child?.value ?? null);
|
|
21643
|
+
if (isNull(transformed)) {
|
|
21644
|
+
// eslint-disable-next-line no-param-reassign
|
|
21645
|
+
scriptletNode.children[index] = null;
|
|
21646
|
+
return;
|
|
21647
|
+
}
|
|
21648
|
+
if (isNull(child)) {
|
|
21649
|
+
// eslint-disable-next-line no-param-reassign
|
|
21650
|
+
scriptletNode.children[index] = {
|
|
21651
|
+
type: 'Value',
|
|
21652
|
+
value: transformed
|
|
21653
|
+
};
|
|
21654
|
+
return;
|
|
21655
|
+
}
|
|
21656
|
+
child.value = transformed;
|
|
21657
|
+
}
|
|
21658
|
+
}
|
|
21659
|
+
/**
|
|
21660
|
+
* Transform all arguments of the scriptlet node
|
|
21661
|
+
*
|
|
21662
|
+
* @param scriptletNode Scriptlet node to transform arguments of
|
|
21663
|
+
* @param transform Function to transform the arguments
|
|
21664
|
+
*/
|
|
21665
|
+
function transformAllScriptletArguments(scriptletNode, transform) {
|
|
21666
|
+
for (let i = 0; i < scriptletNode.children.length; i += 1) {
|
|
21667
|
+
transformNthScriptletArgument(scriptletNode, i, transform);
|
|
21668
|
+
}
|
|
21669
|
+
}
|
|
21510
21670
|
/**
|
|
21511
21671
|
* Set name of the scriptlet.
|
|
21512
21672
|
* Modifies input `scriptletNode` if needed.
|
|
@@ -21515,10 +21675,7 @@ function getScriptletName(scriptletNode) {
|
|
|
21515
21675
|
* @param name Name to set
|
|
21516
21676
|
*/
|
|
21517
21677
|
function setScriptletName(scriptletNode, name) {
|
|
21518
|
-
|
|
21519
|
-
// eslint-disable-next-line no-param-reassign
|
|
21520
|
-
scriptletNode.children[0].value = name;
|
|
21521
|
-
}
|
|
21678
|
+
transformNthScriptletArgument(scriptletNode, 0, () => name);
|
|
21522
21679
|
}
|
|
21523
21680
|
/**
|
|
21524
21681
|
* Set quote type of the scriptlet parameters
|
|
@@ -21527,1422 +21684,1761 @@ function setScriptletName(scriptletNode, name) {
|
|
|
21527
21684
|
* @param quoteType Preferred quote type
|
|
21528
21685
|
*/
|
|
21529
21686
|
function setScriptletQuoteType(scriptletNode, quoteType) {
|
|
21530
|
-
|
|
21531
|
-
|
|
21532
|
-
|
|
21533
|
-
if (isNull(child)) {
|
|
21534
|
-
continue;
|
|
21535
|
-
}
|
|
21536
|
-
// eslint-disable-next-line no-param-reassign
|
|
21537
|
-
child.value = QuoteUtils.setStringQuoteType(child.value, quoteType);
|
|
21538
|
-
}
|
|
21539
|
-
}
|
|
21687
|
+
// null is a special value that means "no value", but we can't change its quote type,
|
|
21688
|
+
// so we need to convert it to empty string
|
|
21689
|
+
transformAllScriptletArguments(scriptletNode, value => QuoteUtils.setStringQuoteType(value ?? EMPTY, quoteType));
|
|
21540
21690
|
}
|
|
21541
21691
|
|
|
21542
21692
|
/**
|
|
21543
|
-
* @file
|
|
21693
|
+
* @file Resource type schema.
|
|
21544
21694
|
*/
|
|
21545
|
-
const ABP_SCRIPTLET_PREFIX = 'abp-';
|
|
21546
|
-
const UBO_SCRIPTLET_PREFIX = 'ubo-';
|
|
21547
21695
|
/**
|
|
21548
|
-
*
|
|
21696
|
+
* Resource type.
|
|
21549
21697
|
*
|
|
21550
|
-
* @
|
|
21698
|
+
* @see {@link https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#type-ResourceType}
|
|
21551
21699
|
*/
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
|
|
21555
|
-
|
|
21556
|
-
|
|
21557
|
-
|
|
21558
|
-
|
|
21559
|
-
|
|
21560
|
-
|
|
21561
|
-
|
|
21562
|
-
|
|
21563
|
-
|
|
21564
|
-
|
|
21565
|
-
|
|
21566
|
-
|
|
21567
|
-
const separator = rule.separator.value;
|
|
21568
|
-
let convertedSeparator = separator;
|
|
21569
|
-
convertedSeparator = rule.exception ? CosmeticRuleSeparator.AdgJsInjectionException : CosmeticRuleSeparator.AdgJsInjection;
|
|
21570
|
-
const convertedScriptlets = [];
|
|
21571
|
-
for (const scriptlet of rule.body.children) {
|
|
21572
|
-
// Clone the node to avoid any side effects
|
|
21573
|
-
const scriptletClone = cloneScriptletRuleNode(scriptlet);
|
|
21574
|
-
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
21575
|
-
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletClone), QuoteType.None);
|
|
21576
|
-
// Add prefix if it's not already there
|
|
21577
|
-
let prefix;
|
|
21578
|
-
switch (rule.syntax) {
|
|
21579
|
-
case AdblockSyntax.Abp:
|
|
21580
|
-
prefix = ABP_SCRIPTLET_PREFIX;
|
|
21581
|
-
break;
|
|
21582
|
-
case AdblockSyntax.Ubo:
|
|
21583
|
-
prefix = UBO_SCRIPTLET_PREFIX;
|
|
21584
|
-
break;
|
|
21585
|
-
default:
|
|
21586
|
-
prefix = EMPTY;
|
|
21587
|
-
}
|
|
21588
|
-
if (!scriptletName.startsWith(prefix)) {
|
|
21589
|
-
setScriptletName(scriptletClone, `${prefix}${scriptletName}`);
|
|
21590
|
-
}
|
|
21591
|
-
// ADG scriptlet parameters should be quoted, and single quoted are preferred
|
|
21592
|
-
setScriptletQuoteType(scriptletClone, QuoteType.Single);
|
|
21593
|
-
convertedScriptlets.push(scriptletClone);
|
|
21594
|
-
}
|
|
21595
|
-
return createNodeConversionResult(convertedScriptlets.map(scriptlet => {
|
|
21596
|
-
const res = {
|
|
21597
|
-
category: rule.category,
|
|
21598
|
-
type: rule.type,
|
|
21599
|
-
syntax: AdblockSyntax.Adg,
|
|
21600
|
-
exception: rule.exception,
|
|
21601
|
-
domains: cloneDomainListNode(rule.domains),
|
|
21602
|
-
separator: {
|
|
21603
|
-
type: 'Value',
|
|
21604
|
-
value: convertedSeparator
|
|
21605
|
-
},
|
|
21606
|
-
body: {
|
|
21607
|
-
type: rule.body.type,
|
|
21608
|
-
children: [scriptlet]
|
|
21609
|
-
}
|
|
21610
|
-
};
|
|
21611
|
-
if (rule.modifiers) {
|
|
21612
|
-
res.modifiers = cloneModifierListNode(rule.modifiers);
|
|
21613
|
-
}
|
|
21614
|
-
return res;
|
|
21615
|
-
}), true);
|
|
21616
|
-
}
|
|
21617
|
-
}
|
|
21618
|
-
|
|
21700
|
+
var ResourceType;
|
|
21701
|
+
(function (ResourceType) {
|
|
21702
|
+
ResourceType["MainFrame"] = "main_frame";
|
|
21703
|
+
ResourceType["SubFrame"] = "sub_frame";
|
|
21704
|
+
ResourceType["Stylesheet"] = "stylesheet";
|
|
21705
|
+
ResourceType["Script"] = "script";
|
|
21706
|
+
ResourceType["Image"] = "image";
|
|
21707
|
+
ResourceType["Font"] = "font";
|
|
21708
|
+
ResourceType["Object"] = "object";
|
|
21709
|
+
ResourceType["XmlHttpRequest"] = "xmlhttprequest";
|
|
21710
|
+
ResourceType["Ping"] = "ping";
|
|
21711
|
+
ResourceType["Media"] = "media";
|
|
21712
|
+
ResourceType["WebSocket"] = "websocket";
|
|
21713
|
+
ResourceType["Other"] = "other";
|
|
21714
|
+
})(ResourceType || (ResourceType = {}));
|
|
21619
21715
|
/**
|
|
21620
|
-
*
|
|
21716
|
+
* Resource type schema.
|
|
21621
21717
|
*/
|
|
21718
|
+
const resourceTypeSchema = zod.nativeEnum(ResourceType);
|
|
21719
|
+
|
|
21622
21720
|
/**
|
|
21623
|
-
*
|
|
21721
|
+
* Map of resource types to their corresponding adblock modifier names.
|
|
21722
|
+
*
|
|
21723
|
+
* @note Record type is used to ensure that all resource types are present in the map.
|
|
21724
|
+
*/
|
|
21725
|
+
const RESOURCE_TYPE_MODIFIER_MAP = Object.freeze({
|
|
21726
|
+
[ResourceType.MainFrame]: 'document',
|
|
21727
|
+
[ResourceType.SubFrame]: 'subdocument',
|
|
21728
|
+
[ResourceType.Stylesheet]: 'stylesheet',
|
|
21729
|
+
[ResourceType.Script]: 'script',
|
|
21730
|
+
[ResourceType.Image]: 'image',
|
|
21731
|
+
[ResourceType.Font]: 'font',
|
|
21732
|
+
[ResourceType.Object]: 'object',
|
|
21733
|
+
[ResourceType.XmlHttpRequest]: 'xmlhttprequest',
|
|
21734
|
+
[ResourceType.Ping]: 'ping',
|
|
21735
|
+
[ResourceType.Media]: 'media',
|
|
21736
|
+
[ResourceType.WebSocket]: 'websocket',
|
|
21737
|
+
[ResourceType.Other]: 'other'
|
|
21738
|
+
});
|
|
21739
|
+
/**
|
|
21740
|
+
* Gets the adblock modifier name for the given resource type.
|
|
21624
21741
|
*
|
|
21625
|
-
* @param
|
|
21626
|
-
* @param
|
|
21627
|
-
*
|
|
21628
|
-
* @returns
|
|
21742
|
+
* @param resourceType Resource type to get the modifier name for.
|
|
21743
|
+
* @param platform Platform to get the modifier for.
|
|
21744
|
+
*
|
|
21745
|
+
* @returns A string containing the adblock modifier name for the given resource type
|
|
21746
|
+
* or `null` if the modifier could not be found.
|
|
21629
21747
|
*/
|
|
21630
|
-
|
|
21631
|
-
const
|
|
21632
|
-
|
|
21633
|
-
|
|
21634
|
-
name: {
|
|
21635
|
-
type: 'Value',
|
|
21636
|
-
value: name
|
|
21637
|
-
}
|
|
21638
|
-
};
|
|
21639
|
-
if (!isUndefined(value)) {
|
|
21640
|
-
result.value = {
|
|
21641
|
-
type: 'Value',
|
|
21642
|
-
value
|
|
21643
|
-
};
|
|
21748
|
+
const getResourceTypeModifier = (resourceType, platform) => {
|
|
21749
|
+
const modifierName = RESOURCE_TYPE_MODIFIER_MAP[resourceType];
|
|
21750
|
+
if (!modifierName) {
|
|
21751
|
+
return null;
|
|
21644
21752
|
}
|
|
21645
|
-
|
|
21646
|
-
|
|
21753
|
+
const modifierData = modifiersCompatibilityTable.getFirst(modifierName, platform);
|
|
21754
|
+
if (isNull(modifierData)) {
|
|
21755
|
+
return null;
|
|
21756
|
+
}
|
|
21757
|
+
return modifierData.name;
|
|
21758
|
+
};
|
|
21647
21759
|
/**
|
|
21648
|
-
*
|
|
21760
|
+
* Checks if the given resource type is valid.
|
|
21649
21761
|
*
|
|
21650
|
-
* @param
|
|
21651
|
-
*
|
|
21762
|
+
* @param resourceType Resource type to check.
|
|
21763
|
+
*
|
|
21764
|
+
* @returns `true` if the resource type is valid, `false` otherwise.
|
|
21652
21765
|
*/
|
|
21653
|
-
|
|
21654
|
-
|
|
21655
|
-
|
|
21656
|
-
// We need to clone the modifiers to avoid side effects
|
|
21657
|
-
children: modifiers.length ? clone(modifiers) : []
|
|
21658
|
-
};
|
|
21659
|
-
return result;
|
|
21660
|
-
}
|
|
21766
|
+
const isValidResourceType = resourceType => {
|
|
21767
|
+
return Object.values(ResourceType).includes(resourceType);
|
|
21768
|
+
};
|
|
21661
21769
|
|
|
21662
21770
|
/**
|
|
21663
|
-
*
|
|
21664
|
-
|
|
21771
|
+
* @file Compatibility tables for redirects.
|
|
21772
|
+
*/
|
|
21773
|
+
/**
|
|
21774
|
+
* Prefix for resource redirection names.
|
|
21775
|
+
*/
|
|
21776
|
+
const ABP_RESOURCE_PREFIX = 'abp-resource:';
|
|
21777
|
+
const ABP_RESOURCE_PREFIX_LENGTH = ABP_RESOURCE_PREFIX.length;
|
|
21778
|
+
/**
|
|
21779
|
+
* Normalizes the redirect name.
|
|
21665
21780
|
*
|
|
21666
|
-
* @
|
|
21781
|
+
* @param name Redirect name to normalize.
|
|
21782
|
+
*
|
|
21783
|
+
* @returns Normalized redirect name.
|
|
21784
|
+
*
|
|
21785
|
+
* @example
|
|
21786
|
+
* redirectNameNormalizer('abp-resource:my-resource') // => 'my-resource'
|
|
21787
|
+
* redirectNameNormalizer('noop.js:99') // => 'noop.js'
|
|
21667
21788
|
*/
|
|
21668
|
-
|
|
21789
|
+
const redirectNameNormalizer = name => {
|
|
21790
|
+
// Remove ABP resource prefix, if present
|
|
21791
|
+
if (name.startsWith(ABP_RESOURCE_PREFIX)) {
|
|
21792
|
+
return name.slice(ABP_RESOURCE_PREFIX_LENGTH);
|
|
21793
|
+
}
|
|
21794
|
+
// Remove :[integer] priority suffix from the name, if present
|
|
21795
|
+
// See:
|
|
21796
|
+
// - https://github.com/AdguardTeam/tsurlfilter/issues/59
|
|
21797
|
+
// - https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#redirect
|
|
21798
|
+
const colonIndex = name.lastIndexOf(COLON);
|
|
21799
|
+
if (colonIndex !== -1 && /^\d+$/.test(name.slice(colonIndex + 1))) {
|
|
21800
|
+
return name.slice(0, colonIndex);
|
|
21801
|
+
}
|
|
21802
|
+
return name;
|
|
21803
|
+
};
|
|
21804
|
+
/**
|
|
21805
|
+
* Compatibility table for redirects.
|
|
21806
|
+
*/
|
|
21807
|
+
class RedirectsCompatibilityTable extends CompatibilityTableBase {
|
|
21669
21808
|
/**
|
|
21670
|
-
*
|
|
21671
|
-
* otherwise a new array will be created for the key.
|
|
21809
|
+
* Creates a new instance of the compatibility table for redirects.
|
|
21672
21810
|
*
|
|
21673
|
-
* @param
|
|
21674
|
-
* @param values Value(s) to add
|
|
21811
|
+
* @param data Compatibility table data.
|
|
21675
21812
|
*/
|
|
21676
|
-
|
|
21677
|
-
|
|
21678
|
-
|
|
21679
|
-
|
|
21680
|
-
|
|
21813
|
+
constructor(data) {
|
|
21814
|
+
super(data, redirectNameNormalizer);
|
|
21815
|
+
}
|
|
21816
|
+
/**
|
|
21817
|
+
* Gets the resource type adblock modifiers for the redirect for the given platform
|
|
21818
|
+
* based on the `resourceTypes` field.
|
|
21819
|
+
*
|
|
21820
|
+
* @param redirect Redirect name or redirect data.
|
|
21821
|
+
* @param platform Platform to get the modifiers for.
|
|
21822
|
+
*
|
|
21823
|
+
* @returns Set of resource type modifiers or an empty set if the redirect is not found or has no resource types.
|
|
21824
|
+
*/
|
|
21825
|
+
getResourceTypeModifiers(redirect, platform) {
|
|
21826
|
+
let redirectData = null;
|
|
21827
|
+
if (isString(redirect)) {
|
|
21828
|
+
redirectData = this.getFirst(redirect, platform);
|
|
21829
|
+
} else {
|
|
21830
|
+
redirectData = redirect;
|
|
21681
21831
|
}
|
|
21682
|
-
|
|
21832
|
+
const modifierNames = new Set();
|
|
21833
|
+
if (isNull(redirectData) || isUndefined(redirectData.resourceTypes)) {
|
|
21834
|
+
return modifierNames;
|
|
21835
|
+
}
|
|
21836
|
+
for (const resourceType of redirectData.resourceTypes) {
|
|
21837
|
+
const modifierName = getResourceTypeModifier(resourceType, platform);
|
|
21838
|
+
if (isNull(modifierName)) {
|
|
21839
|
+
continue;
|
|
21840
|
+
}
|
|
21841
|
+
modifierNames.add(modifierName);
|
|
21842
|
+
}
|
|
21843
|
+
return modifierNames;
|
|
21683
21844
|
}
|
|
21684
21845
|
}
|
|
21846
|
+
/**
|
|
21847
|
+
* Deep freeze the compatibility table data to avoid accidental modifications.
|
|
21848
|
+
*/
|
|
21849
|
+
deepFreeze(redirectsCompatibilityTableData);
|
|
21850
|
+
/**
|
|
21851
|
+
* Compatibility table instance for redirects.
|
|
21852
|
+
*/
|
|
21853
|
+
const redirectsCompatibilityTable = new RedirectsCompatibilityTable(redirectsCompatibilityTableData);
|
|
21685
21854
|
|
|
21686
21855
|
/**
|
|
21687
|
-
* @file
|
|
21856
|
+
* @file Compatibility tables for scriptlets.
|
|
21688
21857
|
*/
|
|
21689
|
-
const UBO_MATCHES_PATH_OPERATOR = 'matches-path';
|
|
21690
|
-
const ADG_PATH_MODIFIER = 'path';
|
|
21691
21858
|
/**
|
|
21692
|
-
*
|
|
21859
|
+
* Compatibility table for scriptlets.
|
|
21693
21860
|
*/
|
|
21694
|
-
|
|
21861
|
+
class ScriptletsCompatibilityTable extends CompatibilityTableBase {}
|
|
21695
21862
|
/**
|
|
21696
|
-
*
|
|
21863
|
+
* Deep freeze the compatibility table data to avoid accidental modifications.
|
|
21697
21864
|
*/
|
|
21698
|
-
|
|
21699
|
-
/**
|
|
21700
|
-
* Converts a uBO cosmetic rule modifier list to ADG, if possible.
|
|
21701
|
-
*
|
|
21702
|
-
* @param modifierList Cosmetic rule modifier list node to convert
|
|
21703
|
-
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
21704
|
-
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
21705
|
-
* If the node was not converted, the result will contain the original node with the same object reference
|
|
21706
|
-
* @throws If the modifier list cannot be converted
|
|
21707
|
-
* @see {@link https://github.com/gorhill/uBlock/wiki/Procedural-cosmetic-filters#cosmetic-filter-operators}
|
|
21708
|
-
*/
|
|
21709
|
-
static convertFromUbo(modifierList) {
|
|
21710
|
-
const conversionMap = new MultiValueMap();
|
|
21711
|
-
modifierList.children.forEach((modifier, index) => {
|
|
21712
|
-
// :matches-path
|
|
21713
|
-
if (modifier.name.value === UBO_MATCHES_PATH_OPERATOR) {
|
|
21714
|
-
if (!modifier.value) {
|
|
21715
|
-
throw new RuleConversionError(`'${UBO_MATCHES_PATH_OPERATOR}' operator requires a value`);
|
|
21716
|
-
}
|
|
21717
|
-
const value = RegExpUtils.isRegexPattern(modifier.value.value) ? StringUtils.escapeCharacters(modifier.value.value, SPECIAL_MODIFIER_REGEX_CHARS) : modifier.value.value;
|
|
21718
|
-
// Convert uBO's `:matches-path(...)` operator to ADG's `$path=...` modifier
|
|
21719
|
-
conversionMap.add(index, createModifierNode(ADG_PATH_MODIFIER,
|
|
21720
|
-
// We should negate the regexp if the modifier is an exception
|
|
21721
|
-
modifier.exception
|
|
21722
|
-
// eslint-disable-next-line max-len
|
|
21723
|
-
? `${REGEX_MARKER}${RegExpUtils.negateRegexPattern(RegExpUtils.patternToRegexp(value))}${REGEX_MARKER}` : value));
|
|
21724
|
-
}
|
|
21725
|
-
});
|
|
21726
|
-
// Check if we have any converted modifiers
|
|
21727
|
-
if (conversionMap.size) {
|
|
21728
|
-
const modifierListClone = clone(modifierList);
|
|
21729
|
-
// Replace the original modifiers with the converted ones
|
|
21730
|
-
modifierListClone.children = modifierListClone.children.map((modifier, index) => {
|
|
21731
|
-
const convertedModifier = conversionMap.get(index);
|
|
21732
|
-
return convertedModifier ?? modifier;
|
|
21733
|
-
}).flat();
|
|
21734
|
-
return createConversionResult(modifierListClone, true);
|
|
21735
|
-
}
|
|
21736
|
-
// Otherwise, just return the original modifier list
|
|
21737
|
-
return createConversionResult(modifierList, false);
|
|
21738
|
-
}
|
|
21739
|
-
}
|
|
21740
|
-
const ERROR_MESSAGES$1 = {
|
|
21741
|
-
// eslint-disable-next-line max-len
|
|
21742
|
-
INVALID_ATTRIBUTE_VALUE: `Expected '${getFormattedTokenName(TokenType$1.Ident)}' or '${getFormattedTokenName(TokenType$1.String)}' as attribute value, but got '%s' with value '%s`
|
|
21743
|
-
};
|
|
21744
|
-
var PseudoClasses;
|
|
21745
|
-
(function (PseudoClasses) {
|
|
21746
|
-
PseudoClasses["AbpContains"] = "-abp-contains";
|
|
21747
|
-
PseudoClasses["AbpHas"] = "-abp-has";
|
|
21748
|
-
PseudoClasses["Contains"] = "contains";
|
|
21749
|
-
PseudoClasses["Has"] = "has";
|
|
21750
|
-
PseudoClasses["HasText"] = "has-text";
|
|
21751
|
-
PseudoClasses["MatchesCss"] = "matches-css";
|
|
21752
|
-
PseudoClasses["MatchesCssAfter"] = "matches-css-after";
|
|
21753
|
-
PseudoClasses["MatchesCssBefore"] = "matches-css-before";
|
|
21754
|
-
PseudoClasses["Not"] = "not";
|
|
21755
|
-
})(PseudoClasses || (PseudoClasses = {}));
|
|
21756
|
-
var PseudoElements;
|
|
21757
|
-
(function (PseudoElements) {
|
|
21758
|
-
PseudoElements["After"] = "after";
|
|
21759
|
-
PseudoElements["Before"] = "before";
|
|
21760
|
-
})(PseudoElements || (PseudoElements = {}));
|
|
21761
|
-
const PSEUDO_ELEMENT_NAMES = new Set([PseudoElements.After, PseudoElements.Before]);
|
|
21865
|
+
deepFreeze(scriptletsCompatibilityTableData);
|
|
21762
21866
|
/**
|
|
21763
|
-
*
|
|
21764
|
-
*
|
|
21765
|
-
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
21867
|
+
* Compatibility table instance for scriptlets.
|
|
21766
21868
|
*/
|
|
21767
|
-
|
|
21768
|
-
/**
|
|
21769
|
-
* Converts Extended CSS elements to AdGuard-compatible ones
|
|
21770
|
-
*
|
|
21771
|
-
* @param selectorList Selector list to convert
|
|
21772
|
-
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
21773
|
-
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
21774
|
-
* If the node was not converted, the result will contain the original node with the same object reference
|
|
21775
|
-
* @throws If the rule is invalid or incompatible
|
|
21776
|
-
*/
|
|
21777
|
-
static convertToAdg(selectorList) {
|
|
21778
|
-
const stream = selectorList instanceof CssTokenStream ? selectorList : new CssTokenStream(selectorList);
|
|
21779
|
-
const converted = [];
|
|
21780
|
-
const convertAndPushPseudo = pseudo => {
|
|
21781
|
-
switch (pseudo) {
|
|
21782
|
-
case PseudoClasses.AbpContains:
|
|
21783
|
-
case PseudoClasses.HasText:
|
|
21784
|
-
converted.push(PseudoClasses.Contains);
|
|
21785
|
-
converted.push(OPEN_PARENTHESIS);
|
|
21786
|
-
break;
|
|
21787
|
-
case PseudoClasses.AbpHas:
|
|
21788
|
-
converted.push(PseudoClasses.Has);
|
|
21789
|
-
converted.push(OPEN_PARENTHESIS);
|
|
21790
|
-
break;
|
|
21791
|
-
// a bit special case:
|
|
21792
|
-
// - `:matches-css-before(...)` → `:matches-css(before, ...)`
|
|
21793
|
-
// - `:matches-css-after(...)` → `:matches-css(after, ...)`
|
|
21794
|
-
case PseudoClasses.MatchesCssBefore:
|
|
21795
|
-
case PseudoClasses.MatchesCssAfter:
|
|
21796
|
-
converted.push(PseudoClasses.MatchesCss);
|
|
21797
|
-
converted.push(OPEN_PARENTHESIS);
|
|
21798
|
-
converted.push(pseudo.substring(PseudoClasses.MatchesCss.length + 1));
|
|
21799
|
-
converted.push(COMMA);
|
|
21800
|
-
break;
|
|
21801
|
-
default:
|
|
21802
|
-
converted.push(pseudo);
|
|
21803
|
-
converted.push(OPEN_PARENTHESIS);
|
|
21804
|
-
break;
|
|
21805
|
-
}
|
|
21806
|
-
};
|
|
21807
|
-
while (!stream.isEof()) {
|
|
21808
|
-
const token = stream.getOrFail();
|
|
21809
|
-
if (token.type === TokenType$1.Colon) {
|
|
21810
|
-
// Advance colon
|
|
21811
|
-
stream.advance();
|
|
21812
|
-
converted.push(COLON);
|
|
21813
|
-
const tempToken = stream.getOrFail();
|
|
21814
|
-
// Double colon is a pseudo-element
|
|
21815
|
-
if (tempToken.type === TokenType$1.Colon) {
|
|
21816
|
-
stream.advance();
|
|
21817
|
-
converted.push(COLON);
|
|
21818
|
-
continue;
|
|
21819
|
-
}
|
|
21820
|
-
if (tempToken.type === TokenType$1.Ident) {
|
|
21821
|
-
const name = stream.source.slice(tempToken.start, tempToken.end);
|
|
21822
|
-
if (PSEUDO_ELEMENT_NAMES.has(name)) {
|
|
21823
|
-
// Add an extra colon to the name
|
|
21824
|
-
converted.push(COLON);
|
|
21825
|
-
converted.push(name);
|
|
21826
|
-
} else {
|
|
21827
|
-
// Add the name as is
|
|
21828
|
-
converted.push(name);
|
|
21829
|
-
}
|
|
21830
|
-
// Advance the names
|
|
21831
|
-
stream.advance();
|
|
21832
|
-
} else if (tempToken.type === TokenType$1.Function) {
|
|
21833
|
-
const name = stream.source.slice(tempToken.start, tempToken.end - 1); // omit the last parenthesis
|
|
21834
|
-
// :-abp-contains(...) → :contains(...)
|
|
21835
|
-
// :has-text(...) → :contains(...)
|
|
21836
|
-
// :-abp-has(...) → :has(...)
|
|
21837
|
-
convertAndPushPseudo(name);
|
|
21838
|
-
// Advance the function name
|
|
21839
|
-
stream.advance();
|
|
21840
|
-
}
|
|
21841
|
-
} else if (token.type === TokenType$1.OpenSquareBracket) {
|
|
21842
|
-
let tempToken;
|
|
21843
|
-
const {
|
|
21844
|
-
start
|
|
21845
|
-
} = token;
|
|
21846
|
-
stream.advance();
|
|
21847
|
-
// Converts legacy Extended CSS selectors to the modern Extended CSS syntax.
|
|
21848
|
-
// For example:
|
|
21849
|
-
// - `[-ext-has=...]` → `:has(...)`
|
|
21850
|
-
// - `[-ext-contains=...]` → `:contains(...)`
|
|
21851
|
-
// - `[-ext-matches-css-before=...]` → `:matches-css(before, ...)`
|
|
21852
|
-
stream.skipWhitespace();
|
|
21853
|
-
stream.expect(TokenType$1.Ident);
|
|
21854
|
-
tempToken = stream.getOrFail();
|
|
21855
|
-
let attr = stream.source.slice(tempToken.start, tempToken.end);
|
|
21856
|
-
// Skip if the attribute name is not a legacy Extended CSS one
|
|
21857
|
-
if (!(attr.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX) || attr.startsWith(ABP_EXT_CSS_PREFIX))) {
|
|
21858
|
-
converted.push(stream.source.slice(start, tempToken.end));
|
|
21859
|
-
stream.advance();
|
|
21860
|
-
continue;
|
|
21861
|
-
}
|
|
21862
|
-
if (attr.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX)) {
|
|
21863
|
-
attr = attr.slice(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX.length);
|
|
21864
|
-
}
|
|
21865
|
-
stream.advance();
|
|
21866
|
-
stream.skipWhitespace();
|
|
21867
|
-
// Next token should be an equality operator (=), because Extended CSS attribute selectors
|
|
21868
|
-
// do not support other operators
|
|
21869
|
-
stream.expect(TokenType$1.Delim, {
|
|
21870
|
-
value: EQUALS
|
|
21871
|
-
});
|
|
21872
|
-
stream.advance();
|
|
21873
|
-
// Skip optional whitespace after the operator
|
|
21874
|
-
stream.skipWhitespace();
|
|
21875
|
-
// Parse attribute value
|
|
21876
|
-
tempToken = stream.getOrFail();
|
|
21877
|
-
// According to the spec, attribute value should be an identifier or a string
|
|
21878
|
-
if (tempToken.type !== TokenType$1.Ident && tempToken.type !== TokenType$1.String) {
|
|
21879
|
-
throw new Error(sprintf(ERROR_MESSAGES$1.INVALID_ATTRIBUTE_VALUE, getFormattedTokenName(tempToken.type), stream.source.slice(tempToken.start, tempToken.end)));
|
|
21880
|
-
}
|
|
21881
|
-
const value = stream.source.slice(tempToken.start, tempToken.end);
|
|
21882
|
-
// Advance the attribute value
|
|
21883
|
-
stream.advance();
|
|
21884
|
-
// Skip optional whitespace after the attribute value
|
|
21885
|
-
stream.skipWhitespace();
|
|
21886
|
-
// Next character should be a closing square bracket
|
|
21887
|
-
// We don't allow flags for Extended CSS attribute selectors
|
|
21888
|
-
stream.expect(TokenType$1.CloseSquareBracket);
|
|
21889
|
-
stream.advance();
|
|
21890
|
-
converted.push(COLON);
|
|
21891
|
-
convertAndPushPseudo(attr);
|
|
21892
|
-
let processedValue = value.slice(1, -1); // omit the quotes
|
|
21893
|
-
if (attr === PseudoClasses.Has) {
|
|
21894
|
-
// TODO: Optimize this to avoid double tokenization
|
|
21895
|
-
processedValue = CssSelectorConverter.convertToAdg(processedValue).result;
|
|
21896
|
-
}
|
|
21897
|
-
converted.push(processedValue);
|
|
21898
|
-
converted.push(CLOSE_PARENTHESIS);
|
|
21899
|
-
} else {
|
|
21900
|
-
converted.push(stream.source.slice(token.start, token.end));
|
|
21901
|
-
// Advance the token
|
|
21902
|
-
stream.advance();
|
|
21903
|
-
}
|
|
21904
|
-
}
|
|
21905
|
-
const convertedSelectorList = converted.join(EMPTY);
|
|
21906
|
-
return createConversionResult(convertedSelectorList, stream.source !== convertedSelectorList);
|
|
21907
|
-
}
|
|
21908
|
-
}
|
|
21869
|
+
const scriptletsCompatibilityTable = new ScriptletsCompatibilityTable(scriptletsCompatibilityTableData);
|
|
21909
21870
|
|
|
21871
|
+
/* eslint-disable no-bitwise */
|
|
21910
21872
|
/**
|
|
21911
|
-
* @file
|
|
21873
|
+
* @file Platform schema.
|
|
21912
21874
|
*/
|
|
21913
21875
|
/**
|
|
21914
|
-
*
|
|
21876
|
+
* Platform separator, e.g. 'adg_os_any|adg_safari_any' means any AdGuard OS platform and
|
|
21877
|
+
* any AdGuard Safari content blocker platform.
|
|
21878
|
+
*/
|
|
21879
|
+
const PLATFORM_SEPARATOR = '|';
|
|
21880
|
+
/**
|
|
21881
|
+
* Platform negation character, e.g. 'adg_any|~adg_safari_any' means any AdGuard product except
|
|
21882
|
+
* Safari content blockers.
|
|
21883
|
+
*/
|
|
21884
|
+
const PLATFORM_NEGATION = '~';
|
|
21885
|
+
/**
|
|
21886
|
+
* Parses a raw platform string into a platform bitmask.
|
|
21915
21887
|
*
|
|
21916
|
-
* @
|
|
21888
|
+
* @param rawPlatforms Raw platform string, e.g. 'adg_safari_any|adg_os_any'.
|
|
21889
|
+
*
|
|
21890
|
+
* @returns Platform bitmask.
|
|
21917
21891
|
*/
|
|
21918
|
-
|
|
21919
|
-
|
|
21920
|
-
|
|
21921
|
-
|
|
21922
|
-
|
|
21923
|
-
|
|
21924
|
-
|
|
21925
|
-
|
|
21926
|
-
|
|
21927
|
-
|
|
21928
|
-
static convertToAdg(rule) {
|
|
21929
|
-
const separator = rule.separator.value;
|
|
21930
|
-
let convertedSeparator = separator;
|
|
21931
|
-
const stream = new CssTokenStream(rule.body.selectorList.value);
|
|
21932
|
-
const convertedSelectorList = CssSelectorConverter.convertToAdg(stream);
|
|
21933
|
-
// Change the separator if the rule contains ExtendedCSS elements,
|
|
21934
|
-
// but do not force non-extended CSS separator if the rule does not contain any ExtendedCSS selectors,
|
|
21935
|
-
// because sometimes we use it to force executing ExtendedCSS library.
|
|
21936
|
-
if (stream.hasAnySelectorExtendedCssNodeStrict() || rule.body.remove) {
|
|
21937
|
-
convertedSeparator = rule.exception ? CosmeticRuleSeparator.AdgExtendedCssInjectionException : CosmeticRuleSeparator.AdgExtendedCssInjection;
|
|
21938
|
-
} else if (rule.syntax !== AdblockSyntax.Adg) {
|
|
21939
|
-
// If the original rule syntax is not AdGuard, use the default separator
|
|
21940
|
-
// e.g. if the input rule is from uBO, we need to convert ## to #$#.
|
|
21941
|
-
convertedSeparator = rule.exception ? CosmeticRuleSeparator.AdgCssInjectionException : CosmeticRuleSeparator.AdgCssInjection;
|
|
21892
|
+
const parseRawPlatforms = rawPlatforms => {
|
|
21893
|
+
// e.g. 'adg_safari_any|adg_os_any'
|
|
21894
|
+
const rawPlatformList = rawPlatforms.split(PLATFORM_SEPARATOR).map(rawPlatform => rawPlatform.trim());
|
|
21895
|
+
let result = 0;
|
|
21896
|
+
for (let rawPlatform of rawPlatformList) {
|
|
21897
|
+
// negation, e.g. 'adg_any|~adg_safari_any' means any AdGuard product except Safari content blockers
|
|
21898
|
+
let negated = false;
|
|
21899
|
+
if (rawPlatform.startsWith(PLATFORM_NEGATION)) {
|
|
21900
|
+
negated = true;
|
|
21901
|
+
rawPlatform = rawPlatform.slice(1).trim();
|
|
21942
21902
|
}
|
|
21943
|
-
|
|
21944
|
-
if (
|
|
21945
|
-
|
|
21946
|
-
|
|
21947
|
-
|
|
21948
|
-
|
|
21949
|
-
|
|
21950
|
-
|
|
21903
|
+
const platform = SPECIFIC_PLATFORM_MAP.get(rawPlatform) ?? GENERIC_PLATFORM_MAP.get(rawPlatform);
|
|
21904
|
+
if (isUndefined(platform)) {
|
|
21905
|
+
throw new Error(`Unknown platform: ${rawPlatform}`);
|
|
21906
|
+
}
|
|
21907
|
+
if (negated) {
|
|
21908
|
+
result &= ~platform;
|
|
21909
|
+
} else {
|
|
21910
|
+
result |= platform;
|
|
21951
21911
|
}
|
|
21952
|
-
// Otherwise, return the original rule
|
|
21953
|
-
return createNodeConversionResult([rule], false);
|
|
21954
21912
|
}
|
|
21955
|
-
|
|
21956
|
-
|
|
21913
|
+
if (result === 0) {
|
|
21914
|
+
throw new Error('No platforms specified');
|
|
21915
|
+
}
|
|
21916
|
+
return result;
|
|
21917
|
+
};
|
|
21957
21918
|
/**
|
|
21958
|
-
*
|
|
21919
|
+
* Platform schema.
|
|
21959
21920
|
*/
|
|
21960
|
-
|
|
21961
|
-
|
|
21962
|
-
|
|
21963
|
-
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
21964
|
-
*/
|
|
21965
|
-
class ElementHidingRuleConverter extends RuleConverterBase {
|
|
21966
|
-
/**
|
|
21967
|
-
* Converts an element hiding rule to AdGuard format, if possible.
|
|
21968
|
-
*
|
|
21969
|
-
* @param rule Rule node to convert
|
|
21970
|
-
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
21971
|
-
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
21972
|
-
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
21973
|
-
* @throws If the rule is invalid or cannot be converted
|
|
21974
|
-
*/
|
|
21975
|
-
static convertToAdg(rule) {
|
|
21976
|
-
const separator = rule.separator.value;
|
|
21977
|
-
let convertedSeparator = separator;
|
|
21978
|
-
const stream = new CssTokenStream(rule.body.selectorList.value);
|
|
21979
|
-
const convertedSelectorList = CssSelectorConverter.convertToAdg(stream);
|
|
21980
|
-
// Change the separator if the rule contains ExtendedCSS elements,
|
|
21981
|
-
// but do not force non-extended CSS separator if the rule does not contain any ExtendedCSS selectors,
|
|
21982
|
-
// because sometimes we use it to force executing ExtendedCSS library.
|
|
21983
|
-
if (stream.hasAnySelectorExtendedCssNodeStrict()) {
|
|
21984
|
-
convertedSeparator = rule.exception ? CosmeticRuleSeparator.ExtendedElementHidingException : CosmeticRuleSeparator.ExtendedElementHiding;
|
|
21985
|
-
}
|
|
21986
|
-
// Check if the rule needs to be converted
|
|
21987
|
-
if (!(rule.syntax === AdblockSyntax.Common || rule.syntax === AdblockSyntax.Adg) || separator !== convertedSeparator || convertedSelectorList.isConverted) {
|
|
21988
|
-
// TODO: Replace with custom clone method
|
|
21989
|
-
const ruleClone = clone(rule);
|
|
21990
|
-
ruleClone.syntax = AdblockSyntax.Adg;
|
|
21991
|
-
ruleClone.separator.value = convertedSeparator;
|
|
21992
|
-
ruleClone.body.selectorList.value = convertedSelectorList.result;
|
|
21993
|
-
return createNodeConversionResult([ruleClone], true);
|
|
21994
|
-
}
|
|
21995
|
-
// Otherwise, return the original rule
|
|
21996
|
-
return createNodeConversionResult([rule], false);
|
|
21997
|
-
}
|
|
21921
|
+
zod.string().min(1).transform(value => parseRawPlatforms(value));
|
|
21922
|
+
function getDefaultExportFromCjs(x) {
|
|
21923
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
21998
21924
|
}
|
|
21925
|
+
var mapObj$1 = {
|
|
21926
|
+
exports: {}
|
|
21927
|
+
};
|
|
21928
|
+
const isObject$1 = value => typeof value === 'object' && value !== null;
|
|
21929
|
+
const mapObjectSkip = Symbol('skip');
|
|
21999
21930
|
|
|
22000
|
-
|
|
22001
|
-
|
|
22002
|
-
|
|
22003
|
-
|
|
22004
|
-
|
|
22005
|
-
|
|
22006
|
-
|
|
22007
|
-
* @param modifiers Rule modifiers (optional, default: undefined)
|
|
22008
|
-
* @param exception Exception rule flag (optional, default: false)
|
|
22009
|
-
* @param syntax Adblock syntax (optional, default: Common)
|
|
22010
|
-
* @returns Network rule node
|
|
22011
|
-
*/
|
|
22012
|
-
function createNetworkRuleNode(pattern, modifiers = undefined, exception = false, syntax = AdblockSyntax.Common) {
|
|
22013
|
-
const result = {
|
|
22014
|
-
category: RuleCategory.Network,
|
|
22015
|
-
type: NetworkRuleType.NetworkRule,
|
|
22016
|
-
syntax,
|
|
22017
|
-
exception,
|
|
22018
|
-
pattern: {
|
|
22019
|
-
type: 'Value',
|
|
22020
|
-
value: pattern
|
|
22021
|
-
}
|
|
21931
|
+
// Customized for this use-case
|
|
21932
|
+
const isObjectCustom = value => isObject$1(value) && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
|
|
21933
|
+
const mapObject = (object, mapper, options, isSeen = new WeakMap()) => {
|
|
21934
|
+
options = {
|
|
21935
|
+
deep: false,
|
|
21936
|
+
target: {},
|
|
21937
|
+
...options
|
|
22022
21938
|
};
|
|
22023
|
-
if (
|
|
22024
|
-
|
|
21939
|
+
if (isSeen.has(object)) {
|
|
21940
|
+
return isSeen.get(object);
|
|
22025
21941
|
}
|
|
22026
|
-
|
|
22027
|
-
|
|
22028
|
-
|
|
22029
|
-
|
|
22030
|
-
|
|
22031
|
-
|
|
22032
|
-
|
|
22033
|
-
|
|
22034
|
-
|
|
22035
|
-
|
|
22036
|
-
|
|
22037
|
-
|
|
22038
|
-
|
|
22039
|
-
/**
|
|
22040
|
-
* Converter for request header removal rules
|
|
22041
|
-
*
|
|
22042
|
-
* @todo Implement `convertToUbo` (ABP currently doesn't support header removal rules)
|
|
22043
|
-
*/
|
|
22044
|
-
class HeaderRemovalRuleConverter extends RuleConverterBase {
|
|
22045
|
-
/**
|
|
22046
|
-
* Converts a header removal rule to AdGuard syntax, if possible.
|
|
22047
|
-
*
|
|
22048
|
-
* @param rule Rule node to convert
|
|
22049
|
-
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
22050
|
-
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
22051
|
-
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
22052
|
-
* @throws If the rule is invalid or cannot be converted
|
|
22053
|
-
* @example
|
|
22054
|
-
* If the input rule is:
|
|
22055
|
-
* ```adblock
|
|
22056
|
-
* example.com##^responseheader(header-name)
|
|
22057
|
-
* ```
|
|
22058
|
-
* The output will be:
|
|
22059
|
-
* ```adblock
|
|
22060
|
-
* ||example.com^$removeheader=header-name
|
|
22061
|
-
* ```
|
|
22062
|
-
*/
|
|
22063
|
-
static convertToAdg(rule) {
|
|
22064
|
-
// TODO: Add support for ABP syntax once it starts supporting header removal rules
|
|
22065
|
-
// Leave the rule as is if it's not a header removal rule
|
|
22066
|
-
if (rule.category !== RuleCategory.Cosmetic || rule.type !== CosmeticRuleType.HtmlFilteringRule) {
|
|
22067
|
-
return createNodeConversionResult([rule], false);
|
|
22068
|
-
}
|
|
22069
|
-
const stream = new CssTokenStream(rule.body.value);
|
|
22070
|
-
let token;
|
|
22071
|
-
// Skip leading whitespace
|
|
22072
|
-
stream.skipWhitespace();
|
|
22073
|
-
// Next token should be the `^` followed by a `responseheader` function
|
|
22074
|
-
token = stream.get();
|
|
22075
|
-
if (!token || token.type !== TokenType$1.Delim || rule.body.value[token.start] !== UBO_HTML_MASK) {
|
|
22076
|
-
return createNodeConversionResult([rule], false);
|
|
22077
|
-
}
|
|
22078
|
-
stream.advance();
|
|
22079
|
-
token = stream.get();
|
|
22080
|
-
if (!token) {
|
|
22081
|
-
return createNodeConversionResult([rule], false);
|
|
22082
|
-
}
|
|
22083
|
-
const functionName = rule.body.value.slice(token.start, token.end - 1);
|
|
22084
|
-
if (functionName !== UBO_RESPONSEHEADER_FN) {
|
|
22085
|
-
return createNodeConversionResult([rule], false);
|
|
21942
|
+
isSeen.set(object, options.target);
|
|
21943
|
+
const {
|
|
21944
|
+
target
|
|
21945
|
+
} = options;
|
|
21946
|
+
delete options.target;
|
|
21947
|
+
const mapArray = array => array.map(element => isObjectCustom(element) ? mapObject(element, mapper, options, isSeen) : element);
|
|
21948
|
+
if (Array.isArray(object)) {
|
|
21949
|
+
return mapArray(object);
|
|
21950
|
+
}
|
|
21951
|
+
for (const [key, value] of Object.entries(object)) {
|
|
21952
|
+
const mapResult = mapper(key, value, object);
|
|
21953
|
+
if (mapResult === mapObjectSkip) {
|
|
21954
|
+
continue;
|
|
22086
21955
|
}
|
|
22087
|
-
|
|
22088
|
-
|
|
22089
|
-
|
|
22090
|
-
|
|
22091
|
-
|
|
22092
|
-
|
|
22093
|
-
|
|
22094
|
-
throw new RuleConversionError(ERROR_MESSAGES.EMPTY_PARAMETER);
|
|
21956
|
+
let [newKey, newValue, {
|
|
21957
|
+
shouldRecurse = true
|
|
21958
|
+
} = {}] = mapResult;
|
|
21959
|
+
|
|
21960
|
+
// Drop `__proto__` keys.
|
|
21961
|
+
if (newKey === '__proto__') {
|
|
21962
|
+
continue;
|
|
22095
21963
|
}
|
|
22096
|
-
|
|
22097
|
-
|
|
22098
|
-
// Skip trailing whitespace after the function call
|
|
22099
|
-
stream.skipWhitespace();
|
|
22100
|
-
// Expect the end of the rule - so nothing should be left in the stream
|
|
22101
|
-
if (!stream.isEof()) {
|
|
22102
|
-
token = stream.getOrFail();
|
|
22103
|
-
throw new RuleConversionError(sprintf(ERROR_MESSAGES.EXPECTED_END_OF_RULE, getFormattedTokenName(token.type)));
|
|
21964
|
+
if (options.deep && shouldRecurse && isObjectCustom(newValue)) {
|
|
21965
|
+
newValue = Array.isArray(newValue) ? mapArray(newValue) : mapObject(newValue, mapper, options, isSeen);
|
|
22104
21966
|
}
|
|
22105
|
-
|
|
22106
|
-
|
|
22107
|
-
|
|
22108
|
-
|
|
22109
|
-
|
|
22110
|
-
|
|
22111
|
-
|
|
22112
|
-
|
|
22113
|
-
|
|
22114
|
-
|
|
22115
|
-
|
|
22116
|
-
|
|
22117
|
-
|
|
21967
|
+
target[newKey] = newValue;
|
|
21968
|
+
}
|
|
21969
|
+
return target;
|
|
21970
|
+
};
|
|
21971
|
+
mapObj$1.exports = (object, mapper, options) => {
|
|
21972
|
+
if (!isObject$1(object)) {
|
|
21973
|
+
throw new TypeError(`Expected an object, got \`${object}\` (${typeof object})`);
|
|
21974
|
+
}
|
|
21975
|
+
return mapObject(object, mapper, options);
|
|
21976
|
+
};
|
|
21977
|
+
mapObj$1.exports.mapObjectSkip = mapObjectSkip;
|
|
21978
|
+
var mapObjExports = mapObj$1.exports;
|
|
21979
|
+
var camelcase = {
|
|
21980
|
+
exports: {}
|
|
21981
|
+
};
|
|
21982
|
+
const UPPERCASE = /[\p{Lu}]/u;
|
|
21983
|
+
const LOWERCASE = /[\p{Ll}]/u;
|
|
21984
|
+
const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
|
|
21985
|
+
const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
|
|
21986
|
+
const SEPARATORS = /[_.\- ]+/;
|
|
21987
|
+
const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
|
|
21988
|
+
const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
|
|
21989
|
+
const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');
|
|
21990
|
+
const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
|
|
21991
|
+
let isLastCharLower = false;
|
|
21992
|
+
let isLastCharUpper = false;
|
|
21993
|
+
let isLastLastCharUpper = false;
|
|
21994
|
+
for (let i = 0; i < string.length; i++) {
|
|
21995
|
+
const character = string[i];
|
|
21996
|
+
if (isLastCharLower && UPPERCASE.test(character)) {
|
|
21997
|
+
string = string.slice(0, i) + '-' + string.slice(i);
|
|
21998
|
+
isLastCharLower = false;
|
|
21999
|
+
isLastLastCharUpper = isLastCharUpper;
|
|
22000
|
+
isLastCharUpper = true;
|
|
22001
|
+
i++;
|
|
22002
|
+
} else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) {
|
|
22003
|
+
string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
|
|
22004
|
+
isLastLastCharUpper = isLastCharUpper;
|
|
22005
|
+
isLastCharUpper = false;
|
|
22006
|
+
isLastCharLower = true;
|
|
22007
|
+
} else {
|
|
22008
|
+
isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
|
|
22009
|
+
isLastLastCharUpper = isLastCharUpper;
|
|
22010
|
+
isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
|
|
22118
22011
|
}
|
|
22119
|
-
// Prepare network rule modifiers
|
|
22120
|
-
const modifiers = createModifierListNode();
|
|
22121
|
-
modifiers.children.push(createModifierNode(ADG_REMOVEHEADER_MODIFIER, param));
|
|
22122
|
-
// Construct the network rule
|
|
22123
|
-
return createNodeConversionResult([createNetworkRuleNode(pattern.join(EMPTY), modifiers,
|
|
22124
|
-
// Copy the exception flag
|
|
22125
|
-
rule.exception, AdblockSyntax.Adg)], true);
|
|
22126
22012
|
}
|
|
22127
|
-
|
|
22128
|
-
|
|
22129
|
-
|
|
22130
|
-
|
|
22131
|
-
|
|
22132
|
-
|
|
22133
|
-
|
|
22134
|
-
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
22138
|
-
|
|
22139
|
-
|
|
22140
|
-
|
|
22141
|
-
|
|
22142
|
-
|
|
22143
|
-
|
|
22144
|
-
|
|
22145
|
-
|
|
22146
|
-
|
|
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
|
-
|
|
22013
|
+
return string;
|
|
22014
|
+
};
|
|
22015
|
+
const preserveConsecutiveUppercase = (input, toLowerCase) => {
|
|
22016
|
+
LEADING_CAPITAL.lastIndex = 0;
|
|
22017
|
+
return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1));
|
|
22018
|
+
};
|
|
22019
|
+
const postProcess = (input, toUpperCase) => {
|
|
22020
|
+
SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
|
|
22021
|
+
NUMBERS_AND_IDENTIFIER.lastIndex = 0;
|
|
22022
|
+
return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier)).replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m));
|
|
22023
|
+
};
|
|
22024
|
+
const camelCase$1 = (input, options) => {
|
|
22025
|
+
if (!(typeof input === 'string' || Array.isArray(input))) {
|
|
22026
|
+
throw new TypeError('Expected the input to be `string | string[]`');
|
|
22027
|
+
}
|
|
22028
|
+
options = {
|
|
22029
|
+
pascalCase: false,
|
|
22030
|
+
preserveConsecutiveUppercase: false,
|
|
22031
|
+
...options
|
|
22032
|
+
};
|
|
22033
|
+
if (Array.isArray(input)) {
|
|
22034
|
+
input = input.map(x => x.trim()).filter(x => x.length).join('-');
|
|
22035
|
+
} else {
|
|
22036
|
+
input = input.trim();
|
|
22037
|
+
}
|
|
22038
|
+
if (input.length === 0) {
|
|
22039
|
+
return '';
|
|
22040
|
+
}
|
|
22041
|
+
const toLowerCase = options.locale === false ? string => string.toLowerCase() : string => string.toLocaleLowerCase(options.locale);
|
|
22042
|
+
const toUpperCase = options.locale === false ? string => string.toUpperCase() : string => string.toLocaleUpperCase(options.locale);
|
|
22043
|
+
if (input.length === 1) {
|
|
22044
|
+
return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
|
|
22045
|
+
}
|
|
22046
|
+
const hasUpperCase = input !== toLowerCase(input);
|
|
22047
|
+
if (hasUpperCase) {
|
|
22048
|
+
input = preserveCamelCase(input, toLowerCase, toUpperCase);
|
|
22049
|
+
}
|
|
22050
|
+
input = input.replace(LEADING_SEPARATORS, '');
|
|
22051
|
+
if (options.preserveConsecutiveUppercase) {
|
|
22052
|
+
input = preserveConsecutiveUppercase(input, toLowerCase);
|
|
22053
|
+
} else {
|
|
22054
|
+
input = toLowerCase(input);
|
|
22055
|
+
}
|
|
22056
|
+
if (options.pascalCase) {
|
|
22057
|
+
input = toUpperCase(input.charAt(0)) + input.slice(1);
|
|
22058
|
+
}
|
|
22059
|
+
return postProcess(input, toUpperCase);
|
|
22060
|
+
};
|
|
22061
|
+
camelcase.exports = camelCase$1;
|
|
22062
|
+
// TODO: Remove this for the next major release
|
|
22063
|
+
camelcase.exports.default = camelCase$1;
|
|
22064
|
+
var camelcaseExports = camelcase.exports;
|
|
22065
|
+
class QuickLRU {
|
|
22066
|
+
constructor(options = {}) {
|
|
22067
|
+
if (!(options.maxSize && options.maxSize > 0)) {
|
|
22068
|
+
throw new TypeError('`maxSize` must be a number greater than 0');
|
|
22175
22069
|
}
|
|
22176
|
-
|
|
22177
|
-
|
|
22178
|
-
|
|
22179
|
-
|
|
22180
|
-
|
|
22181
|
-
|
|
22182
|
-
|
|
22183
|
-
|
|
22070
|
+
this.maxSize = options.maxSize;
|
|
22071
|
+
this.onEviction = options.onEviction;
|
|
22072
|
+
this.cache = new Map();
|
|
22073
|
+
this.oldCache = new Map();
|
|
22074
|
+
this._size = 0;
|
|
22075
|
+
}
|
|
22076
|
+
_set(key, value) {
|
|
22077
|
+
this.cache.set(key, value);
|
|
22078
|
+
this._size++;
|
|
22079
|
+
if (this._size >= this.maxSize) {
|
|
22080
|
+
this._size = 0;
|
|
22081
|
+
if (typeof this.onEviction === 'function') {
|
|
22082
|
+
for (const [key, value] of this.oldCache.entries()) {
|
|
22083
|
+
this.onEviction(key, value);
|
|
22184
22084
|
}
|
|
22185
|
-
convertedModifiers = AdgCosmeticRuleModifierConverter.convertFromUbo(rule.modifiers);
|
|
22186
|
-
} else if (rule.syntax === AdblockSyntax.Abp) {
|
|
22187
|
-
// TODO: Implement once ABP starts supporting cosmetic rule modifiers
|
|
22188
|
-
throw new RuleConversionError('ABP don\'t support cosmetic rule modifiers');
|
|
22189
22085
|
}
|
|
22086
|
+
this.oldCache = this.cache;
|
|
22087
|
+
this.cache = new Map();
|
|
22190
22088
|
}
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22194
|
-
|
|
22195
|
-
|
|
22196
|
-
|
|
22197
|
-
|
|
22198
|
-
|
|
22199
|
-
|
|
22089
|
+
}
|
|
22090
|
+
get(key) {
|
|
22091
|
+
if (this.cache.has(key)) {
|
|
22092
|
+
return this.cache.get(key);
|
|
22093
|
+
}
|
|
22094
|
+
if (this.oldCache.has(key)) {
|
|
22095
|
+
const value = this.oldCache.get(key);
|
|
22096
|
+
this.oldCache.delete(key);
|
|
22097
|
+
this._set(key, value);
|
|
22098
|
+
return value;
|
|
22200
22099
|
}
|
|
22201
|
-
return createNodeConversionResult([rule], false);
|
|
22202
22100
|
}
|
|
22203
|
-
|
|
22204
|
-
|
|
22205
|
-
|
|
22206
|
-
|
|
22207
|
-
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
*/
|
|
22211
|
-
const ABP_RESOURCE_PREFIX = 'abp-resource:';
|
|
22212
|
-
const ABP_RESOURCE_PREFIX_LENGTH = ABP_RESOURCE_PREFIX.length;
|
|
22213
|
-
/**
|
|
22214
|
-
* Transforms the name of an ABP redirect to a normalized form.
|
|
22215
|
-
*
|
|
22216
|
-
* @param name Redirect name to normalize.
|
|
22217
|
-
*
|
|
22218
|
-
* @returns Normalized redirect name.
|
|
22219
|
-
*
|
|
22220
|
-
* @example
|
|
22221
|
-
* abpRedirectNameNormalizer('abp-resource:my-resource') // => 'my-resource'
|
|
22222
|
-
*/
|
|
22223
|
-
const abpRedirectNameNormalizer = name => {
|
|
22224
|
-
if (name.startsWith(ABP_RESOURCE_PREFIX)) {
|
|
22225
|
-
return name.slice(ABP_RESOURCE_PREFIX_LENGTH);
|
|
22101
|
+
set(key, value) {
|
|
22102
|
+
if (this.cache.has(key)) {
|
|
22103
|
+
this.cache.set(key, value);
|
|
22104
|
+
} else {
|
|
22105
|
+
this._set(key, value);
|
|
22106
|
+
}
|
|
22107
|
+
return this;
|
|
22226
22108
|
}
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
/**
|
|
22230
|
-
* Compatibility table for redirects.
|
|
22231
|
-
*/
|
|
22232
|
-
class RedirectsCompatibilityTable extends CompatibilityTableBase {
|
|
22233
|
-
/**
|
|
22234
|
-
* Creates a new instance of the compatibility table for redirects.
|
|
22235
|
-
*
|
|
22236
|
-
* @param data Compatibility table data.
|
|
22237
|
-
*/
|
|
22238
|
-
constructor(data) {
|
|
22239
|
-
super(data, abpRedirectNameNormalizer);
|
|
22109
|
+
has(key) {
|
|
22110
|
+
return this.cache.has(key) || this.oldCache.has(key);
|
|
22240
22111
|
}
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
*/
|
|
22245
|
-
deepFreeze(redirectsCompatibilityTableData);
|
|
22246
|
-
/**
|
|
22247
|
-
* Compatibility table instance for redirects.
|
|
22248
|
-
*/
|
|
22249
|
-
const redirectsCompatibilityTable = new RedirectsCompatibilityTable(redirectsCompatibilityTableData);
|
|
22250
|
-
|
|
22251
|
-
/**
|
|
22252
|
-
* @file Compatibility tables for scriptlets.
|
|
22253
|
-
*/
|
|
22254
|
-
/**
|
|
22255
|
-
* Compatibility table for scriptlets.
|
|
22256
|
-
*/
|
|
22257
|
-
class ScriptletsCompatibilityTable extends CompatibilityTableBase {}
|
|
22258
|
-
/**
|
|
22259
|
-
* Deep freeze the compatibility table data to avoid accidental modifications.
|
|
22260
|
-
*/
|
|
22261
|
-
deepFreeze(scriptletsCompatibilityTableData);
|
|
22262
|
-
/**
|
|
22263
|
-
* Compatibility table instance for scriptlets.
|
|
22264
|
-
*/
|
|
22265
|
-
const scriptletsCompatibilityTable = new ScriptletsCompatibilityTable(scriptletsCompatibilityTableData);
|
|
22266
|
-
|
|
22267
|
-
/* eslint-disable no-bitwise */
|
|
22268
|
-
/**
|
|
22269
|
-
* @file Platform schema.
|
|
22270
|
-
*/
|
|
22271
|
-
/**
|
|
22272
|
-
* Platform separator, e.g. 'adg_os_any|adg_safari_any' means any AdGuard OS platform and
|
|
22273
|
-
* any AdGuard Safari content blocker platform.
|
|
22274
|
-
*/
|
|
22275
|
-
const PLATFORM_SEPARATOR = '|';
|
|
22276
|
-
/**
|
|
22277
|
-
* Platform negation character, e.g. 'adg_any|~adg_safari_any' means any AdGuard product except
|
|
22278
|
-
* Safari content blockers.
|
|
22279
|
-
*/
|
|
22280
|
-
const PLATFORM_NEGATION = '~';
|
|
22281
|
-
/**
|
|
22282
|
-
* Parses a raw platform string into a platform bitmask.
|
|
22283
|
-
*
|
|
22284
|
-
* @param rawPlatforms Raw platform string, e.g. 'adg_safari_any|adg_os_any'.
|
|
22285
|
-
*
|
|
22286
|
-
* @returns Platform bitmask.
|
|
22287
|
-
*/
|
|
22288
|
-
const parseRawPlatforms = rawPlatforms => {
|
|
22289
|
-
// e.g. 'adg_safari_any|adg_os_any'
|
|
22290
|
-
const rawPlatformList = rawPlatforms.split(PLATFORM_SEPARATOR).map(rawPlatform => rawPlatform.trim());
|
|
22291
|
-
let result = 0;
|
|
22292
|
-
for (let rawPlatform of rawPlatformList) {
|
|
22293
|
-
// negation, e.g. 'adg_any|~adg_safari_any' means any AdGuard product except Safari content blockers
|
|
22294
|
-
let negated = false;
|
|
22295
|
-
if (rawPlatform.startsWith(PLATFORM_NEGATION)) {
|
|
22296
|
-
negated = true;
|
|
22297
|
-
rawPlatform = rawPlatform.slice(1).trim();
|
|
22298
|
-
}
|
|
22299
|
-
const platform = SPECIFIC_PLATFORM_MAP.get(rawPlatform) ?? GENERIC_PLATFORM_MAP.get(rawPlatform);
|
|
22300
|
-
if (isUndefined(platform)) {
|
|
22301
|
-
throw new Error(`Unknown platform: ${rawPlatform}`);
|
|
22112
|
+
peek(key) {
|
|
22113
|
+
if (this.cache.has(key)) {
|
|
22114
|
+
return this.cache.get(key);
|
|
22302
22115
|
}
|
|
22303
|
-
if (
|
|
22304
|
-
|
|
22305
|
-
} else {
|
|
22306
|
-
result |= platform;
|
|
22116
|
+
if (this.oldCache.has(key)) {
|
|
22117
|
+
return this.oldCache.get(key);
|
|
22307
22118
|
}
|
|
22308
22119
|
}
|
|
22309
|
-
|
|
22310
|
-
|
|
22120
|
+
delete(key) {
|
|
22121
|
+
const deleted = this.cache.delete(key);
|
|
22122
|
+
if (deleted) {
|
|
22123
|
+
this._size--;
|
|
22124
|
+
}
|
|
22125
|
+
return this.oldCache.delete(key) || deleted;
|
|
22311
22126
|
}
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
*/
|
|
22317
|
-
zod.string().min(1).transform(value => parseRawPlatforms(value));
|
|
22318
|
-
function getDefaultExportFromCjs(x) {
|
|
22319
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
22320
|
-
}
|
|
22321
|
-
var mapObj$1 = {
|
|
22322
|
-
exports: {}
|
|
22323
|
-
};
|
|
22324
|
-
const isObject$1 = value => typeof value === 'object' && value !== null;
|
|
22325
|
-
const mapObjectSkip = Symbol('skip');
|
|
22326
|
-
|
|
22327
|
-
// Customized for this use-case
|
|
22328
|
-
const isObjectCustom = value => isObject$1(value) && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
|
|
22329
|
-
const mapObject = (object, mapper, options, isSeen = new WeakMap()) => {
|
|
22330
|
-
options = {
|
|
22331
|
-
deep: false,
|
|
22332
|
-
target: {},
|
|
22333
|
-
...options
|
|
22334
|
-
};
|
|
22335
|
-
if (isSeen.has(object)) {
|
|
22336
|
-
return isSeen.get(object);
|
|
22127
|
+
clear() {
|
|
22128
|
+
this.cache.clear();
|
|
22129
|
+
this.oldCache.clear();
|
|
22130
|
+
this._size = 0;
|
|
22337
22131
|
}
|
|
22338
|
-
|
|
22339
|
-
|
|
22340
|
-
|
|
22341
|
-
|
|
22342
|
-
delete options.target;
|
|
22343
|
-
const mapArray = array => array.map(element => isObjectCustom(element) ? mapObject(element, mapper, options, isSeen) : element);
|
|
22344
|
-
if (Array.isArray(object)) {
|
|
22345
|
-
return mapArray(object);
|
|
22132
|
+
*keys() {
|
|
22133
|
+
for (const [key] of this) {
|
|
22134
|
+
yield key;
|
|
22135
|
+
}
|
|
22346
22136
|
}
|
|
22347
|
-
|
|
22348
|
-
const
|
|
22349
|
-
|
|
22350
|
-
continue;
|
|
22137
|
+
*values() {
|
|
22138
|
+
for (const [, value] of this) {
|
|
22139
|
+
yield value;
|
|
22351
22140
|
}
|
|
22352
|
-
|
|
22353
|
-
|
|
22354
|
-
|
|
22355
|
-
|
|
22356
|
-
// Drop `__proto__` keys.
|
|
22357
|
-
if (newKey === '__proto__') {
|
|
22358
|
-
continue;
|
|
22141
|
+
}
|
|
22142
|
+
*[Symbol.iterator]() {
|
|
22143
|
+
for (const item of this.cache) {
|
|
22144
|
+
yield item;
|
|
22359
22145
|
}
|
|
22360
|
-
|
|
22361
|
-
|
|
22146
|
+
for (const item of this.oldCache) {
|
|
22147
|
+
const [key] = item;
|
|
22148
|
+
if (!this.cache.has(key)) {
|
|
22149
|
+
yield item;
|
|
22150
|
+
}
|
|
22362
22151
|
}
|
|
22363
|
-
target[newKey] = newValue;
|
|
22364
|
-
}
|
|
22365
|
-
return target;
|
|
22366
|
-
};
|
|
22367
|
-
mapObj$1.exports = (object, mapper, options) => {
|
|
22368
|
-
if (!isObject$1(object)) {
|
|
22369
|
-
throw new TypeError(`Expected an object, got \`${object}\` (${typeof object})`);
|
|
22370
22152
|
}
|
|
22371
|
-
|
|
22372
|
-
|
|
22373
|
-
|
|
22374
|
-
|
|
22375
|
-
|
|
22376
|
-
|
|
22377
|
-
};
|
|
22378
|
-
const UPPERCASE = /[\p{Lu}]/u;
|
|
22379
|
-
const LOWERCASE = /[\p{Ll}]/u;
|
|
22380
|
-
const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
|
|
22381
|
-
const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
|
|
22382
|
-
const SEPARATORS = /[_.\- ]+/;
|
|
22383
|
-
const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
|
|
22384
|
-
const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
|
|
22385
|
-
const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');
|
|
22386
|
-
const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
|
|
22387
|
-
let isLastCharLower = false;
|
|
22388
|
-
let isLastCharUpper = false;
|
|
22389
|
-
let isLastLastCharUpper = false;
|
|
22390
|
-
for (let i = 0; i < string.length; i++) {
|
|
22391
|
-
const character = string[i];
|
|
22392
|
-
if (isLastCharLower && UPPERCASE.test(character)) {
|
|
22393
|
-
string = string.slice(0, i) + '-' + string.slice(i);
|
|
22394
|
-
isLastCharLower = false;
|
|
22395
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
22396
|
-
isLastCharUpper = true;
|
|
22397
|
-
i++;
|
|
22398
|
-
} else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) {
|
|
22399
|
-
string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
|
|
22400
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
22401
|
-
isLastCharUpper = false;
|
|
22402
|
-
isLastCharLower = true;
|
|
22403
|
-
} else {
|
|
22404
|
-
isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
|
|
22405
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
22406
|
-
isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
|
|
22153
|
+
get size() {
|
|
22154
|
+
let oldCacheSize = 0;
|
|
22155
|
+
for (const key of this.oldCache.keys()) {
|
|
22156
|
+
if (!this.cache.has(key)) {
|
|
22157
|
+
oldCacheSize++;
|
|
22158
|
+
}
|
|
22407
22159
|
}
|
|
22160
|
+
return Math.min(this._size + oldCacheSize, this.maxSize);
|
|
22408
22161
|
}
|
|
22409
|
-
|
|
22410
|
-
|
|
22411
|
-
const
|
|
22412
|
-
|
|
22413
|
-
|
|
22414
|
-
|
|
22415
|
-
|
|
22416
|
-
|
|
22417
|
-
|
|
22418
|
-
|
|
22419
|
-
|
|
22420
|
-
|
|
22421
|
-
|
|
22422
|
-
|
|
22162
|
+
}
|
|
22163
|
+
var quickLru = QuickLRU;
|
|
22164
|
+
const mapObj = mapObjExports;
|
|
22165
|
+
const camelCase = camelcaseExports;
|
|
22166
|
+
const QuickLru = quickLru;
|
|
22167
|
+
const has = (array, key) => array.some(x => {
|
|
22168
|
+
if (typeof x === 'string') {
|
|
22169
|
+
return x === key;
|
|
22170
|
+
}
|
|
22171
|
+
x.lastIndex = 0;
|
|
22172
|
+
return x.test(key);
|
|
22173
|
+
});
|
|
22174
|
+
const cache = new QuickLru({
|
|
22175
|
+
maxSize: 100000
|
|
22176
|
+
});
|
|
22177
|
+
|
|
22178
|
+
// Reproduces behavior from `map-obj`
|
|
22179
|
+
const isObject = value => typeof value === 'object' && value !== null && !(value instanceof RegExp) && !(value instanceof Error) && !(value instanceof Date);
|
|
22180
|
+
const camelCaseConvert = (input, options) => {
|
|
22181
|
+
if (!isObject(input)) {
|
|
22182
|
+
return input;
|
|
22423
22183
|
}
|
|
22424
22184
|
options = {
|
|
22185
|
+
deep: false,
|
|
22425
22186
|
pascalCase: false,
|
|
22426
|
-
preserveConsecutiveUppercase: false,
|
|
22427
22187
|
...options
|
|
22428
22188
|
};
|
|
22189
|
+
const {
|
|
22190
|
+
exclude,
|
|
22191
|
+
pascalCase,
|
|
22192
|
+
stopPaths,
|
|
22193
|
+
deep
|
|
22194
|
+
} = options;
|
|
22195
|
+
const stopPathsSet = new Set(stopPaths);
|
|
22196
|
+
const makeMapper = parentPath => (key, value) => {
|
|
22197
|
+
if (deep && isObject(value)) {
|
|
22198
|
+
const path = parentPath === undefined ? key : `${parentPath}.${key}`;
|
|
22199
|
+
if (!stopPathsSet.has(path)) {
|
|
22200
|
+
value = mapObj(value, makeMapper(path));
|
|
22201
|
+
}
|
|
22202
|
+
}
|
|
22203
|
+
if (!(exclude && has(exclude, key))) {
|
|
22204
|
+
const cacheKey = pascalCase ? `${key}_` : key;
|
|
22205
|
+
if (cache.has(cacheKey)) {
|
|
22206
|
+
key = cache.get(cacheKey);
|
|
22207
|
+
} else {
|
|
22208
|
+
const returnValue = camelCase(key, {
|
|
22209
|
+
pascalCase,
|
|
22210
|
+
locale: false
|
|
22211
|
+
});
|
|
22212
|
+
if (key.length < 100) {
|
|
22213
|
+
// Prevent abuse
|
|
22214
|
+
cache.set(cacheKey, returnValue);
|
|
22215
|
+
}
|
|
22216
|
+
key = returnValue;
|
|
22217
|
+
}
|
|
22218
|
+
}
|
|
22219
|
+
return [key, value];
|
|
22220
|
+
};
|
|
22221
|
+
return mapObj(input, makeMapper(undefined));
|
|
22222
|
+
};
|
|
22223
|
+
var camelcaseKeys = (input, options) => {
|
|
22429
22224
|
if (Array.isArray(input)) {
|
|
22430
|
-
|
|
22431
|
-
} else {
|
|
22432
|
-
input = input.trim();
|
|
22433
|
-
}
|
|
22434
|
-
if (input.length === 0) {
|
|
22435
|
-
return '';
|
|
22436
|
-
}
|
|
22437
|
-
const toLowerCase = options.locale === false ? string => string.toLowerCase() : string => string.toLocaleLowerCase(options.locale);
|
|
22438
|
-
const toUpperCase = options.locale === false ? string => string.toUpperCase() : string => string.toLocaleUpperCase(options.locale);
|
|
22439
|
-
if (input.length === 1) {
|
|
22440
|
-
return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
|
|
22225
|
+
return Object.keys(input).map(key => camelCaseConvert(input[key], options));
|
|
22441
22226
|
}
|
|
22442
|
-
|
|
22443
|
-
|
|
22444
|
-
|
|
22227
|
+
return camelCaseConvert(input, options);
|
|
22228
|
+
};
|
|
22229
|
+
var camelCaseKeys = /*@__PURE__*/getDefaultExportFromCjs(camelcaseKeys);
|
|
22230
|
+
|
|
22231
|
+
/**
|
|
22232
|
+
* @file Zod camelCase utility.
|
|
22233
|
+
*/
|
|
22234
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
22235
|
+
/**
|
|
22236
|
+
* Transforms Zod schema to camelCase.
|
|
22237
|
+
*
|
|
22238
|
+
* @param zod Zod schema.
|
|
22239
|
+
*
|
|
22240
|
+
* @returns Zod schema with camelCase properties.
|
|
22241
|
+
*
|
|
22242
|
+
* @see {@link https://github.com/colinhacks/zod/issues/486#issuecomment-1501097361}
|
|
22243
|
+
*/
|
|
22244
|
+
const zodToCamelCase = zod => {
|
|
22245
|
+
return zod.transform(val => camelCaseKeys(val));
|
|
22246
|
+
};
|
|
22247
|
+
|
|
22248
|
+
/**
|
|
22249
|
+
* @file Base compatibility data schema, which is commonly used in compatibility tables.
|
|
22250
|
+
*/
|
|
22251
|
+
/**
|
|
22252
|
+
* Zod schema for boolean values. Accepts both boolean and string values.
|
|
22253
|
+
*/
|
|
22254
|
+
const booleanSchema = zod.union([zod.string().transform(val => val.trim().toLowerCase() === 'true'), zod.boolean()]);
|
|
22255
|
+
/**
|
|
22256
|
+
* Zod schema for non-empty string values.
|
|
22257
|
+
*/
|
|
22258
|
+
const nonEmptyStringSchema = zod.string().transform(val => val.trim()).pipe(zod.string().min(1));
|
|
22259
|
+
/**
|
|
22260
|
+
* Zod schema for base compatibility data.
|
|
22261
|
+
* Here we use snake_case properties because the compatibility data is stored in YAML files.
|
|
22262
|
+
*/
|
|
22263
|
+
const baseCompatibilityDataSchema = zod.object({
|
|
22264
|
+
/**
|
|
22265
|
+
* Name of the actual entity.
|
|
22266
|
+
*/
|
|
22267
|
+
name: nonEmptyStringSchema,
|
|
22268
|
+
/**
|
|
22269
|
+
* List of aliases for the entity (if any).
|
|
22270
|
+
*/
|
|
22271
|
+
aliases: zod.array(nonEmptyStringSchema).nullable().default(null),
|
|
22272
|
+
/**
|
|
22273
|
+
* Short description of the actual entity.
|
|
22274
|
+
* If not specified or it's value is `null`, then the description is not available.
|
|
22275
|
+
*/
|
|
22276
|
+
description: nonEmptyStringSchema.nullable().default(null),
|
|
22277
|
+
/**
|
|
22278
|
+
* Link to the documentation. If not specified or it's value is `null`, then the documentation is not available.
|
|
22279
|
+
*/
|
|
22280
|
+
docs: nonEmptyStringSchema.nullable().default(null),
|
|
22281
|
+
/**
|
|
22282
|
+
* The version of the adblocker in which the entity was added.
|
|
22283
|
+
* For AdGuard resources, the version of the library is specified.
|
|
22284
|
+
*/
|
|
22285
|
+
version_added: nonEmptyStringSchema.nullable().default(null),
|
|
22286
|
+
/**
|
|
22287
|
+
* The version of the adblocker when the entity was removed.
|
|
22288
|
+
*/
|
|
22289
|
+
version_removed: nonEmptyStringSchema.nullable().default(null),
|
|
22290
|
+
/**
|
|
22291
|
+
* Describes whether the entity is deprecated.
|
|
22292
|
+
*/
|
|
22293
|
+
deprecated: booleanSchema.default(false),
|
|
22294
|
+
/**
|
|
22295
|
+
* Message that describes why the entity is deprecated.
|
|
22296
|
+
* If not specified or it's value is `null`, then the message is not available.
|
|
22297
|
+
* It's value is omitted if the entity is not marked as deprecated.
|
|
22298
|
+
*/
|
|
22299
|
+
deprecation_message: nonEmptyStringSchema.nullable().default(null),
|
|
22300
|
+
/**
|
|
22301
|
+
* Describes whether the entity is removed; for *already removed* features.
|
|
22302
|
+
*/
|
|
22303
|
+
removed: booleanSchema.default(false),
|
|
22304
|
+
/**
|
|
22305
|
+
* Message that describes why the entity is removed.
|
|
22306
|
+
* If not specified or it's value is `null`, then the message is not available.
|
|
22307
|
+
* It's value is omitted if the entity is not marked as deprecated.
|
|
22308
|
+
*/
|
|
22309
|
+
removal_message: nonEmptyStringSchema.nullable().default(null)
|
|
22310
|
+
});
|
|
22311
|
+
/**
|
|
22312
|
+
* Zod schema for base compatibility data with camelCase properties.
|
|
22313
|
+
*/
|
|
22314
|
+
zodToCamelCase(baseCompatibilityDataSchema);
|
|
22315
|
+
/**
|
|
22316
|
+
* Refinement logic for base compatibility data.
|
|
22317
|
+
*
|
|
22318
|
+
* @param data Base compatibility data.
|
|
22319
|
+
* @param ctx Refinement context.
|
|
22320
|
+
*/
|
|
22321
|
+
const baseRefineLogic = (data, ctx) => {
|
|
22322
|
+
if (data.deprecated && !data.deprecation_message) {
|
|
22323
|
+
ctx.addIssue({
|
|
22324
|
+
code: zod.ZodIssueCode.custom,
|
|
22325
|
+
message: 'deprecation_message is required for deprecated modifiers'
|
|
22326
|
+
});
|
|
22445
22327
|
}
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
|
|
22449
|
-
|
|
22450
|
-
|
|
22328
|
+
if (!data.deprecated && data.deprecation_message) {
|
|
22329
|
+
ctx.addIssue({
|
|
22330
|
+
code: zod.ZodIssueCode.custom,
|
|
22331
|
+
message: 'deprecation_message is only allowed for deprecated modifiers'
|
|
22332
|
+
});
|
|
22451
22333
|
}
|
|
22452
|
-
if (
|
|
22453
|
-
|
|
22334
|
+
if (data.aliases && data.aliases.length !== new Set(data.aliases).size) {
|
|
22335
|
+
ctx.addIssue({
|
|
22336
|
+
code: zod.ZodIssueCode.custom,
|
|
22337
|
+
message: 'Aliases must be unique'
|
|
22338
|
+
});
|
|
22454
22339
|
}
|
|
22455
|
-
return postProcess(input, toUpperCase);
|
|
22456
22340
|
};
|
|
22457
|
-
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
|
|
22461
|
-
|
|
22462
|
-
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22341
|
+
|
|
22342
|
+
/**
|
|
22343
|
+
* Checks if error has message.
|
|
22344
|
+
*
|
|
22345
|
+
* @param error Error object.
|
|
22346
|
+
* @returns If param is error.
|
|
22347
|
+
*/
|
|
22348
|
+
function isErrorWithMessage(error) {
|
|
22349
|
+
return typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string';
|
|
22350
|
+
}
|
|
22351
|
+
/**
|
|
22352
|
+
* Converts error to the error with message.
|
|
22353
|
+
*
|
|
22354
|
+
* @param maybeError Possible error.
|
|
22355
|
+
* @returns Error with message.
|
|
22356
|
+
*/
|
|
22357
|
+
function toErrorWithMessage(maybeError) {
|
|
22358
|
+
if (isErrorWithMessage(maybeError)) {
|
|
22359
|
+
return maybeError;
|
|
22471
22360
|
}
|
|
22472
|
-
|
|
22473
|
-
|
|
22474
|
-
|
|
22475
|
-
|
|
22476
|
-
|
|
22477
|
-
|
|
22478
|
-
|
|
22479
|
-
|
|
22480
|
-
|
|
22481
|
-
|
|
22482
|
-
|
|
22483
|
-
|
|
22484
|
-
|
|
22361
|
+
try {
|
|
22362
|
+
return new Error(JSON.stringify(maybeError));
|
|
22363
|
+
} catch {
|
|
22364
|
+
// fallback in case there's an error stringifying the maybeError
|
|
22365
|
+
// like with circular references for example.
|
|
22366
|
+
return new Error(String(maybeError));
|
|
22367
|
+
}
|
|
22368
|
+
}
|
|
22369
|
+
/**
|
|
22370
|
+
* Converts error object to error with message. This method might be helpful to handle thrown errors.
|
|
22371
|
+
*
|
|
22372
|
+
* @param error Error object.
|
|
22373
|
+
*
|
|
22374
|
+
* @returns Message of the error.
|
|
22375
|
+
*/
|
|
22376
|
+
function getErrorMessage(error) {
|
|
22377
|
+
return toErrorWithMessage(error).message;
|
|
22378
|
+
}
|
|
22379
|
+
|
|
22380
|
+
/**
|
|
22381
|
+
* @file Schema for modifier data.
|
|
22382
|
+
*/
|
|
22383
|
+
/**
|
|
22384
|
+
* Known validators that don't need to be validated as regex.
|
|
22385
|
+
*/
|
|
22386
|
+
const KNOWN_VALIDATORS = new Set(['domain', 'pipe_separated_domains', 'regexp', 'url']);
|
|
22387
|
+
/**
|
|
22388
|
+
* Zod schema for modifier data.
|
|
22389
|
+
*/
|
|
22390
|
+
zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
22391
|
+
/**
|
|
22392
|
+
* List of modifiers that are incompatible with the actual one.
|
|
22393
|
+
*/
|
|
22394
|
+
conflicts: zod.array(nonEmptyStringSchema).nullable().default(null),
|
|
22395
|
+
/**
|
|
22396
|
+
* The actual modifier is incompatible with all other modifiers, except the ones listed in `conflicts`.
|
|
22397
|
+
*/
|
|
22398
|
+
inverse_conflicts: booleanSchema.default(false),
|
|
22399
|
+
/**
|
|
22400
|
+
* Describes whether the actual modifier supports value assignment. For example, `$domain` is assignable,
|
|
22401
|
+
* so it can be used like this: `$domain=domain.com\|~subdomain.domain.com`, where `=` is the assignment operator
|
|
22402
|
+
* and `domain.com\|~subdomain.domain.com` is the value.
|
|
22403
|
+
*/
|
|
22404
|
+
assignable: booleanSchema.default(false),
|
|
22405
|
+
/**
|
|
22406
|
+
* Describes whether the actual modifier can be negated. For example, `$third-party` is negatable,
|
|
22407
|
+
* so it can be used like this: `$~third-party`.
|
|
22408
|
+
*/
|
|
22409
|
+
negatable: booleanSchema.default(true),
|
|
22410
|
+
/**
|
|
22411
|
+
* The actual modifier can only be used in blocking rules, it cannot be used in exceptions.
|
|
22412
|
+
* If it's value is `true`, then the modifier can be used only in blocking rules.
|
|
22413
|
+
* `exception_only` and `block_only` cannot be used together (they are mutually exclusive).
|
|
22414
|
+
*/
|
|
22415
|
+
block_only: booleanSchema.default(false),
|
|
22416
|
+
/**
|
|
22417
|
+
* The actual modifier can only be used in exceptions, it cannot be used in blocking rules.
|
|
22418
|
+
* If it's value is `true`, then the modifier can be used only in exceptions.
|
|
22419
|
+
* `exception_only` and `block_only` cannot be used together (they are mutually exclusive).
|
|
22420
|
+
*/
|
|
22421
|
+
exception_only: booleanSchema.default(false),
|
|
22422
|
+
/**
|
|
22423
|
+
* Describes whether the *assignable* modifier value is required.
|
|
22424
|
+
* For example, `$cookie` is assignable but it can be used without a value in exception rules:
|
|
22425
|
+
* `@@\|\|example.com^$cookie`.
|
|
22426
|
+
* If `false`, the `value_format` is required, e.g. the value of `$app` should always be specified
|
|
22427
|
+
*/
|
|
22428
|
+
value_optional: booleanSchema.default(false),
|
|
22429
|
+
/**
|
|
22430
|
+
* Describes the format of the value for the *assignable* modifier.
|
|
22431
|
+
* Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
|
|
22432
|
+
*/
|
|
22433
|
+
value_format: nonEmptyStringSchema.nullable().default(null)
|
|
22434
|
+
}).superRefine((data, ctx) => {
|
|
22435
|
+
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
22436
|
+
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
22437
|
+
baseRefineLogic(data, ctx);
|
|
22438
|
+
if (data.block_only && data.exception_only) {
|
|
22439
|
+
ctx.addIssue({
|
|
22440
|
+
code: zod.ZodIssueCode.custom,
|
|
22441
|
+
message: 'block_only and exception_only are mutually exclusive'
|
|
22442
|
+
});
|
|
22485
22443
|
}
|
|
22486
|
-
|
|
22487
|
-
|
|
22488
|
-
|
|
22489
|
-
|
|
22490
|
-
|
|
22491
|
-
const value = this.oldCache.get(key);
|
|
22492
|
-
this.oldCache.delete(key);
|
|
22493
|
-
this._set(key, value);
|
|
22494
|
-
return value;
|
|
22495
|
-
}
|
|
22444
|
+
if (data.assignable && !data.value_format) {
|
|
22445
|
+
ctx.addIssue({
|
|
22446
|
+
code: zod.ZodIssueCode.custom,
|
|
22447
|
+
message: 'value_format is required for assignable modifiers'
|
|
22448
|
+
});
|
|
22496
22449
|
}
|
|
22497
|
-
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
|
|
22450
|
+
if (data.value_format) {
|
|
22451
|
+
const valueFormat = data.value_format.trim();
|
|
22452
|
+
// if it is a known validator, we don't need to validate it further
|
|
22453
|
+
if (KNOWN_VALIDATORS.has(valueFormat)) {
|
|
22454
|
+
return;
|
|
22455
|
+
}
|
|
22456
|
+
// otherwise, we need to validate it as a regex
|
|
22457
|
+
try {
|
|
22458
|
+
XRegExp(valueFormat);
|
|
22459
|
+
} catch (error) {
|
|
22460
|
+
ctx.addIssue({
|
|
22461
|
+
code: zod.ZodIssueCode.custom,
|
|
22462
|
+
message: getErrorMessage(error)
|
|
22463
|
+
});
|
|
22502
22464
|
}
|
|
22503
|
-
return this;
|
|
22504
22465
|
}
|
|
22505
|
-
|
|
22506
|
-
|
|
22466
|
+
}));
|
|
22467
|
+
|
|
22468
|
+
/**
|
|
22469
|
+
* @file Schema for redirect data.
|
|
22470
|
+
*/
|
|
22471
|
+
/**
|
|
22472
|
+
* Zod schema for redirect data.
|
|
22473
|
+
*/
|
|
22474
|
+
zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
22475
|
+
/**
|
|
22476
|
+
* Whether the redirect is blocking.
|
|
22477
|
+
*/
|
|
22478
|
+
is_blocking: booleanSchema.default(false),
|
|
22479
|
+
/**
|
|
22480
|
+
* Resource type(s) belonging to the redirect.
|
|
22481
|
+
*
|
|
22482
|
+
* @see {@link https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#type-ResourceType}
|
|
22483
|
+
*/
|
|
22484
|
+
resource_types: zod.array(resourceTypeSchema).default([])
|
|
22485
|
+
}).superRefine(baseRefineLogic));
|
|
22486
|
+
|
|
22487
|
+
/**
|
|
22488
|
+
* @file Schema for scriptlet data.
|
|
22489
|
+
*/
|
|
22490
|
+
/**
|
|
22491
|
+
* Zod schema for scriptlet parameter data.
|
|
22492
|
+
*/
|
|
22493
|
+
const scriptletParameterSchema = zod.object({
|
|
22494
|
+
/**
|
|
22495
|
+
* Name of the actual parameter.
|
|
22496
|
+
*/
|
|
22497
|
+
name: nonEmptyStringSchema,
|
|
22498
|
+
/**
|
|
22499
|
+
* Describes whether the parameter is required. Empty parameters are not allowed.
|
|
22500
|
+
*/
|
|
22501
|
+
required: booleanSchema,
|
|
22502
|
+
/**
|
|
22503
|
+
* Short description of the parameter.
|
|
22504
|
+
* If not specified or it's value is `null`,then the description is not available.
|
|
22505
|
+
*/
|
|
22506
|
+
description: nonEmptyStringSchema.nullable().default(null),
|
|
22507
|
+
/**
|
|
22508
|
+
* Regular expression that matches the value of the parameter.
|
|
22509
|
+
* If it's value is `null`, then the parameter value is not checked.
|
|
22510
|
+
*/
|
|
22511
|
+
pattern: nonEmptyStringSchema.nullable().default(null),
|
|
22512
|
+
/**
|
|
22513
|
+
* Default value of the parameter (if any).
|
|
22514
|
+
*/
|
|
22515
|
+
default: nonEmptyStringSchema.nullable().default(null),
|
|
22516
|
+
/**
|
|
22517
|
+
* Describes whether the parameter is used only for debugging purposes.
|
|
22518
|
+
*/
|
|
22519
|
+
debug: booleanSchema.default(false)
|
|
22520
|
+
});
|
|
22521
|
+
/**
|
|
22522
|
+
* Zod schema for scriptlet parameters.
|
|
22523
|
+
*/
|
|
22524
|
+
const scriptletParametersSchema = zod.array(scriptletParameterSchema);
|
|
22525
|
+
/**
|
|
22526
|
+
* Zod schema for scriptlet data.
|
|
22527
|
+
*/
|
|
22528
|
+
zodToCamelCase(baseCompatibilityDataSchema.extend({
|
|
22529
|
+
/**
|
|
22530
|
+
* List of parameters that the scriptlet accepts.
|
|
22531
|
+
* **Every** parameter should be listed here, because we check that the scriptlet is used correctly
|
|
22532
|
+
* (e.g. that the number of parameters is correct).
|
|
22533
|
+
*/
|
|
22534
|
+
parameters: scriptletParametersSchema.optional()
|
|
22535
|
+
}).superRefine((data, ctx) => {
|
|
22536
|
+
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
22537
|
+
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
22538
|
+
baseRefineLogic(data, ctx);
|
|
22539
|
+
// we don't allow required parameters after optional ones
|
|
22540
|
+
if (!data.parameters) {
|
|
22541
|
+
return;
|
|
22507
22542
|
}
|
|
22508
|
-
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
|
|
22512
|
-
|
|
22513
|
-
|
|
22543
|
+
let optionalFound = false;
|
|
22544
|
+
for (const parameter of data.parameters) {
|
|
22545
|
+
if (optionalFound && parameter.required) {
|
|
22546
|
+
ctx.addIssue({
|
|
22547
|
+
code: zod.ZodIssueCode.custom,
|
|
22548
|
+
message: 'Required parameters must be before optional ones'
|
|
22549
|
+
});
|
|
22514
22550
|
}
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
const deleted = this.cache.delete(key);
|
|
22518
|
-
if (deleted) {
|
|
22519
|
-
this._size--;
|
|
22551
|
+
if (!parameter.required) {
|
|
22552
|
+
optionalFound = true;
|
|
22520
22553
|
}
|
|
22521
|
-
return this.oldCache.delete(key) || deleted;
|
|
22522
|
-
}
|
|
22523
|
-
clear() {
|
|
22524
|
-
this.cache.clear();
|
|
22525
|
-
this.oldCache.clear();
|
|
22526
|
-
this._size = 0;
|
|
22527
22554
|
}
|
|
22528
|
-
|
|
22529
|
-
|
|
22530
|
-
|
|
22555
|
+
}));
|
|
22556
|
+
|
|
22557
|
+
/**
|
|
22558
|
+
* @file Scriptlet injection rule converter
|
|
22559
|
+
*/
|
|
22560
|
+
const ABP_SCRIPTLET_PREFIX = 'abp-';
|
|
22561
|
+
const UBO_SCRIPTLET_PREFIX = 'ubo-';
|
|
22562
|
+
const UBO_SCRIPTLET_PREFIX_LENGTH = UBO_SCRIPTLET_PREFIX.length;
|
|
22563
|
+
const UBO_SCRIPTLET_JS_SUFFIX = '.js';
|
|
22564
|
+
const UBO_SCRIPTLET_JS_SUFFIX_LENGTH = UBO_SCRIPTLET_JS_SUFFIX.length;
|
|
22565
|
+
const COMMA_SEPARATOR = ',';
|
|
22566
|
+
const ADG_SET_CONSTANT_NAME = 'set-constant';
|
|
22567
|
+
const ADG_SET_CONSTANT_EMPTY_STRING = '';
|
|
22568
|
+
const ADG_SET_CONSTANT_EMPTY_ARRAY = 'emptyArr';
|
|
22569
|
+
const ADG_SET_CONSTANT_EMPTY_OBJECT = 'emptyObj';
|
|
22570
|
+
const UBO_SET_CONSTANT_EMPTY_STRING = '\'\'';
|
|
22571
|
+
const UBO_SET_CONSTANT_EMPTY_ARRAY = '[]';
|
|
22572
|
+
const UBO_SET_CONSTANT_EMPTY_OBJECT = '{}';
|
|
22573
|
+
const ADG_PREVENT_FETCH_NAME = 'prevent-fetch';
|
|
22574
|
+
const ADG_PREVENT_FETCH_EMPTY_STRING = '';
|
|
22575
|
+
const ADG_PREVENT_FETCH_WILDCARD = '*';
|
|
22576
|
+
const UBO_NO_FETCH_IF_WILDCARD = '/^/';
|
|
22577
|
+
const UBO_REMOVE_CLASS_NAME = 'remove-class.js';
|
|
22578
|
+
const UBO_REMOVE_ATTR_NAME = 'remove-attr.js';
|
|
22579
|
+
const setConstantAdgToUboMap = {
|
|
22580
|
+
[ADG_SET_CONSTANT_EMPTY_STRING]: UBO_SET_CONSTANT_EMPTY_STRING,
|
|
22581
|
+
[ADG_SET_CONSTANT_EMPTY_ARRAY]: UBO_SET_CONSTANT_EMPTY_ARRAY,
|
|
22582
|
+
[ADG_SET_CONSTANT_EMPTY_OBJECT]: UBO_SET_CONSTANT_EMPTY_OBJECT
|
|
22583
|
+
};
|
|
22584
|
+
const REMOVE_ATTR_CLASS_APPLYING = new Set(['asap', 'stay', 'complete']);
|
|
22585
|
+
/**
|
|
22586
|
+
* Scriptlet injection rule converter class
|
|
22587
|
+
*
|
|
22588
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22589
|
+
*/
|
|
22590
|
+
class ScriptletRuleConverter extends RuleConverterBase {
|
|
22591
|
+
/**
|
|
22592
|
+
* Converts a scriptlet injection rule to AdGuard format, if possible.
|
|
22593
|
+
*
|
|
22594
|
+
* @param rule Rule node to convert
|
|
22595
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
22596
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
22597
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
22598
|
+
* @throws If the rule is invalid or cannot be converted
|
|
22599
|
+
*/
|
|
22600
|
+
static convertToAdg(rule) {
|
|
22601
|
+
// Ignore AdGuard rules
|
|
22602
|
+
if (rule.syntax === AdblockSyntax.Adg) {
|
|
22603
|
+
return createNodeConversionResult([rule], false);
|
|
22531
22604
|
}
|
|
22532
|
-
|
|
22533
|
-
|
|
22534
|
-
|
|
22535
|
-
|
|
22605
|
+
const separator = rule.separator.value;
|
|
22606
|
+
let convertedSeparator = separator;
|
|
22607
|
+
convertedSeparator = rule.exception ? CosmeticRuleSeparator.AdgJsInjectionException : CosmeticRuleSeparator.AdgJsInjection;
|
|
22608
|
+
const convertedScriptlets = [];
|
|
22609
|
+
// Special case: empty uBO exception scriptlet, e.g. `example.com#@#+js()`
|
|
22610
|
+
if (rule.syntax === AdblockSyntax.Ubo && rule.body.children.length === 1 && rule.body.children[0].children.length === 0) {
|
|
22611
|
+
convertedScriptlets.push(rule.body.children[0]);
|
|
22612
|
+
} else {
|
|
22613
|
+
for (const scriptlet of rule.body.children) {
|
|
22614
|
+
// Clone the node to avoid any side effects
|
|
22615
|
+
const scriptletClone = cloneScriptletRuleNode(scriptlet);
|
|
22616
|
+
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
22617
|
+
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletClone), QuoteType.None);
|
|
22618
|
+
// Add prefix if it's not already there
|
|
22619
|
+
let prefix;
|
|
22620
|
+
// In uBO / ABP syntax, if a parameter contains the separator character, it should be escaped,
|
|
22621
|
+
// but during the conversion, we need to unescape them, because AdGuard syntax uses quotes to
|
|
22622
|
+
// distinguish between parameters.
|
|
22623
|
+
let charToUnescape;
|
|
22624
|
+
switch (rule.syntax) {
|
|
22625
|
+
case AdblockSyntax.Abp:
|
|
22626
|
+
prefix = ABP_SCRIPTLET_PREFIX;
|
|
22627
|
+
charToUnescape = SPACE;
|
|
22628
|
+
break;
|
|
22629
|
+
case AdblockSyntax.Ubo:
|
|
22630
|
+
prefix = UBO_SCRIPTLET_PREFIX;
|
|
22631
|
+
charToUnescape = COMMA_SEPARATOR;
|
|
22632
|
+
break;
|
|
22633
|
+
default:
|
|
22634
|
+
prefix = EMPTY;
|
|
22635
|
+
}
|
|
22636
|
+
if (!scriptletName.startsWith(prefix)) {
|
|
22637
|
+
setScriptletName(scriptletClone, `${prefix}${scriptletName}`);
|
|
22638
|
+
}
|
|
22639
|
+
if (!isUndefined(charToUnescape)) {
|
|
22640
|
+
transformAllScriptletArguments(scriptletClone, value => {
|
|
22641
|
+
if (!isNull(value)) {
|
|
22642
|
+
return QuoteUtils.unescapeSingleEscapedOccurrences(value, charToUnescape);
|
|
22643
|
+
}
|
|
22644
|
+
return value;
|
|
22645
|
+
});
|
|
22646
|
+
}
|
|
22647
|
+
if (rule.syntax === AdblockSyntax.Ubo) {
|
|
22648
|
+
const scriptletData = scriptletsCompatibilityTable.getFirst(scriptletName, GenericPlatform.UboAny);
|
|
22649
|
+
// Some scriptlets have special values that need to be converted
|
|
22650
|
+
if (scriptletData && (scriptletData.name === UBO_REMOVE_CLASS_NAME || scriptletData.name === UBO_REMOVE_ATTR_NAME) && scriptletClone.children.length > 2) {
|
|
22651
|
+
const selectors = [];
|
|
22652
|
+
let applying = null;
|
|
22653
|
+
let lastArg = scriptletClone.children.pop();
|
|
22654
|
+
// The very last argument might be the 'applying' parameter
|
|
22655
|
+
if (lastArg) {
|
|
22656
|
+
if (REMOVE_ATTR_CLASS_APPLYING.has(lastArg.value)) {
|
|
22657
|
+
applying = lastArg.value;
|
|
22658
|
+
} else {
|
|
22659
|
+
selectors.push(lastArg.value);
|
|
22660
|
+
}
|
|
22661
|
+
}
|
|
22662
|
+
while (scriptletClone.children.length > 2) {
|
|
22663
|
+
lastArg = scriptletClone.children.pop();
|
|
22664
|
+
if (lastArg) {
|
|
22665
|
+
selectors.push(lastArg.value.trim());
|
|
22666
|
+
}
|
|
22667
|
+
}
|
|
22668
|
+
// Set last arg to be the combined selectors (in reverse order, because we popped them)
|
|
22669
|
+
if (selectors.length > 0) {
|
|
22670
|
+
scriptletClone.children.push({
|
|
22671
|
+
type: 'Value',
|
|
22672
|
+
value: selectors.reverse().join(', ')
|
|
22673
|
+
});
|
|
22674
|
+
}
|
|
22675
|
+
// Push back the 'applying' parameter if it was found previously
|
|
22676
|
+
if (!isNull(applying)) {
|
|
22677
|
+
// If we don't have any selectors,
|
|
22678
|
+
// we need to add an empty parameter before the 'applying' one
|
|
22679
|
+
if (selectors.length === 0) {
|
|
22680
|
+
scriptletClone.children.push({
|
|
22681
|
+
type: 'Value',
|
|
22682
|
+
value: EMPTY
|
|
22683
|
+
});
|
|
22684
|
+
}
|
|
22685
|
+
scriptletClone.children.push({
|
|
22686
|
+
type: 'Value',
|
|
22687
|
+
value: applying
|
|
22688
|
+
});
|
|
22689
|
+
}
|
|
22690
|
+
}
|
|
22691
|
+
}
|
|
22692
|
+
// ADG scriptlet parameters should be quoted, and single quoted are preferred
|
|
22693
|
+
setScriptletQuoteType(scriptletClone, QuoteType.Single);
|
|
22694
|
+
convertedScriptlets.push(scriptletClone);
|
|
22695
|
+
}
|
|
22536
22696
|
}
|
|
22697
|
+
return createNodeConversionResult(convertedScriptlets.map(scriptlet => {
|
|
22698
|
+
const res = {
|
|
22699
|
+
category: rule.category,
|
|
22700
|
+
type: rule.type,
|
|
22701
|
+
syntax: AdblockSyntax.Adg,
|
|
22702
|
+
exception: rule.exception,
|
|
22703
|
+
domains: cloneDomainListNode(rule.domains),
|
|
22704
|
+
separator: {
|
|
22705
|
+
type: 'Value',
|
|
22706
|
+
value: convertedSeparator
|
|
22707
|
+
},
|
|
22708
|
+
body: {
|
|
22709
|
+
type: rule.body.type,
|
|
22710
|
+
children: [scriptlet]
|
|
22711
|
+
}
|
|
22712
|
+
};
|
|
22713
|
+
if (rule.modifiers) {
|
|
22714
|
+
res.modifiers = cloneModifierListNode(rule.modifiers);
|
|
22715
|
+
}
|
|
22716
|
+
return res;
|
|
22717
|
+
}), true);
|
|
22537
22718
|
}
|
|
22538
|
-
|
|
22539
|
-
|
|
22540
|
-
|
|
22719
|
+
/**
|
|
22720
|
+
* Converts a scriptlet injection rule to uBlock format, if possible.
|
|
22721
|
+
*
|
|
22722
|
+
* @param rule Rule node to convert
|
|
22723
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
22724
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
22725
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
22726
|
+
* @throws If the rule is invalid or cannot be converted
|
|
22727
|
+
*/
|
|
22728
|
+
static convertToUbo(rule) {
|
|
22729
|
+
// Ignore uBlock rules
|
|
22730
|
+
if (rule.syntax === AdblockSyntax.Ubo) {
|
|
22731
|
+
return createNodeConversionResult([rule], false);
|
|
22541
22732
|
}
|
|
22542
|
-
|
|
22543
|
-
|
|
22544
|
-
|
|
22545
|
-
|
|
22733
|
+
const separator = rule.separator.value;
|
|
22734
|
+
let convertedSeparator = separator;
|
|
22735
|
+
convertedSeparator = rule.exception ? CosmeticRuleSeparator.ElementHidingException : CosmeticRuleSeparator.ElementHiding;
|
|
22736
|
+
const convertedScriptlets = [];
|
|
22737
|
+
// Special case: empty AdGuard exception scriptlet, e.g. `example.com#%#//scriptlet()`
|
|
22738
|
+
if (rule.syntax === AdblockSyntax.Adg && rule.body.children.length === 1 && rule.body.children[0].children.length === 0) {
|
|
22739
|
+
convertedScriptlets.push(rule.body.children[0]);
|
|
22740
|
+
} else {
|
|
22741
|
+
for (const scriptlet of rule.body.children) {
|
|
22742
|
+
// Clone the node to avoid any side effects
|
|
22743
|
+
const scriptletClone = cloneScriptletRuleNode(scriptlet);
|
|
22744
|
+
// Remove possible quotes just to make it easier to work with the scriptlet name
|
|
22745
|
+
const scriptletName = QuoteUtils.setStringQuoteType(getScriptletName(scriptletClone), QuoteType.None);
|
|
22746
|
+
let uboScriptletName;
|
|
22747
|
+
if (rule.syntax === AdblockSyntax.Adg && scriptletName.startsWith(UBO_SCRIPTLET_PREFIX)) {
|
|
22748
|
+
// Special case: AdGuard syntax 'preserves' the original scriptlet name,
|
|
22749
|
+
// so we need to convert it back by removing the uBO prefix
|
|
22750
|
+
uboScriptletName = scriptletName.slice(UBO_SCRIPTLET_PREFIX_LENGTH);
|
|
22751
|
+
} else {
|
|
22752
|
+
// Otherwise, try to find the corresponding uBO scriptlet name, or use the original one if not found
|
|
22753
|
+
const uboScriptlet = scriptletsCompatibilityTable.getFirst(scriptletName, GenericPlatform.UboAny);
|
|
22754
|
+
uboScriptletName = uboScriptlet?.name ?? scriptletName;
|
|
22755
|
+
}
|
|
22756
|
+
// Remove the '.js' suffix if it's there - its presence is not mandatory
|
|
22757
|
+
if (uboScriptletName.endsWith(UBO_SCRIPTLET_JS_SUFFIX)) {
|
|
22758
|
+
uboScriptletName = uboScriptletName.slice(0, -UBO_SCRIPTLET_JS_SUFFIX_LENGTH);
|
|
22759
|
+
}
|
|
22760
|
+
setScriptletName(scriptletClone, uboScriptletName);
|
|
22761
|
+
setScriptletQuoteType(scriptletClone, QuoteType.None);
|
|
22762
|
+
// Escape unescaped commas in parameters, because uBlock Origin uses them as separators.
|
|
22763
|
+
// For example, the following AdGuard rule:
|
|
22764
|
+
//
|
|
22765
|
+
// example.com#%#//scriptlet('spoof-css', '.adsbygoogle, #ads', 'visibility', 'visible')
|
|
22766
|
+
//
|
|
22767
|
+
// ↓↓ should be converted to ↓↓
|
|
22768
|
+
//
|
|
22769
|
+
// example.com##+js(spoof-css.js, .adsbygoogle\, #ads, visibility, visible)
|
|
22770
|
+
// ------------ ------------------- ---------- -------
|
|
22771
|
+
// arg 0 arg 1 arg 2 arg 3
|
|
22772
|
+
//
|
|
22773
|
+
// and we need to escape the comma in the second argument to prevent it from being treated
|
|
22774
|
+
// as two separate arguments.
|
|
22775
|
+
transformAllScriptletArguments(scriptletClone, value => {
|
|
22776
|
+
if (!isNull(value)) {
|
|
22777
|
+
return QuoteUtils.escapeUnescapedOccurrences(value, COMMA_SEPARATOR);
|
|
22778
|
+
}
|
|
22779
|
+
return value;
|
|
22780
|
+
});
|
|
22781
|
+
// Unescape spaces in parameters, because uBlock Origin doesn't treat them as separators.
|
|
22782
|
+
if (rule.syntax === AdblockSyntax.Abp) {
|
|
22783
|
+
transformAllScriptletArguments(scriptletClone, value => {
|
|
22784
|
+
if (!isNull(value)) {
|
|
22785
|
+
return QuoteUtils.unescapeSingleEscapedOccurrences(value, SPACE);
|
|
22786
|
+
}
|
|
22787
|
+
return value;
|
|
22788
|
+
});
|
|
22789
|
+
}
|
|
22790
|
+
// Some scriptlets have special values that need to be converted
|
|
22791
|
+
switch (scriptletName) {
|
|
22792
|
+
case ADG_SET_CONSTANT_NAME:
|
|
22793
|
+
transformNthScriptletArgument(scriptletClone, 2, value => {
|
|
22794
|
+
if (!isNull(value)) {
|
|
22795
|
+
return setConstantAdgToUboMap[value] ?? value;
|
|
22796
|
+
}
|
|
22797
|
+
return value;
|
|
22798
|
+
});
|
|
22799
|
+
break;
|
|
22800
|
+
case ADG_PREVENT_FETCH_NAME:
|
|
22801
|
+
transformNthScriptletArgument(scriptletClone, 1, value => {
|
|
22802
|
+
if (value === ADG_PREVENT_FETCH_EMPTY_STRING || value === ADG_PREVENT_FETCH_WILDCARD) {
|
|
22803
|
+
return UBO_NO_FETCH_IF_WILDCARD;
|
|
22804
|
+
}
|
|
22805
|
+
return value;
|
|
22806
|
+
});
|
|
22807
|
+
break;
|
|
22808
|
+
}
|
|
22809
|
+
convertedScriptlets.push(scriptletClone);
|
|
22546
22810
|
}
|
|
22547
22811
|
}
|
|
22548
|
-
|
|
22549
|
-
|
|
22550
|
-
|
|
22551
|
-
|
|
22552
|
-
|
|
22553
|
-
|
|
22812
|
+
return createNodeConversionResult(convertedScriptlets.map(scriptlet => {
|
|
22813
|
+
const res = {
|
|
22814
|
+
category: rule.category,
|
|
22815
|
+
type: rule.type,
|
|
22816
|
+
syntax: AdblockSyntax.Ubo,
|
|
22817
|
+
exception: rule.exception,
|
|
22818
|
+
domains: cloneDomainListNode(rule.domains),
|
|
22819
|
+
separator: {
|
|
22820
|
+
type: 'Value',
|
|
22821
|
+
value: convertedSeparator
|
|
22822
|
+
},
|
|
22823
|
+
body: {
|
|
22824
|
+
type: rule.body.type,
|
|
22825
|
+
children: [scriptlet]
|
|
22826
|
+
}
|
|
22827
|
+
};
|
|
22828
|
+
if (rule.modifiers) {
|
|
22829
|
+
res.modifiers = cloneModifierListNode(rule.modifiers);
|
|
22554
22830
|
}
|
|
22555
|
-
|
|
22556
|
-
|
|
22831
|
+
return res;
|
|
22832
|
+
}), true);
|
|
22557
22833
|
}
|
|
22558
22834
|
}
|
|
22559
|
-
var quickLru = QuickLRU;
|
|
22560
|
-
const mapObj = mapObjExports;
|
|
22561
|
-
const camelCase = camelcaseExports;
|
|
22562
|
-
const QuickLru = quickLru;
|
|
22563
|
-
const has = (array, key) => array.some(x => {
|
|
22564
|
-
if (typeof x === 'string') {
|
|
22565
|
-
return x === key;
|
|
22566
|
-
}
|
|
22567
|
-
x.lastIndex = 0;
|
|
22568
|
-
return x.test(key);
|
|
22569
|
-
});
|
|
22570
|
-
const cache = new QuickLru({
|
|
22571
|
-
maxSize: 100000
|
|
22572
|
-
});
|
|
22573
22835
|
|
|
22574
|
-
|
|
22575
|
-
|
|
22576
|
-
|
|
22577
|
-
|
|
22578
|
-
|
|
22836
|
+
/**
|
|
22837
|
+
* @file Utility functions for working with modifier nodes
|
|
22838
|
+
*/
|
|
22839
|
+
/**
|
|
22840
|
+
* Creates a modifier node
|
|
22841
|
+
*
|
|
22842
|
+
* @param name Name of the modifier
|
|
22843
|
+
* @param value Value of the modifier
|
|
22844
|
+
* @param exception Whether the modifier is an exception
|
|
22845
|
+
* @returns Modifier node
|
|
22846
|
+
*/
|
|
22847
|
+
function createModifierNode(name, value = undefined, exception = false) {
|
|
22848
|
+
const result = {
|
|
22849
|
+
type: 'Modifier',
|
|
22850
|
+
exception,
|
|
22851
|
+
name: {
|
|
22852
|
+
type: 'Value',
|
|
22853
|
+
value: name
|
|
22854
|
+
}
|
|
22855
|
+
};
|
|
22856
|
+
if (!isUndefined(value)) {
|
|
22857
|
+
result.value = {
|
|
22858
|
+
type: 'Value',
|
|
22859
|
+
value
|
|
22860
|
+
};
|
|
22579
22861
|
}
|
|
22580
|
-
|
|
22581
|
-
|
|
22582
|
-
|
|
22583
|
-
|
|
22862
|
+
return result;
|
|
22863
|
+
}
|
|
22864
|
+
/**
|
|
22865
|
+
* Creates a modifier list node
|
|
22866
|
+
*
|
|
22867
|
+
* @param modifiers Modifiers to put in the list (optional, defaults to an empty list)
|
|
22868
|
+
* @returns Modifier list node
|
|
22869
|
+
*/
|
|
22870
|
+
function createModifierListNode(modifiers = []) {
|
|
22871
|
+
const result = {
|
|
22872
|
+
type: 'ModifierList',
|
|
22873
|
+
// We need to clone the modifiers to avoid side effects
|
|
22874
|
+
children: modifiers.length ? clone(modifiers) : []
|
|
22584
22875
|
};
|
|
22585
|
-
|
|
22586
|
-
|
|
22587
|
-
|
|
22588
|
-
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22876
|
+
return result;
|
|
22877
|
+
}
|
|
22878
|
+
|
|
22879
|
+
/**
|
|
22880
|
+
* A very simple map extension that allows to store multiple values for the same key
|
|
22881
|
+
* by storing them in an array.
|
|
22882
|
+
*
|
|
22883
|
+
* @todo Add more methods if needed
|
|
22884
|
+
*/
|
|
22885
|
+
class MultiValueMap extends Map {
|
|
22886
|
+
/**
|
|
22887
|
+
* Adds a value to the map. If the key already exists, the value will be appended to the existing array,
|
|
22888
|
+
* otherwise a new array will be created for the key.
|
|
22889
|
+
*
|
|
22890
|
+
* @param key Key to add
|
|
22891
|
+
* @param values Value(s) to add
|
|
22892
|
+
*/
|
|
22893
|
+
add(key, ...values) {
|
|
22894
|
+
let currentValues = super.get(key);
|
|
22895
|
+
if (isUndefined(currentValues)) {
|
|
22896
|
+
currentValues = [];
|
|
22897
|
+
super.set(key, values);
|
|
22898
|
+
}
|
|
22899
|
+
currentValues.push(...values);
|
|
22900
|
+
}
|
|
22901
|
+
}
|
|
22902
|
+
|
|
22903
|
+
/**
|
|
22904
|
+
* @file Cosmetic rule modifier converter from uBO to ADG
|
|
22905
|
+
*/
|
|
22906
|
+
const UBO_MATCHES_PATH_OPERATOR = 'matches-path';
|
|
22907
|
+
const ADG_PATH_MODIFIER = 'path';
|
|
22908
|
+
/**
|
|
22909
|
+
* Special characters in modifier regexps that should be escaped
|
|
22910
|
+
*/
|
|
22911
|
+
const SPECIAL_MODIFIER_REGEX_CHARS = new Set([OPEN_SQUARE_BRACKET, CLOSE_SQUARE_BRACKET, COMMA, ESCAPE_CHARACTER]);
|
|
22912
|
+
/**
|
|
22913
|
+
* Helper class for converting cosmetic rule modifiers from uBO to ADG
|
|
22914
|
+
*/
|
|
22915
|
+
class AdgCosmeticRuleModifierConverter {
|
|
22916
|
+
/**
|
|
22917
|
+
* Converts a uBO cosmetic rule modifier list to ADG, if possible.
|
|
22918
|
+
*
|
|
22919
|
+
* @param modifierList Cosmetic rule modifier list node to convert
|
|
22920
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
22921
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
22922
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
22923
|
+
* @throws If the modifier list cannot be converted
|
|
22924
|
+
* @see {@link https://github.com/gorhill/uBlock/wiki/Procedural-cosmetic-filters#cosmetic-filter-operators}
|
|
22925
|
+
*/
|
|
22926
|
+
static convertFromUbo(modifierList) {
|
|
22927
|
+
const conversionMap = new MultiValueMap();
|
|
22928
|
+
modifierList.children.forEach((modifier, index) => {
|
|
22929
|
+
// :matches-path
|
|
22930
|
+
if (modifier.name.value === UBO_MATCHES_PATH_OPERATOR) {
|
|
22931
|
+
if (!modifier.value) {
|
|
22932
|
+
throw new RuleConversionError(`'${UBO_MATCHES_PATH_OPERATOR}' operator requires a value`);
|
|
22933
|
+
}
|
|
22934
|
+
const value = RegExpUtils.isRegexPattern(modifier.value.value) ? StringUtils.escapeCharacters(modifier.value.value, SPECIAL_MODIFIER_REGEX_CHARS) : modifier.value.value;
|
|
22935
|
+
// Convert uBO's `:matches-path(...)` operator to ADG's `$path=...` modifier
|
|
22936
|
+
conversionMap.add(index, createModifierNode(ADG_PATH_MODIFIER,
|
|
22937
|
+
// We should negate the regexp if the modifier is an exception
|
|
22938
|
+
modifier.exception
|
|
22939
|
+
// eslint-disable-next-line max-len
|
|
22940
|
+
? `${REGEX_MARKER}${RegExpUtils.negateRegexPattern(RegExpUtils.patternToRegexp(value))}${REGEX_MARKER}` : value));
|
|
22597
22941
|
}
|
|
22942
|
+
});
|
|
22943
|
+
// Check if we have any converted modifiers
|
|
22944
|
+
if (conversionMap.size) {
|
|
22945
|
+
const modifierListClone = clone(modifierList);
|
|
22946
|
+
// Replace the original modifiers with the converted ones
|
|
22947
|
+
modifierListClone.children = modifierListClone.children.map((modifier, index) => {
|
|
22948
|
+
const convertedModifier = conversionMap.get(index);
|
|
22949
|
+
return convertedModifier ?? modifier;
|
|
22950
|
+
}).flat();
|
|
22951
|
+
return createConversionResult(modifierListClone, true);
|
|
22598
22952
|
}
|
|
22599
|
-
|
|
22600
|
-
|
|
22601
|
-
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22606
|
-
|
|
22953
|
+
// Otherwise, just return the original modifier list
|
|
22954
|
+
return createConversionResult(modifierList, false);
|
|
22955
|
+
}
|
|
22956
|
+
}
|
|
22957
|
+
const ERROR_MESSAGES$1 = {
|
|
22958
|
+
// eslint-disable-next-line max-len
|
|
22959
|
+
INVALID_ATTRIBUTE_VALUE: `Expected '${getFormattedTokenName(TokenType$1.Ident)}' or '${getFormattedTokenName(TokenType$1.String)}' as attribute value, but got '%s' with value '%s`
|
|
22960
|
+
};
|
|
22961
|
+
var PseudoClasses;
|
|
22962
|
+
(function (PseudoClasses) {
|
|
22963
|
+
PseudoClasses["AbpContains"] = "-abp-contains";
|
|
22964
|
+
PseudoClasses["AbpHas"] = "-abp-has";
|
|
22965
|
+
PseudoClasses["Contains"] = "contains";
|
|
22966
|
+
PseudoClasses["Has"] = "has";
|
|
22967
|
+
PseudoClasses["HasText"] = "has-text";
|
|
22968
|
+
PseudoClasses["MatchesCss"] = "matches-css";
|
|
22969
|
+
PseudoClasses["MatchesCssAfter"] = "matches-css-after";
|
|
22970
|
+
PseudoClasses["MatchesCssBefore"] = "matches-css-before";
|
|
22971
|
+
PseudoClasses["Not"] = "not";
|
|
22972
|
+
})(PseudoClasses || (PseudoClasses = {}));
|
|
22973
|
+
var PseudoElements;
|
|
22974
|
+
(function (PseudoElements) {
|
|
22975
|
+
PseudoElements["After"] = "after";
|
|
22976
|
+
PseudoElements["Before"] = "before";
|
|
22977
|
+
})(PseudoElements || (PseudoElements = {}));
|
|
22978
|
+
const PSEUDO_ELEMENT_NAMES = new Set([PseudoElements.After, PseudoElements.Before]);
|
|
22979
|
+
/**
|
|
22980
|
+
* CSS selector converter
|
|
22981
|
+
*
|
|
22982
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22983
|
+
*/
|
|
22984
|
+
class CssSelectorConverter extends ConverterBase {
|
|
22985
|
+
/**
|
|
22986
|
+
* Converts Extended CSS elements to AdGuard-compatible ones
|
|
22987
|
+
*
|
|
22988
|
+
* @param selectorList Selector list to convert
|
|
22989
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
22990
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
22991
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
22992
|
+
* @throws If the rule is invalid or incompatible
|
|
22993
|
+
*/
|
|
22994
|
+
static convertToAdg(selectorList) {
|
|
22995
|
+
const stream = selectorList instanceof CssTokenStream ? selectorList : new CssTokenStream(selectorList);
|
|
22996
|
+
const converted = [];
|
|
22997
|
+
const convertAndPushPseudo = pseudo => {
|
|
22998
|
+
switch (pseudo) {
|
|
22999
|
+
case PseudoClasses.AbpContains:
|
|
23000
|
+
case PseudoClasses.HasText:
|
|
23001
|
+
converted.push(PseudoClasses.Contains);
|
|
23002
|
+
converted.push(OPEN_PARENTHESIS);
|
|
23003
|
+
break;
|
|
23004
|
+
case PseudoClasses.AbpHas:
|
|
23005
|
+
converted.push(PseudoClasses.Has);
|
|
23006
|
+
converted.push(OPEN_PARENTHESIS);
|
|
23007
|
+
break;
|
|
23008
|
+
// a bit special case:
|
|
23009
|
+
// - `:matches-css-before(...)` → `:matches-css(before, ...)`
|
|
23010
|
+
// - `:matches-css-after(...)` → `:matches-css(after, ...)`
|
|
23011
|
+
case PseudoClasses.MatchesCssBefore:
|
|
23012
|
+
case PseudoClasses.MatchesCssAfter:
|
|
23013
|
+
converted.push(PseudoClasses.MatchesCss);
|
|
23014
|
+
converted.push(OPEN_PARENTHESIS);
|
|
23015
|
+
converted.push(pseudo.substring(PseudoClasses.MatchesCss.length + 1));
|
|
23016
|
+
converted.push(COMMA);
|
|
23017
|
+
break;
|
|
23018
|
+
default:
|
|
23019
|
+
converted.push(pseudo);
|
|
23020
|
+
converted.push(OPEN_PARENTHESIS);
|
|
23021
|
+
break;
|
|
23022
|
+
}
|
|
23023
|
+
};
|
|
23024
|
+
while (!stream.isEof()) {
|
|
23025
|
+
const token = stream.getOrFail();
|
|
23026
|
+
if (token.type === TokenType$1.Colon) {
|
|
23027
|
+
// Advance colon
|
|
23028
|
+
stream.advance();
|
|
23029
|
+
converted.push(COLON);
|
|
23030
|
+
const tempToken = stream.getOrFail();
|
|
23031
|
+
// Double colon is a pseudo-element
|
|
23032
|
+
if (tempToken.type === TokenType$1.Colon) {
|
|
23033
|
+
stream.advance();
|
|
23034
|
+
converted.push(COLON);
|
|
23035
|
+
continue;
|
|
23036
|
+
}
|
|
23037
|
+
if (tempToken.type === TokenType$1.Ident) {
|
|
23038
|
+
const name = stream.source.slice(tempToken.start, tempToken.end);
|
|
23039
|
+
if (PSEUDO_ELEMENT_NAMES.has(name)) {
|
|
23040
|
+
// Add an extra colon to the name
|
|
23041
|
+
converted.push(COLON);
|
|
23042
|
+
converted.push(name);
|
|
23043
|
+
} else {
|
|
23044
|
+
// Add the name as is
|
|
23045
|
+
converted.push(name);
|
|
23046
|
+
}
|
|
23047
|
+
// Advance the names
|
|
23048
|
+
stream.advance();
|
|
23049
|
+
} else if (tempToken.type === TokenType$1.Function) {
|
|
23050
|
+
const name = stream.source.slice(tempToken.start, tempToken.end - 1); // omit the last parenthesis
|
|
23051
|
+
// :-abp-contains(...) → :contains(...)
|
|
23052
|
+
// :has-text(...) → :contains(...)
|
|
23053
|
+
// :-abp-has(...) → :has(...)
|
|
23054
|
+
convertAndPushPseudo(name);
|
|
23055
|
+
// Advance the function name
|
|
23056
|
+
stream.advance();
|
|
23057
|
+
}
|
|
23058
|
+
} else if (token.type === TokenType$1.OpenSquareBracket) {
|
|
23059
|
+
let tempToken;
|
|
23060
|
+
const {
|
|
23061
|
+
start
|
|
23062
|
+
} = token;
|
|
23063
|
+
stream.advance();
|
|
23064
|
+
// Converts legacy Extended CSS selectors to the modern Extended CSS syntax.
|
|
23065
|
+
// For example:
|
|
23066
|
+
// - `[-ext-has=...]` → `:has(...)`
|
|
23067
|
+
// - `[-ext-contains=...]` → `:contains(...)`
|
|
23068
|
+
// - `[-ext-matches-css-before=...]` → `:matches-css(before, ...)`
|
|
23069
|
+
stream.skipWhitespace();
|
|
23070
|
+
stream.expect(TokenType$1.Ident);
|
|
23071
|
+
tempToken = stream.getOrFail();
|
|
23072
|
+
let attr = stream.source.slice(tempToken.start, tempToken.end);
|
|
23073
|
+
// Skip if the attribute name is not a legacy Extended CSS one
|
|
23074
|
+
if (!(attr.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX) || attr.startsWith(ABP_EXT_CSS_PREFIX))) {
|
|
23075
|
+
converted.push(stream.source.slice(start, tempToken.end));
|
|
23076
|
+
stream.advance();
|
|
23077
|
+
continue;
|
|
23078
|
+
}
|
|
23079
|
+
if (attr.startsWith(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX)) {
|
|
23080
|
+
attr = attr.slice(LEGACY_EXT_CSS_ATTRIBUTE_PREFIX.length);
|
|
23081
|
+
}
|
|
23082
|
+
stream.advance();
|
|
23083
|
+
stream.skipWhitespace();
|
|
23084
|
+
// Next token should be an equality operator (=), because Extended CSS attribute selectors
|
|
23085
|
+
// do not support other operators
|
|
23086
|
+
stream.expect(TokenType$1.Delim, {
|
|
23087
|
+
value: EQUALS
|
|
22607
23088
|
});
|
|
22608
|
-
|
|
22609
|
-
|
|
22610
|
-
|
|
23089
|
+
stream.advance();
|
|
23090
|
+
// Skip optional whitespace after the operator
|
|
23091
|
+
stream.skipWhitespace();
|
|
23092
|
+
// Parse attribute value
|
|
23093
|
+
tempToken = stream.getOrFail();
|
|
23094
|
+
// According to the spec, attribute value should be an identifier or a string
|
|
23095
|
+
if (tempToken.type !== TokenType$1.Ident && tempToken.type !== TokenType$1.String) {
|
|
23096
|
+
throw new Error(sprintf(ERROR_MESSAGES$1.INVALID_ATTRIBUTE_VALUE, getFormattedTokenName(tempToken.type), stream.source.slice(tempToken.start, tempToken.end)));
|
|
22611
23097
|
}
|
|
22612
|
-
|
|
23098
|
+
const value = stream.source.slice(tempToken.start, tempToken.end);
|
|
23099
|
+
// Advance the attribute value
|
|
23100
|
+
stream.advance();
|
|
23101
|
+
// Skip optional whitespace after the attribute value
|
|
23102
|
+
stream.skipWhitespace();
|
|
23103
|
+
// Next character should be a closing square bracket
|
|
23104
|
+
// We don't allow flags for Extended CSS attribute selectors
|
|
23105
|
+
stream.expect(TokenType$1.CloseSquareBracket);
|
|
23106
|
+
stream.advance();
|
|
23107
|
+
converted.push(COLON);
|
|
23108
|
+
convertAndPushPseudo(attr);
|
|
23109
|
+
let processedValue = value.slice(1, -1); // omit the quotes
|
|
23110
|
+
if (attr === PseudoClasses.Has) {
|
|
23111
|
+
// TODO: Optimize this to avoid double tokenization
|
|
23112
|
+
processedValue = CssSelectorConverter.convertToAdg(processedValue).result;
|
|
23113
|
+
}
|
|
23114
|
+
converted.push(processedValue);
|
|
23115
|
+
converted.push(CLOSE_PARENTHESIS);
|
|
23116
|
+
} else {
|
|
23117
|
+
converted.push(stream.source.slice(token.start, token.end));
|
|
23118
|
+
// Advance the token
|
|
23119
|
+
stream.advance();
|
|
22613
23120
|
}
|
|
22614
23121
|
}
|
|
22615
|
-
|
|
22616
|
-
|
|
22617
|
-
return mapObj(input, makeMapper(undefined));
|
|
22618
|
-
};
|
|
22619
|
-
var camelcaseKeys = (input, options) => {
|
|
22620
|
-
if (Array.isArray(input)) {
|
|
22621
|
-
return Object.keys(input).map(key => camelCaseConvert(input[key], options));
|
|
23122
|
+
const convertedSelectorList = converted.join(EMPTY);
|
|
23123
|
+
return createConversionResult(convertedSelectorList, stream.source !== convertedSelectorList);
|
|
22622
23124
|
}
|
|
22623
|
-
|
|
22624
|
-
};
|
|
22625
|
-
var camelCaseKeys = /*@__PURE__*/getDefaultExportFromCjs(camelcaseKeys);
|
|
23125
|
+
}
|
|
22626
23126
|
|
|
22627
23127
|
/**
|
|
22628
|
-
* @file
|
|
23128
|
+
* @file CSS injection rule converter
|
|
22629
23129
|
*/
|
|
22630
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
22631
23130
|
/**
|
|
22632
|
-
*
|
|
22633
|
-
*
|
|
22634
|
-
* @param zod Zod schema.
|
|
22635
|
-
*
|
|
22636
|
-
* @returns Zod schema with camelCase properties.
|
|
23131
|
+
* CSS injection rule converter class
|
|
22637
23132
|
*
|
|
22638
|
-
* @
|
|
22639
|
-
*/
|
|
22640
|
-
const zodToCamelCase = zod => {
|
|
22641
|
-
return zod.transform(val => camelCaseKeys(val));
|
|
22642
|
-
};
|
|
22643
|
-
|
|
22644
|
-
/**
|
|
22645
|
-
* @file Base compatibility data schema, which is commonly used in compatibility tables.
|
|
22646
|
-
*/
|
|
22647
|
-
/**
|
|
22648
|
-
* Zod schema for boolean values. Accepts both boolean and string values.
|
|
22649
|
-
*/
|
|
22650
|
-
const booleanSchema = zod.union([zod.string().transform(val => val.trim().toLowerCase() === 'true'), zod.boolean()]);
|
|
22651
|
-
/**
|
|
22652
|
-
* Zod schema for non-empty string values.
|
|
22653
|
-
*/
|
|
22654
|
-
const nonEmptyStringSchema = zod.string().transform(val => val.trim()).pipe(zod.string().min(1));
|
|
22655
|
-
/**
|
|
22656
|
-
* Zod schema for base compatibility data.
|
|
22657
|
-
* Here we use snake_case properties because the compatibility data is stored in YAML files.
|
|
23133
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22658
23134
|
*/
|
|
22659
|
-
|
|
22660
|
-
/**
|
|
22661
|
-
* Name of the actual entity.
|
|
22662
|
-
*/
|
|
22663
|
-
name: nonEmptyStringSchema,
|
|
22664
|
-
/**
|
|
22665
|
-
* List of aliases for the entity (if any).
|
|
22666
|
-
*/
|
|
22667
|
-
aliases: zod.array(nonEmptyStringSchema).nullable().default(null),
|
|
22668
|
-
/**
|
|
22669
|
-
* Short description of the actual entity.
|
|
22670
|
-
* If not specified or it's value is `null`, then the description is not available.
|
|
22671
|
-
*/
|
|
22672
|
-
description: nonEmptyStringSchema.nullable().default(null),
|
|
22673
|
-
/**
|
|
22674
|
-
* Link to the documentation. If not specified or it's value is `null`, then the documentation is not available.
|
|
22675
|
-
*/
|
|
22676
|
-
docs: nonEmptyStringSchema.nullable().default(null),
|
|
22677
|
-
/**
|
|
22678
|
-
* The version of the adblocker in which the entity was added.
|
|
22679
|
-
* For AdGuard resources, the version of the library is specified.
|
|
22680
|
-
*/
|
|
22681
|
-
version_added: nonEmptyStringSchema.nullable().default(null),
|
|
22682
|
-
/**
|
|
22683
|
-
* The version of the adblocker when the entity was removed.
|
|
22684
|
-
*/
|
|
22685
|
-
version_removed: nonEmptyStringSchema.nullable().default(null),
|
|
22686
|
-
/**
|
|
22687
|
-
* Describes whether the entity is deprecated.
|
|
22688
|
-
*/
|
|
22689
|
-
deprecated: booleanSchema.default(false),
|
|
22690
|
-
/**
|
|
22691
|
-
* Message that describes why the entity is deprecated.
|
|
22692
|
-
* If not specified or it's value is `null`, then the message is not available.
|
|
22693
|
-
* It's value is omitted if the entity is not marked as deprecated.
|
|
22694
|
-
*/
|
|
22695
|
-
deprecation_message: nonEmptyStringSchema.nullable().default(null),
|
|
22696
|
-
/**
|
|
22697
|
-
* Describes whether the entity is removed; for *already removed* features.
|
|
22698
|
-
*/
|
|
22699
|
-
removed: booleanSchema.default(false),
|
|
23135
|
+
class CssInjectionRuleConverter extends RuleConverterBase {
|
|
22700
23136
|
/**
|
|
22701
|
-
*
|
|
22702
|
-
*
|
|
22703
|
-
*
|
|
23137
|
+
* Converts a CSS injection rule to AdGuard format, if possible.
|
|
23138
|
+
*
|
|
23139
|
+
* @param rule Rule node to convert
|
|
23140
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23141
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23142
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23143
|
+
* @throws If the rule is invalid or cannot be converted
|
|
22704
23144
|
*/
|
|
22705
|
-
|
|
22706
|
-
|
|
22707
|
-
|
|
22708
|
-
|
|
22709
|
-
|
|
22710
|
-
|
|
22711
|
-
|
|
22712
|
-
|
|
22713
|
-
|
|
22714
|
-
|
|
22715
|
-
|
|
22716
|
-
|
|
22717
|
-
|
|
22718
|
-
|
|
22719
|
-
|
|
22720
|
-
|
|
22721
|
-
|
|
22722
|
-
|
|
22723
|
-
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
|
|
22727
|
-
|
|
22728
|
-
}
|
|
22729
|
-
|
|
22730
|
-
|
|
22731
|
-
ctx.addIssue({
|
|
22732
|
-
code: zod.ZodIssueCode.custom,
|
|
22733
|
-
message: 'Aliases must be unique'
|
|
22734
|
-
});
|
|
23145
|
+
static convertToAdg(rule) {
|
|
23146
|
+
const separator = rule.separator.value;
|
|
23147
|
+
let convertedSeparator = separator;
|
|
23148
|
+
const stream = new CssTokenStream(rule.body.selectorList.value);
|
|
23149
|
+
const convertedSelectorList = CssSelectorConverter.convertToAdg(stream);
|
|
23150
|
+
// Change the separator if the rule contains ExtendedCSS elements,
|
|
23151
|
+
// but do not force non-extended CSS separator if the rule does not contain any ExtendedCSS selectors,
|
|
23152
|
+
// because sometimes we use it to force executing ExtendedCSS library.
|
|
23153
|
+
if (stream.hasAnySelectorExtendedCssNodeStrict() || rule.body.remove) {
|
|
23154
|
+
convertedSeparator = rule.exception ? CosmeticRuleSeparator.AdgExtendedCssInjectionException : CosmeticRuleSeparator.AdgExtendedCssInjection;
|
|
23155
|
+
} else if (rule.syntax !== AdblockSyntax.Adg) {
|
|
23156
|
+
// If the original rule syntax is not AdGuard, use the default separator
|
|
23157
|
+
// e.g. if the input rule is from uBO, we need to convert ## to #$#.
|
|
23158
|
+
convertedSeparator = rule.exception ? CosmeticRuleSeparator.AdgCssInjectionException : CosmeticRuleSeparator.AdgCssInjection;
|
|
23159
|
+
}
|
|
23160
|
+
// Check if the rule needs to be converted
|
|
23161
|
+
if (!(rule.syntax === AdblockSyntax.Common || rule.syntax === AdblockSyntax.Adg) || separator !== convertedSeparator || convertedSelectorList.isConverted) {
|
|
23162
|
+
// TODO: Replace with custom clone method
|
|
23163
|
+
const ruleClone = clone(rule);
|
|
23164
|
+
ruleClone.syntax = AdblockSyntax.Adg;
|
|
23165
|
+
ruleClone.separator.value = convertedSeparator;
|
|
23166
|
+
ruleClone.body.selectorList.value = convertedSelectorList.result;
|
|
23167
|
+
return createNodeConversionResult([ruleClone], true);
|
|
23168
|
+
}
|
|
23169
|
+
// Otherwise, return the original rule
|
|
23170
|
+
return createNodeConversionResult([rule], false);
|
|
22735
23171
|
}
|
|
22736
|
-
}
|
|
23172
|
+
}
|
|
22737
23173
|
|
|
22738
23174
|
/**
|
|
22739
|
-
*
|
|
22740
|
-
*
|
|
22741
|
-
* @param error Error object.
|
|
22742
|
-
* @returns If param is error.
|
|
23175
|
+
* @file Element hiding rule converter
|
|
22743
23176
|
*/
|
|
22744
|
-
function isErrorWithMessage(error) {
|
|
22745
|
-
return typeof error === 'object' && error !== null && 'message' in error && typeof error.message === 'string';
|
|
22746
|
-
}
|
|
22747
23177
|
/**
|
|
22748
|
-
*
|
|
23178
|
+
* Element hiding rule converter class
|
|
22749
23179
|
*
|
|
22750
|
-
* @
|
|
22751
|
-
* @returns Error with message.
|
|
23180
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22752
23181
|
*/
|
|
22753
|
-
|
|
22754
|
-
|
|
22755
|
-
|
|
22756
|
-
|
|
22757
|
-
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
22761
|
-
|
|
22762
|
-
|
|
23182
|
+
class ElementHidingRuleConverter extends RuleConverterBase {
|
|
23183
|
+
/**
|
|
23184
|
+
* Converts an element hiding rule to AdGuard format, if possible.
|
|
23185
|
+
*
|
|
23186
|
+
* @param rule Rule node to convert
|
|
23187
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23188
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23189
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23190
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23191
|
+
*/
|
|
23192
|
+
static convertToAdg(rule) {
|
|
23193
|
+
const separator = rule.separator.value;
|
|
23194
|
+
let convertedSeparator = separator;
|
|
23195
|
+
const stream = new CssTokenStream(rule.body.selectorList.value);
|
|
23196
|
+
const convertedSelectorList = CssSelectorConverter.convertToAdg(stream);
|
|
23197
|
+
// Change the separator if the rule contains ExtendedCSS elements,
|
|
23198
|
+
// but do not force non-extended CSS separator if the rule does not contain any ExtendedCSS selectors,
|
|
23199
|
+
// because sometimes we use it to force executing ExtendedCSS library.
|
|
23200
|
+
if (stream.hasAnySelectorExtendedCssNodeStrict()) {
|
|
23201
|
+
convertedSeparator = rule.exception ? CosmeticRuleSeparator.ExtendedElementHidingException : CosmeticRuleSeparator.ExtendedElementHiding;
|
|
23202
|
+
}
|
|
23203
|
+
// Check if the rule needs to be converted
|
|
23204
|
+
if (!(rule.syntax === AdblockSyntax.Common || rule.syntax === AdblockSyntax.Adg) || separator !== convertedSeparator || convertedSelectorList.isConverted) {
|
|
23205
|
+
// TODO: Replace with custom clone method
|
|
23206
|
+
const ruleClone = clone(rule);
|
|
23207
|
+
ruleClone.syntax = AdblockSyntax.Adg;
|
|
23208
|
+
ruleClone.separator.value = convertedSeparator;
|
|
23209
|
+
ruleClone.body.selectorList.value = convertedSelectorList.result;
|
|
23210
|
+
return createNodeConversionResult([ruleClone], true);
|
|
23211
|
+
}
|
|
23212
|
+
// Otherwise, return the original rule
|
|
23213
|
+
return createNodeConversionResult([rule], false);
|
|
22763
23214
|
}
|
|
22764
23215
|
}
|
|
22765
|
-
/**
|
|
22766
|
-
* Converts error object to error with message. This method might be helpful to handle thrown errors.
|
|
22767
|
-
*
|
|
22768
|
-
* @param error Error object.
|
|
22769
|
-
*
|
|
22770
|
-
* @returns Message of the error.
|
|
22771
|
-
*/
|
|
22772
|
-
function getErrorMessage(error) {
|
|
22773
|
-
return toErrorWithMessage(error).message;
|
|
22774
|
-
}
|
|
22775
23216
|
|
|
22776
23217
|
/**
|
|
22777
|
-
* @file
|
|
22778
|
-
*/
|
|
22779
|
-
/**
|
|
22780
|
-
* Known validators that don't need to be validated as regex.
|
|
23218
|
+
* @file Utility functions for working with network rule nodes
|
|
22781
23219
|
*/
|
|
22782
|
-
const KNOWN_VALIDATORS = new Set(['domain', 'pipe_separated_domains', 'regexp', 'url']);
|
|
22783
23220
|
/**
|
|
22784
|
-
*
|
|
23221
|
+
* Creates a network rule node
|
|
23222
|
+
*
|
|
23223
|
+
* @param pattern Rule pattern
|
|
23224
|
+
* @param modifiers Rule modifiers (optional, default: undefined)
|
|
23225
|
+
* @param exception Exception rule flag (optional, default: false)
|
|
23226
|
+
* @param syntax Adblock syntax (optional, default: Common)
|
|
23227
|
+
* @returns Network rule node
|
|
22785
23228
|
*/
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22789
|
-
|
|
22790
|
-
|
|
22791
|
-
|
|
22792
|
-
|
|
22793
|
-
|
|
22794
|
-
|
|
22795
|
-
/**
|
|
22796
|
-
* Describes whether the actual modifier supports value assignment. For example, `$domain` is assignable,
|
|
22797
|
-
* so it can be used like this: `$domain=domain.com\|~subdomain.domain.com`, where `=` is the assignment operator
|
|
22798
|
-
* and `domain.com\|~subdomain.domain.com` is the value.
|
|
22799
|
-
*/
|
|
22800
|
-
assignable: booleanSchema.default(false),
|
|
22801
|
-
/**
|
|
22802
|
-
* Describes whether the actual modifier can be negated. For example, `$third-party` is negatable,
|
|
22803
|
-
* so it can be used like this: `$~third-party`.
|
|
22804
|
-
*/
|
|
22805
|
-
negatable: booleanSchema.default(true),
|
|
22806
|
-
/**
|
|
22807
|
-
* The actual modifier can only be used in blocking rules, it cannot be used in exceptions.
|
|
22808
|
-
* If it's value is `true`, then the modifier can be used only in blocking rules.
|
|
22809
|
-
* `exception_only` and `block_only` cannot be used together (they are mutually exclusive).
|
|
22810
|
-
*/
|
|
22811
|
-
block_only: booleanSchema.default(false),
|
|
22812
|
-
/**
|
|
22813
|
-
* The actual modifier can only be used in exceptions, it cannot be used in blocking rules.
|
|
22814
|
-
* If it's value is `true`, then the modifier can be used only in exceptions.
|
|
22815
|
-
* `exception_only` and `block_only` cannot be used together (they are mutually exclusive).
|
|
22816
|
-
*/
|
|
22817
|
-
exception_only: booleanSchema.default(false),
|
|
22818
|
-
/**
|
|
22819
|
-
* Describes whether the *assignable* modifier value is required.
|
|
22820
|
-
* For example, `$cookie` is assignable but it can be used without a value in exception rules:
|
|
22821
|
-
* `@@\|\|example.com^$cookie`.
|
|
22822
|
-
* If `false`, the `value_format` is required, e.g. the value of `$app` should always be specified
|
|
22823
|
-
*/
|
|
22824
|
-
value_optional: booleanSchema.default(false),
|
|
22825
|
-
/**
|
|
22826
|
-
* Describes the format of the value for the *assignable* modifier.
|
|
22827
|
-
* Its value can be a regex pattern or a known validator name (e.g. `domain`, `pipe_separated_domains`, etc.).
|
|
22828
|
-
*/
|
|
22829
|
-
value_format: nonEmptyStringSchema.nullable().default(null)
|
|
22830
|
-
}).superRefine((data, ctx) => {
|
|
22831
|
-
// TODO: find something better, for now we can't add refine logic to the base schema:
|
|
22832
|
-
// https://github.com/colinhacks/zod/issues/454#issuecomment-848370721
|
|
22833
|
-
baseRefineLogic(data, ctx);
|
|
22834
|
-
if (data.block_only && data.exception_only) {
|
|
22835
|
-
ctx.addIssue({
|
|
22836
|
-
code: zod.ZodIssueCode.custom,
|
|
22837
|
-
message: 'block_only and exception_only are mutually exclusive'
|
|
22838
|
-
});
|
|
22839
|
-
}
|
|
22840
|
-
if (data.assignable && !data.value_format) {
|
|
22841
|
-
ctx.addIssue({
|
|
22842
|
-
code: zod.ZodIssueCode.custom,
|
|
22843
|
-
message: 'value_format is required for assignable modifiers'
|
|
22844
|
-
});
|
|
22845
|
-
}
|
|
22846
|
-
if (data.value_format) {
|
|
22847
|
-
const valueFormat = data.value_format.trim();
|
|
22848
|
-
// if it is a known validator, we don't need to validate it further
|
|
22849
|
-
if (KNOWN_VALIDATORS.has(valueFormat)) {
|
|
22850
|
-
return;
|
|
22851
|
-
}
|
|
22852
|
-
// otherwise, we need to validate it as a regex
|
|
22853
|
-
try {
|
|
22854
|
-
XRegExp(valueFormat);
|
|
22855
|
-
} catch (error) {
|
|
22856
|
-
ctx.addIssue({
|
|
22857
|
-
code: zod.ZodIssueCode.custom,
|
|
22858
|
-
message: getErrorMessage(error)
|
|
22859
|
-
});
|
|
23229
|
+
function createNetworkRuleNode(pattern, modifiers = undefined, exception = false, syntax = AdblockSyntax.Common) {
|
|
23230
|
+
const result = {
|
|
23231
|
+
category: RuleCategory.Network,
|
|
23232
|
+
type: NetworkRuleType.NetworkRule,
|
|
23233
|
+
syntax,
|
|
23234
|
+
exception,
|
|
23235
|
+
pattern: {
|
|
23236
|
+
type: 'Value',
|
|
23237
|
+
value: pattern
|
|
22860
23238
|
}
|
|
23239
|
+
};
|
|
23240
|
+
if (!isUndefined(modifiers)) {
|
|
23241
|
+
result.modifiers = clone(modifiers);
|
|
22861
23242
|
}
|
|
22862
|
-
|
|
23243
|
+
return result;
|
|
23244
|
+
}
|
|
22863
23245
|
|
|
22864
23246
|
/**
|
|
22865
|
-
* @file
|
|
23247
|
+
* @file Converter for request header removal rules
|
|
22866
23248
|
*/
|
|
23249
|
+
const UBO_RESPONSEHEADER_FN = 'responseheader';
|
|
23250
|
+
const ADG_REMOVEHEADER_MODIFIER = 'removeheader';
|
|
23251
|
+
const ERROR_MESSAGES = {
|
|
23252
|
+
EMPTY_PARAMETER: `Empty parameter for '${UBO_RESPONSEHEADER_FN}' function`,
|
|
23253
|
+
EXPECTED_END_OF_RULE: "Expected end of rule, but got '%s'",
|
|
23254
|
+
MULTIPLE_DOMAINS_NOT_SUPPORTED: 'Multiple domains are not supported yet'
|
|
23255
|
+
};
|
|
22867
23256
|
/**
|
|
22868
|
-
*
|
|
23257
|
+
* Converter for request header removal rules
|
|
23258
|
+
*
|
|
23259
|
+
* @todo Implement `convertToUbo` (ABP currently doesn't support header removal rules)
|
|
22869
23260
|
*/
|
|
22870
|
-
|
|
23261
|
+
class HeaderRemovalRuleConverter extends RuleConverterBase {
|
|
22871
23262
|
/**
|
|
22872
|
-
*
|
|
23263
|
+
* Converts a header removal rule to AdGuard syntax, if possible.
|
|
23264
|
+
*
|
|
23265
|
+
* @param rule Rule node to convert
|
|
23266
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23267
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23268
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23269
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23270
|
+
* @example
|
|
23271
|
+
* If the input rule is:
|
|
23272
|
+
* ```adblock
|
|
23273
|
+
* example.com##^responseheader(header-name)
|
|
23274
|
+
* ```
|
|
23275
|
+
* The output will be:
|
|
23276
|
+
* ```adblock
|
|
23277
|
+
* ||example.com^$removeheader=header-name
|
|
23278
|
+
* ```
|
|
22873
23279
|
*/
|
|
22874
|
-
|
|
22875
|
-
|
|
23280
|
+
static convertToAdg(rule) {
|
|
23281
|
+
// TODO: Add support for ABP syntax once it starts supporting header removal rules
|
|
23282
|
+
// Leave the rule as is if it's not a header removal rule
|
|
23283
|
+
if (rule.category !== RuleCategory.Cosmetic || rule.type !== CosmeticRuleType.HtmlFilteringRule) {
|
|
23284
|
+
return createNodeConversionResult([rule], false);
|
|
23285
|
+
}
|
|
23286
|
+
const stream = new CssTokenStream(rule.body.value);
|
|
23287
|
+
let token;
|
|
23288
|
+
// Skip leading whitespace
|
|
23289
|
+
stream.skipWhitespace();
|
|
23290
|
+
// Next token should be the `^` followed by a `responseheader` function
|
|
23291
|
+
token = stream.get();
|
|
23292
|
+
if (!token || token.type !== TokenType$1.Delim || rule.body.value[token.start] !== UBO_HTML_MASK) {
|
|
23293
|
+
return createNodeConversionResult([rule], false);
|
|
23294
|
+
}
|
|
23295
|
+
stream.advance();
|
|
23296
|
+
token = stream.get();
|
|
23297
|
+
if (!token) {
|
|
23298
|
+
return createNodeConversionResult([rule], false);
|
|
23299
|
+
}
|
|
23300
|
+
const functionName = rule.body.value.slice(token.start, token.end - 1);
|
|
23301
|
+
if (functionName !== UBO_RESPONSEHEADER_FN) {
|
|
23302
|
+
return createNodeConversionResult([rule], false);
|
|
23303
|
+
}
|
|
23304
|
+
// Parse the parameter
|
|
23305
|
+
const paramStart = token.end;
|
|
23306
|
+
stream.skipUntilBalanced();
|
|
23307
|
+
const paramEnd = stream.getOrFail().end;
|
|
23308
|
+
const param = rule.body.value.slice(paramStart, paramEnd - 1).trim();
|
|
23309
|
+
// Do not allow empty parameter
|
|
23310
|
+
if (param.length === 0) {
|
|
23311
|
+
throw new RuleConversionError(ERROR_MESSAGES.EMPTY_PARAMETER);
|
|
23312
|
+
}
|
|
23313
|
+
stream.expect(TokenType$1.CloseParenthesis);
|
|
23314
|
+
stream.advance();
|
|
23315
|
+
// Skip trailing whitespace after the function call
|
|
23316
|
+
stream.skipWhitespace();
|
|
23317
|
+
// Expect the end of the rule - so nothing should be left in the stream
|
|
23318
|
+
if (!stream.isEof()) {
|
|
23319
|
+
token = stream.getOrFail();
|
|
23320
|
+
throw new RuleConversionError(sprintf(ERROR_MESSAGES.EXPECTED_END_OF_RULE, getFormattedTokenName(token.type)));
|
|
23321
|
+
}
|
|
23322
|
+
// Prepare network rule pattern
|
|
23323
|
+
const pattern = [];
|
|
23324
|
+
if (rule.domains.children.length === 1) {
|
|
23325
|
+
// If the rule has only one domain, we can use a simple network rule pattern:
|
|
23326
|
+
// ||single-domain-from-the-rule^
|
|
23327
|
+
pattern.push(ADBLOCK_URL_START, rule.domains.children[0].value, ADBLOCK_URL_SEPARATOR);
|
|
23328
|
+
} else if (rule.domains.children.length > 1) {
|
|
23329
|
+
// TODO: Add support for multiple domains, for example:
|
|
23330
|
+
// example.com,example.org,example.net##^responseheader(header-name)
|
|
23331
|
+
// We should consider allowing $domain with $removeheader modifier,
|
|
23332
|
+
// for example:
|
|
23333
|
+
// $removeheader=header-name,domain=example.com|example.org|example.net
|
|
23334
|
+
throw new RuleConversionError(ERROR_MESSAGES.MULTIPLE_DOMAINS_NOT_SUPPORTED);
|
|
23335
|
+
}
|
|
23336
|
+
// Prepare network rule modifiers
|
|
23337
|
+
const modifiers = createModifierListNode();
|
|
23338
|
+
modifiers.children.push(createModifierNode(ADG_REMOVEHEADER_MODIFIER, param));
|
|
23339
|
+
// Construct the network rule
|
|
23340
|
+
return createNodeConversionResult([createNetworkRuleNode(pattern.join(EMPTY), modifiers,
|
|
23341
|
+
// Copy the exception flag
|
|
23342
|
+
rule.exception, AdblockSyntax.Adg)], true);
|
|
23343
|
+
}
|
|
23344
|
+
}
|
|
22876
23345
|
|
|
22877
23346
|
/**
|
|
22878
|
-
* @file
|
|
22879
|
-
*/
|
|
22880
|
-
/**
|
|
22881
|
-
* Zod schema for scriptlet parameter data.
|
|
22882
|
-
*/
|
|
22883
|
-
const scriptletParameterSchema = zod.object({
|
|
22884
|
-
/**
|
|
22885
|
-
* Name of the actual parameter.
|
|
22886
|
-
*/
|
|
22887
|
-
name: nonEmptyStringSchema,
|
|
22888
|
-
/**
|
|
22889
|
-
* Describes whether the parameter is required. Empty parameters are not allowed.
|
|
22890
|
-
*/
|
|
22891
|
-
required: booleanSchema,
|
|
22892
|
-
/**
|
|
22893
|
-
* Short description of the parameter.
|
|
22894
|
-
* If not specified or it's value is `null`,then the description is not available.
|
|
22895
|
-
*/
|
|
22896
|
-
description: nonEmptyStringSchema.nullable().default(null),
|
|
22897
|
-
/**
|
|
22898
|
-
* Regular expression that matches the value of the parameter.
|
|
22899
|
-
* If it's value is `null`, then the parameter value is not checked.
|
|
22900
|
-
*/
|
|
22901
|
-
pattern: nonEmptyStringSchema.nullable().default(null),
|
|
22902
|
-
/**
|
|
22903
|
-
* Default value of the parameter (if any).
|
|
22904
|
-
*/
|
|
22905
|
-
default: nonEmptyStringSchema.nullable().default(null),
|
|
22906
|
-
/**
|
|
22907
|
-
* Describes whether the parameter is used only for debugging purposes.
|
|
22908
|
-
*/
|
|
22909
|
-
debug: booleanSchema.default(false)
|
|
22910
|
-
});
|
|
22911
|
-
/**
|
|
22912
|
-
* Zod schema for scriptlet parameters.
|
|
23347
|
+
* @file Cosmetic rule converter
|
|
22913
23348
|
*/
|
|
22914
|
-
const scriptletParametersSchema = zod.array(scriptletParameterSchema);
|
|
22915
23349
|
/**
|
|
22916
|
-
*
|
|
23350
|
+
* Cosmetic rule converter class (also known as "non-basic rule converter")
|
|
23351
|
+
*
|
|
23352
|
+
* @todo Implement `convertToUbo` and `convertToAbp`
|
|
22917
23353
|
*/
|
|
22918
|
-
|
|
23354
|
+
class CosmeticRuleConverter extends RuleConverterBase {
|
|
22919
23355
|
/**
|
|
22920
|
-
*
|
|
22921
|
-
*
|
|
22922
|
-
*
|
|
23356
|
+
* Converts a cosmetic rule to AdGuard syntax, if possible.
|
|
23357
|
+
*
|
|
23358
|
+
* @param rule Rule node to convert
|
|
23359
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23360
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23361
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23362
|
+
* @throws If the rule is invalid or cannot be converted
|
|
22923
23363
|
*/
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
|
|
22929
|
-
|
|
22930
|
-
|
|
22931
|
-
|
|
22932
|
-
|
|
22933
|
-
|
|
22934
|
-
|
|
22935
|
-
|
|
22936
|
-
|
|
22937
|
-
|
|
22938
|
-
|
|
23364
|
+
static convertToAdg(rule) {
|
|
23365
|
+
let subconverterResult;
|
|
23366
|
+
// Convert cosmetic rule based on its type
|
|
23367
|
+
switch (rule.type) {
|
|
23368
|
+
case CosmeticRuleType.ElementHidingRule:
|
|
23369
|
+
subconverterResult = ElementHidingRuleConverter.convertToAdg(rule);
|
|
23370
|
+
break;
|
|
23371
|
+
case CosmeticRuleType.ScriptletInjectionRule:
|
|
23372
|
+
subconverterResult = ScriptletRuleConverter.convertToAdg(rule);
|
|
23373
|
+
break;
|
|
23374
|
+
case CosmeticRuleType.CssInjectionRule:
|
|
23375
|
+
subconverterResult = CssInjectionRuleConverter.convertToAdg(rule);
|
|
23376
|
+
break;
|
|
23377
|
+
case CosmeticRuleType.HtmlFilteringRule:
|
|
23378
|
+
// Handle special case: uBO response header filtering rule
|
|
23379
|
+
// TODO: Optimize double CSS tokenization here
|
|
23380
|
+
subconverterResult = HeaderRemovalRuleConverter.convertToAdg(rule);
|
|
23381
|
+
if (subconverterResult.isConverted) {
|
|
23382
|
+
break;
|
|
23383
|
+
}
|
|
23384
|
+
subconverterResult = HtmlRuleConverter.convertToAdg(rule);
|
|
23385
|
+
break;
|
|
23386
|
+
// Note: Currently, only ADG supports JS injection rules, so we don't need to convert them
|
|
23387
|
+
case CosmeticRuleType.JsInjectionRule:
|
|
23388
|
+
subconverterResult = createNodeConversionResult([rule], false);
|
|
23389
|
+
break;
|
|
23390
|
+
default:
|
|
23391
|
+
throw new RuleConversionError('Unsupported cosmetic rule type');
|
|
23392
|
+
}
|
|
23393
|
+
let convertedModifiers;
|
|
23394
|
+
// Convert cosmetic rule modifiers, if any
|
|
23395
|
+
if (rule.modifiers) {
|
|
23396
|
+
if (rule.syntax === AdblockSyntax.Ubo) {
|
|
23397
|
+
// uBO doesn't support this rule:
|
|
23398
|
+
// example.com##+js(set-constant.js, foo, bar):matches-path(/baz)
|
|
23399
|
+
if (rule.type === CosmeticRuleType.ScriptletInjectionRule) {
|
|
23400
|
+
throw new RuleConversionError('uBO scriptlet injection rules don\'t support cosmetic rule modifiers');
|
|
23401
|
+
}
|
|
23402
|
+
convertedModifiers = AdgCosmeticRuleModifierConverter.convertFromUbo(rule.modifiers);
|
|
23403
|
+
} else if (rule.syntax === AdblockSyntax.Abp) {
|
|
23404
|
+
// TODO: Implement once ABP starts supporting cosmetic rule modifiers
|
|
23405
|
+
throw new RuleConversionError('ABP don\'t support cosmetic rule modifiers');
|
|
23406
|
+
}
|
|
23407
|
+
}
|
|
23408
|
+
if (subconverterResult.result.length > 1 || subconverterResult.isConverted || convertedModifiers && convertedModifiers.isConverted) {
|
|
23409
|
+
// Add modifier list to the subconverter result rules
|
|
23410
|
+
subconverterResult.result.forEach(subconverterRule => {
|
|
23411
|
+
if (convertedModifiers && subconverterRule.category === RuleCategory.Cosmetic) {
|
|
23412
|
+
// eslint-disable-next-line no-param-reassign
|
|
23413
|
+
subconverterRule.modifiers = convertedModifiers.result;
|
|
23414
|
+
}
|
|
22939
23415
|
});
|
|
23416
|
+
return subconverterResult;
|
|
22940
23417
|
}
|
|
22941
|
-
|
|
22942
|
-
|
|
23418
|
+
return createNodeConversionResult([rule], false);
|
|
23419
|
+
}
|
|
23420
|
+
/**
|
|
23421
|
+
* Converts a cosmetic rule to uBlock Origin syntax, if possible.
|
|
23422
|
+
*
|
|
23423
|
+
* @param rule Rule node to convert
|
|
23424
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23425
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23426
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23427
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23428
|
+
*/
|
|
23429
|
+
// TODO: Add support for other cosmetic rule types
|
|
23430
|
+
static convertToUbo(rule) {
|
|
23431
|
+
// Convert cosmetic rule based on its type
|
|
23432
|
+
if (rule.type === CosmeticRuleType.ScriptletInjectionRule) {
|
|
23433
|
+
if (rule.syntax === AdblockSyntax.Adg && rule.modifiers?.children.length) {
|
|
23434
|
+
// e.g. example.com##+js(set-constant.js, foo, bar):matches-path(/baz)
|
|
23435
|
+
throw new RuleConversionError('uBO scriptlet injection rules do not support cosmetic rule modifiers');
|
|
23436
|
+
}
|
|
23437
|
+
return ScriptletRuleConverter.convertToUbo(rule);
|
|
22943
23438
|
}
|
|
23439
|
+
return createNodeConversionResult([rule], false);
|
|
22944
23440
|
}
|
|
22945
|
-
}
|
|
23441
|
+
}
|
|
22946
23442
|
|
|
22947
23443
|
/**
|
|
22948
23444
|
* @file Network rule modifier list converter.
|
|
@@ -22969,6 +23465,10 @@ const REDIRECT_MODIFIER = 'redirect';
|
|
|
22969
23465
|
* @see {@link https://adguard.com/kb/general/ad-filtering/create-own-filters/#redirect-rule-modifier}
|
|
22970
23466
|
*/
|
|
22971
23467
|
const REDIRECT_RULE_MODIFIER = 'redirect-rule';
|
|
23468
|
+
/**
|
|
23469
|
+
* @see {@link https://github.com/gorhill/uBlock/wiki/Resources-Library#empty-redirect-resources}
|
|
23470
|
+
*/
|
|
23471
|
+
const UBO_NOOP_TEXT_RESOURCE = 'noop.txt';
|
|
22972
23472
|
/**
|
|
22973
23473
|
* Redirect-related modifiers.
|
|
22974
23474
|
*/
|
|
@@ -23118,6 +23618,112 @@ class NetworkRuleModifierListConverter extends ConverterBase {
|
|
|
23118
23618
|
}
|
|
23119
23619
|
return createConversionResult(modifierList, false);
|
|
23120
23620
|
}
|
|
23621
|
+
/**
|
|
23622
|
+
* Converts a network rule modifier list to uBlock format, if possible.
|
|
23623
|
+
*
|
|
23624
|
+
* @param modifierList Network rule modifier list node to convert
|
|
23625
|
+
* @param isException If `true`, the rule is an exception rule
|
|
23626
|
+
* @returns An object which follows the {@link ConversionResult} interface. Its `result` property contains
|
|
23627
|
+
* the converted node, and its `isConverted` flag indicates whether the original node was converted.
|
|
23628
|
+
* If the node was not converted, the result will contain the original node with the same object reference
|
|
23629
|
+
* @throws If the conversion is not possible
|
|
23630
|
+
*/
|
|
23631
|
+
// TODO: Optimize
|
|
23632
|
+
static convertToUbo(modifierList, isException = false) {
|
|
23633
|
+
const conversionMap = new MultiValueMap();
|
|
23634
|
+
const resourceTypeModifiersToAdd = new Set();
|
|
23635
|
+
modifierList.children.forEach((modifierNode, index) => {
|
|
23636
|
+
const originalModifierName = modifierNode.name.value;
|
|
23637
|
+
const modifierData = modifiersCompatibilityTable.getFirst(originalModifierName, GenericPlatform.UboAny);
|
|
23638
|
+
// Handle special case: resource redirection modifiers
|
|
23639
|
+
if (REDIRECT_MODIFIERS.has(originalModifierName)) {
|
|
23640
|
+
// Redirect modifiers cannot be negated
|
|
23641
|
+
if (modifierNode.exception === true) {
|
|
23642
|
+
throw new RuleConversionError(`Modifier '${modifierNode.name.value}' cannot be negated`);
|
|
23643
|
+
}
|
|
23644
|
+
// Convert the redirect resource name to uBO format
|
|
23645
|
+
const redirectResourceName = modifierNode.value?.value;
|
|
23646
|
+
// Special case: for exception rules, $redirect without value is allowed,
|
|
23647
|
+
// and in this case it means an exception for all redirects
|
|
23648
|
+
if (!redirectResourceName && !isException) {
|
|
23649
|
+
throw new RuleConversionError(`No redirect resource specified for '${modifierNode.name.value}' modifier`);
|
|
23650
|
+
}
|
|
23651
|
+
if (!redirectResourceName) {
|
|
23652
|
+
// Jump to the next modifier if the redirect resource is not specified
|
|
23653
|
+
return;
|
|
23654
|
+
}
|
|
23655
|
+
// Leave $redirect and $redirect-rule modifiers as is, but convert $rewrite to $redirect
|
|
23656
|
+
const modifierName = modifierNode.name.value === ABP_REWRITE_MODIFIER ? REDIRECT_MODIFIER : modifierNode.name.value;
|
|
23657
|
+
const convertedRedirectResourceData = redirectsCompatibilityTable.getFirst(redirectResourceName, GenericPlatform.UboAny);
|
|
23658
|
+
const convertedRedirectResourceName = convertedRedirectResourceData?.name ?? redirectResourceName;
|
|
23659
|
+
// uBlock requires the $redirect modifier to have a resource type
|
|
23660
|
+
// https://github.com/AdguardTeam/Scriptlets/issues/101
|
|
23661
|
+
if (convertedRedirectResourceData?.resourceTypes?.length) {
|
|
23662
|
+
// Convert the resource types to uBO modifiers
|
|
23663
|
+
const uboResourceTypeModifiers = redirectsCompatibilityTable.getResourceTypeModifiers(convertedRedirectResourceData, GenericPlatform.UboAny);
|
|
23664
|
+
// Special case: noop text resource
|
|
23665
|
+
// If any of resource type is already present, we don't need to add other resource types,
|
|
23666
|
+
// otherwise, add all resource types
|
|
23667
|
+
// TODO: Optimize this logic
|
|
23668
|
+
// Check if the current resource is the noop text resource
|
|
23669
|
+
const isNoopTextResource = convertedRedirectResourceName === UBO_NOOP_TEXT_RESOURCE;
|
|
23670
|
+
// Determine if there are any valid resource types already present
|
|
23671
|
+
const hasValidResourceType = modifierList.children.some(modifier => {
|
|
23672
|
+
const name = modifier.name.value;
|
|
23673
|
+
if (!isValidResourceType(name)) {
|
|
23674
|
+
return false;
|
|
23675
|
+
}
|
|
23676
|
+
const convertedModifierData = modifiersCompatibilityTable.getFirst(name, GenericPlatform.UboAny);
|
|
23677
|
+
return uboResourceTypeModifiers.has(convertedModifierData?.name ?? name);
|
|
23678
|
+
});
|
|
23679
|
+
// If it's not the noop text resource or if no valid resource types are present
|
|
23680
|
+
if (!isNoopTextResource || !hasValidResourceType) {
|
|
23681
|
+
uboResourceTypeModifiers.forEach(resourceType => {
|
|
23682
|
+
resourceTypeModifiersToAdd.add(resourceType);
|
|
23683
|
+
});
|
|
23684
|
+
}
|
|
23685
|
+
}
|
|
23686
|
+
// Check if the modifier name or the redirect resource name is different from the original modifier.
|
|
23687
|
+
// If so, add the converted modifier to the list
|
|
23688
|
+
if (modifierName !== originalModifierName || !isUndefined(convertedRedirectResourceName) && convertedRedirectResourceName !== redirectResourceName) {
|
|
23689
|
+
conversionMap.add(index, createModifierNode(modifierName,
|
|
23690
|
+
// If the redirect resource name is unknown, fall back to the original one
|
|
23691
|
+
// Later, the validator will throw an error if the resource name is invalid
|
|
23692
|
+
convertedRedirectResourceName || redirectResourceName, modifierNode.exception));
|
|
23693
|
+
}
|
|
23694
|
+
return;
|
|
23695
|
+
}
|
|
23696
|
+
// Generic modifier conversion
|
|
23697
|
+
if (modifierData && modifierData.name !== originalModifierName) {
|
|
23698
|
+
conversionMap.add(index, createModifierNode(modifierData.name, modifierNode.value?.value, modifierNode.exception));
|
|
23699
|
+
}
|
|
23700
|
+
});
|
|
23701
|
+
// Prepare the result if there are any converted modifiers or $csp modifiers
|
|
23702
|
+
if (conversionMap.size || resourceTypeModifiersToAdd.size) {
|
|
23703
|
+
const modifierListClone = cloneModifierListNode(modifierList);
|
|
23704
|
+
// Replace the original modifiers with the converted ones
|
|
23705
|
+
// One modifier may be replaced with multiple modifiers, so we need to flatten the array
|
|
23706
|
+
modifierListClone.children = modifierListClone.children.map((modifierNode, index) => {
|
|
23707
|
+
const conversionRecord = conversionMap.get(index);
|
|
23708
|
+
if (conversionRecord) {
|
|
23709
|
+
return conversionRecord;
|
|
23710
|
+
}
|
|
23711
|
+
return modifierNode;
|
|
23712
|
+
}).flat();
|
|
23713
|
+
// Before returning the result, remove duplicated modifiers
|
|
23714
|
+
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);
|
|
23715
|
+
if (resourceTypeModifiersToAdd.size) {
|
|
23716
|
+
const modifierNameSet = new Set(modifierList.children.map(m => m.name.value));
|
|
23717
|
+
resourceTypeModifiersToAdd.forEach(resourceType => {
|
|
23718
|
+
if (!modifierNameSet.has(resourceType)) {
|
|
23719
|
+
modifierListClone.children.push(createModifierNode(resourceType));
|
|
23720
|
+
}
|
|
23721
|
+
});
|
|
23722
|
+
}
|
|
23723
|
+
return createConversionResult(modifierListClone, true);
|
|
23724
|
+
}
|
|
23725
|
+
return createConversionResult(modifierList, false);
|
|
23726
|
+
}
|
|
23121
23727
|
}
|
|
23122
23728
|
|
|
23123
23729
|
/**
|
|
@@ -23167,6 +23773,44 @@ class NetworkRuleConverter extends RuleConverterBase {
|
|
|
23167
23773
|
// If the modifiers were not converted, return the original rule
|
|
23168
23774
|
return createNodeConversionResult([rule], false);
|
|
23169
23775
|
}
|
|
23776
|
+
/**
|
|
23777
|
+
* Converts a network rule to uBlock format, if possible.
|
|
23778
|
+
*
|
|
23779
|
+
* @param rule Rule node to convert
|
|
23780
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23781
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23782
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23783
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23784
|
+
*/
|
|
23785
|
+
static convertToUbo(rule) {
|
|
23786
|
+
// TODO: add support for host rules
|
|
23787
|
+
if (rule.type !== NetworkRuleType.NetworkRule) {
|
|
23788
|
+
throw new Error(`Invalid rule type: ${rule.type}`);
|
|
23789
|
+
}
|
|
23790
|
+
if (rule.modifiers) {
|
|
23791
|
+
const modifiers = NetworkRuleModifierListConverter.convertToUbo(rule.modifiers, rule.exception);
|
|
23792
|
+
// If the object reference is different, it means that the modifiers were converted
|
|
23793
|
+
// In this case, we should clone the entire rule and replace the modifiers with the converted ones
|
|
23794
|
+
if (modifiers.isConverted) {
|
|
23795
|
+
return {
|
|
23796
|
+
result: [{
|
|
23797
|
+
category: RuleCategory.Network,
|
|
23798
|
+
type: NetworkRuleType.NetworkRule,
|
|
23799
|
+
syntax: rule.syntax,
|
|
23800
|
+
exception: rule.exception,
|
|
23801
|
+
pattern: {
|
|
23802
|
+
type: 'Value',
|
|
23803
|
+
value: rule.pattern.value
|
|
23804
|
+
},
|
|
23805
|
+
modifiers: modifiers.result
|
|
23806
|
+
}],
|
|
23807
|
+
isConverted: true
|
|
23808
|
+
};
|
|
23809
|
+
}
|
|
23810
|
+
}
|
|
23811
|
+
// If the modifiers were not converted, return the original rule
|
|
23812
|
+
return createNodeConversionResult([rule], false);
|
|
23813
|
+
}
|
|
23170
23814
|
}
|
|
23171
23815
|
|
|
23172
23816
|
/**
|
|
@@ -23214,6 +23858,25 @@ class RuleConverter extends RuleConverterBase {
|
|
|
23214
23858
|
throw new RuleConversionError('Unknown rule category');
|
|
23215
23859
|
}
|
|
23216
23860
|
}
|
|
23861
|
+
/**
|
|
23862
|
+
* Converts an adblock filtering rule to uBlock Origin format, if possible.
|
|
23863
|
+
*
|
|
23864
|
+
* @param rule Rule node to convert
|
|
23865
|
+
* @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains
|
|
23866
|
+
* the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted.
|
|
23867
|
+
* If the rule was not converted, the result array will contain the original node with the same object reference
|
|
23868
|
+
* @throws If the rule is invalid or cannot be converted
|
|
23869
|
+
*/
|
|
23870
|
+
// TODO: Add support for other rule types
|
|
23871
|
+
static convertToUbo(rule) {
|
|
23872
|
+
if (rule.category === RuleCategory.Cosmetic) {
|
|
23873
|
+
return CosmeticRuleConverter.convertToUbo(rule);
|
|
23874
|
+
}
|
|
23875
|
+
if (rule.category === RuleCategory.Network) {
|
|
23876
|
+
return NetworkRuleConverter.convertToUbo(rule);
|
|
23877
|
+
}
|
|
23878
|
+
return createConversionResult([rule], false);
|
|
23879
|
+
}
|
|
23217
23880
|
}
|
|
23218
23881
|
|
|
23219
23882
|
/**
|
|
@@ -23607,7 +24270,7 @@ class ByteBuffer {
|
|
|
23607
24270
|
* @see {@link https://stackoverflow.com/a/62797156}
|
|
23608
24271
|
*/
|
|
23609
24272
|
const isChromium = () => {
|
|
23610
|
-
return typeof window !== 'undefined' && (Object.prototype.hasOwnProperty.call(window, 'chrome') || typeof window.navigator !== 'undefined' && /chrome/i.test(window.navigator.userAgent
|
|
24273
|
+
return typeof window !== 'undefined' && (Object.prototype.hasOwnProperty.call(window, 'chrome') || typeof window.navigator !== 'undefined' && /chrome/i.test(window.navigator.userAgent));
|
|
23611
24274
|
};
|
|
23612
24275
|
|
|
23613
24276
|
/* eslint-disable no-param-reassign */
|
|
@@ -23698,16 +24361,23 @@ class OutputByteBuffer extends ByteBuffer {
|
|
|
23698
24361
|
*/
|
|
23699
24362
|
offset;
|
|
23700
24363
|
/**
|
|
23701
|
-
* Size of the shared buffer for encoding strings.
|
|
24364
|
+
* Size of the shared buffer for encoding strings in bytes.
|
|
24365
|
+
* This is a divisor of ByteBuffer.CHUNK_SIZE and experience shows that this value works optimally.
|
|
24366
|
+
* This is sufficient for most strings that occur in filter lists (we checked average string length in popular
|
|
24367
|
+
* filter lists).
|
|
23702
24368
|
*/
|
|
23703
24369
|
static ENCODER_BUFFER_SIZE = 8192;
|
|
23704
24370
|
/**
|
|
23705
|
-
*
|
|
24371
|
+
* Length threshold for using a shared buffer for encoding strings.
|
|
24372
|
+
* This temp buffer is needed because we write the short strings in it
|
|
24373
|
+
* (so there is no need to constantly allocate a new buffer).
|
|
24374
|
+
* The reason for dividing ENCODER_BUFFER_SIZE by 4 is to ensure that the encoded string fits in the buffer,
|
|
24375
|
+
* if we also take into account the worst possible case (each character is encoded with 4 bytes).
|
|
23706
24376
|
*/
|
|
23707
24377
|
static SHORT_STRING_THRESHOLD = 2048; // 8192 / 4
|
|
23708
24378
|
/**
|
|
23709
24379
|
* Represents the maximum value that can be written as a 'storage optimized' unsigned integer.
|
|
23710
|
-
* 0x1FFFFFFF means 32 bits minus 3 bits
|
|
24380
|
+
* 0x1FFFFFFF means 29 bits — 32 bits minus 3 bits — because the last bit in each byte is a flag indicating
|
|
23711
24381
|
* if there are more bytes (except for the last byte).
|
|
23712
24382
|
*/
|
|
23713
24383
|
static MAX_OPTIMIZED_UINT = 0x1FFFFFFF;
|
|
@@ -24254,7 +24924,7 @@ class RuleCategorizer {
|
|
|
24254
24924
|
}
|
|
24255
24925
|
}
|
|
24256
24926
|
}
|
|
24257
|
-
const version = "2.0.
|
|
24927
|
+
const version = "2.0.1";
|
|
24258
24928
|
|
|
24259
24929
|
/**
|
|
24260
24930
|
* @file AGTree version
|
|
@@ -24264,4 +24934,4 @@ const version = "2.0.0-alpha.0";
|
|
|
24264
24934
|
// `tsc` in the root directory, it will generate `dist/types/src/version.d.ts`
|
|
24265
24935
|
// with wrong relative path to `package.json`. So we need this little "hack"
|
|
24266
24936
|
const AGTREE_VERSION = version;
|
|
24267
|
-
export { ADBLOCK_URL_SEPARATOR, ADBLOCK_URL_SEPARATOR_REGEX, ADBLOCK_URL_START, ADBLOCK_URL_START_REGEX, ADBLOCK_WILDCARD, ADBLOCK_WILDCARD_REGEX, ADG_SCRIPTLET_MASK, AGLINT_COMMAND_PREFIX, AGTREE_VERSION, AdblockSyntax, AdblockSyntaxError, AgentCommentRuleParser, AgentParser, AppListParser, BINARY_SCHEMA_VERSION, BinarySchemaMismatchError, ByteBuffer, COMMA_DOMAIN_LIST_SEPARATOR, CommentMarker, CommentRuleParser, CommentRuleType, ConfigCommentRuleParser, CosmeticRuleParser, CosmeticRuleSeparator, CosmeticRuleSeparatorUtils, CosmeticRuleType, DomainListParser, DomainUtils, EXT_CSS_LEGACY_ATTRIBUTES, EXT_CSS_PSEUDO_CLASSES, FORBIDDEN_CSS_FUNCTIONS, FilterListConverter, FilterListParser, GenericPlatform, HINT_MARKER, HintCommentRuleParser, HintParser, HostRuleParser, IF, INCLUDE, InputByteBuffer, KNOWN_METADATA_HEADERS, LogicalExpressionParser, LogicalExpressionUtils, MODIFIERS_SEPARATOR, MODIFIER_ASSIGN_OPERATOR, MetadataCommentRuleParser, MethodListParser, ModifierListParser, ModifierParser, NEGATION_MARKER, NETWORK_RULE_EXCEPTION_MARKER, NETWORK_RULE_SEPARATOR, NetworkRuleParser, NetworkRuleType, NotImplementedError, OutputByteBuffer, PIPE_MODIFIER_SEPARATOR, PREPROCESSOR_MARKER, ParameterListParser, PositionProvider, PreProcessorCommentRuleParser, QuoteType, QuoteUtils, RawFilterListConverter, RawRuleConverter, RegExpUtils, RuleCategorizer, RuleCategory, RuleConversionError, RuleConverter, RuleParser, SAFARI_CB_AFFINITY, SPECIAL_REGEX_SYMBOLS, SpecificPlatform, StealthOptionListParser, UBO_SCRIPTLET_MASK, decodeTextPolyfill, defaultParserOptions, encodeIntoPolyfill, getPlatformId, getSpecificPlatformName, isGenericPlatform, modifierValidator, modifiersCompatibilityTable, parseRawPlatforms, redirectsCompatibilityTable, scriptletsCompatibilityTable };
|
|
24937
|
+
export { ADBLOCK_URL_SEPARATOR, ADBLOCK_URL_SEPARATOR_REGEX, ADBLOCK_URL_START, ADBLOCK_URL_START_REGEX, ADBLOCK_WILDCARD, ADBLOCK_WILDCARD_REGEX, ADG_SCRIPTLET_MASK, AGLINT_COMMAND_PREFIX, AGTREE_VERSION, AdblockSyntax, AdblockSyntaxError, AgentCommentRuleParser, AgentParser, AppListParser, BINARY_SCHEMA_VERSION, BinarySchemaMismatchError, ByteBuffer, COMMA_DOMAIN_LIST_SEPARATOR, CommentMarker, CommentRuleParser, CommentRuleType, ConfigCommentRuleParser, CosmeticRuleParser, CosmeticRuleSeparator, CosmeticRuleSeparatorUtils, CosmeticRuleType, DomainListParser, DomainUtils, EXT_CSS_LEGACY_ATTRIBUTES, EXT_CSS_PSEUDO_CLASSES, FORBIDDEN_CSS_FUNCTIONS, FilterListConverter, FilterListParser, GenericPlatform, HINT_MARKER, HintCommentRuleParser, HintParser, HostRuleParser, IF, INCLUDE, InputByteBuffer, KNOWN_METADATA_HEADERS, LogicalExpressionParser, LogicalExpressionUtils, MODIFIERS_SEPARATOR, MODIFIER_ASSIGN_OPERATOR, MetadataCommentRuleParser, MethodListParser, ModifierListParser, ModifierParser, NEGATION_MARKER, NETWORK_RULE_EXCEPTION_MARKER, NETWORK_RULE_SEPARATOR, NetworkRuleParser, NetworkRuleType, NotImplementedError, OutputByteBuffer, PIPE_MODIFIER_SEPARATOR, PREPROCESSOR_MARKER, ParameterListParser, PositionProvider, PreProcessorCommentRuleParser, QuoteType, QuoteUtils, RawFilterListConverter, RawRuleConverter, RegExpUtils, ResourceType, RuleCategorizer, RuleCategory, RuleConversionError, RuleConverter, RuleParser, SAFARI_CB_AFFINITY, SPECIAL_REGEX_SYMBOLS, SpecificPlatform, StealthOptionListParser, UBO_SCRIPTLET_MASK, decodeTextPolyfill, defaultParserOptions, encodeIntoPolyfill, getPlatformId, getResourceTypeModifier, getSpecificPlatformName, isGenericPlatform, isValidResourceType, modifierValidator, modifiersCompatibilityTable, parseRawPlatforms, redirectsCompatibilityTable, scriptletsCompatibilityTable };
|