From a76f5b15ac4ff6460b51325b0dcfd6ca2543880d Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 23 Oct 2017 12:21:37 -0400 Subject: [PATCH] fix https://github.com/gorhill/uBlock/issues/3160#issuecomment-338509997 --- platform/chromium/vapi-usercss.js | 37 +++---- platform/webext/vapi-usercss.js | 25 ++--- src/js/contentscript.js | 53 +++++----- src/js/cosmetic-filtering.js | 139 +++++++++++++++------------ src/js/scriptlets/cosmetic-logger.js | 8 +- 5 files changed, 143 insertions(+), 119 deletions(-) diff --git a/platform/chromium/vapi-usercss.js b/platform/chromium/vapi-usercss.js index a02b92882..af9e40366 100644 --- a/platform/chromium/vapi-usercss.js +++ b/platform/chromium/vapi-usercss.js @@ -91,8 +91,6 @@ vAPI.DOMFilterer = function() { this.commitTimer = new vAPI.SafeAnimationFrame(this.commitNow.bind(this)); this.domIsReady = document.readyState !== 'loading'; this.listeners = []; - this.hideNodeAttr = vAPI.randomToken(); - this.hideNodeStylesheet = false; this.excludedNodeSet = new WeakSet(); this.addedNodes = new Set(); this.removedNodes = false; @@ -119,8 +117,6 @@ vAPI.DOMFilterer = function() { }; vAPI.DOMFilterer.prototype = { - reHideStyle: /^display:none!important;$/, - // https://www.w3.org/community/webed/wiki/CSS/Selectors#Combinators reCSSCombinators: /[ >+~]/, @@ -136,27 +132,27 @@ vAPI.DOMFilterer.prototype = { // Filterset changed. if ( this.addedSpecificSimpleHide.length !== 0 ) { - console.time('specific simple filterset changed'); - console.log('added %d specific simple selectors', this.addedSpecificSimpleHide.length); + //console.time('specific simple filterset changed'); + //console.log('added %d specific simple selectors', this.addedSpecificSimpleHide.length); nodes = document.querySelectorAll(this.addedSpecificSimpleHide.join(',')); for ( node of nodes ) { this.hideNode(node); } this.addedSpecificSimpleHide = []; this.specificSimpleHideAggregated = undefined; - console.timeEnd('specific simple filterset changed'); + //console.timeEnd('specific simple filterset changed'); } if ( this.addedSpecificComplexHide.length !== 0 ) { - console.time('specific complex filterset changed'); - console.log('added %d specific complex selectors', this.addedSpecificComplexHide.length); + //console.time('specific complex filterset changed'); + //console.log('added %d specific complex selectors', this.addedSpecificComplexHide.length); nodes = document.querySelectorAll(this.addedSpecificComplexHide.join(',')); for ( node of nodes ) { this.hideNode(node); } this.addedSpecificComplexHide = []; this.specificComplexHideAggregated = undefined; - console.timeEnd('specific complex filterset changed'); + //console.timeEnd('specific complex filterset changed'); } // DOM layout changed. @@ -168,10 +164,10 @@ vAPI.DOMFilterer.prototype = { return; } - console.log('%d nodes added', this.addedNodes.size); + //console.log('%d nodes added', this.addedNodes.size); if ( this.specificSimpleHide.size !== 0 && domNodesAdded ) { - console.time('dom layout changed/specific simple selectors'); + //console.time('dom layout changed/specific simple selectors'); if ( this.specificSimpleHideAggregated === undefined ) { this.specificSimpleHideAggregated = Array.from(this.specificSimpleHide).join(',\n'); @@ -185,11 +181,11 @@ vAPI.DOMFilterer.prototype = { this.hideNode(node); } } - console.timeEnd('dom layout changed/specific simple selectors'); + //console.timeEnd('dom layout changed/specific simple selectors'); } if ( this.specificComplexHide.size !== 0 && domLayoutChanged ) { - console.time('dom layout changed/specific complex selectors'); + //console.time('dom layout changed/specific complex selectors'); if ( this.specificComplexHideAggregated === undefined ) { this.specificComplexHideAggregated = Array.from(this.specificComplexHide).join(',\n'); @@ -198,7 +194,7 @@ vAPI.DOMFilterer.prototype = { for ( node of nodes ) { this.hideNode(node); } - console.timeEnd('dom layout changed/specific complex selectors'); + //console.timeEnd('dom layout changed/specific complex selectors'); } this.addedNodes.clear(); @@ -228,7 +224,7 @@ vAPI.DOMFilterer.prototype = { this.commit(); this.triggerListeners('declarative', selectorsStr); - if ( this.reHideStyle.test(declarations) === false ) { + if ( declarations !== 'display:none!important;' ) { this.specificOthers.push({ selectors: selectorsStr, declarations: declarations @@ -381,8 +377,8 @@ vAPI.DOMFilterer.prototype = { new vAPI.SafeAnimationFrame(this.hideNodeBatchProcess.bind(this)); this.hiddenNodeObserver = new MutationObserver(this.hideNodeObserverHandler.bind(this)); - if ( this.hideNodeStylesheet === false ) { - this.hideNodeStylesheet = true; + if ( this.hideNodeStyleSheetInjected === false ) { + this.hideNodeStyleSheetInjected = true; vAPI.userStylesheet.add( '[' + this.hideNodeAttr + ']\n{display:none!important;}' ); @@ -400,7 +396,9 @@ vAPI.DOMFilterer.prototype = { hideNode: function(node) { if ( this.excludedNodeSet.has(node) ) { return; } + if ( this.hideNodeAttr === undefined ) { return; } if ( this.hiddenNodeset.has(node) ) { return; } + node.hidden = true; this.hiddenNodeset.add(node); if ( this.hideNodeExpando === undefined ) { this.hideNodeInit(); } node.setAttribute(this.hideNodeAttr, ''); @@ -416,6 +414,7 @@ vAPI.DOMFilterer.prototype = { unhideNode: function(node) { if ( this.hiddenNodeset.has(node) === false ) { return; } + node.hidden = false; node.removeAttribute(this.hideNodeAttr); this.hiddenNodesetToProcess.delete(node); if ( this.hideNodeExpando === undefined ) { return; } @@ -430,6 +429,7 @@ vAPI.DOMFilterer.prototype = { }, showNode: function(node) { + node.hidden = false; var attr = node[this.hideNodeExpando]; if ( attr === false ) { node.removeAttribute('style'); @@ -439,6 +439,7 @@ vAPI.DOMFilterer.prototype = { }, unshowNode: function(node) { + node.hidden = true; this.hiddenNodesetToProcess.add(node); }, diff --git a/platform/webext/vapi-usercss.js b/platform/webext/vapi-usercss.js index 35c7a0370..62a623c2f 100644 --- a/platform/webext/vapi-usercss.js +++ b/platform/webext/vapi-usercss.js @@ -61,8 +61,6 @@ vAPI.DOMFilterer = function() { this.disabled = false; this.listeners = []; this.filterset = new Set(); - this.hideNodeId = vAPI.randomToken(); - this.hideNodeStylesheet = false; this.excludedNodeSet = new WeakSet(); this.addedCSSRules = new Set(); @@ -123,7 +121,6 @@ vAPI.DOMFilterer.prototype = { selectors: selectorsStr, declarations, lazy: details.lazy === true, - internal: details.internal === true, injected: details.injected === true }; this.addedCSSRules.add(entry); @@ -167,19 +164,20 @@ vAPI.DOMFilterer.prototype = { hideNode: function(node) { if ( this.excludedNodeSet.has(node) ) { return; } - node.setAttribute(this.hideNodeId, ''); - if ( this.hideNodeStylesheet === false ) { - this.hideNodeStylesheet = true; + if ( this.hideNodeAttr === undefined ) { return; } + node.setAttribute(this.hideNodeAttr, ''); + if ( this.hideNodeStyleSheetInjected === false ) { + this.hideNodeStyleSheetInjected = true; this.addCSSRule( - '[' + this.hideNodeId + ']', - 'display:none!important;', - { internal: true } + '[' + this.hideNodeAttr + ']', + 'display:none!important;' ); } }, unhideNode: function(node) { - node.removeAttribute(this.hideNodeId); + if ( this.hideNodeAttr === undefined ) { return; } + node.removeAttribute(this.hideNodeAttr); }, toggle: function(state) { @@ -204,7 +202,12 @@ vAPI.DOMFilterer.prototype = { if ( all === false && entry.internal ) { continue; } selectors.push(entry.selectors); } - return selectors.join(',\n'); + var out = selectors.join(',\n'); + if ( !all && this.hideNodeAttr !== undefined ) { + out = out.replace('[' + this.hideNodeAttr + ']', '') + .replace(/^,\n|\n,|,\n$/, ''); + } + return out; }, getFilteredElementCount: function() { diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 96643ffc8..f1950a304 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -156,7 +156,7 @@ vAPI.domWatcher = (function() { safeObserverHandlerTimer; var safeObserverHandler = function() { - console.time('dom watcher/safe observer handler'); + //console.time('dom watcher/safe observer handler'); safeObserverHandlerTimer.clear(); var i = addedNodeLists.length, j = addedNodes.length, @@ -184,7 +184,7 @@ vAPI.domWatcher = (function() { } } removedNodeLists.length = 0; - console.timeEnd('dom watcher/safe observer handler'); + //console.timeEnd('dom watcher/safe observer handler'); if ( addedNodes.length === 0 && removedNodes === false ) { return; } for ( var listener of getListenerIterator() ) { listener.onDOMChanged(addedNodes, removedNodes); @@ -196,7 +196,7 @@ vAPI.domWatcher = (function() { // https://github.com/chrisaljoudi/uBlock/issues/205 // Do not handle added node directly from within mutation observer. var observerHandler = function(mutations) { - console.time('dom watcher/observer handler'); + //console.time('dom watcher/observer handler'); var nodeList, mutation, i = mutations.length; while ( i-- ) { @@ -214,7 +214,7 @@ vAPI.domWatcher = (function() { if ( addedNodeLists.length !== 0 || removedNodes ) { safeObserverHandlerTimer.start(); } - console.timeEnd('dom watcher/observer handler'); + //console.timeEnd('dom watcher/observer handler'); }; var startMutationObserver = function() { @@ -557,7 +557,7 @@ vAPI.DOMFilterer = (function() { entry, nodes, i, node; if ( this.addedSelectors.size !== 0 ) { - console.time('procedural filterset changed'); + //console.time('procedural filterset changed'); for ( entry of this.addedSelectors ) { nodes = entry[1].exec(); i = nodes.length; @@ -568,11 +568,11 @@ vAPI.DOMFilterer = (function() { } } this.addedSelectors.clear(); - console.timeEnd('procedural filterset changed'); + //console.timeEnd('procedural filterset changed'); return; } - console.time('dom layout changed/procedural selectors'); + //console.time('dom layout changed/procedural selectors'); this.addedNodes = this.removedNodes = false; @@ -608,7 +608,7 @@ vAPI.DOMFilterer = (function() { this.currentResultset = afterResultset; - console.timeEnd('dom layout changed/procedural selectors'); + //console.timeEnd('dom layout changed/procedural selectors'); }, createProceduralFilter: function(o) { @@ -617,7 +617,7 @@ vAPI.DOMFilterer = (function() { onDOMCreated: function() { this.domIsReady = true; - this.domFilterer.commit(); + this.domFilterer.commitNow(); }, onDOMChanged: function(addedNodes, removedNodes) { @@ -634,6 +634,8 @@ vAPI.DOMFilterer = (function() { DOMFiltererBase.call(this); this.exceptions = []; this.proceduralFilterer = new DOMProceduralFilterer(this); + this.hideNodeAttr = undefined; + this.hideNodeStyleSheetInjected = false; // May or may not exist: cache locally since this may be called often. this.baseOnDOMChanged = DOMFiltererBase.prototype.onDOMChanged; @@ -1084,7 +1086,7 @@ vAPI.domSurveyor = (function() { // http://jsperf.com/enumerate-classes/6 var surveyPhase1 = function() { - console.time('dom surveyor/surveying'); + //console.time('dom surveyor/surveying'); surveyTimer.clear(); var t0 = window.performance.now(); var rews = reWhitespace, @@ -1145,7 +1147,7 @@ vAPI.domSurveyor = (function() { } else { surveyPhase3(null); } - console.timeEnd('dom surveyor/surveying'); + //console.timeEnd('dom surveyor/surveying'); }; var reWhitespace = /\s/; @@ -1164,16 +1166,16 @@ vAPI.domSurveyor = (function() { } return; } - console.time('dom surveyor/dom layout created'); + //console.time('dom surveyor/dom layout created'); domFilterer = vAPI.domFilterer; addChunk(pendingIdNodes, document.querySelectorAll('[id]')); addChunk(pendingClassNodes, document.querySelectorAll('[class]')); surveyTimer.start(); - console.timeEnd('dom surveyor/dom layout created'); + //console.timeEnd('dom surveyor/dom layout created'); }, onDOMChanged: function(addedNodes) { if ( addedNodes.length === 0 ) { return; } - console.time('dom surveyor/dom layout changed'); + //console.time('dom surveyor/dom layout changed'); var idNodes = [], iid = 0, classNodes = [], iclass = 0; var i = addedNodes.length, @@ -1199,7 +1201,7 @@ vAPI.domSurveyor = (function() { addChunk(pendingClassNodes, classNodes); surveyTimer.start(1); } - console.timeEnd('dom surveyor/dom layout changed'); + //console.timeEnd('dom surveyor/dom layout changed'); } }; @@ -1274,8 +1276,6 @@ vAPI.domSurveyor = (function() { return; } - var injected = cfeDetails.rulesInjected === true; - if ( response.noCosmeticFiltering ) { vAPI.domFilterer = null; vAPI.domSurveyor = null; @@ -1285,27 +1285,34 @@ vAPI.domSurveyor = (function() { vAPI.domSurveyor = null; } domFilterer.exceptions = cfeDetails.exceptionFilters; + domFilterer.hideNodeAttr = cfeDetails.hideNodeAttr; + domFilterer.hideNodeStyleSheetInjected = + cfeDetails.hideNodeStyleSheetInjected === true; domFilterer.addCSSRule( cfeDetails.declarativeFilters, - 'display:none!important;', - { injected: injected } + 'display:none!important;' ); domFilterer.addCSSRule( cfeDetails.highGenericHideSimple, 'display:none!important;', - { type: 'simple', lazy: true, injected: injected } + { type: 'simple', lazy: true } ); domFilterer.addCSSRule( cfeDetails.highGenericHideComplex, 'display:none!important;', - { type: 'complex', lazy: true, injected: injected } + { type: 'complex', lazy: true } + ); + domFilterer.addCSSRule( + cfeDetails.injectedHideFilters, + 'display:none!important;', + { injected: true } ); domFilterer.addProceduralSelectors(cfeDetails.proceduralFilters); } - if ( cfeDetails.netFilters.length !== 0 && injected !== true ) { + if ( cfeDetails.networkFilters.length !== 0 ) { vAPI.userStylesheet.add( - cfeDetails.netFilters + '\n{display:none!important;}'); + cfeDetails.networkFilters + '\n{display:none!important;}'); } vAPI.userStylesheet.apply(); diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 4d6ef1b8a..514ad1035 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -1887,11 +1887,63 @@ FilterContainer.prototype.pruneSelectorCacheAsync = function() { /******************************************************************************/ +FilterContainer.prototype.randomAlphaToken = function() { + return String.fromCharCode(Date.now() % 26 + 97) + + Math.floor(Math.random() * 982451653 + 982451653).toString(36); +}; + +/******************************************************************************/ + +FilterContainer.prototype.injectRules = function(target, data) { + if ( + target instanceof Object === false || + target.tab instanceof Object === false || + typeof target.frameId !== 'number' + ) { + return; + } + var details = { + code: '', + cssOrigin: 'user', + frameId: target.frameId, + runAt: 'document_start' + }; + var injectedHideFilters = []; + if ( data.declarativeFilters.length !== 0 ) { + injectedHideFilters.push(data.declarativeFilters.join(',\n')); + data.declarativeFilters = []; + } + if ( data.proceduralFilters.length !== 0 ) { + injectedHideFilters.push('[' + data.hideNodeAttr + ']'); + data.hideNodeStyleSheetInjected = true; + } + if ( data.highGenericHideSimple.length !== 0 ) { + injectedHideFilters.push(data.highGenericHideSimple); + data.highGenericHideSimple = ''; + } + if ( data.highGenericHideComplex.length !== 0 ) { + injectedHideFilters.push(data.highGenericHideComplex); + data.highGenericHideComplex = ''; + } + data.injectedHideFilters = injectedHideFilters.join(',\n'); + if ( data.injectedHideFilters.length !== 0 ) { + details.code = data.injectedHideFilters + '\n{display:none!important;}'; + vAPI.insertCSS(target.tab.id, details); + } + if ( data.networkFilters.length !== 0 ) { + details.code = data.networkFilters + '\n{display:none!important;}'; + vAPI.insertCSS(target.tab.id, details); + data.networkFilters = ''; + } +}; + +/******************************************************************************/ + FilterContainer.prototype.retrieveGenericSelectors = function(request) { if ( this.acceptedCount === 0 ) { return; } if ( !request.ids && !request.classes ) { return; } - console.time('cosmeticFilteringEngine.retrieveGenericSelectors'); + //console.time('cosmeticFilteringEngine.retrieveGenericSelectors'); var simpleSelectors = this.setRegister0, complexSelectors = this.setRegister1; @@ -1959,33 +2011,13 @@ FilterContainer.prototype.retrieveGenericSelectors = function(request) { this.setRegister0.clear(); this.setRegister1.clear(); - console.timeEnd('cosmeticFilteringEngine.retrieveGenericSelectors'); + //console.timeEnd('cosmeticFilteringEngine.retrieveGenericSelectors'); return out; }; /******************************************************************************/ -FilterContainer.prototype.injectHideRules = function( - tabId, - frameId, - selectors, - runAt -) { - var details = { - code: '', - cssOrigin: 'user', - frameId: frameId, - runAt: runAt - }; - for ( var selector of selectors ) { - details.code = selector + '\n{display:none!important;}'; - vAPI.insertCSS(tabId, details); - } -}; - -/******************************************************************************/ - FilterContainer.prototype.retrieveDomainSelectors = function( request, sender, @@ -1993,7 +2025,7 @@ FilterContainer.prototype.retrieveDomainSelectors = function( ) { if ( !request.locationURL ) { return; } - console.time('cosmeticFilteringEngine.retrieveDomainSelectors'); + //console.time('cosmeticFilteringEngine.retrieveDomainSelectors'); // TODO: consider using MRUCache to quickly lookup all the previously // looked-up data. @@ -2015,15 +2047,17 @@ FilterContainer.prototype.retrieveDomainSelectors = function( ready: this.frozen, domain: domain, entity: entity, - noDOMSurveying: this.hasGenericHide === false, declarativeFilters: [], exceptionFilters: [], + hideNodeAttr: this.randomAlphaToken(), + hideNodeStyleSheetInjected: false, highGenericSimple: '', highGenericComplex: '', - netFilters: '', + injectedHideFilters: '', + networkFilters: '', + noDOMSurveying: this.hasGenericHide === false, proceduralFilters: [], - scripts: undefined, - rulesInjected: false + scripts: undefined }; if ( options.noCosmeticFiltering !== true ) { @@ -2041,30 +2075,23 @@ FilterContainer.prototype.retrieveDomainSelectors = function( if ( (bucket = this.specificFilters.get('!' + domainHash)) ) { bucket.retrieve(hostname, exceptionSet); } + if ( (bucket = this.proceduralFilters.get('!' + domainHash)) ) { + bucket.retrieve(hostname, exceptionSet); + } // Specific entity-based exception cosmetic filters. if ( entityHash !== undefined ) { if ( (bucket = this.specificFilters.get('!' + entityHash)) ) { bucket.retrieve(entity, exceptionSet); } + if ( (bucket = this.proceduralFilters.get('!' + entityHash)) ) { + bucket.retrieve(entity, exceptionSet); + } } // Special bucket for those filters without a valid // domain name as per PSL. if ( (bucket = this.specificFilters.get('!' + this.noDomainHash)) ) { bucket.retrieve(hostname, exceptionSet); } - - // Specific exception procedural cosmetic filters. - if ( (bucket = this.proceduralFilters.get('!' + domainHash)) ) { - bucket.retrieve(hostname, exceptionSet); - } - // Specific entity-based exception procedural cosmetic filters. - if ( entityHash !== undefined ) { - if ( (bucket = this.proceduralFilters.get('!' + entityHash)) ) { - bucket.retrieve(entity, exceptionSet); - } - } - // Special bucket for those filters without a valid - // domain name as per PSL. if ( (bucket = this.proceduralFilters.get('!' + this.noDomainHash)) ) { bucket.retrieve(hostname, exceptionSet); } @@ -2162,33 +2189,19 @@ FilterContainer.prototype.retrieveDomainSelectors = function( r.scripts = this.retrieveUserScripts(domain, hostname); if ( cacheEntry ) { - var netFilters = []; - cacheEntry.retrieve('net', netFilters); - r.netFilters = netFilters.join(',\n'); + var networkFilters = []; + cacheEntry.retrieve('net', networkFilters); + r.networkFilters = networkFilters.join(',\n'); } - if ( - this.supportsUserStylesheets && - sender instanceof Object && - sender.tab instanceof Object && - typeof sender.frameId === 'number' - ) { - this.injectHideRules( - sender.tab.id, - sender.frameId, - [ r.declarativeFilters.join(',\n'), r.netFilters ], - 'document_start' - ); - this.injectHideRules( - sender.tab.id, - sender.frameId, - [ r.highGenericHideSimple, r.highGenericHideComplex ], - 'document_end' - ); - r.rulesInjected = true; + // https://github.com/gorhill/uBlock/issues/3160 + // If user stylesheets are supported in the current process, inject the + // cosmetic filter now. + if ( this.supportsUserStylesheets ) { + this.injectRules(sender, r); } - console.timeEnd('cosmeticFilteringEngine.retrieveDomainSelectors'); + //console.timeEnd('cosmeticFilteringEngine.retrieveDomainSelectors'); return r; }; diff --git a/src/js/scriptlets/cosmetic-logger.js b/src/js/scriptlets/cosmetic-logger.js index 7cb4a8b17..d72e3cbed 100644 --- a/src/js/scriptlets/cosmetic-logger.js +++ b/src/js/scriptlets/cosmetic-logger.js @@ -119,7 +119,7 @@ ProceduralJob.prototype.lookup = function(out) { }; var jobQueueTimer = new vAPI.SafeAnimationFrame(function processJobQueue() { - console.time('dom logger/scanning for matches'); + //console.time('dom logger/scanning for matches'); jobQueueTimer.clear(); var toLog = [], t0 = Date.now(), @@ -145,13 +145,13 @@ var jobQueueTimer = new vAPI.SafeAnimationFrame(function processJobQueue() { if ( jobQueue.length !== 0 ) { jobQueueTimer.start(100); } - console.timeEnd('dom logger/scanning for matches'); + //console.timeEnd('dom logger/scanning for matches'); }); var handlers = { onFiltersetChanged: function(type, selectors) { - console.time('dom logger/filterset changed'); + //console.time('dom logger/filterset changed'); var selector, sanitized; if ( type === 'declarative' ) { @@ -189,7 +189,7 @@ var handlers = { if ( jobQueue.length !== 0 ) { jobQueueTimer.start(1); } - console.timeEnd('dom logger/filterset changed'); + //console.timeEnd('dom logger/filterset changed'); }, onDOMCreated: function() {