diff --git a/platform/chromium/vapi-usercss.js b/platform/chromium/vapi-usercss.js index af9e40366..1de253cdd 100644 --- a/platform/chromium/vapi-usercss.js +++ b/platform/chromium/vapi-usercss.js @@ -222,7 +222,11 @@ vAPI.DOMFilterer.prototype = { vAPI.userStylesheet.add(selectorsStr + '\n{' + declarations + '}'); this.commit(); - this.triggerListeners('declarative', selectorsStr); + if ( this.hasListeners() ) { + this.triggerListeners({ + declarative: [ [ selectorsStr, declarations ] ] + }); + } if ( declarations !== 'display:none!important;' ) { this.specificOthers.push({ @@ -309,10 +313,14 @@ vAPI.DOMFilterer.prototype = { this.listeners.splice(pos, 1); }, - triggerListeners: function(type, selectors) { + hasListeners: function() { + return this.listeners.length !== 0; + }, + + triggerListeners: function(changes) { var i = this.listeners.length; while ( i-- ) { - this.listeners[i].onFiltersetChanged(type, selectors); + this.listeners[i].onFiltersetChanged(changes); } }, @@ -459,37 +467,59 @@ vAPI.DOMFilterer.prototype = { } }, - getAllDeclarativeSelectors_: function(all) { - var out = []; + getAllSelectors_: function(all) { + var out = { + declarative: [] + }; if ( this.specificSimpleHide.size !== 0 ) { - out.push(Array.from(this.specificSimpleHide).join(',\n')); + out.declarative.push([ + Array.from(this.specificSimpleHide).join(',\n'), + 'display:none!important;' + ]); } if ( this.specificComplexHide.size !== 0 ) { - out.push(Array.from(this.specificComplexHide).join(',\n')); + out.declarative.push([ + Array.from(this.specificComplexHide).join(',\n'), + 'display:none!important;' + ]); } if ( this.genericSimpleHide.size !== 0 ) { - out.push(Array.from(this.genericSimpleHide).join(',\n')); + out.declarative.push([ + Array.from(this.genericSimpleHide).join(',\n'), + 'display:none!important;' + ]); } if ( this.genericComplexHide.size !== 0 ) { - out.push(Array.from(this.genericComplexHide).join(',\n')); + out.declarative.push([ + Array.from(this.genericComplexHide).join(',\n'), + 'display:none!important;' + ]); } if ( all ) { - out.push('[' + this.hideNodeAttr + ']'); + out.declarative.push([ + '[' + this.hideNodeAttr + ']', + 'display:none!important;' + ]); } for ( var entry of this.specificOthers ) { - out.push(entry.selectors); + out.declarative.push([ entry.selectors, entry.declarations ]); } - return out.join(',\n'); + return out; }, getFilteredElementCount: function() { - var selectors = this.getAllDeclarativeSelectors_(true); + var details = this.getAllSelectors_(true); + if ( Array.isArray(details.declarative) === false ) { return 0; } + var selectors = details.declarative.reduce(function(acc, entry) { + acc.push(entry[0]); + return acc; + }, []); if ( selectors.length === 0 ) { return 0; } - return document.querySelectorAll(selectors).length; + return document.querySelectorAll(selectors.join(',\n')).length; }, - getAllDeclarativeSelectors: function() { - return this.getAllDeclarativeSelectors_(); + getAllSelectors: function() { + return this.getAllSelectors_(false); } }; diff --git a/platform/webext/vapi-usercss.js b/platform/webext/vapi-usercss.js index 62a623c2f..b8199d99b 100644 --- a/platform/webext/vapi-usercss.js +++ b/platform/webext/vapi-usercss.js @@ -80,8 +80,7 @@ vAPI.DOMFilterer.prototype = { // - Notifying listeners about changed filterset. commitNow: function() { this.commitTimer.clear(); - var userStylesheet = vAPI.userStylesheet, - addedSelectors = []; + var userStylesheet = vAPI.userStylesheet; for ( var entry of this.addedCSSRules ) { if ( this.disabled === false && @@ -92,13 +91,9 @@ vAPI.DOMFilterer.prototype = { entry.selectors + '\n{' + entry.declarations + '}' ); } - addedSelectors.push(entry.selectors); } this.addedCSSRules.clear(); userStylesheet.apply(); - if ( addedSelectors.length !== 0 ) { - this.triggerListeners('declarative', addedSelectors.join(',\n')); - } }, commit: function(commitNow) { @@ -133,6 +128,11 @@ vAPI.DOMFilterer.prototype = { vAPI.userStylesheet.add(selectorsStr + '\n{' + declarations + '}'); } this.commit(); + if ( this.hasListeners() ) { + this.triggerListeners({ + declarative: [ [ selectorsStr, declarations ] ] + }); + } }, addListener: function(listener) { @@ -146,10 +146,14 @@ vAPI.DOMFilterer.prototype = { this.listeners.splice(pos, 1); }, - triggerListeners: function(type, selectors) { + hasListeners: function() { + return this.listeners.length !== 0; + }, + + triggerListeners: function(changes) { var i = this.listeners.length; while ( i-- ) { - this.listeners[i].onFiltersetChanged(type, selectors); + this.listeners[i].onFiltersetChanged(changes); } }, @@ -196,29 +200,30 @@ vAPI.DOMFilterer.prototype = { userStylesheet.apply(); }, - getAllDeclarativeSelectors_: function(all) { - let selectors = []; + getAllSelectors_: function(all) { + var out = { + declarative: [] + }; for ( var entry of this.filterset ) { if ( all === false && entry.internal ) { continue; } - selectors.push(entry.selectors); - } - var out = selectors.join(',\n'); - if ( !all && this.hideNodeAttr !== undefined ) { - out = out.replace('[' + this.hideNodeAttr + ']', '') - .replace(/^,\n|\n,|,\n$/, ''); + out.declarative.push([ entry.selectors, entry.declarations ]); } return out; }, getFilteredElementCount: function() { - let selectors = this.getAllDeclarativeSelectors_(true); - return selectors.length !== 0 - ? document.querySelectorAll(selectors).length - : 0; + let details = this.getAllSelectors_(true); + if ( Array.isArray(details.declarative) === false ) { return 0; } + let selectors = details.declarative.reduce(function(acc, entry) { + acc.push(entry[0]); + return acc; + }, []); + if ( selectors.length === 0 ) { return 0; } + return document.querySelectorAll(selectors.join(',\n')).length; }, - getAllDeclarativeSelectors: function() { - return this.getAllDeclarativeSelectors_(false); + getAllSelectors: function() { + return this.getAllSelectors_(false); } }; diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 4c420bea1..8a68e7a25 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -571,10 +571,11 @@ vAPI.DOMFilterer = (function() { } if ( mustCommit === false ) { return; } this.domFilterer.commit(); - this.domFilterer.triggerListeners( - 'procedural', - new Map(this.addedSelectors) - ); + if ( this.domFilterer.hasListeners() ) { + this.domFilterer.triggerListeners({ + procedural: Array.from(this.addedSelectors.values()) + }); + } }, commitNow: function() { @@ -696,8 +697,10 @@ vAPI.DOMFilterer = (function() { return this.proceduralFilterer.createProceduralFilter(o); }; - domFilterer.prototype.getAllProceduralSelectors = function() { - return new Map(this.proceduralFilterer.selectors); + domFilterer.prototype.getAllSelectors = function() { + var out = DOMFiltererBase.prototype.getAllSelectors.call(this); + out.procedural = Array.from(this.proceduralFilterer.selectors.values()); + return out; }; domFilterer.prototype.getAllExceptionSelectors = function() { diff --git a/src/js/scriptlets/cosmetic-logger.js b/src/js/scriptlets/cosmetic-logger.js index d72e3cbed..bbfafa43e 100644 --- a/src/js/scriptlets/cosmetic-logger.js +++ b/src/js/scriptlets/cosmetic-logger.js @@ -149,40 +149,55 @@ var jobQueueTimer = new vAPI.SafeAnimationFrame(function processJobQueue() { }); var handlers = { - - onFiltersetChanged: function(type, selectors) { + onFiltersetChanged: function(changes) { //console.time('dom logger/filterset changed'); - var selector, - sanitized; - if ( type === 'declarative' ) { - var simpleSizeBefore = simple.dict.size, - complexSizeBefore = complex.dict.size; - for ( selector of selectors.split(',\n') ) { - if ( reHasPseudoClass.test(selector) ) { - sanitized = selector.replace(reHasPseudoClass, ''); - sanitizedSelectors.set(sanitized, selector); - selector = sanitized; - } - if ( reHasCSSCombinators.test(selector) ) { - complex.dict.add(selector); - complex.str = undefined; + var selector, sanitized, entry, + simpleSizeBefore = simple.dict.size, + complexSizeBefore = complex.dict.size, + logNow = []; + for ( entry of (changes.declarative || []) ) { + for ( selector of entry[0].split(',\n') ) { + if ( entry[1] === 'display:none!important;' ) { + if ( reHasPseudoClass.test(selector) ) { + sanitized = selector.replace(reHasPseudoClass, ''); + sanitizedSelectors.set(sanitized, selector); + selector = sanitized; + } + if ( reHasCSSCombinators.test(selector) ) { + complex.dict.add(selector); + complex.str = undefined; + } else { + simple.dict.add(selector); + simple.str = undefined; + } } else { - simple.dict.add(selector); - simple.str = undefined; + logNow.push(selector + ':style(' + entry[1] + ')'); } } - if ( simple.dict.size !== simpleSizeBefore ) { - jobQueue.push(DeclarativeSimpleJob.create(document)); + } + 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 ) { + vAPI.messaging.send( + 'scriptlets', + { + what: 'logCosmeticFilteringData', + frameURL: window.location.href, + frameHostname: window.location.hostname, + matchedSelectors: logNow + } + ); + } + if ( Array.isArray(changes.procedural) ) { + for ( selector of changes.procedural ) { + procedural.dict.set(selector.raw, selector); } - if ( complex.dict.size !== complexSizeBefore ) { - complex.str = Array.from(complex.dict).join(',\n'); - jobQueue.push(DeclarativeComplexJob.create()); - } - } else if ( type === 'procedural' ) { - for ( selector of selectors ) { - procedural.dict.set(selector[0], selector[1]); - } - if ( selectors.size !== 0 ) { + if ( changes.procedural.size !== 0 ) { jobQueue.push(ProceduralJob.create()); } } @@ -193,14 +208,7 @@ var handlers = { }, onDOMCreated: function() { - handlers.onFiltersetChanged( - 'declarative', - vAPI.domFilterer.getAllDeclarativeSelectors() - ); - handlers.onFiltersetChanged( - 'procedural', - vAPI.domFilterer.getAllProceduralSelectors() - ); + handlers.onFiltersetChanged(vAPI.domFilterer.getAllSelectors()); vAPI.domFilterer.addListener(handlers); }, @@ -225,7 +233,6 @@ var handlers = { jobQueueTimer.start(100); } } - }; /******************************************************************************/ diff --git a/src/js/scriptlets/dom-inspector.js b/src/js/scriptlets/dom-inspector.js index d0b7e1388..e306945e4 100644 --- a/src/js/scriptlets/dom-inspector.js +++ b/src/js/scriptlets/dom-inspector.js @@ -693,43 +693,42 @@ var cosmeticFilterMapper = (function() { var nodesFromStyleTag = function(rootNode) { var filterMap = roRedNodes, - selectors, selector, - nodes, node, - i, j; + entry, selector, canonical, nodes, node; + + var details = vAPI.domFilterer.getAllSelectors(); // Declarative selectors. - selectors = vAPI.domFilterer.getAllDeclarativeSelectors().split(',\n'); - i = selectors.length; - while ( i-- ) { - selector = selectors[i]; - if ( reHasCSSCombinators.test(selector) ) { - nodes = document.querySelectorAll(selector); - } else { - if ( - filterMap.has(rootNode) === false && - rootNode[matchesFnName](selector) - ) { - filterMap.set(rootNode, selector); + for ( entry of (details.declarative || []) ) { + for ( selector of entry[0].split(',\n') ) { + canonical = selector; + if ( entry[1] !== 'display:none!important;' ) { + canonical += ':style(' + entry[1] + ')'; } - nodes = rootNode.querySelectorAll(selector); - } - j = nodes.length; - while ( j-- ) { - node = nodes[j]; - if ( filterMap.has(node) === false ) { - filterMap.set(node, selector); + if ( reHasCSSCombinators.test(selector) ) { + nodes = document.querySelectorAll(selector); + } else { + if ( + filterMap.has(rootNode) === false && + rootNode[matchesFnName](selector) + ) { + filterMap.set(rootNode, canonical); + } + nodes = rootNode.querySelectorAll(selector); + } + for ( node of nodes ) { + if ( filterMap.has(node) === false ) { + filterMap.set(node, canonical); + } } } } // Procedural selectors. - selectors = vAPI.domFilterer.getAllProceduralSelectors(); - for ( var entry of selectors ) { - nodes = entry[1].exec(); - j = nodes.length; - while ( j-- ) { + for ( entry of (details.procedural || []) ) { + nodes = entry.exec(); + for ( node of nodes ) { if ( filterMap.has(node) === false ) { - filterMap.set(node, entry[0]); + filterMap.set(node, entry.raw); } } } @@ -968,6 +967,18 @@ var selectNodes = function(selector, nid) { /******************************************************************************/ +var nodesFromFilter = function(selector) { + var out = []; + for ( var entry of roRedNodes ) { + if ( entry[1] === selector ) { + out.push(entry[0]); + } + } + return out; +}; + +/******************************************************************************/ + var shutdown = function() { cosmeticFilterMapper.shutdown(); domLayout.shutdown(); @@ -1042,7 +1053,7 @@ var onMessage = function(request) { case 'toggleFilter': toggleExceptions( - selectNodes(request.filter, request.nid), + nodesFromFilter(request.filter), request.target ); highlightElements(true);