1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-03 09:39:38 +02:00

Mitigate generic cosmetic filters erroneously targeting html/body

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/1692

The ids/classes from html/body elements will leave out
looking up lowly generic cosmetic filters made of a single
identifier.

This does not absolutely guarantee that html/body elements
will never be targeted, but it should greatly mitigate the
probability that this erroneously happens.
This commit is contained in:
Raymond Hill 2021-08-24 10:03:25 -04:00
parent 925c01dc14
commit f0b46bde4f
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 74 additions and 53 deletions

View File

@ -982,14 +982,14 @@ vAPI.DOMFilterer = class {
accepted: 0,
iterated: 0,
stopped: false,
add: function(nodes) {
add(nodes) {
if ( nodes.length === 0 || this.accepted >= maxSurveyNodes ) {
return;
}
this.nodeLists.push(nodes);
this.accepted += nodes.length;
},
next: function() {
next() {
if ( this.nodeLists.length === 0 || this.stopped ) { return 0; }
const nodeLists = this.nodeLists;
let ib = 0;
@ -1018,7 +1018,7 @@ vAPI.DOMFilterer = class {
}
return ib;
},
hasNodes: function() {
hasNodes() {
return this.nodeLists.length !== 0;
},
};
@ -1032,46 +1032,46 @@ vAPI.DOMFilterer = class {
// http://www.w3.org/TR/2014/REC-html5-20141028/infrastructure.html#space-separated-tokens
// http://jsperf.com/enumerate-classes/6
const idFromNode = (node, out) => {
const raw = node.id;
if ( typeof raw !== 'string' || raw.length === 0 ) { return; }
const s = raw.trim();
if ( queriedIds.has(s) || s.length === 0 ) { return; }
out.push(s);
queriedIds.add(s);
};
const classesFromNode = (node, out) => {
const s = node.className;
if ( typeof s !== 'string' || s.length === 0 ) { return; }
if ( reWhitespace.test(s) === false ) {
if ( queriedClasses.has(s) ) { return; }
out.push(s);
queriedClasses.add(s);
return;
}
for ( const s of node.classList.values() ) {
if ( queriedClasses.has(s) ) { continue; }
out.push(s);
queriedClasses.add(s);
}
};
const surveyPhase1 = function() {
//console.time('dom surveyor/surveying');
const t0 = performance.now();
const rews = reWhitespace;
const ids = [];
const classes = [];
const nodes = pendingNodes.buffer;
const deadline = t0 + maxSurveyTimeSlice;
let qids = queriedIds;
let qcls = queriedClasses;
let processed = 0;
for (;;) {
const n = pendingNodes.next();
if ( n === 0 ) { break; }
for ( let i = 0; i < n; i++ ) {
const node = nodes[i]; nodes[i] = null;
let v = node.id;
if ( typeof v === 'string' && v.length !== 0 ) {
v = v.trim();
if ( qids.has(v) === false && v.length !== 0 ) {
ids.push(v); qids.add(v);
}
}
let vv = node.className;
if ( typeof vv === 'string' && vv.length !== 0 ) {
if ( rews.test(vv) === false ) {
if ( qcls.has(vv) === false ) {
classes.push(vv); qcls.add(vv);
}
} else {
vv = node.classList;
let j = vv.length;
while ( j-- ) {
const v = vv[j];
if ( qcls.has(v) === false ) {
classes.push(v); qcls.add(v);
}
}
}
}
idFromNode(node, ids);
classesFromNode(node, classes);
}
processed += n;
if ( performance.now() >= deadline ) { break; }
@ -1084,8 +1084,7 @@ vAPI.DOMFilterer = class {
messaging.send('contentscript', {
what: 'retrieveGenericCosmeticSelectors',
hostname,
ids,
classes,
ids, classes,
exceptions: domFilterer.exceptions,
cost: surveyCost,
}).then(response => {
@ -1164,19 +1163,40 @@ vAPI.DOMFilterer = class {
}
//console.time('dom surveyor/dom layout created');
domFilterer = vAPI.domFilterer;
pendingNodes.add(document.querySelectorAll('[id],[class]'));
pendingNodes.add(document.querySelectorAll(
'[id]:not(html):not(body),[class]:not(html):not(body)'
));
surveyTimer.start();
// https://github.com/uBlockOrigin/uBlock-issues/issues/1692
// Look-up safe-only selectors to mitigate probability of
// html/body elements of erroneously being targeted.
const ids = [], classes = [];
idFromNode(document.documentElement, ids);
idFromNode(document.body, ids);
classesFromNode(document.documentElement, classes);
classesFromNode(document.body, classes);
if ( ids.length !== 0 || classes.length !== 0 ) {
messaging.send('contentscript', {
what: 'retrieveGenericCosmeticSelectors',
hostname,
ids, classes,
exceptions: domFilterer.exceptions,
safeOnly: true,
}).then(response => {
surveyPhase3(response);
});
}
//console.timeEnd('dom surveyor/dom layout created');
},
onDOMChanged: function(addedNodes) {
if ( addedNodes.length === 0 ) { return; }
//console.time('dom surveyor/dom layout changed');
let i = addedNodes.length;
while ( i-- ) {
const node = addedNodes[i];
for ( const node of addedNodes ) {
pendingNodes.add([ node ]);
if ( node.firstElementChild === null ) { continue; }
pendingNodes.add(node.querySelectorAll('[id],[class]'));
pendingNodes.add(node.querySelectorAll(
'[id]:not(html):not(body),[class]:not(html):not(body)'
));
}
if ( pendingNodes.hasNodes() ) {
surveyTimer.start(1);

View File

@ -829,6 +829,7 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) {
if ( this.acceptedCount === 0 ) { return; }
if ( !request.ids && !request.classes ) { return; }
const { safeOnly = false } = request;
//console.time('cosmeticFilteringEngine.retrieveGenericSelectors');
const simpleSelectors = this.$simpleSet;
@ -841,24 +842,24 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) {
const entry = this.lowlyGeneric[type];
const selectors = request[entry.canonical];
if ( Array.isArray(selectors) === false ) { continue; }
for ( let selector of selectors ) {
for ( const selector of selectors ) {
if ( entry.simple.has(selector) === false ) { continue; }
const bucket = entry.complex.get(selector);
if ( bucket !== undefined ) {
if ( Array.isArray(bucket) ) {
for ( const selector of bucket ) {
if ( previousHits.has(selector) === false ) {
complexSelectors.add(selector);
}
}
} else if ( previousHits.has(bucket) === false ) {
complexSelectors.add(bucket);
}
} else {
selector = entry.prefix + selector;
if ( previousHits.has(selector) === false ) {
simpleSelectors.add(selector);
}
if ( bucket === undefined ) {
if ( safeOnly ) { continue; }
const simpleSelector = entry.prefix + selector;
if ( previousHits.has(simpleSelector) ) { continue; }
simpleSelectors.add(simpleSelector);
continue;
}
if ( Array.isArray(bucket) === false ) {
if ( previousHits.has(bucket) ) { continue; }
complexSelectors.add(bucket);
continue;
}
for ( const selector of bucket ) {
if ( previousHits.has(selector) ) { continue; }
complexSelectors.add(selector);
}
}
}