mirror of https://github.com/gorhill/uBlock.git
Add support for `nth-ancestor` operator in HTML filtering
Also opportunitisically converted some code to ES6's `class`.
This commit is contained in:
parent
d42d86dd12
commit
8a7e704080
|
@ -42,135 +42,160 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelectorHasTextTask = function(task) {
|
const PSelectorHasTextTask = class {
|
||||||
let arg0 = task[1], arg1;
|
constructor(task) {
|
||||||
if ( Array.isArray(task[1]) ) {
|
let arg0 = task[1], arg1;
|
||||||
arg1 = arg0[1]; arg0 = arg0[0];
|
if ( Array.isArray(task[1]) ) {
|
||||||
}
|
arg1 = arg0[1]; arg0 = arg0[0];
|
||||||
this.needle = new RegExp(arg0, arg1);
|
|
||||||
};
|
|
||||||
PSelectorHasTextTask.prototype.exec = function(input) {
|
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
if ( this.needle.test(node.textContent) ) {
|
|
||||||
output.push(node);
|
|
||||||
}
|
}
|
||||||
|
this.needle = new RegExp(arg0, arg1);
|
||||||
}
|
}
|
||||||
return output;
|
exec(input) {
|
||||||
};
|
const output = [];
|
||||||
|
for ( const node of input ) {
|
||||||
const PSelectorIfTask = function(task) {
|
if ( this.needle.test(node.textContent) ) {
|
||||||
this.pselector = new PSelector(task[1]);
|
|
||||||
};
|
|
||||||
PSelectorIfTask.prototype.target = true;
|
|
||||||
Object.defineProperty(PSelectorIfTask.prototype, 'invalid', {
|
|
||||||
get: function() {
|
|
||||||
return this.pselector.invalid;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
PSelectorIfTask.prototype.exec = function(input) {
|
|
||||||
const output = [];
|
|
||||||
for ( const node of input ) {
|
|
||||||
if ( this.pselector.test(node) === this.target ) {
|
|
||||||
output.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
const PSelectorIfNotTask = function(task) {
|
|
||||||
PSelectorIfTask.call(this, task);
|
|
||||||
this.target = false;
|
|
||||||
};
|
|
||||||
PSelectorIfNotTask.prototype = Object.create(PSelectorIfTask.prototype);
|
|
||||||
PSelectorIfNotTask.prototype.constructor = PSelectorIfNotTask;
|
|
||||||
|
|
||||||
const PSelectorXpathTask = function(task) {
|
|
||||||
this.xpe = task[1];
|
|
||||||
};
|
|
||||||
PSelectorXpathTask.prototype.exec = function(input) {
|
|
||||||
const output = [];
|
|
||||||
const xpe = docRegister.createExpression(this.xpe, null);
|
|
||||||
let xpr = null;
|
|
||||||
for ( const node of input ) {
|
|
||||||
xpr = xpe.evaluate(
|
|
||||||
node,
|
|
||||||
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
|
||||||
xpr
|
|
||||||
);
|
|
||||||
let j = xpr.snapshotLength;
|
|
||||||
while ( j-- ) {
|
|
||||||
const node = xpr.snapshotItem(j);
|
|
||||||
if ( node.nodeType === 1 ) {
|
|
||||||
output.push(node);
|
output.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
return output;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const PSelector = function(o) {
|
const PSelectorIfTask = class {
|
||||||
if ( PSelector.prototype.operatorToTaskMap === undefined ) {
|
constructor(task) {
|
||||||
PSelector.prototype.operatorToTaskMap = new Map([
|
this.pselector = new PSelector(task[1]);
|
||||||
[ ':has', PSelectorIfTask ],
|
|
||||||
[ ':has-text', PSelectorHasTextTask ],
|
|
||||||
[ ':if', PSelectorIfTask ],
|
|
||||||
[ ':if-not', PSelectorIfNotTask ],
|
|
||||||
[ ':not', PSelectorIfNotTask ],
|
|
||||||
[ ':xpath', PSelectorXpathTask ]
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
this.raw = o.raw;
|
exec(input) {
|
||||||
this.selector = o.selector;
|
const output = [];
|
||||||
this.tasks = [];
|
for ( const node of input ) {
|
||||||
if ( !o.tasks ) { return; }
|
if ( this.pselector.test(node) === this.target ) {
|
||||||
for ( const task of o.tasks ) {
|
output.push(node);
|
||||||
const ctor = this.operatorToTaskMap.get(task[0]);
|
}
|
||||||
if ( ctor === undefined ) {
|
|
||||||
this.invalid = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
const pselector = new ctor(task);
|
return output;
|
||||||
if ( pselector instanceof PSelectorIfTask && pselector.invalid ) {
|
}
|
||||||
this.invalid = true;
|
get invalid() {
|
||||||
break;
|
return this.pselector.invalid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PSelectorIfTask.prototype.target = true;
|
||||||
|
|
||||||
|
const PSelectorIfNotTask = class extends PSelectorIfTask {
|
||||||
|
constructor(task) {
|
||||||
|
super.call(task);
|
||||||
|
this.target = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.tasks.push(pselector);
|
return output;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
PSelector.prototype.operatorToTaskMap = undefined;
|
|
||||||
PSelector.prototype.invalid = false;
|
const PSelectorXpathTask = class {
|
||||||
PSelector.prototype.prime = function(input) {
|
constructor(task) {
|
||||||
const root = input || docRegister;
|
this.xpe = task[1];
|
||||||
if ( this.selector !== '' ) {
|
|
||||||
return root.querySelectorAll(this.selector);
|
|
||||||
}
|
}
|
||||||
return [ root ];
|
exec(input) {
|
||||||
};
|
const output = [];
|
||||||
PSelector.prototype.exec = function(input) {
|
const xpe = docRegister.createExpression(this.xpe, null);
|
||||||
if ( this.invalid ) { return []; }
|
let xpr = null;
|
||||||
let nodes = this.prime(input);
|
for ( const node of input ) {
|
||||||
for ( const task of this.tasks ) {
|
xpr = xpe.evaluate(
|
||||||
if ( nodes.length === 0 ) { break; }
|
node,
|
||||||
nodes = task.exec(nodes);
|
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
||||||
|
xpr
|
||||||
|
);
|
||||||
|
let j = xpr.snapshotLength;
|
||||||
|
while ( j-- ) {
|
||||||
|
const node = xpr.snapshotItem(j);
|
||||||
|
if ( node.nodeType === 1 ) {
|
||||||
|
output.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
return nodes;
|
|
||||||
};
|
};
|
||||||
PSelector.prototype.test = function(input) {
|
|
||||||
if ( this.invalid ) { return false; }
|
const PSelector = class {
|
||||||
const nodes = this.prime(input);
|
constructor(o) {
|
||||||
const AA = [ null ];
|
this.raw = o.raw;
|
||||||
for ( const node of nodes ) {
|
this.selector = o.selector;
|
||||||
AA[0] = node;
|
this.tasks = [];
|
||||||
let aa = AA;
|
if ( !o.tasks ) { return; }
|
||||||
|
for ( const task of o.tasks ) {
|
||||||
|
const ctor = this.operatorToTaskMap.get(task[0]);
|
||||||
|
if ( ctor === undefined ) {
|
||||||
|
this.invalid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const pselector = new ctor(task);
|
||||||
|
if ( pselector instanceof PSelectorIfTask && pselector.invalid ) {
|
||||||
|
this.invalid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.tasks.push(pselector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prime(input) {
|
||||||
|
const root = input || docRegister;
|
||||||
|
if ( this.selector !== '' ) {
|
||||||
|
return root.querySelectorAll(this.selector);
|
||||||
|
}
|
||||||
|
return [ root ];
|
||||||
|
}
|
||||||
|
exec(input) {
|
||||||
|
if ( this.invalid ) { return []; }
|
||||||
|
let nodes = this.prime(input);
|
||||||
for ( const task of this.tasks ) {
|
for ( const task of this.tasks ) {
|
||||||
aa = task.exec(aa);
|
if ( nodes.length === 0 ) { break; }
|
||||||
if ( aa.length === 0 ) { break; }
|
nodes = task.exec(nodes);
|
||||||
}
|
}
|
||||||
if ( aa.length !== 0 ) { return true; }
|
return nodes;
|
||||||
|
}
|
||||||
|
test(input) {
|
||||||
|
if ( this.invalid ) { return false; }
|
||||||
|
const nodes = this.prime(input);
|
||||||
|
const AA = [ null ];
|
||||||
|
for ( const node of nodes ) {
|
||||||
|
AA[0] = node;
|
||||||
|
let aa = AA;
|
||||||
|
for ( const task of this.tasks ) {
|
||||||
|
aa = task.exec(aa);
|
||||||
|
if ( aa.length === 0 ) { break; }
|
||||||
|
}
|
||||||
|
if ( aa.length !== 0 ) { return true; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
PSelector.prototype.operatorToTaskMap = new Map([
|
||||||
|
[ ':has', PSelectorIfTask ],
|
||||||
|
[ ':has-text', PSelectorHasTextTask ],
|
||||||
|
[ ':if', PSelectorIfTask ],
|
||||||
|
[ ':if-not', PSelectorIfNotTask ],
|
||||||
|
[ ':not', PSelectorIfNotTask ],
|
||||||
|
[ ':nth-ancestor', PSelectorNthAncestorTask ],
|
||||||
|
[ ':xpath', PSelectorXpathTask ]
|
||||||
|
]);
|
||||||
|
PSelector.prototype.invalid = false;
|
||||||
|
|
||||||
const logOne = function(details, exception, selector) {
|
const logOne = function(details, exception, selector) {
|
||||||
µBlock.filteringContext
|
µBlock.filteringContext
|
||||||
|
|
Loading…
Reference in New Issue