mirror of https://github.com/gorhill/uBlock.git
Count hidden elements on-demand only in popup panel
Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/756 The badge value for the no-cosmetic-filtering switch will be evaluated on-demand only, when the user hover over the switch with the mouse cursor. For touch screen displays, a tap on the switch will cause the badge to be rendered if not already done, otherwise this will toggle the switch as usual.
This commit is contained in:
parent
7c0294bd5f
commit
c090d2fde4
|
@ -366,39 +366,34 @@ const popupDataFromRequest = async function(request) {
|
||||||
return popupDataFromTabId(tabId, tabTitle);
|
return popupDataFromTabId(tabId, tabTitle);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDOMStats = async function(tabId) {
|
const getElementCount = async function(tabId, what) {
|
||||||
const results = await vAPI.tabs.executeScript(tabId, {
|
const results = await vAPI.tabs.executeScript(tabId, {
|
||||||
allFrames: true,
|
allFrames: true,
|
||||||
file: '/js/scriptlets/dom-survey.js',
|
file: `/js/scriptlets/dom-survey-${what}.js`,
|
||||||
runAt: 'document_end',
|
runAt: 'document_end',
|
||||||
});
|
});
|
||||||
|
|
||||||
let elementCount = 0;
|
let total = 0;
|
||||||
let scriptCount = 0;
|
results.forEach(count => {
|
||||||
results.forEach(result => {
|
if ( typeof count !== 'number' ) { return; }
|
||||||
if ( result instanceof Object === false ) { return; }
|
total += count;
|
||||||
if ( result.hiddenElementCount > 0 ) {
|
|
||||||
elementCount += result.hiddenElementCount;
|
|
||||||
}
|
|
||||||
if ( result.externalScriptCount > 0 ) {
|
|
||||||
scriptCount += result.externalScriptCount;
|
|
||||||
}
|
|
||||||
if ( result.inlineScriptCount > 0 ) {
|
|
||||||
scriptCount += 1;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return { elementCount, scriptCount };
|
return total;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMessage = function(request, sender, callback) {
|
const onMessage = function(request, sender, callback) {
|
||||||
let pageStore;
|
|
||||||
|
|
||||||
// Async
|
// Async
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
case 'getPopupLazyData':
|
case 'getHiddenElementCount':
|
||||||
getDOMStats(request.tabId).then(results => {
|
getElementCount(request.tabId, 'elements').then(count => {
|
||||||
callback(results);
|
callback(count);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'getScriptCount':
|
||||||
|
getElementCount(request.tabId, 'scripts').then(count => {
|
||||||
|
callback(count);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -414,6 +409,7 @@ const onMessage = function(request, sender, callback) {
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
let response;
|
let response;
|
||||||
|
let pageStore;
|
||||||
|
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
case 'hasPopupContentChanged':
|
case 'hasPopupContentChanged':
|
||||||
|
|
|
@ -668,24 +668,48 @@ let renderOnce = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const renderPopupLazy = async function() {
|
const renderPopupLazy = (( ) => {
|
||||||
const result = await messaging.send('popupPanel', {
|
let mustRenderCosmeticFilteringBadge = true;
|
||||||
what: 'getPopupLazyData',
|
|
||||||
tabId: popupData.tabId,
|
|
||||||
});
|
|
||||||
if ( result instanceof Object === false ) { return; }
|
|
||||||
|
|
||||||
let count = result.elementCount || 0;
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/756
|
||||||
uDom.nodeFromSelector('#no-cosmetic-filtering > span.fa-icon-badge')
|
// Launch potentially expensive hidden elements-counting scriptlet on
|
||||||
.textContent = count !== 0
|
// demand only.
|
||||||
? Math.min(count, 99).toLocaleString()
|
{
|
||||||
: '';
|
const sw = uDom.nodeFromId('no-cosmetic-filtering');
|
||||||
count = result.scriptCount || 0;
|
const badge = sw.querySelector(':scope > span.fa-icon-badge');
|
||||||
uDom.nodeFromSelector('#no-scripting > span.fa-icon-badge')
|
badge.textContent = '\u22EF';
|
||||||
.textContent = count !== 0
|
|
||||||
? Math.min(count, 99).toLocaleString()
|
const render = ( ) => {
|
||||||
: '';
|
if ( mustRenderCosmeticFilteringBadge === false ) { return; }
|
||||||
};
|
mustRenderCosmeticFilteringBadge = false;
|
||||||
|
if ( sw.classList.contains('hnSwitchBusy') ) { return; }
|
||||||
|
sw.classList.add('hnSwitchBusy');
|
||||||
|
messaging.send('popupPanel', {
|
||||||
|
what: 'getHiddenElementCount',
|
||||||
|
tabId: popupData.tabId,
|
||||||
|
}).then(count => {
|
||||||
|
badge.textContent = (count || 0) !== 0
|
||||||
|
? Math.min(count, 99).toLocaleString()
|
||||||
|
: '';
|
||||||
|
sw.classList.remove('hnSwitchBusy');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
sw.addEventListener('mouseenter', render, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return async function() {
|
||||||
|
const count = await messaging.send('popupPanel', {
|
||||||
|
what: 'getScriptCount',
|
||||||
|
tabId: popupData.tabId,
|
||||||
|
});
|
||||||
|
uDom.nodeFromSelector('#no-scripting > span.fa-icon-badge')
|
||||||
|
.textContent = (count || 0) !== 0
|
||||||
|
? Math.min(count, 99).toLocaleString()
|
||||||
|
: '';
|
||||||
|
mustRenderCosmeticFilteringBadge = true;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
@ -954,6 +978,13 @@ const toggleHostnameSwitch = async function(ev) {
|
||||||
const target = ev.currentTarget;
|
const target = ev.currentTarget;
|
||||||
const switchName = target.getAttribute('id');
|
const switchName = target.getAttribute('id');
|
||||||
if ( !switchName ) { return; }
|
if ( !switchName ) { return; }
|
||||||
|
// For touch displays, process click only if the switch is not "busy".
|
||||||
|
if (
|
||||||
|
vAPI.webextFlavor.soup.has('mobile') &&
|
||||||
|
target.classList.contains('hnSwitchBusy')
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
target.classList.toggle('on');
|
target.classList.toggle('on');
|
||||||
renderTooltips('#' + switchName);
|
renderTooltips('#' + switchName);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
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: -1,
|
||||||
|
surveyTime: t0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const surveyResults = vAPI.domSurveyElements;
|
||||||
|
|
||||||
|
if ( surveyResults.busy ) { return; }
|
||||||
|
surveyResults.busy = true;
|
||||||
|
|
||||||
|
if ( surveyResults.surveyTime < vAPI.domMutationTime ) {
|
||||||
|
surveyResults.hiddenElementCount = -1;
|
||||||
|
}
|
||||||
|
surveyResults.surveyTime = t0;
|
||||||
|
|
||||||
|
if ( surveyResults.hiddenElementCount === -1 ) {
|
||||||
|
surveyResults.hiddenElementCount = (( ) => {
|
||||||
|
if ( vAPI.domFilterer instanceof Object === false ) { return 0; }
|
||||||
|
const details = vAPI.domFilterer.getAllSelectors_(true);
|
||||||
|
if ( Array.isArray(details.declarative) === false ) { return 0; }
|
||||||
|
const selectors = details.declarative.map(entry => entry[0]);
|
||||||
|
const simple = [], complex = [];
|
||||||
|
for ( const selectorStr of selectors ) {
|
||||||
|
for ( const selector of selectorStr.split(',\n') ) {
|
||||||
|
if ( /[ +>~]/.test(selector) ) {
|
||||||
|
complex.push(selector);
|
||||||
|
} else {
|
||||||
|
simple.push(selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const simpleStr = simple.join(',\n');
|
||||||
|
const complexStr = complex.join(',\n');
|
||||||
|
const nodeIter = document.createNodeIterator(
|
||||||
|
document.body,
|
||||||
|
NodeFilter.SHOW_ELEMENT
|
||||||
|
);
|
||||||
|
const matched = new Set();
|
||||||
|
for (;;) {
|
||||||
|
const node = nodeIter.nextNode();
|
||||||
|
if ( node === null ) { break; }
|
||||||
|
if ( node.offsetParent !== null ) { continue; }
|
||||||
|
if (
|
||||||
|
node.matches(simpleStr) === false &&
|
||||||
|
node.closest(complexStr) !== node
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
matched.add(node);
|
||||||
|
if ( matched.size === 99 ) { break; }
|
||||||
|
}
|
||||||
|
return matched.size;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
surveyResults.busy = false;
|
||||||
|
|
||||||
|
// IMPORTANT: This is returned to the injector, so this MUST be
|
||||||
|
// the last statement.
|
||||||
|
return surveyResults.hiddenElementCount;
|
||||||
|
})();
|
|
@ -23,31 +23,28 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/756
|
// Scriptlets to count the number of script tags in a document.
|
||||||
// Keep in mind CPU usage witj large DOM and/or filterset.
|
|
||||||
|
|
||||||
(( ) => {
|
(( ) => {
|
||||||
if ( typeof vAPI !== 'object' ) { return; }
|
if ( typeof vAPI !== 'object' ) { return; }
|
||||||
|
|
||||||
const t0 = Date.now();
|
const t0 = Date.now();
|
||||||
const tMax = t0 + 60;
|
const tMax = t0 + 50;
|
||||||
|
|
||||||
if ( vAPI.domSurveyResults instanceof Object === false ) {
|
if ( vAPI.domSurveyScripts instanceof Object === false ) {
|
||||||
vAPI.domSurveyResults = {
|
vAPI.domSurveyScripts = {
|
||||||
busy: false,
|
busy: false,
|
||||||
hiddenElementCount: -1,
|
|
||||||
inlineScriptCount: -1,
|
inlineScriptCount: -1,
|
||||||
externalScriptCount: -1,
|
externalScriptCount: -1,
|
||||||
surveyTime: t0,
|
surveyTime: t0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const surveyResults = vAPI.domSurveyResults;
|
const surveyResults = vAPI.domSurveyScripts;
|
||||||
|
|
||||||
if ( surveyResults.busy ) { return; }
|
if ( surveyResults.busy ) { return; }
|
||||||
surveyResults.busy = true;
|
surveyResults.busy = true;
|
||||||
|
|
||||||
if ( surveyResults.surveyTime < vAPI.domMutationTime ) {
|
if ( surveyResults.surveyTime < vAPI.domMutationTime ) {
|
||||||
surveyResults.hiddenElementCount = -1;
|
|
||||||
surveyResults.inlineScriptCount = -1;
|
surveyResults.inlineScriptCount = -1;
|
||||||
surveyResults.externalScriptCount = -1;
|
surveyResults.externalScriptCount = -1;
|
||||||
}
|
}
|
||||||
|
@ -71,46 +68,6 @@
|
||||||
surveyResults.externalScriptCount = externalScriptCount;
|
surveyResults.externalScriptCount = externalScriptCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( surveyResults.hiddenElementCount === -1 ) {
|
|
||||||
surveyResults.hiddenElementCount = (( ) => {
|
|
||||||
if ( vAPI.domFilterer instanceof Object === false ) { return 0; }
|
|
||||||
const details = vAPI.domFilterer.getAllSelectors_(true);
|
|
||||||
if ( Array.isArray(details.declarative) === false ) { return 0; }
|
|
||||||
const selectors = details.declarative.map(entry => entry[0]);
|
|
||||||
const simple = [], complex = [];
|
|
||||||
for ( const selectorStr of selectors ) {
|
|
||||||
for ( const selector of selectorStr.split(',\n') ) {
|
|
||||||
if ( /[ +>~]/.test(selector) ) {
|
|
||||||
complex.push(selector);
|
|
||||||
} else {
|
|
||||||
simple.push(selector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const simpleStr = simple.join(',\n');
|
|
||||||
const complexStr = complex.join(',\n');
|
|
||||||
const nodeIter = document.createNodeIterator(
|
|
||||||
document.body,
|
|
||||||
NodeFilter.SHOW_ELEMENT
|
|
||||||
);
|
|
||||||
const matched = new Set();
|
|
||||||
for (;;) {
|
|
||||||
const node = nodeIter.nextNode();
|
|
||||||
if ( node === null ) { break; }
|
|
||||||
if ( node.offsetParent !== null ) { continue; }
|
|
||||||
if (
|
|
||||||
node.matches(simpleStr) === false &&
|
|
||||||
node.closest(complexStr) !== node
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
matched.add(node);
|
|
||||||
if ( matched.size === 99 ) { break; }
|
|
||||||
}
|
|
||||||
return matched.size;
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/756
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/756
|
||||||
// Keep trying to find inline script-like instances but only if we
|
// Keep trying to find inline script-like instances but only if we
|
||||||
// have the time-budget to do so.
|
// have the time-budget to do so.
|
||||||
|
@ -164,9 +121,9 @@
|
||||||
|
|
||||||
// IMPORTANT: This is returned to the injector, so this MUST be
|
// IMPORTANT: This is returned to the injector, so this MUST be
|
||||||
// the last statement.
|
// the last statement.
|
||||||
return {
|
let total = surveyResults.externalScriptCount;
|
||||||
hiddenElementCount: surveyResults.hiddenElementCount,
|
if ( surveyResults.inlineScriptCount !== -1 ) {
|
||||||
inlineScriptCount: surveyResults.inlineScriptCount,
|
total += surveyResults.inlineScriptCount;
|
||||||
externalScriptCount: surveyResults.externalScriptCount,
|
}
|
||||||
};
|
return total;
|
||||||
})();
|
})();
|
Loading…
Reference in New Issue