Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix uBlockOrigin/uBlock-issues#341 and other smaller problems #239

Merged
merged 9 commits into from
Jul 4, 2020
2 changes: 1 addition & 1 deletion src/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ var µBlock = (function() { // jshint ignore:line

// read-only
systemSettings: {
compiledMagic: 1,
compiledMagic: 2,
selfieMagic: 1
},

Expand Down
145 changes: 70 additions & 75 deletions src/js/contentscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,57 +374,57 @@ vAPI.DOMFilterer = (function() {

// 'P' stands for 'Procedural'

var PSelectorHasTextTask = function(task) {
var arg0 = task[1], arg1;
const PSelectorHasTextTask = function(task) {
let arg0 = task[1], arg1;
if ( Array.isArray(task[1]) ) {
arg1 = arg0[1]; arg0 = arg0[0];
}
this.needle = new RegExp(arg0, arg1);
};
PSelectorHasTextTask.prototype.exec = function(input) {
var output = [];
for ( var node of input ) {
const output = [];
for ( const node of input ) {
if ( this.needle.test(node.textContent) ) {
output.push(node);
}
}
return output;
};

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

var PSelectorIfNotTask = function(task) {
const PSelectorIfNotTask = function(task) {
PSelectorIfTask.call(this, task);
this.target = false;
};
PSelectorIfNotTask.prototype = Object.create(PSelectorIfTask.prototype);
PSelectorIfNotTask.prototype.constructor = PSelectorIfNotTask;

var PSelectorMatchesCSSTask = function(task) {
const PSelectorMatchesCSSTask = function(task) {
this.name = task[1].name;
var arg0 = task[1].value, arg1;
let arg0 = task[1].value, arg1;
if ( Array.isArray(arg0) ) {
arg1 = arg0[1]; arg0 = arg0[0];
}
this.value = new RegExp(arg0, arg1);
};
PSelectorMatchesCSSTask.prototype.pseudo = null;
PSelectorMatchesCSSTask.prototype.exec = function(input) {
var output = [], style;
for ( var node of input ) {
style = window.getComputedStyle(node, this.pseudo);
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);
Expand All @@ -433,35 +433,35 @@ vAPI.DOMFilterer = (function() {
return output;
};

var PSelectorMatchesCSSAfterTask = function(task) {
const PSelectorMatchesCSSAfterTask = function(task) {
PSelectorMatchesCSSTask.call(this, task);
this.pseudo = ':after';
};
PSelectorMatchesCSSAfterTask.prototype = Object.create(PSelectorMatchesCSSTask.prototype);
PSelectorMatchesCSSAfterTask.prototype.constructor = PSelectorMatchesCSSAfterTask;

var PSelectorMatchesCSSBeforeTask = function(task) {
const PSelectorMatchesCSSBeforeTask = function(task) {
PSelectorMatchesCSSTask.call(this, task);
this.pseudo = ':before';
};
PSelectorMatchesCSSBeforeTask.prototype = Object.create(PSelectorMatchesCSSTask.prototype);
PSelectorMatchesCSSBeforeTask.prototype.constructor = PSelectorMatchesCSSBeforeTask;

var PSelectorXpathTask = function(task) {
const PSelectorXpathTask = function(task) {
this.xpe = document.createExpression(task[1], null);
this.xpr = null;
};
PSelectorXpathTask.prototype.exec = function(input) {
var output = [], j;
for ( var node of input ) {
const output = [];
for ( const node of input ) {
this.xpr = this.xpe.evaluate(
node,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
this.xpr
);
j = this.xpr.snapshotLength;
let j = this.xpr.snapshotLength;
while ( j-- ) {
node = this.xpr.snapshotItem(j);
const node = this.xpr.snapshotItem(j);
if ( node.nodeType === 1 ) {
output.push(node);
}
Expand All @@ -470,7 +470,7 @@ vAPI.DOMFilterer = (function() {
return output;
};

var PSelector = function(o) {
const PSelector = function(o) {
if ( PSelector.prototype.operatorToTaskMap === undefined ) {
PSelector.prototype.operatorToTaskMap = new Map([
[ ':has', PSelectorIfTask ],
Expand All @@ -480,6 +480,7 @@ vAPI.DOMFilterer = (function() {
[ ':matches-css', PSelectorMatchesCSSTask ],
[ ':matches-css-after', PSelectorMatchesCSSAfterTask ],
[ ':matches-css-before', PSelectorMatchesCSSBeforeTask ],
[ ':not', PSelectorIfNotTask ],
[ ':xpath', PSelectorXpathTask ]
]);
}
Expand All @@ -489,33 +490,35 @@ vAPI.DOMFilterer = (function() {
this.lastAllowanceTime = 0;
this.selector = o.selector;
this.tasks = [];
var tasks = o.tasks;
const tasks = o.tasks;
if ( !tasks ) { return; }
for ( var task of tasks ) {
for ( const task of tasks ) {
this.tasks.push(new (this.operatorToTaskMap.get(task[0]))(task));
}
};
PSelector.prototype.operatorToTaskMap = undefined;
PSelector.prototype.prime = function(input) {
var root = input || document;
const root = input || document;
if ( this.selector !== '' ) {
return root.querySelectorAll(this.selector);
}
return [ root ];
};
PSelector.prototype.exec = function(input) {
var nodes = this.prime(input);
for ( var task of this.tasks ) {
let nodes = this.prime(input);
for ( const task of this.tasks ) {
if ( nodes.length === 0 ) { break; }
nodes = task.exec(nodes);
}
return nodes;
};
PSelector.prototype.test = function(input) {
var nodes = this.prime(input), AA = [ null ], aa;
for ( var node of nodes ) {
AA[0] = node; aa = AA;
for ( var task of this.tasks ) {
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; }
}
Expand All @@ -524,24 +527,23 @@ vAPI.DOMFilterer = (function() {
return false;
};

var DOMProceduralFilterer = function(domFilterer) {
const DOMProceduralFilterer = function(domFilterer) {
this.domFilterer = domFilterer;
this.domIsReady = false;
this.domIsWatched = false;
this.addedSelectors = new Map();
this.addedNodes = false;
this.removedNodes = false;
this.mustApplySelectors = false;
this.selectors = new Map();
this.hiddenNodes = new Set();
};

DOMProceduralFilterer.prototype = {

addProceduralSelectors: function(aa) {
var raw, o, pselector,
mustCommit = this.domIsWatched;
for ( var i = 0, n = aa.length; i < n; i++ ) {
raw = aa[i];
o = JSON.parse(raw);
const addedSelectors = [];
let mustCommit = this.domIsWatched;
for ( let i = 0, n = aa.length; i < n; i++ ) {
const raw = aa[i];
const o = JSON.parse(raw);
if ( o.style ) {
this.domFilterer.addCSSRule(o.style[0], o.style[1]);
mustCommit = true;
Expand All @@ -557,19 +559,20 @@ vAPI.DOMFilterer = (function() {
}
if ( o.tasks ) {
if ( this.selectors.has(raw) === false ) {
pselector = new PSelector(o);
const pselector = new PSelector(o);
this.selectors.set(raw, pselector);
this.addedSelectors.set(raw, pselector);
addedSelectors.push(pselector);
mustCommit = true;
}
continue;
}
}
if ( mustCommit === false ) { return; }
this.mustApplySelectors = this.selectors.size !== 0;
this.domFilterer.commit();
if ( this.domFilterer.hasListeners() ) {
this.domFilterer.triggerListeners({
procedural: Array.from(this.addedSelectors.values())
procedural: addedSelectors
});
}
},
Expand All @@ -579,56 +582,46 @@ vAPI.DOMFilterer = (function() {
return;
}

if ( this.addedNodes || this.removedNodes ) {
this.addedSelectors.clear();
}

var entry, nodes, i;

if ( this.addedSelectors.size !== 0 ) {
//console.time('procedural selectors/filterset changed');
for ( entry of this.addedSelectors ) {
nodes = entry[1].exec();
i = nodes.length;
while ( i-- ) {
this.domFilterer.hideNode(nodes[i]);
}
}
this.addedSelectors.clear();
//console.timeEnd('procedural selectors/filterset changed');
return;
}
this.mustApplySelectors = false;

//console.time('procedural selectors/dom layout changed');

this.addedNodes = this.removedNodes = false;
// https://github.com/uBlockOrigin/uBlock-issues/issues/341
// Be ready to unhide nodes which no longer matches any of
// the procedural selectors.
const toRemove = this.hiddenNodes;
this.hiddenNodes = new Set();

var t0 = Date.now(),
t1, pselector, allowance;
let t0 = Date.now();

for ( entry of this.selectors ) {
pselector = entry[1];
allowance = Math.floor((t0 - pselector.lastAllowanceTime) / 2000);
for ( const entry of this.selectors ) {
const pselector = entry[1];
const allowance = Math.floor((t0 - pselector.lastAllowanceTime) / 2000);
if ( allowance >= 1 ) {
pselector.budget += allowance * 50;
if ( pselector.budget > 200 ) { pselector.budget = 200; }
pselector.lastAllowanceTime = t0;
}
if ( pselector.budget <= 0 ) { continue; }
nodes = pselector.exec();
t1 = Date.now();
const nodes = pselector.exec();
const t1 = Date.now();
pselector.budget += t0 - t1;
if ( pselector.budget < -500 ) {
console.info('uBO: disabling %s', pselector.raw);
pselector.budget = -0x7FFFFFFF;
}
t0 = t1;
i = nodes.length;
let i = nodes.length;
while ( i-- ) {
this.domFilterer.hideNode(nodes[i]);
this.hiddenNodes.add(nodes[i]);
}
}

for ( const node of toRemove ) {
if ( this.hiddenNodes.has(node) ) { continue; }
this.domFilterer.unhideNode(node);
}
//console.timeEnd('procedural selectors/dom layout changed');
},

Expand All @@ -643,15 +636,17 @@ vAPI.DOMFilterer = (function() {

onDOMChanged: function(addedNodes, removedNodes) {
if ( this.selectors.size === 0 ) { return; }
this.addedNodes = this.addedNodes || addedNodes.length !== 0;
this.removedNodes = this.removedNodes || removedNodes;
this.mustApplySelectors =
this.mustApplySelectors ||
addedNodes.length !== 0 ||
removedNodes;
this.domFilterer.commit();
}
};

var DOMFiltererBase = vAPI.DOMFilterer;
const DOMFiltererBase = vAPI.DOMFilterer;

var domFilterer = function() {
const domFilterer = function() {
DOMFiltererBase.call(this);
this.exceptions = [];
this.proceduralFilterer = new DOMProceduralFilterer(this);
Expand Down Expand Up @@ -682,7 +677,7 @@ vAPI.DOMFilterer = (function() {
};

domFilterer.prototype.getAllSelectors = function() {
var out = DOMFiltererBase.prototype.getAllSelectors.call(this);
const out = DOMFiltererBase.prototype.getAllSelectors.call(this);
out.procedural = Array.from(this.proceduralFilterer.selectors.values());
return out;
};
Expand Down
Loading