Implement network filter option replace= · gorhill/uBlock@7c3e060 (original) (raw)

`@@ -187,6 +187,7 @@ export const NODE_TYPE_NET_OPTION_NAME_POPUP = iota++;

`

187

187

`export const NODE_TYPE_NET_OPTION_NAME_REDIRECT = iota++;

`

188

188

`export const NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE = iota++;

`

189

189

`export const NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM = iota++;

`

``

190

`+

export const NODE_TYPE_NET_OPTION_NAME_REPLACE = iota++;

`

190

191

`export const NODE_TYPE_NET_OPTION_NAME_SCRIPT = iota++;

`

191

192

`export const NODE_TYPE_NET_OPTION_NAME_SHIDE = iota++;

`

192

193

`export const NODE_TYPE_NET_OPTION_NAME_TO = iota++;

`

`@@ -265,6 +266,7 @@ export const nodeTypeFromOptionName = new Map([

`

265

266

`/* synonym */ [ 'rewrite', NODE_TYPE_NET_OPTION_NAME_REDIRECT ],

`

266

267

`[ 'redirect-rule', NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE ],

`

267

268

`[ 'removeparam', NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM ],

`

``

269

`+

[ 'replace', NODE_TYPE_NET_OPTION_NAME_REPLACE ],

`

268

270

`/* synonym */ [ 'queryprune', NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM ],

`

269

271

`[ 'script', NODE_TYPE_NET_OPTION_NAME_SCRIPT ],

`

270

272

`[ 'shide', NODE_TYPE_NET_OPTION_NAME_SHIDE ],

`

`@@ -597,9 +599,14 @@ const exCharCodeAt = (s, i) => {

`

597

599

`return pos >= 0 ? s.charCodeAt(pos) : -1;

`

598

600

`};

`

599

601

``

``

602

`+

const toEscapedCharRegex = c => {

`

``

603

`+

const safe = c.replace(/[.*+?^${}()|[]\]/g, '\$&');

`

``

604

`` +

return new RegExp(((?:^|[^\\\\])(?:\\\\\\\\)*)\\\\${safe}, 'g');

``

``

605

`+

};

`

``

606

+

600

607

`/******************************************************************************/

`

601

608

``

602

``

`-

class argListParser {

`

``

609

`+

class ArgListParser {

`

603

610

`constructor(separatorChar = ',', mustQuote = false) {

`

604

611

`this.separatorChar = this.actualSeparatorChar = separatorChar;

`

605

612

`this.separatorCode = this.actualSeparatorCode = separatorChar.charCodeAt(0);

`

`@@ -612,10 +619,10 @@ class argListParser {

`

612

619

`this.reWhitespaceStart = /^\s+/;

`

613

620

`this.reWhitespaceEnd = /\s+$/;

`

614

621

`this.reOddTrailingEscape = /(?:^|[^\])(?:\\)*\$/;

`

615

``

`-

this.reEscapedDoubleQuote = /((?:^|[^\])(?:\\)*)\"/g;

`

616

``

`-

this.reEscapedSingleQuote = /((?:^|[^\])(?:\\)*)\'/g;

`

617

``

`` -

this.reEscapedBacktick = /((?:^|[^\])(?:\\)*)\`/g;

``

618

``

`` -

this.reEscapedSeparator = new RegExp(((?:^|[^\\\\])(?:\\\\\\\\)*)\\\\${this.separatorChar}, 'g');

``

``

622

`+

this.reEscapedDoubleQuote = toEscapedCharRegex('"');

`

``

623

`+

this.reEscapedSingleQuote = toEscapedCharRegex("'");

`

``

624

`` +

this.reEscapedBacktick = toEscapedCharRegex('`');

``

``

625

`+

this.reEscapedSeparator = toEscapedCharRegex(this.separatorChar);

`

619

626

`` this.unescapedSeparator = $1${this.separatorChar};

``

620

627

`}

`

621

628

`nextArg(pattern, beg = 0) {

`

`@@ -871,7 +878,7 @@ export class AstFilterParser {

`

871

878

`this.rePlainEntity = /^(?:[\da-z][\da-z_-]*.)+*$/;

`

872

879

`this.reHostsSink = /^[\w%.:[]-]+\s+/;

`

873

880

`this.reHostsRedirect = /(?:0.0.0.0|broadcasthost|local|localhost(?:.localdomain)?|ip6-\w+)(?:[^\w.-]|$)/;

`

874

``

`-

this.reNetOptionComma = /,(?!\d*})/g;

`

``

881

`+

this.reNetOptionComma = /,(?:~?[13a-z-]+(?:=.*?)?|_+)(?:,|$)/;

`

875

882

`this.rePointlessLeftAnchor = /^||?*+/;

`

876

883

`this.reIsTokenChar = /^[%0-9A-Za-z]/;

`

877

884

`this.rePointlessLeadingWildcards = /^(*+)[^%0-9A-Za-z\u{a0}-\u{10FFFF}]/u;

`

`@@ -898,7 +905,7 @@ export class AstFilterParser {

`

898

905

`this.reGoodRegexToken = /[^\x01%0-9A-Za-z][%0-9A-Za-z]{7,}|[^\x01%0-9A-Za-z][%0-9A-Za-z]{1,6}[^\x01%0-9A-Za-z]/;

`

899

906

`this.reBadCSP = /(?:=|;)\s*report-(?:to|uri)\b/;

`

900

907

`this.reNoopOption = /^_+$/;

`

901

``

`-

this.scriptletArgListParser = new argListParser(',');

`

``

908

`+

this.scriptletArgListParser = new ArgListParser(',');

`

902

909

`}

`

903

910

``

904

911

`parse(raw) {

`

`@@ -1414,6 +1421,7 @@ export class AstFilterParser {

`

1414

1421

`break;

`

1415

1422

`case NODE_TYPE_NET_OPTION_NAME_REDIRECT:

`

1416

1423

`case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE:

`

``

1424

`+

case NODE_TYPE_NET_OPTION_NAME_REPLACE:

`

1417

1425

`case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM:

`

1418

1426

`realBad = isNegated || (isException || hasValue) === false ||

`

1419

1427

`modifierType !== 0;

`

`@@ -1474,6 +1482,20 @@ export class AstFilterParser {

`

1474

1482

`realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount;

`

1475

1483

`break;

`

1476

1484

`}

`

``

1485

`+

case NODE_TYPE_NET_OPTION_NAME_REPLACE: {

`

``

1486

`+

realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount;

`

``

1487

`+

if ( realBad ) { break; }

`

``

1488

`+

if ( this.options.trustedSource !== true ) {

`

``

1489

`+

this.astError = AST_ERROR_UNTRUSTED_SOURCE;

`

``

1490

`+

realBad = true;

`

``

1491

`+

break;

`

``

1492

`+

}

`

``

1493

`+

if ( this.interactive ) {

`

``

1494

`+

const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_REPLACE);

`

``

1495

`+

realBad = parseReplaceValue(value) === undefined;

`

``

1496

`+

}

`

``

1497

`+

break;

`

``

1498

`+

}

`

1477

1499

`case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM:

`

1478

1500

`realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount;

`

1479

1501

`if ( realBad ) { break; }

`

`@@ -1959,9 +1981,8 @@ export class AstFilterParser {

`

1959

1981

`}

`

1960

1982

``

1961

1983

`endOfNetOption(s, beg) {

`

1962

``

`-

this.reNetOptionComma.lastIndex = beg;

`

1963

``

`-

const match = this.reNetOptionComma.exec(s);

`

1964

``

`-

return match !== null ? match.index : s.length;

`

``

1984

`+

const match = this.reNetOptionComma.exec(s.slice(beg));

`

``

1985

`+

return match !== null ? beg + match.index : s.length;

`

1965

1986

`}

`

1966

1987

``

1967

1988

`parseNetOption(parent) {

`

`@@ -2975,6 +2996,39 @@ export function parseHeaderValue(arg) {

`

2975

2996

`return out;

`

2976

2997

`}

`

2977

2998

``

``

2999

+

``

3000

`+

// https://adguard.com/kb/general/ad-filtering/create-own-filters/#replace-modifier

`

``

3001

+

``

3002

`+

export function parseReplaceValue(s) {

`

``

3003

`+

if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; }

`

``

3004

`+

const { reEscapedComma, reEscapedDollarSign } = parseReplaceValue;

`

``

3005

`+

const parser = new ArgListParser('/');

`

``

3006

`+

parser.nextArg(s, 1);

`

``

3007

`+

let pattern = s.slice(parser.argBeg, parser.argEnd);

`

``

3008

`+

if ( parser.transform ) {

`

``

3009

`+

pattern = parser.normalizeArg(pattern);

`

``

3010

`+

}

`

``

3011

`+

pattern = pattern

`

``

3012

`+

.replace(reEscapedDollarSign, '$1$$$')

`

``

3013

`+

.replace(reEscapedComma, '$1,');

`

``

3014

`+

parser.nextArg(s, parser.separatorEnd);

`

``

3015

`+

let replacement = s.slice(parser.argBeg, parser.argEnd);

`

``

3016

`+

if ( parser.separatorEnd === parser.separatorBeg ) { return; }

`

``

3017

`+

if ( parser.transform ) {

`

``

3018

`+

replacement = parser.normalizeArg(replacement);

`

``

3019

`+

}

`

``

3020

`+

replacement = replacement

`

``

3021

`+

.replace(reEscapedDollarSign, '$1$$')

`

``

3022

`+

.replace(reEscapedComma, '$1,');

`

``

3023

`+

const flags = s.slice(parser.separatorEnd);

`

``

3024

`+

try {

`

``

3025

`+

return { re: new RegExp(pattern, flags), replacement };

`

``

3026

`+

} catch(_) {

`

``

3027

`+

}

`

``

3028

`+

}

`

``

3029

`+

parseReplaceValue.reEscapedDollarSign = toEscapedCharRegex('$');

`

``

3030

`+

parseReplaceValue.reEscapedComma = toEscapedCharRegex(',');

`

``

3031

+

2978

3032

`/******************************************************************************/

`

2979

3033

``

2980

3034

`export const netOptionTokenDescriptors = new Map([

`

`@@ -3025,6 +3079,7 @@ export const netOptionTokenDescriptors = new Map([

`

3025

3079

`/* synonym */ [ 'rewrite', { mustAssign: true } ],

`

3026

3080

`[ 'redirect-rule', { mustAssign: true } ],

`

3027

3081

`[ 'removeparam', { } ],

`

``

3082

`+

[ 'replace', { mustAssign: true } ],

`

3028

3083

`/* synonym */ [ 'queryprune', { } ],

`

3029

3084

`[ 'script', { canNegate: true } ],

`

3030

3085

`[ 'shide', { } ],

`