This commit is contained in:
gorhill 2017-09-30 10:18:41 -04:00
parent b3e4caa59f
commit ca299a394f
No known key found for this signature in database
GPG key ID: 25E1490B761470C2

View file

@ -723,9 +723,17 @@ FilterContainer.prototype.compileSelector = (function() {
reStyleBad = /url\([^)]+\)/, reStyleBad = /url\([^)]+\)/,
reScriptSelector = /^script:(contains|inject)\((.+)\)$/, reScriptSelector = /^script:(contains|inject)\((.+)\)$/,
reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/, reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/,
reExtendedSyntaxHas = /\[-ext-has=(['"])(.+?)\1\]/, reExtendedSyntaxParser = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/,
div = document.createElement('div'); div = document.createElement('div');
var normalizedExtendedSyntaxOperators = new Map([
[ 'contains', ':has-text' ],
[ 'has', ':if' ],
[ 'matches-css', ':matches-css' ],
[ 'matches-css-after', ':matches-css-after' ],
[ 'matches-css-before', ':matches-css-before' ],
]);
var isValidStyleProperty = function(cssText) { var isValidStyleProperty = function(cssText) {
if ( reStyleBad.test(cssText) ) { return false; } if ( reStyleBad.test(cssText) ) { return false; }
div.style.cssText = cssText; div.style.cssText = cssText;
@ -735,32 +743,32 @@ FilterContainer.prototype.compileSelector = (function() {
}; };
var entryPoint = function(raw) { var entryPoint = function(raw) {
if ( isValidCSSSelector(raw) && reExtendedSyntax.test(raw) === false ) { var extendedSyntax = reExtendedSyntax.test(raw);
if ( isValidCSSSelector(raw) && extendedSyntax === false ) {
return raw; return raw;
} }
// We rarely reach this point -- majority of selectors are plain // We rarely reach this point -- majority of selectors are plain
// CSS selectors. // CSS selectors.
// Unsupported ABP's advanced selector syntax. var matches, operator;
if ( raw.indexOf('[-abp-properties=') !== -1 ) {
return;
}
var matches; // Supported Adguard/ABP advanced selector syntax: will translate into
// Supported Adguard's advanced selector syntax: will translate into
// uBO's syntax before further processing. // uBO's syntax before further processing.
// // Mind unsupported advanced selector syntax, such as ABP's
// [-ext-has=...] // `-abp-properties`.
// Converted to `:if(...)`, because Adguard accepts procedural // Note: extended selector syntax has been deprecated in ABP, in favor
// selectors within its `:has(...)` selector. // of the procedural one (i.e. `:operator(...)`). See
if ( (matches = reExtendedSyntaxHas.exec(raw)) !== null ) { // https://issues.adblockplus.org/ticket/5287
return this.compileSelector( if ( extendedSyntax ) {
raw.slice(0, matches.index) + while ( (matches = reExtendedSyntaxParser.exec(raw)) !== null ) {
':if('+ matches[2].replace(/:contains\(/g, ':has-text(') + ')' + operator = normalizedExtendedSyntaxOperators.get(matches[1]);
raw.slice(matches.index + matches[0].length) if ( operator === undefined ) { return; }
); raw = raw.slice(0, matches.index) +
operator + '(' + matches[3] + ')' +
raw.slice(matches.index + matches[0].length);
}
return this.compileSelector(raw);
} }
var selector = raw, var selector = raw,
@ -838,7 +846,8 @@ FilterContainer.prototype.compileProceduralSelector = (function() {
reFirstParentheses = /^\(*/, reFirstParentheses = /^\(*/,
reLastParentheses = /\)*$/, reLastParentheses = /\)*$/,
reEscapeRegex = /[.*+?^${}()|[\]\\]/g, reEscapeRegex = /[.*+?^${}()|[\]\\]/g,
reNeedScope = /^\s*[+>~]/; reNeedScope = /^\s*[+>~]/,
reAllForwardSlashes = /\//g;
var lastProceduralSelector = '', var lastProceduralSelector = '',
lastProceduralSelectorCompiled; lastProceduralSelectorCompiled;
@ -916,6 +925,53 @@ FilterContainer.prototype.compileProceduralSelector = (function() {
[ ':xpath', compileXpathExpression ] [ ':xpath', compileXpathExpression ]
]); ]);
// https://github.com/gorhill/uBlock/issues/2793#issuecomment-333269387
// - Normalize (somewhat) the stringified version of procedural cosmetic
// filters -- this increase the likelihood of detecting duplicates given
// that uBO is able to understand syntax specific to other blockers.
// The normalized string version is what is reported in the logger, by
// design.
var decompile = function(compiled) {
var raw = [ compiled.selector ],
tasks = compiled.tasks;
if ( Array.isArray(tasks) ) {
for ( var i = 0, n = tasks.length, task; i < n; i++ ) {
task = tasks[i];
switch ( task[0] ) {
case ':has':
case ':xpath':
raw.push(task[0], '(', task[1], ')');
break;
case ':has-text':
raw.push(
task[0],
'(/',
task[1].replace(reAllForwardSlashes, '\\/'),
'/)'
);
break;
case ':matches-css':
case ':matches-css-after':
case ':matches-css-before':
raw.push(
task[0],
'(',
task[1].name,
': /',
task[1].value.replace(reAllForwardSlashes, '\\/'),
'/)'
);
break;
case ':if':
case ':if-not':
raw.push(task[0], '(', decompile(task[1]), ')');
break;
}
}
}
return raw.join('');
};
var compile = function(raw) { var compile = function(raw) {
var matches = reOperatorParser.exec(raw); var matches = reOperatorParser.exec(raw);
if ( matches === null ) { if ( matches === null ) {
@ -970,7 +1026,7 @@ FilterContainer.prototype.compileProceduralSelector = (function() {
lastProceduralSelector = raw; lastProceduralSelector = raw;
var compiled = compile(raw); var compiled = compile(raw);
if ( compiled !== undefined ) { if ( compiled !== undefined ) {
compiled.raw = raw; compiled.raw = decompile(compiled);
compiled = JSON.stringify(compiled); compiled = JSON.stringify(compiled);
} }
lastProceduralSelectorCompiled = compiled; lastProceduralSelectorCompiled = compiled;
@ -1027,7 +1083,6 @@ FilterContainer.prototype.compile = function(s, writer) {
return false; return false;
} }
if ( parsed.invalid ) { if ( parsed.invalid ) {
//console.error("uBlock Origin> discarding invalid cosmetic filter '%s'", s);
return true; return true;
} }