use exceptionList for blacklist for clean migration

This commit is contained in:
2026-02-11 13:34:23 +03:00
parent 226f2efe4f
commit 289fdd9257
6 changed files with 83 additions and 22 deletions

View File

@@ -4,6 +4,7 @@
"description": "__MSG_extension_description__", "description": "__MSG_extension_description__",
"version": "1.12.1", "version": "1.12.1",
"default_locale": "en", "default_locale": "en",
"id": "kdoehicejfnccbmecpkfjlbljpfogoep",
"permissions": [ "permissions": [
"scripting", "scripting",
"storage", "storage",

View File

@@ -209,5 +209,6 @@
"github.com", "github.com",
"www.wikipedia.org" "www.wikipedia.org"
], ],
"exceptionsWhiteList": [],
"exceptionsMode": "blacklist" "exceptionsMode": "blacklist"
} }

View File

@@ -38,16 +38,16 @@ class BackgroundService {
private setupInstallListener(): void { private setupInstallListener(): void {
chrome.runtime.onInstalled.addListener(async (): Promise<void> => { chrome.runtime.onInstalled.addListener(async (): Promise<void> => {
const data = await StorageService.get(['exceptionsList', 'exceptionsMode']); const data = await StorageService.get(['exceptionsList', 'exceptionsWhiteList', 'exceptionsMode']);
const updates: { exceptionsList?: string[]; exceptionsMode?: 'blacklist' | 'whitelist' } = {}; const updates: Record<string, unknown> = {};
if (!data.exceptionsList) { if (data.exceptionsWhiteList === undefined) {
updates.exceptionsList = []; updates.exceptionsWhiteList = [];
} }
if (data.exceptionsMode !== 'blacklist' && data.exceptionsMode !== 'whitelist') { if (data.exceptionsMode !== 'blacklist' && data.exceptionsMode !== 'whitelist') {
updates.exceptionsMode = 'blacklist'; updates.exceptionsMode = 'blacklist';
} }
if (Object.keys(updates).length > 0) { if (Object.keys(updates).length > 0) {
await StorageService.set(updates); await chrome.storage.local.set(updates);
} }
}); });
} }

View File

@@ -7,6 +7,7 @@ export class ContentScript {
private lists: HighlightList[] = []; private lists: HighlightList[] = [];
private isGlobalHighlightEnabled = true; private isGlobalHighlightEnabled = true;
private exceptionsList: string[] = []; private exceptionsList: string[] = [];
private exceptionsWhiteList: string[] = [];
private exceptionsMode: ExceptionsMode = 'blacklist'; private exceptionsMode: ExceptionsMode = 'blacklist';
private shouldSkipDueToExceptions = false; private shouldSkipDueToExceptions = false;
private matchCase = false; private matchCase = false;
@@ -32,6 +33,7 @@ export class ContentScript {
'matchCaseEnabled', 'matchCaseEnabled',
'matchWholeEnabled', 'matchWholeEnabled',
'exceptionsList', 'exceptionsList',
'exceptionsWhiteList',
'exceptionsMode' 'exceptionsMode'
]); ]);
@@ -40,13 +42,19 @@ export class ContentScript {
this.matchCase = data.matchCaseEnabled ?? false; this.matchCase = data.matchCaseEnabled ?? false;
this.matchWhole = data.matchWholeEnabled ?? false; this.matchWhole = data.matchWholeEnabled ?? false;
this.exceptionsList = data.exceptionsList || []; this.exceptionsList = data.exceptionsList || [];
this.exceptionsWhiteList = data.exceptionsWhiteList || [];
this.exceptionsMode = data.exceptionsMode === 'whitelist' ? 'whitelist' : 'blacklist'; this.exceptionsMode = data.exceptionsMode === 'whitelist' ? 'whitelist' : 'blacklist';
this.shouldSkipDueToExceptions = this.computeShouldSkipDueToExceptions(); this.shouldSkipDueToExceptions = this.computeShouldSkipDueToExceptions();
} }
private getCurrentExceptionsList(): string[] {
return this.exceptionsMode === 'whitelist' ? this.exceptionsWhiteList : this.exceptionsList;
}
private computeShouldSkipDueToExceptions(): boolean { private computeShouldSkipDueToExceptions(): boolean {
const currentHostname = window.location.hostname; const currentHostname = window.location.hostname;
const isInList = this.exceptionsList.includes(currentHostname); const list = this.getCurrentExceptionsList();
const isInList = list.includes(currentHostname);
if (this.exceptionsMode === 'blacklist') { if (this.exceptionsMode === 'blacklist') {
return isInList; return isInList;
} }
@@ -98,8 +106,9 @@ export class ContentScript {
} }
private async handleExceptionsUpdate(): Promise<void> { private async handleExceptionsUpdate(): Promise<void> {
const data = await StorageService.get(['exceptionsList', 'exceptionsMode']); const data = await StorageService.get(['exceptionsList', 'exceptionsWhiteList', 'exceptionsMode']);
this.exceptionsList = data.exceptionsList || []; this.exceptionsList = data.exceptionsList || [];
this.exceptionsWhiteList = data.exceptionsWhiteList || [];
this.exceptionsMode = data.exceptionsMode === 'whitelist' ? 'whitelist' : 'blacklist'; this.exceptionsMode = data.exceptionsMode === 'whitelist' ? 'whitelist' : 'blacklist';
this.shouldSkipDueToExceptions = this.computeShouldSkipDueToExceptions(); this.shouldSkipDueToExceptions = this.computeShouldSkipDueToExceptions();
this.processHighlights(); this.processHighlights();

View File

@@ -15,7 +15,13 @@ export class PopupController {
private matchCaseEnabled = false; private matchCaseEnabled = false;
private matchWholeEnabled = false; private matchWholeEnabled = false;
private exceptionsList: string[] = []; private exceptionsList: string[] = [];
private exceptionsWhiteList: string[] = [];
private exceptionsMode: ExceptionsMode = 'blacklist'; private exceptionsMode: ExceptionsMode = 'blacklist';
private getCurrentExceptionsList(): string[] {
return this.exceptionsMode === 'whitelist' ? this.exceptionsWhiteList : this.exceptionsList;
}
private currentTabHost = ''; private currentTabHost = '';
private activeTab = 'lists'; private activeTab = 'lists';
private pageHighlights: Array<{ word: string; count: number; background: string; foreground: string; listId?: number; listName?: string; listNames: string[] }> = []; private pageHighlights: Array<{ word: string; count: number; background: string; foreground: string; listId?: number; listName?: string; listNames: string[] }> = [];
@@ -59,6 +65,7 @@ export class PopupController {
this.matchCaseEnabled = data.matchCaseEnabled ?? false; this.matchCaseEnabled = data.matchCaseEnabled ?? false;
this.matchWholeEnabled = data.matchWholeEnabled ?? false; this.matchWholeEnabled = data.matchWholeEnabled ?? false;
this.exceptionsList = data.exceptionsList || []; this.exceptionsList = data.exceptionsList || [];
this.exceptionsWhiteList = data.exceptionsWhiteList || [];
this.exceptionsMode = data.exceptionsMode === 'whitelist' ? 'whitelist' : 'blacklist'; this.exceptionsMode = data.exceptionsMode === 'whitelist' ? 'whitelist' : 'blacklist';
if (this.lists.length === 0) { if (this.lists.length === 0) {
@@ -285,6 +292,7 @@ export class PopupController {
const data: ExportData = { const data: ExportData = {
lists: this.lists, lists: this.lists,
exceptionsList: [...this.exceptionsList], exceptionsList: [...this.exceptionsList],
exceptionsWhiteList: [...this.exceptionsWhiteList],
exceptionsMode: this.exceptionsMode exceptionsMode: this.exceptionsMode
}; };
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
@@ -336,7 +344,10 @@ export class PopupController {
this.exceptionsList = obj.exceptionsList.filter((d): d is string => typeof d === 'string'); this.exceptionsList = obj.exceptionsList.filter((d): d is string => typeof d === 'string');
exceptionsApplied = true; exceptionsApplied = true;
} }
if (Array.isArray(obj.exceptionsWhiteList)) {
this.exceptionsWhiteList = obj.exceptionsWhiteList.filter((d): d is string => typeof d === 'string');
exceptionsApplied = true;
}
if (obj.exceptionsMode === 'whitelist' || obj.exceptionsMode === 'blacklist') { if (obj.exceptionsMode === 'whitelist' || obj.exceptionsMode === 'blacklist') {
this.exceptionsMode = obj.exceptionsMode; this.exceptionsMode = obj.exceptionsMode;
exceptionsApplied = true; exceptionsApplied = true;
@@ -1213,6 +1224,8 @@ export class PopupController {
MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' }); MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' });
this.updateExceptionsModeLabel(); this.updateExceptionsModeLabel();
this.updateExceptionsModeHint(); this.updateExceptionsModeHint();
this.renderExceptions();
this.updateAddCurrentSiteButton();
}); });
document.getElementById('addExceptionBtn')?.addEventListener('click', () => this.addExceptionFromInput()); document.getElementById('addExceptionBtn')?.addEventListener('click', () => this.addExceptionFromInput());
@@ -1223,10 +1236,17 @@ export class PopupController {
document.getElementById('clearExceptionsBtn')?.addEventListener('click', async () => { document.getElementById('clearExceptionsBtn')?.addEventListener('click', async () => {
if (confirm(chrome.i18n.getMessage('confirm_clear_exceptions') || 'Clear all exceptions?')) { if (confirm(chrome.i18n.getMessage('confirm_clear_exceptions') || 'Clear all exceptions?')) {
this.exceptionsList = []; if (this.exceptionsMode === 'whitelist') {
this.exceptionsWhiteList = [];
} else {
this.exceptionsList = [];
}
this.renderExceptions(); this.renderExceptions();
this.updateAddCurrentSiteButton(); this.updateAddCurrentSiteButton();
await StorageService.update('exceptionsList', this.exceptionsList); await StorageService.set({
exceptionsList: this.exceptionsList,
exceptionsWhiteList: this.exceptionsWhiteList
});
MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' }); MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' });
} }
}); });
@@ -1235,10 +1255,17 @@ export class PopupController {
const button = (e.target as HTMLElement).closest('.exception-remove'); const button = (e.target as HTMLElement).closest('.exception-remove');
if (button) { if (button) {
const domain = (button as HTMLElement).dataset.domain!; const domain = (button as HTMLElement).dataset.domain!;
this.exceptionsList = this.exceptionsList.filter(d => d !== domain); if (this.exceptionsMode === 'whitelist') {
this.exceptionsWhiteList = this.exceptionsWhiteList.filter(d => d !== domain);
} else {
this.exceptionsList = this.exceptionsList.filter(d => d !== domain);
}
this.renderExceptions(); this.renderExceptions();
this.updateAddCurrentSiteButton(); this.updateAddCurrentSiteButton();
await StorageService.update('exceptionsList', this.exceptionsList); await StorageService.set({
exceptionsList: this.exceptionsList,
exceptionsWhiteList: this.exceptionsWhiteList
});
MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' }); MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' });
} }
}); });
@@ -1377,6 +1404,7 @@ export class PopupController {
matchCaseEnabled: this.matchCaseEnabled, matchCaseEnabled: this.matchCaseEnabled,
matchWholeEnabled: this.matchWholeEnabled, matchWholeEnabled: this.matchWholeEnabled,
exceptionsList: this.exceptionsList, exceptionsList: this.exceptionsList,
exceptionsWhiteList: this.exceptionsWhiteList,
exceptionsMode: this.exceptionsMode exceptionsMode: this.exceptionsMode
}); });
@@ -1387,7 +1415,7 @@ export class PopupController {
private setupStorageSync(): void { private setupStorageSync(): void {
chrome.storage.onChanged.addListener((changes, areaName) => { chrome.storage.onChanged.addListener((changes, areaName) => {
if (areaName !== 'local') return; if (areaName !== 'local') return;
if (changes.lists || changes.globalHighlightEnabled || changes.matchCaseEnabled || changes.matchWholeEnabled || changes.exceptionsList || changes.exceptionsMode) { if (changes.lists || changes.globalHighlightEnabled || changes.matchCaseEnabled || changes.matchWholeEnabled || changes.exceptionsList || changes.exceptionsWhiteList || changes.exceptionsMode) {
this.reloadFromStorage(); this.reloadFromStorage();
} }
}); });
@@ -1400,6 +1428,7 @@ export class PopupController {
this.matchCaseEnabled = data.matchCaseEnabled ?? false; this.matchCaseEnabled = data.matchCaseEnabled ?? false;
this.matchWholeEnabled = data.matchWholeEnabled ?? false; this.matchWholeEnabled = data.matchWholeEnabled ?? false;
this.exceptionsList = data.exceptionsList || []; this.exceptionsList = data.exceptionsList || [];
this.exceptionsWhiteList = data.exceptionsWhiteList || [];
this.exceptionsMode = data.exceptionsMode === 'whitelist' ? 'whitelist' : 'blacklist'; this.exceptionsMode = data.exceptionsMode === 'whitelist' ? 'whitelist' : 'blacklist';
if (this.lists.length === 0) { if (this.lists.length === 0) {
@@ -1676,15 +1705,23 @@ export class PopupController {
const domain = this.normalizeDomain(input.value); const domain = this.normalizeDomain(input.value);
if (!domain) return; if (!domain) return;
if (this.exceptionsList.includes(domain)) { const list = this.getCurrentExceptionsList();
if (list.includes(domain)) {
input.value = ''; input.value = '';
return; return;
} }
this.exceptionsList.push(domain); if (this.exceptionsMode === 'whitelist') {
this.exceptionsWhiteList.push(domain);
} else {
this.exceptionsList.push(domain);
}
input.value = ''; input.value = '';
this.renderExceptions(); this.renderExceptions();
StorageService.update('exceptionsList', this.exceptionsList).then(() => { StorageService.set({
exceptionsList: this.exceptionsList,
exceptionsWhiteList: this.exceptionsWhiteList
}).then(() => {
MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' }); MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' });
}); });
} }
@@ -1697,10 +1734,18 @@ export class PopupController {
} }
if (!host) return; if (!host) return;
const domain = host.toLowerCase(); const domain = host.toLowerCase();
if (this.exceptionsList.includes(domain)) return; const list = this.getCurrentExceptionsList();
this.exceptionsList.push(domain); if (list.includes(domain)) return;
if (this.exceptionsMode === 'whitelist') {
this.exceptionsWhiteList.push(domain);
} else {
this.exceptionsList.push(domain);
}
this.renderExceptions(); this.renderExceptions();
await StorageService.update('exceptionsList', this.exceptionsList); await StorageService.set({
exceptionsList: this.exceptionsList,
exceptionsWhiteList: this.exceptionsWhiteList
});
MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' }); MessageService.sendToAllTabs({ type: 'EXCEPTIONS_LIST_UPDATED' });
this.updateAddCurrentSiteButton(); this.updateAddCurrentSiteButton();
} }
@@ -1729,7 +1774,8 @@ export class PopupController {
const btn = document.getElementById('addCurrentSiteBtn') as HTMLButtonElement | null; const btn = document.getElementById('addCurrentSiteBtn') as HTMLButtonElement | null;
if (!btn) return; if (!btn) return;
const host = this.currentTabHost.toLowerCase(); const host = this.currentTabHost.toLowerCase();
const alreadyInList = host !== '' && this.exceptionsList.includes(host); const list = this.getCurrentExceptionsList();
const alreadyInList = host !== '' && list.includes(host);
btn.disabled = !host || alreadyInList; btn.disabled = !host || alreadyInList;
} }
@@ -1737,12 +1783,13 @@ export class PopupController {
const container = document.getElementById('exceptionsList'); const container = document.getElementById('exceptionsList');
if (!container) return; if (!container) return;
if (this.exceptionsList.length === 0) { const list = this.getCurrentExceptionsList();
if (list.length === 0) {
container.innerHTML = `<div class="exception-item exception-empty">${chrome.i18n.getMessage('no_exceptions') || 'No exceptions'}</div>`; container.innerHTML = `<div class="exception-item exception-empty">${chrome.i18n.getMessage('no_exceptions') || 'No exceptions'}</div>`;
return; return;
} }
container.innerHTML = this.exceptionsList.map(domain => container.innerHTML = list.map(domain =>
`<div class="exception-item"> `<div class="exception-item">
<span class="exception-domain-icon"><i class="fa-solid fa-at"></i></span> <span class="exception-domain-icon"><i class="fa-solid fa-at"></i></span>
<span class="exception-domain">${DOMUtils.escapeHtml(domain)}</span> <span class="exception-domain">${DOMUtils.escapeHtml(domain)}</span>

View File

@@ -22,6 +22,7 @@ export interface StorageData {
matchCaseEnabled: boolean; matchCaseEnabled: boolean;
matchWholeEnabled: boolean; matchWholeEnabled: boolean;
exceptionsList: string[]; exceptionsList: string[];
exceptionsWhiteList: string[];
exceptionsMode: ExceptionsMode; exceptionsMode: ExceptionsMode;
} }
@@ -56,6 +57,7 @@ export interface MessageData {
export interface ExportData { export interface ExportData {
lists: HighlightList[]; lists: HighlightList[];
exceptionsList: string[]; exceptionsList: string[];
exceptionsWhiteList?: string[];
exceptionsMode?: ExceptionsMode; exceptionsMode?: ExceptionsMode;
} }
@@ -65,6 +67,7 @@ export const DEFAULT_STORAGE: StorageData = {
matchCaseEnabled: false, matchCaseEnabled: false,
matchWholeEnabled: false, matchWholeEnabled: false,
exceptionsList: [], exceptionsList: [],
exceptionsWhiteList: [],
exceptionsMode: 'blacklist' exceptionsMode: 'blacklist'
}; };