@browserless.io/browserless 2.32.0-beta-1 → 2.32.0

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 (165) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/build/routes/chrome/http/content.post.body.json +8 -8
  3. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  4. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  5. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  6. package/build/routes/chromium/http/content.post.body.json +8 -8
  7. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  8. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  9. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  10. package/build/routes/edge/http/content.post.body.json +8 -8
  11. package/build/routes/edge/http/pdf.post.body.json +8 -8
  12. package/build/routes/edge/http/scrape.post.body.json +8 -8
  13. package/build/routes/edge/http/screenshot.post.body.json +8 -8
  14. package/extensions/ublocklite/_locales/be/messages.json +9 -9
  15. package/extensions/ublocklite/_locales/cs/messages.json +7 -7
  16. package/extensions/ublocklite/_locales/hi/messages.json +1 -1
  17. package/extensions/ublocklite/_locales/ko/messages.json +7 -7
  18. package/extensions/ublocklite/_locales/vi/messages.json +8 -8
  19. package/extensions/ublocklite/_locales/zh_TW/messages.json +7 -7
  20. package/extensions/ublocklite/css/develop.css +1 -2
  21. package/extensions/ublocklite/js/arglist-parser.js +116 -0
  22. package/extensions/ublocklite/js/develop.js +18 -11
  23. package/extensions/ublocklite/js/jsonpath.js +470 -0
  24. package/extensions/ublocklite/js/redirect-resources.js +192 -0
  25. package/extensions/ublocklite/js/rw-dnr-editor.js +17 -3
  26. package/extensions/ublocklite/js/static-filtering-parser.js +4413 -0
  27. package/extensions/ublocklite/js/ubo-parser.js +461 -0
  28. package/extensions/ublocklite/lib/codemirror/cm6.bundle.ubol.min.js +1 -1
  29. package/extensions/ublocklite/lib/csstree/LICENSE +19 -0
  30. package/extensions/ublocklite/lib/csstree/css-tree.js +17 -0
  31. package/extensions/ublocklite/manifest.json +1 -1
  32. package/extensions/ublocklite/rulesets/generic-details.json +3 -8
  33. package/extensions/ublocklite/rulesets/main/annoyances-cookies.json +7 -5
  34. package/extensions/ublocklite/rulesets/main/annoyances-others.json +2 -1
  35. package/extensions/ublocklite/rulesets/main/chn-0.json +11 -7
  36. package/extensions/ublocklite/rulesets/main/deu-0.json +0 -3
  37. package/extensions/ublocklite/rulesets/main/easylist.json +13 -9
  38. package/extensions/ublocklite/rulesets/main/easyprivacy.json +17 -4
  39. package/extensions/ublocklite/rulesets/main/fra-0.json +4 -6
  40. package/extensions/ublocklite/rulesets/main/idn-0.json +1 -1
  41. package/extensions/ublocklite/rulesets/main/ind-0.json +13 -6
  42. package/extensions/ublocklite/rulesets/main/nld-0.json +1 -0
  43. package/extensions/ublocklite/rulesets/main/pgl.json +2 -11
  44. package/extensions/ublocklite/rulesets/main/rus-0.json +7 -4
  45. package/extensions/ublocklite/rulesets/main/stevenblack-hosts.json +2092 -363
  46. package/extensions/ublocklite/rulesets/main/swe-1.json +8 -7
  47. package/extensions/ublocklite/rulesets/main/tur-0.json +3 -0
  48. package/extensions/ublocklite/rulesets/main/ublock-badware.json +48 -49
  49. package/extensions/ublocklite/rulesets/main/ublock-filters.json +21 -17
  50. package/extensions/ublocklite/rulesets/main/urlhaus-full.json +272 -234
  51. package/extensions/ublocklite/rulesets/main/vie-1.json +18 -15
  52. package/extensions/ublocklite/rulesets/modify-headers/chn-0.json +1 -0
  53. package/extensions/ublocklite/rulesets/redirect/nld-0.json +19 -0
  54. package/extensions/ublocklite/rulesets/redirect/swe-1.json +1 -1
  55. package/extensions/ublocklite/rulesets/redirect/ublock-filters.json +5 -6
  56. package/extensions/ublocklite/rulesets/redirect/vie-1.json +2 -2
  57. package/extensions/ublocklite/rulesets/regex/chn-0.json +1 -1
  58. package/extensions/ublocklite/rulesets/regex/ublock-filters.json +5 -4
  59. package/extensions/ublocklite/rulesets/removeparam/adguard-spyware-url.json +1 -1
  60. package/extensions/ublocklite/rulesets/removeparam/rus-1.json +3 -4
  61. package/extensions/ublocklite/rulesets/ruleset-details.json +111 -111
  62. package/extensions/ublocklite/rulesets/scripting/declarative/annoyances-overlays.js +1 -1
  63. package/extensions/ublocklite/rulesets/scripting/declarative/fra-0.js +3 -3
  64. package/extensions/ublocklite/rulesets/scripting/declarative/rus-0.js +1 -1
  65. package/extensions/ublocklite/rulesets/scripting/declarative/rus-1.js +3 -3
  66. package/extensions/ublocklite/rulesets/scripting/declarative/tur-0.js +3 -3
  67. package/extensions/ublocklite/rulesets/scripting/declarative/ublock-filters.js +3 -3
  68. package/extensions/ublocklite/rulesets/scripting/generic/annoyances-cookies.js +1 -1
  69. package/extensions/ublocklite/rulesets/scripting/generic/annoyances-social.js +1 -1
  70. package/extensions/ublocklite/rulesets/scripting/generic/rus-0.js +1 -1
  71. package/extensions/ublocklite/rulesets/scripting/generic/ublock-badware.js +1 -1
  72. package/extensions/ublocklite/rulesets/scripting/generic/ublock-filters.js +1 -1
  73. package/extensions/ublocklite/rulesets/scripting/generichigh/chn-0.css +1 -0
  74. package/extensions/ublocklite/rulesets/scripting/generichigh/idn-0.css +1 -0
  75. package/extensions/ublocklite/rulesets/scripting/generichigh/ublock-filters.css +0 -4
  76. package/extensions/ublocklite/rulesets/scripting/procedural/chn-0.js +3 -3
  77. package/extensions/ublocklite/rulesets/scripting/procedural/easylist.js +3 -3
  78. package/extensions/ublocklite/rulesets/scripting/procedural/rus-0.js +1 -1
  79. package/extensions/ublocklite/rulesets/scripting/procedural/swe-1.js +3 -3
  80. package/extensions/ublocklite/rulesets/scripting/procedural/ublock-filters.js +3 -3
  81. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.set-cookie.js +2 -2
  82. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.set-local-storage-item.js +1 -1
  83. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-click-element.js +2 -2
  84. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-set-cookie.js +2 -2
  85. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-set-local-storage-item.js +2 -2
  86. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-others.remove-cookie.js +2 -2
  87. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.addEventListener-defuser.js +1 -1
  88. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.prevent-fetch.js +2 -2
  89. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.set-cookie.js +2 -2
  90. package/extensions/ublocklite/rulesets/scripting/scriptlet/fra-0.prevent-fetch.js +2 -2
  91. package/extensions/ublocklite/rulesets/scripting/scriptlet/hun-0.prevent-fetch.js +2 -2
  92. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.addEventListener-defuser.js +2 -2
  93. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.abort-on-property-read.js +1 -1
  94. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-attr.js +1 -1
  95. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-constant.js +1 -1
  96. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-cookie.js +2 -2
  97. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-1.addEventListener-defuser.js +1 -1
  98. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-1.remove-cookie.js +2 -2
  99. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.prevent-window-open.js +1 -1
  100. package/extensions/ublocklite/rulesets/scripting/scriptlet/swe-1.addEventListener-defuser.js +2 -2
  101. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.prevent-window-open.js +1 -1
  102. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.remove-node-text.js +2 -2
  103. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-current-script.js +3 -3
  104. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-on-property-read.js +1 -1
  105. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-on-stack-trace.js +2 -2
  106. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.addEventListener-defuser.js +3 -3
  107. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.adjust-setInterval.js +2 -2
  108. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.adjust-setTimeout.js +2 -2
  109. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.alert-buster.js +1 -1
  110. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.href-sanitizer.js +1 -1
  111. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.noeval-if.js +1 -1
  112. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-fetch.js +2 -2
  113. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-setTimeout.js +2 -2
  114. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-window-open.js +1 -1
  115. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-xhr.js +1 -1
  116. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.remove-cookie.js +2 -2
  117. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.remove-node-text.js +3 -3
  118. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-constant.js +3 -3
  119. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-local-storage-item.js +2 -2
  120. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-session-storage-item.js +2 -2
  121. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-argument.js +1 -1
  122. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-node-text.js +2 -2
  123. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-outbound-text.js +1 -1
  124. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-set-constant.js +2 -2
  125. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-set-local-storage-item.js +1 -1
  126. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-suppress-native-method.js +1 -1
  127. package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.abort-current-script.js +1 -1
  128. package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.abort-on-property-read.js +1 -1
  129. package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.set-constant.js +2 -2
  130. package/extensions/ublocklite/rulesets/scripting/specific/adguard-mobile.js +3 -3
  131. package/extensions/ublocklite/rulesets/scripting/specific/annoyances-cookies.js +3 -3
  132. package/extensions/ublocklite/rulesets/scripting/specific/annoyances-others.js +2 -2
  133. package/extensions/ublocklite/rulesets/scripting/specific/annoyances-overlays.js +3 -3
  134. package/extensions/ublocklite/rulesets/scripting/specific/annoyances-social.js +3 -3
  135. package/extensions/ublocklite/rulesets/scripting/specific/chn-0.js +3 -3
  136. package/extensions/ublocklite/rulesets/scripting/specific/deu-0.js +3 -3
  137. package/extensions/ublocklite/rulesets/scripting/specific/easylist.js +3 -3
  138. package/extensions/ublocklite/rulesets/scripting/specific/fin-0.js +3 -3
  139. package/extensions/ublocklite/rulesets/scripting/specific/fra-0.js +3 -3
  140. package/extensions/ublocklite/rulesets/scripting/specific/hun-0.js +3 -3
  141. package/extensions/ublocklite/rulesets/scripting/specific/idn-0.js +3 -3
  142. package/extensions/ublocklite/rulesets/scripting/specific/ind-0.js +3 -3
  143. package/extensions/ublocklite/rulesets/scripting/specific/jpn-1.js +3 -3
  144. package/extensions/ublocklite/rulesets/scripting/specific/rus-0.js +3 -3
  145. package/extensions/ublocklite/rulesets/scripting/specific/spa-0.js +3 -3
  146. package/extensions/ublocklite/rulesets/scripting/specific/spa-1.js +3 -3
  147. package/extensions/ublocklite/rulesets/scripting/specific/swe-1.js +3 -3
  148. package/extensions/ublocklite/rulesets/scripting/specific/tur-0.js +3 -3
  149. package/extensions/ublocklite/rulesets/scripting/specific/ublock-filters.js +3 -3
  150. package/extensions/ublocklite/rulesets/scripting/specific/vie-1.js +3 -3
  151. package/extensions/ublocklite/rulesets/scriptlet-details.json +82 -70
  152. package/extensions/ublocklite/rulesets/strictblock/chn-0.json +64 -0
  153. package/extensions/ublocklite/rulesets/strictblock/easylist.json +1 -1
  154. package/extensions/ublocklite/rulesets/strictblock/easyprivacy.json +7 -1
  155. package/extensions/ublocklite/rulesets/strictblock/idn-0.json +0 -1
  156. package/extensions/ublocklite/rulesets/strictblock/pgl.json +2 -11
  157. package/extensions/ublocklite/rulesets/strictblock/stevenblack-hosts.json +2092 -363
  158. package/extensions/ublocklite/rulesets/strictblock/ublock-badware.json +633 -632
  159. package/extensions/ublocklite/rulesets/strictblock/ublock-filters.json +1 -1
  160. package/extensions/ublocklite/rulesets/strictblock/urlhaus-full.json +272 -234
  161. package/extensions/ublocklite/rulesets/urlskip/ublock-filters.json +6 -0
  162. package/extensions/ublocklite/ublock.zip +0 -0
  163. package/package.json +3 -3
  164. package/static/docs/swagger.json +10 -10
  165. package/static/docs/swagger.min.json +9 -9
@@ -0,0 +1,4413 @@
1
+ /*******************************************************************************
2
+
3
+ uBlock Origin - a comprehensive, efficient content blocker
4
+ Copyright (C) 2020-present Raymond Hill
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
18
+
19
+ Home: https://github.com/gorhill/uBlock
20
+ */
21
+
22
+ import * as cssTree from '../lib/csstree/css-tree.js';
23
+ import { ArglistParser } from './arglist-parser.js';
24
+ import { JSONPath } from './jsonpath.js';
25
+
26
+ /*******************************************************************************
27
+ *
28
+ * The parser creates a simple unidirectional AST from a raw line of text.
29
+ * Each node in the AST is a sequence of numbers, so as to avoid the need to
30
+ * make frequent memory allocation to represent the AST.
31
+ *
32
+ * All the AST nodes are allocated in the same integer-only array, which
33
+ * array is reused when parsing new lines.
34
+ *
35
+ * The AST can only be walked from top to bottom, then left to right.
36
+ *
37
+ * Each node typically refer to a corresponding string slice in the source
38
+ * text.
39
+ *
40
+ * It may happens a node requires to normalize the corresponding source slice,
41
+ * in which case there will be a reference in the AST to a transformed source
42
+ * string. (For example, a domain name might contain unicode characters, in
43
+ * which case the corresponding node will contain a reference to the
44
+ * (transformed) punycoded version of the domain name.)
45
+ *
46
+ * The AST can be easily used for syntax coloring purpose, in which case it's
47
+ * just a matter of walking through all the nodes in natural order.
48
+ *
49
+ * A tree walking utility class exists for compilation and syntax coloring
50
+ * purpose.
51
+ *
52
+ **/
53
+
54
+ /******************************************************************************/
55
+
56
+ let iota = 0;
57
+
58
+ iota = 0;
59
+ export const AST_TYPE_NONE = iota++;
60
+ export const AST_TYPE_UNKNOWN = iota++;
61
+ export const AST_TYPE_COMMENT = iota++;
62
+ export const AST_TYPE_NETWORK = iota++;
63
+ export const AST_TYPE_EXTENDED = iota++;
64
+
65
+ iota = 0;
66
+ export const AST_TYPE_NETWORK_PATTERN_ANY = iota++;
67
+ export const AST_TYPE_NETWORK_PATTERN_HOSTNAME = iota++;
68
+ export const AST_TYPE_NETWORK_PATTERN_PLAIN = iota++;
69
+ export const AST_TYPE_NETWORK_PATTERN_REGEX = iota++;
70
+ export const AST_TYPE_NETWORK_PATTERN_GENERIC = iota++;
71
+ export const AST_TYPE_NETWORK_PATTERN_BAD = iota++;
72
+ export const AST_TYPE_EXTENDED_COSMETIC = iota++;
73
+ export const AST_TYPE_EXTENDED_SCRIPTLET = iota++;
74
+ export const AST_TYPE_EXTENDED_HTML = iota++;
75
+ export const AST_TYPE_EXTENDED_RESPONSEHEADER = iota++;
76
+ export const AST_TYPE_COMMENT_PREPARSER = iota++;
77
+
78
+ iota = 0;
79
+ export const AST_FLAG_UNSUPPORTED = 1 << iota++;
80
+ export const AST_FLAG_IGNORE = 1 << iota++;
81
+ export const AST_FLAG_HAS_ERROR = 1 << iota++;
82
+ export const AST_FLAG_IS_EXCEPTION = 1 << iota++;
83
+ export const AST_FLAG_EXT_STRONG = 1 << iota++;
84
+ export const AST_FLAG_EXT_STYLE = 1 << iota++;
85
+ export const AST_FLAG_EXT_SCRIPTLET_ADG = 1 << iota++;
86
+ export const AST_FLAG_NET_PATTERN_LEFT_HNANCHOR = 1 << iota++;
87
+ export const AST_FLAG_NET_PATTERN_RIGHT_PATHANCHOR = 1 << iota++;
88
+ export const AST_FLAG_NET_PATTERN_LEFT_ANCHOR = 1 << iota++;
89
+ export const AST_FLAG_NET_PATTERN_RIGHT_ANCHOR = 1 << iota++;
90
+ export const AST_FLAG_HAS_OPTIONS = 1 << iota++;
91
+
92
+ iota = 0;
93
+ export const AST_ERROR_NONE = 1 << iota++;
94
+ export const AST_ERROR_REGEX = 1 << iota++;
95
+ export const AST_ERROR_PATTERN = 1 << iota++;
96
+ export const AST_ERROR_DOMAIN_NAME = 1 << iota++;
97
+ export const AST_ERROR_OPTION_DUPLICATE = 1 << iota++;
98
+ export const AST_ERROR_OPTION_UNKNOWN = 1 << iota++;
99
+ export const AST_ERROR_OPTION_BADVALUE = 1 << iota++;
100
+ export const AST_ERROR_OPTION_EXCLUDED = 1 << iota++;
101
+ export const AST_ERROR_IF_TOKEN_UNKNOWN = 1 << iota++;
102
+ export const AST_ERROR_UNTRUSTED_SOURCE = 1 << iota++;
103
+
104
+ iota = 0;
105
+ const NODE_RIGHT_INDEX = iota++;
106
+ const NOOP_NODE_SIZE = iota;
107
+ const NODE_TYPE_INDEX = iota++;
108
+ const NODE_DOWN_INDEX = iota++;
109
+ const NODE_BEG_INDEX = iota++;
110
+ const NODE_END_INDEX = iota++;
111
+ const NODE_FLAGS_INDEX = iota++;
112
+ const NODE_TRANSFORM_INDEX = iota++;
113
+ const FULL_NODE_SIZE = iota;
114
+
115
+ iota = 0;
116
+ export const NODE_TYPE_NOOP = iota++;
117
+ export const NODE_TYPE_LINE_RAW = iota++;
118
+ export const NODE_TYPE_LINE_BODY = iota++;
119
+ export const NODE_TYPE_WHITESPACE = iota++;
120
+ export const NODE_TYPE_COMMENT = iota++;
121
+ export const NODE_TYPE_IGNORE = iota++;
122
+ export const NODE_TYPE_EXT_RAW = iota++;
123
+ export const NODE_TYPE_EXT_OPTIONS_ANCHOR = iota++;
124
+ export const NODE_TYPE_EXT_OPTIONS = iota++;
125
+ export const NODE_TYPE_EXT_DECORATION = iota++;
126
+ export const NODE_TYPE_EXT_PATTERN_RAW = iota++;
127
+ export const NODE_TYPE_EXT_PATTERN_COSMETIC = iota++;
128
+ export const NODE_TYPE_EXT_PATTERN_HTML = iota++;
129
+ export const NODE_TYPE_EXT_PATTERN_RESPONSEHEADER = iota++;
130
+ export const NODE_TYPE_EXT_PATTERN_SCRIPTLET = iota++;
131
+ export const NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN = iota++;
132
+ export const NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARGS = iota++;
133
+ export const NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG = iota++;
134
+ export const NODE_TYPE_NET_RAW = iota++;
135
+ export const NODE_TYPE_NET_EXCEPTION = iota++;
136
+ export const NODE_TYPE_NET_PATTERN_RAW = iota++;
137
+ export const NODE_TYPE_NET_PATTERN = iota++;
138
+ export const NODE_TYPE_NET_PATTERN_PART = iota++;
139
+ export const NODE_TYPE_NET_PATTERN_PART_SPECIAL = iota++;
140
+ export const NODE_TYPE_NET_PATTERN_PART_UNICODE = iota++;
141
+ export const NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR = iota++;
142
+ export const NODE_TYPE_NET_PATTERN_LEFT_ANCHOR = iota++;
143
+ export const NODE_TYPE_NET_PATTERN_RIGHT_ANCHOR = iota++;
144
+ export const NODE_TYPE_NET_OPTIONS_ANCHOR = iota++;
145
+ export const NODE_TYPE_NET_OPTIONS = iota++;
146
+ export const NODE_TYPE_NET_OPTION_SEPARATOR = iota++;
147
+ export const NODE_TYPE_NET_OPTION_SENTINEL = iota++;
148
+ export const NODE_TYPE_NET_OPTION_RAW = iota++;
149
+ export const NODE_TYPE_NET_OPTION_NAME_NOT = iota++;
150
+ export const NODE_TYPE_NET_OPTION_NAME_UNKNOWN = iota++;
151
+ export const NODE_TYPE_NET_OPTION_NAME_1P = iota++;
152
+ export const NODE_TYPE_NET_OPTION_NAME_STRICT1P = iota++;
153
+ export const NODE_TYPE_NET_OPTION_NAME_3P = iota++;
154
+ export const NODE_TYPE_NET_OPTION_NAME_STRICT3P = iota++;
155
+ export const NODE_TYPE_NET_OPTION_NAME_ALL = iota++;
156
+ export const NODE_TYPE_NET_OPTION_NAME_BADFILTER = iota++;
157
+ export const NODE_TYPE_NET_OPTION_NAME_CNAME = iota++;
158
+ export const NODE_TYPE_NET_OPTION_NAME_CSP = iota++;
159
+ export const NODE_TYPE_NET_OPTION_NAME_CSS = iota++;
160
+ export const NODE_TYPE_NET_OPTION_NAME_DENYALLOW = iota++;
161
+ export const NODE_TYPE_NET_OPTION_NAME_DOC = iota++;
162
+ export const NODE_TYPE_NET_OPTION_NAME_EHIDE = iota++;
163
+ export const NODE_TYPE_NET_OPTION_NAME_EMPTY = iota++;
164
+ export const NODE_TYPE_NET_OPTION_NAME_FONT = iota++;
165
+ export const NODE_TYPE_NET_OPTION_NAME_FRAME = iota++;
166
+ export const NODE_TYPE_NET_OPTION_NAME_FROM = iota++;
167
+ export const NODE_TYPE_NET_OPTION_NAME_GENERICBLOCK = iota++;
168
+ export const NODE_TYPE_NET_OPTION_NAME_GHIDE = iota++;
169
+ export const NODE_TYPE_NET_OPTION_NAME_HEADER = iota++;
170
+ export const NODE_TYPE_NET_OPTION_NAME_IMAGE = iota++;
171
+ export const NODE_TYPE_NET_OPTION_NAME_IMPORTANT = iota++;
172
+ export const NODE_TYPE_NET_OPTION_NAME_INLINEFONT = iota++;
173
+ export const NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT = iota++;
174
+ export const NODE_TYPE_NET_OPTION_NAME_IPADDRESS = iota++;
175
+ export const NODE_TYPE_NET_OPTION_NAME_MATCHCASE = iota++;
176
+ export const NODE_TYPE_NET_OPTION_NAME_MEDIA = iota++;
177
+ export const NODE_TYPE_NET_OPTION_NAME_METHOD = iota++;
178
+ export const NODE_TYPE_NET_OPTION_NAME_MP4 = iota++;
179
+ export const NODE_TYPE_NET_OPTION_NAME_NOOP = iota++;
180
+ export const NODE_TYPE_NET_OPTION_NAME_OBJECT = iota++;
181
+ export const NODE_TYPE_NET_OPTION_NAME_OTHER = iota++;
182
+ export const NODE_TYPE_NET_OPTION_NAME_PERMISSIONS = iota++;
183
+ export const NODE_TYPE_NET_OPTION_NAME_PING = iota++;
184
+ export const NODE_TYPE_NET_OPTION_NAME_POPUNDER = iota++;
185
+ export const NODE_TYPE_NET_OPTION_NAME_POPUP = iota++;
186
+ export const NODE_TYPE_NET_OPTION_NAME_REASON = iota++;
187
+ export const NODE_TYPE_NET_OPTION_NAME_REDIRECT = iota++;
188
+ export const NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE = iota++;
189
+ export const NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM = iota++;
190
+ export const NODE_TYPE_NET_OPTION_NAME_REPLACE = iota++;
191
+ export const NODE_TYPE_NET_OPTION_NAME_SCRIPT = iota++;
192
+ export const NODE_TYPE_NET_OPTION_NAME_SHIDE = iota++;
193
+ export const NODE_TYPE_NET_OPTION_NAME_TO = iota++;
194
+ export const NODE_TYPE_NET_OPTION_NAME_URLSKIP = iota++;
195
+ export const NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM = iota++;
196
+ export const NODE_TYPE_NET_OPTION_NAME_XHR = iota++;
197
+ export const NODE_TYPE_NET_OPTION_NAME_WEBRTC = iota++;
198
+ export const NODE_TYPE_NET_OPTION_NAME_WEBSOCKET = iota++;
199
+ export const NODE_TYPE_NET_OPTION_ASSIGN = iota++;
200
+ export const NODE_TYPE_NET_OPTION_QUOTE = iota++;
201
+ export const NODE_TYPE_NET_OPTION_VALUE = iota++;
202
+ export const NODE_TYPE_OPTION_VALUE_DOMAIN_LIST = iota++;
203
+ export const NODE_TYPE_OPTION_VALUE_DOMAIN_RAW = iota++;
204
+ export const NODE_TYPE_OPTION_VALUE_NOT = iota++;
205
+ export const NODE_TYPE_OPTION_VALUE_DOMAIN = iota++;
206
+ export const NODE_TYPE_OPTION_VALUE_SEPARATOR = iota++;
207
+ export const NODE_TYPE_PREPARSE_DIRECTIVE = iota++;
208
+ export const NODE_TYPE_PREPARSE_DIRECTIVE_VALUE = iota++;
209
+ export const NODE_TYPE_PREPARSE_DIRECTIVE_IF = iota++;
210
+ export const NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE = iota++;
211
+ export const NODE_TYPE_COMMENT_URL = iota++;
212
+ export const NODE_TYPE_COUNT = iota;
213
+
214
+ iota = 0;
215
+ export const NODE_FLAG_IGNORE = 1 << iota++;
216
+ export const NODE_FLAG_ERROR = 1 << iota++;
217
+ export const NODE_FLAG_IS_NEGATED = 1 << iota++;
218
+ export const NODE_FLAG_OPTION_HAS_VALUE = 1 << iota++;
219
+ export const NODE_FLAG_PATTERN_UNTOKENIZABLE = 1 << iota++;
220
+
221
+ export const nodeTypeFromOptionName = new Map([
222
+ [ '', NODE_TYPE_NET_OPTION_NAME_UNKNOWN ],
223
+ [ '1p', NODE_TYPE_NET_OPTION_NAME_1P ],
224
+ /* synonym */ [ 'first-party', NODE_TYPE_NET_OPTION_NAME_1P ],
225
+ [ 'strict1p', NODE_TYPE_NET_OPTION_NAME_STRICT1P ],
226
+ /* synonym */ [ 'strict-first-party', NODE_TYPE_NET_OPTION_NAME_STRICT1P ],
227
+ [ '3p', NODE_TYPE_NET_OPTION_NAME_3P ],
228
+ /* synonym */ [ 'third-party', NODE_TYPE_NET_OPTION_NAME_3P ],
229
+ [ 'strict3p', NODE_TYPE_NET_OPTION_NAME_STRICT3P ],
230
+ /* synonym */ [ 'strict-third-party', NODE_TYPE_NET_OPTION_NAME_STRICT3P ],
231
+ [ 'all', NODE_TYPE_NET_OPTION_NAME_ALL ],
232
+ [ 'badfilter', NODE_TYPE_NET_OPTION_NAME_BADFILTER ],
233
+ [ 'cname', NODE_TYPE_NET_OPTION_NAME_CNAME ],
234
+ [ 'csp', NODE_TYPE_NET_OPTION_NAME_CSP ],
235
+ [ 'css', NODE_TYPE_NET_OPTION_NAME_CSS ],
236
+ /* synonym */ [ 'stylesheet', NODE_TYPE_NET_OPTION_NAME_CSS ],
237
+ [ 'denyallow', NODE_TYPE_NET_OPTION_NAME_DENYALLOW ],
238
+ [ 'doc', NODE_TYPE_NET_OPTION_NAME_DOC ],
239
+ /* synonym */ [ 'document', NODE_TYPE_NET_OPTION_NAME_DOC ],
240
+ [ 'ehide', NODE_TYPE_NET_OPTION_NAME_EHIDE ],
241
+ /* synonym */ [ 'elemhide', NODE_TYPE_NET_OPTION_NAME_EHIDE ],
242
+ [ 'empty', NODE_TYPE_NET_OPTION_NAME_EMPTY ],
243
+ [ 'font', NODE_TYPE_NET_OPTION_NAME_FONT ],
244
+ [ 'frame', NODE_TYPE_NET_OPTION_NAME_FRAME ],
245
+ /* synonym */ [ 'subdocument', NODE_TYPE_NET_OPTION_NAME_FRAME ],
246
+ [ 'from', NODE_TYPE_NET_OPTION_NAME_FROM ],
247
+ /* synonym */ [ 'domain', NODE_TYPE_NET_OPTION_NAME_FROM ],
248
+ [ 'genericblock', NODE_TYPE_NET_OPTION_NAME_GENERICBLOCK ],
249
+ [ 'ghide', NODE_TYPE_NET_OPTION_NAME_GHIDE ],
250
+ /* synonym */ [ 'generichide', NODE_TYPE_NET_OPTION_NAME_GHIDE ],
251
+ [ 'header', NODE_TYPE_NET_OPTION_NAME_HEADER ],
252
+ [ 'image', NODE_TYPE_NET_OPTION_NAME_IMAGE ],
253
+ [ 'important', NODE_TYPE_NET_OPTION_NAME_IMPORTANT ],
254
+ [ 'inline-font', NODE_TYPE_NET_OPTION_NAME_INLINEFONT ],
255
+ [ 'inline-script', NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT ],
256
+ [ 'ipaddress', NODE_TYPE_NET_OPTION_NAME_IPADDRESS ],
257
+ [ 'match-case', NODE_TYPE_NET_OPTION_NAME_MATCHCASE ],
258
+ [ 'media', NODE_TYPE_NET_OPTION_NAME_MEDIA ],
259
+ [ 'method', NODE_TYPE_NET_OPTION_NAME_METHOD ],
260
+ [ 'mp4', NODE_TYPE_NET_OPTION_NAME_MP4 ],
261
+ [ '_', NODE_TYPE_NET_OPTION_NAME_NOOP ],
262
+ [ 'object', NODE_TYPE_NET_OPTION_NAME_OBJECT ],
263
+ /* synonym */ [ 'object-subrequest', NODE_TYPE_NET_OPTION_NAME_OBJECT ],
264
+ [ 'other', NODE_TYPE_NET_OPTION_NAME_OTHER ],
265
+ [ 'permissions', NODE_TYPE_NET_OPTION_NAME_PERMISSIONS ],
266
+ [ 'ping', NODE_TYPE_NET_OPTION_NAME_PING ],
267
+ /* synonym */ [ 'beacon', NODE_TYPE_NET_OPTION_NAME_PING ],
268
+ [ 'popunder', NODE_TYPE_NET_OPTION_NAME_POPUNDER ],
269
+ [ 'popup', NODE_TYPE_NET_OPTION_NAME_POPUP ],
270
+ [ 'reason', NODE_TYPE_NET_OPTION_NAME_REASON ],
271
+ [ 'redirect', NODE_TYPE_NET_OPTION_NAME_REDIRECT ],
272
+ /* synonym */ [ 'rewrite', NODE_TYPE_NET_OPTION_NAME_REDIRECT ],
273
+ [ 'redirect-rule', NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE ],
274
+ [ 'removeparam', NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM ],
275
+ [ 'replace', NODE_TYPE_NET_OPTION_NAME_REPLACE ],
276
+ /* synonym */ [ 'queryprune', NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM ],
277
+ [ 'script', NODE_TYPE_NET_OPTION_NAME_SCRIPT ],
278
+ [ 'shide', NODE_TYPE_NET_OPTION_NAME_SHIDE ],
279
+ /* synonym */ [ 'specifichide', NODE_TYPE_NET_OPTION_NAME_SHIDE ],
280
+ [ 'to', NODE_TYPE_NET_OPTION_NAME_TO ],
281
+ [ 'urlskip', NODE_TYPE_NET_OPTION_NAME_URLSKIP ],
282
+ [ 'uritransform', NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM ],
283
+ [ 'xhr', NODE_TYPE_NET_OPTION_NAME_XHR ],
284
+ /* synonym */ [ 'xmlhttprequest', NODE_TYPE_NET_OPTION_NAME_XHR ],
285
+ [ 'webrtc', NODE_TYPE_NET_OPTION_NAME_WEBRTC ],
286
+ [ 'websocket', NODE_TYPE_NET_OPTION_NAME_WEBSOCKET ],
287
+ ]);
288
+
289
+ export const nodeNameFromNodeType = new Map([
290
+ [ NODE_TYPE_NOOP, 'noop' ],
291
+ [ NODE_TYPE_LINE_RAW, 'lineRaw' ],
292
+ [ NODE_TYPE_LINE_BODY, 'lineBody' ],
293
+ [ NODE_TYPE_WHITESPACE, 'whitespace' ],
294
+ [ NODE_TYPE_COMMENT, 'comment' ],
295
+ [ NODE_TYPE_IGNORE, 'ignore' ],
296
+ [ NODE_TYPE_EXT_RAW, 'extRaw' ],
297
+ [ NODE_TYPE_EXT_OPTIONS_ANCHOR, 'extOptionsAnchor' ],
298
+ [ NODE_TYPE_EXT_OPTIONS, 'extOptions' ],
299
+ [ NODE_TYPE_EXT_DECORATION, 'extDecoration' ],
300
+ [ NODE_TYPE_EXT_PATTERN_RAW, 'extPatternRaw' ],
301
+ [ NODE_TYPE_EXT_PATTERN_COSMETIC, 'extPatternCosmetic' ],
302
+ [ NODE_TYPE_EXT_PATTERN_HTML, 'extPatternHtml' ],
303
+ [ NODE_TYPE_EXT_PATTERN_RESPONSEHEADER, 'extPatternResponseheader' ],
304
+ [ NODE_TYPE_EXT_PATTERN_SCRIPTLET, 'extPatternScriptlet' ],
305
+ [ NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN, 'extPatternScriptletToken' ],
306
+ [ NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARGS, 'extPatternScriptletArgs' ],
307
+ [ NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG, 'extPatternScriptletArg' ],
308
+ [ NODE_TYPE_NET_RAW, 'netRaw' ],
309
+ [ NODE_TYPE_NET_EXCEPTION, 'netException' ],
310
+ [ NODE_TYPE_NET_PATTERN_RAW, 'netPatternRaw' ],
311
+ [ NODE_TYPE_NET_PATTERN, 'netPattern' ],
312
+ [ NODE_TYPE_NET_PATTERN_PART, 'netPatternPart' ],
313
+ [ NODE_TYPE_NET_PATTERN_PART_SPECIAL, 'netPatternPartSpecial' ],
314
+ [ NODE_TYPE_NET_PATTERN_PART_UNICODE, 'netPatternPartUnicode' ],
315
+ [ NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR, 'netPatternLeftHnanchor' ],
316
+ [ NODE_TYPE_NET_PATTERN_LEFT_ANCHOR, 'netPatternLeftAnchor' ],
317
+ [ NODE_TYPE_NET_PATTERN_RIGHT_ANCHOR, 'netPatternRightAnchor' ],
318
+ [ NODE_TYPE_NET_OPTIONS_ANCHOR, 'netOptionsAnchor' ],
319
+ [ NODE_TYPE_NET_OPTIONS, 'netOptions' ],
320
+ [ NODE_TYPE_NET_OPTION_RAW, 'netOptionRaw' ],
321
+ [ NODE_TYPE_NET_OPTION_SEPARATOR, 'netOptionSeparator'],
322
+ [ NODE_TYPE_NET_OPTION_SENTINEL, 'netOptionSentinel' ],
323
+ [ NODE_TYPE_NET_OPTION_NAME_NOT, 'netOptionNameNot'],
324
+ [ NODE_TYPE_NET_OPTION_ASSIGN, 'netOptionAssign' ],
325
+ [ NODE_TYPE_NET_OPTION_VALUE, 'netOptionValue' ],
326
+ [ NODE_TYPE_OPTION_VALUE_DOMAIN_LIST, 'netOptionValueDomainList' ],
327
+ [ NODE_TYPE_OPTION_VALUE_DOMAIN_RAW, 'netOptionValueDomainRaw' ],
328
+ [ NODE_TYPE_OPTION_VALUE_NOT, 'netOptionValueNot' ],
329
+ [ NODE_TYPE_OPTION_VALUE_DOMAIN, 'netOptionValueDomain' ],
330
+ [ NODE_TYPE_OPTION_VALUE_SEPARATOR, 'netOptionsValueSeparator' ],
331
+ ]);
332
+ {
333
+ for ( const [ name, type ] of nodeTypeFromOptionName ) {
334
+ nodeNameFromNodeType.set(type, name);
335
+ }
336
+ }
337
+
338
+ /******************************************************************************/
339
+
340
+ // Local constants
341
+
342
+ const DOMAIN_CAN_USE_WILDCARD = 0b0000001;
343
+ const DOMAIN_CAN_USE_ENTITY = 0b0000010;
344
+ const DOMAIN_CAN_USE_SINGLE_WILDCARD = 0b0000100;
345
+ const DOMAIN_CAN_BE_NEGATED = 0b0001000;
346
+ const DOMAIN_CAN_BE_REGEX = 0b0010000;
347
+ const DOMAIN_CAN_BE_ANCESTOR = 0b0100000;
348
+ const DOMAIN_CAN_HAVE_PATH = 0b1000000;
349
+
350
+ const DOMAIN_FROM_FROMTO_LIST = DOMAIN_CAN_USE_ENTITY |
351
+ DOMAIN_CAN_BE_NEGATED |
352
+ DOMAIN_CAN_BE_REGEX;
353
+ const DOMAIN_FROM_DENYALLOW_LIST = 0;
354
+ const DOMAIN_FROM_EXT_LIST = DOMAIN_CAN_USE_ENTITY |
355
+ DOMAIN_CAN_USE_SINGLE_WILDCARD |
356
+ DOMAIN_CAN_BE_NEGATED |
357
+ DOMAIN_CAN_BE_REGEX |
358
+ DOMAIN_CAN_BE_ANCESTOR |
359
+ DOMAIN_CAN_HAVE_PATH;
360
+
361
+ /******************************************************************************/
362
+
363
+ // Precomputed AST layouts for most common filters.
364
+
365
+ const astTemplates = {
366
+ // ||example.com^
367
+ netHnAnchoredHostnameAscii: {
368
+ flags: AST_FLAG_NET_PATTERN_LEFT_HNANCHOR |
369
+ AST_FLAG_NET_PATTERN_RIGHT_PATHANCHOR,
370
+ type: NODE_TYPE_LINE_BODY,
371
+ beg: 0,
372
+ end: 0,
373
+ children: [{
374
+ type: NODE_TYPE_NET_RAW,
375
+ beg: 0,
376
+ end: 0,
377
+ children: [{
378
+ type: NODE_TYPE_NET_PATTERN_RAW,
379
+ beg: 0,
380
+ end: 0,
381
+ register: true,
382
+ children: [{
383
+ type: NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR,
384
+ beg: 0,
385
+ end: 2,
386
+ }, {
387
+ type: NODE_TYPE_NET_PATTERN,
388
+ beg: 2,
389
+ end: -1,
390
+ register: true,
391
+ }, {
392
+ type: NODE_TYPE_NET_PATTERN_PART_SPECIAL,
393
+ beg: -1,
394
+ end: 0,
395
+ }],
396
+ }],
397
+ }],
398
+ },
399
+ // ||example.com^$third-party
400
+ net3pHnAnchoredHostnameAscii: {
401
+ flags: AST_FLAG_NET_PATTERN_LEFT_HNANCHOR |
402
+ AST_FLAG_NET_PATTERN_RIGHT_PATHANCHOR |
403
+ AST_FLAG_HAS_OPTIONS,
404
+ type: NODE_TYPE_LINE_BODY,
405
+ beg: 0,
406
+ end: 0,
407
+ children: [{
408
+ type: NODE_TYPE_NET_RAW,
409
+ beg: 0,
410
+ end: 0,
411
+ children: [{
412
+ type: NODE_TYPE_NET_PATTERN_RAW,
413
+ beg: 0,
414
+ end: 0,
415
+ register: true,
416
+ children: [{
417
+ type: NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR,
418
+ beg: 0,
419
+ end: 2,
420
+ }, {
421
+ type: NODE_TYPE_NET_PATTERN,
422
+ beg: 2,
423
+ end: -13,
424
+ register: true,
425
+ }, {
426
+ type: NODE_TYPE_NET_PATTERN_PART_SPECIAL,
427
+ beg: -13,
428
+ end: -12,
429
+ }],
430
+ }, {
431
+ type: NODE_TYPE_NET_OPTIONS_ANCHOR,
432
+ beg: -12,
433
+ end: -11,
434
+ }, {
435
+ type: NODE_TYPE_NET_OPTIONS,
436
+ beg: -11,
437
+ end: 0,
438
+ register: true,
439
+ children: [{
440
+ type: NODE_TYPE_NET_OPTION_RAW,
441
+ beg: 0,
442
+ end: 0,
443
+ children: [{
444
+ type: NODE_TYPE_NET_OPTION_NAME_3P,
445
+ beg: 0,
446
+ end: 0,
447
+ register: true,
448
+ }],
449
+ }],
450
+ }],
451
+ }],
452
+ },
453
+ // ||example.com/path/to/resource
454
+ netHnAnchoredPlainAscii: {
455
+ flags: AST_FLAG_NET_PATTERN_LEFT_HNANCHOR,
456
+ type: NODE_TYPE_LINE_BODY,
457
+ beg: 0,
458
+ end: 0,
459
+ children: [{
460
+ type: NODE_TYPE_NET_RAW,
461
+ beg: 0,
462
+ end: 0,
463
+ children: [{
464
+ type: NODE_TYPE_NET_PATTERN_RAW,
465
+ beg: 0,
466
+ end: 0,
467
+ register: true,
468
+ children: [{
469
+ type: NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR,
470
+ beg: 0,
471
+ end: 2,
472
+ }, {
473
+ type: NODE_TYPE_NET_PATTERN,
474
+ beg: 2,
475
+ end: 0,
476
+ register: true,
477
+ }],
478
+ }],
479
+ }],
480
+ },
481
+ // example.com
482
+ // -resource.
483
+ netPlainAscii: {
484
+ type: NODE_TYPE_LINE_BODY,
485
+ beg: 0,
486
+ end: 0,
487
+ children: [{
488
+ type: NODE_TYPE_NET_RAW,
489
+ beg: 0,
490
+ end: 0,
491
+ children: [{
492
+ type: NODE_TYPE_NET_PATTERN_RAW,
493
+ beg: 0,
494
+ end: 0,
495
+ register: true,
496
+ children: [{
497
+ type: NODE_TYPE_NET_PATTERN,
498
+ beg: 0,
499
+ end: 0,
500
+ register: true,
501
+ }],
502
+ }],
503
+ }],
504
+ },
505
+ // 127.0.0.1 example.com
506
+ netHosts1: {
507
+ type: NODE_TYPE_LINE_BODY,
508
+ beg: 0,
509
+ end: 0,
510
+ children: [{
511
+ type: NODE_TYPE_NET_RAW,
512
+ beg: 0,
513
+ end: 0,
514
+ children: [{
515
+ type: NODE_TYPE_NET_PATTERN_RAW,
516
+ beg: 0,
517
+ end: 0,
518
+ register: true,
519
+ children: [{
520
+ type: NODE_TYPE_IGNORE,
521
+ beg: 0,
522
+ end: 10,
523
+ }, {
524
+ type: NODE_TYPE_NET_PATTERN,
525
+ beg: 10,
526
+ end: 0,
527
+ register: true,
528
+ }],
529
+ }],
530
+ }],
531
+ },
532
+ // 0.0.0.0 example.com
533
+ netHosts2: {
534
+ type: NODE_TYPE_LINE_BODY,
535
+ beg: 0,
536
+ end: 0,
537
+ children: [{
538
+ type: NODE_TYPE_NET_RAW,
539
+ beg: 0,
540
+ end: 0,
541
+ children: [{
542
+ type: NODE_TYPE_NET_PATTERN_RAW,
543
+ beg: 0,
544
+ end: 0,
545
+ register: true,
546
+ children: [{
547
+ type: NODE_TYPE_IGNORE,
548
+ beg: 0,
549
+ end: 8,
550
+ }, {
551
+ type: NODE_TYPE_NET_PATTERN,
552
+ beg: 8,
553
+ end: 0,
554
+ register: true,
555
+ }],
556
+ }],
557
+ }],
558
+ },
559
+ // ##.ads-container
560
+ extPlainGenericSelector: {
561
+ type: NODE_TYPE_LINE_BODY,
562
+ beg: 0,
563
+ end: 0,
564
+ children: [{
565
+ type: NODE_TYPE_EXT_RAW,
566
+ beg: 0,
567
+ end: 0,
568
+ children: [{
569
+ type: NODE_TYPE_EXT_OPTIONS_ANCHOR,
570
+ beg: 0,
571
+ end: 2,
572
+ register: true,
573
+ }, {
574
+ type: NODE_TYPE_EXT_PATTERN_RAW,
575
+ beg: 2,
576
+ end: 0,
577
+ register: true,
578
+ children: [{
579
+ type: NODE_TYPE_EXT_PATTERN_COSMETIC,
580
+ beg: 0,
581
+ end: 0,
582
+ }],
583
+ }],
584
+ }],
585
+ },
586
+ };
587
+
588
+ /******************************************************************************/
589
+
590
+ export const removableHTTPHeaders = new Set([
591
+ 'location',
592
+ 'refresh',
593
+ 'report-to',
594
+ 'set-cookie',
595
+ ]);
596
+
597
+ export const preparserIfTokens = new Set([
598
+ 'ext_ublock',
599
+ 'ext_ubol',
600
+ 'ext_devbuild',
601
+ 'env_chromium',
602
+ 'env_edge',
603
+ 'env_firefox',
604
+ 'env_legacy',
605
+ 'env_mobile',
606
+ 'env_mv3',
607
+ 'env_safari',
608
+ 'cap_html_filtering',
609
+ 'cap_ipaddress',
610
+ 'cap_user_stylesheet',
611
+ 'false',
612
+ 'ext_abp',
613
+ 'adguard',
614
+ 'adguard_app_android',
615
+ 'adguard_app_cli',
616
+ 'adguard_app_ios',
617
+ 'adguard_app_mac',
618
+ 'adguard_app_windows',
619
+ 'adguard_ext_android_cb',
620
+ 'adguard_ext_chromium',
621
+ 'adguard_ext_chromium_mv3',
622
+ 'adguard_ext_edge',
623
+ 'adguard_ext_firefox',
624
+ 'adguard_ext_opera',
625
+ 'adguard_ext_safari',
626
+ ]);
627
+
628
+ /******************************************************************************/
629
+
630
+ const exCharCodeAt = (s, i) => {
631
+ const pos = i >= 0 ? i : s.length + i;
632
+ return pos >= 0 ? s.charCodeAt(pos) : -1;
633
+ };
634
+
635
+ const escapeForRegex = s =>
636
+ s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
637
+
638
+ /******************************************************************************/
639
+
640
+ class AstWalker {
641
+ constructor(parser, from = 0) {
642
+ this.parser = parser;
643
+ this.stack = [];
644
+ this.reset(from);
645
+ }
646
+ get depth() {
647
+ return this.stackPtr;
648
+ }
649
+ reset(from = 0) {
650
+ this.nodes = this.parser.nodes;
651
+ this.stackPtr = 0;
652
+ return (this.current = from || this.parser.rootNode);
653
+ }
654
+ next() {
655
+ const current = this.current;
656
+ if ( current === 0 ) { return 0; }
657
+ const down = this.nodes[current+NODE_DOWN_INDEX];
658
+ if ( down !== 0 ) {
659
+ this.stack[this.stackPtr++] = this.current;
660
+ return (this.current = down);
661
+ }
662
+ const right = this.nodes[current+NODE_RIGHT_INDEX];
663
+ if ( right !== 0 && this.stackPtr !== 0 ) {
664
+ return (this.current = right);
665
+ }
666
+ while ( this.stackPtr !== 0 ) {
667
+ const parent = this.stack[--this.stackPtr];
668
+ const right = this.nodes[parent+NODE_RIGHT_INDEX];
669
+ if ( right !== 0 ) {
670
+ return (this.current = right);
671
+ }
672
+ }
673
+ return (this.current = 0);
674
+ }
675
+ right() {
676
+ const current = this.current;
677
+ if ( current === 0 ) { return 0; }
678
+ const right = this.nodes[current+NODE_RIGHT_INDEX];
679
+ if ( right !== 0 && this.stackPtr !== 0 ) {
680
+ return (this.current = right);
681
+ }
682
+ while ( this.stackPtr !== 0 ) {
683
+ const parent = this.stack[--this.stackPtr];
684
+ const right = this.nodes[parent+NODE_RIGHT_INDEX];
685
+ if ( right !== 0 ) {
686
+ return (this.current = right);
687
+ }
688
+ }
689
+ return (this.current = 0);
690
+ }
691
+ until(which) {
692
+ let node = this.next();
693
+ while ( node !== 0 ) {
694
+ if ( this.nodes[node+NODE_TYPE_INDEX] === which ) { return node; }
695
+ node = this.next();
696
+ }
697
+ return 0;
698
+ }
699
+ canGoDown() {
700
+ return this.nodes[this.current+NODE_DOWN_INDEX] !== 0;
701
+ }
702
+ dispose() {
703
+ this.parser.walkerJunkyard.push(this);
704
+ }
705
+ }
706
+
707
+ /******************************************************************************/
708
+
709
+ class DomainListIterator {
710
+ constructor(parser, root) {
711
+ this.parser = parser;
712
+ this.walker = parser.getWalker();
713
+ this.value = undefined;
714
+ this.item = { hn: '', not: false, bad: false };
715
+ this.reuse(root);
716
+ }
717
+ next() {
718
+ if ( this.done ) { return this.value; }
719
+ let node = this.walker.current;
720
+ let ready = false;
721
+ while ( node !== 0 ) {
722
+ switch ( this.parser.getNodeType(node) ) {
723
+ case NODE_TYPE_OPTION_VALUE_DOMAIN_RAW:
724
+ this.item.hn = '';
725
+ this.item.not = false;
726
+ this.item.bad = this.parser.getNodeFlags(node, NODE_FLAG_ERROR) !== 0;
727
+ break;
728
+ case NODE_TYPE_OPTION_VALUE_NOT:
729
+ this.item.not = true;
730
+ break;
731
+ case NODE_TYPE_OPTION_VALUE_DOMAIN:
732
+ this.item.hn = this.parser.getNodeTransform(node);
733
+ this.value = this.item;
734
+ ready = true;
735
+ break;
736
+ default:
737
+ break;
738
+ }
739
+ node = this.walker.next();
740
+ if ( ready ) { return this; }
741
+ }
742
+ return this.stop();
743
+ }
744
+ reuse(root) {
745
+ this.walker.reset(root);
746
+ this.done = false;
747
+ return this;
748
+ }
749
+ stop() {
750
+ this.done = true;
751
+ this.value = undefined;
752
+ this.parser.domainListIteratorJunkyard.push(this);
753
+ return this;
754
+ }
755
+ [Symbol.iterator]() {
756
+ return this;
757
+ }
758
+ }
759
+
760
+ /******************************************************************************/
761
+
762
+ export class AstFilterParser {
763
+ constructor(options = {}) {
764
+ this.raw = '';
765
+ this.rawEnd = 0;
766
+ this.nodes = new Uint32Array(16384);
767
+ this.nodePoolPtr = FULL_NODE_SIZE;
768
+ this.nodePoolEnd = this.nodes.length;
769
+ this.astTransforms = [ null ];
770
+ this.astTransformPtr = 1;
771
+ this.rootNode = 0;
772
+ this.astType = AST_TYPE_NONE;
773
+ this.astTypeFlavor = AST_TYPE_NONE;
774
+ this.astFlags = 0;
775
+ this.astError = 0;
776
+ this.nodeTypeRegister = [];
777
+ this.nodeTypeRegisterPtr = 0;
778
+ this.nodeTypeLookupTable = new Uint32Array(NODE_TYPE_COUNT);
779
+ this.punycoder = new URL('https://ublock0.invalid/');
780
+ this.domainListIteratorJunkyard = [];
781
+ this.walkerJunkyard = [];
782
+ this.hasWhitespace = false;
783
+ this.hasUnicode = false;
784
+ this.hasUppercase = false;
785
+ // Options
786
+ this.options = options;
787
+ this.interactive = options.interactive || false;
788
+ this.badTypes = new Set(options.badTypes || []);
789
+ this.maxTokenLength = options.maxTokenLength || 7;
790
+ // TODO: rethink this
791
+ this.result = { exception: false, raw: '', compiled: '', error: undefined };
792
+ this.selectorCompiler = new ExtSelectorCompiler(options);
793
+ // Regexes
794
+ this.reWhitespaceStart = /^\s+/;
795
+ this.reWhitespaceEnd = /(?:^|\S)(\s+)$/;
796
+ this.reCommentLine = /^(?:!|#\s|####|\[adblock)/i;
797
+ this.reExtAnchor = /(#@?(?:\$\?|\$|%|\?)?#).{1,2}/;
798
+ this.reInlineComment = /(?:\s+#).*?$/;
799
+ this.reNetException = /^@@/;
800
+ this.reNetAnchor = /(?:)\$[^,\w~]/;
801
+ this.reHnAnchoredPlainAscii = /^\|\|[0-9a-z%&,\-./:;=?_]+$/;
802
+ this.reHnAnchoredHostnameAscii = /^\|\|(?:[\da-z][\da-z_-]*\.)*[\da-z_-]*[\da-z]\^$/;
803
+ this.reHnAnchoredHostnameUnicode = /^\|\|(?:[\p{L}\p{N}][\p{L}\p{N}\u{2d}]*\.)*[\p{L}\p{N}\u{2d}]*[\p{L}\p{N}]\^$/u;
804
+ this.reHn3pAnchoredHostnameAscii = /^\|\|(?:[\da-z][\da-z_-]*\.)*[\da-z_-]*[\da-z]\^\$third-party$/;
805
+ this.rePlainAscii = /^[0-9a-z%&\-./:;=?_]{2,}$/;
806
+ this.reNetHosts1 = /^127\.0\.0\.1 (?:[\da-z][\da-z_-]*\.)+[\da-z-]*[a-z]$/;
807
+ this.reNetHosts2 = /^0\.0\.0\.0 (?:[\da-z][\da-z_-]*\.)+[\da-z-]*[a-z]$/;
808
+ this.rePlainGenericCosmetic = /^##[.#][A-Za-z_][\w-]*$/;
809
+ this.reHostnameAscii = /^(?:[\da-z][\da-z_-]*\.)*[\da-z][\da-z-]*[\da-z]$/;
810
+ this.rePlainEntity = /^(?:[\da-z][\da-z_-]*\.)+\*$/;
811
+ this.reHostsSink = /^[\w%.:[\]-]+\s+/;
812
+ this.reHostsRedirect = /(?:0\.0\.0\.0|broadcasthost|local|localhost(?:\.localdomain)?|ip6-\w+)(?:[^\w.-]|$)/;
813
+ this.reNetOptionComma = /,(?:~?[13a-z-]+(?:=.*?)?|_+)(?:,|$)/;
814
+ this.rePointlessLeftAnchor = /^\|\|?\*+/;
815
+ this.rePointlessLeadingWildcards = /^(\*+)[^%0-9A-Za-z\u{a0}-\u{10FFFF}]/u;
816
+ this.rePointlessTrailingSeparator = /\*(\^\**)$/;
817
+ this.rePointlessTrailingWildcards = /(?:[^%0-9A-Za-z]|[%0-9A-Za-z]{7,})(\*+)$/;
818
+ this.reHasWhitespaceChar = /\s/;
819
+ this.reHasUppercaseChar = /[A-Z]/;
820
+ this.reHasUnicodeChar = /[^\x00-\x7F]/;
821
+ this.reUnicodeChars = /\P{ASCII}/gu;
822
+ this.reBadHostnameChars = /[\x00-\x24\x26-\x29\x2b\x2c\x2f\x3b-\x40\x5c\x5e\x60\x7b-\x7f]/;
823
+ this.reIsEntity = /^[^*]+\.\*$/;
824
+ this.rePreparseDirectiveIf = /^!#if /;
825
+ this.rePreparseDirectiveAny = /^!#(?:else|endif|if |include )/;
826
+ this.reURL = /\bhttps?:\/\/\S+/;
827
+ this.reHasPatternSpecialChars = /[*^]/;
828
+ this.rePatternAllSpecialChars = /[*^]+|[^\x00-\x7f]+/g;
829
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/1146
830
+ // From https://codemirror.net/doc/manual.html#option_specialChars
831
+ this.reHasInvalidChar = /[\x00-\x1F\x7F-\x9F\xAD\u061C\u200B-\u200F\u2028\u2029\uFEFF\uFFF9-\uFFFC]/;
832
+ this.reHostnamePatternPart = /^[^\x00-\x24\x26-\x29\x2B\x2C\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F]+/;
833
+ this.reHostnameLabel = /[^.]+/g;
834
+ this.reResponseheaderPattern = /^\^responseheader\(.*\)$/;
835
+ this.rePatternScriptletJsonArgs = /^\{.*\}$/;
836
+ this.reBadCSP = /(?:^|[;,])\s*report-(?:to|uri)\b/i;
837
+ this.reBadPP = /(?:^|[;,])\s*report-to\b/i;
838
+ this.reNetOption = /^(~?)([134a-z_-]+)(=?)/;
839
+ this.reNoopOption = /^_+$/;
840
+ this.reAdvancedDomainSyntax = /^([^>]+?)(>>)?(\/.*)?$/;
841
+ this.netOptionValueParser = new ArglistParser(',');
842
+ this.scriptletArgListParser = new ArglistParser(',');
843
+ this.domainRegexValueParser = new ArglistParser('/');
844
+ }
845
+
846
+ finish() {
847
+ this.selectorCompiler.finish();
848
+ }
849
+
850
+ parse(raw) {
851
+ this.raw = raw;
852
+ this.rawEnd = raw.length;
853
+ this.nodePoolPtr = FULL_NODE_SIZE;
854
+ this.nodeTypeRegisterPtr = 0;
855
+ this.astTransformPtr = 1;
856
+ this.astType = AST_TYPE_NONE;
857
+ this.astTypeFlavor = AST_TYPE_NONE;
858
+ this.astFlags = 0;
859
+ this.astError = 0;
860
+ this.rootNode = this.allocTypedNode(NODE_TYPE_LINE_RAW, 0, this.rawEnd);
861
+ if ( this.rawEnd === 0 ) { return; }
862
+
863
+ // Fast-track very common simple filters using pre-computed AST layouts
864
+ // to skip parsing and validation.
865
+ const c1st = this.raw.charCodeAt(0);
866
+ const clast = exCharCodeAt(this.raw, -1);
867
+ if ( c1st === 0x7C /* | */ ) {
868
+ if (
869
+ clast === 0x5E /* ^ */ &&
870
+ this.reHnAnchoredHostnameAscii.test(this.raw)
871
+ ) {
872
+ // ||example.com^
873
+ this.astType = AST_TYPE_NETWORK;
874
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_HOSTNAME;
875
+ const node = this.astFromTemplate(this.rootNode,
876
+ astTemplates.netHnAnchoredHostnameAscii
877
+ );
878
+ this.linkDown(this.rootNode, node);
879
+ return;
880
+ }
881
+ if (
882
+ this.raw.endsWith('$third-party') &&
883
+ this.reHn3pAnchoredHostnameAscii.test(this.raw)
884
+ ) {
885
+ // ||example.com^$third-party
886
+ this.astType = AST_TYPE_NETWORK;
887
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_HOSTNAME;
888
+ const node = this.astFromTemplate(this.rootNode,
889
+ astTemplates.net3pHnAnchoredHostnameAscii
890
+ );
891
+ this.linkDown(this.rootNode, node);
892
+ return;
893
+ }
894
+ if ( this.reHnAnchoredPlainAscii.test(this.raw) ) {
895
+ // ||example.com/path/to/resource
896
+ this.astType = AST_TYPE_NETWORK;
897
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_PLAIN;
898
+ const node = this.astFromTemplate(this.rootNode,
899
+ astTemplates.netHnAnchoredPlainAscii
900
+ );
901
+ this.linkDown(this.rootNode, node);
902
+ return;
903
+ }
904
+ } else if ( c1st === 0x23 /* # */ ) {
905
+ if ( this.rePlainGenericCosmetic.test(this.raw) ) {
906
+ // ##.ads-container
907
+ this.astType = AST_TYPE_EXTENDED;
908
+ this.astTypeFlavor = AST_TYPE_EXTENDED_COSMETIC;
909
+ const node = this.astFromTemplate(this.rootNode,
910
+ astTemplates.extPlainGenericSelector
911
+ );
912
+ this.linkDown(this.rootNode, node);
913
+ this.result.exception = false;
914
+ this.result.raw = this.raw.slice(2);
915
+ this.result.compiled = this.raw.slice(2);
916
+ return;
917
+ }
918
+ } else if ( c1st === 0x31 /* 1 */ ) {
919
+ if ( this.reNetHosts1.test(this.raw) ) {
920
+ // 127.0.0.1 example.com
921
+ this.astType = AST_TYPE_NETWORK;
922
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_HOSTNAME;
923
+ const node = this.astFromTemplate(this.rootNode,
924
+ astTemplates.netHosts1
925
+ );
926
+ this.linkDown(this.rootNode, node);
927
+ return;
928
+ }
929
+ } else if ( c1st === 0x30 /* 0 */ ) {
930
+ if ( this.reNetHosts2.test(this.raw) ) {
931
+ // 0.0.0.0 example.com
932
+ this.astType = AST_TYPE_NETWORK;
933
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_HOSTNAME;
934
+ const node = this.astFromTemplate(this.rootNode,
935
+ astTemplates.netHosts2
936
+ );
937
+ this.linkDown(this.rootNode, node);
938
+ return;
939
+ }
940
+ } else if (
941
+ (c1st !== 0x2F /* / */ || clast !== 0x2F /* / */) &&
942
+ (this.rePlainAscii.test(this.raw))
943
+ ) {
944
+ // example.com
945
+ // -resource.
946
+ this.astType = AST_TYPE_NETWORK;
947
+ this.astTypeFlavor = this.reHostnameAscii.test(this.raw)
948
+ ? AST_TYPE_NETWORK_PATTERN_HOSTNAME
949
+ : AST_TYPE_NETWORK_PATTERN_PLAIN;
950
+ const node = this.astFromTemplate(this.rootNode,
951
+ astTemplates.netPlainAscii
952
+ );
953
+ this.linkDown(this.rootNode, node);
954
+ return;
955
+ }
956
+
957
+ // All else: full parsing and validation.
958
+ this.hasWhitespace = this.reHasWhitespaceChar.test(raw);
959
+ this.linkDown(this.rootNode, this.parseRaw(this.rootNode));
960
+ }
961
+
962
+ astFromTemplate(parent, template) {
963
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
964
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
965
+ const beg = template.beg + (template.beg >= 0 ? parentBeg : parentEnd);
966
+ const end = template.end + (template.end <= 0 ? parentEnd : parentBeg);
967
+ const node = this.allocTypedNode(template.type, beg, end);
968
+ if ( template.register ) {
969
+ this.addNodeToRegister(template.type, node);
970
+ }
971
+ if ( template.flags ) {
972
+ this.addFlags(template.flags);
973
+ }
974
+ if ( template.nodeFlags ) {
975
+ this.addNodeFlags(node, template.nodeFlags);
976
+ }
977
+ const children = template.children;
978
+ if ( children === undefined ) { return node; }
979
+ const head = this.astFromTemplate(node, children[0]);
980
+ this.linkDown(node, head);
981
+ const n = children.length;
982
+ if ( n === 1 ) { return node; }
983
+ let prev = head;
984
+ for ( let i = 1; i < n; i++ ) {
985
+ prev = this.linkRight(prev, this.astFromTemplate(node, children[i]));
986
+ }
987
+ return node;
988
+ }
989
+
990
+ getType() {
991
+ return this.astType;
992
+ }
993
+
994
+ isComment() {
995
+ return this.astType === AST_TYPE_COMMENT;
996
+ }
997
+
998
+ isFilter() {
999
+ return this.isNetworkFilter() || this.isExtendedFilter();
1000
+ }
1001
+
1002
+ isNetworkFilter() {
1003
+ return this.astType === AST_TYPE_NETWORK;
1004
+ }
1005
+
1006
+ isExtendedFilter() {
1007
+ return this.astType === AST_TYPE_EXTENDED;
1008
+ }
1009
+
1010
+ isCosmeticFilter() {
1011
+ return this.astType === AST_TYPE_EXTENDED &&
1012
+ this.astTypeFlavor === AST_TYPE_EXTENDED_COSMETIC;
1013
+ }
1014
+
1015
+ isScriptletFilter() {
1016
+ return this.astType === AST_TYPE_EXTENDED &&
1017
+ this.astTypeFlavor === AST_TYPE_EXTENDED_SCRIPTLET;
1018
+ }
1019
+
1020
+ isHtmlFilter() {
1021
+ return this.astType === AST_TYPE_EXTENDED &&
1022
+ this.astTypeFlavor === AST_TYPE_EXTENDED_HTML;
1023
+ }
1024
+
1025
+ isResponseheaderFilter() {
1026
+ return this.astType === AST_TYPE_EXTENDED &&
1027
+ this.astTypeFlavor === AST_TYPE_EXTENDED_RESPONSEHEADER;
1028
+ }
1029
+
1030
+ getFlags(flags = 0xFFFFFFFF) {
1031
+ return this.astFlags & flags;
1032
+ }
1033
+
1034
+ addFlags(flags) {
1035
+ this.astFlags |= flags;
1036
+ }
1037
+
1038
+ parseRaw(parent) {
1039
+ const head = this.allocHeadNode();
1040
+ let prev = head, next = 0;
1041
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1042
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
1043
+ const l1 = this.hasWhitespace
1044
+ ? this.leftWhitespaceCount(this.getNodeString(parent))
1045
+ : 0;
1046
+ if ( l1 !== 0 ) {
1047
+ next = this.allocTypedNode(
1048
+ NODE_TYPE_WHITESPACE,
1049
+ parentBeg,
1050
+ parentBeg + l1
1051
+ );
1052
+ prev = this.linkRight(prev, next);
1053
+ if ( l1 === parentEnd ) { return this.throwHeadNode(head); }
1054
+ }
1055
+ const r0 = this.hasWhitespace
1056
+ ? parentEnd - this.rightWhitespaceCount(this.getNodeString(parent))
1057
+ : parentEnd;
1058
+ if ( r0 !== l1 ) {
1059
+ next = this.allocTypedNode(
1060
+ NODE_TYPE_LINE_BODY,
1061
+ parentBeg + l1,
1062
+ parentBeg + r0
1063
+ );
1064
+ this.linkDown(next, this.parseFilter(next));
1065
+ prev = this.linkRight(prev, next);
1066
+ }
1067
+ if ( r0 !== parentEnd ) {
1068
+ next = this.allocTypedNode(
1069
+ NODE_TYPE_WHITESPACE,
1070
+ parentBeg + r0,
1071
+ parentEnd
1072
+ );
1073
+ this.linkRight(prev, next);
1074
+ }
1075
+ return this.throwHeadNode(head);
1076
+ }
1077
+
1078
+ parseFilter(parent) {
1079
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1080
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
1081
+ const parentStr = this.getNodeString(parent);
1082
+
1083
+ // A comment?
1084
+ if ( this.reCommentLine.test(parentStr) ) {
1085
+ const head = this.allocTypedNode(NODE_TYPE_COMMENT, parentBeg, parentEnd);
1086
+ this.astType = AST_TYPE_COMMENT;
1087
+ if ( this.interactive ) {
1088
+ this.linkDown(head, this.parseComment(head));
1089
+ }
1090
+ return head;
1091
+ }
1092
+
1093
+ // An extended filter? (or rarely, a comment)
1094
+ if ( this.reExtAnchor.test(parentStr) ) {
1095
+ const match = this.reExtAnchor.exec(parentStr);
1096
+ const matchLen = match[1].length;
1097
+ const head = this.allocTypedNode(NODE_TYPE_EXT_RAW, parentBeg, parentEnd);
1098
+ this.linkDown(head, this.parseExt(head, parentBeg + match.index, matchLen));
1099
+ return head;
1100
+ } else if ( parentStr.charCodeAt(0) === 0x23 /* # */ ) {
1101
+ const head = this.allocTypedNode(NODE_TYPE_COMMENT, parentBeg, parentEnd);
1102
+ this.astType = AST_TYPE_COMMENT;
1103
+ return head;
1104
+ }
1105
+
1106
+ // Good to know in advance to avoid costly tests later on
1107
+ this.hasUppercase = this.reHasUppercaseChar.test(parentStr);
1108
+ this.hasUnicode = this.reHasUnicodeChar.test(parentStr);
1109
+
1110
+ // A network filter (probably)
1111
+ this.astType = AST_TYPE_NETWORK;
1112
+
1113
+ // Parse inline comment if any
1114
+ let tail = 0, tailStart = parentEnd;
1115
+ if ( this.hasWhitespace && this.reInlineComment.test(parentStr) ) {
1116
+ const match = this.reInlineComment.exec(parentStr);
1117
+ tailStart = parentBeg + match.index;
1118
+ tail = this.allocTypedNode(NODE_TYPE_COMMENT, tailStart, parentEnd);
1119
+ }
1120
+
1121
+ const head = this.allocTypedNode(NODE_TYPE_NET_RAW, parentBeg, tailStart);
1122
+ if ( this.linkDown(head, this.parseNet(head)) === 0 ) {
1123
+ this.astType = AST_TYPE_UNKNOWN;
1124
+ this.addFlags(AST_FLAG_UNSUPPORTED | AST_FLAG_HAS_ERROR);
1125
+ }
1126
+ if ( tail !== 0 ) {
1127
+ this.linkRight(head, tail);
1128
+ }
1129
+ return head;
1130
+ }
1131
+
1132
+ parseComment(parent) {
1133
+ const parentStr = this.getNodeString(parent);
1134
+ if ( this.rePreparseDirectiveAny.test(parentStr) ) {
1135
+ this.astTypeFlavor = AST_TYPE_COMMENT_PREPARSER;
1136
+ return this.parsePreparseDirective(parent, parentStr);
1137
+ }
1138
+ if ( this.reURL.test(parentStr) === false ) { return 0; }
1139
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1140
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
1141
+ const match = this.reURL.exec(parentStr);
1142
+ const urlBeg = parentBeg + match.index;
1143
+ const urlEnd = urlBeg + match[0].length;
1144
+ const head = this.allocTypedNode(NODE_TYPE_COMMENT, parentBeg, urlBeg);
1145
+ let next = this.allocTypedNode(NODE_TYPE_COMMENT_URL, urlBeg, urlEnd);
1146
+ let prev = this.linkRight(head, next);
1147
+ if ( urlEnd !== parentEnd ) {
1148
+ next = this.allocTypedNode(NODE_TYPE_COMMENT, urlEnd, parentEnd);
1149
+ this.linkRight(prev, next);
1150
+ }
1151
+ return head;
1152
+ }
1153
+
1154
+ parsePreparseDirective(parent, s) {
1155
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1156
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
1157
+ const match = this.rePreparseDirectiveAny.exec(s);
1158
+ const directiveEnd = parentBeg + match[0].length;
1159
+ const head = this.allocTypedNode(
1160
+ NODE_TYPE_PREPARSE_DIRECTIVE,
1161
+ parentBeg,
1162
+ directiveEnd
1163
+ );
1164
+ if ( directiveEnd !== parentEnd ) {
1165
+ const type = s.startsWith('!#if ')
1166
+ ? NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE
1167
+ : NODE_TYPE_PREPARSE_DIRECTIVE_VALUE;
1168
+ const next = this.allocTypedNode(type, directiveEnd, parentEnd);
1169
+ this.addNodeToRegister(type, next);
1170
+ this.linkRight(head, next);
1171
+ if ( type === NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE ) {
1172
+ const rawToken = this.getNodeString(next).trim();
1173
+ if ( utils.preparser.evaluateExpr(rawToken) === undefined ) {
1174
+ this.addNodeFlags(next, NODE_FLAG_ERROR);
1175
+ this.addFlags(AST_FLAG_HAS_ERROR);
1176
+ this.astError = AST_ERROR_IF_TOKEN_UNKNOWN;
1177
+ }
1178
+ }
1179
+ }
1180
+ return head;
1181
+ }
1182
+
1183
+ // Very common, look into fast-tracking such plain pattern:
1184
+ // /^[^!#\$\*\^][^#\$\*\^]*[^\$\*\|]$/
1185
+ parseNet(parent) {
1186
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1187
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
1188
+ const parentStr = this.getNodeString(parent);
1189
+ const head = this.allocHeadNode();
1190
+ let patternBeg = parentBeg;
1191
+ let prev = head, next = 0, tail = 0;
1192
+ if ( this.reNetException.test(parentStr) ) {
1193
+ this.addFlags(AST_FLAG_IS_EXCEPTION);
1194
+ next = this.allocTypedNode(NODE_TYPE_NET_EXCEPTION, parentBeg, parentBeg+2);
1195
+ prev = this.linkRight(prev, next);
1196
+ patternBeg += 2;
1197
+ }
1198
+ let anchorBeg = this.indexOfNetAnchor(parentStr, patternBeg);
1199
+ if ( anchorBeg === -1 ) { return 0; }
1200
+ anchorBeg += parentBeg;
1201
+ if ( anchorBeg !== parentEnd ) {
1202
+ tail = this.allocTypedNode(
1203
+ NODE_TYPE_NET_OPTIONS_ANCHOR,
1204
+ anchorBeg,
1205
+ anchorBeg + 1
1206
+ );
1207
+ next = this.allocTypedNode(
1208
+ NODE_TYPE_NET_OPTIONS,
1209
+ anchorBeg + 1,
1210
+ parentEnd
1211
+ );
1212
+ this.addFlags(AST_FLAG_HAS_OPTIONS);
1213
+ this.addNodeToRegister(NODE_TYPE_NET_OPTIONS, next);
1214
+ this.linkDown(next, this.parseNetOptions(next));
1215
+ this.linkRight(tail, next);
1216
+ }
1217
+ next = this.allocTypedNode(
1218
+ NODE_TYPE_NET_PATTERN_RAW,
1219
+ patternBeg,
1220
+ anchorBeg
1221
+ );
1222
+ this.addNodeToRegister(NODE_TYPE_NET_PATTERN_RAW, next);
1223
+ this.linkDown(next, this.parseNetPattern(next));
1224
+ prev = this.linkRight(prev, next);
1225
+ if ( tail !== 0 ) {
1226
+ this.linkRight(prev, tail);
1227
+ }
1228
+ if ( this.astType === AST_TYPE_NETWORK ) {
1229
+ this.validateNet();
1230
+ }
1231
+ return this.throwHeadNode(head);
1232
+ }
1233
+
1234
+ validateNet() {
1235
+ const isException = this.isException();
1236
+ let bad = false, realBad = false;
1237
+ let abstractTypeCount = 0;
1238
+ let behaviorTypeCount = 0;
1239
+ let docTypeCount = 0;
1240
+ let modifierType = 0;
1241
+ let requestTypeCount = 0;
1242
+ let unredirectableTypeCount = 0;
1243
+ let badfilter = false;
1244
+ for ( let i = 0, n = this.nodeTypeRegisterPtr; i < n; i++ ) {
1245
+ const type = this.nodeTypeRegister[i];
1246
+ const targetNode = this.nodeTypeLookupTable[type];
1247
+ if ( targetNode === 0 ) { continue; }
1248
+ if ( this.badTypes.has(type) ) {
1249
+ this.addNodeFlags(NODE_FLAG_ERROR);
1250
+ this.addFlags(AST_FLAG_HAS_ERROR);
1251
+ this.astError = AST_ERROR_OPTION_EXCLUDED;
1252
+ }
1253
+ const flags = this.getNodeFlags(targetNode);
1254
+ if ( (flags & NODE_FLAG_ERROR) !== 0 ) { continue; }
1255
+ const isNegated = (flags & NODE_FLAG_IS_NEGATED) !== 0;
1256
+ const hasValue = (flags & NODE_FLAG_OPTION_HAS_VALUE) !== 0;
1257
+ bad = false; realBad = false;
1258
+ switch ( type ) {
1259
+ case NODE_TYPE_NET_OPTION_NAME_ALL:
1260
+ realBad = isNegated || hasValue || modifierType !== 0;
1261
+ break;
1262
+ case NODE_TYPE_NET_OPTION_NAME_1P:
1263
+ case NODE_TYPE_NET_OPTION_NAME_3P:
1264
+ realBad = hasValue;
1265
+ break;
1266
+ case NODE_TYPE_NET_OPTION_NAME_BADFILTER:
1267
+ badfilter = true;
1268
+ /* falls through */
1269
+ case NODE_TYPE_NET_OPTION_NAME_NOOP:
1270
+ realBad = isNegated || hasValue;
1271
+ break;
1272
+ case NODE_TYPE_NET_OPTION_NAME_CSS:
1273
+ case NODE_TYPE_NET_OPTION_NAME_FONT:
1274
+ case NODE_TYPE_NET_OPTION_NAME_IMAGE:
1275
+ case NODE_TYPE_NET_OPTION_NAME_MEDIA:
1276
+ case NODE_TYPE_NET_OPTION_NAME_OBJECT:
1277
+ case NODE_TYPE_NET_OPTION_NAME_OTHER:
1278
+ case NODE_TYPE_NET_OPTION_NAME_SCRIPT:
1279
+ case NODE_TYPE_NET_OPTION_NAME_XHR:
1280
+ realBad = hasValue;
1281
+ if ( realBad ) { break; }
1282
+ requestTypeCount += 1;
1283
+ break;
1284
+ case NODE_TYPE_NET_OPTION_NAME_CNAME:
1285
+ realBad = isException === false || isNegated || hasValue;
1286
+ if ( realBad ) { break; }
1287
+ modifierType = type;
1288
+ break;
1289
+ case NODE_TYPE_NET_OPTION_NAME_CSP:
1290
+ realBad = (hasValue || isException) === false ||
1291
+ modifierType !== 0 ||
1292
+ this.reBadCSP.test(
1293
+ this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_CSP)
1294
+ );
1295
+ if ( realBad ) { break; }
1296
+ modifierType = type;
1297
+ break;
1298
+ case NODE_TYPE_NET_OPTION_NAME_DENYALLOW:
1299
+ realBad = isNegated || hasValue === false ||
1300
+ this.getBranchFromType(NODE_TYPE_NET_OPTION_NAME_FROM) === 0;
1301
+ break;
1302
+ case NODE_TYPE_NET_OPTION_NAME_DOC:
1303
+ case NODE_TYPE_NET_OPTION_NAME_FRAME:
1304
+ realBad = hasValue;
1305
+ if ( realBad ) { break; }
1306
+ docTypeCount += 1;
1307
+ break;
1308
+ case NODE_TYPE_NET_OPTION_NAME_EHIDE:
1309
+ case NODE_TYPE_NET_OPTION_NAME_GHIDE:
1310
+ case NODE_TYPE_NET_OPTION_NAME_SHIDE:
1311
+ realBad = isNegated || hasValue || modifierType !== 0;
1312
+ if ( realBad ) { break; }
1313
+ behaviorTypeCount += 1;
1314
+ unredirectableTypeCount += 1;
1315
+ break;
1316
+ case NODE_TYPE_NET_OPTION_NAME_EMPTY:
1317
+ case NODE_TYPE_NET_OPTION_NAME_MP4:
1318
+ realBad = isNegated || hasValue || modifierType !== 0;
1319
+ if ( realBad ) { break; }
1320
+ modifierType = type;
1321
+ break;
1322
+ case NODE_TYPE_NET_OPTION_NAME_FROM:
1323
+ case NODE_TYPE_NET_OPTION_NAME_METHOD:
1324
+ case NODE_TYPE_NET_OPTION_NAME_TO:
1325
+ realBad = isNegated || hasValue === false;
1326
+ break;
1327
+ case NODE_TYPE_NET_OPTION_NAME_GENERICBLOCK:
1328
+ bad = true;
1329
+ realBad = isException === false || isNegated || hasValue;
1330
+ break;
1331
+ case NODE_TYPE_NET_OPTION_NAME_HEADER:
1332
+ realBad = isNegated || hasValue === false;
1333
+ break;
1334
+ case NODE_TYPE_NET_OPTION_NAME_IMPORTANT:
1335
+ realBad = isException || isNegated || hasValue;
1336
+ break;
1337
+ case NODE_TYPE_NET_OPTION_NAME_INLINEFONT:
1338
+ case NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT:
1339
+ realBad = hasValue;
1340
+ if ( realBad ) { break; }
1341
+ modifierType = type;
1342
+ unredirectableTypeCount += 1;
1343
+ break;
1344
+ case NODE_TYPE_NET_OPTION_NAME_IPADDRESS: {
1345
+ const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_IPADDRESS);
1346
+ if ( /^\/.+\/$/.test(value) ) {
1347
+ try { void new RegExp(value); }
1348
+ catch { realBad = true; }
1349
+ }
1350
+ break;
1351
+ }
1352
+ case NODE_TYPE_NET_OPTION_NAME_MATCHCASE:
1353
+ realBad = this.isRegexPattern() === false;
1354
+ break;
1355
+ case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
1356
+ realBad = modifierType !== 0 ||
1357
+ (hasValue || isException) === false ||
1358
+ this.reBadPP.test(
1359
+ this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_PERMISSIONS)
1360
+ );
1361
+ if ( realBad ) { break; }
1362
+ modifierType = type;
1363
+ break;
1364
+ case NODE_TYPE_NET_OPTION_NAME_PING:
1365
+ case NODE_TYPE_NET_OPTION_NAME_WEBSOCKET:
1366
+ realBad = hasValue;
1367
+ if ( realBad ) { break; }
1368
+ requestTypeCount += 1;
1369
+ unredirectableTypeCount += 1;
1370
+ break;
1371
+ case NODE_TYPE_NET_OPTION_NAME_POPUNDER:
1372
+ case NODE_TYPE_NET_OPTION_NAME_POPUP:
1373
+ realBad = hasValue;
1374
+ if ( realBad ) { break; }
1375
+ abstractTypeCount += 1;
1376
+ unredirectableTypeCount += 1;
1377
+ break;
1378
+ case NODE_TYPE_NET_OPTION_NAME_REASON:
1379
+ realBad = hasValue === false;
1380
+ break;
1381
+ case NODE_TYPE_NET_OPTION_NAME_REDIRECT:
1382
+ case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE:
1383
+ case NODE_TYPE_NET_OPTION_NAME_REPLACE:
1384
+ case NODE_TYPE_NET_OPTION_NAME_URLSKIP:
1385
+ case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM:
1386
+ realBad = isNegated || (isException || hasValue) === false ||
1387
+ modifierType !== 0;
1388
+ if ( realBad ) { break; }
1389
+ modifierType = type;
1390
+ break;
1391
+ case NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM:
1392
+ realBad = isNegated || modifierType !== 0;
1393
+ if ( realBad ) { break; }
1394
+ modifierType = type;
1395
+ break;
1396
+ case NODE_TYPE_NET_OPTION_NAME_STRICT1P:
1397
+ case NODE_TYPE_NET_OPTION_NAME_STRICT3P:
1398
+ realBad = isNegated || hasValue;
1399
+ break;
1400
+ case NODE_TYPE_NET_OPTION_NAME_UNKNOWN:
1401
+ this.astError = AST_ERROR_OPTION_UNKNOWN;
1402
+ realBad = true;
1403
+ break;
1404
+ case NODE_TYPE_NET_OPTION_NAME_WEBRTC:
1405
+ realBad = true;
1406
+ break;
1407
+ case NODE_TYPE_NET_PATTERN_RAW:
1408
+ realBad = this.hasOptions() === false &&
1409
+ this.getNetPattern().length <= 1;
1410
+ break;
1411
+ default:
1412
+ break;
1413
+ }
1414
+ if ( bad || realBad ) {
1415
+ this.addNodeFlags(targetNode, NODE_FLAG_ERROR);
1416
+ }
1417
+ if ( realBad ) {
1418
+ this.addFlags(AST_FLAG_HAS_ERROR);
1419
+ }
1420
+ }
1421
+ const requiresTrustedSource = ( ) =>
1422
+ this.options.trustedSource !== true &&
1423
+ isException === false && badfilter === false;
1424
+ switch ( modifierType ) {
1425
+ case NODE_TYPE_NET_OPTION_NAME_CNAME:
1426
+ realBad = abstractTypeCount || behaviorTypeCount || requestTypeCount;
1427
+ break;
1428
+ case NODE_TYPE_NET_OPTION_NAME_CSP:
1429
+ case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
1430
+ realBad = abstractTypeCount || behaviorTypeCount || requestTypeCount;
1431
+ break;
1432
+ case NODE_TYPE_NET_OPTION_NAME_INLINEFONT:
1433
+ case NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT:
1434
+ realBad = behaviorTypeCount;
1435
+ break;
1436
+ case NODE_TYPE_NET_OPTION_NAME_EMPTY:
1437
+ realBad = abstractTypeCount || behaviorTypeCount;
1438
+ break;
1439
+ case NODE_TYPE_NET_OPTION_NAME_MEDIA:
1440
+ case NODE_TYPE_NET_OPTION_NAME_MP4:
1441
+ realBad = abstractTypeCount || behaviorTypeCount || docTypeCount || requestTypeCount;
1442
+ break;
1443
+ case NODE_TYPE_NET_OPTION_NAME_REDIRECT:
1444
+ case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: {
1445
+ realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount;
1446
+ break;
1447
+ }
1448
+ case NODE_TYPE_NET_OPTION_NAME_REPLACE: {
1449
+ realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount;
1450
+ if ( realBad ) { break; }
1451
+ if ( requiresTrustedSource() ) {
1452
+ this.astError = AST_ERROR_UNTRUSTED_SOURCE;
1453
+ realBad = true;
1454
+ break;
1455
+ }
1456
+ const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_REPLACE);
1457
+ if ( parseReplaceValue(value) === undefined ) {
1458
+ this.astError = AST_ERROR_OPTION_BADVALUE;
1459
+ realBad = true;
1460
+ }
1461
+ break;
1462
+ }
1463
+ case NODE_TYPE_NET_OPTION_NAME_URLSKIP: {
1464
+ realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount;
1465
+ if ( realBad ) { break; }
1466
+ if ( requiresTrustedSource() ) {
1467
+ this.astError = AST_ERROR_UNTRUSTED_SOURCE;
1468
+ realBad = true;
1469
+ break;
1470
+ }
1471
+ const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_URLSKIP);
1472
+ if ( value.length < 2 ) {
1473
+ this.astError = AST_ERROR_OPTION_BADVALUE;
1474
+ realBad = true;
1475
+ }
1476
+ break;
1477
+ }
1478
+ case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: {
1479
+ realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount;
1480
+ if ( realBad ) { break; }
1481
+ if ( requiresTrustedSource() ) {
1482
+ this.astError = AST_ERROR_UNTRUSTED_SOURCE;
1483
+ realBad = true;
1484
+ break;
1485
+ }
1486
+ const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM);
1487
+ if ( value !== '' && parseReplaceByRegexValue(value) === undefined ) {
1488
+ this.astError = AST_ERROR_OPTION_BADVALUE;
1489
+ realBad = true;
1490
+ }
1491
+ break;
1492
+ }
1493
+ case NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM:
1494
+ realBad = abstractTypeCount || behaviorTypeCount;
1495
+ break;
1496
+ default:
1497
+ break;
1498
+ }
1499
+ if ( realBad ) {
1500
+ const targetNode = this.getBranchFromType(modifierType);
1501
+ this.addNodeFlags(targetNode, NODE_FLAG_ERROR);
1502
+ this.addFlags(AST_FLAG_HAS_ERROR);
1503
+ }
1504
+ }
1505
+
1506
+ indexOfNetAnchor(s, start = 0) {
1507
+ const end = s.length;
1508
+ if ( end === start ) { return end; }
1509
+ let j = s.lastIndexOf('$');
1510
+ if ( j === -1 ) { return end; }
1511
+ if ( (j+1) === end ) { return end; }
1512
+ for (;;) {
1513
+ const before = s.charAt(j-1);
1514
+ if ( before === '$' ) { return -1; }
1515
+ const after = s.charAt(j+1);
1516
+ if ( ')/|'.includes(after) === false ) {
1517
+ if ( before === '' || '"\'\\`'.includes(before) === false ) {
1518
+ return j;
1519
+ }
1520
+ }
1521
+ if ( j === start ) { break; }
1522
+ j = s.lastIndexOf('$', j-1);
1523
+ if ( j === -1 ) { break; }
1524
+ }
1525
+ return end;
1526
+ }
1527
+
1528
+ parseNetPattern(parent) {
1529
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1530
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
1531
+
1532
+ // Empty pattern
1533
+ if ( parentEnd === parentBeg ) {
1534
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_ANY;
1535
+ const node = this.allocTypedNode(
1536
+ NODE_TYPE_NET_PATTERN,
1537
+ parentBeg,
1538
+ parentEnd
1539
+ );
1540
+ this.addNodeToRegister(NODE_TYPE_NET_PATTERN, node);
1541
+ this.setNodeTransform(node, '*');
1542
+ return node;
1543
+ }
1544
+
1545
+ const head = this.allocHeadNode();
1546
+ let prev = head, next = 0, tail = 0;
1547
+ let pattern = this.getNodeString(parent);
1548
+ const hasWildcard = pattern.includes('*');
1549
+ const c1st = pattern.charCodeAt(0);
1550
+ const c2nd = pattern.charCodeAt(1) || 0;
1551
+ const clast = exCharCodeAt(pattern, -1);
1552
+
1553
+ // Common case: Easylist syntax-based hostname
1554
+ if (
1555
+ hasWildcard === false &&
1556
+ c1st === 0x7C /* | */ && c2nd === 0x7C /* | */ &&
1557
+ clast === 0x5E /* ^ */ &&
1558
+ this.isAdblockHostnamePattern(pattern)
1559
+ ) {
1560
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_HOSTNAME;
1561
+ this.addFlags(
1562
+ AST_FLAG_NET_PATTERN_LEFT_HNANCHOR |
1563
+ AST_FLAG_NET_PATTERN_RIGHT_PATHANCHOR
1564
+ );
1565
+ next = this.allocTypedNode(
1566
+ NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR,
1567
+ parentBeg,
1568
+ parentBeg + 2
1569
+ );
1570
+ prev = this.linkRight(prev, next);
1571
+ next = this.allocTypedNode(
1572
+ NODE_TYPE_NET_PATTERN,
1573
+ parentBeg + 2,
1574
+ parentEnd - 1
1575
+ );
1576
+ pattern = pattern.slice(2, -1);
1577
+ const normal = this.hasUnicode
1578
+ ? this.normalizeHostnameValue(pattern)
1579
+ : pattern;
1580
+ if ( normal !== undefined && normal !== pattern ) {
1581
+ this.setNodeTransform(next, normal);
1582
+ }
1583
+ this.addNodeToRegister(NODE_TYPE_NET_PATTERN, next);
1584
+ prev = this.linkRight(prev, next);
1585
+ next = this.allocTypedNode(
1586
+ NODE_TYPE_NET_PATTERN_PART_SPECIAL,
1587
+ parentEnd - 1,
1588
+ parentEnd
1589
+ );
1590
+ this.linkRight(prev, next);
1591
+ return this.throwHeadNode(head);
1592
+ }
1593
+
1594
+ let patternBeg = parentBeg;
1595
+ let patternEnd = parentEnd;
1596
+
1597
+ // Hosts file entry?
1598
+ if (
1599
+ this.hasWhitespace &&
1600
+ this.isException() === false &&
1601
+ this.hasOptions() === false &&
1602
+ this.reHostsSink.test(pattern)
1603
+ ) {
1604
+ const match = this.reHostsSink.exec(pattern);
1605
+ patternBeg += match[0].length;
1606
+ pattern = pattern.slice(patternBeg);
1607
+ next = this.allocTypedNode(NODE_TYPE_IGNORE, parentBeg, patternBeg);
1608
+ prev = this.linkRight(prev, next);
1609
+ if (
1610
+ this.reHostsRedirect.test(pattern) ||
1611
+ this.reHostnameAscii.test(pattern) === false
1612
+ ) {
1613
+ this.astType = AST_TYPE_NONE;
1614
+ this.addFlags(AST_FLAG_IGNORE);
1615
+ next = this.allocTypedNode(NODE_TYPE_IGNORE, patternBeg, parentEnd);
1616
+ prev = this.linkRight(prev, next);
1617
+ return this.throwHeadNode(head);
1618
+ }
1619
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_HOSTNAME;
1620
+ this.addFlags(
1621
+ AST_FLAG_NET_PATTERN_LEFT_HNANCHOR |
1622
+ AST_FLAG_NET_PATTERN_RIGHT_PATHANCHOR
1623
+ );
1624
+ next = this.allocTypedNode(
1625
+ NODE_TYPE_NET_PATTERN,
1626
+ patternBeg,
1627
+ parentEnd
1628
+ );
1629
+ this.addNodeToRegister(NODE_TYPE_NET_PATTERN, next);
1630
+ this.linkRight(prev, next);
1631
+ return this.throwHeadNode(head);
1632
+ }
1633
+
1634
+ // Regex?
1635
+ if (
1636
+ c1st === 0x2F /* / */ && clast === 0x2F /* / */ &&
1637
+ pattern.length > 2
1638
+ ) {
1639
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_REGEX;
1640
+ const normal = this.normalizeRegexPattern(pattern);
1641
+ next = this.allocTypedNode(
1642
+ NODE_TYPE_NET_PATTERN,
1643
+ patternBeg,
1644
+ patternEnd
1645
+ );
1646
+ this.addNodeToRegister(NODE_TYPE_NET_PATTERN, next);
1647
+ if ( normal !== '' ) {
1648
+ if ( normal !== pattern ) {
1649
+ this.setNodeTransform(next, normal);
1650
+ }
1651
+ } else {
1652
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_BAD;
1653
+ this.astError = AST_ERROR_REGEX;
1654
+ this.addFlags(AST_FLAG_HAS_ERROR);
1655
+ this.addNodeFlags(next, NODE_FLAG_ERROR);
1656
+ }
1657
+ this.linkRight(prev, next);
1658
+ return this.throwHeadNode(head);
1659
+ }
1660
+
1661
+ // Left anchor
1662
+ if ( c1st === 0x7C /* '|' */ ) {
1663
+ if ( c2nd === 0x7C /* '|' */ ) {
1664
+ const type = this.isTokenCharCode(pattern.charCodeAt(2) || 0)
1665
+ ? NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR
1666
+ : NODE_TYPE_IGNORE;
1667
+ next = this.allocTypedNode(type, patternBeg, patternBeg+2);
1668
+ if ( type === NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR ) {
1669
+ this.addFlags(AST_FLAG_NET_PATTERN_LEFT_HNANCHOR);
1670
+ }
1671
+ patternBeg += 2;
1672
+ pattern = pattern.slice(2);
1673
+ } else {
1674
+ const type = this.isTokenCharCode(c2nd)
1675
+ ? NODE_TYPE_NET_PATTERN_LEFT_ANCHOR
1676
+ : NODE_TYPE_IGNORE;
1677
+ next = this.allocTypedNode(type, patternBeg, patternBeg+1);
1678
+ if ( type === NODE_TYPE_NET_PATTERN_LEFT_ANCHOR ) {
1679
+ this.addFlags(AST_FLAG_NET_PATTERN_LEFT_ANCHOR);
1680
+ }
1681
+ patternBeg += 1;
1682
+ pattern = pattern.slice(1);
1683
+ }
1684
+ prev = this.linkRight(prev, next);
1685
+ if ( patternBeg === patternEnd ) {
1686
+ this.addNodeFlags(next, NODE_FLAG_IGNORE);
1687
+ }
1688
+ }
1689
+
1690
+ // Right anchor
1691
+ if ( exCharCodeAt(pattern, -1) === 0x7C /* | */ ) {
1692
+ const type = exCharCodeAt(pattern, -2) !== 0x2A /* * */
1693
+ ? NODE_TYPE_NET_PATTERN_RIGHT_ANCHOR
1694
+ : NODE_TYPE_IGNORE;
1695
+ tail = this.allocTypedNode(type, patternEnd-1, patternEnd);
1696
+ if ( type === NODE_TYPE_NET_PATTERN_RIGHT_ANCHOR ) {
1697
+ this.addFlags(AST_FLAG_NET_PATTERN_RIGHT_ANCHOR);
1698
+ }
1699
+ patternEnd -= 1;
1700
+ pattern = pattern.slice(0, -1);
1701
+ if ( patternEnd === patternBeg ) {
1702
+ this.addNodeFlags(tail, NODE_FLAG_IGNORE);
1703
+ }
1704
+ }
1705
+
1706
+ // Ignore pointless leading wildcards
1707
+ if ( hasWildcard && this.rePointlessLeadingWildcards.test(pattern) ) {
1708
+ const match = this.rePointlessLeadingWildcards.exec(pattern);
1709
+ const ignoreLen = match[1].length;
1710
+ next = this.allocTypedNode(
1711
+ NODE_TYPE_IGNORE,
1712
+ patternBeg,
1713
+ patternBeg + ignoreLen
1714
+ );
1715
+ prev = this.linkRight(prev, next);
1716
+ patternBeg += ignoreLen;
1717
+ pattern = pattern.slice(ignoreLen);
1718
+ }
1719
+
1720
+ // Ignore pointless trailing separators
1721
+ if ( this.rePointlessTrailingSeparator.test(pattern) ) {
1722
+ const match = this.rePointlessTrailingSeparator.exec(pattern);
1723
+ const ignoreLen = match[1].length;
1724
+ next = this.allocTypedNode(
1725
+ NODE_TYPE_IGNORE,
1726
+ patternEnd - ignoreLen,
1727
+ patternEnd
1728
+ );
1729
+ patternEnd -= ignoreLen;
1730
+ pattern = pattern.slice(0, -ignoreLen);
1731
+ if ( tail !== 0 ) { this.linkRight(next, tail); }
1732
+ tail = next;
1733
+ }
1734
+
1735
+ // Ignore pointless trailing wildcards. Exception: when removing the
1736
+ // trailing wildcard make the pattern look like a regex.
1737
+ if ( hasWildcard && this.rePointlessTrailingWildcards.test(pattern) ) {
1738
+ const match = this.rePointlessTrailingWildcards.exec(pattern);
1739
+ const ignoreLen = match[1].length;
1740
+ const needWildcard = pattern.charCodeAt(0) === 0x2F &&
1741
+ exCharCodeAt(pattern, -ignoreLen-1) === 0x2F;
1742
+ const goodWildcardBeg = patternEnd - ignoreLen;
1743
+ const badWildcardBeg = goodWildcardBeg + (needWildcard ? 1 : 0);
1744
+ if ( badWildcardBeg !== patternEnd ) {
1745
+ next = this.allocTypedNode(
1746
+ NODE_TYPE_IGNORE,
1747
+ badWildcardBeg,
1748
+ patternEnd
1749
+ );
1750
+ if ( tail !== 0 ) {this.linkRight(next, tail); }
1751
+ tail = next;
1752
+ }
1753
+ if ( goodWildcardBeg !== badWildcardBeg ) {
1754
+ next = this.allocTypedNode(
1755
+ NODE_TYPE_NET_PATTERN_PART_SPECIAL,
1756
+ goodWildcardBeg,
1757
+ badWildcardBeg
1758
+ );
1759
+ if ( tail !== 0 ) { this.linkRight(next, tail); }
1760
+ tail = next;
1761
+ }
1762
+ patternEnd -= ignoreLen;
1763
+ pattern = pattern.slice(0, -ignoreLen);
1764
+ }
1765
+
1766
+ const patternHasWhitespace = this.hasWhitespace &&
1767
+ this.reHasWhitespaceChar.test(pattern);
1768
+ const needNormalization = this.needPatternNormalization(pattern);
1769
+ const normal = needNormalization
1770
+ ? this.normalizePattern(pattern)
1771
+ : pattern;
1772
+ next = this.allocTypedNode(NODE_TYPE_NET_PATTERN, patternBeg, patternEnd);
1773
+ if ( patternHasWhitespace || normal === undefined ) {
1774
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_BAD;
1775
+ this.addFlags(AST_FLAG_HAS_ERROR);
1776
+ this.astError = AST_ERROR_PATTERN;
1777
+ this.addNodeFlags(next, NODE_FLAG_ERROR);
1778
+ } else if ( normal === '*' ) {
1779
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_ANY;
1780
+ } else if ( this.reHostnameAscii.test(normal) ) {
1781
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_HOSTNAME;
1782
+ } else if ( this.reHasPatternSpecialChars.test(normal) ) {
1783
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_GENERIC;
1784
+ } else {
1785
+ this.astTypeFlavor = AST_TYPE_NETWORK_PATTERN_PLAIN;
1786
+ }
1787
+ this.addNodeToRegister(NODE_TYPE_NET_PATTERN, next);
1788
+ if ( needNormalization && normal !== undefined ) {
1789
+ this.setNodeTransform(next, normal);
1790
+ }
1791
+ if ( this.interactive ) {
1792
+ this.linkDown(next, this.parsePatternParts(next, pattern));
1793
+ }
1794
+ prev = this.linkRight(prev, next);
1795
+
1796
+ if ( tail !== 0 ) {
1797
+ this.linkRight(prev, tail);
1798
+ }
1799
+ return this.throwHeadNode(head);
1800
+ }
1801
+
1802
+ isAdblockHostnamePattern(pattern) {
1803
+ if ( this.hasUnicode ) {
1804
+ return this.reHnAnchoredHostnameUnicode.test(pattern);
1805
+ }
1806
+ return this.reHnAnchoredHostnameAscii.test(pattern);
1807
+ }
1808
+
1809
+ parsePatternParts(parent, pattern) {
1810
+ if ( pattern.length === 0 ) { return 0; }
1811
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1812
+ const matches = pattern.matchAll(this.rePatternAllSpecialChars);
1813
+ const head = this.allocHeadNode();
1814
+ let prev = head, next = 0;
1815
+ let plainPartBeg = 0;
1816
+ for ( const match of matches ) {
1817
+ const plainPartEnd = match.index;
1818
+ if ( plainPartEnd !== plainPartBeg ) {
1819
+ next = this.allocTypedNode(
1820
+ NODE_TYPE_NET_PATTERN_PART,
1821
+ parentBeg + plainPartBeg,
1822
+ parentBeg + plainPartEnd
1823
+ );
1824
+ prev = this.linkRight(prev, next);
1825
+ }
1826
+ plainPartBeg = plainPartEnd + match[0].length;
1827
+ const type = match[0].charCodeAt(0) < 0x80
1828
+ ? NODE_TYPE_NET_PATTERN_PART_SPECIAL
1829
+ : NODE_TYPE_NET_PATTERN_PART_UNICODE;
1830
+ next = this.allocTypedNode(
1831
+ type,
1832
+ parentBeg + plainPartEnd,
1833
+ parentBeg + plainPartBeg
1834
+ );
1835
+ prev = this.linkRight(prev, next);
1836
+ }
1837
+ if ( plainPartBeg !== pattern.length ) {
1838
+ next = this.allocTypedNode(
1839
+ NODE_TYPE_NET_PATTERN_PART,
1840
+ parentBeg + plainPartBeg,
1841
+ parentBeg + pattern.length
1842
+ );
1843
+ this.linkRight(prev, next);
1844
+ }
1845
+ return this.throwHeadNode(head);
1846
+ }
1847
+
1848
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/1118#issuecomment-650730158
1849
+ // Be ready to deal with non-punycode-able Unicode characters.
1850
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/772
1851
+ // Encode Unicode characters beyond the hostname part.
1852
+ // Prepend with '*' character to prevent the browser API from refusing to
1853
+ // punycode -- this occurs when the extracted label starts with a dash.
1854
+ needPatternNormalization(pattern) {
1855
+ return pattern.length === 0 || this.hasUppercase || this.hasUnicode;
1856
+ }
1857
+
1858
+ normalizePattern(pattern) {
1859
+ if ( pattern.length === 0 ) { return '*'; }
1860
+ if ( this.reHasInvalidChar.test(pattern) ) { return; }
1861
+ let normal = pattern.toLowerCase();
1862
+ if ( this.hasUnicode === false ) { return normal; }
1863
+ // Punycode hostname part of the pattern.
1864
+ if ( this.reHostnamePatternPart.test(normal) ) {
1865
+ const match = this.reHostnamePatternPart.exec(normal);
1866
+ const hn = match[0].replace(this.reHostnameLabel, s => {
1867
+ if ( this.reHasUnicodeChar.test(s) === false ) { return s; }
1868
+ if ( s.charCodeAt(0) === 0x2D /* - */ ) { s = '*' + s; }
1869
+ return this.normalizeHostnameValue(s, DOMAIN_CAN_USE_WILDCARD) || s;
1870
+ });
1871
+ normal = hn + normal.slice(match.index + match[0].length);
1872
+ }
1873
+ if ( this.reHasUnicodeChar.test(normal) === false ) { return normal; }
1874
+ // Percent-encode remaining Unicode characters.
1875
+ try {
1876
+ normal = normal.replace(this.reUnicodeChars, s =>
1877
+ encodeURIComponent(s).toLowerCase()
1878
+ );
1879
+ } catch {
1880
+ return;
1881
+ }
1882
+ return normal;
1883
+ }
1884
+
1885
+ getNetPattern() {
1886
+ const node = this.nodeTypeLookupTable[NODE_TYPE_NET_PATTERN];
1887
+ return this.getNodeTransform(node);
1888
+ }
1889
+
1890
+ isAnyPattern() {
1891
+ return this.astTypeFlavor === AST_TYPE_NETWORK_PATTERN_ANY;
1892
+ }
1893
+
1894
+ isHostnamePattern() {
1895
+ return this.astTypeFlavor === AST_TYPE_NETWORK_PATTERN_HOSTNAME;
1896
+ }
1897
+
1898
+ isRegexPattern() {
1899
+ return this.astTypeFlavor === AST_TYPE_NETWORK_PATTERN_REGEX;
1900
+ }
1901
+
1902
+ isPlainPattern() {
1903
+ return this.astTypeFlavor === AST_TYPE_NETWORK_PATTERN_PLAIN;
1904
+ }
1905
+
1906
+ isGenericPattern() {
1907
+ return this.astTypeFlavor === AST_TYPE_NETWORK_PATTERN_GENERIC;
1908
+ }
1909
+
1910
+ isBadPattern() {
1911
+ return this.astTypeFlavor === AST_TYPE_NETWORK_PATTERN_BAD;
1912
+ }
1913
+
1914
+ parseNetOptions(parent) {
1915
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1916
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
1917
+ if ( parentEnd === parentBeg ) { return 0; }
1918
+ const s = this.getNodeString(parent);
1919
+ const optionsEnd = s.length;
1920
+ const parseDetails = { node: 0, len: 0 };
1921
+ const head = this.allocHeadNode();
1922
+ let prev = head, next = 0;
1923
+ let optionBeg = 0, optionEnd = 0;
1924
+ while ( optionBeg !== optionsEnd ) {
1925
+ next = this.allocTypedNode(
1926
+ NODE_TYPE_NET_OPTION_RAW,
1927
+ parentBeg + optionBeg,
1928
+ parentBeg + optionsEnd // open ended
1929
+ );
1930
+ this.parseNetOption(next, parseDetails);
1931
+ // set next's end to down's end
1932
+ optionEnd += parseDetails.len;
1933
+ this.nodes[next+NODE_END_INDEX] = parentBeg + optionEnd;
1934
+ this.linkDown(next, parseDetails.node);
1935
+ prev = this.linkRight(prev, next);
1936
+ if ( optionEnd === optionsEnd ) { break; }
1937
+ optionBeg = optionEnd + 1;
1938
+ next = this.allocTypedNode(
1939
+ NODE_TYPE_NET_OPTION_SEPARATOR,
1940
+ parentBeg + optionEnd,
1941
+ parentBeg + optionBeg
1942
+ );
1943
+ if ( parseDetails.len === 0 || optionBeg === optionsEnd ) {
1944
+ this.addNodeFlags(next, NODE_FLAG_ERROR);
1945
+ this.addFlags(AST_FLAG_HAS_ERROR);
1946
+ }
1947
+ prev = this.linkRight(prev, next);
1948
+ optionEnd = optionBeg;
1949
+ }
1950
+ this.linkRight(prev,
1951
+ this.allocSentinelNode(NODE_TYPE_NET_OPTION_SENTINEL, parentEnd)
1952
+ );
1953
+ return this.throwHeadNode(head);
1954
+ }
1955
+
1956
+ parseNetOption(parent, parseDetails) {
1957
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
1958
+ const s = this.getNodeString(parent);
1959
+ const match = this.reNetOption.exec(s) || [];
1960
+ if ( match.length === 0 ) {
1961
+ this.addNodeFlags(parent, NODE_FLAG_ERROR);
1962
+ this.addFlags(AST_FLAG_HAS_ERROR);
1963
+ this.astError = AST_ERROR_OPTION_UNKNOWN;
1964
+ parseDetails.node = 0;
1965
+ parseDetails.len = s.length;
1966
+ return;
1967
+ }
1968
+ const head = this.allocHeadNode();
1969
+ let prev = head, next = 0;
1970
+ const matchEnd = match && match[0].length || 0;
1971
+ const negated = match[1] === '~';
1972
+ if ( negated ) {
1973
+ this.addNodeFlags(parent, NODE_FLAG_IS_NEGATED);
1974
+ next = this.allocTypedNode(
1975
+ NODE_TYPE_NET_OPTION_NAME_NOT,
1976
+ parentBeg,
1977
+ parentBeg+1
1978
+ );
1979
+ prev = this.linkRight(prev, next);
1980
+ }
1981
+ const nameBeg = negated ? 1 : 0;
1982
+ const assigned = match[3] === '=';
1983
+ const nameEnd = matchEnd - (assigned ? 1 : 0);
1984
+ const name = match[2] || '';
1985
+ let nodeOptionType = nodeTypeFromOptionName.get(name);
1986
+ if ( nodeOptionType === undefined ) {
1987
+ nodeOptionType = this.reNoopOption.test(name)
1988
+ ? NODE_TYPE_NET_OPTION_NAME_NOOP
1989
+ : NODE_TYPE_NET_OPTION_NAME_UNKNOWN;
1990
+ }
1991
+ next = this.allocTypedNode(
1992
+ nodeOptionType,
1993
+ parentBeg + nameBeg,
1994
+ parentBeg + nameEnd
1995
+ );
1996
+ if (
1997
+ nodeOptionType !== NODE_TYPE_NET_OPTION_NAME_NOOP &&
1998
+ this.getBranchFromType(nodeOptionType) !== 0
1999
+ ) {
2000
+ this.addNodeFlags(parent, NODE_FLAG_ERROR);
2001
+ this.addFlags(AST_FLAG_HAS_ERROR);
2002
+ this.astError = AST_ERROR_OPTION_DUPLICATE;
2003
+ } else {
2004
+ this.addNodeToRegister(nodeOptionType, parent);
2005
+ }
2006
+ prev = this.linkRight(prev, next);
2007
+ if ( assigned === false ) {
2008
+ parseDetails.node = this.throwHeadNode(head);
2009
+ parseDetails.len = matchEnd;
2010
+ return;
2011
+ }
2012
+ next = this.allocTypedNode(
2013
+ NODE_TYPE_NET_OPTION_ASSIGN,
2014
+ parentBeg + matchEnd - 1,
2015
+ parentBeg + matchEnd
2016
+ );
2017
+ prev = this.linkRight(prev, next);
2018
+ this.addNodeFlags(parent, NODE_FLAG_OPTION_HAS_VALUE);
2019
+ const details = this.netOptionValueParser.nextArg(s, matchEnd);
2020
+ if ( details.quoteBeg !== details.argBeg ) {
2021
+ next = this.allocTypedNode(
2022
+ NODE_TYPE_NET_OPTION_QUOTE,
2023
+ parentBeg + details.quoteBeg,
2024
+ parentBeg + details.argBeg
2025
+ );
2026
+ prev = this.linkRight(prev, next);
2027
+ } else {
2028
+ const argEnd = this.endOfNetOption(s, matchEnd);
2029
+ if ( argEnd !== details.argEnd ) {
2030
+ details.argEnd = details.quoteEnd = argEnd;
2031
+ }
2032
+ }
2033
+ next = this.allocTypedNode(
2034
+ NODE_TYPE_NET_OPTION_VALUE,
2035
+ parentBeg + details.argBeg,
2036
+ parentBeg + details.argEnd
2037
+ );
2038
+ if ( details.argBeg === details.argEnd ) {
2039
+ this.addNodeFlags(parent, NODE_FLAG_ERROR);
2040
+ this.addFlags(AST_FLAG_HAS_ERROR);
2041
+ this.astError = AST_ERROR_OPTION_BADVALUE;
2042
+ } else if ( details.transform ) {
2043
+ const arg = s.slice(details.argBeg, details.argEnd);
2044
+ this.setNodeTransform(next, this.netOptionValueParser.normalizeArg(arg));
2045
+ }
2046
+ switch ( nodeOptionType ) {
2047
+ case NODE_TYPE_NET_OPTION_NAME_DENYALLOW:
2048
+ this.linkDown(next, this.parseDomainList(next, '|'), DOMAIN_FROM_DENYALLOW_LIST);
2049
+ break;
2050
+ case NODE_TYPE_NET_OPTION_NAME_FROM:
2051
+ case NODE_TYPE_NET_OPTION_NAME_TO:
2052
+ this.linkDown(next, this.parseDomainList(next, '|', DOMAIN_FROM_FROMTO_LIST));
2053
+ break;
2054
+ default:
2055
+ break;
2056
+ }
2057
+ prev = this.linkRight(prev, next);
2058
+ if ( details.quoteEnd !== details.argEnd ) {
2059
+ next = this.allocTypedNode(
2060
+ NODE_TYPE_NET_OPTION_QUOTE,
2061
+ parentBeg + details.argEnd,
2062
+ parentBeg + details.quoteEnd
2063
+ );
2064
+ this.linkRight(prev, next);
2065
+ }
2066
+ parseDetails.node = this.throwHeadNode(head);
2067
+ parseDetails.len = details.quoteEnd;
2068
+ }
2069
+
2070
+ endOfNetOption(s, beg) {
2071
+ const match = this.reNetOptionComma.exec(s.slice(beg));
2072
+ return match !== null ? beg + match.index : s.length;
2073
+ }
2074
+
2075
+ getNetOptionValue(type) {
2076
+ if ( this.nodeTypeRegister.includes(type) === false ) { return ''; }
2077
+ const optionNode = this.nodeTypeLookupTable[type];
2078
+ if ( optionNode === 0 ) { return ''; }
2079
+ const valueNode = this.findDescendantByType(optionNode, NODE_TYPE_NET_OPTION_VALUE);
2080
+ if ( valueNode === 0 ) { return ''; }
2081
+ return this.getNodeTransform(valueNode);
2082
+ }
2083
+
2084
+ parseDomainList(parent, separator, mode = 0) {
2085
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
2086
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
2087
+ const containerNode = this.allocTypedNode(
2088
+ NODE_TYPE_OPTION_VALUE_DOMAIN_LIST,
2089
+ parentBeg,
2090
+ parentEnd
2091
+ );
2092
+ if ( parentEnd === parentBeg ) { return containerNode; }
2093
+ const separatorCode = separator.charCodeAt(0);
2094
+ const parseDetails = { separator, mode, node: 0, len: 0 };
2095
+ const listNode = this.allocHeadNode();
2096
+ let prev = listNode;
2097
+ let domainNode = 0;
2098
+ let separatorNode = 0;
2099
+ const s = this.getNodeString(parent);
2100
+ const listEnd = s.length;
2101
+ let beg = 0, end = 0;
2102
+ while ( beg < listEnd ) {
2103
+ const next = this.allocTypedNode(
2104
+ NODE_TYPE_OPTION_VALUE_DOMAIN_RAW,
2105
+ parentBeg + beg,
2106
+ parentBeg + listEnd // open ended
2107
+ );
2108
+ this.parseDomain(next, parseDetails);
2109
+ end = beg + parseDetails.len;
2110
+ const badSeparator = end < listEnd && s.charCodeAt(end) !== separatorCode;
2111
+ if ( badSeparator ) {
2112
+ end = s.indexOf(separator, end);
2113
+ if ( end === -1 ) { end = listEnd; }
2114
+ }
2115
+ this.nodes[next+NODE_END_INDEX] = parentBeg + end;
2116
+ if ( end !== beg ) {
2117
+ domainNode = next;
2118
+ this.linkDown(domainNode, parseDetails.node);
2119
+ prev = this.linkRight(prev, domainNode);
2120
+ if ( badSeparator ) {
2121
+ this.addNodeFlags(domainNode, NODE_FLAG_ERROR);
2122
+ this.addFlags(AST_FLAG_HAS_ERROR);
2123
+ }
2124
+ } else {
2125
+ domainNode = 0;
2126
+ if ( separatorNode !== 0 ) {
2127
+ this.addNodeFlags(separatorNode, NODE_FLAG_ERROR);
2128
+ this.addFlags(AST_FLAG_HAS_ERROR);
2129
+ }
2130
+ }
2131
+ if ( s.charCodeAt(end) === separatorCode ) {
2132
+ beg = end;
2133
+ end += 1;
2134
+ separatorNode = this.allocTypedNode(
2135
+ NODE_TYPE_OPTION_VALUE_SEPARATOR,
2136
+ parentBeg + beg,
2137
+ parentBeg + end
2138
+ );
2139
+ prev = this.linkRight(prev, separatorNode);
2140
+ if ( domainNode === 0 ) {
2141
+ this.addNodeFlags(separatorNode, NODE_FLAG_ERROR);
2142
+ this.addFlags(AST_FLAG_HAS_ERROR);
2143
+ }
2144
+ } else {
2145
+ separatorNode = 0;
2146
+ }
2147
+ beg = end;
2148
+ }
2149
+ // Dangling separator node
2150
+ if ( separatorNode !== 0 ) {
2151
+ this.addNodeFlags(separatorNode, NODE_FLAG_ERROR);
2152
+ this.addFlags(AST_FLAG_HAS_ERROR);
2153
+ }
2154
+ this.linkDown(containerNode, this.throwHeadNode(listNode));
2155
+ return containerNode;
2156
+ }
2157
+
2158
+ parseDomain(parent, parseDetails) {
2159
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
2160
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
2161
+ const not = this.charCodeAt(parentBeg) === 0x7E /* ~ */;
2162
+ let head = 0, next = 0;
2163
+ let beg = parentBeg;
2164
+ if ( not ) {
2165
+ this.addNodeFlags(parent, NODE_FLAG_IS_NEGATED);
2166
+ head = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_NOT, beg, beg + 1);
2167
+ if ( (parseDetails.mode & DOMAIN_CAN_BE_NEGATED) === 0 ) {
2168
+ this.addNodeFlags(parent, NODE_FLAG_ERROR);
2169
+ }
2170
+ beg += 1;
2171
+ }
2172
+ const c0 = this.charCodeAt(beg);
2173
+ let end = beg;
2174
+ let isRegex = false;
2175
+ if ( c0 === 0x2F /* / */ ) {
2176
+ this.domainRegexValueParser.nextArg(this.raw, beg+1);
2177
+ end = this.domainRegexValueParser.separatorEnd;
2178
+ isRegex = true;
2179
+ } else if ( c0 === 0x5B /* [ */ && this.startsWith('[$domain=/', beg) ) {
2180
+ end = this.indexOf('/]', beg + 10, parentEnd);
2181
+ if ( end !== -1 ) { end += 2; }
2182
+ isRegex = true;
2183
+ } else {
2184
+ end = this.indexOf(parseDetails.separator, end, parentEnd);
2185
+ }
2186
+ if ( end === -1 ) { end = parentEnd; }
2187
+ if ( beg !== end ) {
2188
+ next = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_DOMAIN, beg, end);
2189
+ const hn = this.normalizeDomainValue(next, isRegex, parseDetails.mode);
2190
+ if ( hn !== undefined ) {
2191
+ if ( hn !== '' ) {
2192
+ this.setNodeTransform(next, hn);
2193
+ } else {
2194
+ this.addNodeFlags(parent, NODE_FLAG_ERROR);
2195
+ this.addFlags(AST_FLAG_HAS_ERROR);
2196
+ this.astError = AST_ERROR_DOMAIN_NAME;
2197
+ }
2198
+ }
2199
+ if ( head === 0 ) {
2200
+ head = next;
2201
+ } else {
2202
+ this.linkRight(head, next);
2203
+ }
2204
+ } else {
2205
+ this.addNodeFlags(parent, NODE_FLAG_ERROR);
2206
+ this.addFlags(AST_FLAG_HAS_ERROR);
2207
+ }
2208
+ parseDetails.node = head;
2209
+ parseDetails.len = end - parentBeg;
2210
+ }
2211
+
2212
+ normalizeDomainValue(node, isRegex, modeBits) {
2213
+ const raw = this.getNodeString(node);
2214
+ if ( isRegex ) {
2215
+ if ( (modeBits & DOMAIN_CAN_BE_REGEX) === 0 ) { return ''; }
2216
+ return this.normalizeDomainRegexValue(raw);
2217
+ }
2218
+ // Common: Assume plain hostname
2219
+ const r1 = this.normalizeHostnameValue(raw, modeBits);
2220
+ if ( r1 === undefined ) { return; }
2221
+ if ( r1 !== '' ) { return r1; }
2222
+ // Rare: Maybe advanced syntax is used
2223
+ const match = this.reAdvancedDomainSyntax.exec(raw);
2224
+ if ( match === null ) { return '' };
2225
+ const isAncestor = match[2] !== undefined;
2226
+ if ( isAncestor && (modeBits & DOMAIN_CAN_BE_ANCESTOR) === 0 ) { return ''; }
2227
+ const hasPath = match[3] !== undefined;
2228
+ if ( hasPath && (modeBits & DOMAIN_CAN_HAVE_PATH) === 0 ) { return ''; }
2229
+ if ( isAncestor && hasPath ) { return ''; }
2230
+ const r2 = this.normalizeHostnameValue(match[1], modeBits);
2231
+ if ( r2 === undefined ) { return; }
2232
+ if ( r2 === '' ) { return ''; }
2233
+ return `${r2}${match[2] ?? ''}${match[3] ?? ''}`;
2234
+ }
2235
+
2236
+ normalizeDomainRegexValue(before) {
2237
+ const regex = before.startsWith('[$domain=/')
2238
+ ? `${before.slice(9, -1)}`
2239
+ : before;
2240
+ const source = this.normalizeRegexPattern(regex);
2241
+ if ( source === '' ) { return ''; }
2242
+ const after = `/${source}/`;
2243
+ if ( after === before ) { return; }
2244
+ return after;
2245
+ }
2246
+
2247
+ parseExt(parent, anchorBeg, anchorLen) {
2248
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
2249
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
2250
+ const head = this.allocHeadNode();
2251
+ let prev = head, next = 0;
2252
+ this.astType = AST_TYPE_EXTENDED;
2253
+ this.addFlags(this.extFlagsFromAnchor(anchorBeg));
2254
+ if ( anchorBeg > parentBeg ) {
2255
+ next = this.allocTypedNode(
2256
+ NODE_TYPE_EXT_OPTIONS,
2257
+ parentBeg,
2258
+ anchorBeg
2259
+ );
2260
+ this.addFlags(AST_FLAG_HAS_OPTIONS);
2261
+ this.addNodeToRegister(NODE_TYPE_EXT_OPTIONS, next);
2262
+ const down = this.parseDomainList(next, ',', DOMAIN_FROM_EXT_LIST);
2263
+ this.linkDown(next, down);
2264
+ prev = this.linkRight(prev, next);
2265
+ }
2266
+ next = this.allocTypedNode(
2267
+ NODE_TYPE_EXT_OPTIONS_ANCHOR,
2268
+ anchorBeg,
2269
+ anchorBeg + anchorLen
2270
+ );
2271
+ this.addNodeToRegister(NODE_TYPE_EXT_OPTIONS_ANCHOR, next);
2272
+ prev = this.linkRight(prev, next);
2273
+ next = this.allocTypedNode(
2274
+ NODE_TYPE_EXT_PATTERN_RAW,
2275
+ anchorBeg + anchorLen,
2276
+ parentEnd
2277
+ );
2278
+ this.addNodeToRegister(NODE_TYPE_EXT_PATTERN_RAW, next);
2279
+ const down = this.parseExtPattern(next);
2280
+ if ( down !== 0 ) {
2281
+ this.linkDown(next, down);
2282
+ } else {
2283
+ this.addNodeFlags(next, NODE_FLAG_ERROR);
2284
+ this.addFlags(AST_FLAG_HAS_ERROR);
2285
+ }
2286
+ this.linkRight(prev, next);
2287
+ this.validateExt();
2288
+ return this.throwHeadNode(head);
2289
+ }
2290
+
2291
+ extFlagsFromAnchor(anchorBeg) {
2292
+ let c = this.charCodeAt(anchorBeg+1) ;
2293
+ if ( c === 0x23 /* # */ ) { return 0; }
2294
+ if ( c === 0x25 /* % */ ) { return AST_FLAG_EXT_SCRIPTLET_ADG; }
2295
+ if ( c === 0x3F /* ? */ ) { return AST_FLAG_EXT_STRONG; }
2296
+ if ( c === 0x24 /* $ */ ) {
2297
+ c = this.charCodeAt(anchorBeg+2);
2298
+ if ( c === 0x23 /* # */ ) { return AST_FLAG_EXT_STYLE; }
2299
+ if ( c === 0x3F /* ? */ ) {
2300
+ return AST_FLAG_EXT_STYLE | AST_FLAG_EXT_STRONG;
2301
+ }
2302
+ }
2303
+ if ( c === 0x40 /* @ */ ) {
2304
+ return AST_FLAG_IS_EXCEPTION | this.extFlagsFromAnchor(anchorBeg+1);
2305
+ }
2306
+ return AST_FLAG_UNSUPPORTED | AST_FLAG_HAS_ERROR;
2307
+ }
2308
+
2309
+ validateExt() {
2310
+ const isException = this.isException();
2311
+ let realBad = false;
2312
+ for ( let i = 0, n = this.nodeTypeRegisterPtr; i < n; i++ ) {
2313
+ const type = this.nodeTypeRegister[i];
2314
+ const targetNode = this.nodeTypeLookupTable[type];
2315
+ if ( targetNode === 0 ) { continue; }
2316
+ const flags = this.getNodeFlags(targetNode);
2317
+ if ( (flags & NODE_FLAG_ERROR) !== 0 ) { continue; }
2318
+ realBad = false;
2319
+ switch ( type ) {
2320
+ case NODE_TYPE_EXT_PATTERN_RESPONSEHEADER: {
2321
+ const pattern = this.getNodeString(targetNode);
2322
+ realBad =
2323
+ pattern !== '' && removableHTTPHeaders.has(pattern) === false ||
2324
+ pattern === '' && isException === false;
2325
+ break;
2326
+ }
2327
+ case NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN: {
2328
+ if ( this.interactive !== true ) { break; }
2329
+ if ( isException ) { break; }
2330
+ const { trustedSource, trustedScriptletTokens } = this.options;
2331
+ if ( trustedScriptletTokens instanceof Set === false ) { break; }
2332
+ const token = this.getNodeString(targetNode);
2333
+ if ( trustedScriptletTokens.has(token) && trustedSource !== true ) {
2334
+ this.astError = AST_ERROR_UNTRUSTED_SOURCE;
2335
+ realBad = true;
2336
+ }
2337
+ break;
2338
+ }
2339
+ default:
2340
+ break;
2341
+ }
2342
+ if ( realBad ) {
2343
+ this.addNodeFlags(targetNode, NODE_FLAG_ERROR);
2344
+ this.addFlags(AST_FLAG_HAS_ERROR);
2345
+ }
2346
+ }
2347
+ }
2348
+
2349
+ parseExtPattern(parent) {
2350
+ const c = this.charCodeAt(this.nodes[parent+NODE_BEG_INDEX]);
2351
+ // ##+js(...)
2352
+ if ( c === 0x2B /* + */ ) {
2353
+ const s = this.getNodeString(parent);
2354
+ if ( /^\+js\(.*\)$/.exec(s) !== null ) {
2355
+ this.astTypeFlavor = AST_TYPE_EXTENDED_SCRIPTLET;
2356
+ return this.parseExtPatternScriptlet(parent);
2357
+ }
2358
+ }
2359
+ // #%#//scriptlet(...)
2360
+ if ( this.getFlags(AST_FLAG_EXT_SCRIPTLET_ADG) ) {
2361
+ const s = this.getNodeString(parent);
2362
+ if ( /^\/\/scriptlet\(.*\)$/.exec(s) !== null ) {
2363
+ this.astTypeFlavor = AST_TYPE_EXTENDED_SCRIPTLET;
2364
+ return this.parseExtPatternScriptlet(parent);
2365
+ }
2366
+ return 0;
2367
+ }
2368
+ // ##^... | ##^responseheader(...)
2369
+ if ( c === 0x5E /* ^ */ ) {
2370
+ const s = this.getNodeString(parent);
2371
+ if ( this.reResponseheaderPattern.test(s) ) {
2372
+ this.astTypeFlavor = AST_TYPE_EXTENDED_RESPONSEHEADER;
2373
+ return this.parseExtPatternResponseheader(parent);
2374
+ }
2375
+ this.astTypeFlavor = AST_TYPE_EXTENDED_HTML;
2376
+ return this.parseExtPatternHtml(parent);
2377
+ }
2378
+ // ##...
2379
+ this.astTypeFlavor = AST_TYPE_EXTENDED_COSMETIC;
2380
+ return this.parseExtPatternCosmetic(parent);
2381
+ }
2382
+
2383
+ parseExtPatternScriptlet(parent) {
2384
+ const beg = this.nodes[parent+NODE_BEG_INDEX];
2385
+ const end = this.nodes[parent+NODE_END_INDEX];
2386
+ const s = this.getNodeString(parent);
2387
+ const rawArg0 = beg + (s.startsWith('+js') ? 4 : 12);
2388
+ const rawArg1 = end - 1;
2389
+ const head = this.allocTypedNode(NODE_TYPE_EXT_DECORATION, beg, rawArg0);
2390
+ let prev = head, next = 0;
2391
+ next = this.allocTypedNode(NODE_TYPE_EXT_PATTERN_SCRIPTLET, rawArg0, rawArg1);
2392
+ this.addNodeToRegister(NODE_TYPE_EXT_PATTERN_SCRIPTLET, next);
2393
+ this.linkDown(next, this.parseExtPatternScriptletArgs(next));
2394
+ prev = this.linkRight(prev, next);
2395
+ next = this.allocTypedNode(NODE_TYPE_EXT_DECORATION, rawArg1, end);
2396
+ this.linkRight(prev, next);
2397
+ return head;
2398
+ }
2399
+
2400
+ parseExtPatternScriptletArgs(parent) {
2401
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
2402
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
2403
+ if ( parentEnd === parentBeg ) { return 0; }
2404
+ const head = this.allocHeadNode();
2405
+ let prev = head, next = 0;
2406
+ const s = this.getNodeString(parent);
2407
+ const argsEnd = s.length;
2408
+ // token
2409
+ this.scriptletArgListParser.mustQuote =
2410
+ this.getFlags(AST_FLAG_EXT_SCRIPTLET_ADG) !== 0;
2411
+ const details = this.scriptletArgListParser.nextArg(s, 0);
2412
+ if ( details.argBeg > 0 ) {
2413
+ next = this.allocTypedNode(
2414
+ NODE_TYPE_EXT_DECORATION,
2415
+ parentBeg,
2416
+ parentBeg + details.argBeg
2417
+ );
2418
+ prev = this.linkRight(prev, next);
2419
+ }
2420
+ const token = s.slice(details.argBeg, details.argEnd);
2421
+ const tokenEnd = details.argEnd - (token.endsWith('.js') ? 3 : 0);
2422
+ next = this.allocTypedNode(
2423
+ NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN,
2424
+ parentBeg + details.argBeg,
2425
+ parentBeg + tokenEnd
2426
+ );
2427
+ this.addNodeToRegister(NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN, next);
2428
+ if ( details.failed ) {
2429
+ this.addNodeFlags(next, NODE_FLAG_ERROR);
2430
+ this.addFlags(AST_FLAG_HAS_ERROR);
2431
+ }
2432
+ prev = this.linkRight(prev, next);
2433
+ if ( tokenEnd < details.argEnd ) {
2434
+ next = this.allocTypedNode(
2435
+ NODE_TYPE_IGNORE,
2436
+ parentBeg + tokenEnd,
2437
+ parentBeg + details.argEnd
2438
+ );
2439
+ prev = this.linkRight(prev, next);
2440
+ }
2441
+ if ( details.quoteEnd < argsEnd ) {
2442
+ next = this.allocTypedNode(
2443
+ NODE_TYPE_EXT_DECORATION,
2444
+ parentBeg + details.argEnd,
2445
+ parentBeg + details.separatorEnd
2446
+ );
2447
+ prev = this.linkRight(prev, next);
2448
+ }
2449
+ // all args
2450
+ next = this.allocTypedNode(
2451
+ NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARGS,
2452
+ parentBeg + details.separatorEnd,
2453
+ parentBeg + argsEnd
2454
+ );
2455
+ this.linkDown(next, this.parseExtPatternScriptletArglist(next));
2456
+ this.linkRight(prev, next);
2457
+ return this.throwHeadNode(head);
2458
+ }
2459
+
2460
+ parseExtPatternScriptletArglist(parent) {
2461
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
2462
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
2463
+ if ( parentEnd === parentBeg ) { return 0; }
2464
+ const s = this.getNodeString(parent);
2465
+ const argsEnd = s.length;
2466
+ const head = this.allocHeadNode();
2467
+ let prev = head, next = 0;
2468
+ let decorationBeg = 0;
2469
+ let i = 0;
2470
+ for (;;) {
2471
+ const details = this.scriptletArgListParser.nextArg(s, i);
2472
+ if ( decorationBeg < details.argBeg ) {
2473
+ next = this.allocTypedNode(
2474
+ NODE_TYPE_EXT_DECORATION,
2475
+ parentBeg + decorationBeg,
2476
+ parentBeg + details.argBeg
2477
+ );
2478
+ prev = this.linkRight(prev, next);
2479
+ }
2480
+ if ( i === argsEnd ) { break; }
2481
+ next = this.allocTypedNode(
2482
+ NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG,
2483
+ parentBeg + details.argBeg,
2484
+ parentBeg + details.argEnd
2485
+ );
2486
+ if ( details.transform ) {
2487
+ const arg = s.slice(details.argBeg, details.argEnd);
2488
+ this.setNodeTransform(next,
2489
+ this.scriptletArgListParser.normalizeArg(arg)
2490
+ );
2491
+ }
2492
+ prev = this.linkRight(prev, next);
2493
+ if ( details.failed ) {
2494
+ this.addNodeFlags(next, NODE_FLAG_ERROR);
2495
+ this.addFlags(AST_FLAG_HAS_ERROR);
2496
+ }
2497
+ decorationBeg = details.argEnd;
2498
+ i = details.separatorEnd;
2499
+ }
2500
+ return this.throwHeadNode(head);
2501
+ }
2502
+
2503
+ getScriptletArgs() {
2504
+ const args = [];
2505
+ if ( this.isScriptletFilter() === false ) { return args; }
2506
+ const root = this.getBranchFromType(NODE_TYPE_EXT_PATTERN_SCRIPTLET);
2507
+ const walker = this.getWalker(root);
2508
+ for ( let node = walker.next(); node !== 0; node = walker.next() ) {
2509
+ switch ( this.getNodeType(node) ) {
2510
+ case NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN:
2511
+ case NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG:
2512
+ args.push(this.getNodeTransform(node));
2513
+ break;
2514
+ default:
2515
+ break;
2516
+ }
2517
+ }
2518
+ walker.dispose();
2519
+ return args;
2520
+ }
2521
+
2522
+ parseExtPatternResponseheader(parent) {
2523
+ const beg = this.nodes[parent+NODE_BEG_INDEX];
2524
+ const end = this.nodes[parent+NODE_END_INDEX];
2525
+ const s = this.getNodeString(parent);
2526
+ const rawArg0 = beg + 16;
2527
+ const rawArg1 = end - 1;
2528
+ const head = this.allocTypedNode(NODE_TYPE_EXT_DECORATION, beg, rawArg0);
2529
+ let prev = head, next = 0;
2530
+ const trimmedArg0 = rawArg0 + this.leftWhitespaceCount(s);
2531
+ const trimmedArg1 = rawArg1 - this.rightWhitespaceCount(s);
2532
+ if ( trimmedArg0 !== rawArg0 ) {
2533
+ next = this.allocTypedNode(NODE_TYPE_WHITESPACE, rawArg0, trimmedArg0);
2534
+ prev = this.linkRight(prev, next);
2535
+ }
2536
+ next = this.allocTypedNode(NODE_TYPE_EXT_PATTERN_RESPONSEHEADER, rawArg0, rawArg1);
2537
+ this.addNodeToRegister(NODE_TYPE_EXT_PATTERN_RESPONSEHEADER, next);
2538
+ if ( rawArg1 === rawArg0 && this.isException() === false ) {
2539
+ this.addNodeFlags(parent, NODE_FLAG_ERROR);
2540
+ this.addFlags(AST_FLAG_HAS_ERROR);
2541
+ }
2542
+ prev = this.linkRight(prev, next);
2543
+ if ( trimmedArg1 !== rawArg1 ) {
2544
+ next = this.allocTypedNode(NODE_TYPE_WHITESPACE, trimmedArg1, rawArg1);
2545
+ prev = this.linkRight(prev, next);
2546
+ }
2547
+ next = this.allocTypedNode(NODE_TYPE_EXT_DECORATION, rawArg1, end);
2548
+ this.linkRight(prev, next);
2549
+ return head;
2550
+ }
2551
+
2552
+ getResponseheaderName() {
2553
+ if ( this.isResponseheaderFilter() === false ) { return ''; }
2554
+ const root = this.getBranchFromType(NODE_TYPE_EXT_PATTERN_RESPONSEHEADER);
2555
+ return this.getNodeString(root);
2556
+ }
2557
+
2558
+ parseExtPatternHtml(parent) {
2559
+ const beg = this.nodes[parent+NODE_BEG_INDEX];
2560
+ const end = this.nodes[parent+NODE_END_INDEX];
2561
+ const head = this.allocTypedNode(NODE_TYPE_EXT_DECORATION, beg, beg + 1);
2562
+ let prev = head, next = 0;
2563
+ next = this.allocTypedNode(NODE_TYPE_EXT_PATTERN_HTML, beg + 1, end);
2564
+ this.linkRight(prev, next);
2565
+ if ( (this.hasOptions() || this.isException()) === false ) {
2566
+ this.addNodeFlags(parent, NODE_FLAG_ERROR);
2567
+ this.addFlags(AST_FLAG_HAS_ERROR);
2568
+ return head;
2569
+ }
2570
+ this.result.exception = this.isException();
2571
+ this.result.raw = this.getNodeString(next);
2572
+ this.result.compiled = undefined;
2573
+ const success = this.selectorCompiler.compile(
2574
+ this.result.raw,
2575
+ this.result, {
2576
+ asProcedural: this.getFlags(AST_FLAG_EXT_STRONG) !== 0
2577
+ }
2578
+ );
2579
+ if ( success !== true ) {
2580
+ this.addNodeFlags(next, NODE_FLAG_ERROR);
2581
+ this.addFlags(AST_FLAG_HAS_ERROR);
2582
+ }
2583
+ return head;
2584
+ }
2585
+
2586
+ parseExtPatternCosmetic(parent) {
2587
+ const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
2588
+ const parentEnd = this.nodes[parent+NODE_END_INDEX];
2589
+ const head = this.allocTypedNode(
2590
+ NODE_TYPE_EXT_PATTERN_COSMETIC,
2591
+ parentBeg,
2592
+ parentEnd
2593
+ );
2594
+ this.result.exception = this.isException();
2595
+ this.result.raw = this.getNodeString(head);
2596
+ this.result.compiled = undefined;
2597
+ const success = this.selectorCompiler.compile(
2598
+ this.result.raw,
2599
+ this.result, {
2600
+ asProcedural: this.getFlags(AST_FLAG_EXT_STRONG) !== 0,
2601
+ adgStyleSyntax: this.getFlags(AST_FLAG_EXT_STYLE) !== 0,
2602
+ }
2603
+ );
2604
+ if ( success !== true ) {
2605
+ this.addNodeFlags(head, NODE_FLAG_ERROR);
2606
+ this.addFlags(AST_FLAG_HAS_ERROR);
2607
+ }
2608
+ return head;
2609
+ }
2610
+
2611
+ hasError() {
2612
+ return (this.astFlags & AST_FLAG_HAS_ERROR) !== 0;
2613
+ }
2614
+
2615
+ isUnsupported() {
2616
+ return (this.astFlags & AST_FLAG_UNSUPPORTED) !== 0;
2617
+ }
2618
+
2619
+ hasOptions() {
2620
+ return (this.astFlags & AST_FLAG_HAS_OPTIONS) !== 0;
2621
+ }
2622
+
2623
+ isNegatedOption(type) {
2624
+ const node = this.nodeTypeLookupTable[type];
2625
+ const flags = this.nodes[node+NODE_FLAGS_INDEX];
2626
+ return (flags & NODE_FLAG_IS_NEGATED) !== 0;
2627
+ }
2628
+
2629
+ isException() {
2630
+ return (this.astFlags & AST_FLAG_IS_EXCEPTION) !== 0;
2631
+ }
2632
+
2633
+ isLeftHnAnchored() {
2634
+ return (this.astFlags & AST_FLAG_NET_PATTERN_LEFT_HNANCHOR) !== 0;
2635
+ }
2636
+
2637
+ isLeftAnchored() {
2638
+ return (this.astFlags & AST_FLAG_NET_PATTERN_LEFT_ANCHOR) !== 0;
2639
+ }
2640
+
2641
+ isRightAnchored() {
2642
+ return (this.astFlags & AST_FLAG_NET_PATTERN_RIGHT_ANCHOR) !== 0;
2643
+ }
2644
+
2645
+ linkRight(prev, next) {
2646
+ return (this.nodes[prev+NODE_RIGHT_INDEX] = next);
2647
+ }
2648
+
2649
+ linkDown(node, down) {
2650
+ return (this.nodes[node+NODE_DOWN_INDEX] = down);
2651
+ }
2652
+
2653
+ makeChain(nodes) {
2654
+ for ( let i = 1; i < nodes.length; i++ ) {
2655
+ this.nodes[nodes[i-1]+NODE_RIGHT_INDEX] = nodes[i];
2656
+ }
2657
+ return nodes[0];
2658
+ }
2659
+
2660
+ allocHeadNode() {
2661
+ const node = this.nodePoolPtr;
2662
+ this.nodePoolPtr += NOOP_NODE_SIZE;
2663
+ if ( this.nodePoolPtr > this.nodePoolEnd ) {
2664
+ this.growNodePool(this.nodePoolPtr);
2665
+ }
2666
+ this.nodes[node+NODE_RIGHT_INDEX] = 0;
2667
+ return node;
2668
+ }
2669
+
2670
+ throwHeadNode(head) {
2671
+ return this.nodes[head+NODE_RIGHT_INDEX];
2672
+ }
2673
+
2674
+ allocTypedNode(type, beg, end) {
2675
+ const node = this.nodePoolPtr;
2676
+ this.nodePoolPtr += FULL_NODE_SIZE;
2677
+ if ( this.nodePoolPtr > this.nodePoolEnd ) {
2678
+ this.growNodePool(this.nodePoolPtr);
2679
+ }
2680
+ this.nodes[node+NODE_RIGHT_INDEX] = 0;
2681
+ this.nodes[node+NODE_TYPE_INDEX] = type;
2682
+ this.nodes[node+NODE_DOWN_INDEX] = 0;
2683
+ this.nodes[node+NODE_BEG_INDEX] = beg;
2684
+ this.nodes[node+NODE_END_INDEX] = end;
2685
+ this.nodes[node+NODE_TRANSFORM_INDEX] = 0;
2686
+ this.nodes[node+NODE_FLAGS_INDEX] = 0;
2687
+ return node;
2688
+ }
2689
+
2690
+ allocSentinelNode(type, beg) {
2691
+ return this.allocTypedNode(type, beg, beg);
2692
+ }
2693
+
2694
+ growNodePool(min) {
2695
+ const oldSize = this.nodes.length;
2696
+ const newSize = (min + 16383) & ~16383;
2697
+ if ( newSize === oldSize ) { return; }
2698
+ const newArray = new Uint32Array(newSize);
2699
+ newArray.set(this.nodes);
2700
+ this.nodes = newArray;
2701
+ this.nodePoolEnd = newSize;
2702
+ }
2703
+
2704
+ getNodeTypes() {
2705
+ return this.nodeTypeRegister.slice(0, this.nodeTypeRegisterPtr);
2706
+ }
2707
+
2708
+ getNodeType(node) {
2709
+ return node !== 0 ? this.nodes[node+NODE_TYPE_INDEX] : 0;
2710
+ }
2711
+
2712
+ getNodeFlags(node, flags = 0xFFFFFFFF) {
2713
+ return this.nodes[node+NODE_FLAGS_INDEX] & flags;
2714
+ }
2715
+
2716
+ setNodeFlags(node, flags) {
2717
+ this.nodes[node+NODE_FLAGS_INDEX] = flags;
2718
+ }
2719
+
2720
+ addNodeFlags(node, flags) {
2721
+ if ( node === 0 ) { return; }
2722
+ this.nodes[node+NODE_FLAGS_INDEX] |= flags;
2723
+ }
2724
+
2725
+ removeNodeFlags(node, flags) {
2726
+ this.nodes[node+NODE_FLAGS_INDEX] &= ~flags;
2727
+ }
2728
+
2729
+ addNodeToRegister(type, node) {
2730
+ this.nodeTypeRegister[this.nodeTypeRegisterPtr++] = type;
2731
+ this.nodeTypeLookupTable[type] = node;
2732
+ }
2733
+
2734
+ getBranchFromType(type) {
2735
+ const ptr = this.nodeTypeRegisterPtr;
2736
+ if ( ptr === 0 ) { return 0; }
2737
+ return this.nodeTypeRegister.lastIndexOf(type, ptr-1) !== -1
2738
+ ? this.nodeTypeLookupTable[type]
2739
+ : 0;
2740
+ }
2741
+
2742
+ nodeIsEmptyString(node) {
2743
+ return this.nodes[node+NODE_END_INDEX] ===
2744
+ this.nodes[node+NODE_BEG_INDEX];
2745
+ }
2746
+
2747
+ getNodeString(node) {
2748
+ const beg = this.nodes[node+NODE_BEG_INDEX];
2749
+ const end = this.nodes[node+NODE_END_INDEX];
2750
+ if ( end === beg ) { return ''; }
2751
+ if ( beg === 0 && end === this.rawEnd ) {
2752
+ return this.raw;
2753
+ }
2754
+ return this.raw.slice(beg, end);
2755
+ }
2756
+
2757
+ getNodeStringBeg(node) {
2758
+ return this.nodes[node+NODE_BEG_INDEX];
2759
+ }
2760
+
2761
+ getNodeStringEnd(node) {
2762
+ return this.nodes[node+NODE_END_INDEX];
2763
+ }
2764
+
2765
+ getNodeStringLen(node) {
2766
+ if ( node === 0 ) { return ''; }
2767
+ return this.nodes[node+NODE_END_INDEX] - this.nodes[node+NODE_BEG_INDEX];
2768
+ }
2769
+
2770
+ isNodeTransformed(node) {
2771
+ return this.nodes[node+NODE_TRANSFORM_INDEX] !== 0;
2772
+ }
2773
+
2774
+ getNodeTransform(node) {
2775
+ if ( node === 0 ) { return ''; }
2776
+ const slot = this.nodes[node+NODE_TRANSFORM_INDEX];
2777
+ return slot !== 0 ? this.astTransforms[slot] : this.getNodeString(node);
2778
+ }
2779
+
2780
+ setNodeTransform(node, value) {
2781
+ const slot = this.astTransformPtr++;
2782
+ this.astTransforms[slot] = value;
2783
+ this.nodes[node+NODE_TRANSFORM_INDEX] = slot;
2784
+ }
2785
+
2786
+ getTypeString(type) {
2787
+ const node = this.getBranchFromType(type);
2788
+ if ( node === 0 ) { return; }
2789
+ return this.getNodeString(node);
2790
+ }
2791
+
2792
+ leftWhitespaceCount(s) {
2793
+ const match = this.reWhitespaceStart.exec(s);
2794
+ return match === null ? 0 : match[0].length;
2795
+ }
2796
+
2797
+ rightWhitespaceCount(s) {
2798
+ const match = this.reWhitespaceEnd.exec(s);
2799
+ return match === null ? 0 : match[1].length;
2800
+ }
2801
+
2802
+ nextCommaInCommaSeparatedListString(s, start) {
2803
+ const n = s.length;
2804
+ if ( n === 0 ) { return -1; }
2805
+ const ilastchar = n - 1;
2806
+ let i = start;
2807
+ while ( i < n ) {
2808
+ const c = s.charCodeAt(i);
2809
+ if ( c === 0x2C /* ',' */ ) { return i + 1; }
2810
+ if ( c === 0x5C /* '\\' */ ) {
2811
+ if ( i < ilastchar ) { i += 1; }
2812
+ }
2813
+ }
2814
+ return -1;
2815
+ }
2816
+
2817
+ endOfLiteralRegex(s, start) {
2818
+ const n = s.length;
2819
+ if ( n === 0 ) { return -1; }
2820
+ const ilastchar = n - 1;
2821
+ let i = start + 1;
2822
+ while ( i < n ) {
2823
+ const c = s.charCodeAt(i);
2824
+ if ( c === 0x2F /* '/' */ ) { return i + 1; }
2825
+ if ( c === 0x5C /* '\\' */ ) {
2826
+ if ( i < ilastchar ) { i += 1; }
2827
+ }
2828
+ i += 1;
2829
+ }
2830
+ return -1;
2831
+ }
2832
+
2833
+ charCodeAt(pos) {
2834
+ return pos < this.rawEnd ? this.raw.charCodeAt(pos) : -1;
2835
+ }
2836
+
2837
+ indexOf(needle, beg, end = 0) {
2838
+ const haystack = end === 0 ? this.raw : this.raw.slice(0, end);
2839
+ return haystack.indexOf(needle, beg);
2840
+ }
2841
+
2842
+ startsWith(s, pos) {
2843
+ return pos < this.rawEnd && this.raw.startsWith(s, pos);
2844
+ }
2845
+
2846
+ isTokenCharCode(c) {
2847
+ return c === 0x25 ||
2848
+ c >= 0x30 && c <= 0x39 ||
2849
+ c >= 0x41 && c <= 0x5A ||
2850
+ c >= 0x61 && c <= 0x7A;
2851
+ }
2852
+
2853
+ // Ultimately, let the browser API do the hostname normalization, after
2854
+ // making some other trivial checks.
2855
+ //
2856
+ // returns:
2857
+ // undefined: no normalization needed, use original hostname
2858
+ // empty string: hostname is invalid
2859
+ // non-empty string: normalized hostname
2860
+ normalizeHostnameValue(s, modeBits = 0) {
2861
+ if ( this.reHostnameAscii.test(s) ) { return; }
2862
+ if ( this.reBadHostnameChars.test(s) ) { return ''; }
2863
+ let hn = s;
2864
+ const hasWildcard = hn.includes('*');
2865
+ if ( hasWildcard ) {
2866
+ if ( modeBits === 0 ) { return ''; }
2867
+ if ( hn.length === 1 ) {
2868
+ if ( (modeBits & DOMAIN_CAN_USE_SINGLE_WILDCARD) === 0 ) { return ''; }
2869
+ return;
2870
+ }
2871
+ if ( (modeBits & DOMAIN_CAN_USE_ENTITY) !== 0 ) {
2872
+ if ( this.rePlainEntity.test(hn) ) { return; }
2873
+ if ( this.reIsEntity.test(hn) === false ) { return ''; }
2874
+ } else if ( (modeBits & DOMAIN_CAN_USE_WILDCARD) === 0 ) {
2875
+ return '';
2876
+ }
2877
+ hn = hn.replace(/\*/g, '__asterisk__');
2878
+ }
2879
+ this.punycoder.hostname = '_';
2880
+ try {
2881
+ this.punycoder.hostname = hn;
2882
+ hn = this.punycoder.hostname;
2883
+ } catch {
2884
+ return '';
2885
+ }
2886
+ if ( hn === '_' || hn === '' ) { return ''; }
2887
+ if ( hasWildcard ) {
2888
+ hn = this.punycoder.hostname.replace(/__asterisk__/g, '*');
2889
+ }
2890
+ if (
2891
+ (modeBits & DOMAIN_CAN_USE_WILDCARD) === 0 && (
2892
+ hn.charCodeAt(0) === 0x2E /* . */ ||
2893
+ exCharCodeAt(hn, -1) === 0x2E /* . */
2894
+ )
2895
+ ) {
2896
+ return '';
2897
+ }
2898
+ return hn;
2899
+ }
2900
+
2901
+ normalizeRegexPattern(s) {
2902
+ try {
2903
+ const source = /^\/.+\/$/.test(s) ? s.slice(1,-1) : s;
2904
+ const regex = new RegExp(source);
2905
+ return regex.source;
2906
+ } catch (ex) {
2907
+ this.normalizeRegexPattern.message = ex.toString();
2908
+ }
2909
+ return '';
2910
+ }
2911
+
2912
+ getDomainListIterator(root) {
2913
+ const iter = this.domainListIteratorJunkyard.length !== 0
2914
+ ? this.domainListIteratorJunkyard.pop().reuse(root)
2915
+ : new DomainListIterator(this, root);
2916
+ return root !== 0 ? iter : iter.stop();
2917
+ }
2918
+
2919
+ getNetFilterFromOptionIterator() {
2920
+ return this.getDomainListIterator(
2921
+ this.getBranchFromType(NODE_TYPE_NET_OPTION_NAME_FROM)
2922
+ );
2923
+ }
2924
+
2925
+ getNetFilterToOptionIterator() {
2926
+ return this.getDomainListIterator(
2927
+ this.getBranchFromType(NODE_TYPE_NET_OPTION_NAME_TO)
2928
+ );
2929
+ }
2930
+
2931
+ getNetFilterDenyallowOptionIterator() {
2932
+ return this.getDomainListIterator(
2933
+ this.getBranchFromType(NODE_TYPE_NET_OPTION_NAME_DENYALLOW)
2934
+ );
2935
+ }
2936
+
2937
+ getExtFilterDomainIterator() {
2938
+ return this.getDomainListIterator(
2939
+ this.getBranchFromType(NODE_TYPE_EXT_OPTIONS)
2940
+ );
2941
+ }
2942
+
2943
+ getWalker(from) {
2944
+ if ( this.walkerJunkyard.length === 0 ) {
2945
+ return new AstWalker(this, from);
2946
+ }
2947
+ const walker = this.walkerJunkyard.pop();
2948
+ walker.reset(from);
2949
+ return walker;
2950
+ }
2951
+
2952
+ findDescendantByType(from, type) {
2953
+ const walker = this.getWalker(from);
2954
+ let node = walker.next();
2955
+ while ( node !== 0 ) {
2956
+ if ( this.getNodeType(node) === type ) { return node; }
2957
+ node = walker.next();
2958
+ }
2959
+ return 0;
2960
+ }
2961
+
2962
+ dump() {
2963
+ if ( this.astType === AST_TYPE_COMMENT ) { return; }
2964
+ const walker = this.getWalker();
2965
+ for ( let node = walker.reset(); node !== 0; node = walker.next() ) {
2966
+ const type = this.nodes[node+NODE_TYPE_INDEX];
2967
+ const value = this.getNodeString(node);
2968
+ const name = nodeNameFromNodeType.get(type) || `${type}`;
2969
+ const bits = this.getNodeFlags(node).toString(2).padStart(4, '0');
2970
+ const indent = ' '.repeat(walker.depth);
2971
+ console.log(`${indent}type=${name} "${value}" 0b${bits}`);
2972
+ if ( this.isNodeTransformed(node) ) {
2973
+ console.log(`${indent} transform="${this.getNodeTransform(node)}`);
2974
+ }
2975
+ }
2976
+ }
2977
+ }
2978
+
2979
+ /******************************************************************************/
2980
+
2981
+ export function parseRedirectValue(arg) {
2982
+ let token = arg.trim();
2983
+ let priority = 0;
2984
+ const asDataURI = token.charCodeAt(0) === 0x25 /* '%' */;
2985
+ if ( asDataURI ) { token = token.slice(1); }
2986
+ const match = /:-?\d+$/.exec(token);
2987
+ if ( match !== null ) {
2988
+ priority = parseInt(token.slice(match.index + 1), 10);
2989
+ token = token.slice(0, match.index);
2990
+ }
2991
+ return { token, priority, asDataURI };
2992
+ }
2993
+
2994
+ export function parseQueryPruneValue(arg) {
2995
+ let s = arg.trim();
2996
+ if ( s === '' ) { return { all: true }; }
2997
+ const out = { };
2998
+ out.not = s.charCodeAt(0) === 0x7E /* '~' */;
2999
+ if ( out.not ) {
3000
+ s = s.slice(1);
3001
+ }
3002
+ const match = /^\/(.+)\/(i)?$/.exec(s);
3003
+ if ( match !== null ) {
3004
+ try {
3005
+ out.re = new RegExp(match[1], match[2] || '');
3006
+ }
3007
+ catch {
3008
+ out.bad = true;
3009
+ }
3010
+ return out;
3011
+ }
3012
+ // TODO: remove once no longer used in filter lists
3013
+ if ( s.startsWith('|') ) {
3014
+ try {
3015
+ out.re = new RegExp('^' + s.slice(1), 'i');
3016
+ } catch {
3017
+ out.bad = true;
3018
+ }
3019
+ return out;
3020
+ }
3021
+ // Multiple values not supported (because very inefficient)
3022
+ if ( s.includes('|') ) {
3023
+ out.bad = true;
3024
+ return out;
3025
+ }
3026
+ out.name = s;
3027
+ return out;
3028
+ }
3029
+
3030
+ export function parseHeaderValue(arg) {
3031
+ let s = arg.trim();
3032
+ const out = { };
3033
+ let pos = s.indexOf(':');
3034
+ if ( pos === -1 ) { pos = s.length; }
3035
+ out.name = s.slice(0, pos).toLowerCase();
3036
+ out.bad = out.name === '';
3037
+ s = s.slice(pos + 1);
3038
+ out.not = s.charCodeAt(0) === 0x7E /* '~' */;
3039
+ if ( out.not ) { s = s.slice(1); }
3040
+ out.value = s;
3041
+ if ( s === '' ) { return out; }
3042
+ const match = /^\/(.+)\/(i)?$/.exec(s);
3043
+ out.isRegex = match !== null;
3044
+ if ( out.isRegex ) {
3045
+ out.reStr = match[1];
3046
+ out.reFlags = match[2] || '';
3047
+ try { new RegExp(out.reStr, out.reFlags); }
3048
+ catch { out.bad = true; }
3049
+ return out;
3050
+ }
3051
+ out.reFlags = 'i';
3052
+ if ( /[*?]/.test(s) === false ) {
3053
+ out.reStr = escapeForRegex(s);
3054
+ return out;
3055
+ }
3056
+ const reConstruct = /(?<!\\)[*?]/g;
3057
+ const reParts = [];
3058
+ let beg = 0;
3059
+ for (;;) {
3060
+ const match = reConstruct.exec(s);
3061
+ if ( match === null ) { break; }
3062
+ reParts.push(
3063
+ escapeForRegex(s.slice(beg, match.index)),
3064
+ match[0] === '*' ? '.*' : '.?',
3065
+ );
3066
+ beg = reConstruct.lastIndex;
3067
+ }
3068
+ reParts.push(escapeForRegex(s.slice(beg)));
3069
+ out.reStr = reParts.join('');
3070
+ return out;
3071
+ }
3072
+
3073
+ // https://adguard.com/kb/general/ad-filtering/create-own-filters/#replace-modifier
3074
+
3075
+ export function parseReplaceByRegexValue(s) {
3076
+ if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; }
3077
+ const parser = new ArglistParser('/');
3078
+ parser.nextArg(s, 1);
3079
+ let pattern = s.slice(parser.argBeg, parser.argEnd);
3080
+ if ( parser.transform ) {
3081
+ pattern = parser.normalizeArg(pattern);
3082
+ }
3083
+ if ( pattern === '' ) { return; }
3084
+ pattern = parser.normalizeArg(pattern, '$');
3085
+ pattern = parser.normalizeArg(pattern, ',');
3086
+ parser.nextArg(s, parser.separatorEnd);
3087
+ let replacement = s.slice(parser.argBeg, parser.argEnd);
3088
+ if ( parser.separatorEnd === parser.separatorBeg ) { return; }
3089
+ if ( parser.transform ) {
3090
+ replacement = parser.normalizeArg(replacement);
3091
+ }
3092
+ replacement = parser.normalizeArg(replacement, '$');
3093
+ replacement = parser.normalizeArg(replacement, ',');
3094
+ const flags = s.slice(parser.separatorEnd);
3095
+ try {
3096
+ return { re: new RegExp(pattern, flags), replacement };
3097
+ } catch {
3098
+ }
3099
+ }
3100
+
3101
+ export function parseReplaceValue(s) {
3102
+ if ( s.startsWith('/') ) {
3103
+ const r = parseReplaceByRegexValue(s);
3104
+ if ( r ) { r.type = 'text'; }
3105
+ return r;
3106
+ }
3107
+ const pos = s.indexOf(':');
3108
+ if ( pos === -1 ) { return; }
3109
+ const type = s.slice(0, pos);
3110
+ if ( type === 'json' || type === 'jsonl' ) {
3111
+ const query = s.slice(pos+1);
3112
+ const jsonp = JSONPath.create(query);
3113
+ if ( jsonp.valid === false ) { return; }
3114
+ return { type, jsonp };
3115
+ }
3116
+ }
3117
+
3118
+ /******************************************************************************/
3119
+
3120
+ export const netOptionTokenDescriptors = new Map([
3121
+ [ '1p', { canNegate: true } ],
3122
+ /* synonym */ [ 'first-party', { canNegate: true } ],
3123
+ [ 'strict1p', { } ],
3124
+ /* synonym */ [ 'strict-first-party', { } ],
3125
+ [ '3p', { canNegate: true } ],
3126
+ /* synonym */ [ 'third-party', { canNegate: true } ],
3127
+ [ 'strict3p', { } ],
3128
+ /* synonym */ [ 'strict-third-party', { } ],
3129
+ [ 'all', { } ],
3130
+ [ 'badfilter', { } ],
3131
+ [ 'cname', { allowOnly: true } ],
3132
+ [ 'csp', { mustAssign: true } ],
3133
+ [ 'css', { canNegate: true } ],
3134
+ /* synonym */ [ 'stylesheet', { canNegate: true } ],
3135
+ [ 'denyallow', { mustAssign: true } ],
3136
+ [ 'doc', { canNegate: true } ],
3137
+ /* synonym */ [ 'document', { canNegate: true } ],
3138
+ [ 'ehide', { } ],
3139
+ /* synonym */ [ 'elemhide', { } ],
3140
+ [ 'empty', { blockOnly: true } ],
3141
+ [ 'frame', { canNegate: true } ],
3142
+ /* synonym */ [ 'subdocument', { canNegate: true } ],
3143
+ [ 'from', { mustAssign: true } ],
3144
+ /* synonym */ [ 'domain', { mustAssign: true } ],
3145
+ [ 'font', { canNegate: true } ],
3146
+ [ 'genericblock', { } ],
3147
+ [ 'ghide', { } ],
3148
+ /* synonym */ [ 'generichide', { } ],
3149
+ [ 'header', { mustAssign: true } ],
3150
+ [ 'image', { canNegate: true } ],
3151
+ [ 'important', { blockOnly: true } ],
3152
+ [ 'inline-font', { canNegate: true } ],
3153
+ [ 'inline-script', { canNegate: true } ],
3154
+ [ 'ipaddress', { mustAssign: true } ],
3155
+ [ 'match-case', { } ],
3156
+ [ 'media', { canNegate: true } ],
3157
+ [ 'method', { mustAssign: true } ],
3158
+ [ 'mp4', { blockOnly: true } ],
3159
+ [ '_', { } ],
3160
+ [ 'object', { canNegate: true } ],
3161
+ /* synonym */ [ 'object-subrequest', { canNegate: true } ],
3162
+ [ 'other', { canNegate: true } ],
3163
+ [ 'permissions', { mustAssign: true } ],
3164
+ [ 'ping', { canNegate: true } ],
3165
+ /* synonym */ [ 'beacon', { canNegate: true } ],
3166
+ [ 'popunder', { } ],
3167
+ [ 'popup', { canNegate: true } ],
3168
+ [ 'reason', { mustAssign: true } ],
3169
+ [ 'redirect', { mustAssign: true } ],
3170
+ /* synonym */ [ 'rewrite', { mustAssign: true } ],
3171
+ [ 'redirect-rule', { mustAssign: true } ],
3172
+ [ 'removeparam', { } ],
3173
+ /* synonym */ [ 'queryprune', { } ],
3174
+ [ 'replace', { mustAssign: true } ],
3175
+ [ 'script', { canNegate: true } ],
3176
+ [ 'shide', { } ],
3177
+ /* synonym */ [ 'specifichide', { } ],
3178
+ [ 'to', { mustAssign: true } ],
3179
+ [ 'urlskip', { mustAssign: true } ],
3180
+ [ 'uritransform', { mustAssign: true } ],
3181
+ [ 'xhr', { canNegate: true } ],
3182
+ /* synonym */ [ 'xmlhttprequest', { canNegate: true } ],
3183
+ [ 'webrtc', { } ],
3184
+ [ 'websocket', { canNegate: true } ],
3185
+ ]);
3186
+
3187
+ /******************************************************************************/
3188
+
3189
+ // https://github.com/chrisaljoudi/uBlock/issues/1004
3190
+ // Detect and report invalid CSS selectors.
3191
+
3192
+ // Discard new ABP's `-abp-properties` directive until it is
3193
+ // implemented (if ever). Unlikely, see:
3194
+ // https://github.com/gorhill/uBlock/issues/1752
3195
+
3196
+ // https://github.com/gorhill/uBlock/issues/2624
3197
+ // Convert Adguard's `-ext-has='...'` into uBO's `:has(...)`.
3198
+
3199
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/89
3200
+ // Do not discard unknown pseudo-elements.
3201
+
3202
+ class ExtSelectorCompiler {
3203
+ constructor(instanceOptions) {
3204
+ this.reParseRegexLiteral = /^\/(.+)\/([imu]+)?$/;
3205
+
3206
+ // Use a regex for most common CSS selectors known to be valid in any
3207
+ // context.
3208
+ const cssIdentifier = '[A-Za-z_][\\w-]*';
3209
+ const cssClassOrId = `[.#]${cssIdentifier}`;
3210
+ const cssAttribute = `\\[${cssIdentifier}(?:[*^$]?="[^"\\]\\\\\\x09-\\x0D]+")?\\]`;
3211
+ const cssSimple =
3212
+ '(?:' +
3213
+ `${cssIdentifier}(?:${cssClassOrId})*(?:${cssAttribute})*` + '|' +
3214
+ `${cssClassOrId}(?:${cssClassOrId})*(?:${cssAttribute})*` + '|' +
3215
+ `${cssAttribute}(?:${cssAttribute})*` +
3216
+ ')';
3217
+ const cssCombinator = '(?: | [+>~] )';
3218
+ this.reCommonSelector = new RegExp(
3219
+ `^${cssSimple}(?:${cssCombinator}${cssSimple})*$`
3220
+ );
3221
+ // Resulting regex literal:
3222
+ // /^(?:[A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|[.#][A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\](?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*)(?:(?:\s+|\s*[>+~]\s*)(?:[A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|[.#][A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\](?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*))*$/
3223
+
3224
+ this.reEatBackslashes = /\\([()])/g;
3225
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
3226
+ this.knownPseudoClasses = new Set([
3227
+ 'active', 'any-link', 'autofill',
3228
+ 'blank',
3229
+ 'checked', 'current',
3230
+ 'default', 'defined', 'dir', 'disabled',
3231
+ 'empty', 'enabled',
3232
+ 'first', 'first-child', 'first-of-type', 'fullscreen', 'future', 'focus', 'focus-visible', 'focus-within',
3233
+ 'has', 'host', 'host-context', 'hover',
3234
+ 'indeterminate', 'in-range', 'invalid', 'is',
3235
+ 'lang', 'last-child', 'last-of-type', 'left', 'link', 'local-link',
3236
+ 'modal',
3237
+ 'not', 'nth-child', 'nth-col', 'nth-last-child', 'nth-last-col', 'nth-last-of-type', 'nth-of-type',
3238
+ 'only-child', 'only-of-type', 'optional', 'out-of-range',
3239
+ 'past', 'picture-in-picture', 'placeholder-shown', 'paused', 'playing',
3240
+ 'read-only', 'read-write', 'required', 'right', 'root',
3241
+ 'scope', 'state', 'target', 'target-within',
3242
+ 'user-invalid', 'valid', 'visited',
3243
+ 'where',
3244
+ ]);
3245
+ this.knownPseudoClassesWithArgs = new Set([
3246
+ 'dir',
3247
+ 'has', 'host-context',
3248
+ 'is',
3249
+ 'lang',
3250
+ 'not', 'nth-child', 'nth-col', 'nth-last-child', 'nth-last-col', 'nth-last-of-type', 'nth-of-type',
3251
+ 'state',
3252
+ 'where',
3253
+ ]);
3254
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
3255
+ this.knownPseudoElements = new Set([
3256
+ 'after',
3257
+ 'backdrop', 'before',
3258
+ 'cue', 'cue-region',
3259
+ 'first-letter', 'first-line', 'file-selector-button',
3260
+ 'grammar-error', 'marker',
3261
+ 'part', 'placeholder',
3262
+ 'selection', 'slotted', 'spelling-error',
3263
+ 'target-text',
3264
+ ]);
3265
+ this.knownPseudoElementsWithArgs = new Set([
3266
+ 'part',
3267
+ 'slotted',
3268
+ ]);
3269
+ // https://github.com/gorhill/uBlock/issues/2793
3270
+ this.normalizedOperators = new Map([
3271
+ [ '-abp-has', 'has' ],
3272
+ [ '-abp-contains', 'has-text' ],
3273
+ [ 'contains', 'has-text' ],
3274
+ [ 'nth-ancestor', 'upward' ],
3275
+ [ 'watch-attrs', 'watch-attr' ],
3276
+ ]);
3277
+ this.actionOperators = new Set([
3278
+ ':remove',
3279
+ ':style',
3280
+ ]);
3281
+ this.proceduralOperatorNames = new Set([
3282
+ 'has-text',
3283
+ 'if',
3284
+ 'if-not',
3285
+ 'matches-attr',
3286
+ 'matches-css',
3287
+ 'matches-css-after',
3288
+ 'matches-css-before',
3289
+ 'matches-media',
3290
+ 'matches-path',
3291
+ 'matches-prop',
3292
+ 'min-text-length',
3293
+ 'others',
3294
+ 'shadow',
3295
+ 'upward',
3296
+ 'watch-attr',
3297
+ 'xpath',
3298
+ ]);
3299
+ this.maybeProceduralOperatorNames = new Set([
3300
+ 'has',
3301
+ 'not',
3302
+ ]);
3303
+ this.proceduralActionNames = new Set([
3304
+ 'remove',
3305
+ 'remove-attr',
3306
+ 'remove-class',
3307
+ 'style',
3308
+ ]);
3309
+ this.normalizedExtendedSyntaxOperators = new Map([
3310
+ [ 'contains', 'has-text' ],
3311
+ [ 'has', 'has' ],
3312
+ ]);
3313
+ this.reIsRelativeSelector = /^\s*[+>~]/;
3314
+ this.reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/;
3315
+ this.reExtendedSyntaxReplacer = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/g;
3316
+ this.abpProceduralOpReplacer = /:-abp-(?:[a-z]+)\(/g;
3317
+ this.nativeCssHas = instanceOptions.nativeCssHas === true;
3318
+ // https://www.w3.org/TR/css-syntax-3/#typedef-ident-token
3319
+ this.reInvalidIdentifier = /^\d/;
3320
+ this.error = undefined;
3321
+ }
3322
+
3323
+ // CSSTree library holds onto last string parsed, and this is problematic
3324
+ // when the string is a slice of a huge parent string (typically a whole
3325
+ // filter list), it causes the huge parent string to stay in memory.
3326
+ // Asking CSSTree to parse an empty string resolves this issue.
3327
+ finish() {
3328
+ cssTree.parse('');
3329
+ }
3330
+
3331
+ compile(raw, out, compileOptions = {}) {
3332
+ this.asProcedural = compileOptions.asProcedural === true;
3333
+
3334
+ // https://github.com/gorhill/uBlock/issues/952
3335
+ // Find out whether we are dealing with an Adguard-specific cosmetic
3336
+ // filter, and if so, translate it if supported, or discard it if not
3337
+ // supported.
3338
+ // We have an Adguard/ABP cosmetic filter if and only if the
3339
+ // character is `$`, `%` or `?`, otherwise it's not a cosmetic
3340
+ // filter.
3341
+ // Adguard/EasyList style injection: translate to uBO's format.
3342
+ if ( this.isStyleInjectionFilter(raw) ) {
3343
+ const translated = this.translateStyleInjectionFilter(raw);
3344
+ if ( translated === undefined ) { return false; }
3345
+ raw = translated;
3346
+ } else if ( compileOptions.adgStyleSyntax === true ) {
3347
+ return false;
3348
+ }
3349
+
3350
+ // Normalize AdGuard's attribute-based procedural operators.
3351
+ // Normalize ABP's procedural operator names
3352
+ if ( this.asProcedural ) {
3353
+ if ( this.reExtendedSyntax.test(raw) ) {
3354
+ raw = raw.replace(this.reExtendedSyntaxReplacer, (a, a1, a2, a3) => {
3355
+ const op = this.normalizedExtendedSyntaxOperators.get(a1);
3356
+ if ( op === undefined ) { return a; }
3357
+ return `:${op}(${a3})`;
3358
+ });
3359
+ } else {
3360
+ let asProcedural = false;
3361
+ raw = raw.replace(this.abpProceduralOpReplacer, match => {
3362
+ if ( match === ':-abp-contains(' ) { return ':has-text('; }
3363
+ if ( match === ':-abp-has(' ) { return ':has('; }
3364
+ asProcedural = true;
3365
+ return match;
3366
+ });
3367
+ this.asProcedural = asProcedural;
3368
+ }
3369
+ }
3370
+
3371
+ // Relative selectors not allowed at top level.
3372
+ if ( this.reIsRelativeSelector.test(raw) ) { return false; }
3373
+
3374
+ if ( this.reCommonSelector.test(raw) ) {
3375
+ out.compiled = raw;
3376
+ return true;
3377
+ }
3378
+
3379
+ this.error = undefined;
3380
+ out.compiled = this.compileSelector(raw);
3381
+ if ( out.compiled === undefined ) {
3382
+ out.error = this.error;
3383
+ return false;
3384
+ }
3385
+
3386
+ if ( out.compiled instanceof Object ) {
3387
+ out.compiled.raw = raw;
3388
+ out.compiled = JSON.stringify(out.compiled);
3389
+ }
3390
+ return true;
3391
+ }
3392
+
3393
+ compileSelector(raw) {
3394
+ const parts = this.astFromRaw(raw, 'selectorList');
3395
+ if ( parts === undefined ) { return; }
3396
+ if ( this.astHasType(parts, 'Error') ) { return; }
3397
+ if ( this.astHasType(parts, 'Selector') === false ) { return; }
3398
+ if ( this.astIsValidSelectorList(parts) === false ) { return; }
3399
+ if ( this.astHasType(parts, 'ProceduralSelector') ) {
3400
+ if ( this.astHasType(parts, 'PseudoElementSelector') ) { return; }
3401
+ } else if ( this.astHasType(parts, 'ActionSelector') === false ) {
3402
+ return this.astSerialize(parts);
3403
+ }
3404
+ const r = this.astCompile(parts);
3405
+ if ( this.isCssable(r) ) {
3406
+ r.cssable = true;
3407
+ }
3408
+ return r;
3409
+ }
3410
+
3411
+ isCssable(r) {
3412
+ if ( r instanceof Object === false ) { return false; }
3413
+ if ( Array.isArray(r.action) && r.action[0] !== 'style' ) { return false; }
3414
+ if ( Array.isArray(r.tasks) === false ) { return true; }
3415
+ if ( r.tasks[0][0] === 'matches-media' ) {
3416
+ if ( r.tasks.length === 1 ) { return true; }
3417
+ if ( r.tasks.length === 2 ) {
3418
+ if ( r.selector !== '' ) { return false; }
3419
+ if ( r.tasks[1][0] === 'spath' ) { return true; }
3420
+ }
3421
+ }
3422
+ return false;
3423
+ }
3424
+
3425
+ astFromRaw(raw, type) {
3426
+ let ast;
3427
+ try {
3428
+ ast = cssTree.parse(raw, {
3429
+ context: type,
3430
+ parseValue: false,
3431
+ });
3432
+ } catch(reason) {
3433
+ const lines = [ reason.message ];
3434
+ const extra = reason.sourceFragment().split('\n');
3435
+ if ( extra.length !== 0 ) { lines.push(''); }
3436
+ const match = /^[^|]+\|/.exec(extra[0]);
3437
+ const beg = match !== null ? match[0].length : 0;
3438
+ lines.push(...extra.map(a => a.slice(beg)));
3439
+ this.error = lines.join('\n');
3440
+ return;
3441
+ }
3442
+ const parts = [];
3443
+ this.astFlatten(ast, parts);
3444
+ return parts;
3445
+ }
3446
+
3447
+ astFlatten(data, out) {
3448
+ const head = data.children && data.children.head;
3449
+ let args;
3450
+ switch ( data.type ) {
3451
+ case 'AttributeSelector':
3452
+ case 'ClassSelector':
3453
+ case 'Combinator':
3454
+ case 'IdSelector':
3455
+ case 'MediaFeature':
3456
+ case 'Nth':
3457
+ case 'Raw':
3458
+ case 'TypeSelector':
3459
+ out.push({ data });
3460
+ break;
3461
+ case 'Declaration':
3462
+ if ( data.value ) {
3463
+ this.astFlatten(data.value, args = []);
3464
+ }
3465
+ out.push({ data, args });
3466
+ args = undefined;
3467
+ break;
3468
+ case 'DeclarationList':
3469
+ case 'Identifier':
3470
+ case 'MediaQueryList':
3471
+ case 'Selector':
3472
+ case 'SelectorList':
3473
+ args = out;
3474
+ out.push({ data });
3475
+ break;
3476
+ case 'MediaQuery':
3477
+ case 'PseudoClassSelector':
3478
+ case 'PseudoElementSelector':
3479
+ if ( head ) { args = []; }
3480
+ out.push({ data, args });
3481
+ break;
3482
+ case 'Value':
3483
+ args = out;
3484
+ break;
3485
+ default:
3486
+ break;
3487
+ }
3488
+ if ( head ) {
3489
+ if ( args ) {
3490
+ this.astFlatten(head.data, args);
3491
+ }
3492
+ let next = head.next;
3493
+ while ( next ) {
3494
+ this.astFlatten(next.data, args);
3495
+ next = next.next;
3496
+ }
3497
+ }
3498
+ if ( data.type !== 'PseudoClassSelector' ) { return; }
3499
+ if ( data.name.startsWith('-abp-') && this.asProcedural === false ) {
3500
+ this.error = `${data.name} requires '#?#' separator syntax`;
3501
+ return;
3502
+ }
3503
+ // Post-analysis, mind:
3504
+ // - https://w3c.github.io/csswg-drafts/selectors-4/#has-pseudo
3505
+ // - https://w3c.github.io/csswg-drafts/selectors-4/#negation
3506
+ data.name = this.normalizedOperators.get(data.name) || data.name;
3507
+ if ( this.proceduralOperatorNames.has(data.name) ) {
3508
+ data.type = 'ProceduralSelector';
3509
+ } else if ( this.proceduralActionNames.has(data.name) ) {
3510
+ data.type = 'ActionSelector';
3511
+ } else if ( data.name.startsWith('-abp-') ) {
3512
+ data.type = 'Error';
3513
+ this.error = `${data.name} is not supported`;
3514
+ return;
3515
+ }
3516
+ if ( this.maybeProceduralOperatorNames.has(data.name) === false ) {
3517
+ return;
3518
+ }
3519
+ if ( this.astHasType(args, 'ActionSelector') ) {
3520
+ data.type = 'Error';
3521
+ this.error = 'invalid use of action operator';
3522
+ return;
3523
+ }
3524
+ if ( this.astHasType(args, 'ProceduralSelector') ) {
3525
+ data.type = 'ProceduralSelector';
3526
+ return;
3527
+ }
3528
+ switch ( data.name ) {
3529
+ case 'has':
3530
+ if (
3531
+ this.asProcedural ||
3532
+ this.nativeCssHas !== true ||
3533
+ this.astHasName(args, 'has')
3534
+ ) {
3535
+ data.type = 'ProceduralSelector';
3536
+ } else if ( this.astHasType(args, 'PseudoElementSelector') ) {
3537
+ data.type = 'Error';
3538
+ }
3539
+ break;
3540
+ case 'not': {
3541
+ if ( this.astHasType(args, 'Combinator', 0) === false ) { break; }
3542
+ if ( this.astIsValidSelectorList(args) !== true ) {
3543
+ data.type = 'Error';
3544
+ }
3545
+ break;
3546
+ }
3547
+ default:
3548
+ break;
3549
+ }
3550
+ }
3551
+
3552
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/2300
3553
+ // Unquoted attribute values are parsed as Identifier instead of String.
3554
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/3127
3555
+ // Escape [\t\n\v\f\r]
3556
+ astSerializePart(part) {
3557
+ const out = [];
3558
+ const { data } = part;
3559
+ switch ( data.type ) {
3560
+ case 'AttributeSelector': {
3561
+ const name = data.name.name;
3562
+ if ( this.reInvalidIdentifier.test(name) ) { return; }
3563
+ if ( data.matcher === null ) {
3564
+ out.push(`[${name}]`);
3565
+ break;
3566
+ }
3567
+ let value = data.value.value;
3568
+ if ( typeof value !== 'string' ) {
3569
+ value = data.value.name;
3570
+ }
3571
+ if ( /["\\]/.test(value) ) {
3572
+ value = value.replace(/["\\]/g, '\\$&');
3573
+ }
3574
+ if ( /[\x09-\x0D]/.test(value) ) {
3575
+ value = value.replace(/[\x09-\x0D]/g, s =>
3576
+ `\\${s.charCodeAt(0).toString(16).toUpperCase()} `
3577
+ );
3578
+ }
3579
+ let flags = '';
3580
+ if ( typeof data.flags === 'string' ) {
3581
+ if ( /^(is?|si?)$/.test(data.flags) === false ) { return; }
3582
+ flags = ` ${data.flags}`;
3583
+ }
3584
+ out.push(`[${name}${data.matcher}"${value}"${flags}]`);
3585
+ break;
3586
+ }
3587
+ case 'ClassSelector':
3588
+ if ( this.reInvalidIdentifier.test(data.name) ) { return; }
3589
+ out.push(`.${data.name}`);
3590
+ break;
3591
+ case 'Combinator':
3592
+ out.push(data.name);
3593
+ break;
3594
+ case 'Identifier':
3595
+ if ( this.reInvalidIdentifier.test(data.name) ) { return; }
3596
+ out.push(data.name);
3597
+ break;
3598
+ case 'IdSelector':
3599
+ if ( this.reInvalidIdentifier.test(data.name) ) { return; }
3600
+ out.push(`#${data.name}`);
3601
+ break;
3602
+ case 'Nth': {
3603
+ if ( data.selector !== null ) { return; }
3604
+ if ( data.nth.type === 'AnPlusB' ) {
3605
+ const a = parseInt(data.nth.a, 10) || null;
3606
+ const b = parseInt(data.nth.b, 10) || null;
3607
+ if ( a !== null ) {
3608
+ out.push(`${a}n`);
3609
+ if ( b === null ) { break; }
3610
+ if ( b < 0 ) {
3611
+ out.push(`${b}`);
3612
+ } else {
3613
+ out.push(`+${b}`);
3614
+ }
3615
+ } else if ( b !== null ) {
3616
+ out.push(`${b}`);
3617
+ }
3618
+ } else if ( data.nth.type === 'Identifier' ) {
3619
+ out.push(data.nth.name);
3620
+ }
3621
+ break;
3622
+ }
3623
+ case 'PseudoElementSelector': {
3624
+ const hasArgs = Array.isArray(part.args);
3625
+ if ( data.name.charCodeAt(0) !== 0x2D /* '-' */ ) {
3626
+ if ( this.knownPseudoElements.has(data.name) === false ) { return; }
3627
+ if ( this.knownPseudoElementsWithArgs.has(data.name) && hasArgs === false ) { return; }
3628
+ }
3629
+ out.push(`::${data.name}`);
3630
+ if ( hasArgs ) {
3631
+ const arg = this.astSerialize(part.args);
3632
+ if ( typeof arg !== 'string' ) { return; }
3633
+ out.push(`(${arg})`);
3634
+ }
3635
+ break;
3636
+ }
3637
+ case 'PseudoClassSelector': {
3638
+ const hasArgs = Array.isArray(part.args);
3639
+ if ( data.name.charCodeAt(0) !== 0x2D /* '-' */ ) {
3640
+ if ( this.knownPseudoClasses.has(data.name) === false ) { return; }
3641
+ if ( this.knownPseudoClassesWithArgs.has(data.name) && hasArgs === false ) { return; }
3642
+ }
3643
+ out.push(`:${data.name}`);
3644
+ if ( hasArgs ) {
3645
+ const arg = this.astSerialize(part.args);
3646
+ if ( typeof arg !== 'string' ) { return; }
3647
+ out.push(`(${arg.trim()})`);
3648
+ }
3649
+ break;
3650
+ }
3651
+ case 'Raw':
3652
+ out.push(data.value);
3653
+ break;
3654
+ case 'TypeSelector':
3655
+ if ( this.reInvalidIdentifier.test(data.name) ) { return; }
3656
+ out.push(data.name);
3657
+ break;
3658
+ default:
3659
+ break;
3660
+ }
3661
+ return out.join('');
3662
+ }
3663
+
3664
+ astSerialize(parts, plainCSS = true) {
3665
+ const out = [];
3666
+ for ( const part of parts ) {
3667
+ const { data } = part;
3668
+ switch ( data.type ) {
3669
+ case 'AttributeSelector':
3670
+ case 'ClassSelector':
3671
+ case 'Identifier':
3672
+ case 'IdSelector':
3673
+ case 'Nth':
3674
+ case 'PseudoClassSelector':
3675
+ case 'PseudoElementSelector': {
3676
+ const s = this.astSerializePart(part);
3677
+ if ( s === undefined ) { return; }
3678
+ out.push(s);
3679
+ break;
3680
+ }
3681
+ case 'Combinator': {
3682
+ const s = this.astSerializePart(part);
3683
+ if ( s === undefined ) { return; }
3684
+ if ( out.length !== 0 ) { out.push(' '); }
3685
+ if ( s !== ' ' ) { out.push(s, ' '); }
3686
+ break;
3687
+ }
3688
+ case 'TypeSelector': {
3689
+ const s = this.astSerializePart(part);
3690
+ if ( s === undefined ) { return; }
3691
+ if ( s === '*' && out.length !== 0 ) {
3692
+ const before = out[out.length-1];
3693
+ if ( before.endsWith(' ') === false ) { return; }
3694
+ }
3695
+ out.push(s);
3696
+ break;
3697
+ }
3698
+ case 'Raw':
3699
+ if ( plainCSS ) { return; }
3700
+ out.push(this.astSerializePart(part));
3701
+ break;
3702
+ case 'Selector':
3703
+ if ( out.length !== 0 ) { out.push(', '); }
3704
+ break;
3705
+ case 'SelectorList':
3706
+ break;
3707
+ default:
3708
+ return;
3709
+ }
3710
+ }
3711
+ return out.join('');
3712
+ }
3713
+
3714
+ astCompile(parts, details = {}) {
3715
+ if ( Array.isArray(parts) === false ) { return; }
3716
+ if ( parts.length === 0 ) { return; }
3717
+ if ( parts[0].data.type !== 'SelectorList' ) { return; }
3718
+ const out = { selector: '' };
3719
+ const prelude = [];
3720
+ const tasks = [];
3721
+ let startOfSelector = true;
3722
+ for ( const part of parts ) {
3723
+ if ( out.action !== undefined ) { return; }
3724
+ const { data } = part;
3725
+ switch ( data.type ) {
3726
+ case 'ActionSelector': {
3727
+ if ( details.noaction ) { return; }
3728
+ if ( prelude.length !== 0 ) {
3729
+ if ( tasks.length === 0 ) {
3730
+ out.selector = prelude.join('');
3731
+ } else {
3732
+ tasks.push(this.createSpathTask(prelude.join('')));
3733
+ }
3734
+ prelude.length = 0;
3735
+ }
3736
+ const args = this.compileArgumentAst(data.name, part.args);
3737
+ if ( args === undefined ) { return; }
3738
+ out.action = [ data.name, args ];
3739
+ break;
3740
+ }
3741
+ case 'AttributeSelector':
3742
+ case 'ClassSelector':
3743
+ case 'IdSelector':
3744
+ case 'PseudoClassSelector':
3745
+ case 'PseudoElementSelector':
3746
+ case 'TypeSelector': {
3747
+ const s = this.astSerializePart(part);
3748
+ if ( s === undefined ) { return; }
3749
+ prelude.push(s);
3750
+ startOfSelector = false;
3751
+ break;
3752
+ }
3753
+ case 'Combinator': {
3754
+ const s = this.astSerializePart(part);
3755
+ if ( s === undefined ) { return; }
3756
+ if ( startOfSelector === false || prelude.length !== 0 ) {
3757
+ prelude.push(' ');
3758
+ }
3759
+ if ( s !== ' ' ) { prelude.push(s, ' '); }
3760
+ startOfSelector = false;
3761
+ break;
3762
+ }
3763
+ case 'ProceduralSelector': {
3764
+ if ( prelude.length !== 0 ) {
3765
+ let spath = prelude.join('');
3766
+ prelude.length = 0;
3767
+ if ( spath.endsWith(' ') ) { spath += '*'; }
3768
+ if ( tasks.length === 0 ) {
3769
+ out.selector = spath;
3770
+ } else {
3771
+ tasks.push(this.createSpathTask(spath));
3772
+ }
3773
+ }
3774
+ const args = this.compileArgumentAst(data.name, part.args);
3775
+ if ( args === undefined ) { return; }
3776
+ tasks.push([ data.name, args ]);
3777
+ startOfSelector = false;
3778
+ break;
3779
+ }
3780
+ case 'Selector':
3781
+ if ( prelude.length !== 0 ) {
3782
+ prelude.push(', ');
3783
+ }
3784
+ startOfSelector = true;
3785
+ break;
3786
+ case 'SelectorList':
3787
+ startOfSelector = true;
3788
+ break;
3789
+ default:
3790
+ return;
3791
+ }
3792
+ }
3793
+ if ( tasks.length === 0 && out.action === undefined ) {
3794
+ if ( prelude.length === 0 ) { return; }
3795
+ return prelude.join('').trim();
3796
+ }
3797
+ if ( prelude.length !== 0 ) {
3798
+ tasks.push(this.createSpathTask(prelude.join('')));
3799
+ }
3800
+ if ( tasks.length !== 0 ) {
3801
+ out.tasks = tasks;
3802
+ }
3803
+ return out;
3804
+ }
3805
+
3806
+ astHasType(parts, type, depth = 0x7FFFFFFF) {
3807
+ if ( Array.isArray(parts) === false ) { return false; }
3808
+ for ( const part of parts ) {
3809
+ if ( part.data.type === type ) { return true; }
3810
+ if (
3811
+ Array.isArray(part.args) &&
3812
+ depth !== 0 &&
3813
+ this.astHasType(part.args, type, depth-1)
3814
+ ) {
3815
+ return true;
3816
+ }
3817
+ }
3818
+ return false;
3819
+ }
3820
+
3821
+ astHasName(parts, name) {
3822
+ if ( Array.isArray(parts) === false ) { return false; }
3823
+ for ( const part of parts ) {
3824
+ if ( part.data.name === name ) { return true; }
3825
+ if ( Array.isArray(part.args) && this.astHasName(part.args, name) ) {
3826
+ return true;
3827
+ }
3828
+ }
3829
+ return false;
3830
+ }
3831
+
3832
+ astSelectorsFromSelectorList(args) {
3833
+ if ( Array.isArray(args) === false ) { return; }
3834
+ if ( args.length < 3 ) { return; }
3835
+ if ( args[0].data instanceof Object === false ) { return; }
3836
+ if ( args[0].data.type !== 'SelectorList' ) { return; }
3837
+ if ( args[1].data instanceof Object === false ) { return; }
3838
+ if ( args[1].data.type !== 'Selector' ) { return; }
3839
+ const out = [];
3840
+ let beg = 1, end = 0, i = 2;
3841
+ for (;;) {
3842
+ if ( i < args.length ) {
3843
+ const type = args[i].data instanceof Object && args[i].data.type;
3844
+ if ( type === 'Selector' ) {
3845
+ end = i;
3846
+ }
3847
+ } else {
3848
+ end = args.length;
3849
+ }
3850
+ if ( end !== 0 ) {
3851
+ const components = args.slice(beg+1, end);
3852
+ if ( components.length === 0 ) { return; }
3853
+ out.push(components);
3854
+ if ( end === args.length ) { break; }
3855
+ beg = end; end = 0;
3856
+ }
3857
+ if ( i === args.length ) { break; }
3858
+ i += 1;
3859
+ }
3860
+ return out;
3861
+ }
3862
+
3863
+ astIsValidSelector(components) {
3864
+ const len = components.length;
3865
+ if ( len === 0 ) { return false; }
3866
+ if ( components[0].data.type === 'Combinator' ) { return false; }
3867
+ if ( len === 1 ) { return true; }
3868
+ if ( components[len-1].data.type === 'Combinator' ) { return false; }
3869
+ return true;
3870
+ }
3871
+
3872
+ astIsValidSelectorList(args) {
3873
+ const selectors = this.astSelectorsFromSelectorList(args);
3874
+ if ( Array.isArray(selectors) === false || selectors.length === 0 ) {
3875
+ return false;
3876
+ }
3877
+ for ( const selector of selectors ) {
3878
+ if ( this.astIsValidSelector(selector) !== true ) { return false; }
3879
+ }
3880
+ return true;
3881
+ }
3882
+
3883
+ isStyleInjectionFilter(selector) {
3884
+ const len = selector.length;
3885
+ return len !== 0 && selector.charCodeAt(len-1) === 0x7D /* } */;
3886
+ }
3887
+
3888
+ translateStyleInjectionFilter(raw) {
3889
+ const matches = /^(.+)\s*\{([^}]+)\}$/.exec(raw);
3890
+ if ( matches === null ) { return; }
3891
+ const selector = matches[1].trim();
3892
+ const style = matches[2].trim();
3893
+ // Special style directive `remove: true` is converted into a
3894
+ // `:remove()` operator.
3895
+ if ( /^\s*remove:\s*true[; ]*$/.test(style) ) {
3896
+ return `${selector}:remove()`;
3897
+ }
3898
+ // For some reasons, many of Adguard's plain cosmetic filters are
3899
+ // "disguised" as style-based cosmetic filters: convert such filters
3900
+ // to plain cosmetic filters.
3901
+ return /display\s*:\s*none\s*!important;?$/.test(style)
3902
+ ? selector
3903
+ : `${selector}:style(${style})`;
3904
+ }
3905
+
3906
+ createSpathTask(selector) {
3907
+ return [ 'spath', selector ];
3908
+ }
3909
+
3910
+ compileArgumentAst(operator, parts) {
3911
+ switch ( operator ) {
3912
+ case 'has': {
3913
+ let r = this.astCompile(parts, { noaction: true });
3914
+ if ( typeof r === 'string' ) {
3915
+ r = { selector: r.replace(/^\s*:scope\s*/, '') };
3916
+ }
3917
+ return r;
3918
+ }
3919
+ case 'not': {
3920
+ return this.astCompile(parts, { noaction: true });
3921
+ }
3922
+ default:
3923
+ break;
3924
+ }
3925
+ if ( Array.isArray(parts) === false || parts.length === 0 ) { return; }
3926
+ const arg = this.astSerialize(parts, false);
3927
+ if ( arg === undefined ) { return; }
3928
+ switch ( operator ) {
3929
+ case 'has-text':
3930
+ return this.compileText(arg);
3931
+ case 'if':
3932
+ return this.compileSelector(arg);
3933
+ case 'if-not':
3934
+ return this.compileSelector(arg);
3935
+ case 'matches-attr':
3936
+ case 'matches-prop':
3937
+ return this.compileMatchAttrArgument(arg);
3938
+ case 'matches-css':
3939
+ return this.compileCSSDeclaration(arg);
3940
+ case 'matches-css-after':
3941
+ return this.compileCSSDeclaration(`after, ${arg}`);
3942
+ case 'matches-css-before':
3943
+ return this.compileCSSDeclaration(`before, ${arg}`);
3944
+ case 'matches-media':
3945
+ return this.compileMediaQuery(arg);
3946
+ case 'matches-path':
3947
+ return this.compileText(arg);
3948
+ case 'min-text-length':
3949
+ return this.compileInteger(arg);
3950
+ case 'others':
3951
+ return this.compileNoArgument(arg);
3952
+ case 'remove':
3953
+ return this.compileNoArgument(arg);
3954
+ case 'remove-attr':
3955
+ return this.compileText(arg);
3956
+ case 'remove-class':
3957
+ return this.compileText(arg);
3958
+ case 'shadow':
3959
+ return this.compileSelector(arg);
3960
+ case 'style':
3961
+ return this.compileStyleProperties(arg);
3962
+ case 'upward':
3963
+ return this.compileUpwardArgument(arg);
3964
+ case 'watch-attr':
3965
+ return this.compileAttrList(arg);
3966
+ case 'xpath':
3967
+ return this.compileXpathExpression(arg);
3968
+ default:
3969
+ break;
3970
+ }
3971
+ }
3972
+
3973
+ isBadRegex(s) {
3974
+ try {
3975
+ void new RegExp(s);
3976
+ } catch (ex) {
3977
+ this.isBadRegex.message = ex.toString();
3978
+ return true;
3979
+ }
3980
+ return false;
3981
+ }
3982
+
3983
+ unquoteString(s) {
3984
+ const end = s.length;
3985
+ if ( end === 0 ) {
3986
+ return { s: '', end };
3987
+ }
3988
+ if ( /^['"]/.test(s) === false ) {
3989
+ return { s, i: end };
3990
+ }
3991
+ const quote = s.charCodeAt(0);
3992
+ const out = [];
3993
+ let i = 1, c = 0;
3994
+ for (;;) {
3995
+ c = s.charCodeAt(i);
3996
+ if ( c === quote ) {
3997
+ i += 1;
3998
+ break;
3999
+ }
4000
+ if ( c === 0x5C /* '\\' */ ) {
4001
+ i += 1;
4002
+ if ( i === end ) { break; }
4003
+ c = s.charCodeAt(i);
4004
+ if ( c !== 0x5C && c !== quote ) {
4005
+ out.push(0x5C);
4006
+ }
4007
+ }
4008
+ out.push(c);
4009
+ i += 1;
4010
+ if ( i === end ) { break; }
4011
+ }
4012
+ return { s: String.fromCharCode(...out), i };
4013
+ }
4014
+
4015
+ compileMatchAttrArgument(s) {
4016
+ if ( s === '' ) { return; }
4017
+ let attr = '', value = '';
4018
+ let r = this.unquoteString(s);
4019
+ if ( r.i === s.length ) {
4020
+ const pos = r.s.indexOf('=');
4021
+ if ( pos === -1 ) {
4022
+ attr = r.s;
4023
+ } else {
4024
+ attr = r.s.slice(0, pos);
4025
+ value = r.s.slice(pos+1);
4026
+ }
4027
+ } else {
4028
+ attr = r.s;
4029
+ if ( s.charCodeAt(r.i) !== 0x3D ) { return; }
4030
+ value = s.slice(r.i+1);
4031
+ }
4032
+ if ( attr === '' ) { return; }
4033
+ if ( value.length !== 0 ) {
4034
+ r = this.unquoteString(value);
4035
+ if ( r.i !== value.length ) { return; }
4036
+ value = r.s;
4037
+ }
4038
+ return { attr, value };
4039
+ }
4040
+
4041
+ // Remove potentially present quotes before processing.
4042
+ compileText(s) {
4043
+ if ( s === '' ) {
4044
+ this.error = 'argument missing';
4045
+ return;
4046
+ }
4047
+ const r = this.unquoteString(s);
4048
+ if ( r.i !== s.length ) { return; }
4049
+ return r.s;
4050
+ }
4051
+
4052
+ compileCSSDeclaration(s) {
4053
+ let pseudo; {
4054
+ const match = /^[a-z-]+,/.exec(s);
4055
+ if ( match !== null ) {
4056
+ pseudo = match[0].slice(0, -1);
4057
+ s = s.slice(match[0].length).trim();
4058
+ }
4059
+ }
4060
+ const pos = s.indexOf(':');
4061
+ if ( pos === -1 ) { return; }
4062
+ const name = s.slice(0, pos).trim();
4063
+ const value = s.slice(pos + 1).trim();
4064
+ const match = this.reParseRegexLiteral.exec(value);
4065
+ let regexDetails;
4066
+ if ( match !== null ) {
4067
+ regexDetails = match[1];
4068
+ if ( this.isBadRegex(regexDetails) ) { return; }
4069
+ if ( match[2] ) {
4070
+ regexDetails = [ regexDetails, match[2] ];
4071
+ }
4072
+ } else {
4073
+ regexDetails = `^${escapeForRegex(value)}$`;
4074
+ }
4075
+ return { name, pseudo, value: regexDetails };
4076
+ }
4077
+
4078
+ compileInteger(s, min = 0, max = 0x7FFFFFFF) {
4079
+ if ( /^\d+$/.test(s) === false ) { return; }
4080
+ const n = parseInt(s, 10);
4081
+ if ( n < min || n >= max ) { return; }
4082
+ return n;
4083
+ }
4084
+
4085
+ compileMediaQuery(s) {
4086
+ const parts = this.astFromRaw(s, 'mediaQueryList');
4087
+ if ( parts === undefined ) { return; }
4088
+ if ( this.astHasType(parts, 'Raw') ) { return; }
4089
+ if ( this.astHasType(parts, 'MediaQuery') === false ) { return; }
4090
+ // TODO: normalize by serializing resulting AST
4091
+ return s;
4092
+ }
4093
+
4094
+ compileUpwardArgument(s) {
4095
+ const i = this.compileInteger(s, 1, 256);
4096
+ if ( i !== undefined ) { return i; }
4097
+ return this.compilePlainSelector(s);
4098
+ }
4099
+
4100
+ compilePlainSelector(s) {
4101
+ const parts = this.astFromRaw(s, 'selectorList' );
4102
+ if ( this.astIsValidSelectorList(parts) !== true ) { return; }
4103
+ if ( this.astHasType(parts, 'ProceduralSelector') ) { return; }
4104
+ if ( this.astHasType(parts, 'ActionSelector') ) { return; }
4105
+ if ( this.astHasType(parts, 'Error') ) { return; }
4106
+ return s;
4107
+ }
4108
+
4109
+ compileNoArgument(s) {
4110
+ if ( s === '' ) { return s; }
4111
+ }
4112
+
4113
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/668
4114
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/1693
4115
+ // https://github.com/uBlockOrigin/uBlock-issues/issues/1811
4116
+ // Forbid instances of:
4117
+ // - `image-set(`
4118
+ // - `url(`
4119
+ // - any instance of `//`
4120
+ // - backslashes `\`
4121
+ // - opening comment `/*`
4122
+ compileStyleProperties(s) {
4123
+ if ( /image-set\(|url\(|\/\s*\/|\\|\/\*/i.test(s) ) { return; }
4124
+ const parts = this.astFromRaw(s, 'declarationList');
4125
+ if ( parts === undefined ) { return; }
4126
+ if ( this.astHasType(parts, 'Declaration') === false ) { return; }
4127
+ return s;
4128
+ }
4129
+
4130
+ compileAttrList(s) {
4131
+ if ( s === '' ) { return s; }
4132
+ const attrs = s.split(/\s*,\s*/);
4133
+ const out = [];
4134
+ for ( const attr of attrs ) {
4135
+ if ( attr !== '' ) {
4136
+ out.push(attr);
4137
+ }
4138
+ }
4139
+ return out;
4140
+ }
4141
+
4142
+ compileXpathExpression(s) {
4143
+ const r = this.unquoteString(s);
4144
+ if ( r.i !== s.length ) { return; }
4145
+ const doc = globalThis.document;
4146
+ if ( doc instanceof Object === false ) { return r.s; }
4147
+ try {
4148
+ const expr = doc.createExpression(r.s, null);
4149
+ expr.evaluate(doc, XPathResult.ANY_UNORDERED_NODE_TYPE);
4150
+ } catch {
4151
+ return;
4152
+ }
4153
+ return r.s;
4154
+ }
4155
+ }
4156
+
4157
+ // bit 0: can be used as auto-completion hint
4158
+ // bit 1: can not be used in HTML filtering
4159
+ //
4160
+ export const proceduralOperatorTokens = new Map([
4161
+ [ '-abp-contains', 0b00 ],
4162
+ [ '-abp-has', 0b00, ],
4163
+ [ 'contains', 0b00, ],
4164
+ [ 'has', 0b01 ],
4165
+ [ 'has-text', 0b01 ],
4166
+ [ 'if', 0b00 ],
4167
+ [ 'if-not', 0b00 ],
4168
+ [ 'matches-attr', 0b11 ],
4169
+ [ 'matches-css', 0b11 ],
4170
+ [ 'matches-media', 0b11 ],
4171
+ [ 'matches-path', 0b11 ],
4172
+ [ 'matches-prop', 0b11 ],
4173
+ [ 'min-text-length', 0b01 ],
4174
+ [ 'not', 0b01 ],
4175
+ [ 'nth-ancestor', 0b00 ],
4176
+ [ 'others', 0b11 ],
4177
+ [ 'remove', 0b11 ],
4178
+ [ 'remove-attr', 0b11 ],
4179
+ [ 'remove-class', 0b11 ],
4180
+ [ 'style', 0b11 ],
4181
+ [ 'upward', 0b01 ],
4182
+ [ 'watch-attr', 0b11 ],
4183
+ [ 'watch-attrs', 0b00 ],
4184
+ [ 'xpath', 0b01 ],
4185
+ ]);
4186
+
4187
+ /******************************************************************************/
4188
+
4189
+ export const utils = (( ) => {
4190
+
4191
+ const preparserTokens = new Map([
4192
+ [ 'ext_ublock', 'ublock' ],
4193
+ [ 'ext_ubol', 'ubol' ],
4194
+ [ 'ext_devbuild', 'devbuild' ],
4195
+ [ 'env_chromium', 'chromium' ],
4196
+ [ 'env_edge', 'edge' ],
4197
+ [ 'env_firefox', 'firefox' ],
4198
+ [ 'env_legacy', 'legacy' ],
4199
+ [ 'env_mobile', 'mobile' ],
4200
+ [ 'env_mv3', 'mv3' ],
4201
+ [ 'env_safari', 'safari' ],
4202
+ [ 'cap_html_filtering', 'html_filtering' ],
4203
+ [ 'cap_user_stylesheet', 'user_stylesheet' ],
4204
+ [ 'cap_ipaddress', 'ipaddress' ],
4205
+ [ 'false', 'false' ],
4206
+ // Hoping ABP-only list maintainers can at least make use of it to
4207
+ // help non-ABP content blockers better deal with filters benefiting
4208
+ // only ABP.
4209
+ [ 'ext_abp', 'false' ],
4210
+ // Compatibility with other blockers
4211
+ // https://adguard.com/kb/general/ad-filtering/create-own-filters/#conditions-directive
4212
+ [ 'adguard', 'adguard' ],
4213
+ [ 'adguard_app_android', 'false' ],
4214
+ [ 'adguard_app_cli', 'false' ],
4215
+ [ 'adguard_app_ios', 'false' ],
4216
+ [ 'adguard_app_mac', 'false' ],
4217
+ [ 'adguard_app_windows', 'false' ],
4218
+ [ 'adguard_ext_android_cb', 'false' ],
4219
+ [ 'adguard_ext_chromium', 'chromium' ],
4220
+ [ 'adguard_ext_chromium_mv3', 'mv3' ],
4221
+ [ 'adguard_ext_edge', 'edge' ],
4222
+ [ 'adguard_ext_firefox', 'firefox' ],
4223
+ [ 'adguard_ext_opera', 'chromium' ],
4224
+ [ 'adguard_ext_safari', 'false' ],
4225
+ ]);
4226
+
4227
+ const toURL = url => {
4228
+ try {
4229
+ return new URL(url.trim());
4230
+ } catch {
4231
+ }
4232
+ };
4233
+
4234
+ // Useful reference:
4235
+ // https://adguard.com/kb/general/ad-filtering/create-own-filters/#conditions-directive
4236
+
4237
+ class preparser {
4238
+ static evaluateExprToken(token, env = []) {
4239
+ const not = token.charCodeAt(0) === 0x21 /* ! */;
4240
+ if ( not ) { token = token.slice(1); }
4241
+ let state = preparserTokens.get(token);
4242
+ if ( state === undefined ) {
4243
+ if ( token.startsWith('cap_') === false ) { return; }
4244
+ state = 'false';
4245
+ }
4246
+ return state === 'false' && not || env.includes(state) !== not;
4247
+ }
4248
+
4249
+ static evaluateExpr(expr, env = []) {
4250
+ if ( expr.startsWith('(') && expr.endsWith(')') ) {
4251
+ expr = expr.slice(1, -1);
4252
+ }
4253
+ const matches = Array.from(expr.matchAll(/(?:(?:&&|\|\|)\s+)?\S+/g));
4254
+ if ( matches.length === 0 ) { return; }
4255
+ if ( matches[0][0].startsWith('|') || matches[0][0].startsWith('&') ) { return; }
4256
+ let result = this.evaluateExprToken(matches[0][0], env);
4257
+ for ( let i = 1; i < matches.length; i++ ) {
4258
+ const parts = matches[i][0].split(/ +/);
4259
+ if ( parts.length !== 2 ) { return; }
4260
+ const state = this.evaluateExprToken(parts[1], env);
4261
+ if ( state === undefined ) { return; }
4262
+ if ( parts[0] === '||' ) {
4263
+ result = result || state;
4264
+ } else if ( parts[0] === '&&' ) {
4265
+ result = result && state;
4266
+ } else {
4267
+ return;
4268
+ }
4269
+ }
4270
+ return result;
4271
+ }
4272
+
4273
+ // This method returns an array of indices, corresponding to position in
4274
+ // the content string which should alternatively be parsed and discarded.
4275
+ static splitter(content, env = []) {
4276
+ const reIf = /^!#(if|else|endif)\b([^\n]*)(?:[\n\r]+|$)/gm;
4277
+ const stack = [];
4278
+ const parts = [ 0 ];
4279
+ let discard = false;
4280
+
4281
+ const shouldDiscard = ( ) => stack.some(v => v.known && v.discard);
4282
+
4283
+ const begif = details => {
4284
+ if ( discard === false && details.known && details.discard ) {
4285
+ parts.push(details.pos);
4286
+ discard = true;
4287
+ }
4288
+ stack.push(details);
4289
+ };
4290
+
4291
+ const endif = match => {
4292
+ stack.pop();
4293
+ const stopDiscard = shouldDiscard() === false;
4294
+ if ( discard && stopDiscard ) {
4295
+ parts.push(match.index + match[0].length);
4296
+ discard = false;
4297
+ }
4298
+ };
4299
+
4300
+ for (;;) {
4301
+ const match = reIf.exec(content);
4302
+ if ( match === null ) { break; }
4303
+
4304
+ switch ( match[1] ) {
4305
+ case 'if': {
4306
+ const result = this.evaluateExpr(match[2].trim(), env);
4307
+ begif({
4308
+ known: result !== undefined,
4309
+ discard: result === false,
4310
+ pos: match.index,
4311
+ });
4312
+ break;
4313
+ }
4314
+ case 'else': {
4315
+ if ( stack.length === 0 ) { break; }
4316
+ const details = stack[stack.length-1];
4317
+ endif(match);
4318
+ details.discard = details.discard === false;
4319
+ details.pos = match.index;
4320
+ begif(details);
4321
+ break;
4322
+ }
4323
+ case 'endif': {
4324
+ endif(match);
4325
+ break;
4326
+ }
4327
+ default:
4328
+ break;
4329
+ }
4330
+ }
4331
+
4332
+ parts.push(content.length);
4333
+ return parts;
4334
+ }
4335
+
4336
+ static expandIncludes(parts, env = []) {
4337
+ const out = [];
4338
+ const reInclude = /^!#include +(\S+)[^\n\r]*(?:[\n\r]+|$)/gm;
4339
+ for ( const part of parts ) {
4340
+ if ( typeof part === 'string' ) {
4341
+ out.push(part);
4342
+ continue;
4343
+ }
4344
+ if ( part instanceof Object === false ) { continue; }
4345
+ const content = part.content;
4346
+ const slices = this.splitter(content, env);
4347
+ for ( let i = 0, n = slices.length - 1; i < n; i++ ) {
4348
+ const slice = content.slice(slices[i+0], slices[i+1]);
4349
+ if ( (i & 1) !== 0 ) {
4350
+ out.push(slice);
4351
+ continue;
4352
+ }
4353
+ let lastIndex = 0;
4354
+ for (;;) {
4355
+ const match = reInclude.exec(slice);
4356
+ if ( match === null ) { break; }
4357
+ if ( toURL(match[1]) !== undefined ) { continue; }
4358
+ if ( match[1].indexOf('..') !== -1 ) { continue; }
4359
+ // Compute nested list path relative to parent list path
4360
+ const pos = part.url.lastIndexOf('/');
4361
+ if ( pos === -1 ) { continue; }
4362
+ const subURL = part.url.slice(0, pos + 1) + match[1].trim();
4363
+ out.push(
4364
+ slice.slice(lastIndex, match.index + match[0].length),
4365
+ `! >>>>>>>> ${subURL}\n`,
4366
+ { url: subURL },
4367
+ `! <<<<<<<< ${subURL}\n`
4368
+ );
4369
+ lastIndex = reInclude.lastIndex;
4370
+ }
4371
+ out.push(lastIndex === 0 ? slice : slice.slice(lastIndex));
4372
+ }
4373
+ }
4374
+ return out;
4375
+ }
4376
+
4377
+ static prune(content, env) {
4378
+ const parts = this.splitter(content, env);
4379
+ const out = [];
4380
+ for ( let i = 0, n = parts.length - 1; i < n; i += 2 ) {
4381
+ const beg = parts[i+0];
4382
+ const end = parts[i+1];
4383
+ out.push(content.slice(beg, end));
4384
+ }
4385
+ return out.join('\n');
4386
+ }
4387
+
4388
+ static getHints() {
4389
+ const out = [];
4390
+ const vals = new Set();
4391
+ for ( const [ key, val ] of preparserTokens ) {
4392
+ if ( vals.has(val) ) { continue; }
4393
+ vals.add(val);
4394
+ out.push(key);
4395
+ }
4396
+ return out;
4397
+ }
4398
+
4399
+ static getTokens(env) {
4400
+ const out = new Map();
4401
+ for ( const [ key, val ] of preparserTokens ) {
4402
+ out.set(key, val !== 'false' && env.includes(val));
4403
+ }
4404
+ return Array.from(out);
4405
+ }
4406
+ }
4407
+
4408
+ return {
4409
+ preparser,
4410
+ };
4411
+ })();
4412
+
4413
+ /******************************************************************************/