diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 1ba69ad5c..dcc3f1482 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -388,7 +388,11 @@ vAPI.DOMFilterer = (function() { }; var PSelectorHasTextTask = function(task) { - this.needle = new RegExp(task[1]); + var arg0 = task[1], arg1; + if ( Array.isArray(task[1]) ) { + arg1 = arg0[1]; arg0 = arg0[0]; + } + this.needle = new RegExp(arg0, arg1); }; PSelectorHasTextTask.prototype.exec = function(input) { var output = []; @@ -423,7 +427,11 @@ vAPI.DOMFilterer = (function() { var PSelectorMatchesCSSTask = function(task) { this.name = task[1].name; - this.value = new RegExp(task[1].value); + var arg0 = task[1].value, arg1; + if ( Array.isArray(arg0) ) { + arg1 = arg0[1]; arg0 = arg0[0]; + } + this.value = new RegExp(arg0, arg1); }; PSelectorMatchesCSSTask.prototype.pseudo = null; PSelectorMatchesCSSTask.prototype.exec = function(input) { diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 6bde40b3c..1dec988cf 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -48,7 +48,11 @@ }; var PSelectorHasTextTask = function(task) { - this.needle = new RegExp(task[1]); + var arg0 = task[1], arg1; + if ( Array.isArray(task[1]) ) { + arg1 = arg0[1]; arg0 = arg0[0]; + } + this.needle = new RegExp(arg0, arg1); }; PSelectorHasTextTask.prototype.exec = function(input) { var output = []; diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index f7b98684f..f8ccff91d 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -53,7 +53,7 @@ var µb = µBlock, reHostnameSeparator = /\s*,\s*/, reHasUnicode = /[^\x00-\x7F]/, - reIsRegexLiteral = /^\/.+\/$/, + reParseRegexLiteral = /^\/(.+)\/([im]+)?$/, emptyArray = [], parsed = { hostnames: [], @@ -158,31 +158,39 @@ }; var compileText = function(s) { - var reText; - if ( reIsRegexLiteral.test(s) ) { - reText = s.slice(1, -1); - if ( isBadRegex(reText) ) { return; } + var regexDetails, + match = reParseRegexLiteral.exec(s); + if ( match !== null ) { + regexDetails = match[1]; + if ( isBadRegex(regexDetails) ) { return; } + if ( match[2] ) { + regexDetails = [ regexDetails, match[2] ]; + } } else { - reText = s.replace(reEscapeRegex, '\\$&'); - regexToRawValue.set(reText, s); + regexDetails = s.replace(reEscapeRegex, '\\$&'); + regexToRawValue.set(regexDetails, s); } - return reText; + return regexDetails; }; var compileCSSDeclaration = function(s) { - var name, value, reText, + var name, value, regexDetails, pos = s.indexOf(':'); if ( pos === -1 ) { return; } name = s.slice(0, pos).trim(); value = s.slice(pos + 1).trim(); - if ( reIsRegexLiteral.test(value) ) { - reText = value.slice(1, -1); - if ( isBadRegex(reText) ) { return; } + var match = reParseRegexLiteral.exec(value); + if ( match !== null ) { + regexDetails = match[1]; + if ( isBadRegex(regexDetails) ) { return; } + if ( match[2] ) { + regexDetails = [ regexDetails, match[2] ]; + } } else { - reText = '^' + value.replace(reEscapeRegex, '\\$&') + '$'; - regexToRawValue.set(reText, value); + regexDetails = '^' + value.replace(reEscapeRegex, '\\$&') + '$'; + regexToRawValue.set(regexDetails, value); } - return { name: name, value: reText }; + return { name: name, value: regexDetails }; }; var compileConditionalSelector = function(s) { @@ -229,38 +237,47 @@ // The normalized string version is what is reported in the logger, // by design. var decompile = function(compiled) { + var tasks = compiled.tasks; + if ( Array.isArray(tasks) === false ) { + return compiled.selector; + } var raw = [ compiled.selector ], - tasks = compiled.tasks, - value; - 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': + value; + 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': + if ( Array.isArray(task[1]) ) { + value = '/' + task[1][0] + '/' + task[1][1]; + } else { value = regexToRawValue.get(task[1]); if ( value === undefined ) { value = '/' + task[1] + '/'; } - raw.push(task[0], '(', value, ')'); - break; - case ':matches-css': - case ':matches-css-after': - case ':matches-css-before': + } + raw.push(task[0], '(', value, ')'); + break; + case ':matches-css': + case ':matches-css-after': + case ':matches-css-before': + if ( Array.isArray(task[1].value) ) { + value = '/' + task[1].value[0] + '/' + task[1].value[1]; + } else { value = regexToRawValue.get(task[1].value); if ( value === undefined ) { value = '/' + task[1].value + '/'; } - raw.push(task[0], '(', task[1].name, ': ', value, ')'); - break; - case ':if': - case ':if-not': - raw.push(task[0], '(', decompile(task[1]), ')'); - break; } + raw.push(task[0], '(', task[1].name, ': ', value, ')'); + break; + case ':if': + case ':if-not': + raw.push(task[0], '(', decompile(task[1]), ')'); + break; } } return raw.join('');