diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index 65312d476..6725ba125 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -143,6 +143,10 @@ "message": "Advanced network filtering plus specific and generic extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.\n\nGeneric extended filtering may cause higher webpage resources usage.", "description": "This describes the 'complete' filtering mode" }, + "noFilteringModeDescription": { + "message": "List of hostnames for which no filtering will take place", + "description": "A short description for the editable field which lists trusted sites" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index eb5c6db10..f03e276e9 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -64,6 +64,16 @@ p { width: calc(240px / 2); } +h3[data-i18n="filteringMode0Name"]::first-letter { + text-transform: capitalize; + } +#trustedSites { + box-sizing: border-box; + height: 6rem; + resize: vertical; + width: 100%; + } + #lists { margin: 0.5em 0 0 0; padding: 0; diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index b310572b0..698e1fb3c 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -12,7 +12,6 @@ - @@ -20,7 +19,7 @@
-
@@ -83,6 +82,13 @@ +
+

+

_

+

+

+
+

diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index cd6d73c1d..4acb6ba46 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -51,6 +51,8 @@ import { setFilteringMode, getDefaultFilteringMode, setDefaultFilteringMode, + getTrustedSites, + setTrustedSites, syncWithBrowserPermissions, } from './mode-manager.js'; @@ -151,8 +153,7 @@ function onMessage(request, sender, callback) { }).catch(reason => { console.log(reason); }); - callback(); - return; + return false; } default: @@ -181,16 +182,19 @@ function onMessage(request, sender, callback) { case 'getOptionsPageData': { Promise.all([ getDefaultFilteringMode(), + getTrustedSites(), getRulesetDetails(), dnr.getEnabledRulesets(), ]).then(results => { const [ defaultFilteringMode, + trustedSites, rulesetDetails, enabledRulesets, ] = results; callback({ defaultFilteringMode, + trustedSites: Array.from(trustedSites), enabledRulesets, maxNumberOfEnabledRulesets: dnr.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS, rulesetDetails: Array.from(rulesetDetails.values()), @@ -262,6 +266,21 @@ function onMessage(request, sender, callback) { return true; } + case 'setTrustedSites': + setTrustedSites(request.hostnames).then(( ) => { + registerInjectables(); + return Promise.all([ + getDefaultFilteringMode(), + getTrustedSites(), + ]); + }).then(results => { + callback({ + defaultFilteringMode: results[0], + trustedSites: Array.from(results[1]), + }); + }); + return true; + default: break; } diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index b69bfd55e..de7a55515 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -354,6 +354,35 @@ export function setDefaultFilteringMode(afterLevel) { /******************************************************************************/ +export async function getTrustedSites() { + const filteringModes = await getFilteringModeDetails(); + return filteringModes.none; +} + +export async function setTrustedSites(hostnames) { + const filteringModes = await getFilteringModeDetails(); + const { none } = filteringModes; + const hnSet = new Set(hostnames); + let modified = false; + for ( const hn of none ) { + if ( hnSet.has(hn) ) { + hnSet.delete(hn); + } else { + none.delete(hn); + modified = true; + } + } + for ( const hn of hnSet ) { + const level = applyFilteringMode(filteringModes, hn, MODE_NONE); + if ( level !== MODE_NONE ) { continue; } + modified = true; + } + if ( modified === false ) { return; } + return writeFilteringModeDetails(filteringModes); +} + +/******************************************************************************/ + export async function syncWithBrowserPermissions() { const [ permissions, beforeMode ] = await Promise.all([ browser.permissions.getAll(), diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 3afb1f79b..3aaa158e1 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -24,6 +24,7 @@ import { browser, sendMessage, localRead, localWrite } from './ext.js'; import { i18n$, i18n } from './i18n.js'; import { dom, qs$, qsa$ } from './dom.js'; +import punycode from './punycode.js'; /******************************************************************************/ @@ -204,8 +205,6 @@ function renderFilterLists(soft = false) { } dom.remove('#lists .listEntries .listEntry.discard'); - - renderWidgets(); } /******************************************************************************/ @@ -216,7 +215,13 @@ const renderWidgets = function() { } const defaultLevel = cachedRulesetData.defaultFilteringMode; - qs$(`.filteringModeCard input[type="radio"][value="${defaultLevel}"]`).checked = true; + if ( defaultLevel !== 0 ) { + qs$(`.filteringModeCard input[type="radio"][value="${defaultLevel}"]`).checked = true; + } else { + dom.prop('.filteringModeCard input[type="radio"]', 'checked', false); + } + + renderTrustedSites(cachedRulesetData.trustedSites); qs$('#autoReload input[type="checkbox"').checked = cachedRulesetData.autoReload; @@ -295,6 +300,45 @@ dom.on('#autoReload input[type="checkbox"', 'change', ev => { /******************************************************************************/ +let trustedSitesHash = ''; + +function renderTrustedSites(hostnames) { + const textarea = qs$('#trustedSites'); + trustedSitesHash = hostnames.sort().join('\n'); + textarea.value = hostnames.map(hn => punycode.toUnicode(hn)).join('\n'); + if ( textarea.value !== '' ) { + textarea.value += '\n'; + } +} + +function changeTrustedSites() { + const textarea = qs$('#trustedSites'); + const hostnames = textarea.value.split(/\s/).map(hn => { + try { + return punycode.toASCII( + (new URL(`https://${hn}/`)).hostname + ); + } catch(_) { + } + return ''; + }).filter(hn => hn !== '').sort(); + if ( hostnames.join('\n') === trustedSitesHash ) { return; } + sendMessage({ + what: 'setTrustedSites', + hostnames, + }).then(response => { + cachedRulesetData.defaultFilteringMode = response.defaultFilteringMode; + cachedRulesetData.trustedSites = response.trustedSites; + renderWidgets(); + }); +} + +dom.on('#trustedSites', 'blur', changeTrustedSites); + +self.addEventListener('beforeunload', changeTrustedSites); + +/******************************************************************************/ + async function applyEnabledRulesets() { const enabledRulesets = []; for ( const liEntry of qsa$('#lists .listEntry[data-listkey]') ) { @@ -385,6 +429,7 @@ sendMessage({ cachedRulesetData.rulesetDetails.forEach(rule => rulesetMap.set(rule.id, rule)); try { renderFilterLists(); + renderWidgets(); } catch(ex) { } }).catch(reason => {