mirror of https://github.com/gorhill/uBlock.git
Replace `exec` with `transpose` in procedural filters
The purpose is to avoid having to iterate through all input nodes at each operator implementation level. The `transpose` method deals with only one input node, and the iteration is performed by the main procedural filtering entry points. Additionally: - Add `:spath` to HTML filtering - Rename `:watch-attrs` to `:watch-attr` - `:watch=attrs` is deprecated and will be kept around until it is safe to remove it completely
This commit is contained in:
parent
4956a166d3
commit
41685f4cce
|
@ -469,39 +469,28 @@ vAPI.DOMFilterer = (function() {
|
||||||
}
|
}
|
||||||
this.needle = new RegExp(arg0, arg1);
|
this.needle = new RegExp(arg0, arg1);
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
if ( this.needle.test(node.textContent) ) {
|
if ( this.needle.test(node.textContent) ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelectorIfTask = class {
|
const PSelectorIfTask = class {
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
this.pselector = new PSelector(task[1]);
|
this.pselector = new PSelector(task[1]);
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
if ( this.pselector.test(node) === this.target ) {
|
if ( this.pselector.test(node) === this.target ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
PSelectorIfTask.prototype.target = true;
|
PSelectorIfTask.prototype.target = true;
|
||||||
|
|
||||||
const PSelectorIfNotTask = class extends PSelectorIfTask {
|
const PSelectorIfNotTask = class extends PSelectorIfTask {
|
||||||
constructor(task) {
|
|
||||||
super(task);
|
|
||||||
this.target = false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
PSelectorIfNotTask.prototype.target = false;
|
||||||
|
|
||||||
const PSelectorMatchesCSSTask = class {
|
const PSelectorMatchesCSSTask = class {
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
|
@ -512,67 +501,47 @@ vAPI.DOMFilterer = (function() {
|
||||||
}
|
}
|
||||||
this.value = new RegExp(arg0, arg1);
|
this.value = new RegExp(arg0, arg1);
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
const style = window.getComputedStyle(node, this.pseudo);
|
const style = window.getComputedStyle(node, this.pseudo);
|
||||||
if ( style === null ) { return null; } /* FF */
|
if ( style !== null && this.value.test(style[this.name]) ) {
|
||||||
if ( this.value.test(style[this.name]) ) {
|
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
PSelectorMatchesCSSTask.prototype.pseudo = null;
|
PSelectorMatchesCSSTask.prototype.pseudo = null;
|
||||||
|
|
||||||
const PSelectorMatchesCSSAfterTask = class extends PSelectorMatchesCSSTask {
|
const PSelectorMatchesCSSAfterTask = class extends PSelectorMatchesCSSTask {
|
||||||
constructor(task) {
|
|
||||||
super(task);
|
|
||||||
this.pseudo = ':after';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
PSelectorMatchesCSSAfterTask.prototype.pseudo = ':after';
|
||||||
|
|
||||||
const PSelectorMatchesCSSBeforeTask = class extends PSelectorMatchesCSSTask {
|
const PSelectorMatchesCSSBeforeTask = class extends PSelectorMatchesCSSTask {
|
||||||
constructor(task) {
|
|
||||||
super(task);
|
|
||||||
this.pseudo = ':before';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
PSelectorMatchesCSSBeforeTask.prototype.pseudo = ':before';
|
||||||
|
|
||||||
const PSelectorMinTextLengthTask = class {
|
const PSelectorMinTextLengthTask = class {
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
this.min = task[1];
|
this.min = task[1];
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
if ( node.textContent.length >= this.min ) {
|
if ( node.textContent.length >= this.min ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelectorNthAncestorTask = class {
|
const PSelectorNthAncestorTask = class {
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
this.nth = task[1];
|
this.nth = task[1];
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( let node of input ) {
|
|
||||||
let nth = this.nth;
|
let nth = this.nth;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
node = node.parentElement;
|
node = node.parentElement;
|
||||||
if ( node === null ) { break; }
|
if ( node === null ) { return; }
|
||||||
nth -= 1;
|
nth -= 1;
|
||||||
if ( nth !== 0 ) { continue; }
|
if ( nth === 0 ) { break; }
|
||||||
|
}
|
||||||
output.push(node);
|
output.push(node);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -580,11 +549,9 @@ vAPI.DOMFilterer = (function() {
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
this.spath = task[1];
|
this.spath = task[1];
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( let node of input ) {
|
|
||||||
const parent = node.parentElement;
|
const parent = node.parentElement;
|
||||||
if ( parent === null ) { continue; }
|
if ( parent === null ) { return; }
|
||||||
let pos = 1;
|
let pos = 1;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
node = node.previousElementSibling;
|
node = node.previousElementSibling;
|
||||||
|
@ -592,14 +559,12 @@ vAPI.DOMFilterer = (function() {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
const nodes = parent.querySelectorAll(
|
const nodes = parent.querySelectorAll(
|
||||||
':scope > :nth-child(' + pos + ')' + this.spath
|
`:scope > :nth-child(${pos})${this.spath}`
|
||||||
);
|
);
|
||||||
for ( const node of nodes ) {
|
for ( const node of nodes ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelectorWatchAttrs = class {
|
const PSelectorWatchAttrs = class {
|
||||||
|
@ -623,18 +588,15 @@ vAPI.DOMFilterer = (function() {
|
||||||
filterer.onDOMChanged([ null ]);
|
filterer.onDOMChanged([ null ]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
if ( input.length === 0 ) { return input; }
|
output.push(node);
|
||||||
|
if ( this.observed.has(node) ) { return; }
|
||||||
if ( this.observer === null ) {
|
if ( this.observer === null ) {
|
||||||
this.observer = new MutationObserver(this.handler);
|
this.observer = new MutationObserver(this.handler);
|
||||||
}
|
}
|
||||||
for ( const node of input ) {
|
|
||||||
if ( this.observed.has(node) ) { continue; }
|
|
||||||
this.observer.observe(node, this.observerOptions);
|
this.observer.observe(node, this.observerOptions);
|
||||||
this.observed.add(node);
|
this.observed.add(node);
|
||||||
}
|
}
|
||||||
return input;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelectorXpathTask = class {
|
const PSelectorXpathTask = class {
|
||||||
|
@ -642,9 +604,7 @@ vAPI.DOMFilterer = (function() {
|
||||||
this.xpe = document.createExpression(task[1], null);
|
this.xpe = document.createExpression(task[1], null);
|
||||||
this.xpr = null;
|
this.xpr = null;
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
this.xpr = this.xpe.evaluate(
|
this.xpr = this.xpe.evaluate(
|
||||||
node,
|
node,
|
||||||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||||
|
@ -658,8 +618,6 @@ vAPI.DOMFilterer = (function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelector = class {
|
const PSelector = class {
|
||||||
|
@ -677,7 +635,7 @@ vAPI.DOMFilterer = (function() {
|
||||||
[ ':not', PSelectorIfNotTask ],
|
[ ':not', PSelectorIfNotTask ],
|
||||||
[ ':nth-ancestor', PSelectorNthAncestorTask ],
|
[ ':nth-ancestor', PSelectorNthAncestorTask ],
|
||||||
[ ':spath', PSelectorSpathTask ],
|
[ ':spath', PSelectorSpathTask ],
|
||||||
[ ':watch-attrs', PSelectorWatchAttrs ],
|
[ ':watch-attr', PSelectorWatchAttrs ],
|
||||||
[ ':xpath', PSelectorXpathTask ],
|
[ ':xpath', PSelectorXpathTask ],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -704,21 +662,27 @@ vAPI.DOMFilterer = (function() {
|
||||||
let nodes = this.prime(input);
|
let nodes = this.prime(input);
|
||||||
for ( const task of this.tasks ) {
|
for ( const task of this.tasks ) {
|
||||||
if ( nodes.length === 0 ) { break; }
|
if ( nodes.length === 0 ) { break; }
|
||||||
nodes = task.exec(nodes);
|
const transposed = [];
|
||||||
|
for ( const node of nodes ) {
|
||||||
|
task.transpose(node, transposed);
|
||||||
|
}
|
||||||
|
nodes = transposed;
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
test(input) {
|
test(input) {
|
||||||
const nodes = this.prime(input);
|
const nodes = this.prime(input);
|
||||||
const AA = [ null ];
|
|
||||||
for ( const node of nodes ) {
|
for ( const node of nodes ) {
|
||||||
AA[0] = node;
|
let output = [ node ];
|
||||||
let aa = AA;
|
|
||||||
for ( const task of this.tasks ) {
|
for ( const task of this.tasks ) {
|
||||||
aa = task.exec(aa);
|
const transposed = [];
|
||||||
if ( aa.length === 0 ) { break; }
|
for ( const node of output ) {
|
||||||
|
task.transpose(node, transposed);
|
||||||
}
|
}
|
||||||
if ( aa.length !== 0 ) { return true; }
|
output = transposed;
|
||||||
|
if ( output.length === 0 ) { break; }
|
||||||
|
}
|
||||||
|
if ( output.length !== 0 ) { return true; }
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,30 +50,22 @@
|
||||||
}
|
}
|
||||||
this.needle = new RegExp(arg0, arg1);
|
this.needle = new RegExp(arg0, arg1);
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
if ( this.needle.test(node.textContent) ) {
|
if ( this.needle.test(node.textContent) ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelectorIfTask = class {
|
const PSelectorIfTask = class {
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
this.pselector = new PSelector(task[1]);
|
this.pselector = new PSelector(task[1]);
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
if ( this.pselector.test(node) === this.target ) {
|
if ( this.pselector.test(node) === this.target ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
get invalid() {
|
get invalid() {
|
||||||
return this.pselector.invalid;
|
return this.pselector.invalid;
|
||||||
}
|
}
|
||||||
|
@ -81,45 +73,55 @@
|
||||||
PSelectorIfTask.prototype.target = true;
|
PSelectorIfTask.prototype.target = true;
|
||||||
|
|
||||||
const PSelectorIfNotTask = class extends PSelectorIfTask {
|
const PSelectorIfNotTask = class extends PSelectorIfTask {
|
||||||
constructor(task) {
|
|
||||||
super(task);
|
|
||||||
this.target = false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
PSelectorIfNotTask.prototype.target = false;
|
||||||
|
|
||||||
const PSelectorMinTextLengthTask = class {
|
const PSelectorMinTextLengthTask = class {
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
this.min = task[1];
|
this.min = task[1];
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
if ( node.textContent.length >= this.min ) {
|
if ( node.textContent.length >= this.min ) {
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelectorNthAncestorTask = class {
|
const PSelectorNthAncestorTask = class {
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
this.nth = task[1];
|
this.nth = task[1];
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
|
||||||
for ( let node of input ) {
|
|
||||||
let nth = this.nth;
|
let nth = this.nth;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
node = node.parentElement;
|
node = node.parentElement;
|
||||||
if ( node === null ) { break; }
|
if ( node === null ) { return; }
|
||||||
nth -= 1;
|
nth -= 1;
|
||||||
if ( nth !== 0 ) { continue; }
|
if ( nth === 0 ) { break; }
|
||||||
|
}
|
||||||
output.push(node);
|
output.push(node);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const PSelectorSpathTask = class {
|
||||||
|
constructor(task) {
|
||||||
|
this.spath = task[1];
|
||||||
|
}
|
||||||
|
transpose(node, output) {
|
||||||
|
const parent = node.parentElement;
|
||||||
|
if ( parent === null ) { return; }
|
||||||
|
let pos = 1;
|
||||||
|
for (;;) {
|
||||||
|
node = node.previousElementSibling;
|
||||||
|
if ( node === null ) { break; }
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
const nodes = parent.querySelectorAll(
|
||||||
|
`:scope > :nth-child(${pos})${this.spath}`
|
||||||
|
);
|
||||||
|
for ( const node of nodes ) {
|
||||||
|
output.push(node);
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,15 +129,13 @@
|
||||||
constructor(task) {
|
constructor(task) {
|
||||||
this.xpe = task[1];
|
this.xpe = task[1];
|
||||||
}
|
}
|
||||||
exec(input) {
|
transpose(node, output) {
|
||||||
const output = [];
|
const xpr = docRegister.evaluate(
|
||||||
const xpe = docRegister.createExpression(this.xpe, null);
|
this.xpe,
|
||||||
let xpr = null;
|
|
||||||
for ( const node of input ) {
|
|
||||||
xpr = xpe.evaluate(
|
|
||||||
node,
|
node,
|
||||||
|
null,
|
||||||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||||
xpr
|
null
|
||||||
);
|
);
|
||||||
let j = xpr.snapshotLength;
|
let j = xpr.snapshotLength;
|
||||||
while ( j-- ) {
|
while ( j-- ) {
|
||||||
|
@ -145,8 +145,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelector = class {
|
const PSelector = class {
|
||||||
|
@ -181,22 +179,28 @@
|
||||||
let nodes = this.prime(input);
|
let nodes = this.prime(input);
|
||||||
for ( const task of this.tasks ) {
|
for ( const task of this.tasks ) {
|
||||||
if ( nodes.length === 0 ) { break; }
|
if ( nodes.length === 0 ) { break; }
|
||||||
nodes = task.exec(nodes);
|
const transposed = [];
|
||||||
|
for ( const node of nodes ) {
|
||||||
|
task.transpose(node, transposed);
|
||||||
|
}
|
||||||
|
nodes = transposed;
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
test(input) {
|
test(input) {
|
||||||
if ( this.invalid ) { return false; }
|
if ( this.invalid ) { return false; }
|
||||||
const nodes = this.prime(input);
|
const nodes = this.prime(input);
|
||||||
const AA = [ null ];
|
|
||||||
for ( const node of nodes ) {
|
for ( const node of nodes ) {
|
||||||
AA[0] = node;
|
let output = [ node ];
|
||||||
let aa = AA;
|
|
||||||
for ( const task of this.tasks ) {
|
for ( const task of this.tasks ) {
|
||||||
aa = task.exec(aa);
|
const transposed = [];
|
||||||
if ( aa.length === 0 ) { break; }
|
for ( const node of output ) {
|
||||||
|
task.transpose(node, transposed);
|
||||||
}
|
}
|
||||||
if ( aa.length !== 0 ) { return true; }
|
output = transposed;
|
||||||
|
if ( output.length === 0 ) { break; }
|
||||||
|
}
|
||||||
|
if ( output.length !== 0 ) { return true; }
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +213,8 @@
|
||||||
[ ':min-text-length', PSelectorMinTextLengthTask ],
|
[ ':min-text-length', PSelectorMinTextLengthTask ],
|
||||||
[ ':not', PSelectorIfNotTask ],
|
[ ':not', PSelectorIfNotTask ],
|
||||||
[ ':nth-ancestor', PSelectorNthAncestorTask ],
|
[ ':nth-ancestor', PSelectorNthAncestorTask ],
|
||||||
[ ':xpath', PSelectorXpathTask ]
|
[ ':spath', PSelectorSpathTask ],
|
||||||
|
[ ':xpath', PSelectorXpathTask ],
|
||||||
]);
|
]);
|
||||||
PSelector.prototype.invalid = false;
|
PSelector.prototype.invalid = false;
|
||||||
|
|
||||||
|
|
|
@ -169,6 +169,7 @@
|
||||||
'min-text-length',
|
'min-text-length',
|
||||||
'not',
|
'not',
|
||||||
'nth-ancestor',
|
'nth-ancestor',
|
||||||
|
'watch-attr',
|
||||||
'watch-attrs',
|
'watch-attrs',
|
||||||
'xpath'
|
'xpath'
|
||||||
].join('|'),
|
].join('|'),
|
||||||
|
@ -285,6 +286,7 @@
|
||||||
[ ':-abp-contains', ':has-text' ],
|
[ ':-abp-contains', ':has-text' ],
|
||||||
[ ':-abp-has', ':has' ],
|
[ ':-abp-has', ':has' ],
|
||||||
[ ':contains', ':has-text' ],
|
[ ':contains', ':has-text' ],
|
||||||
|
[ ':watch-attrs', ':watch-attr' ],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const compileArgument = new Map([
|
const compileArgument = new Map([
|
||||||
|
@ -299,7 +301,7 @@
|
||||||
[ ':not', compileNotSelector ],
|
[ ':not', compileNotSelector ],
|
||||||
[ ':nth-ancestor', compileNthAncestorSelector ],
|
[ ':nth-ancestor', compileNthAncestorSelector ],
|
||||||
[ ':spath', compileSpathExpression ],
|
[ ':spath', compileSpathExpression ],
|
||||||
[ ':watch-attrs', compileAttrList ],
|
[ ':watch-attr', compileAttrList ],
|
||||||
[ ':xpath', compileXpathExpression ]
|
[ ':xpath', compileXpathExpression ]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -356,7 +358,7 @@
|
||||||
break;
|
break;
|
||||||
case ':min-text-length':
|
case ':min-text-length':
|
||||||
case ':nth-ancestor':
|
case ':nth-ancestor':
|
||||||
case ':watch-attrs':
|
case ':watch-attr':
|
||||||
case ':xpath':
|
case ':xpath':
|
||||||
raw.push(`${task[0]}(${task[1]})`);
|
raw.push(`${task[0]}(${task[1]})`);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue