1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-24 11:22:44 +01:00

fix regression in 8f05a2f8d3 (related issue: #3683)

This commit is contained in:
Raymond Hill 2018-12-16 09:17:26 -05:00
parent 08d4604e31
commit d3a3505a54
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -146,7 +146,7 @@
};
const compileProceduralSelector = (function() {
var reProceduralOperator = new RegExp([
const reProceduralOperator = new RegExp([
'^(?:',
[
'-abp-contains',
@ -165,17 +165,17 @@
')\\('
].join(''));
var reEscapeRegex = /[.*+?^${}()|[\]\\]/g,
const reEscapeRegex = /[.*+?^${}()|[\]\\]/g,
reNeedScope = /^\s*[+>~]/,
reIsDanglingSelector = /(?:[+>~]\s*|\s+)$/;
var lastProceduralSelector = '',
lastProceduralSelectorCompiled,
regexToRawValue = new Map();
const regexToRawValue = new Map();
let lastProceduralSelector = '',
lastProceduralSelectorCompiled;
var compileText = function(s) {
var regexDetails,
match = reParseRegexLiteral.exec(s);
const compileText = function(s) {
const match = reParseRegexLiteral.exec(s);
let regexDetails;
if ( match !== null ) {
regexDetails = match[1];
if ( isBadRegex(regexDetails) ) { return; }
@ -189,13 +189,13 @@
return regexDetails;
};
var compileCSSDeclaration = function(s) {
var name, value, regexDetails,
pos = s.indexOf(':');
const compileCSSDeclaration = function(s) {
const pos = s.indexOf(':');
if ( pos === -1 ) { return; }
name = s.slice(0, pos).trim();
value = s.slice(pos + 1).trim();
var match = reParseRegexLiteral.exec(value);
const name = s.slice(0, pos).trim();
const value = s.slice(pos + 1).trim();
const match = reParseRegexLiteral.exec(value);
let regexDetails;
if ( match !== null ) {
regexDetails = match[1];
if ( isBadRegex(regexDetails) ) { return; }
@ -209,10 +209,7 @@
return { name: name, value: regexDetails };
};
var compileConditionalSelector = function(s) {
if ( isValidCSSSelector(s) ) {
return { selector: s, tasks: [] };
}
const compileConditionalSelector = function(s) {
// https://github.com/AdguardTeam/ExtendedCss/issues/31#issuecomment-302391277
// Prepend `:scope ` if needed.
if ( reNeedScope.test(s) ) {
@ -221,7 +218,17 @@
return compile(s);
};
var compileXpathExpression = function(s) {
const compileNotSelector = function(s) {
// https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588
// Reject instances of :not() filters for which the argument is
// a valid CSS selector, otherwise we would be adversely
// changing the behavior of CSS4's :not().
if ( isValidCSSSelector(s) === false ) {
return compileConditionalSelector(s);
}
};
const compileXpathExpression = function(s) {
try {
document.createExpression(s, null);
} catch (e) {
@ -231,13 +238,13 @@
};
// https://github.com/gorhill/uBlock/issues/2793
var normalizedOperators = new Map([
const normalizedOperators = new Map([
[ ':-abp-contains', ':has-text' ],
[ ':-abp-has', ':has' ],
[ ':contains', ':has-text' ],
]);
var compileArgument = new Map([
const compileArgument = new Map([
[ ':has', compileConditionalSelector ],
[ ':has-text', compileText ],
[ ':if', compileConditionalSelector ],
@ -245,7 +252,7 @@
[ ':matches-css', compileCSSDeclaration ],
[ ':matches-css-after', compileCSSDeclaration ],
[ ':matches-css-before', compileCSSDeclaration ],
[ ':not', compileConditionalSelector ],
[ ':not', compileNotSelector ],
[ ':xpath', compileXpathExpression ]
]);
@ -256,15 +263,14 @@
// to other blockers.
// The normalized string version is what is reported in the logger,
// by design.
var decompile = function(compiled) {
var tasks = compiled.tasks;
const decompile = function(compiled) {
const tasks = compiled.tasks;
if ( Array.isArray(tasks) === false ) {
return compiled.selector;
}
var raw = [ compiled.selector ],
value;
for ( var i = 0, n = tasks.length, task; i < n; i++ ) {
task = tasks[i];
const raw = [ compiled.selector ];
let value;
for ( let task of tasks ) {
switch ( task[0] ) {
case ':xpath':
raw.push(task[0], '(', task[1], ')');
@ -306,14 +312,15 @@
return raw.join('');
};
var compile = function(raw) {
const compile = function(raw) {
if ( raw === '' ) { return; }
var prefix = '',
let prefix = '',
tasks = [];
let i = 0,
n = raw.length,
opPrefixBeg = 0;
for (;;) {
var i = 0,
n = raw.length,
c, match;
let c, match;
// Advance to next operator.
while ( i < n ) {
c = raw.charCodeAt(i++);
@ -323,14 +330,14 @@
}
}
if ( i === n ) { break; }
var opNameBeg = i - 1;
var opNameEnd = i + match[0].length - 1;
const opNameBeg = i - 1;
const opNameEnd = i + match[0].length - 1;
i += match[0].length;
// Find end of argument: first balanced closing parenthesis.
// Note: unbalanced parenthesis can be used in a regex literal
// when they are escaped using `\`.
// TODO: need to handle quoted parentheses.
var pcnt = 1;
let pcnt = 1;
while ( i < n ) {
c = raw.charCodeAt(i++);
if ( c === 0x5C /* '\\' */ ) {
@ -345,27 +352,39 @@
// Unbalanced parenthesis? An unbalanced parenthesis is fine
// as long as the last character is a closing parenthesis.
if ( pcnt !== 0 && c !== 0x29 ) { return; }
// https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588
// Maybe that one operator is a valid CSS selector and if so,
// then consider it to be part of the prefix. If there is
// at least one task present, then we fail, as we do not
// support suffix CSS selectors.
// TODO: AdGuard does support suffix CSS selectors, so
// supporting this would increase compatibility with
// AdGuard filter lists.
if ( isValidCSSSelector(raw.slice(opNameBeg, i)) ) {
if ( opPrefixBeg !== 0 ) { return; }
continue;
}
// Extract and remember operator details.
var operator = raw.slice(opNameBeg, opNameEnd);
let operator = raw.slice(opNameBeg, opNameEnd);
operator = normalizedOperators.get(operator) || operator;
var args = raw.slice(opNameEnd + 1, i - 1);
let args = raw.slice(opNameEnd + 1, i - 1);
args = compileArgument.get(operator)(args);
if ( args === undefined ) { return; }
if ( tasks.length === 0 ) {
if ( opPrefixBeg === 0 ) {
prefix = raw.slice(0, opNameBeg);
} else if ( opNameBeg !== 0 ) {
} else if ( opNameBeg !== opPrefixBeg ) {
return;
}
tasks.push([ operator, args ]);
raw = raw.slice(i);
if ( i === n ) { break; }
opPrefixBeg = i;
}
// No task found: then we have a CSS selector.
// At least one task found: nothing should be left to parse.
if ( tasks.length === 0 ) {
prefix = raw;
tasks = undefined;
} else if ( raw.length !== 0 ) {
} else if ( i < n ) {
return;
}
// https://github.com/NanoAdblocker/NanoCore/issues/1#issuecomment-354394894
@ -376,7 +395,7 @@
return { selector: prefix, tasks: tasks };
};
var entryPoint = function(raw) {
const entryPoint = function(raw) {
if ( raw === lastProceduralSelector ) {
return lastProceduralSelectorCompiled;
}
@ -391,7 +410,7 @@
};
entryPoint.reset = function() {
regexToRawValue = new Map();
regexToRawValue.clear();
lastProceduralSelector = '';
lastProceduralSelectorCompiled = undefined;
};
@ -403,7 +422,18 @@
// Public API
//--------------------------------------------------------------------------
var api = {};
const api = {
get acceptedCount() {
return µb.cosmeticFilteringEngine.acceptedCount +
µb.scriptletFilteringEngine.acceptedCount +
µb.htmlFilteringEngine.acceptedCount;
},
get discardedCount() {
return µb.cosmeticFilteringEngine.discardedCount +
µb.scriptletFilteringEngine.discardedCount +
µb.htmlFilteringEngine.discardedCount;
}
};
//--------------------------------------------------------------------------
// Public classes
@ -464,12 +494,12 @@
};
api.HostnameBasedDB.prototype[Symbol.iterator] = (function() {
var Iter = function(db) {
const Iter = function(db) {
this.mapIter = db.values();
this.arrayIter = undefined;
};
Iter.prototype.next = function() {
var result;
let result;
if ( this.arrayIter !== undefined ) {
result = this.arrayIter.next();
if ( result.done === false ) { return result; }
@ -516,11 +546,11 @@
// Based on: FNV32a
// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-reference-source
// The rest is custom, suited for uBlock.
let i1 = token.length;
const i1 = token.length;
if ( i1 === 0 ) { return 0; }
let i2 = i1 >> 1;
let i4 = i1 >> 2;
let i8 = i1 >> 3;
const i2 = i1 >> 1;
const i4 = i1 >> 2;
const i8 = i1 >> 3;
let hval = (0x811c9dc5 ^ token.charCodeAt(0)) >>> 0;
hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
hval >>>= 0;
@ -553,7 +583,7 @@
api.compileHostnameToHash = function(hostname) {
let domain;
if ( hostname.endsWith('.*') ) {
let pos = hostname.lastIndexOf('.', hostname.length - 3);
const pos = hostname.lastIndexOf('.', hostname.length - 3);
domain = pos !== -1 ? hostname.slice(pos + 1) : hostname;
} else {
domain = µb.URI.domainFromHostnameNoCache(hostname);
@ -575,14 +605,14 @@
// Do not discard unknown pseudo-elements.
api.compileSelector = (function() {
var reAfterBeforeSelector = /^(.+?)(::?after|::?before|::[a-z-]+)$/,
reStyleSelector = /^(.+?):style\((.+?)\)$/,
reStyleBad = /url\([^)]+\)/,
reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/,
reExtendedSyntaxParser = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/,
div = document.createElement('div');
const reAfterBeforeSelector = /^(.+?)(::?after|::?before|::[a-z-]+)$/;
const reStyleSelector = /^(.+?):style\((.+?)\)$/;
const reStyleBad = /url\([^)]+\)/;
const reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/;
const reExtendedSyntaxParser = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/;
const div = document.createElement('div');
var normalizedExtendedSyntaxOperators = new Map([
const normalizedExtendedSyntaxOperators = new Map([
[ 'contains', ':has-text' ],
[ 'has', ':has' ],
[ 'matches-css', ':matches-css' ],
@ -590,7 +620,7 @@
[ 'matches-css-before', ':matches-css-before' ],
]);
var isValidStyleProperty = function(cssText) {
const isValidStyleProperty = function(cssText) {
if ( reStyleBad.test(cssText) ) { return false; }
div.style.cssText = cssText;
if ( div.style.cssText === '' ) { return false; }
@ -598,8 +628,8 @@
return true;
};
var entryPoint = function(raw) {
var extendedSyntax = reExtendedSyntax.test(raw);
const entryPoint = function(raw) {
const extendedSyntax = reExtendedSyntax.test(raw);
if ( isValidCSSSelector(raw) && extendedSyntax === false ) {
return raw;
}
@ -607,7 +637,7 @@
// We rarely reach this point -- majority of selectors are plain
// CSS selectors.
var matches, operator;
let matches, operator;
// Supported Adguard/ABP advanced selector syntax: will translate into
// uBO's syntax before further processing.
@ -627,7 +657,7 @@
return entryPoint(raw);
}
var selector = raw,
let selector = raw,
pseudoclass, style;
// `:style` selector?
@ -664,7 +694,7 @@
}
// Procedural selector?
var compiled;
let compiled;
if ( (compiled = compileProceduralSelector(raw)) ) {
return compiled;
}
@ -770,23 +800,6 @@
};
};
Object.defineProperties(api, {
acceptedCount: {
get: function() {
return µb.cosmeticFilteringEngine.acceptedCount +
µb.scriptletFilteringEngine.acceptedCount +
µb.htmlFilteringEngine.acceptedCount;
}
},
discardedCount: {
get: function() {
return µb.cosmeticFilteringEngine.discardedCount +
µb.scriptletFilteringEngine.discardedCount +
µb.htmlFilteringEngine.discardedCount;
}
}
});
api.fromSelfie = function(selfie) {
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic);
µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets);