@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.
- package/CHANGELOG.md +17 -1
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/pdf.post.body.json +8 -8
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chromium/http/content.post.body.json +8 -8
- package/build/routes/chromium/http/pdf.post.body.json +8 -8
- package/build/routes/chromium/http/scrape.post.body.json +8 -8
- package/build/routes/chromium/http/screenshot.post.body.json +8 -8
- package/build/routes/edge/http/content.post.body.json +8 -8
- package/build/routes/edge/http/pdf.post.body.json +8 -8
- package/build/routes/edge/http/scrape.post.body.json +8 -8
- package/build/routes/edge/http/screenshot.post.body.json +8 -8
- package/extensions/ublocklite/_locales/be/messages.json +9 -9
- package/extensions/ublocklite/_locales/cs/messages.json +7 -7
- package/extensions/ublocklite/_locales/hi/messages.json +1 -1
- package/extensions/ublocklite/_locales/ko/messages.json +7 -7
- package/extensions/ublocklite/_locales/vi/messages.json +8 -8
- package/extensions/ublocklite/_locales/zh_TW/messages.json +7 -7
- package/extensions/ublocklite/css/develop.css +1 -2
- package/extensions/ublocklite/js/arglist-parser.js +116 -0
- package/extensions/ublocklite/js/develop.js +18 -11
- package/extensions/ublocklite/js/jsonpath.js +470 -0
- package/extensions/ublocklite/js/redirect-resources.js +192 -0
- package/extensions/ublocklite/js/rw-dnr-editor.js +17 -3
- package/extensions/ublocklite/js/static-filtering-parser.js +4413 -0
- package/extensions/ublocklite/js/ubo-parser.js +461 -0
- package/extensions/ublocklite/lib/codemirror/cm6.bundle.ubol.min.js +1 -1
- package/extensions/ublocklite/lib/csstree/LICENSE +19 -0
- package/extensions/ublocklite/lib/csstree/css-tree.js +17 -0
- package/extensions/ublocklite/manifest.json +1 -1
- package/extensions/ublocklite/rulesets/generic-details.json +3 -8
- package/extensions/ublocklite/rulesets/main/annoyances-cookies.json +7 -5
- package/extensions/ublocklite/rulesets/main/annoyances-others.json +2 -1
- package/extensions/ublocklite/rulesets/main/chn-0.json +11 -7
- package/extensions/ublocklite/rulesets/main/deu-0.json +0 -3
- package/extensions/ublocklite/rulesets/main/easylist.json +13 -9
- package/extensions/ublocklite/rulesets/main/easyprivacy.json +17 -4
- package/extensions/ublocklite/rulesets/main/fra-0.json +4 -6
- package/extensions/ublocklite/rulesets/main/idn-0.json +1 -1
- package/extensions/ublocklite/rulesets/main/ind-0.json +13 -6
- package/extensions/ublocklite/rulesets/main/nld-0.json +1 -0
- package/extensions/ublocklite/rulesets/main/pgl.json +2 -11
- package/extensions/ublocklite/rulesets/main/rus-0.json +7 -4
- package/extensions/ublocklite/rulesets/main/stevenblack-hosts.json +2092 -363
- package/extensions/ublocklite/rulesets/main/swe-1.json +8 -7
- package/extensions/ublocklite/rulesets/main/tur-0.json +3 -0
- package/extensions/ublocklite/rulesets/main/ublock-badware.json +48 -49
- package/extensions/ublocklite/rulesets/main/ublock-filters.json +21 -17
- package/extensions/ublocklite/rulesets/main/urlhaus-full.json +272 -234
- package/extensions/ublocklite/rulesets/main/vie-1.json +18 -15
- package/extensions/ublocklite/rulesets/modify-headers/chn-0.json +1 -0
- package/extensions/ublocklite/rulesets/redirect/nld-0.json +19 -0
- package/extensions/ublocklite/rulesets/redirect/swe-1.json +1 -1
- package/extensions/ublocklite/rulesets/redirect/ublock-filters.json +5 -6
- package/extensions/ublocklite/rulesets/redirect/vie-1.json +2 -2
- package/extensions/ublocklite/rulesets/regex/chn-0.json +1 -1
- package/extensions/ublocklite/rulesets/regex/ublock-filters.json +5 -4
- package/extensions/ublocklite/rulesets/removeparam/adguard-spyware-url.json +1 -1
- package/extensions/ublocklite/rulesets/removeparam/rus-1.json +3 -4
- package/extensions/ublocklite/rulesets/ruleset-details.json +111 -111
- package/extensions/ublocklite/rulesets/scripting/declarative/annoyances-overlays.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/declarative/fra-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/declarative/rus-0.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/declarative/rus-1.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/declarative/tur-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/declarative/ublock-filters.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/generic/annoyances-cookies.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/generic/annoyances-social.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/generic/rus-0.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/generic/ublock-badware.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/generic/ublock-filters.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/generichigh/chn-0.css +1 -0
- package/extensions/ublocklite/rulesets/scripting/generichigh/idn-0.css +1 -0
- package/extensions/ublocklite/rulesets/scripting/generichigh/ublock-filters.css +0 -4
- package/extensions/ublocklite/rulesets/scripting/procedural/chn-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/procedural/easylist.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/procedural/rus-0.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/procedural/swe-1.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/procedural/ublock-filters.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.set-cookie.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.set-local-storage-item.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-click-element.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-set-cookie.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-set-local-storage-item.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-others.remove-cookie.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.addEventListener-defuser.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.prevent-fetch.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.set-cookie.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/fra-0.prevent-fetch.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/hun-0.prevent-fetch.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.addEventListener-defuser.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.abort-on-property-read.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-attr.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-constant.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-cookie.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-1.addEventListener-defuser.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-1.remove-cookie.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.prevent-window-open.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/swe-1.addEventListener-defuser.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.prevent-window-open.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.remove-node-text.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-current-script.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-on-property-read.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-on-stack-trace.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.addEventListener-defuser.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.adjust-setInterval.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.adjust-setTimeout.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.alert-buster.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.href-sanitizer.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.noeval-if.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-fetch.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-setTimeout.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-window-open.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-xhr.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.remove-cookie.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.remove-node-text.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-constant.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-local-storage-item.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-session-storage-item.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-argument.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-node-text.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-outbound-text.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-set-constant.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-set-local-storage-item.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-suppress-native-method.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.abort-current-script.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.abort-on-property-read.js +1 -1
- package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.set-constant.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/specific/adguard-mobile.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/annoyances-cookies.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/annoyances-others.js +2 -2
- package/extensions/ublocklite/rulesets/scripting/specific/annoyances-overlays.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/annoyances-social.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/chn-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/deu-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/easylist.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/fin-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/fra-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/hun-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/idn-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/ind-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/jpn-1.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/rus-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/spa-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/spa-1.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/swe-1.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/tur-0.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/ublock-filters.js +3 -3
- package/extensions/ublocklite/rulesets/scripting/specific/vie-1.js +3 -3
- package/extensions/ublocklite/rulesets/scriptlet-details.json +82 -70
- package/extensions/ublocklite/rulesets/strictblock/chn-0.json +64 -0
- package/extensions/ublocklite/rulesets/strictblock/easylist.json +1 -1
- package/extensions/ublocklite/rulesets/strictblock/easyprivacy.json +7 -1
- package/extensions/ublocklite/rulesets/strictblock/idn-0.json +0 -1
- package/extensions/ublocklite/rulesets/strictblock/pgl.json +2 -11
- package/extensions/ublocklite/rulesets/strictblock/stevenblack-hosts.json +2092 -363
- package/extensions/ublocklite/rulesets/strictblock/ublock-badware.json +633 -632
- package/extensions/ublocklite/rulesets/strictblock/ublock-filters.json +1 -1
- package/extensions/ublocklite/rulesets/strictblock/urlhaus-full.json +272 -234
- package/extensions/ublocklite/rulesets/urlskip/ublock-filters.json +6 -0
- package/extensions/ublocklite/ublock.zip +0 -0
- package/package.json +3 -3
- package/static/docs/swagger.json +10 -10
- 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
|
+
/******************************************************************************/
|