diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 4c0f2df4f9868..05f5446a8578e 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -540,6 +540,21 @@ vAPI.DOMFilterer = (function() { } }; + const PSelectorMinTextLengthTask = class { + constructor(task) { + this.min = task[1]; + } + exec(input) { + const output = []; + for ( const node of input ) { + if ( node.textContent.length >= this.min ) { + output.push(node); + } + } + return output; + } + }; + const PSelectorNthAncestorTask = class { constructor(task) { this.nth = task[1]; @@ -658,6 +673,7 @@ vAPI.DOMFilterer = (function() { [ ':matches-css', PSelectorMatchesCSSTask ], [ ':matches-css-after', PSelectorMatchesCSSAfterTask ], [ ':matches-css-before', PSelectorMatchesCSSBeforeTask ], + [ ':min-text-length', PSelectorMinTextLengthTask ], [ ':not', PSelectorIfNotTask ], [ ':nth-ancestor', PSelectorNthAncestorTask ], [ ':spath', PSelectorSpathTask ], diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 129ffabe67de7..e79fd401c7735 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -82,11 +82,26 @@ const PSelectorIfNotTask = class extends PSelectorIfTask { constructor(task) { - super.call(task); + super(task); this.target = false; } }; + const PSelectorMinTextLengthTask = class { + constructor(task) { + this.min = task[1]; + } + exec(input) { + const output = []; + for ( const node of input ) { + if ( node.textContent.length >= this.min ) { + output.push(node); + } + } + return output; + } + }; + const PSelectorNthAncestorTask = class { constructor(task) { this.nth = task[1]; @@ -191,6 +206,7 @@ [ ':has-text', PSelectorHasTextTask ], [ ':if', PSelectorIfTask ], [ ':if-not', PSelectorIfNotTask ], + [ ':min-text-length', PSelectorMinTextLengthTask ], [ ':not', PSelectorIfNotTask ], [ ':nth-ancestor', PSelectorNthAncestorTask ], [ ':xpath', PSelectorXpathTask ] diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index c8a82e1cad1e2..b9d4c873e8aa5 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -166,6 +166,7 @@ 'matches-css', 'matches-css-after', 'matches-css-before', + 'min-text-length', 'not', 'nth-ancestor', 'watch-attrs', @@ -231,6 +232,14 @@ return compile(s); }; + const compileInteger = function(s, min = 0, max = 0x7FFFFFFF) { + if ( /^\d+$/.test(s) === false ) { return; } + const n = parseInt(s, 10); + if ( n >= min && n < max ) { + return n; + } + }; + const compileNotSelector = function(s) { // /~https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588 // Reject instances of :not() filters for which the argument is @@ -242,10 +251,7 @@ }; const compileNthAncestorSelector = function(s) { - const n = parseInt(s, 10); - if ( isNaN(n) === false && n >= 1 && n < 256 ) { - return n; - } + return compileInteger(s, 1, 256); }; const compileSpathExpression = function(s) { @@ -289,6 +295,7 @@ [ ':matches-css', compileCSSDeclaration ], [ ':matches-css-after', compileCSSDeclaration ], [ ':matches-css-before', compileCSSDeclaration ], + [ ':min-text-length', compileInteger ], [ ':not', compileNotSelector ], [ ':nth-ancestor', compileNthAncestorSelector ], [ ':spath', compileSpathExpression ], @@ -344,12 +351,11 @@ case ':if-not': raw.push(`:not(${decompile(task[1])})`); break; - case ':nth-ancestor': - raw.push(`:nth-ancestor(${task[1]})`); - break; case ':spath': raw.push(task[1]); break; + case ':min-text-length': + case ':nth-ancestor': case ':watch-attrs': case ':xpath': raw.push(`${task[0]}(${task[1]})`);