@browserless.io/browserless 2.20.2 → 2.21.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.
Files changed (136) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/bin/browserless.js +2 -1
  3. package/build/browserless.js +3 -2
  4. package/build/browsers/index.d.ts +3 -2
  5. package/build/browsers/index.js +3 -1
  6. package/build/routes/chrome/http/content.post.body.json +8 -8
  7. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  8. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  9. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  10. package/build/routes/chrome/tests/pdf.spec.js +24 -12
  11. package/build/routes/chromium/http/content.post.body.json +8 -8
  12. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  13. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  14. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  15. package/build/routes/chromium/tests/pdf.spec.js +24 -12
  16. package/build/shared/pdf.http.js +10 -5
  17. package/docker/sdk/Dockerfile +11 -0
  18. package/extensions/ublock/_locales/ar/messages.json +5 -1
  19. package/extensions/ublock/_locales/az/messages.json +4 -0
  20. package/extensions/ublock/_locales/be/messages.json +4 -0
  21. package/extensions/ublock/_locales/bg/messages.json +4 -0
  22. package/extensions/ublock/_locales/bn/messages.json +4 -0
  23. package/extensions/ublock/_locales/br_FR/messages.json +9 -5
  24. package/extensions/ublock/_locales/bs/messages.json +4 -0
  25. package/extensions/ublock/_locales/ca/messages.json +5 -1
  26. package/extensions/ublock/_locales/cs/messages.json +4 -0
  27. package/extensions/ublock/_locales/cv/messages.json +4 -0
  28. package/extensions/ublock/_locales/cy/messages.json +4 -0
  29. package/extensions/ublock/_locales/da/messages.json +4 -0
  30. package/extensions/ublock/_locales/de/messages.json +4 -0
  31. package/extensions/ublock/_locales/el/messages.json +8 -4
  32. package/extensions/ublock/_locales/en/messages.json +4 -0
  33. package/extensions/ublock/_locales/en_GB/messages.json +4 -0
  34. package/extensions/ublock/_locales/eo/messages.json +9 -5
  35. package/extensions/ublock/_locales/es/messages.json +4 -0
  36. package/extensions/ublock/_locales/et/messages.json +4 -0
  37. package/extensions/ublock/_locales/eu/messages.json +4 -0
  38. package/extensions/ublock/_locales/fa/messages.json +4 -0
  39. package/extensions/ublock/_locales/fi/messages.json +6 -2
  40. package/extensions/ublock/_locales/fil/messages.json +4 -0
  41. package/extensions/ublock/_locales/fr/messages.json +4 -0
  42. package/extensions/ublock/_locales/fy/messages.json +4 -0
  43. package/extensions/ublock/_locales/gl/messages.json +12 -8
  44. package/extensions/ublock/_locales/gu/messages.json +4 -0
  45. package/extensions/ublock/_locales/he/messages.json +4 -0
  46. package/extensions/ublock/_locales/hi/messages.json +4 -0
  47. package/extensions/ublock/_locales/hr/messages.json +4 -0
  48. package/extensions/ublock/_locales/hu/messages.json +68 -64
  49. package/extensions/ublock/_locales/hy/messages.json +4 -0
  50. package/extensions/ublock/_locales/id/messages.json +6 -2
  51. package/extensions/ublock/_locales/it/messages.json +4 -0
  52. package/extensions/ublock/_locales/ja/messages.json +4 -0
  53. package/extensions/ublock/_locales/ka/messages.json +4 -0
  54. package/extensions/ublock/_locales/kk/messages.json +4 -0
  55. package/extensions/ublock/_locales/kn/messages.json +4 -0
  56. package/extensions/ublock/_locales/ko/messages.json +4 -0
  57. package/extensions/ublock/_locales/lt/messages.json +4 -0
  58. package/extensions/ublock/_locales/lv/messages.json +4 -0
  59. package/extensions/ublock/_locales/mk/messages.json +4 -0
  60. package/extensions/ublock/_locales/ml/messages.json +4 -0
  61. package/extensions/ublock/_locales/mr/messages.json +4 -0
  62. package/extensions/ublock/_locales/ms/messages.json +4 -0
  63. package/extensions/ublock/_locales/nb/messages.json +4 -0
  64. package/extensions/ublock/_locales/nl/messages.json +4 -0
  65. package/extensions/ublock/_locales/no/messages.json +4 -0
  66. package/extensions/ublock/_locales/oc/messages.json +4 -0
  67. package/extensions/ublock/_locales/pa/messages.json +8 -4
  68. package/extensions/ublock/_locales/pl/messages.json +4 -0
  69. package/extensions/ublock/_locales/pt_BR/messages.json +4 -0
  70. package/extensions/ublock/_locales/pt_PT/messages.json +4 -0
  71. package/extensions/ublock/_locales/ro/messages.json +4 -0
  72. package/extensions/ublock/_locales/ru/messages.json +4 -0
  73. package/extensions/ublock/_locales/si/messages.json +4 -0
  74. package/extensions/ublock/_locales/sk/messages.json +4 -0
  75. package/extensions/ublock/_locales/sl/messages.json +4 -0
  76. package/extensions/ublock/_locales/so/messages.json +4 -0
  77. package/extensions/ublock/_locales/sq/messages.json +4 -0
  78. package/extensions/ublock/_locales/sr/messages.json +6 -2
  79. package/extensions/ublock/_locales/sv/messages.json +4 -0
  80. package/extensions/ublock/_locales/sw/messages.json +4 -0
  81. package/extensions/ublock/_locales/ta/messages.json +4 -0
  82. package/extensions/ublock/_locales/te/messages.json +4 -0
  83. package/extensions/ublock/_locales/th/messages.json +4 -0
  84. package/extensions/ublock/_locales/tr/messages.json +4 -0
  85. package/extensions/ublock/_locales/uk/messages.json +4 -0
  86. package/extensions/ublock/_locales/ur/messages.json +4 -0
  87. package/extensions/ublock/_locales/vi/messages.json +4 -0
  88. package/extensions/ublock/_locales/zh_CN/messages.json +4 -0
  89. package/extensions/ublock/_locales/zh_TW/messages.json +5 -1
  90. package/extensions/ublock/assets/assets.json +8 -6
  91. package/extensions/ublock/assets/resources/scriptlets.js +455 -301
  92. package/extensions/ublock/assets/thirdparties/easylist/easylist.txt +4441 -6643
  93. package/extensions/ublock/assets/thirdparties/easylist/easyprivacy.txt +401 -307
  94. package/extensions/ublock/assets/thirdparties/pgl.yoyo.org/as/serverlist +34 -29
  95. package/extensions/ublock/assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat +682 -709
  96. package/extensions/ublock/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt +2439 -1999
  97. package/extensions/ublock/assets/ublock/badlists.txt +7 -0
  98. package/extensions/ublock/assets/ublock/badware.min.txt +992 -552
  99. package/extensions/ublock/assets/ublock/filters.min.txt +1253 -864
  100. package/extensions/ublock/assets/ublock/privacy.min.txt +58 -31
  101. package/extensions/ublock/assets/ublock/quick-fixes.min.txt +118 -126
  102. package/extensions/ublock/assets/ublock/unbreak.min.txt +129 -109
  103. package/extensions/ublock/js/background.js +4 -3
  104. package/extensions/ublock/js/benchmarks.js +29 -29
  105. package/extensions/ublock/js/codemirror/ubo-static-filtering.js +1 -0
  106. package/extensions/ublock/js/contextmenu.js +20 -21
  107. package/extensions/ublock/js/devtools.js +137 -3
  108. package/extensions/ublock/js/filtering-context.js +98 -43
  109. package/extensions/ublock/js/logger-ui.js +1 -1
  110. package/extensions/ublock/js/messaging.js +57 -135
  111. package/extensions/ublock/js/pagestore.js +44 -31
  112. package/extensions/ublock/js/redirect-resources.js +14 -4
  113. package/extensions/ublock/js/scriptlet-filtering.js +4 -1
  114. package/extensions/ublock/js/static-filtering-parser.js +107 -37
  115. package/extensions/ublock/js/static-net-filtering.js +514 -250
  116. package/extensions/ublock/js/storage.js +2 -1
  117. package/extensions/ublock/js/traffic.js +8 -4
  118. package/extensions/ublock/js/vapi-background.js +1 -0
  119. package/extensions/ublock/js/vapi-common.js +1 -0
  120. package/extensions/ublock/logger-ui.html +2 -3
  121. package/extensions/ublock/manifest.json +2 -2
  122. package/extensions/ublock/support.html +1 -0
  123. package/extensions/ublock/web_accessible_resources/noop-vast2.xml +1 -0
  124. package/extensions/ublock/web_accessible_resources/noop-vast3.xml +1 -0
  125. package/extensions/ublock/web_accessible_resources/noop-vast4.xml +1 -0
  126. package/package.json +17 -17
  127. package/src/browserless.ts +3 -2
  128. package/src/browsers/index.ts +2 -0
  129. package/src/routes/chrome/tests/pdf.spec.ts +24 -12
  130. package/src/routes/chromium/tests/pdf.spec.ts +24 -12
  131. package/src/shared/pdf.http.ts +10 -6
  132. package/static/docs/swagger.json +10 -10
  133. package/static/docs/swagger.min.json +9 -9
  134. package/static/function/client.js +9 -9
  135. package/static/function/index.html +9 -9
  136. /package/extensions/ublock/web_accessible_resources/{noop-vmap1.0.xml → noop-vmap1.xml} +0 -0
@@ -19,28 +19,15 @@
19
19
  Home: https://github.com/gorhill/uBlock
20
20
  */
21
21
 
22
- /* globals vAPI */
23
-
24
- 'use strict';
22
+ import * as sfp from './static-filtering-parser.js';
25
23
 
26
- /******************************************************************************/
24
+ import { domainFromHostname, hostnameFromNetworkURL } from './uri-utils.js';
25
+ import { dropTask, queueTask } from './tasks.js';
27
26
 
28
- import { queueTask, dropTask } from './tasks.js';
29
27
  import BidiTrieContainer from './biditrie.js';
30
- import HNTrieContainer from './hntrie.js';
31
28
  import { CompiledListReader } from './static-filtering-io.js';
32
- import * as sfp from './static-filtering-parser.js';
33
-
34
- import {
35
- domainFromHostname,
36
- hostnameFromNetworkURL,
37
- } from './uri-utils.js';
38
-
39
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility
40
- //
41
- // This import would be best done dynamically, but since dynamic imports are
42
- // not supported by older browsers, for now a static import is necessary.
43
29
  import { FilteringContext } from './filtering-context.js';
30
+ import HNTrieContainer from './hntrie.js';
44
31
 
45
32
  /******************************************************************************/
46
33
 
@@ -56,97 +43,99 @@ const keyvalStore = typeof vAPI !== 'undefined'
56
43
 
57
44
  /******************************************************************************/
58
45
 
59
- // 0fedcba9876543210
60
- // ||||||| | || |
61
- // ||||||| | || |
62
- // ||||||| | || |
63
- // ||||||| | || |
64
- // ||||||| | || +---- bit 0- 1: block=0, allow=1, block important=2
65
- // ||||||| | |+------ bit 2: unused
66
- // ||||||| | +------- bit 3- 4: party [0-3]
67
- // ||||||| +--------- bit 5- 9: type [0-31]
68
- // ||||||+-------------- bit 10: headers-based filters
69
- // |||||+--------------- bit 11: redirect filters
70
- // ||||+---------------- bit 12: removeparam filters
71
- // |||+----------------- bit 13: csp filters
72
- // ||+------------------ bit 14: permissions filters
73
- // |+------------------- bit 15: uritransform filters
74
- // +-------------------- bit 16: replace filters
75
- // TODO: bit 11-16 can be converted into 3-bit value, as these options are not
46
+ // 10fedcba9876543210
47
+ // |||||||| | || |
48
+ // |||||||| | || |
49
+ // |||||||| | || |
50
+ // |||||||| | || |
51
+ // |||||||| | || +---- bit 0- 1: block=0, allow=1, block important=2
52
+ // |||||||| | |+------ bit 2: unused
53
+ // |||||||| | +------- bit 3- 4: party [0-3]
54
+ // |||||||| +--------- bit 5- 9: type [0-31]
55
+ // |||||||+-------------- bit 10: headers-based filters
56
+ // ||||||+--------------- bit 11: redirect filters
57
+ // |||||+---------------- bit 12: removeparam filters
58
+ // ||||+----------------- bit 13: csp filters
59
+ // |||+------------------ bit 14: permissions filters
60
+ // ||+------------------- bit 15: uritransform filters
61
+ // |+-------------------- bit 16: replace filters
62
+ // +--------------------- bit 17: urlskip filters
63
+ // TODO: bit 11-17 could be converted into 3-bit value, as these options are not
76
64
  // meant to be combined.
77
65
 
78
- const RealmBitsMask = 0b00000000111;
79
- const ActionBitsMask = 0b00000000011;
80
- const TypeBitsMask = 0b01111100000;
81
- const TypeBitsOffset = 5;
82
-
83
- const BLOCK_REALM = 0b00000000000000000;
84
- const ALLOW_REALM = 0b00000000000000001;
85
- const IMPORTANT_REALM = 0b00000000000000010;
66
+ const BLOCK_REALM = 0b0000_0000_0000_0000_0000;
67
+ const ALLOW_REALM = 0b0000_0000_0000_0000_0001;
68
+ const IMPORTANT_REALM = 0b0000_0000_0000_0000_0010;
69
+ const BLOCKALLOW_REALM = BLOCK_REALM | ALLOW_REALM | IMPORTANT_REALM;
86
70
  const BLOCKIMPORTANT_REALM = BLOCK_REALM | IMPORTANT_REALM;
87
- const ANYPARTY_REALM = 0b00000000000000000;
88
- const FIRSTPARTY_REALM = 0b00000000000001000;
89
- const THIRDPARTY_REALM = 0b00000000000010000;
71
+ const ANYPARTY_REALM = 0b0000_0000_0000_0000_0000;
72
+ const FIRSTPARTY_REALM = 0b0000_0000_0000_0000_1000;
73
+ const THIRDPARTY_REALM = 0b0000_0000_0000_0001_0000;
90
74
  const ALLPARTIES_REALM = FIRSTPARTY_REALM | THIRDPARTY_REALM;
91
- const HEADERS_REALM = 0b00000010000000000;
92
- const REDIRECT_REALM = 0b00000100000000000;
93
- const REMOVEPARAM_REALM = 0b00001000000000000;
94
- const CSP_REALM = 0b00010000000000000;
95
- const PERMISSIONS_REALM = 0b00100000000000000;
96
- const URLTRANSFORM_REALM = 0b01000000000000000;
97
- const REPLACE_REALM = 0b10000000000000000;
75
+ const TYPE_REALM = 0b0000_0000_0011_1110_0000;
76
+ const HEADERS_REALM = 0b0000_0000_0100_0000_0000;
77
+ const REDIRECT_REALM = 0b0000_0000_1000_0000_0000;
78
+ const REMOVEPARAM_REALM = 0b0000_0001_0000_0000_0000;
79
+ const CSP_REALM = 0b0000_0010_0000_0000_0000;
80
+ const PERMISSIONS_REALM = 0b0000_0100_0000_0000_0000;
81
+ const URLTRANSFORM_REALM = 0b0000_1000_0000_0000_0000;
82
+ const REPLACE_REALM = 0b0001_0000_0000_0000_0000;
83
+ const URLSKIP_REALM = 0b0010_0000_0000_0000_0000;
98
84
  const MODIFY_REALMS = REDIRECT_REALM | CSP_REALM |
99
85
  REMOVEPARAM_REALM | PERMISSIONS_REALM |
100
- URLTRANSFORM_REALM | REPLACE_REALM;
86
+ URLTRANSFORM_REALM | REPLACE_REALM |
87
+ URLSKIP_REALM;
88
+
89
+ const TYPE_REALM_OFFSET = 5;
101
90
 
102
91
  const typeNameToTypeValue = {
103
- 'no_type': 0 << TypeBitsOffset,
104
- 'stylesheet': 1 << TypeBitsOffset,
105
- 'image': 2 << TypeBitsOffset,
106
- 'object': 3 << TypeBitsOffset,
107
- 'object_subrequest': 3 << TypeBitsOffset,
108
- 'script': 4 << TypeBitsOffset,
109
- 'fetch': 5 << TypeBitsOffset,
110
- 'xmlhttprequest': 5 << TypeBitsOffset,
111
- 'sub_frame': 6 << TypeBitsOffset,
112
- 'font': 7 << TypeBitsOffset,
113
- 'media': 8 << TypeBitsOffset,
114
- 'websocket': 9 << TypeBitsOffset,
115
- 'beacon': 10 << TypeBitsOffset,
116
- 'ping': 10 << TypeBitsOffset,
117
- 'other': 11 << TypeBitsOffset,
118
- 'popup': 12 << TypeBitsOffset, // start of behavioral filtering
119
- 'popunder': 13 << TypeBitsOffset,
120
- 'main_frame': 14 << TypeBitsOffset, // start of 1p behavioral filtering
121
- 'generichide': 15 << TypeBitsOffset,
122
- 'specifichide': 16 << TypeBitsOffset,
123
- 'inline-font': 17 << TypeBitsOffset,
124
- 'inline-script': 18 << TypeBitsOffset,
125
- 'cname': 19 << TypeBitsOffset,
126
- 'webrtc': 20 << TypeBitsOffset,
127
- 'unsupported': 21 << TypeBitsOffset,
92
+ 'no_type': 0 << TYPE_REALM_OFFSET,
93
+ 'stylesheet': 1 << TYPE_REALM_OFFSET,
94
+ 'image': 2 << TYPE_REALM_OFFSET,
95
+ 'object': 3 << TYPE_REALM_OFFSET,
96
+ 'object_subrequest': 3 << TYPE_REALM_OFFSET,
97
+ 'script': 4 << TYPE_REALM_OFFSET,
98
+ 'fetch': 5 << TYPE_REALM_OFFSET,
99
+ 'xmlhttprequest': 5 << TYPE_REALM_OFFSET,
100
+ 'sub_frame': 6 << TYPE_REALM_OFFSET,
101
+ 'font': 7 << TYPE_REALM_OFFSET,
102
+ 'media': 8 << TYPE_REALM_OFFSET,
103
+ 'websocket': 9 << TYPE_REALM_OFFSET,
104
+ 'beacon': 10 << TYPE_REALM_OFFSET,
105
+ 'ping': 10 << TYPE_REALM_OFFSET,
106
+ 'other': 11 << TYPE_REALM_OFFSET,
107
+ 'popup': 12 << TYPE_REALM_OFFSET, // start of behavioral filtering
108
+ 'popunder': 13 << TYPE_REALM_OFFSET,
109
+ 'main_frame': 14 << TYPE_REALM_OFFSET, // start of 1p behavioral filtering
110
+ 'generichide': 15 << TYPE_REALM_OFFSET,
111
+ 'specifichide': 16 << TYPE_REALM_OFFSET,
112
+ 'inline-font': 17 << TYPE_REALM_OFFSET,
113
+ 'inline-script': 18 << TYPE_REALM_OFFSET,
114
+ 'cname': 19 << TYPE_REALM_OFFSET,
115
+ 'webrtc': 20 << TYPE_REALM_OFFSET,
116
+ 'unsupported': 21 << TYPE_REALM_OFFSET,
128
117
  };
129
118
 
130
119
  const otherTypeBitValue = typeNameToTypeValue.other;
131
120
 
132
121
  const bitFromType = type =>
133
- 1 << ((typeNameToTypeValue[type] >>> TypeBitsOffset) - 1);
122
+ 1 << ((typeNameToTypeValue[type] >>> TYPE_REALM_OFFSET) - 1);
134
123
 
135
124
  // All network request types to bitmap
136
- // bring origin to 0 (from TypeBitsOffset -- see typeNameToTypeValue)
125
+ // bring origin to 0 (from TYPE_REALM_OFFSET -- see typeNameToTypeValue)
137
126
  // left-shift 1 by the above-calculated value
138
127
  // subtract 1 to set all type bits
139
128
  const allNetworkTypesBits =
140
- (1 << (otherTypeBitValue >>> TypeBitsOffset)) - 1;
129
+ (1 << (otherTypeBitValue >>> TYPE_REALM_OFFSET)) - 1;
141
130
 
142
131
  const allTypesBits =
143
132
  allNetworkTypesBits |
144
- 1 << (typeNameToTypeValue['popup'] >>> TypeBitsOffset) - 1 |
145
- 1 << (typeNameToTypeValue['main_frame'] >>> TypeBitsOffset) - 1 |
146
- 1 << (typeNameToTypeValue['inline-font'] >>> TypeBitsOffset) - 1 |
147
- 1 << (typeNameToTypeValue['inline-script'] >>> TypeBitsOffset) - 1;
133
+ 1 << (typeNameToTypeValue['popup'] >>> TYPE_REALM_OFFSET) - 1 |
134
+ 1 << (typeNameToTypeValue['main_frame'] >>> TYPE_REALM_OFFSET) - 1 |
135
+ 1 << (typeNameToTypeValue['inline-font'] >>> TYPE_REALM_OFFSET) - 1 |
136
+ 1 << (typeNameToTypeValue['inline-script'] >>> TYPE_REALM_OFFSET) - 1;
148
137
  const unsupportedTypeBit =
149
- 1 << (typeNameToTypeValue['unsupported'] >>> TypeBitsOffset) - 1;
138
+ 1 << (typeNameToTypeValue['unsupported'] >>> TYPE_REALM_OFFSET) - 1;
150
139
 
151
140
  const typeValueToTypeName = [
152
141
  '',
@@ -199,6 +188,7 @@ const MODIFIER_TYPE_CSP = 4;
199
188
  const MODIFIER_TYPE_PERMISSIONS = 5;
200
189
  const MODIFIER_TYPE_URLTRANSFORM = 6;
201
190
  const MODIFIER_TYPE_REPLACE = 7;
191
+ const MODIFIER_TYPE_URLSKIP = 8;
202
192
 
203
193
  const modifierBitsFromType = new Map([
204
194
  [ MODIFIER_TYPE_REDIRECT, REDIRECT_REALM ],
@@ -208,6 +198,7 @@ const modifierBitsFromType = new Map([
208
198
  [ MODIFIER_TYPE_PERMISSIONS, PERMISSIONS_REALM ],
209
199
  [ MODIFIER_TYPE_URLTRANSFORM, URLTRANSFORM_REALM ],
210
200
  [ MODIFIER_TYPE_REPLACE, REPLACE_REALM ],
201
+ [ MODIFIER_TYPE_URLSKIP, URLSKIP_REALM ],
211
202
  ]);
212
203
 
213
204
  const modifierTypeFromName = new Map([
@@ -218,6 +209,7 @@ const modifierTypeFromName = new Map([
218
209
  [ 'permissions', MODIFIER_TYPE_PERMISSIONS ],
219
210
  [ 'uritransform', MODIFIER_TYPE_URLTRANSFORM ],
220
211
  [ 'replace', MODIFIER_TYPE_REPLACE ],
212
+ [ 'urlskip', MODIFIER_TYPE_URLSKIP ],
221
213
  ]);
222
214
 
223
215
  const modifierNameFromType = new Map([
@@ -228,22 +220,23 @@ const modifierNameFromType = new Map([
228
220
  [ MODIFIER_TYPE_PERMISSIONS, 'permissions' ],
229
221
  [ MODIFIER_TYPE_URLTRANSFORM, 'uritransform' ],
230
222
  [ MODIFIER_TYPE_REPLACE, 'replace' ],
223
+ [ MODIFIER_TYPE_URLSKIP, 'urlskip' ],
231
224
  ]);
232
225
 
233
- //const typeValueFromCatBits = catBits => (catBits >>> TypeBitsOffset) & 0b11111;
226
+ //const typeValueFromCatBits = catBits => (catBits >>> TYPE_REALM_OFFSET) & 0b11111;
234
227
 
235
228
  const MAX_TOKEN_LENGTH = 7;
236
229
 
237
230
  // Four upper bits of token hash are reserved for built-in predefined
238
231
  // token hashes, which should never end up being used when tokenizing
239
232
  // any arbitrary string.
240
- const NO_TOKEN_HASH = 0x50000000;
241
- const DOT_TOKEN_HASH = 0x10000000;
242
- const ANY_TOKEN_HASH = 0x20000000;
243
- const ANY_HTTPS_TOKEN_HASH = 0x30000000;
244
- const ANY_HTTP_TOKEN_HASH = 0x40000000;
245
- const EMPTY_TOKEN_HASH = 0xF0000000;
246
- const INVALID_TOKEN_HASH = 0xFFFFFFFF;
233
+ const NO_TOKEN_HASH = 0x5000_0000;
234
+ const DOT_TOKEN_HASH = 0x1000_0000;
235
+ const ANY_TOKEN_HASH = 0x2000_0000;
236
+ const ANY_HTTPS_TOKEN_HASH = 0x3000_0000;
237
+ const ANY_HTTP_TOKEN_HASH = 0x4000_0000;
238
+ const EMPTY_TOKEN_HASH = 0xF000_0000;
239
+ const INVALID_TOKEN_HASH = 0xFFFF_FFFF;
247
240
 
248
241
  /******************************************************************************/
249
242
 
@@ -255,6 +248,7 @@ let $requestTypeValue = 0;
255
248
  let $requestURL = '';
256
249
  let $requestURLRaw = '';
257
250
  let $requestHostname = '';
251
+ let $requestAddress = '';
258
252
  let $docHostname = '';
259
253
  let $docDomain = '';
260
254
  let $tokenBeg = 0;
@@ -386,9 +380,9 @@ class LogData {
386
380
  } else if ( (categoryBits & FIRSTPARTY_REALM) !== 0 ) {
387
381
  logData.options.unshift('1p');
388
382
  }
389
- const type = categoryBits & TypeBitsMask;
383
+ const type = categoryBits & TYPE_REALM;
390
384
  if ( type !== 0 ) {
391
- logData.options.unshift(typeValueToTypeName[type >>> TypeBitsOffset]);
385
+ logData.options.unshift(typeValueToTypeName[type >>> TYPE_REALM_OFFSET]);
392
386
  }
393
387
  let raw = logData.pattern.join('');
394
388
  if (
@@ -422,6 +416,14 @@ class LogData {
422
416
  isPureHostname() {
423
417
  return this.tokenHash === DOT_TOKEN_HASH;
424
418
  }
419
+
420
+ static requote(s) {
421
+ if ( /^(["'`]).+\1$|,/.test(s) === false ) { return s; }
422
+ if ( s.includes("'") === false ) { return `'${s}'`; }
423
+ if ( s.includes('"') === false ) { return `"${s}"`; }
424
+ if ( s.includes('`') === false ) { return `\`${s}\``; }
425
+ return `'${s.replace(/'/g, "\\'")}'`;
426
+ }
425
427
  }
426
428
 
427
429
  /******************************************************************************/
@@ -707,6 +709,8 @@ const dnrAddRuleWarning = (rule, msg) => {
707
709
  FilterNotType
708
710
  FilterStrictParty
709
711
  FilterModifier
712
+ FilterOnHeaders
713
+ FilterIPAddress
710
714
 
711
715
  Collection:
712
716
  FilterCollection
@@ -765,7 +769,7 @@ class FilterImportant {
765
769
  }
766
770
 
767
771
  static dnrFromCompiled(args, rule) {
768
- rule.priority = (rule.priority || 1) + 10;
772
+ rule.priority = (rule.priority || 0) + 30;
769
773
  }
770
774
 
771
775
  static keyFromArgs() {
@@ -1239,7 +1243,7 @@ class FilterRegex {
1239
1243
  return [
1240
1244
  FilterRegex.fid,
1241
1245
  details.pattern,
1242
- details.patternMatchCase ? 1 : 0
1246
+ details.optionValues.has('match-case') ? 1 : 0,
1243
1247
  ];
1244
1248
  }
1245
1249
 
@@ -2080,7 +2084,7 @@ const compileToDomainOpt = (...args) => {
2080
2084
 
2081
2085
  class FilterDenyAllow extends FilterToDomainMissSet {
2082
2086
  static compile(details) {
2083
- return super.compile(details.denyallowOpt, 0b01);
2087
+ return super.compile(details.optionValues.get('denyallow'), 0b01);
2084
2088
  }
2085
2089
 
2086
2090
  static logData(idata, details) {
@@ -2141,7 +2145,7 @@ class FilterModifier {
2141
2145
  let opt = modifierNameFromType.get(filterData[idata+2]);
2142
2146
  const refs = filterRefs[filterData[idata+3]];
2143
2147
  if ( refs.value !== '' ) {
2144
- opt += `=${refs.value}`;
2148
+ opt += `=${LogData.requote(refs.value)}`;
2145
2149
  }
2146
2150
  details.options.push(opt);
2147
2151
  }
@@ -2165,7 +2169,7 @@ class FilterModifierResult {
2165
2169
  this.refs = filterRefs[filterData[imodifierunit+3]];
2166
2170
  this.ireportedunit = env.iunit;
2167
2171
  this.th = env.th;
2168
- this.bits = (env.bits & ~RealmBitsMask) | filterData[imodifierunit+1];
2172
+ this.bits = (env.bits & ~BLOCKALLOW_REALM) | filterData[imodifierunit+1];
2169
2173
  }
2170
2174
 
2171
2175
  get result() {
@@ -2907,7 +2911,7 @@ class FilterStrictParty {
2907
2911
 
2908
2912
  static dnrFromCompiled(args, rule) {
2909
2913
  const partyness = args[1] === 0 ? 1 : 3;
2910
- dnrAddRuleError(rule, `FilterStrictParty: Strict partyness strict${partyness}p not supported`);
2914
+ dnrAddRuleError(rule, `strict${partyness}p not supported`);
2911
2915
  }
2912
2916
 
2913
2917
  static keyFromArgs(args) {
@@ -2942,12 +2946,12 @@ class FilterOnHeaders {
2942
2946
  }
2943
2947
 
2944
2948
  static compile(details) {
2945
- return [ FilterOnHeaders.fid, details.headerOpt ];
2949
+ return [ FilterOnHeaders.fid, details.optionValues.get('header') ];
2946
2950
  }
2947
2951
 
2948
2952
  static fromCompiled(args) {
2949
2953
  return filterDataAlloc(
2950
- args[0], // fid
2954
+ args[0], // fid
2951
2955
  filterRefAdd({
2952
2956
  headerOpt: args[1],
2953
2957
  $parsed: null,
@@ -2955,12 +2959,16 @@ class FilterOnHeaders {
2955
2959
  );
2956
2960
  }
2957
2961
 
2962
+ static dnrFromCompiled(args, rule) {
2963
+ dnrAddRuleError(rule, `header="${args[1]}" not supported`);
2964
+ }
2965
+
2958
2966
  static logData(idata, details) {
2959
2967
  const irefs = filterData[idata+1];
2960
2968
  const headerOpt = filterRefs[irefs].headerOpt;
2961
2969
  let opt = 'header';
2962
2970
  if ( headerOpt !== '' ) {
2963
- opt += `=${headerOpt}`;
2971
+ opt += `=${LogData.requote(headerOpt)}`;
2964
2972
  }
2965
2973
  details.options.push(opt);
2966
2974
  }
@@ -2968,6 +2976,113 @@ class FilterOnHeaders {
2968
2976
 
2969
2977
  registerFilterClass(FilterOnHeaders);
2970
2978
 
2979
+ /******************************************************************************/
2980
+
2981
+ class FilterIPAddress {
2982
+ static TYPE_UNKNOWN = 0;
2983
+ static TYPE_EQUAL = 1;
2984
+ static TYPE_STARTSWITH = 2;
2985
+ static TYPE_LAN = 3;
2986
+ static TYPE_LOOPBACK = 4;
2987
+ static TYPE_RE = 5;
2988
+ static reIPv6IPv4lan = /^::ffff:(7f\w{2}|a\w{2}|a9fe|c0a8):\w+$/;
2989
+ static reIPv6local = /^f[cd]\w{2}:/;
2990
+
2991
+ static match(idata) {
2992
+ const ipaddr = $requestAddress;
2993
+ if ( ipaddr === '' ) { return false; }
2994
+ const details = filterRefs[filterData[idata+1]];
2995
+ switch ( details.$type || this.TYPE_UNKNOWN ) {
2996
+ case this.TYPE_LAN:
2997
+ return this.isLAN(ipaddr);
2998
+ case this.TYPE_LOOPBACK:
2999
+ return this.isLoopback(ipaddr);
3000
+ case this.TYPE_EQUAL:
3001
+ case this.TYPE_STARTSWITH:
3002
+ case this.TYPE_RE:
3003
+ return details.$pattern.test(ipaddr);
3004
+ default:
3005
+ break;
3006
+ }
3007
+ const { pattern } = details;
3008
+ if ( pattern === 'lan' ) {
3009
+ details.$type = this.TYPE_LAN;
3010
+ } else if ( pattern === 'loopback' ) {
3011
+ details.$type = this.TYPE_LOOPBACK;
3012
+ } else if ( pattern.startsWith('/') && pattern.endsWith('/') ) {
3013
+ details.$type = this.TYPE_RE;
3014
+ details.$pattern = new RegExp(pattern.slice(1, -1), 'm');
3015
+ } else if ( pattern.endsWith('*') ) {
3016
+ details.$type = this.TYPE_STARTSWITH;
3017
+ details.$pattern = new RegExp(`^${restrFromPlainPattern(pattern.slice(0, -1))}`, 'm');
3018
+ } else {
3019
+ details.$type = this.TYPE_EQUAL;
3020
+ details.$pattern = new RegExp(`^${restrFromPlainPattern(pattern)}$`, 'm');
3021
+ }
3022
+ return this.match(idata);
3023
+ }
3024
+
3025
+ // https://github.com/uBlockOrigin/uAssets/blob/master/filters/lan-block.txt
3026
+ // https://en.wikipedia.org/wiki/Reserved_IP_addresses
3027
+ // `ipaddr` is assumed well-formed
3028
+ static isLAN(ipaddr) {
3029
+ const c0 = ipaddr.charCodeAt(0);
3030
+ // ipv4
3031
+ if ( c0 === 0x30 /* 0 */ ) {
3032
+ return ipaddr.startsWith('0.');
3033
+ }
3034
+ if ( c0 === 0x31 /* 1 */ ) {
3035
+ if ( ipaddr.startsWith('10.') ) { return true; }
3036
+ if ( ipaddr.startsWith('127.') ) { return true; }
3037
+ if ( ipaddr.startsWith('169.254.') ) { return true; }
3038
+ if ( ipaddr.startsWith('172.') ) {
3039
+ const v = parseInt(ipaddr.slice(4), 10);
3040
+ return v >= 16 && v <= 31;
3041
+ }
3042
+ return ipaddr.startsWith('192.168.');
3043
+ }
3044
+ // ipv6
3045
+ if ( c0 === 0x3A /* : */ ) {
3046
+ if ( ipaddr.startsWith('::') === false ) { return false; }
3047
+ if ( ipaddr === '::' || ipaddr === '::1' ) { return true; }
3048
+ if ( ipaddr.startsWith('::ffff:') === false ) { return false; }
3049
+ return this.reIPv6IPv4lan.test(ipaddr);
3050
+ }
3051
+ if ( c0 === 0x36 /* 6 */ ) {
3052
+ return ipaddr.startsWith('64:ff9b:');
3053
+ }
3054
+ if ( c0 === 0x66 /* f */ ) {
3055
+ return this.reIPv6local.test(ipaddr);
3056
+ }
3057
+ return false;
3058
+ }
3059
+
3060
+ static isLoopback(ipaddr) {
3061
+ return ipaddr === '127.0.0.1' || ipaddr === '::1';
3062
+ }
3063
+
3064
+ static compile(details) {
3065
+ return [ FilterIPAddress.fid, details.optionValues.get('ipaddress') ];
3066
+ }
3067
+
3068
+ static fromCompiled(args) {
3069
+ const pattern = args[1];
3070
+ const details = { pattern };
3071
+ return filterDataAlloc(args[0], filterRefAdd(details));
3072
+ }
3073
+
3074
+ static dnrFromCompiled(args, rule) {
3075
+ dnrAddRuleError(rule, `"ipaddress=${args[1]}" not supported`);
3076
+ }
3077
+
3078
+ static logData(idata, details) {
3079
+ const irefs = filterData[idata+1];
3080
+ details.options.push(`ipaddress=${LogData.requote(filterRefs[irefs].pattern)}`);
3081
+ }
3082
+ }
3083
+
3084
+ registerFilterClass(FilterIPAddress);
3085
+
2971
3086
  /******************************************************************************/
2972
3087
  /******************************************************************************/
2973
3088
 
@@ -3151,8 +3266,7 @@ class FilterCompiler {
3151
3266
  return Object.assign(this, other);
3152
3267
  }
3153
3268
  this.reToken = /[%0-9A-Za-z]+/g;
3154
- this.fromDomainOptList = [];
3155
- this.toDomainOptList = [];
3269
+ this.optionValues = new Map();
3156
3270
  this.tokenIdToNormalizedType = new Map([
3157
3271
  [ sfp.NODE_TYPE_NET_OPTION_NAME_CNAME, bitFromType('cname') ],
3158
3272
  [ sfp.NODE_TYPE_NET_OPTION_NAME_CSS, bitFromType('stylesheet') ],
@@ -3184,6 +3298,7 @@ class FilterCompiler {
3184
3298
  [ sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM, MODIFIER_TYPE_REMOVEPARAM ],
3185
3299
  [ sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM, MODIFIER_TYPE_URLTRANSFORM ],
3186
3300
  [ sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE, MODIFIER_TYPE_REPLACE ],
3301
+ [ sfp.NODE_TYPE_NET_OPTION_NAME_URLSKIP, MODIFIER_TYPE_URLSKIP ],
3187
3302
  ]);
3188
3303
  // These top 100 "bad tokens" are collated using the "miss" histogram
3189
3304
  // from tokenHistograms(). The "score" is their occurrence among the
@@ -3309,13 +3424,9 @@ class FilterCompiler {
3309
3424
  this.modifyType = undefined;
3310
3425
  this.modifyValue = undefined;
3311
3426
  this.pattern = '';
3312
- this.patternMatchCase = false;
3313
3427
  this.party = ANYPARTY_REALM;
3314
3428
  this.optionUnitBits = 0;
3315
- this.fromDomainOpt = '';
3316
- this.toDomainOpt = '';
3317
- this.denyallowOpt = '';
3318
- this.headerOpt = undefined;
3429
+ this.optionValues.clear();
3319
3430
  this.isPureHostname = false;
3320
3431
  this.isGeneric = false;
3321
3432
  this.isRegex = false;
@@ -3327,8 +3438,6 @@ class FilterCompiler {
3327
3438
  this.notTypeBits = 0;
3328
3439
  this.methodBits = 0;
3329
3440
  this.notMethodBits = 0;
3330
- this.wildcardPos = -1;
3331
- this.caretPos = -1;
3332
3441
  return this;
3333
3442
  }
3334
3443
 
@@ -3423,63 +3532,74 @@ class FilterCompiler {
3423
3532
 
3424
3533
  processOptionWithValue(parser, id) {
3425
3534
  switch ( id ) {
3426
- case sfp.NODE_TYPE_NET_OPTION_NAME_CSP:
3427
- if ( this.processCspOption(parser.getNetOptionValue(id)) === false ) { return false; }
3428
- break;
3429
- case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW:
3430
- this.denyallowOpt = this.processHostnameList(
3431
- parser.getNetFilterDenyallowOptionIterator(),
3432
- );
3433
- if ( this.denyallowOpt === '' ) { return false; }
3434
- this.optionUnitBits |= DENYALLOW_BIT;
3435
- break;
3436
- case sfp.NODE_TYPE_NET_OPTION_NAME_FROM:
3437
- this.fromDomainOpt = this.processHostnameList(
3438
- parser.getNetFilterFromOptionIterator(),
3439
- this.fromDomainOptList
3440
- );
3441
- if ( this.fromDomainOpt === '' ) { return false; }
3442
- this.optionUnitBits |= FROM_BIT;
3443
- break;
3444
- case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: {
3445
- this.headerOpt = parser.getNetOptionValue(id) || '';
3446
- this.optionUnitBits |= HEADER_BIT;
3447
- break;
3535
+ case sfp.NODE_TYPE_NET_OPTION_NAME_CSP:
3536
+ if ( this.processCspOption(parser.getNetOptionValue(id)) === false ) { return false; }
3537
+ break;
3538
+ case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: {
3539
+ const value = this.processHostnameList(
3540
+ parser.getNetFilterDenyallowOptionIterator()
3541
+ );
3542
+ if ( value === '' ) { return false; }
3543
+ this.optionValues.set('denyallow', value);
3544
+ this.optionUnitBits |= DENYALLOW_BIT;
3545
+ break;
3546
+ }
3547
+ case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: {
3548
+ const iter = parser.getNetFilterFromOptionIterator();
3549
+ const list = [];
3550
+ const value = this.processHostnameList(iter, list);
3551
+ if ( value === '' ) { return false; }
3552
+ this.optionValues.set('from', value);
3553
+ this.optionValues.set('fromList', list);
3554
+ this.optionUnitBits |= FROM_BIT;
3555
+ break;
3556
+ }
3557
+ case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: {
3558
+ this.optionValues.set('header', parser.getNetOptionValue(id) || '');
3559
+ this.optionUnitBits |= HEADER_BIT;
3560
+ break;
3561
+ }
3562
+ case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS:
3563
+ this.optionValues.set('ipaddress', parser.getNetOptionValue(id) || '');
3564
+ this.optionUnitBits |= IPADDRESS_BIT;
3565
+ break;
3566
+ case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD:
3567
+ this.processMethodOption(parser.getNetOptionValue(id));
3568
+ this.optionUnitBits |= METHOD_BIT;
3569
+ break;
3570
+ case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
3571
+ case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE:
3572
+ case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM:
3573
+ case sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE:
3574
+ case sfp.NODE_TYPE_NET_OPTION_NAME_URLSKIP:
3575
+ case sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM:
3576
+ if ( this.processModifierOption(id, parser.getNetOptionValue(id)) === false ) {
3577
+ return false;
3448
3578
  }
3449
- case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD:
3450
- this.processMethodOption(parser.getNetOptionValue(id));
3451
- this.optionUnitBits |= METHOD_BIT;
3452
- break;
3453
- case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
3454
- case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE:
3455
- case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM:
3456
- case sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE:
3457
- case sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM:
3458
- if ( this.processModifierOption(id, parser.getNetOptionValue(id)) === false ) {
3459
- return false;
3460
- }
3461
- this.optionUnitBits |= MODIFY_BIT;
3462
- break;
3463
- case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: {
3464
- const actualId = this.action === ALLOW_REALM
3465
- ? sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE
3466
- : id;
3467
- if ( this.processModifierOption(actualId, parser.getNetOptionValue(id)) === false ) {
3468
- return false;
3469
- }
3470
- this.optionUnitBits |= MODIFY_BIT;
3471
- break;
3579
+ this.optionUnitBits |= MODIFY_BIT;
3580
+ break;
3581
+ case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: {
3582
+ const actualId = this.action === ALLOW_REALM
3583
+ ? sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE
3584
+ : id;
3585
+ if ( this.processModifierOption(actualId, parser.getNetOptionValue(id)) === false ) {
3586
+ return false;
3472
3587
  }
3473
- case sfp.NODE_TYPE_NET_OPTION_NAME_TO:
3474
- this.toDomainOpt = this.processHostnameList(
3475
- parser.getNetFilterToOptionIterator(),
3476
- this.toDomainOptList
3477
- );
3478
- if ( this.toDomainOpt === '' ) { return false; }
3479
- this.optionUnitBits |= TO_BIT;
3480
- break;
3481
- default:
3482
- break;
3588
+ this.optionUnitBits |= MODIFY_BIT;
3589
+ break;
3590
+ }
3591
+ case sfp.NODE_TYPE_NET_OPTION_NAME_TO: {
3592
+ const iter = parser.getNetFilterToOptionIterator();
3593
+ const list = [];
3594
+ const value = this.processHostnameList(iter, list);
3595
+ if ( value === '' ) { return false; }
3596
+ this.optionValues.set('to', value);
3597
+ this.optionValues.set('toList', list);
3598
+ this.optionUnitBits |= TO_BIT;
3599
+ break;
3600
+ }
3601
+ default:
3602
+ break;
3483
3603
  }
3484
3604
  return true;
3485
3605
  }
@@ -3563,6 +3683,7 @@ class FilterCompiler {
3563
3683
  case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW:
3564
3684
  case sfp.NODE_TYPE_NET_OPTION_NAME_FROM:
3565
3685
  case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER:
3686
+ case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS:
3566
3687
  case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD:
3567
3688
  case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
3568
3689
  case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT:
@@ -3570,6 +3691,7 @@ class FilterCompiler {
3570
3691
  case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM:
3571
3692
  case sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE:
3572
3693
  case sfp.NODE_TYPE_NET_OPTION_NAME_TO:
3694
+ case sfp.NODE_TYPE_NET_OPTION_NAME_URLSKIP:
3573
3695
  case sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM:
3574
3696
  if ( this.processOptionWithValue(parser, type) === false ) {
3575
3697
  return this.FILTER_INVALID;
@@ -3596,7 +3718,7 @@ class FilterCompiler {
3596
3718
  this.action = BLOCKIMPORTANT_REALM;
3597
3719
  break;
3598
3720
  case sfp.NODE_TYPE_NET_OPTION_NAME_MATCHCASE:
3599
- this.patternMatchCase = true;
3721
+ this.optionValues.set('match-case', true);
3600
3722
  break;
3601
3723
  case sfp.NODE_TYPE_NET_OPTION_NAME_MP4: {
3602
3724
  const id = this.action === ALLOW_REALM
@@ -3666,11 +3788,6 @@ class FilterCompiler {
3666
3788
  return this.FILTER_OK;
3667
3789
  }
3668
3790
 
3669
- if ( this.isGeneric ) {
3670
- this.wildcardPos = this.pattern.indexOf('*');
3671
- this.caretPos = this.pattern.indexOf('^');
3672
- }
3673
-
3674
3791
  if ( this.pattern.length > 1024 ) {
3675
3792
  return this.FILTER_UNSUPPORTED;
3676
3793
  }
@@ -3798,7 +3915,7 @@ class FilterCompiler {
3798
3915
  isJustOrigin() {
3799
3916
  if ( this.optionUnitBits !== FROM_BIT ) { return false; }
3800
3917
  if ( this.isRegex ) { return false; }
3801
- if ( /[\/~]/.test(this.fromDomainOpt) ) { return false; }
3918
+ if ( /[/~]/.test(this.optionValues.get('from')) ) { return false; }
3802
3919
  if ( this.pattern === '*' ) { return true; }
3803
3920
  if ( this.anchor !== 0b010 ) { return false; }
3804
3921
  if ( /^(?:http[s*]?:(?:\/\/)?)$/.test(this.pattern) ) { return true; }
@@ -3875,7 +3992,7 @@ class FilterCompiler {
3875
3992
  } else /* 'http:' */ {
3876
3993
  this.tokenHash = ANY_HTTP_TOKEN_HASH;
3877
3994
  }
3878
- for ( const hn of this.fromDomainOptList ) {
3995
+ for ( const hn of this.optionValues.get('fromList') ) {
3879
3996
  this.compileToAtomicFilter(hn, writer);
3880
3997
  }
3881
3998
  return;
@@ -3916,30 +4033,35 @@ class FilterCompiler {
3916
4033
  }
3917
4034
 
3918
4035
  // Origin
3919
- if ( this.fromDomainOpt !== '' ) {
4036
+ if ( (this.optionUnitBits & FROM_BIT) !== 0 ) {
3920
4037
  compileFromDomainOpt(
3921
- this.fromDomainOptList,
4038
+ this.optionValues.get('fromList'),
3922
4039
  units.length !== 0 && patternClass.isSlow === true,
3923
4040
  units
3924
4041
  );
3925
4042
  }
3926
4043
 
3927
4044
  // Destination
3928
- if ( this.toDomainOpt !== '' ) {
4045
+ if ( (this.optionUnitBits & TO_BIT) !== 0 ) {
3929
4046
  compileToDomainOpt(
3930
- this.toDomainOptList,
4047
+ this.optionValues.get('toList'),
3931
4048
  units.length !== 0 && patternClass.isSlow === true,
3932
4049
  units
3933
4050
  );
3934
4051
  }
3935
4052
 
3936
4053
  // Deny-allow
3937
- if ( this.denyallowOpt !== '' ) {
4054
+ if ( (this.optionUnitBits & DENYALLOW_BIT) !== 0 ) {
3938
4055
  units.push(FilterDenyAllow.compile(this));
3939
4056
  }
3940
4057
 
4058
+ // IP address
4059
+ if ( (this.optionUnitBits & IPADDRESS_BIT) !== 0 ) {
4060
+ units.push(FilterIPAddress.compile(this));
4061
+ }
4062
+
3941
4063
  // Header
3942
- if ( this.headerOpt !== undefined ) {
4064
+ if ( (this.optionUnitBits & HEADER_BIT) !== 0 ) {
3943
4065
  units.push(FilterOnHeaders.compile(this));
3944
4066
  this.action |= HEADERS_REALM;
3945
4067
  }
@@ -3957,16 +4079,21 @@ class FilterCompiler {
3957
4079
  // IMPORTANT: the modifier unit MUST always appear first in a sequence
3958
4080
  if ( this.modifyType !== undefined ) {
3959
4081
  units.unshift(FilterModifier.compile(this));
3960
- this.action = (this.action & ~ActionBitsMask) |
4082
+ this.action = (this.action & ~BLOCKALLOW_REALM) |
3961
4083
  modifierBitsFromType.get(this.modifyType);
3962
4084
  }
3963
4085
 
3964
- this.compileToAtomicFilter(
3965
- units.length === 1
3966
- ? units[0]
3967
- : FilterCompositeAll.compile(units),
3968
- writer
3969
- );
4086
+ const fdata = units.length === 1
4087
+ ? units[0]
4088
+ : FilterCompositeAll.compile(units);
4089
+
4090
+ this.compileToAtomicFilter(fdata, writer);
4091
+
4092
+ if ( (this.optionUnitBits & IPADDRESS_BIT) !== 0 ) {
4093
+ if ( (this.action & HEADERS_REALM) !== 0 ) { return; }
4094
+ this.action |= HEADERS_REALM;
4095
+ this.compileToAtomicFilter(fdata, writer);
4096
+ }
3970
4097
  }
3971
4098
 
3972
4099
  compilePattern(units) {
@@ -3982,12 +4109,13 @@ class FilterCompiler {
3982
4109
  units.push(FilterPatternGeneric.compile(this));
3983
4110
  return FilterPatternGeneric;
3984
4111
  }
3985
- if ( this.wildcardPos === -1 ) {
3986
- if ( this.caretPos === -1 ) {
4112
+ if ( this.pattern.includes('*') === false ) {
4113
+ const caretPos = this.pattern.indexOf('^');
4114
+ if ( caretPos === -1 ) {
3987
4115
  units.push(FilterPatternPlain.compile(this));
3988
4116
  return FilterPatternPlain;
3989
4117
  }
3990
- if ( this.caretPos === (this.pattern.length - 1) ) {
4118
+ if ( caretPos === (this.pattern.length - 1) ) {
3991
4119
  this.pattern = this.pattern.slice(0, -1);
3992
4120
  units.push(FilterPatternPlain.compile(this));
3993
4121
  units.push(FilterTrailingSeparator.compile());
@@ -4020,7 +4148,7 @@ class FilterCompiler {
4020
4148
  do {
4021
4149
  if ( typeBits & 1 ) {
4022
4150
  writer.push([
4023
- catBits | (bitOffset << TypeBitsOffset),
4151
+ catBits | (bitOffset << TYPE_REALM_OFFSET),
4024
4152
  this.tokenHash,
4025
4153
  fdata
4026
4154
  ]);
@@ -4032,15 +4160,16 @@ class FilterCompiler {
4032
4160
  }
4033
4161
 
4034
4162
  // These are to quickly test whether a filter is composite
4035
- const FROM_BIT = 0b000000001;
4036
- const TO_BIT = 0b000000010;
4037
- const DENYALLOW_BIT = 0b000000100;
4038
- const HEADER_BIT = 0b000001000;
4039
- const STRICT_PARTY_BIT = 0b000010000;
4040
- const MODIFY_BIT = 0b000100000;
4041
- const NOT_TYPE_BIT = 0b001000000;
4042
- const IMPORTANT_BIT = 0b010000000;
4043
- const METHOD_BIT = 0b100000000;
4163
+ const FROM_BIT = 0b0000000001;
4164
+ const TO_BIT = 0b0000000010;
4165
+ const DENYALLOW_BIT = 0b0000000100;
4166
+ const HEADER_BIT = 0b0000001000;
4167
+ const STRICT_PARTY_BIT = 0b0000010000;
4168
+ const MODIFY_BIT = 0b0000100000;
4169
+ const NOT_TYPE_BIT = 0b0001000000;
4170
+ const IMPORTANT_BIT = 0b0010000000;
4171
+ const METHOD_BIT = 0b0100000000;
4172
+ const IPADDRESS_BIT = 0b1000000000;
4044
4173
 
4045
4174
  FilterCompiler.prototype.FILTER_OK = 0;
4046
4175
  FilterCompiler.prototype.FILTER_INVALID = 1;
@@ -4182,7 +4311,7 @@ StaticNetFilteringEngine.prototype.freeze = function() {
4182
4311
  // the block-important realm should be checked when and only when
4183
4312
  // there is a matched exception filter, which important filters are
4184
4313
  // meant to override.
4185
- if ( (bits & ActionBitsMask) === BLOCKIMPORTANT_REALM ) {
4314
+ if ( (bits & BLOCKALLOW_REALM) === BLOCKIMPORTANT_REALM ) {
4186
4315
  this.addFilterUnit(
4187
4316
  bits & ~IMPORTANT_REALM,
4188
4317
  tokenHash,
@@ -4325,14 +4454,24 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar
4325
4454
  }
4326
4455
  }
4327
4456
 
4457
+ // Priority:
4458
+ // Block: 1 (default priority)
4459
+ // Redirect: 2-9
4460
+ // Excepted redirect: 12-19
4461
+ // Allow: 20
4462
+ // Block important: 30
4463
+ // Redirect important: 32-39
4464
+
4328
4465
  const realms = new Map([
4329
- [ BLOCK_REALM, 'block' ],
4330
- [ ALLOW_REALM, 'allow' ],
4331
- [ REDIRECT_REALM, 'redirect' ],
4332
- [ REMOVEPARAM_REALM, 'removeparam' ],
4333
- [ CSP_REALM, 'csp' ],
4334
- [ PERMISSIONS_REALM, 'permissions' ],
4335
- [ URLTRANSFORM_REALM, 'uritransform' ],
4466
+ [ BLOCK_REALM, { type: 'block', priority: 0 } ],
4467
+ [ ALLOW_REALM, { type: 'allow', priority: 20 } ],
4468
+ [ REDIRECT_REALM, { type: 'redirect', priority: 2 } ],
4469
+ [ REMOVEPARAM_REALM, { type: 'removeparam', priority: 0 } ],
4470
+ [ CSP_REALM, { type: 'csp', priority: 0 } ],
4471
+ [ PERMISSIONS_REALM, { type: 'permissions', priority: 0 } ],
4472
+ [ URLTRANSFORM_REALM, { type: 'uritransform', priority: 0 } ],
4473
+ [ HEADERS_REALM, { type: 'block', priority: 0 } ],
4474
+ [ URLSKIP_REALM, { type: 'urlskip', priority: 0 } ],
4336
4475
  ]);
4337
4476
  const partyness = new Map([
4338
4477
  [ ANYPARTY_REALM, '' ],
@@ -4355,7 +4494,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar
4355
4494
  'other',
4356
4495
  ]);
4357
4496
  const ruleset = [];
4358
- for ( const [ realmBits, realmName ] of realms ) {
4497
+ for ( const [ realmBits, realmDetails ] of realms ) {
4359
4498
  for ( const [ partyBits, partyName ] of partyness ) {
4360
4499
  for ( const typeName in typeNameToTypeValue ) {
4361
4500
  if ( types.has(typeName) === false ) { continue; }
@@ -4366,7 +4505,10 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar
4366
4505
  for ( const rules of bucket.values() ) {
4367
4506
  for ( const rule of rules ) {
4368
4507
  rule.action = rule.action || {};
4369
- rule.action.type = realmName;
4508
+ rule.action.type = realmDetails.type;
4509
+ if ( realmDetails.priority !== 0 ) {
4510
+ rule.priority = (rule.priority || 0) + realmDetails.priority;
4511
+ }
4370
4512
  if ( partyName !== '' ) {
4371
4513
  rule.condition = rule.condition || {};
4372
4514
  rule.condition.domainType = partyName;
@@ -4462,12 +4604,11 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar
4462
4604
  }
4463
4605
  break;
4464
4606
  case 'redirect-rule': {
4465
- let priority = rule.priority || 1;
4466
4607
  let token = rule.__modifierValue;
4467
4608
  if ( token !== '' ) {
4468
4609
  const match = /:(\d+)$/.exec(token);
4469
4610
  if ( match !== null ) {
4470
- priority += parseInt(match[1], 10);
4611
+ rule.priority = Math.min(rule.priority + parseInt(match[1], 10), 9);
4471
4612
  token = token.slice(0, match.index);
4472
4613
  }
4473
4614
  }
@@ -4479,10 +4620,9 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar
4479
4620
  const extensionPath = resource || token;
4480
4621
  rule.action.type = 'redirect';
4481
4622
  rule.action.redirect = { extensionPath };
4482
- rule.priority = priority + 1;
4483
4623
  } else {
4484
4624
  rule.action.type = 'block';
4485
- rule.priority = priority + 2;
4625
+ rule.priority += 10;
4486
4626
  }
4487
4627
  break;
4488
4628
  }
@@ -4746,7 +4886,8 @@ StaticNetFilteringEngine.prototype.matchAndFetchModifiers = function(
4746
4886
  $docDomain = fctxt.getDocDomain();
4747
4887
  $requestHostname = fctxt.getHostname();
4748
4888
  $requestMethodBit = fctxt.method || 0;
4749
- $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
4889
+ $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET;
4890
+ $requestAddress = fctxt.getIPAddress();
4750
4891
 
4751
4892
  const modifierType = modifierTypeFromName.get(modifierName);
4752
4893
  const modifierBits = modifierBitsFromType.get(modifierType);
@@ -4840,7 +4981,7 @@ StaticNetFilteringEngine.prototype.matchAndFetchModifiers = function(
4840
4981
  const toRemove = new Map();
4841
4982
 
4842
4983
  for ( const result of results ) {
4843
- const actionBits = result.bits & ActionBitsMask;
4984
+ const actionBits = result.bits & BLOCKALLOW_REALM;
4844
4985
  const modifyValue = result.value;
4845
4986
  if ( actionBits === BLOCKIMPORTANT_REALM ) {
4846
4987
  toAddImportant.set(modifyValue, result);
@@ -5043,7 +5184,8 @@ StaticNetFilteringEngine.prototype.matchRequestReverse = function(type, url) {
5043
5184
  $requestURL = urlTokenizer.setURL(url);
5044
5185
  $requestURLRaw = url;
5045
5186
  $requestMethodBit = 0;
5046
- $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
5187
+ $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET;
5188
+ $requestAddress = '';
5047
5189
  $isBlockImportant = false;
5048
5190
  this.$filterUnit = 0;
5049
5191
 
@@ -5111,7 +5253,8 @@ StaticNetFilteringEngine.prototype.matchRequest = function(fctxt, modifiers = 0)
5111
5253
  $docDomain = fctxt.getDocDomain();
5112
5254
  $requestHostname = fctxt.getHostname();
5113
5255
  $requestMethodBit = fctxt.method || 0;
5114
- $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
5256
+ $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET;
5257
+ $requestAddress = fctxt.getIPAddress();
5115
5258
  $isBlockImportant = false;
5116
5259
 
5117
5260
  // Evaluate block realm before allow realm, and allow realm before
@@ -5146,7 +5289,8 @@ StaticNetFilteringEngine.prototype.matchHeaders = function(fctxt, headers) {
5146
5289
  $docDomain = fctxt.getDocDomain();
5147
5290
  $requestHostname = fctxt.getHostname();
5148
5291
  $requestMethodBit = fctxt.method || 0;
5149
- $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
5292
+ $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET;
5293
+ $requestAddress = fctxt.getIPAddress();
5150
5294
  $httpHeaders.init(headers);
5151
5295
 
5152
5296
  let r = 0;
@@ -5156,6 +5300,10 @@ StaticNetFilteringEngine.prototype.matchHeaders = function(fctxt, headers) {
5156
5300
  if ( r !== 0 && $isBlockImportant !== true ) {
5157
5301
  if ( this.realmMatchString(HEADERS_REALM | ALLOW_REALM, typeBits, partyBits) ) {
5158
5302
  r = 2;
5303
+ } else if ( this.realmMatchString(ALLOW_REALM, typeBits, partyBits) ) {
5304
+ r = 2;
5305
+ }
5306
+ if ( r === 2 ) {
5159
5307
  if ( this.realmMatchString(HEADERS_REALM | BLOCKIMPORTANT_REALM, typeBits, partyBits) ) {
5160
5308
  r = 1;
5161
5309
  }
@@ -5187,11 +5335,35 @@ StaticNetFilteringEngine.prototype.redirectRequest = function(redirectEngine, fc
5187
5335
  return directives;
5188
5336
  };
5189
5337
 
5190
- StaticNetFilteringEngine.prototype.transformRequest = function(fctxt) {
5338
+ function parseRedirectRequestValue(directive) {
5339
+ if ( directive.cache === null ) {
5340
+ directive.cache = sfp.parseRedirectValue(directive.value);
5341
+ }
5342
+ return directive.cache;
5343
+ }
5344
+
5345
+ function compareRedirectRequests(redirectEngine, a, b) {
5346
+ const { token: atok, priority: aint, bits: abits } =
5347
+ parseRedirectRequestValue(a);
5348
+ if ( redirectEngine.hasToken(atok) === false ) { return -1; }
5349
+ const { token: btok, priority: bint, bits: bbits } =
5350
+ parseRedirectRequestValue(b);
5351
+ if ( redirectEngine.hasToken(btok) === false ) { return 1; }
5352
+ if ( abits !== bbits ) {
5353
+ if ( (abits & IMPORTANT_REALM) !== 0 ) { return 1; }
5354
+ if ( (bbits & IMPORTANT_REALM) !== 0 ) { return -1; }
5355
+ if ( (abits & ALLOW_REALM) !== 0 ) { return -1; }
5356
+ if ( (bbits & ALLOW_REALM) !== 0 ) { return 1; }
5357
+ }
5358
+ return aint - bint;
5359
+ }
5360
+
5361
+ /******************************************************************************/
5362
+
5363
+ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) {
5191
5364
  const directives = this.matchAndFetchModifiers(fctxt, 'uritransform');
5192
5365
  if ( directives === undefined ) { return; }
5193
5366
  const redirectURL = new URL(fctxt.url);
5194
- const out = [];
5195
5367
  for ( const directive of directives ) {
5196
5368
  if ( (directive.bits & ALLOW_REALM) !== 0 ) {
5197
5369
  out.push(directive);
@@ -5223,27 +5395,59 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt) {
5223
5395
  return out;
5224
5396
  };
5225
5397
 
5226
- function parseRedirectRequestValue(directive) {
5227
- if ( directive.cache === null ) {
5228
- directive.cache = sfp.parseRedirectValue(directive.value);
5398
+ /******************************************************************************/
5399
+
5400
+ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) {
5401
+ if ( fctxt.redirectURL !== undefined ) { return; }
5402
+ const directives = this.matchAndFetchModifiers(fctxt, 'urlskip');
5403
+ if ( directives === undefined ) { return; }
5404
+ for ( const directive of directives ) {
5405
+ if ( (directive.bits & ALLOW_REALM) !== 0 ) {
5406
+ out.push(directive);
5407
+ continue;
5408
+ }
5409
+ const urlin = fctxt.url;
5410
+ const value = directive.value;
5411
+ const steps = value.includes(' ') && value.split(/ +/) || [ value ];
5412
+ const urlout = urlSkip(urlin, steps);
5413
+ if ( urlout === undefined ) { continue; }
5414
+ if ( urlout === urlin ) { continue; }
5415
+ fctxt.redirectURL = urlout;
5416
+ out.push(directive);
5417
+ break;
5229
5418
  }
5230
- return directive.cache;
5231
- }
5419
+ if ( out.length === 0 ) { return; }
5420
+ return out;
5421
+ };
5232
5422
 
5233
- function compareRedirectRequests(redirectEngine, a, b) {
5234
- const { token: atok, priority: aint, bits: abits } =
5235
- parseRedirectRequestValue(a);
5236
- if ( redirectEngine.hasToken(atok) === false ) { return -1; }
5237
- const { token: btok, priority: bint, bits: bbits } =
5238
- parseRedirectRequestValue(b);
5239
- if ( redirectEngine.hasToken(btok) === false ) { return 1; }
5240
- if ( abits !== bbits ) {
5241
- if ( (abits & IMPORTANT_REALM) !== 0 ) { return 1; }
5242
- if ( (bbits & IMPORTANT_REALM) !== 0 ) { return -1; }
5243
- if ( (abits & ALLOW_REALM) !== 0 ) { return -1; }
5244
- if ( (bbits & ALLOW_REALM) !== 0 ) { return 1; }
5423
+ function urlSkip(urlin, steps) {
5424
+ try {
5425
+ let urlout;
5426
+ for ( const step of steps ) {
5427
+ // Extract from URL parameter
5428
+ if ( step.startsWith('?') ) {
5429
+ urlout = (new URL(urlin)).searchParams.get(step.slice(1));
5430
+ if ( urlout === null ) { return; }
5431
+ if ( urlout.includes(' ') ) {
5432
+ urlout = urlout.replace(/ /g, '%20');
5433
+ }
5434
+ urlin = urlout;
5435
+ continue;
5436
+ }
5437
+ // Enforce https
5438
+ if ( step === '+https' ) {
5439
+ const s = urlin.replace(/^https?:\/\//, '');
5440
+ if ( /^[\w-]:\/\//.test(s) ) { return; }
5441
+ urlin = urlout = `https://${s}`;
5442
+ continue;
5443
+ }
5444
+ // Unknown directive
5445
+ return;
5446
+ }
5447
+ void new URL(urlout);
5448
+ return urlout;
5449
+ } catch(x) {
5245
5450
  }
5246
- return aint - bint;
5247
5451
  }
5248
5452
 
5249
5453
  /******************************************************************************/
@@ -5251,7 +5455,8 @@ function compareRedirectRequests(redirectEngine, a, b) {
5251
5455
  // https://github.com/uBlockOrigin/uBlock-issues/issues/1626
5252
5456
  // Do not redirect when the number of query parameters does not change.
5253
5457
 
5254
- StaticNetFilteringEngine.prototype.filterQuery = function(fctxt) {
5458
+ StaticNetFilteringEngine.prototype.filterQuery = function(fctxt, out = []) {
5459
+ if ( fctxt.redirectURL !== undefined ) { return; }
5255
5460
  const directives = this.matchAndFetchModifiers(fctxt, 'removeparam');
5256
5461
  if ( directives === undefined ) { return; }
5257
5462
  const url = fctxt.url;
@@ -5274,7 +5479,6 @@ StaticNetFilteringEngine.prototype.filterQuery = function(fctxt) {
5274
5479
  }
5275
5480
  }
5276
5481
  const inParamCount = params.size;
5277
- const out = [];
5278
5482
  for ( const directive of directives ) {
5279
5483
  if ( params.size === 0 ) { break; }
5280
5484
  const isException = (directive.bits & ALLOW_REALM) !== 0;
@@ -5384,17 +5588,76 @@ StaticNetFilteringEngine.prototype.enableWASM = function(wasmModuleFetcher, path
5384
5588
 
5385
5589
  /******************************************************************************/
5386
5590
 
5387
- StaticNetFilteringEngine.prototype.test = async function(docURL, type, url) {
5591
+ StaticNetFilteringEngine.prototype.test = function(details) {
5592
+ const { url, type, from, redirectEngine } = details;
5593
+ if ( url === undefined ) { return; }
5388
5594
  const fctxt = new FilteringContext();
5389
- fctxt.setDocOriginFromURL(docURL);
5390
- fctxt.setType(type);
5391
5595
  fctxt.setURL(url);
5596
+ fctxt.setType(type || '');
5597
+ fctxt.setDocOriginFromURL(from || '');
5392
5598
  const r = this.matchRequest(fctxt);
5393
- console.info(`${r}`);
5599
+ const out = [ `url: ${url}` ];
5600
+ if ( type ) {
5601
+ out.push(`type: ${type}`);
5602
+ }
5603
+ if ( from ) {
5604
+ out.push(`context: ${from}`);
5605
+ }
5394
5606
  if ( r !== 0 ) {
5395
- console.info(this.toLogData());
5607
+ const logdata = this.toLogData();
5608
+ if ( r === 1 ) {
5609
+ out.push(`blocked: ${logdata.raw}`);
5610
+ } else if ( r === 2 ) {
5611
+ out.push(`unblocked: ${logdata.raw}`);
5612
+ }
5613
+ } else {
5614
+ out.push('not blocked');
5396
5615
  }
5397
- };
5616
+ if ( r !== 1 ) {
5617
+ const entries = this.transformRequest(fctxt);
5618
+ if ( entries ) {
5619
+ for ( const entry of entries ) {
5620
+ out.push(`modified: ${entry.logData().raw}`);
5621
+ }
5622
+ }
5623
+ if ( fctxt.redirectURL !== undefined && this.hasQuery(fctxt) ) {
5624
+ const entries = this.filterQuery(fctxt, 'removeparam');
5625
+ if ( entries ) {
5626
+ for ( const entry of entries ) {
5627
+ out.push(`modified: ${entry.logData().raw}`);
5628
+ }
5629
+ }
5630
+ }
5631
+ if ( fctxt.type === 'main_frame' || fctxt.type === 'sub_frame' ) {
5632
+ const csps = this.matchAndFetchModifiers(fctxt, 'csp');
5633
+ if ( csps ) {
5634
+ for ( const csp of csps ) {
5635
+ out.push(`modified: ${csp.logData().raw}`);
5636
+ }
5637
+ }
5638
+ const pps = this.matchAndFetchModifiers(fctxt, 'permissions');
5639
+ if ( pps ) {
5640
+ for ( const pp of pps ) {
5641
+ out.push(`modified: ${pp.logData().raw}`);
5642
+ }
5643
+ }
5644
+ }
5645
+ } else if ( redirectEngine ) {
5646
+ const redirects = this.redirectRequest(redirectEngine, fctxt);
5647
+ if ( redirects ) {
5648
+ for ( const redirect of redirects ) {
5649
+ out.push(`modified: ${redirect.logData().raw}`);
5650
+ }
5651
+ }
5652
+ }
5653
+ const urlskips = this.matchAndFetchModifiers(fctxt, 'urlskip');
5654
+ if ( urlskips ) {
5655
+ for ( const urlskip of urlskips ) {
5656
+ out.push(`modified: ${urlskip.logData().raw}`);
5657
+ }
5658
+ }
5659
+ return out.join('\n');
5660
+ }
5398
5661
 
5399
5662
  /******************************************************************************/
5400
5663
 
@@ -5489,6 +5752,7 @@ StaticNetFilteringEngine.prototype.dump = function() {
5489
5752
  [ PERMISSIONS_REALM, 'permissions' ],
5490
5753
  [ URLTRANSFORM_REALM, 'uritransform' ],
5491
5754
  [ REPLACE_REALM, 'replace' ],
5755
+ [ URLSKIP_REALM, 'urlskip' ],
5492
5756
  ]);
5493
5757
  const partyness = new Map([
5494
5758
  [ ANYPARTY_REALM, 'any-party' ],