Add new procedural operator: `:min-text-length(x)`

Where `x` is the minimal text length of the subject
DOM element. DOM elements whose text length is
greater than or equal to `x` will be selected.

The original rationale for such procedural cosmetic
operator[1] is to be able to remove inline script
elements according to a minimum text length using
HTML filtering.

[1] As a result of internal discussion with filter
    list maintainers @ uAssets.
This commit is contained in:
Raymond Hill 2019-06-20 14:11:54 -04:00
parent 793aca7ddb
commit b428a25c3f
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
3 changed files with 46 additions and 8 deletions

View File

@ -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 { const PSelectorNthAncestorTask = class {
constructor(task) { constructor(task) {
this.nth = task[1]; this.nth = task[1];
@ -658,6 +673,7 @@ vAPI.DOMFilterer = (function() {
[ ':matches-css', PSelectorMatchesCSSTask ], [ ':matches-css', PSelectorMatchesCSSTask ],
[ ':matches-css-after', PSelectorMatchesCSSAfterTask ], [ ':matches-css-after', PSelectorMatchesCSSAfterTask ],
[ ':matches-css-before', PSelectorMatchesCSSBeforeTask ], [ ':matches-css-before', PSelectorMatchesCSSBeforeTask ],
[ ':min-text-length', PSelectorMinTextLengthTask ],
[ ':not', PSelectorIfNotTask ], [ ':not', PSelectorIfNotTask ],
[ ':nth-ancestor', PSelectorNthAncestorTask ], [ ':nth-ancestor', PSelectorNthAncestorTask ],
[ ':spath', PSelectorSpathTask ], [ ':spath', PSelectorSpathTask ],

View File

@ -82,11 +82,26 @@
const PSelectorIfNotTask = class extends PSelectorIfTask { const PSelectorIfNotTask = class extends PSelectorIfTask {
constructor(task) { constructor(task) {
super.call(task); super(task);
this.target = false; 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 { const PSelectorNthAncestorTask = class {
constructor(task) { constructor(task) {
this.nth = task[1]; this.nth = task[1];
@ -191,6 +206,7 @@
[ ':has-text', PSelectorHasTextTask ], [ ':has-text', PSelectorHasTextTask ],
[ ':if', PSelectorIfTask ], [ ':if', PSelectorIfTask ],
[ ':if-not', PSelectorIfNotTask ], [ ':if-not', PSelectorIfNotTask ],
[ ':min-text-length', PSelectorMinTextLengthTask ],
[ ':not', PSelectorIfNotTask ], [ ':not', PSelectorIfNotTask ],
[ ':nth-ancestor', PSelectorNthAncestorTask ], [ ':nth-ancestor', PSelectorNthAncestorTask ],
[ ':xpath', PSelectorXpathTask ] [ ':xpath', PSelectorXpathTask ]

View File

@ -166,6 +166,7 @@
'matches-css', 'matches-css',
'matches-css-after', 'matches-css-after',
'matches-css-before', 'matches-css-before',
'min-text-length',
'not', 'not',
'nth-ancestor', 'nth-ancestor',
'watch-attrs', 'watch-attrs',
@ -231,6 +232,14 @@
return compile(s); 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) { const compileNotSelector = function(s) {
// https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588 // https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588
// Reject instances of :not() filters for which the argument is // Reject instances of :not() filters for which the argument is
@ -242,10 +251,7 @@
}; };
const compileNthAncestorSelector = function(s) { const compileNthAncestorSelector = function(s) {
const n = parseInt(s, 10); return compileInteger(s, 1, 256);
if ( isNaN(n) === false && n >= 1 && n < 256 ) {
return n;
}
}; };
const compileSpathExpression = function(s) { const compileSpathExpression = function(s) {
@ -289,6 +295,7 @@
[ ':matches-css', compileCSSDeclaration ], [ ':matches-css', compileCSSDeclaration ],
[ ':matches-css-after', compileCSSDeclaration ], [ ':matches-css-after', compileCSSDeclaration ],
[ ':matches-css-before', compileCSSDeclaration ], [ ':matches-css-before', compileCSSDeclaration ],
[ ':min-text-length', compileInteger ],
[ ':not', compileNotSelector ], [ ':not', compileNotSelector ],
[ ':nth-ancestor', compileNthAncestorSelector ], [ ':nth-ancestor', compileNthAncestorSelector ],
[ ':spath', compileSpathExpression ], [ ':spath', compileSpathExpression ],
@ -344,12 +351,11 @@
case ':if-not': case ':if-not':
raw.push(`:not(${decompile(task[1])})`); raw.push(`:not(${decompile(task[1])})`);
break; break;
case ':nth-ancestor':
raw.push(`:nth-ancestor(${task[1]})`);
break;
case ':spath': case ':spath':
raw.push(task[1]); raw.push(task[1]);
break; break;
case ':min-text-length':
case ':nth-ancestor':
case ':watch-attrs': case ':watch-attrs':
case ':xpath': case ':xpath':
raw.push(`${task[0]}(${task[1]})`); raw.push(`${task[0]}(${task[1]})`);