Report ruleset stats in popup panel

This commit is contained in:
Raymond Hill 2022-09-08 10:04:08 -04:00
parent 41d66a78ba
commit 1258414f37
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
7 changed files with 178 additions and 19 deletions

View File

@ -106,7 +106,8 @@ body.needSave #revertRules {
visibility: visible; visibility: visible;
} }
#hostname { #hostname {
margin: var(--popup-gap) var(--popup-gap-extra-thin); font-size: var(--font-size-larger);
margin: var(--popup-gap) var(--popup-gap-thin);
text-align: center; text-align: center;
} }
#hostname > span { #hostname > span {
@ -116,6 +117,18 @@ body.needSave #revertRules {
font-weight: 600; font-weight: 600;
} }
#rulesetStats {
padding: 0 var(--popup-gap-thin);
}
#rulesetStats > h1 {
font-size: 1em;
margin-bottom: var(--popup-gap-thin);
}
#rulesetStats > p {
font-size: var(--font-size-smaller);
margin: var(--popup-gap-thin) 0 var(--popup-gap) var(--popup-gap-thin);
}
.itemRibbon { .itemRibbon {
column-gap: var(--popup-gap); column-gap: var(--popup-gap);
display: grid; display: grid;
@ -170,6 +183,39 @@ body.mobile.no-tooltips .toolRibbon .tool {
margin-top: var(--default-gap); margin-top: var(--default-gap);
} }
#moreOrLess {
column-gap: 0;
display: grid;
grid-template: auto / 1fr 1fr;
justify-items: stretch;
margin: 1px 0 0 0;
}
#moreOrLess > span {
cursor: pointer;
margin: 0;
padding: var(--popup-gap-thin) var(--popup-gap);
user-select: none;
white-space: nowrap;
}
#moreButton .fa-icon {
transform: rotate(180deg);
}
#lessButton {
border-inline-start: 1px solid var(--surface-1);
text-align: end;
}
body[data-section="a"] #moreButton {
pointer-events: none;
visibility: hidden;
}
body[data-section=""] #lessButton {
pointer-events: none;
visibility: hidden;
}
body:not([data-section~="a"]) [data-section="a"] {
display: none;
}
/* configurable UI elements */ /* configurable UI elements */
:root:not(.mobile) .toolRibbon .caption, :root:not(.mobile) .toolRibbon .caption,
:root.mobile body.no-tooltips .toolRibbon .caption, :root.mobile body.no-tooltips .toolRibbon .caption,
@ -185,6 +231,9 @@ body.mobile.no-tooltips .toolRibbon .tool {
:root.mobile body[data-ui~="-captions"] .toolRibbon .tool { :root.mobile body[data-ui~="-captions"] .toolRibbon .tool {
padding: var(--popup-gap) var(--popup-gap-thin); padding: var(--popup-gap) var(--popup-gap-thin);
} }
:root.mobile #moreOrLess > span {
padding: var(--popup-gap);
}
/* horizontally-constrained viewport */ /* horizontally-constrained viewport */
:root.portrait body { :root.portrait body {

View File

@ -15,7 +15,7 @@ async function updateRegexRules() {
const toCheck = []; const toCheck = [];
for ( const details of rulesetDetails ) { for ( const details of rulesetDetails ) {
if ( details.enabled !== true ) { continue; } if ( details.enabled !== true ) { continue; }
for ( const rule of details.ruleDetails.regexes ) { for ( const rule of details.rules.regexes ) {
const regex = rule.condition.regexFilter; const regex = rule.condition.regexFilter;
const isCaseSensitive = rule.condition.isUrlFilterCaseSensitive === true; const isCaseSensitive = rule.condition.isUrlFilterCaseSensitive === true;
allRules.push(rule); allRules.push(rule);
@ -171,9 +171,18 @@ async function toggleTrustedSiteDirective(details) {
chrome.runtime.onMessage.addListener((request, sender, callback) => { chrome.runtime.onMessage.addListener((request, sender, callback) => {
switch ( request.what ) { switch ( request.what ) {
case 'matchesTrustedSiteDirective': case 'popupPanelData':
matchesTrustedSiteDirective(request).then(response => { matchesTrustedSiteDirective(request).then(response => {
callback(response); callback({
isTrusted: response,
rulesetDetails: rulesetDetails.filter(details =>
details.enabled
).map(details => ({
name: details.name,
filterCount: details.filters.accepted,
ruleCount: details.rules.accepted,
})),
});
}); });
return true; return true;
case 'toggleTrustedSiteDirective': case 'toggleTrustedSiteDirective':

View File

@ -28,6 +28,26 @@ let originalTrustedState = false;
/******************************************************************************/ /******************************************************************************/
class safeLocalStorage {
static getItem(k) {
try {
return self.localStorage.getItem(k);
}
catch(ex) {
}
return null;
}
static setItem(k, v) {
try {
self.localStorage.setItem(k, v);
}
catch(ex) {
}
}
}
/******************************************************************************/
async function toggleTrustedSiteDirective() { async function toggleTrustedSiteDirective() {
let url; let url;
try { try {
@ -42,7 +62,9 @@ async function toggleTrustedSiteDirective() {
origin: url.origin, origin: url.origin,
state: targetTrustedState, state: targetTrustedState,
tabId: currentTab.id, tabId: currentTab.id,
}).catch(( ) => targetTrustedState === false); }).catch(( ) =>
targetTrustedState === false
);
document.body.classList.toggle('off', newTrustedState === true); document.body.classList.toggle('off', newTrustedState === true);
document.body.classList.toggle( document.body.classList.toggle(
'needReload', 'needReload',
@ -73,17 +95,18 @@ async function init() {
} catch(ex) { } catch(ex) {
} }
let popupPanelData;
if ( url !== undefined ) { if ( url !== undefined ) {
originalTrustedState = await chrome.runtime.sendMessage({ popupPanelData = await chrome.runtime.sendMessage({
what: 'matchesTrustedSiteDirective', what: 'popupPanelData',
origin: url.origin, origin: url.origin,
}) === true; });
originalTrustedState = popupPanelData.isTrusted === true;
} }
const body = document.body; const body = document.body;
body.classList.toggle('off', originalTrustedState); body.classList.toggle('off', originalTrustedState);
const elemHn = document.querySelector('#hostname'); const elemHn = document.querySelector('#hostname');
elemHn.textContent = url && url.hostname || ''; elemHn.textContent = url && url.hostname || '';
document.querySelector('#switch').addEventListener( document.querySelector('#switch').addEventListener(
@ -96,6 +119,18 @@ async function init() {
reloadTab reloadTab
); );
if ( popupPanelData ) {
const parent = document.querySelector('#rulesetStats');
for ( const details of popupPanelData.rulesetDetails ) {
const h1 = document.createElement('h1');
h1.textContent = details.name;
parent.append(h1);
const p = document.createElement('p');
p.textContent = `${details.ruleCount.toLocaleString()} rules, converted from ${details.filterCount.toLocaleString()} network filters`;
parent.append(p);
}
}
document.body.classList.remove('loading'); document.body.classList.remove('loading');
return true; return true;
@ -112,3 +147,62 @@ async function tryInit() {
tryInit(); tryInit();
/******************************************************************************/ /******************************************************************************/
// The popup panel is made of sections. Visibility of sections can be
// toggled on/off.
const maxNumberOfSections = 1;
const sectionBitsFromAttribute = function() {
const attr = document.body.dataset.section;
if ( attr === '' ) { return 0; }
let bits = 0;
for ( const c of attr.split(' ') ) {
bits |= 1 << (c.charCodeAt(0) - 97);
}
return bits;
};
const sectionBitsToAttribute = function(bits) {
if ( typeof bits !== 'number' ) { return; }
if ( isNaN(bits) ) { return; }
const attr = [];
for ( let i = 0; i < maxNumberOfSections; i++ ) {
const bit = 1 << i;
if ( (bits & bit) === 0 ) { continue; }
attr.push(String.fromCharCode(97 + i));
}
document.body.dataset.section = attr.join(' ');
};
async function toggleSections(more) {
let currentBits = sectionBitsFromAttribute();
let newBits = currentBits;
for ( let i = 0; i < maxNumberOfSections; i++ ) {
const bit = 1 << (more ? i : maxNumberOfSections - i - 1);
if ( more ) {
newBits |= bit;
} else {
newBits &= ~bit;
}
if ( newBits !== currentBits ) { break; }
}
if ( newBits === currentBits ) { return; }
sectionBitsToAttribute(newBits);
safeLocalStorage.setItem('popupPanelSections', newBits);
}
sectionBitsToAttribute(
parseInt(safeLocalStorage.getItem('popupPanelSections'), 10)
);
document.querySelector('#moreButton').addEventListener('click', ( ) => {
toggleSections(true);
});
document.querySelector('#lessButton').addEventListener('click', ( ) => {
toggleSections(false);
});
/******************************************************************************/

View File

@ -11,7 +11,7 @@
<title data-i18n="extName"></title> <title data-i18n="extName"></title>
</head> </head>
<body class="loading"> <body class="loading" data-section="">
<div id="main"> <div id="main">
<div id="sticky"> <div id="sticky">
<div id="stickyTools"> <div id="stickyTools">
@ -40,12 +40,17 @@
</div> </div>
<div id="hostname"><span></span>&shy;<span></span></div> <div id="hostname"><span></span>&shy;<span></span></div>
</div> </div>
<div id="basicTools" class="toolRibbon"> <hr data-section="a">
<span></span> <div id="rulesetStats" data-section="a">
<span></span> </div>
<span></span> <hr>
<span></span> <div id="moreOrLess" class="">
<a href="dashboard.html" class="fa-icon tool" target="uBODashboard" tabindex="0" data-i18n-title="popupTipDashboard">cogs<span class="caption" data-i18n="popupTipDashboard"></span></a> <span id="moreButton">
<span>More</span>&emsp;<span class="fa-icon">angle-up</span>
</span>
<span id="lessButton">
<span class="fa-icon">angle-up</span>&emsp;<span>Less</span>
</span>
</div> </div>
</div> </div>

View File

@ -204,13 +204,14 @@ async function main() {
rulesetDetails.push({ rulesetDetails.push({
id: ruleset.id, id: ruleset.id,
name: ruleset.name,
enabled: ruleset.enabled, enabled: ruleset.enabled,
filterDetails: { filters: {
total: details.filterCount, total: details.filterCount,
accepted: details.acceptedFilterCount, accepted: details.acceptedFilterCount,
rejected: details.rejectedFilterCount, rejected: details.rejectedFilterCount,
}, },
ruleDetails: { rules: {
total: rules.length, total: rules.length,
accepted: good.length, accepted: good.length,
discarded: redirects.length + headers.length + removeparams.length, discarded: redirects.length + headers.length + removeparams.length,

View File

@ -24,7 +24,7 @@
export default [ export default [
{ {
id: 'default', id: 'default',
name: 'Default ruleset', name: 'Default: Ads and trackers',
enabled: true, enabled: true,
paths: [ paths: [
], ],

View File

@ -128,6 +128,7 @@
:root { :root {
--font-size: 14px; --font-size: 14px;
--font-size-smaller: 13px; --font-size-smaller: 13px;
--font-size-larger: 15px;
--font-family: Inter, sans-serif; --font-family: Inter, sans-serif;
--monospace-size: 12px; --monospace-size: 12px;
} }