[mv3] Improve and cleanup options page

Additionally, ensure the options page stay in sync with the
internal state of the extension.
This commit is contained in:
Raymond Hill 2023-11-29 16:11:22 -05:00
parent 554400f7bd
commit a77c23c74f
No known key found for this signature in database
GPG key ID: 25E1490B761470C2
4 changed files with 116 additions and 46 deletions

View file

@ -57,6 +57,7 @@ import {
} from './mode-manager.js'; } from './mode-manager.js';
import { import {
broadcastMessage,
ubolLog, ubolLog,
} from './utils.js'; } from './utils.js';
@ -65,7 +66,7 @@ import {
const rulesetConfig = { const rulesetConfig = {
version: '', version: '',
enabledRulesets: [ 'default' ], enabledRulesets: [ 'default' ],
autoReload: 1, autoReload: true,
}; };
const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, ''); const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, '');
@ -84,7 +85,7 @@ async function loadRulesetConfig() {
if ( data ) { if ( data ) {
rulesetConfig.version = data.version; rulesetConfig.version = data.version;
rulesetConfig.enabledRulesets = data.enabledRulesets; rulesetConfig.enabledRulesets = data.enabledRulesets;
rulesetConfig.autoReload = data.autoReload; rulesetConfig.autoReload = data.autoReload && true || false;
wakeupRun = true; wakeupRun = true;
return; return;
} }
@ -92,7 +93,7 @@ async function loadRulesetConfig() {
if ( data ) { if ( data ) {
rulesetConfig.version = data.version; rulesetConfig.version = data.version;
rulesetConfig.enabledRulesets = data.enabledRulesets; rulesetConfig.enabledRulesets = data.enabledRulesets;
rulesetConfig.autoReload = data.autoReload; rulesetConfig.autoReload = data.autoReload && true || false;
sessionWrite('rulesetConfig', rulesetConfig); sessionWrite('rulesetConfig', rulesetConfig);
return; return;
} }
@ -175,6 +176,7 @@ function onMessage(request, sender, callback) {
}).then(( ) => { }).then(( ) => {
registerInjectables(); registerInjectables();
callback(); callback();
broadcastMessage({ enabledRulesets: rulesetConfig.enabledRulesets });
}); });
return true; return true;
} }
@ -198,7 +200,7 @@ function onMessage(request, sender, callback) {
enabledRulesets, enabledRulesets,
maxNumberOfEnabledRulesets: dnr.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS, maxNumberOfEnabledRulesets: dnr.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS,
rulesetDetails: Array.from(rulesetDetails.values()), rulesetDetails: Array.from(rulesetDetails.values()),
autoReload: rulesetConfig.autoReload === 1, autoReload: rulesetConfig.autoReload,
firstRun, firstRun,
}); });
firstRun = false; firstRun = false;
@ -207,9 +209,10 @@ function onMessage(request, sender, callback) {
} }
case 'setAutoReload': case 'setAutoReload':
rulesetConfig.autoReload = request.state ? 1 : 0; rulesetConfig.autoReload = request.state && true || false;
saveRulesetConfig().then(( ) => { saveRulesetConfig().then(( ) => {
callback(); callback();
broadcastMessage({ autoReload: rulesetConfig.autoReload });
}); });
return true; return true;
@ -222,7 +225,7 @@ function onMessage(request, sender, callback) {
]).then(results => { ]).then(results => {
callback({ callback({
level: results[0], level: results[0],
autoReload: rulesetConfig.autoReload === 1, autoReload: rulesetConfig.autoReload,
hasOmnipotence: results[1], hasOmnipotence: results[1],
hasGreatPowers: results[2], hasGreatPowers: results[2],
rulesetDetails: results[3], rulesetDetails: results[3],

View file

@ -34,6 +34,7 @@ import {
} from './ext.js'; } from './ext.js';
import { import {
broadcastMessage,
hostnamesFromMatches, hostnamesFromMatches,
isDescendantHostnameOfIter, isDescendantHostnameOfIter,
toBroaderHostname, toBroaderHostname,
@ -271,6 +272,16 @@ async function writeFilteringModeDetails(afterDetails) {
localWrite('filteringModeDetails', data); localWrite('filteringModeDetails', data);
sessionWrite('filteringModeDetails', data); sessionWrite('filteringModeDetails', data);
readFilteringModeDetails.cache = unserializeModeDetails(data); readFilteringModeDetails.cache = unserializeModeDetails(data);
Promise.all([
getDefaultFilteringMode(),
getTrustedSites(),
]).then(results => {
broadcastMessage({
defaultFilteringMode: results[0],
trustedSites: Array.from(results[1]),
});
});
} }
/******************************************************************************/ /******************************************************************************/

View file

@ -38,6 +38,10 @@ function renderNumber(value) {
return value.toLocaleString(); return value.toLocaleString();
} }
function hashFromIterable(iter) {
return Array.from(iter).sort().join('\n');
}
/******************************************************************************/ /******************************************************************************/
function rulesetStats(rulesetId) { function rulesetStats(rulesetId) {
@ -55,7 +59,7 @@ function rulesetStats(rulesetId) {
/******************************************************************************/ /******************************************************************************/
function renderFilterLists(soft = false) { function renderFilterLists() {
const { enabledRulesets, rulesetDetails } = cachedRulesetData; const { enabledRulesets, rulesetDetails } = cachedRulesetData;
const listGroupTemplate = qs$('#templates .groupEntry'); const listGroupTemplate = qs$('#templates .groupEntry');
const listEntryTemplate = qs$('#templates .listEntry'); const listEntryTemplate = qs$('#templates .listEntry');
@ -68,9 +72,10 @@ function renderFilterLists(soft = false) {
} }
const on = enabledRulesets.includes(ruleset.id); const on = enabledRulesets.includes(ruleset.id);
dom.cl.toggle(li, 'checked', on); dom.cl.toggle(li, 'checked', on);
dom.cl.toggle(li, 'unused', hideUnused && !on);
qs$(li, 'input[type="checkbox"]').checked = on;
if ( dom.attr(li, 'data-listkey') !== ruleset.id ) { if ( dom.attr(li, 'data-listkey') !== ruleset.id ) {
dom.attr(li, 'data-listkey', ruleset.id); dom.attr(li, 'data-listkey', ruleset.id);
qs$(li, 'input[type="checkbox"]').checked = on;
qs$(li, '.listname').append(i18n.patchUnicodeFlags(ruleset.name)); qs$(li, '.listname').append(i18n.patchUnicodeFlags(ruleset.name));
dom.cl.remove(li, 'toRemove'); dom.cl.remove(li, 'toRemove');
if ( ruleset.homeURL ) { if ( ruleset.homeURL ) {
@ -85,12 +90,7 @@ function renderFilterLists(soft = false) {
} else { } else {
dom.cl.remove(li, 'mustread'); dom.cl.remove(li, 'mustread');
} }
dom.cl.toggle(li, 'isDefault', ruleset.isDefault === true); dom.cl.toggle(li, 'isDefault', ruleset.id === 'default');
dom.cl.toggle(li, 'unused', hideUnused && !on);
}
// https://github.com/gorhill/uBlock/issues/1429
if ( soft !== true ) {
qs$(li, 'input[type="checkbox"]').checked = on;
} }
const stats = rulesetStats(ruleset.id); const stats = rulesetStats(ruleset.id);
li.title = listStatsTemplate li.title = listStatsTemplate
@ -159,10 +159,6 @@ function renderFilterLists(soft = false) {
return liGroup; return liGroup;
}; };
// Incremental rendering: this will allow us to easily discard unused
// DOM list entries.
dom.cl.add('#lists .listEntries .listEntry[data-listkey]', 'discard');
// Visually split the filter lists in groups // Visually split the filter lists in groups
const ulLists = qs$('#lists'); const ulLists = qs$('#lists');
const groups = new Map([ const groups = new Map([
@ -203,25 +199,17 @@ function renderFilterLists(soft = false) {
ulLists.appendChild(liGroup); ulLists.appendChild(liGroup);
} }
} }
dom.remove('#lists .listEntries .listEntry.discard');
} }
/******************************************************************************/ /******************************************************************************/
const renderWidgets = function() { function renderWidgets() {
if ( cachedRulesetData.firstRun ) { if ( cachedRulesetData.firstRun ) {
dom.cl.add(dom.body, 'firstRun'); dom.cl.add(dom.body, 'firstRun');
} }
const defaultLevel = cachedRulesetData.defaultFilteringMode; renderDefaultMode();
if ( defaultLevel !== 0 ) { renderTrustedSites();
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; qs$('#autoReload input[type="checkbox"').checked = cachedRulesetData.autoReload;
@ -245,7 +233,18 @@ const renderWidgets = function() {
dom.cl.toggle(dom.body, 'noMoreRuleset', dom.cl.toggle(dom.body, 'noMoreRuleset',
rulesetCount === cachedRulesetData.maxNumberOfEnabledRulesets rulesetCount === cachedRulesetData.maxNumberOfEnabledRulesets
); );
}; }
/******************************************************************************/
function renderDefaultMode() {
const defaultLevel = cachedRulesetData.defaultFilteringMode;
if ( defaultLevel !== 0 ) {
qs$(`.filteringModeCard input[type="radio"][value="${defaultLevel}"]`).checked = true;
} else {
dom.prop('.filteringModeCard input[type="radio"]', 'checked', false);
}
}
/******************************************************************************/ /******************************************************************************/
@ -278,7 +277,7 @@ async function onFilteringModeChange(ev) {
default: default:
break; break;
} }
renderFilterLists(true); renderFilterLists();
renderWidgets(); renderWidgets();
} }
@ -300,11 +299,9 @@ dom.on('#autoReload input[type="checkbox"', 'change', ev => {
/******************************************************************************/ /******************************************************************************/
let trustedSitesHash = ''; function renderTrustedSites() {
function renderTrustedSites(hostnames) {
const textarea = qs$('#trustedSites'); const textarea = qs$('#trustedSites');
trustedSitesHash = hostnames.sort().join('\n'); const hostnames = cachedRulesetData.trustedSites;
textarea.value = hostnames.map(hn => punycode.toUnicode(hn)).join('\n'); textarea.value = hostnames.map(hn => punycode.toUnicode(hn)).join('\n');
if ( textarea.value !== '' ) { if ( textarea.value !== '' ) {
textarea.value += '\n'; textarea.value += '\n';
@ -312,8 +309,18 @@ function renderTrustedSites(hostnames) {
} }
function changeTrustedSites() { function changeTrustedSites() {
const hostnames = getStagedTrustedSites();
const hash = hashFromIterable(cachedRulesetData.trustedSites);
if ( hashFromIterable(hostnames) === hash ) { return; }
sendMessage({
what: 'setTrustedSites',
hostnames,
});
}
function getStagedTrustedSites() {
const textarea = qs$('#trustedSites'); const textarea = qs$('#trustedSites');
const hostnames = textarea.value.split(/\s/).map(hn => { return textarea.value.split(/\s/).map(hn => {
try { try {
return punycode.toASCII( return punycode.toASCII(
(new URL(`https://${hn}/`)).hostname (new URL(`https://${hn}/`)).hostname
@ -321,16 +328,7 @@ function changeTrustedSites() {
} catch(_) { } catch(_) {
} }
return ''; return '';
}).filter(hn => hn !== '').sort(); }).filter(hn => hn !== '');
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); dom.on('#trustedSites', 'blur', changeTrustedSites);
@ -420,6 +418,57 @@ localRead('hideUnusedFilterLists').then(value => {
/******************************************************************************/ /******************************************************************************/
const bc = new self.BroadcastChannel('uBOL');
bc.onmessage = ev => {
const message = ev.data;
if ( message instanceof Object === false ) { return; }
const local = cachedRulesetData;
let render = false;
// Keep added sites which have not yet been committed
if ( message.trustedSites !== undefined ) {
if ( hashFromIterable(message.trustedSites) !== hashFromIterable(local.trustedSites) ) {
const current = new Set(local.trustedSites);
const staged = new Set(getStagedTrustedSites());
for ( const hn of staged ) {
if ( current.has(hn) === false ) { continue; }
staged.delete(hn);
}
const combined = Array.from(new Set([ ...message.trustedSites, ...staged ]));
local.trustedSites = combined;
render = true;
}
}
if ( message.defaultFilteringMode !== undefined ) {
if ( message.defaultFilteringMode !== local.defaultFilteringMode ) {
local.defaultFilteringMode = message.defaultFilteringMode;
render = true;
}
}
if ( message.autoReload !== undefined ) {
if ( message.autoReload !== local.autoReload ) {
local.autoReload = message.autoReload;
render = true;
}
}
if ( message.enabledRulesets !== undefined ) {
if ( hashFromIterable(message.enabledRulesets) !== hashFromIterable(local.enabledRulesets) ) {
local.enabledRulesets = message.enabledRulesets;
render = true;
}
}
if ( render === false ) { return; }
renderFilterLists();
renderWidgets();
};
/******************************************************************************/
sendMessage({ sendMessage({
what: 'getOptionsPageData', what: 'getOptionsPageData',
}).then(data => { }).then(data => {

View file

@ -123,6 +123,13 @@ const hostnamesFromMatches = origins => {
/******************************************************************************/ /******************************************************************************/
export const broadcastMessage = message => {
const bc = new self.BroadcastChannel('uBOL');
bc.postMessage(message);
};
/******************************************************************************/
const ubolLog = (...args) => { const ubolLog = (...args) => {
// Do not pollute dev console in stable release. // Do not pollute dev console in stable release.
if ( browser.runtime.id === 'ddkjiahejlhfcafbddmgiahcphecmpfh' ) { return; } if ( browser.runtime.id === 'ddkjiahejlhfcafbddmgiahcphecmpfh' ) { return; }