diff --git a/src/js/background.js b/src/js/background.js index a63a598f6..1fd23f5ca 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -56,6 +56,7 @@ const µBlock = (( ) => { // jshint ignore:line filterAuthorMode: false, loggerPopupType: 'popup', manualUpdateAssetFetchPeriod: 500, + popupCosmeticFilterBadgeSlow: false, popupFontSize: 'unset', requestJournalProcessPeriod: 1000, selfieAfter: 3, diff --git a/src/js/messaging.js b/src/js/messaging.js index 5fc6513a8..1305e6466 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -387,7 +387,10 @@ const onMessage = function(request, sender, callback) { // Async switch ( request.what ) { case 'getHiddenElementCount': - getElementCount(request.tabId, 'elements').then(count => { + const scriptlet = µb.hiddenSettings.popupCosmeticFilterBadgeSlow + ? 'elements-all' + : 'elements'; + getElementCount(request.tabId, scriptlet).then(count => { callback(count); }); return; diff --git a/src/js/scriptlets/dom-survey-elements-all.js b/src/js/scriptlets/dom-survey-elements-all.js new file mode 100644 index 000000000..51b109d60 --- /dev/null +++ b/src/js/scriptlets/dom-survey-elements-all.js @@ -0,0 +1,72 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2015-present Raymond Hill + + 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +'use strict'; + +/******************************************************************************/ + +// https://github.com/uBlockOrigin/uBlock-issues/issues/756 +// Keep in mind CPU usage with large DOM and/or filterset. + +(( ) => { + if ( typeof vAPI !== 'object' ) { return; } + + const t0 = Date.now(); + + if ( vAPI.domSurveyElements instanceof Object === false ) { + vAPI.domSurveyElements = { + busy: false, + hiddenElementCount: Number.NaN, + surveyTime: t0, + }; + } + const surveyResults = vAPI.domSurveyElements; + + if ( surveyResults.busy ) { return; } + surveyResults.busy = true; + + if ( surveyResults.surveyTime < vAPI.domMutationTime ) { + surveyResults.hiddenElementCount = Number.NaN; + } + surveyResults.surveyTime = t0; + + if ( isNaN(surveyResults.hiddenElementCount) ) { + surveyResults.hiddenElementCount = (( ) => { + if ( vAPI.domFilterer instanceof Object === false ) { return 0; } + const details = vAPI.domFilterer.getAllSelectors_(true); + if ( + Array.isArray(details.declarative) === false || + details.declarative.length === 0 + ) { + return 0; + } + return document.querySelectorAll( + details.declarative.map(entry => entry[0]).join(',') + ).length; + })(); + } + + surveyResults.busy = false; + + // IMPORTANT: This is returned to the injector, so this MUST be + // the last statement. + return surveyResults.hiddenElementCount; +})(); diff --git a/src/js/scriptlets/dom-survey-elements.js b/src/js/scriptlets/dom-survey-elements.js index e9ad5f568..d6bc83471 100644 --- a/src/js/scriptlets/dom-survey-elements.js +++ b/src/js/scriptlets/dom-survey-elements.js @@ -72,34 +72,32 @@ } const simpleStr = simple.join(',\n'); const complexStr = complex.join(',\n'); - const nodeIter = document.createNodeIterator( + const nodeIter = document.createTreeWalker( document.body, NodeFilter.SHOW_ELEMENT ); - const candidates = new Set(); - for (;;) { - const node = nodeIter.nextNode(); - if ( node === null ) { break; } - if ( node.offsetParent === null ) { - candidates.add(node); - } - } const matched = new Set(); - if ( simpleStr !== '') { - for ( const node of candidates ) { - if ( Date.now() > tMax ) { return -1; } - if ( node.matches(simpleStr) === false ) { continue; } - candidates.delete(node); - matched.add(node); - if ( matched.size === 99 ) { break; } + let node = nodeIter.nextNode(); + for (;;) { + if ( node === null ) { break; } + if ( Date.now() > tMax ) { return -1; } + if ( + (node.offsetParent !== null) || + (simpleStr === '' || node.matches(simpleStr) === false) && + (complexStr === '' || node.closest(complexStr) !== node) + ) { + node = nodeIter.nextNode(); + continue; } - } - if ( matched.size < 99 && complexStr !== '') { - for ( const node of candidates ) { - if ( Date.now() > tMax ) { return -1; } - if ( node.closest(complexStr) !== node ) { continue; } - matched.add(node); - if ( matched.size === 99 ) { break; } + matched.add(node); + if ( matched.size === 99 ) { break; } + // https://github.com/uBlockOrigin/uBlock-issues/issues/756#issuecomment-549079064 + // Skip descendants when a match is detected. + for (;;) { + node = nodeIter.nextSibling(); + if ( node !== null ) { break; } + node = nodeIter.parentNode(); + if ( node === null ) { break; } } } return matched.size;