mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
Add ability to parse removeparam= as queryprune=
Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/1356
Related commit:
- bde3164eb4
It is not possible to achieve perfect compatiblity at this
point, but reasonable compatibility should be achieved for
a majority of instances of `removeparam=`.
Notable differences:
--------------------
uBO always matches in a case insensitive manner, there is
no need to ask for case-insensitivity, and no need to use
uppercase characters in `queryprune=` values.
uBO does not escape special regex characters since the
`queryprune=` values are always assumed to be literal
regex expression (leaving out the documented special
characters). This means `removeparam=` with characters
which are special regex characters won't be properly
translated and are unlikely to work properly in uBO.
For example, the `queryprune` value of a filter such as
`$removeparam=__xts__[0]` internally become the literal
regex `/__xts__[0]/`, and consequently would not match
a query parameter such as `...?__xts__[0]=...`.
Notes:
------
Additionally, for performance reason, when uBO encounter
a pattern-less `queryprune=` (or `removeparam=`) filter,
it will try to extract a valid pattern from the
`queryprune=` value. For instance, the following filter:
$queryprune=utm_campaign
Will be translated internally into:
utm_campaign$queryprune=utm_campaign
The logger will reflect this internal translation.
This commit is contained in:
parent
80413dff83
commit
6ac09a2856
2 changed files with 74 additions and 20 deletions
|
|
@ -2092,6 +2092,7 @@ const netOptionTokenDescriptors = new Map([
|
||||||
[ 'popunder', OPTTokenPopunder | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
|
[ 'popunder', OPTTokenPopunder | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ],
|
||||||
[ 'popup', OPTTokenPopup | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ],
|
[ 'popup', OPTTokenPopup | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ],
|
||||||
[ 'queryprune', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ],
|
[ 'queryprune', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ],
|
||||||
|
[ 'removeparam', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ],
|
||||||
[ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ],
|
[ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ],
|
||||||
[ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType ],
|
[ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType ],
|
||||||
[ 'script', OPTTokenScript | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
|
[ 'script', OPTTokenScript | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
|
||||||
|
|
@ -2147,6 +2148,7 @@ Parser.netOptionTokenIds = new Map([
|
||||||
[ 'popunder', OPTTokenPopunder ],
|
[ 'popunder', OPTTokenPopunder ],
|
||||||
[ 'popup', OPTTokenPopup ],
|
[ 'popup', OPTTokenPopup ],
|
||||||
[ 'queryprune', OPTTokenQueryprune ],
|
[ 'queryprune', OPTTokenQueryprune ],
|
||||||
|
[ 'removeparam', OPTTokenQueryprune ],
|
||||||
[ 'redirect', OPTTokenRedirect ],
|
[ 'redirect', OPTTokenRedirect ],
|
||||||
[ 'redirect-rule', OPTTokenRedirectRule ],
|
[ 'redirect-rule', OPTTokenRedirectRule ],
|
||||||
[ 'script', OPTTokenScript ],
|
[ 'script', OPTTokenScript ],
|
||||||
|
|
|
||||||
|
|
@ -2628,7 +2628,7 @@ const FilterParser = class {
|
||||||
this.noTokenHash = urlTokenizer.noTokenHash;
|
this.noTokenHash = urlTokenizer.noTokenHash;
|
||||||
this.reIsolateHostname = /^(\*?\.)?([^\x00-\x24\x26-\x2C\x2F\x3A-\x5E\x60\x7B-\x7F]+)(.*)/;
|
this.reIsolateHostname = /^(\*?\.)?([^\x00-\x24\x26-\x2C\x2F\x3A-\x5E\x60\x7B-\x7F]+)(.*)/;
|
||||||
this.reBadCSP = /(?:=|;)\s*report-(?:to|uri)\b/;
|
this.reBadCSP = /(?:=|;)\s*report-(?:to|uri)\b/;
|
||||||
this.reRegexToken = /[%0-9A-Za-z]+/g;
|
this.reToken = /[%0-9A-Za-z]+/g;
|
||||||
this.reRegexTokenAbort = /[\(\)\[\]]/;
|
this.reRegexTokenAbort = /[\(\)\[\]]/;
|
||||||
this.reRegexBadPrefix = /(^|[^\\]\.|\\[%SDWsdw]|[^\\][()*+?[\\\]{}])$/;
|
this.reRegexBadPrefix = /(^|[^\\]\.|\\[%SDWsdw]|[^\\][()*+?[\\\]{}])$/;
|
||||||
this.reRegexBadSuffix = /^([^\\]\.|\\[%SDWsdw]|[()*+?[\]{}]|$)/;
|
this.reRegexBadSuffix = /^([^\\]\.|\\[%SDWsdw]|[()*+?[\]{}]|$)/;
|
||||||
|
|
@ -3110,34 +3110,48 @@ const FilterParser = class {
|
||||||
// i.e. very common with a high probability of ending up as a miss,
|
// i.e. very common with a high probability of ending up as a miss,
|
||||||
// are not good. Avoid if possible. This has a significant positive
|
// are not good. Avoid if possible. This has a significant positive
|
||||||
// impact on performance.
|
// impact on performance.
|
||||||
|
//
|
||||||
|
// For pattern-less queryprune filters, try to derive a pattern from
|
||||||
|
// the queryprune value.
|
||||||
|
|
||||||
makeToken() {
|
makeToken() {
|
||||||
if ( this.pattern === '*' ) { return; }
|
if ( this.pattern === '*' ) {
|
||||||
|
if (
|
||||||
|
this.modifyType !== this.parser.OPTTokenQueryprune ||
|
||||||
|
this.makePatternFromQuerypruneValue() === false
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if ( this.isRegex ) {
|
if ( this.isRegex ) {
|
||||||
return this.extractTokenFromRegex();
|
return this.extractTokenFromRegex();
|
||||||
}
|
}
|
||||||
const match = this.extractTokenFromPattern();
|
this.extractTokenFromPattern();
|
||||||
if ( match === null ) { return; }
|
|
||||||
this.token = match.token;
|
|
||||||
this.tokenHash = urlTokenizer.tokenHashFromString(this.token);
|
|
||||||
this.tokenBeg = match.pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: a one-char token is better than a documented bad token.
|
// Note: a one-char token is better than a documented bad token.
|
||||||
extractTokenFromPattern() {
|
extractTokenFromPattern() {
|
||||||
|
this.reToken.lastIndex = 0;
|
||||||
|
const pattern = this.pattern;
|
||||||
let bestMatch = null;
|
let bestMatch = null;
|
||||||
let bestBadness = 0x7FFFFFFF;
|
let bestBadness = 0x7FFFFFFF;
|
||||||
for ( const match of this.parser.patternTokens() ) {
|
for (;;) {
|
||||||
const badness = match.token.length > 1
|
const match = this.reToken.exec(pattern);
|
||||||
? this.badTokens.get(match.token) || 0
|
if ( match === null ) { break; }
|
||||||
|
const badness = match[0].length > 1
|
||||||
|
? this.badTokens.get(match[0]) || 0
|
||||||
: 1;
|
: 1;
|
||||||
if ( badness === 0 ) { return match; }
|
|
||||||
if ( badness < bestBadness ) {
|
if ( badness < bestBadness ) {
|
||||||
bestMatch = match;
|
bestMatch = match;
|
||||||
|
if ( badness === 0 ) { break; }
|
||||||
bestBadness = badness;
|
bestBadness = badness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bestMatch;
|
if ( bestMatch !== null ) {
|
||||||
|
this.token = bestMatch[0];
|
||||||
|
this.tokenHash = urlTokenizer.tokenHashFromString(this.token);
|
||||||
|
this.tokenBeg = bestMatch.index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/2781
|
// https://github.com/gorhill/uBlock/issues/2781
|
||||||
|
|
@ -3147,15 +3161,16 @@ const FilterParser = class {
|
||||||
// Mind `\b` directives: `/\bads\b/` should result in token being `ads`,
|
// Mind `\b` directives: `/\bads\b/` should result in token being `ads`,
|
||||||
// not `bads`.
|
// not `bads`.
|
||||||
extractTokenFromRegex() {
|
extractTokenFromRegex() {
|
||||||
this.reRegexToken.lastIndex = 0;
|
this.reToken.lastIndex = 0;
|
||||||
const s = this.pattern;
|
const pattern = this.pattern;
|
||||||
|
let bestToken;
|
||||||
let bestBadness = 0x7FFFFFFF;
|
let bestBadness = 0x7FFFFFFF;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const matches = this.reRegexToken.exec(s);
|
const matches = this.reToken.exec(pattern);
|
||||||
if ( matches === null ) { break; }
|
if ( matches === null ) { break; }
|
||||||
let token = matches[0];
|
let token = matches[0];
|
||||||
let prefix = s.slice(0, matches.index);
|
let prefix = pattern.slice(0, matches.index);
|
||||||
let suffix = s.slice(this.reRegexToken.lastIndex);
|
let suffix = pattern.slice(this.reToken.lastIndex);
|
||||||
if (
|
if (
|
||||||
this.reRegexTokenAbort.test(prefix) &&
|
this.reRegexTokenAbort.test(prefix) &&
|
||||||
this.reRegexTokenAbort.test(suffix)
|
this.reRegexTokenAbort.test(suffix)
|
||||||
|
|
@ -3181,13 +3196,47 @@ const FilterParser = class {
|
||||||
? this.badTokens.get(token) || 0
|
? this.badTokens.get(token) || 0
|
||||||
: 1;
|
: 1;
|
||||||
if ( badness < bestBadness ) {
|
if ( badness < bestBadness ) {
|
||||||
this.token = token.toLowerCase();
|
bestToken = token;
|
||||||
this.tokenHash = urlTokenizer.tokenHashFromString(this.token);
|
|
||||||
this.tokenBeg = matches.index;
|
|
||||||
if ( badness === 0 ) { break; }
|
if ( badness === 0 ) { break; }
|
||||||
bestBadness = badness;
|
bestBadness = badness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ( bestToken !== undefined ) {
|
||||||
|
this.token = bestToken.toLowerCase();
|
||||||
|
this.tokenHash = urlTokenizer.tokenHashFromString(this.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makePatternFromQuerypruneValue() {
|
||||||
|
let pattern = this.modifyValue;
|
||||||
|
if ( pattern === '*' || pattern.charCodeAt(0) === 0x21 /* '!' */ ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( /^\w+$/.test(pattern) ) {
|
||||||
|
this.pattern = `${pattern}=`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const reRegex = /^\/(.+)\/i?$/;
|
||||||
|
if ( reRegex.test(pattern) ) {
|
||||||
|
pattern = reRegex.exec(pattern)[1];
|
||||||
|
} else {
|
||||||
|
let prefix = '', suffix = '';
|
||||||
|
if ( pattern.startsWith('|') ) {
|
||||||
|
pattern = pattern.slice(1);
|
||||||
|
prefix = '\\b';
|
||||||
|
}
|
||||||
|
if ( pattern.endsWith('|') ) {
|
||||||
|
pattern = pattern.slice(0, -1);
|
||||||
|
suffix = '\\b';
|
||||||
|
}
|
||||||
|
if ( pattern.indexOf('|') !== -1 ) {
|
||||||
|
pattern = `(?:${pattern})`;
|
||||||
|
}
|
||||||
|
pattern = prefix + pattern + suffix;
|
||||||
|
}
|
||||||
|
this.pattern = pattern;
|
||||||
|
this.isRegex = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasNoOptionUnits() {
|
hasNoOptionUnits() {
|
||||||
|
|
@ -4288,6 +4337,7 @@ FilterContainer.prototype.filterQuery = function(fctxt) {
|
||||||
|
|
||||||
FilterContainer.prototype.parseFilterPruneValue = function(modifier) {
|
FilterContainer.prototype.parseFilterPruneValue = function(modifier) {
|
||||||
const cache = {};
|
const cache = {};
|
||||||
|
const reRegex = /^\/(.+)\/i?$/;
|
||||||
let retext = modifier.value;
|
let retext = modifier.value;
|
||||||
if ( retext === '*' ) {
|
if ( retext === '*' ) {
|
||||||
cache.all = true;
|
cache.all = true;
|
||||||
|
|
@ -4296,6 +4346,8 @@ FilterContainer.prototype.parseFilterPruneValue = function(modifier) {
|
||||||
if ( cache.not ) { retext = retext.slice(1); }
|
if ( cache.not ) { retext = retext.slice(1); }
|
||||||
if ( /^\w+$/.test(retext) ) {
|
if ( /^\w+$/.test(retext) ) {
|
||||||
retext = `^${retext}=`;
|
retext = `^${retext}=`;
|
||||||
|
} else if ( reRegex.test(retext) ) {
|
||||||
|
retext = reRegex.exec(retext)[1];
|
||||||
} else {
|
} else {
|
||||||
if ( retext.startsWith('|') ) { retext = `^${retext.slice(1)}`; }
|
if ( retext.startsWith('|') ) { retext = `^${retext.slice(1)}`; }
|
||||||
if ( retext.endsWith('|') ) { retext = `${retext.slice(0,-1)}$`; }
|
if ( retext.endsWith('|') ) { retext = `${retext.slice(0,-1)}$`; }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue