Log `:style(...)` cosmetic filters only when there is a match

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/627
This commit is contained in:
Raymond Hill 2019-07-23 11:42:04 -04:00
parent 1eba94da0b
commit 08c3f06160
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 57 additions and 88 deletions

View File

@ -23,7 +23,7 @@
/******************************************************************************/ /******************************************************************************/
(function() { (( ) => {
/******************************************************************************/ /******************************************************************************/
@ -42,26 +42,16 @@ const simpleDeclarativeSet = new Set();
let simpleDeclarativeStr; let simpleDeclarativeStr;
const complexDeclarativeSet = new Set(); const complexDeclarativeSet = new Set();
let complexDeclarativeStr; let complexDeclarativeStr;
const declarativeStyleDict = new Map();
let declarativeStyleStr;
const proceduralDict = new Map(); const proceduralDict = new Map();
const exceptionDict = new Map(); const exceptionDict = new Map();
let exceptionStr; let exceptionStr;
const nodesToProcess = new Set(); const nodesToProcess = new Set();
let shouldProcessDeclarativeComplex = false;
let shouldProcessProcedural = false;
let shouldProcessExceptions = false;
const loggedSelectors = new Set(); const loggedSelectors = new Set();
/******************************************************************************/ /******************************************************************************/
const shouldProcess = function() {
return nodesToProcess.size !== 0 ||
shouldProcessDeclarativeComplex ||
shouldProcessProcedural ||
shouldProcessExceptions;
};
/******************************************************************************/
const processDeclarativeSimple = function(node, out) { const processDeclarativeSimple = function(node, out) {
if ( simpleDeclarativeSet.size === 0 ) { return; } if ( simpleDeclarativeSet.size === 0 ) { return; }
if ( simpleDeclarativeStr === undefined ) { if ( simpleDeclarativeStr === undefined ) {
@ -108,6 +98,26 @@ const processDeclarativeComplex = function(out) {
/******************************************************************************/ /******************************************************************************/
const processDeclarativeStyle = function(out) {
if ( declarativeStyleDict.size === 0 ) { return; }
if ( declarativeStyleStr === undefined ) {
declarativeStyleStr = Array.from(declarativeStyleDict.keys())
.join(',\n');
}
if ( document.querySelector(declarativeStyleStr) === null ) { return; }
for ( const [ selector, style ] of declarativeStyleDict ) {
if ( document.querySelector(selector) === null ) { continue; }
const raw = `##${selector}:style(${style})`;
out.push(raw);
declarativeStyleDict.delete(selector);
declarativeStyleStr = undefined;
loggedSelectors.add(raw);
if ( declarativeStyleDict.size === 0 ) { return; }
}
};
/******************************************************************************/
const processProcedural = function(out) { const processProcedural = function(out) {
if ( proceduralDict.size === 0 ) { return; } if ( proceduralDict.size === 0 ) { return; }
for ( const entry of proceduralDict ) { for ( const entry of proceduralDict ) {
@ -140,30 +150,29 @@ const processExceptions = function(out) {
const processTimer = new vAPI.SafeAnimationFrame(( ) => { const processTimer = new vAPI.SafeAnimationFrame(( ) => {
//console.time('dom logger/scanning for matches'); //console.time('dom logger/scanning for matches');
processTimer.clear(); processTimer.clear();
const toLog = []; if ( nodesToProcess.size === 0 ) { return; }
if ( nodesToProcess.size !== 0 && simpleDeclarativeSet.size !== 0 ) {
if ( nodesToProcess.size !== 1 && nodesToProcess.has(document) ) { if ( nodesToProcess.size !== 1 && nodesToProcess.has(document) ) {
nodesToProcess.clear(); nodesToProcess.clear();
nodesToProcess.add(document); nodesToProcess.add(document);
} }
const toLog = [];
if ( simpleDeclarativeSet.size !== 0 ) {
for ( const node of nodesToProcess ) { for ( const node of nodesToProcess ) {
processDeclarativeSimple(node, toLog); processDeclarativeSimple(node, toLog);
} }
nodesToProcess.clear();
} }
if ( shouldProcessDeclarativeComplex ) {
processDeclarativeComplex(toLog); processDeclarativeComplex(toLog);
shouldProcessDeclarativeComplex = false; processDeclarativeStyle(toLog);
}
if ( shouldProcessProcedural ) {
processProcedural(toLog); processProcedural(toLog);
shouldProcessProcedural = false;
}
if ( shouldProcessExceptions ) {
processExceptions(toLog); processExceptions(toLog);
shouldProcessExceptions = false;
} nodesToProcess.clear();
if ( toLog.length === 0 ) { return; } if ( toLog.length === 0 ) { return; }
vAPI.messaging.send( vAPI.messaging.send(
'scriptlets', 'scriptlets',
{ {
@ -179,20 +188,13 @@ const processTimer = new vAPI.SafeAnimationFrame(() => {
/******************************************************************************/ /******************************************************************************/
const attributeObserver = new MutationObserver(mutations => { const attributeObserver = new MutationObserver(mutations => {
if ( simpleDeclarativeSet.size !== 0 ) { if ( nodesToProcess.has(document) ) { return; }
for ( const mutation of mutations ) { for ( const mutation of mutations ) {
const node = mutation.target; const node = mutation.target;
if ( node.nodeType !== 1 ) { continue; } if ( node.nodeType !== 1 ) { continue; }
nodesToProcess.add(node); nodesToProcess.add(node);
} }
} if ( nodesToProcess.size !== 0 ) {
if ( complexDeclarativeSet.size !== 0 ) {
shouldProcessDeclarativeComplex = true;
}
if ( proceduralDict.size !== 0 ) {
shouldProcessProcedural = true;
}
if ( shouldProcess() ) {
processTimer.start(100); processTimer.start(100);
} }
}); });
@ -202,13 +204,11 @@ const attributeObserver = new MutationObserver(mutations => {
const handlers = { const handlers = {
onFiltersetChanged: function(changes) { onFiltersetChanged: function(changes) {
//console.time('dom logger/filterset changed'); //console.time('dom logger/filterset changed');
const simpleSizeBefore = simpleDeclarativeSet.size,
complexSizeBefore = complexDeclarativeSet.size,
logNow = [];
for ( const entry of (changes.declarative || []) ) { for ( const entry of (changes.declarative || []) ) {
for ( let 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;' ) {
logNow.push(`##${selector}:style(${entry[1]})`); declarativeStyleDict.set(selector, entry[1]);
declarativeStyleStr = undefined;
continue; continue;
} }
if ( reHasPseudoClass.test(selector) ) { if ( reHasPseudoClass.test(selector) ) {
@ -226,23 +226,6 @@ const handlers = {
} }
} }
} }
if ( logNow.length !== 0 ) {
vAPI.messaging.send(
'scriptlets',
{
what: 'logCosmeticFilteringData',
frameURL: window.location.href,
frameHostname: window.location.hostname,
matchedSelectors: logNow
}
);
}
if ( simpleDeclarativeSet.size !== simpleSizeBefore ) {
nodesToProcess.add(document.documentElement);
}
if ( complexDeclarativeSet.size !== complexSizeBefore ) {
shouldProcessDeclarativeComplex = true;
}
if ( if (
Array.isArray(changes.procedural) && Array.isArray(changes.procedural) &&
changes.procedural.length !== 0 changes.procedural.length !== 0
@ -250,7 +233,6 @@ const handlers = {
for ( const selector of changes.procedural ) { for ( const selector of changes.procedural ) {
proceduralDict.set(selector.raw, selector); proceduralDict.set(selector.raw, selector);
} }
shouldProcessProcedural = true;
} }
if ( Array.isArray(changes.exceptions) ) { if ( Array.isArray(changes.exceptions) ) {
for ( const selector of changes.exceptions ) { for ( const selector of changes.exceptions ) {
@ -263,11 +245,10 @@ const handlers = {
} }
} }
exceptionStr = undefined; exceptionStr = undefined;
shouldProcessExceptions = true;
} }
if ( shouldProcess() ) { nodesToProcess.clear();
nodesToProcess.add(document);
processTimer.start(1); processTimer.start(1);
}
//console.timeEnd('dom logger/filterset changed'); //console.timeEnd('dom logger/filterset changed');
}, },
@ -281,24 +262,12 @@ const handlers = {
}, },
onDOMChanged: function(addedNodes) { onDOMChanged: function(addedNodes) {
// This is to guard against runaway job queue. I suspect this could if ( nodesToProcess.has(document) ) { return; }
// occur on slower devices.
if ( simpleDeclarativeSet.size !== 0 ) {
for ( const node of addedNodes ) { for ( const node of addedNodes ) {
if ( node.parentNode === null ) { continue; } if ( node.parentNode === null ) { continue; }
nodesToProcess.add(node); nodesToProcess.add(node);
} }
} if ( nodesToProcess.size !== 0 ) {
if ( complexDeclarativeSet.size !== 0 ) {
shouldProcessDeclarativeComplex = true;
}
if ( proceduralDict.size !== 0 ) {
shouldProcessProcedural = true;
}
if ( exceptionDict.size !== 0 ) {
shouldProcessExceptions = true;
}
if ( shouldProcess() ) {
processTimer.start(100); processTimer.start(100);
} }
} }