Skip to content
Permalink
Browse files
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:
- Rename `:watch-attrs` to `:watch-attr`
  - `:watch=attrs` is deprecated and will be kept around
    until it is safe to remove it completely
  • Loading branch information
gorhill authored and hawkeye116477 committed Jun 29, 2020
1 parent 8123d49 commit 279fe2714bedaf4b0d7c09bfa6dfdc40eaed0599
Showing with 72 additions and 106 deletions.
  1. +68 −104 src/js/contentscript.js
  2. +4 −2 src/js/static-ext-filtering.js
@@ -377,39 +377,28 @@ vAPI.DOMFilterer = (function() {
}
this.needle = new RegExp(arg0, arg1);
}
exec(input) {
const output = [];
for ( const node of input ) {
if ( this.needle.test(node.textContent) ) {
output.push(node);
}
transpose(node, output) {
if ( this.needle.test(node.textContent) ) {
output.push(node);
}
return output;
}
};

const PSelectorIfTask = class {
constructor(task) {
this.pselector = new PSelector(task[1]);
}
exec(input) {
const output = [];
for ( const node of input ) {
if ( this.pselector.test(node) === this.target ) {
output.push(node);
}
transpose(node, output) {
if ( this.pselector.test(node) === this.target ) {
output.push(node);
}
return output;
}
};
PSelectorIfTask.prototype.target = true;

const PSelectorIfNotTask = class extends PSelectorIfTask {
constructor(task) {
super(task);
this.target = false;
}
};
PSelectorIfNotTask.prototype.target = false;

const PSelectorMatchesCSSTask = class {
constructor(task) {
@@ -420,93 +409,69 @@ vAPI.DOMFilterer = (function() {
}
this.value = new RegExp(arg0, arg1);
}
exec(input) {
const output = [];
for ( const node of input ) {
const style = window.getComputedStyle(node, this.pseudo);
if ( style === null ) { return null; } /* FF */
if ( this.value.test(style[this.name]) ) {
output.push(node);
}
transpose(node, output) {
const style = window.getComputedStyle(node, this.pseudo);
if ( style !== null && this.value.test(style[this.name]) ) {
output.push(node);
}
return output;
}
};
PSelectorMatchesCSSTask.prototype.pseudo = null;

const PSelectorMatchesCSSAfterTask = class extends PSelectorMatchesCSSTask {
constructor(task) {
super(task);
this.pseudo = ':after';
}
};
PSelectorMatchesCSSAfterTask.prototype.pseudo = ':after';

const PSelectorMatchesCSSBeforeTask = class extends PSelectorMatchesCSSTask {
constructor(task) {
super(task);
this.pseudo = ':before';
}
};
PSelectorMatchesCSSBeforeTask.prototype.pseudo = ':before';

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);
}
transpose(node, output) {
if ( node.textContent.length >= this.min ) {
output.push(node);
}
return output;
}
};

const PSelectorNthAncestorTask = class {
constructor(task) {
this.nth = task[1];
}
exec(input) {
const output = [];
for ( let node of input ) {
let nth = this.nth;
for (;;) {
node = node.parentElement;
if ( node === null ) { break; }
nth -= 1;
if ( nth !== 0 ) { continue; }
output.push(node);
break;
}
transpose(node, output) {
let nth = this.nth;
for (;;) {
node = node.parentElement;
if ( node === null ) { return; }
nth -= 1;
if ( nth === 0 ) { break; }
}
return output;
output.push(node);
}
};

const PSelectorSpathTask = class {
constructor(task) {
this.spath = task[1];
}
exec(input) {
const output = [];
for ( let node of input ) {
const parent = node.parentElement;
if ( parent === null ) { continue; }
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);
}
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;
}
};

@@ -531,17 +496,14 @@ vAPI.DOMFilterer = (function() {
filterer.onDOMChanged([ null ]);
}
}
exec(input) {
if ( input.length === 0 ) { return input; }
transpose(node, output) {
output.push(node);
if ( this.observed.has(node) ) { return; }
if ( this.observer === null ) {
this.observer = new MutationObserver(this.handler);
}
for ( const node of input ) {
if ( this.observed.has(node) ) { continue; }
this.observer.observe(node, this.observerOptions);
this.observed.add(node);
}
return input;
this.observer.observe(node, this.observerOptions);
this.observed.add(node);
}
};

@@ -550,23 +512,19 @@ vAPI.DOMFilterer = (function() {
this.xpe = document.createExpression(task[1], null);
this.xpr = null;
}
exec(input) {
const output = [];
for ( const node of input ) {
this.xpr = this.xpe.evaluate(
node,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
this.xpr
);
let j = this.xpr.snapshotLength;
while ( j-- ) {
const node = this.xpr.snapshotItem(j);
if ( node.nodeType === 1 ) {
output.push(node);
}
transpose(node, output) {
this.xpr = this.xpe.evaluate(
node,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
this.xpr
);
let j = this.xpr.snapshotLength;
while ( j-- ) {
const node = this.xpr.snapshotItem(j);
if ( node.nodeType === 1 ) {
output.push(node);
}
}
return output;
}
};

@@ -585,7 +543,7 @@ vAPI.DOMFilterer = (function() {
[ ':not', PSelectorIfNotTask ],
[ ':nth-ancestor', PSelectorNthAncestorTask ],
[ ':spath', PSelectorSpathTask ],
[ ':watch-attrs', PSelectorWatchAttrs ],
[ ':watch-attr', PSelectorWatchAttrs ],
[ ':xpath', PSelectorXpathTask ],
]);
}
@@ -612,21 +570,27 @@ vAPI.DOMFilterer = (function() {
let nodes = this.prime(input);
for ( const task of this.tasks ) {
if ( nodes.length === 0 ) { break; }
nodes = task.exec(nodes);
const transposed = [];
for ( const node of nodes ) {
task.transpose(node, transposed);
}
nodes = transposed;
}
return nodes;
}
test(input) {
const nodes = this.prime(input);
const AA = [ null ];
for ( const node of nodes ) {
AA[0] = node;
let aa = AA;
let output = [ node ];
for ( const task of this.tasks ) {
aa = task.exec(aa);
if ( aa.length === 0 ) { break; }
const transposed = [];
for ( const node of output ) {
task.transpose(node, transposed);
}
output = transposed;
if ( output.length === 0 ) { break; }
}
if ( aa.length !== 0 ) { return true; }
if ( output.length !== 0 ) { return true; }
}
return false;
}
@@ -169,6 +169,7 @@
'min-text-length',
'not',
'nth-ancestor',
'watch-attr',
'watch-attrs',
'xpath'
].join('|'),
@@ -281,6 +282,7 @@
[ ':-abp-contains', ':has-text' ],
[ ':-abp-has', ':has' ],
[ ':contains', ':has-text' ],
[ ':watch-attrs', ':watch-attr' ],
]);

const compileArgument = new Map([
@@ -295,7 +297,7 @@
[ ':not', compileNotSelector ],
[ ':nth-ancestor', compileNthAncestorSelector ],
[ ':spath', compileSpathExpression ],
[ ':watch-attrs', compileAttrList ],
[ ':watch-attr', compileAttrList ],
[ ':xpath', compileXpathExpression ]
]);

@@ -352,7 +354,7 @@
break;
case ':min-text-length':
case ':nth-ancestor':
case ':watch-attrs':
case ':watch-attr':
case ':xpath':
raw.push(`${task[0]}(${task[1]})`);
break;

0 comments on commit 279fe27

Please sign in to comment.