From 1258414f3765ef341cb1b4992e84e7d697546fbb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 8 Sep 2022 10:04:08 -0400 Subject: [PATCH] Report ruleset stats in popup panel --- platform/mv3/extension/css/popup.css | 51 +++++++++++- platform/mv3/extension/js/background.js | 15 +++- platform/mv3/extension/js/popup.js | 104 ++++++++++++++++++++++-- platform/mv3/extension/popup.html | 19 +++-- platform/mv3/make-rulesets.js | 5 +- platform/mv3/ruleset-config.js | 2 +- src/css/themes/default.css | 1 + 7 files changed, 178 insertions(+), 19 deletions(-) diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index fc3eb2d2d..5d72ff246 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -106,7 +106,8 @@ body.needSave #revertRules { visibility: visible; } #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; } #hostname > span { @@ -116,6 +117,18 @@ body.needSave #revertRules { 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 { column-gap: var(--popup-gap); display: grid; @@ -170,6 +183,39 @@ body.mobile.no-tooltips .toolRibbon .tool { 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 */ :root:not(.mobile) .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 { padding: var(--popup-gap) var(--popup-gap-thin); } +:root.mobile #moreOrLess > span { + padding: var(--popup-gap); + } /* horizontally-constrained viewport */ :root.portrait body { diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index d90d451ed..2d3c1e904 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -15,7 +15,7 @@ async function updateRegexRules() { const toCheck = []; for ( const details of rulesetDetails ) { 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 isCaseSensitive = rule.condition.isUrlFilterCaseSensitive === true; allRules.push(rule); @@ -171,9 +171,18 @@ async function toggleTrustedSiteDirective(details) { chrome.runtime.onMessage.addListener((request, sender, callback) => { switch ( request.what ) { - case 'matchesTrustedSiteDirective': + case 'popupPanelData': 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; case 'toggleTrustedSiteDirective': diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index a252cf7d2..167b7fe5f 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -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() { let url; try { @@ -42,7 +62,9 @@ async function toggleTrustedSiteDirective() { origin: url.origin, state: targetTrustedState, tabId: currentTab.id, - }).catch(( ) => targetTrustedState === false); + }).catch(( ) => + targetTrustedState === false + ); document.body.classList.toggle('off', newTrustedState === true); document.body.classList.toggle( 'needReload', @@ -73,17 +95,18 @@ async function init() { } catch(ex) { } + let popupPanelData; if ( url !== undefined ) { - originalTrustedState = await chrome.runtime.sendMessage({ - what: 'matchesTrustedSiteDirective', + popupPanelData = await chrome.runtime.sendMessage({ + what: 'popupPanelData', origin: url.origin, - }) === true; + }); + originalTrustedState = popupPanelData.isTrusted === true; } const body = document.body; body.classList.toggle('off', originalTrustedState); const elemHn = document.querySelector('#hostname'); - elemHn.textContent = url && url.hostname || ''; document.querySelector('#switch').addEventListener( @@ -96,6 +119,18 @@ async function init() { 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'); return true; @@ -112,3 +147,62 @@ async function 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); +}); + +/******************************************************************************/ + diff --git a/platform/mv3/extension/popup.html b/platform/mv3/extension/popup.html index d000a4e20..4acf0cf99 100644 --- a/platform/mv3/extension/popup.html +++ b/platform/mv3/extension/popup.html @@ -11,7 +11,7 @@ - +
@@ -40,12 +40,17 @@
­
-
- - - - - cogs +
+
+
+
+
+ + Moreangle-up + + + angle-upLess +
diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index e45df7663..691d21dde 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -204,13 +204,14 @@ async function main() { rulesetDetails.push({ id: ruleset.id, + name: ruleset.name, enabled: ruleset.enabled, - filterDetails: { + filters: { total: details.filterCount, accepted: details.acceptedFilterCount, rejected: details.rejectedFilterCount, }, - ruleDetails: { + rules: { total: rules.length, accepted: good.length, discarded: redirects.length + headers.length + removeparams.length, diff --git a/platform/mv3/ruleset-config.js b/platform/mv3/ruleset-config.js index 6912718b8..951e3cc45 100644 --- a/platform/mv3/ruleset-config.js +++ b/platform/mv3/ruleset-config.js @@ -24,7 +24,7 @@ export default [ { id: 'default', - name: 'Default ruleset', + name: 'Default: Ads and trackers', enabled: true, paths: [ ], diff --git a/src/css/themes/default.css b/src/css/themes/default.css index f2443b78b..1fb898686 100644 --- a/src/css/themes/default.css +++ b/src/css/themes/default.css @@ -128,6 +128,7 @@ :root { --font-size: 14px; --font-size-smaller: 13px; + --font-size-larger: 15px; --font-family: Inter, sans-serif; --monospace-size: 12px; }