From 9f0ac76fde888bce2fdea225120ba0a5d493e34d Mon Sep 17 00:00:00 2001 From: wanhose Date: Thu, 19 May 2022 14:17:31 +0200 Subject: [PATCH 1/5] feat(browser-extension): group queries and improve mutation observer --- .../src/scripts/background.js | 76 ++++++++++++------- .../browser-extension/src/scripts/content.js | 46 +++++------ 2 files changed, 67 insertions(+), 55 deletions(-) diff --git a/packages/browser-extension/src/scripts/background.js b/packages/browser-extension/src/scripts/background.js index 8a7cba7..1a0a104 100644 --- a/packages/browser-extension/src/scripts/background.js +++ b/packages/browser-extension/src/scripts/background.js @@ -64,24 +64,36 @@ const getCache = (hostname, callback) => { /** * @async - * @description Retrieves data from GitHub - * @param {string} key + * @description Get all data from GitHub * @param {void} callback - * @returns {Promise<{ any: string[] }>} + * @returns {Promise<{ attributes: string[], classes: string[], fixes: string[], selectors: string[], skips: [] }>} */ -const query = async (key, callback) => { - try { - const url = `${baseDataUrl}/${key}.txt`; - const response = await fetch(url); - const data = await response.text(); +const getData = async (callback) => { + const data = await Promise.all([ + query('classes'), + query('elements'), + query('fixes'), + query('skips'), + ]); - if (response.status !== 200) throw new Error(); + callback({ + attributes: [ + ...new Set( + data[1].elements.flatMap((element) => { + const attributes = element.match(/(?<=\[)[^(){}[\]]+(?=\])/g); - callback({ [key]: data.split('\n') }); - } catch { - callback({ [key]: [] }); - } + return attributes?.length + ? [...attributes.map((attribute) => attribute.replace(/\".*\"|(=|\^|\*|\$)/g, ''))] + : []; + }) + ), + ], + classes: data[0].classes, + fixes: data[2].fixes, + selectors: data[1].elements, + skips: data[3].skips, + }); }; /** @@ -90,7 +102,7 @@ const query = async (key, callback) => { * @returns {Promise<{ id: string, location: string }>} */ -const queryTab = (callback) => { +const getTab = (callback) => { chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { callback({ id: tabs[0]?.id, @@ -99,6 +111,27 @@ const queryTab = (callback) => { }); }; +/** + * @async + * @description Retrieves data from GitHub + * @param {string} key + * @returns {Promise<{ [key]: string[] }>} + */ + +const query = async (key) => { + try { + const url = `${baseDataUrl}/${key}.txt`; + const response = await fetch(url); + const data = await response.text(); + + if (response.status !== 200) throw new Error(); + + return { [key]: [...new Set(data.split('\n'))] }; + } catch { + return { [key]: [] }; + } +}; + /** * @description Reports active tab URL */ @@ -165,20 +198,11 @@ chrome.runtime.onMessage.addListener((request, sender, callback) => { case 'GET_CACHE': getCache(hostname, callback); break; - case 'GET_CLASSES': - query('classes', callback); - break; - case 'GET_SKIPS': - query('skips', callback); - break; - case 'GET_FIXES': - query('fixes', callback); - break; - case 'GET_SELECTORS': - query('elements', callback); + case 'GET_DATA': + getData(callback); break; case 'GET_TAB': - queryTab(callback); + getTab(callback); break; case 'UPDATE_CACHE': updateCache(hostname, state); diff --git a/packages/browser-extension/src/scripts/content.js b/packages/browser-extension/src/scripts/content.js index f54d039..fdb3936 100644 --- a/packages/browser-extension/src/scripts/content.js +++ b/packages/browser-extension/src/scripts/content.js @@ -32,18 +32,18 @@ const fixes = []; const hostname = document.location.hostname.split('.').slice(-2).join('.'); -/** - * @description Is consent preview page? - */ - -const preview = hostname.startsWith('consent.') || hostname.startsWith('myprivacy.'); - /** * @description Options provided to observer * @type {MutationObserverInit} */ -const options = { attributes: true, childList: true, subtree: true }; +const options = { childList: true, subtree: true }; + +/** + * @description Is consent preview page? + */ + +const preview = hostname.startsWith('consent.') || hostname.startsWith('myprivacy.'); /** * @description Selectors list @@ -138,19 +138,6 @@ const observer = new MutationObserver((mutations, instance) => { instance.observe(target, options); }); -/** - * @description Gets data - * @returns {Promise} - */ - -const promiseAll = () => - Promise.all([ - new Promise((resolve) => dispatch({ type: 'GET_CLASSES' }, null, resolve)), - new Promise((resolve) => dispatch({ type: 'GET_FIXES' }, null, resolve)), - new Promise((resolve) => dispatch({ type: 'GET_SELECTORS' }, null, resolve)), - new Promise((resolve) => dispatch({ type: 'GET_SKIPS' }, null, resolve)), - ]); - /** * @description Cleans DOM again after all * @listens document#readystatechange @@ -179,17 +166,18 @@ window.addEventListener('unload', () => {}); * @description Setups everything and starts to observe if enabled */ -dispatch({ hostname, type: 'GET_CACHE' }, null, async ({ enabled }) => { +dispatch({ hostname, type: 'GET_CACHE' }, null, ({ enabled }) => { dispatch({ type: 'ENABLE_POPUP' }); if (enabled) { - const results = await promiseAll(); - - classes.push(...(results[0]?.classes ?? [])); - fixes.push(...(results[1]?.fixes ?? [])); - selectors.push(...(results[2]?.elements ?? [])); - skips.push(...(results[3]?.skips ?? [])); - observer.observe(target, options); - dispatch({ type: 'ENABLE_ICON' }); + dispatch({ type: 'GET_DATA' }, null, (data) => { + classes.push(...data.classes); + fixes.push(...data.fixes); + options.attributeFilter = data.attributes; + selectors.push(...data.selectors); + skips.push(...data.skips); + observer.observe(target, options); + dispatch({ type: 'ENABLE_ICON' }); + }); } }); From 7b68541ce0634c1624cfd652df8e9c9a6b19dcd5 Mon Sep 17 00:00:00 2001 From: wanhose Date: Thu, 19 May 2022 14:17:47 +0200 Subject: [PATCH 2/5] chore(browser-extension): upgrade version --- packages/browser-extension/src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/browser-extension/src/manifest.json b/packages/browser-extension/src/manifest.json index 77a5feb..2e999fd 100644 --- a/packages/browser-extension/src/manifest.json +++ b/packages/browser-extension/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Cookie Dialog Monster", - "version": "5.5.2", + "version": "5.5.3", "default_locale": "en", "description": "__MSG_appDesc__", "icons": { From 51bd0e67175ee9d2fe808a025801ca28cc21933f Mon Sep 17 00:00:00 2001 From: wanhose Date: Thu, 19 May 2022 15:42:53 +0200 Subject: [PATCH 3/5] feat(browser-extension): cache requests --- .../src/scripts/background.js | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/browser-extension/src/scripts/background.js b/packages/browser-extension/src/scripts/background.js index 1a0a104..09f062b 100644 --- a/packages/browser-extension/src/scripts/background.js +++ b/packages/browser-extension/src/scripts/background.js @@ -12,6 +12,13 @@ const apiUrl = 'https://api.cookie-dialog-monster.com/rest/v1'; const baseDataUrl = 'https://raw.githubusercontent.com/wanhose/cookie-dialog-monster/main/data'; +/** + * @description Cache data + * @type {{ attributes: string[], classes: string[], fixes: string[], selectors: string[], skips: string[] }} + */ + +let cache = undefined; + /** * @description Context menu identifier * @type {string} @@ -66,10 +73,15 @@ const getCache = (hostname, callback) => { * @async * @description Get all data from GitHub * @param {void} callback - * @returns {Promise<{ attributes: string[], classes: string[], fixes: string[], selectors: string[], skips: [] }>} + * @returns {Promise<{ attributes: string[], classes: string[], fixes: string[], selectors: string[], skips: string[] }>} */ const getData = async (callback) => { + if (cache) { + callback(cache); + return; + } + const data = await Promise.all([ query('classes'), query('elements'), @@ -77,14 +89,18 @@ const getData = async (callback) => { query('skips'), ]); - callback({ + const result = { attributes: [ ...new Set( data[1].elements.flatMap((element) => { const attributes = element.match(/(?<=\[)[^(){}[\]]+(?=\])/g); return attributes?.length - ? [...attributes.map((attribute) => attribute.replace(/\".*\"|(=|\^|\*|\$)/g, ''))] + ? [ + ...attributes.flatMap((attribute) => { + return attribute ? [attribute.replace(/\".*\"|(=|\^|\*|\$)/g, '')] : []; + }), + ] : []; }) ), @@ -93,7 +109,10 @@ const getData = async (callback) => { fixes: data[2].fixes, selectors: data[1].elements, skips: data[3].skips, - }); + }; + + if (Object.keys(result).every((key) => result[key].length > 0)) cache = result; + callback(result); }; /** @@ -145,7 +164,7 @@ const report = () => { if (tab) { fetch(`${apiUrl}/report/`, { body: JSON.stringify({ - html: `Browser: ${userAgent}
Site: ${tab.url}
Version: ${version}`, + html: `Browser: ${userAgent}
Site: ${tab.url}
Version: ${version}`, to: 'wanhose.development@gmail.com', subject: 'Cookie Dialog Monster Report', }), From 1463f9bafed86c8a3bc01f33028457e9b12d4953 Mon Sep 17 00:00:00 2001 From: wanhose Date: Thu, 19 May 2022 15:52:10 +0200 Subject: [PATCH 4/5] fix(browser-extension): skip match already matched --- packages/browser-extension/src/scripts/content.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/browser-extension/src/scripts/content.js b/packages/browser-extension/src/scripts/content.js index fdb3936..1a422df 100644 --- a/packages/browser-extension/src/scripts/content.js +++ b/packages/browser-extension/src/scripts/content.js @@ -61,23 +61,26 @@ const target = document.body || document.documentElement; /** * @description Checks if node element is removable * @param {any} node + * @param {boolean} skipMatch * @returns {boolean} */ -const check = (node) => +const check = (node, skipMatch) => node instanceof HTMLElement && node.parentElement && !['BODY', 'HTML'].includes(node.tagName) && !(node.id && ['APP', 'ROOT'].includes(node.id.toUpperCase?.())) && - node.matches(selectors); + (skipMatch || node.matches(selectors)); /** * @description Cleans DOM * @param {HTMLElement[]} nodes + * @param {boolean} skipMatch * @returns {void} */ -const clean = (nodes) => nodes.filter(check).forEach((node) => (node.outerHTML = '')); +const clean = (nodes, skipMatch) => + nodes.filter((node) => check(node, skipMatch)).forEach((node) => (node.outerHTML = '')); /** * @description Fixes scroll issues @@ -149,8 +152,8 @@ document.addEventListener('readystatechange', () => { const nodes = selectors.length ? Array.from(document.querySelectorAll(selectors)) : []; fix(); - clean(nodes); - setTimeout(() => clean(nodes), 2000); + clean(nodes, true); + setTimeout(() => clean(nodes, true), 2000); } }); }); From 99beac808072f19dd1ca48a7299fa864275eb292 Mon Sep 17 00:00:00 2001 From: wanhose Date: Thu, 19 May 2022 16:50:14 +0200 Subject: [PATCH 5/5] refactor(browser-extension): cache to store avoiding duplicate names --- .../src/scripts/background.js | 19 ++++++++++--------- .../browser-extension/src/scripts/content.js | 4 ++-- .../browser-extension/src/scripts/popup.js | 10 +++------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/packages/browser-extension/src/scripts/background.js b/packages/browser-extension/src/scripts/background.js index 09f062b..1ee567c 100644 --- a/packages/browser-extension/src/scripts/background.js +++ b/packages/browser-extension/src/scripts/background.js @@ -57,13 +57,13 @@ const enableIcon = (tabId) => const enablePopup = (tabId) => chrome.browserAction.setPopup({ popup: 'popup.html', tabId }); /** - * @description Retrieves cache state + * @description Retrieves store * @param {string} hostname * @param {void} callback * @returns {{ enabled: boolean }} */ -const getCache = (hostname, callback) => { +const getStore = (hostname, callback) => { chrome.storage.local.get(null, (store) => { callback(store[hostname] ?? initial); }); @@ -178,12 +178,12 @@ const report = () => { }; /** - * @description Update cache state + * @description Update store * @param {string} [hostname] * @param {object} [state] */ -const updateCache = (hostname, state) => { +const updateStore = (hostname, state) => { chrome.storage.local.get(null, (cache) => { const current = cache[hostname]; @@ -214,17 +214,17 @@ chrome.runtime.onMessage.addListener((request, sender, callback) => { case 'ENABLE_POPUP': if (tabId) enablePopup(tabId); break; - case 'GET_CACHE': - getCache(hostname, callback); - break; case 'GET_DATA': getData(callback); break; + case 'GET_STORE': + getStore(hostname, callback); + break; case 'GET_TAB': getTab(callback); break; - case 'UPDATE_CACHE': - updateCache(hostname, state); + case 'UPDATE_STORE': + updateStore(hostname, state); break; default: break; @@ -239,6 +239,7 @@ chrome.runtime.onMessage.addListener((request, sender, callback) => { chrome.contextMenus.create({ contexts: ['all'], + documentUrlPatterns: chrome.runtime.getManifest().content_scripts[0].matches, id: contextMenuId, title: chrome.i18n.getMessage('contextMenuText'), }); diff --git a/packages/browser-extension/src/scripts/content.js b/packages/browser-extension/src/scripts/content.js index 1a422df..da2e93f 100644 --- a/packages/browser-extension/src/scripts/content.js +++ b/packages/browser-extension/src/scripts/content.js @@ -147,7 +147,7 @@ const observer = new MutationObserver((mutations, instance) => { */ document.addEventListener('readystatechange', () => { - dispatch({ hostname, type: 'GET_CACHE' }, null, async ({ enabled }) => { + dispatch({ hostname, type: 'GET_STORE' }, null, async ({ enabled }) => { if (document.readyState === 'complete' && enabled && !preview) { const nodes = selectors.length ? Array.from(document.querySelectorAll(selectors)) : []; @@ -169,7 +169,7 @@ window.addEventListener('unload', () => {}); * @description Setups everything and starts to observe if enabled */ -dispatch({ hostname, type: 'GET_CACHE' }, null, ({ enabled }) => { +dispatch({ hostname, type: 'GET_STORE' }, null, ({ enabled }) => { dispatch({ type: 'ENABLE_POPUP' }); if (enabled) { diff --git a/packages/browser-extension/src/scripts/popup.js b/packages/browser-extension/src/scripts/popup.js index 0e2db4f..2d1d375 100644 --- a/packages/browser-extension/src/scripts/popup.js +++ b/packages/browser-extension/src/scripts/popup.js @@ -36,14 +36,10 @@ const isChromium = chrome.runtime.getURL('').startsWith('chrome-extension://'); const handlePowerChange = () => { dispatch({ type: 'GET_TAB' }, null, ({ hostname, id }) => { - dispatch({ hostname, type: 'GET_CACHE' }, null, ({ enabled }) => { + dispatch({ hostname, type: 'GET_STORE' }, null, ({ enabled }) => { const power = document.getElementById('power'); - dispatch({ - hostname, - state: { enabled: !enabled }, - type: 'UPDATE_CACHE', - }); + dispatch({ hostname, state: { enabled: !enabled }, type: 'UPDATE_STORE' }); if (!enabled === false) power.removeAttribute('checked'); if (!enabled === true) power.setAttribute('checked', 'checked'); chrome.tabs.reload(id, { bypassCache: true }); @@ -90,7 +86,7 @@ const handleRate = (event) => { const handleContentLoaded = () => { dispatch({ type: 'GET_TAB' }, null, ({ hostname }) => { - dispatch({ hostname, type: 'GET_CACHE' }, null, ({ enabled }) => { + dispatch({ hostname, type: 'GET_STORE' }, null, ({ enabled }) => { translate(); const host = document.getElementById('host');