Skip to content

Commit 58bfe4c

Browse files
committed
Add support to parse Adguard's [$domain=/.../] regex-based modifier
The modifier will be converted to one that is compatible with uBO, i.e.: [$domain=/^trgoals\d+\.xyz$/]##+js(set-constant, isShow, true) Will parsed as equivalent of: /^trgoals\d+\.xyz$/##+js(set-constant, isShow, true) Related issue: AdguardTeam/FiltersCompiler#204 Reference: https://adguard.com/kb/general/ad-filtering/create-own-filters/#non-basic-domain-modifier
1 parent ec3852b commit 58bfe4c

File tree

1 file changed

+66
-41
lines changed

1 file changed

+66
-41
lines changed

src/js/static-filtering-parser.js

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,6 +1988,7 @@ export class AstFilterParser {
19881988
if ( parentEnd === parentBeg ) { return 0; }
19891989
const s = this.getNodeString(parent);
19901990
const optionsEnd = s.length;
1991+
const parseDetails = { node: 0, len: 0 };
19911992
const head = this.allocHeadNode();
19921993
let prev = head, next = 0;
19931994
let optionBeg = 0, optionEnd = 0;
@@ -1997,11 +1998,11 @@ export class AstFilterParser {
19971998
parentBeg + optionBeg,
19981999
parentBeg + optionsEnd // open ended
19992000
);
2000-
const { node: down, len: optionLen } = this.parseNetOption(next);
2001+
this.parseNetOption(next, parseDetails);
20012002
// set next's end to down's end
2002-
optionEnd += optionLen;
2003+
optionEnd += parseDetails.len;
20032004
this.nodes[next+NODE_END_INDEX] = parentBeg + optionEnd;
2004-
this.linkDown(next, down);
2005+
this.linkDown(next, parseDetails.node);
20052006
prev = this.linkRight(prev, next);
20062007
if ( optionEnd === optionsEnd ) { break; }
20072008
optionBeg = optionEnd + 1;
@@ -2010,7 +2011,7 @@ export class AstFilterParser {
20102011
parentBeg + optionEnd,
20112012
parentBeg + optionBeg
20122013
);
2013-
if ( optionLen === 0 || optionBeg === optionsEnd ) {
2014+
if ( parseDetails.len === 0 || optionBeg === optionsEnd ) {
20142015
this.addNodeFlags(next, NODE_FLAG_ERROR);
20152016
this.addFlags(AST_FLAG_HAS_ERROR);
20162017
}
@@ -2023,15 +2024,17 @@ export class AstFilterParser {
20232024
return this.throwHeadNode(head);
20242025
}
20252026

2026-
parseNetOption(parent) {
2027+
parseNetOption(parent, parseDetails) {
20272028
const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
20282029
const s = this.getNodeString(parent);
20292030
const match = this.reNetOption.exec(s) || [];
20302031
if ( match.length === 0 ) {
20312032
this.addNodeFlags(parent, NODE_FLAG_ERROR);
20322033
this.addFlags(AST_FLAG_HAS_ERROR);
20332034
this.astError = AST_ERROR_OPTION_UNKNOWN;
2034-
return { node: 0, len: s.length };
2035+
parseDetails.node = 0;
2036+
parseDetails.len = s.length;
2037+
return;
20352038
}
20362039
const head = this.allocHeadNode();
20372040
let prev = head, next = 0;
@@ -2073,7 +2076,9 @@ export class AstFilterParser {
20732076
}
20742077
prev = this.linkRight(prev, next);
20752078
if ( assigned === false ) {
2076-
return { node: this.throwHeadNode(head), len: matchEnd };
2079+
parseDetails.node = this.throwHeadNode(head);
2080+
parseDetails.len = matchEnd;
2081+
return;
20772082
}
20782083
next = this.allocTypedNode(
20792084
NODE_TYPE_NET_OPTION_ASSIGN,
@@ -2129,7 +2134,8 @@ export class AstFilterParser {
21292134
);
21302135
this.linkRight(prev, next);
21312136
}
2132-
return { node: this.throwHeadNode(head), len: details.quoteEnd };
2137+
parseDetails.node = this.throwHeadNode(head);
2138+
parseDetails.len = details.quoteEnd;
21332139
}
21342140

21352141
endOfNetOption(s, beg) {
@@ -2156,32 +2162,26 @@ export class AstFilterParser {
21562162
);
21572163
if ( parentEnd === parentBeg ) { return containerNode; }
21582164
const separatorCode = separator.charCodeAt(0);
2165+
const parseDetails = { separator, mode, node: 0, len: 0 };
21592166
const listNode = this.allocHeadNode();
21602167
let prev = listNode;
21612168
let domainNode = 0;
21622169
let separatorNode = 0;
21632170
const s = this.getNodeString(parent);
21642171
const listEnd = s.length;
2165-
let beg = 0, end = 0, c = 0;
2172+
let beg = 0, end = 0;
21662173
while ( beg < listEnd ) {
2167-
c = s.charCodeAt(beg);
2168-
if ( c === 0x7E /* ~ */ ) {
2169-
c = s.charCodeAt(beg+1) || 0;
2170-
}
2171-
if ( c !== 0x2F /* / */ ) {
2172-
end = s.indexOf(separator, beg);
2173-
} else {
2174-
end = s.indexOf('/', beg+1);
2175-
end = s.indexOf(separator, end !== -1 ? end+1 : beg);
2176-
}
2177-
if ( end === -1 ) { end = listEnd; }
2174+
const next = this.allocTypedNode(
2175+
NODE_TYPE_OPTION_VALUE_DOMAIN_RAW,
2176+
parentBeg + beg,
2177+
parentBeg + listEnd // open ended
2178+
);
2179+
this.parseDomain(next, parseDetails);
2180+
end = beg + parseDetails.len;
2181+
this.nodes[next+NODE_END_INDEX] = parentBeg + end;
21782182
if ( end !== beg ) {
2179-
domainNode = this.allocTypedNode(
2180-
NODE_TYPE_OPTION_VALUE_DOMAIN_RAW,
2181-
parentBeg + beg,
2182-
parentBeg + end
2183-
);
2184-
this.linkDown(domainNode, this.parseDomain(domainNode, mode));
2183+
domainNode = next;
2184+
this.linkDown(domainNode, parseDetails.node);
21852185
prev = this.linkRight(prev, domainNode);
21862186
} else {
21872187
domainNode = 0;
@@ -2217,23 +2217,38 @@ export class AstFilterParser {
22172217
return containerNode;
22182218
}
22192219

2220-
parseDomain(parent, mode = 0b0000) {
2220+
parseDomain(parent, parseDetails) {
22212221
const parentBeg = this.nodes[parent+NODE_BEG_INDEX];
22222222
const parentEnd = this.nodes[parent+NODE_END_INDEX];
2223+
const not = this.charCodeAt(parentBeg) === 0x7E /* ~ */;
22232224
let head = 0, next = 0;
22242225
let beg = parentBeg;
2225-
const c = this.charCodeAt(beg);
2226-
if ( c === 0x7E /* ~ */ ) {
2226+
if ( not ) {
22272227
this.addNodeFlags(parent, NODE_FLAG_IS_NEGATED);
22282228
head = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_NOT, beg, beg + 1);
2229-
if ( (mode & 0b1000) === 0 ) {
2229+
if ( (parseDetails.mode & 0b1000) === 0 ) {
22302230
this.addNodeFlags(parent, NODE_FLAG_ERROR);
22312231
}
22322232
beg += 1;
22332233
}
2234-
if ( beg !== parentEnd ) {
2235-
next = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_DOMAIN, beg, parentEnd);
2236-
const hn = this.normalizeDomainValue(this.getNodeString(next), mode);
2234+
const c0 = this.charCodeAt(beg);
2235+
let end = beg;
2236+
let type = 0;
2237+
if ( c0 === 0x2F /* / */ ) {
2238+
end = this.indexOf('/', beg + 1, parentEnd);
2239+
if ( end !== -1 ) { end += 1; }
2240+
type = 1;
2241+
} else if ( c0 === 0x5B /* [ */ && this.startsWith('[$domain=/', beg) ) {
2242+
end = this.indexOf('/]', beg + 10, parentEnd);
2243+
if ( end !== -1 ) { end += 2; }
2244+
type = 2;
2245+
} else {
2246+
end = this.indexOf(parseDetails.separator, end, parentEnd);
2247+
}
2248+
if ( end === -1 ) { end = parentEnd; }
2249+
if ( beg !== end ) {
2250+
next = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_DOMAIN, beg, end);
2251+
const hn = this.normalizeDomainValue(next, type, parseDetails.mode);
22372252
if ( hn !== undefined ) {
22382253
if ( hn !== '' ) {
22392254
this.setNodeTransform(next, hn);
@@ -2252,7 +2267,8 @@ export class AstFilterParser {
22522267
this.addNodeFlags(parent, NODE_FLAG_ERROR);
22532268
this.addFlags(AST_FLAG_HAS_ERROR);
22542269
}
2255-
return head;
2270+
parseDetails.node = head;
2271+
parseDetails.len = end - parentBeg;
22562272
}
22572273

22582274
// mode bits:
@@ -2261,16 +2277,16 @@ export class AstFilterParser {
22612277
// 0b00100: can use single wildcard
22622278
// 0b01000: can be negated
22632279
// 0b10000: can be a regex
2264-
normalizeDomainValue(s, modeBits) {
2265-
if ( (modeBits & 0b10000) === 0 ||
2266-
s.length <= 2 ||
2267-
s.charCodeAt(0) !== 0x2F /* / */ ||
2268-
exCharCodeAt(s, -1) !== 0x2F /* / */
2269-
) {
2280+
normalizeDomainValue(node, type, modeBits) {
2281+
const s = this.getNodeString(node);
2282+
if ( type === 0 ) {
22702283
return this.normalizeHostnameValue(s, modeBits);
22712284
}
2272-
const source = this.normalizeRegexPattern(s);
2285+
if ( (modeBits & 0b10000) === 0 ) { return ''; }
2286+
const regex = type === 1 ? s : `/${s.slice(10, -2)}/`;
2287+
const source = this.normalizeRegexPattern(regex);
22732288
if ( source === '' ) { return ''; }
2289+
if ( type === 1 && source === regex ) { return; }
22742290
return `/${source}/`;
22752291
}
22762292

@@ -2857,6 +2873,15 @@ export class AstFilterParser {
28572873
return pos < this.rawEnd ? this.raw.charCodeAt(pos) : -1;
28582874
}
28592875

2876+
indexOf(needle, beg, end = 0) {
2877+
const haystack = end === 0 ? this.raw : this.raw.slice(0, end);
2878+
return haystack.indexOf(needle, beg);
2879+
}
2880+
2881+
startsWith(s, pos) {
2882+
return pos < this.rawEnd && this.raw.startsWith(s, pos);
2883+
}
2884+
28602885
isTokenCharCode(c) {
28612886
return c === 0x25 ||
28622887
c >= 0x30 && c <= 0x39 ||

0 commit comments

Comments
 (0)