mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
fix #2356
This commit is contained in:
parent
51a4e9ccf4
commit
8db80dc4b8
1 changed files with 150 additions and 127 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
uBlock Origin - a browser extension to block requests.
|
uBlock Origin - a browser extension to block requests.
|
||||||
Copyright (C) 2015-2018 Raymond Hill
|
Copyright (C) 2015-present Raymond Hill
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -35,153 +35,166 @@ if (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var reHasCSSCombinators = /[ >+~]/,
|
let reHasCSSCombinators = /[ >+~]/,
|
||||||
reHasPseudoClass = /:+(?:after|before)$/,
|
reHasPseudoClass = /:+(?:after|before)$/,
|
||||||
sanitizedSelectors = new Map(),
|
sanitizedSelectors = new Map(),
|
||||||
matchProp = vAPI.matchesProp,
|
simpleDeclarativeSet = new Set(),
|
||||||
simple = { dict: new Set(), str: undefined },
|
simpleDeclarativeStr,
|
||||||
complex = { dict: new Set(), str: undefined },
|
complexDeclarativeSet = new Set(),
|
||||||
procedural = { dict: new Map() },
|
complexDeclarativeStr,
|
||||||
jobQueue = [];
|
proceduralDict = new Map(),
|
||||||
|
nodesToProcess = new Set(),
|
||||||
|
shouldProcessDeclarativeComplex = false,
|
||||||
|
shouldProcessProcedural = false;
|
||||||
|
|
||||||
var DeclarativeSimpleJob = function(node) {
|
/******************************************************************************/
|
||||||
this.node = node;
|
|
||||||
|
let shouldProcess = function() {
|
||||||
|
return nodesToProcess.size !== 0 ||
|
||||||
|
shouldProcessDeclarativeComplex ||
|
||||||
|
shouldProcessProcedural;
|
||||||
};
|
};
|
||||||
DeclarativeSimpleJob.create = function(node) {
|
|
||||||
return new DeclarativeSimpleJob(node);
|
/******************************************************************************/
|
||||||
};
|
|
||||||
DeclarativeSimpleJob.prototype.lookup = function(out) {
|
let processDeclarativeSimple = function(node, out) {
|
||||||
if ( simple.dict.size === 0 ) { return; }
|
if ( simpleDeclarativeSet.size === 0 ) { return; }
|
||||||
if ( simple.str === undefined ) {
|
if ( simpleDeclarativeStr === undefined ) {
|
||||||
simple.str = Array.from(simple.dict).join(',\n');
|
simpleDeclarativeStr = Array.from(simpleDeclarativeSet).join(',\n');
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(this.node === document || this.node[matchProp](simple.str) === false) &&
|
(node === document || node.matches(simpleDeclarativeStr) === false) &&
|
||||||
(this.node.querySelector(simple.str) === null)
|
(node.querySelector(simpleDeclarativeStr) === null)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for ( var selector of simple.dict ) {
|
for ( let selector of simpleDeclarativeSet ) {
|
||||||
if (
|
if (
|
||||||
this.node !== document && this.node[matchProp](selector) ||
|
node !== document && node.matches(selector) ||
|
||||||
this.node.querySelector(selector) !== null
|
node.querySelector(selector) !== null
|
||||||
) {
|
) {
|
||||||
out.push(sanitizedSelectors.get(selector) || selector);
|
out.push(sanitizedSelectors.get(selector) || selector);
|
||||||
simple.dict.delete(selector);
|
simpleDeclarativeSet.delete(selector);
|
||||||
simple.str = undefined;
|
simpleDeclarativeStr = undefined;
|
||||||
if ( simple.dict.size === 0 ) { return; }
|
if ( simpleDeclarativeSet.size === 0 ) { return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var DeclarativeComplexJob = function() {
|
/******************************************************************************/
|
||||||
};
|
|
||||||
DeclarativeComplexJob.instance = null;
|
let processDeclarativeComplex = function(out) {
|
||||||
DeclarativeComplexJob.create = function() {
|
if ( complexDeclarativeSet.size === 0 ) { return; }
|
||||||
if ( DeclarativeComplexJob.instance === null ) {
|
if ( complexDeclarativeStr === undefined ) {
|
||||||
DeclarativeComplexJob.instance = new DeclarativeComplexJob();
|
complexDeclarativeStr = Array.from(complexDeclarativeSet).join(',\n');
|
||||||
}
|
}
|
||||||
return DeclarativeComplexJob.instance;
|
if ( document.querySelector(complexDeclarativeStr) === null ) { return; }
|
||||||
};
|
for ( let selector of complexDeclarativeSet ) {
|
||||||
DeclarativeComplexJob.prototype.lookup = function(out) {
|
if ( document.querySelector(selector) === null ) { continue; }
|
||||||
if ( complex.dict.size === 0 ) { return; }
|
out.push(sanitizedSelectors.get(selector) || selector);
|
||||||
if ( complex.str === undefined ) {
|
complexDeclarativeSet.delete(selector);
|
||||||
complex.str = Array.from(complex.dict).join(',\n');
|
complexDeclarativeStr = undefined;
|
||||||
}
|
if ( complexDeclarativeSet.size === 0 ) { return; }
|
||||||
if ( document.querySelector(complex.str) === null ) { return; }
|
|
||||||
for ( var selector of complex.dict ) {
|
|
||||||
if ( document.querySelector(selector) !== null ) {
|
|
||||||
out.push(sanitizedSelectors.get(selector) || selector);
|
|
||||||
complex.dict.delete(selector);
|
|
||||||
complex.str = undefined;
|
|
||||||
if ( complex.dict.size === 0 ) { return; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var ProceduralJob = function() {
|
/******************************************************************************/
|
||||||
};
|
|
||||||
ProceduralJob.instance = null;
|
let processProcedural = function(out) {
|
||||||
ProceduralJob.create = function() {
|
if ( proceduralDict.size === 0 ) { return; }
|
||||||
if ( ProceduralJob.instance === null ) {
|
for ( let entry of proceduralDict ) {
|
||||||
ProceduralJob.instance = new ProceduralJob();
|
if ( entry[1].test() === false ) { continue; }
|
||||||
}
|
out.push(entry[1].raw);
|
||||||
return ProceduralJob.instance;
|
proceduralDict.delete(entry[0]);
|
||||||
};
|
if ( proceduralDict.size === 0 ) { break; }
|
||||||
ProceduralJob.prototype.lookup = function(out) {
|
|
||||||
for ( var entry of procedural.dict ) {
|
|
||||||
if ( entry[1].test() ) {
|
|
||||||
procedural.dict.delete(entry[0]);
|
|
||||||
out.push(entry[1].raw);
|
|
||||||
if ( procedural.dict.size === 0 ) { return; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var jobQueueTimer = new vAPI.SafeAnimationFrame(function processJobQueue() {
|
/******************************************************************************/
|
||||||
|
|
||||||
|
let processTimer = new vAPI.SafeAnimationFrame(() => {
|
||||||
//console.time('dom logger/scanning for matches');
|
//console.time('dom logger/scanning for matches');
|
||||||
jobQueueTimer.clear();
|
processTimer.clear();
|
||||||
var toLog = [],
|
let toLog = [];
|
||||||
t0 = Date.now(),
|
if ( nodesToProcess.size !== 0 && simpleDeclarativeSet.size !== 0 ) {
|
||||||
job;
|
if ( nodesToProcess.has(document) ) {
|
||||||
while ( (job = jobQueue.shift()) ) {
|
nodesToProcess = new Set([ document ]);
|
||||||
job.lookup(toLog);
|
}
|
||||||
if ( (Date.now() - t0) > 10 ) { break; }
|
for ( let node of nodesToProcess ) {
|
||||||
|
processDeclarativeSimple(node, toLog);
|
||||||
|
}
|
||||||
|
nodesToProcess.clear();
|
||||||
}
|
}
|
||||||
if ( toLog.length !== 0 ) {
|
if ( shouldProcessDeclarativeComplex ) {
|
||||||
vAPI.messaging.send(
|
processDeclarativeComplex(toLog);
|
||||||
'scriptlets',
|
shouldProcessDeclarativeComplex = false;
|
||||||
{
|
|
||||||
what: 'logCosmeticFilteringData',
|
|
||||||
frameURL: window.location.href,
|
|
||||||
frameHostname: window.location.hostname,
|
|
||||||
matchedSelectors: toLog
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if ( simple.dict.size === 0 && complex.dict.size === 0 ) {
|
if ( shouldProcessProcedural ) {
|
||||||
jobQueue = [];
|
processProcedural(toLog);
|
||||||
}
|
shouldProcessProcedural = false;
|
||||||
if ( jobQueue.length !== 0 ) {
|
|
||||||
jobQueueTimer.start(100);
|
|
||||||
}
|
}
|
||||||
|
if ( toLog.length === 0 ) { return; }
|
||||||
|
vAPI.messaging.send(
|
||||||
|
'scriptlets',
|
||||||
|
{
|
||||||
|
what: 'logCosmeticFilteringData',
|
||||||
|
frameURL: window.location.href,
|
||||||
|
frameHostname: window.location.hostname,
|
||||||
|
matchedSelectors: toLog
|
||||||
|
}
|
||||||
|
);
|
||||||
//console.timeEnd('dom logger/scanning for matches');
|
//console.timeEnd('dom logger/scanning for matches');
|
||||||
});
|
});
|
||||||
|
|
||||||
var handlers = {
|
/******************************************************************************/
|
||||||
|
|
||||||
|
let attributeObserver = new MutationObserver(mutations => {
|
||||||
|
if ( simpleDeclarativeSet.size !== 0 ) {
|
||||||
|
for ( let mutation of mutations ) {
|
||||||
|
let node = mutation.target;
|
||||||
|
if ( node.nodeType !== 1 ) { continue; }
|
||||||
|
nodesToProcess.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( complexDeclarativeSet.size !== 0 ) {
|
||||||
|
shouldProcessDeclarativeComplex = true;
|
||||||
|
}
|
||||||
|
if ( proceduralDict.size !== 0 ) {
|
||||||
|
shouldProcessProcedural = true;
|
||||||
|
}
|
||||||
|
if ( shouldProcess() ) {
|
||||||
|
processTimer.start(100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
let handlers = {
|
||||||
onFiltersetChanged: function(changes) {
|
onFiltersetChanged: function(changes) {
|
||||||
//console.time('dom logger/filterset changed');
|
//console.time('dom logger/filterset changed');
|
||||||
var selector, sanitized, entry,
|
let simpleSizeBefore = simpleDeclarativeSet.size,
|
||||||
simpleSizeBefore = simple.dict.size,
|
complexSizeBefore = complexDeclarativeSet.size,
|
||||||
complexSizeBefore = complex.dict.size,
|
|
||||||
logNow = [];
|
logNow = [];
|
||||||
for ( entry of (changes.declarative || []) ) {
|
for ( let entry of (changes.declarative || []) ) {
|
||||||
for ( selector of entry[0].split(',\n') ) {
|
for ( let selector of entry[0].split(',\n') ) {
|
||||||
if ( entry[1] === 'display:none!important;' ) {
|
if ( entry[1] === 'display:none!important;' ) {
|
||||||
if ( reHasPseudoClass.test(selector) ) {
|
if ( reHasPseudoClass.test(selector) ) {
|
||||||
sanitized = selector.replace(reHasPseudoClass, '');
|
let sanitized = selector.replace(reHasPseudoClass, '');
|
||||||
sanitizedSelectors.set(sanitized, selector);
|
sanitizedSelectors.set(sanitized, selector);
|
||||||
selector = sanitized;
|
selector = sanitized;
|
||||||
}
|
}
|
||||||
if ( reHasCSSCombinators.test(selector) ) {
|
if ( reHasCSSCombinators.test(selector) ) {
|
||||||
complex.dict.add(selector);
|
complexDeclarativeSet.add(selector);
|
||||||
complex.str = undefined;
|
complexDeclarativeStr = undefined;
|
||||||
} else {
|
} else {
|
||||||
simple.dict.add(selector);
|
simpleDeclarativeSet.add(selector);
|
||||||
simple.str = undefined;
|
simpleDeclarativeStr = undefined;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logNow.push(selector + ':style(' + entry[1] + ')');
|
logNow.push(selector + ':style(' + entry[1] + ')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( simple.dict.size !== simpleSizeBefore ) {
|
|
||||||
jobQueue.push(DeclarativeSimpleJob.create(document));
|
|
||||||
}
|
|
||||||
if ( complex.dict.size !== complexSizeBefore ) {
|
|
||||||
complex.str = Array.from(complex.dict).join(',\n');
|
|
||||||
jobQueue.push(DeclarativeComplexJob.create());
|
|
||||||
}
|
|
||||||
if ( logNow.length !== 0 ) {
|
if ( logNow.length !== 0 ) {
|
||||||
vAPI.messaging.send(
|
vAPI.messaging.send(
|
||||||
'scriptlets',
|
'scriptlets',
|
||||||
|
|
@ -193,16 +206,23 @@ var handlers = {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ( Array.isArray(changes.procedural) ) {
|
if ( simpleDeclarativeSet.size !== simpleSizeBefore ) {
|
||||||
for ( selector of changes.procedural ) {
|
nodesToProcess.add(document.documentElement);
|
||||||
procedural.dict.set(selector.raw, selector);
|
|
||||||
}
|
|
||||||
if ( changes.procedural.length !== 0 ) {
|
|
||||||
jobQueue.push(ProceduralJob.create());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ( jobQueue.length !== 0 ) {
|
if ( complexDeclarativeSet.size !== complexSizeBefore ) {
|
||||||
jobQueueTimer.start(1);
|
shouldProcessDeclarativeComplex = true;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Array.isArray(changes.procedural) &&
|
||||||
|
changes.procedural.length !== 0
|
||||||
|
) {
|
||||||
|
for ( let selector of changes.procedural ) {
|
||||||
|
proceduralDict.set(selector.raw, selector);
|
||||||
|
}
|
||||||
|
shouldProcessProcedural = true;
|
||||||
|
}
|
||||||
|
if ( shouldProcess() ) {
|
||||||
|
processTimer.start(1);
|
||||||
}
|
}
|
||||||
//console.timeEnd('dom logger/filterset changed');
|
//console.timeEnd('dom logger/filterset changed');
|
||||||
},
|
},
|
||||||
|
|
@ -210,36 +230,39 @@ var handlers = {
|
||||||
onDOMCreated: function() {
|
onDOMCreated: function() {
|
||||||
handlers.onFiltersetChanged(vAPI.domFilterer.getAllSelectors());
|
handlers.onFiltersetChanged(vAPI.domFilterer.getAllSelectors());
|
||||||
vAPI.domFilterer.addListener(handlers);
|
vAPI.domFilterer.addListener(handlers);
|
||||||
|
attributeObserver.observe(document.body, {
|
||||||
|
attributes: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onDOMChanged: function(addedNodes) {
|
onDOMChanged: function(addedNodes) {
|
||||||
if ( simple.dict.size === 0 && complex.dict.size === 0 ) { return; }
|
|
||||||
// This is to guard against runaway job queue. I suspect this could
|
// This is to guard against runaway job queue. I suspect this could
|
||||||
// occur on slower devices.
|
// occur on slower devices.
|
||||||
if ( jobQueue.length <= 300 ) {
|
if ( simpleDeclarativeSet.size !== 0 ) {
|
||||||
if ( simple.dict.size !== 0 ) {
|
for ( let node of addedNodes ) {
|
||||||
for ( var node of addedNodes ) {
|
if ( node.parentNode === null ) { continue; }
|
||||||
jobQueue.push(DeclarativeSimpleJob.create(node));
|
nodesToProcess.add(node);
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( complex.dict.size !== 0 ) {
|
|
||||||
jobQueue.push(DeclarativeComplexJob.create());
|
|
||||||
}
|
|
||||||
if ( procedural.dict.size !== 0 ) {
|
|
||||||
jobQueue.push(ProceduralJob.create());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( jobQueue.length !== 0 ) {
|
if ( complexDeclarativeSet.size !== 0 ) {
|
||||||
jobQueueTimer.start(100);
|
shouldProcessDeclarativeComplex = true;
|
||||||
|
}
|
||||||
|
if ( proceduralDict.size !== 0 ) {
|
||||||
|
shouldProcessProcedural = true;
|
||||||
|
}
|
||||||
|
if ( shouldProcess() ) {
|
||||||
|
processTimer.start(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var onMessage = function(msg) {
|
let onMessage = function(msg) {
|
||||||
if ( msg.what === 'loggerDisabled' ) {
|
if ( msg.what === 'loggerDisabled' ) {
|
||||||
jobQueueTimer.clear();
|
processTimer.clear();
|
||||||
|
attributeObserver.disconnect();
|
||||||
vAPI.domFilterer.removeListener(handlers);
|
vAPI.domFilterer.removeListener(handlers);
|
||||||
vAPI.domWatcher.removeListener(handlers);
|
vAPI.domWatcher.removeListener(handlers);
|
||||||
vAPI.messaging.removeChannelListener('domLogger', onMessage);
|
vAPI.messaging.removeChannelListener('domLogger', onMessage);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue