From 354e311411f0744e0369e1c8bfb922ff7004578c Mon Sep 17 00:00:00 2001 From: wanhose Date: Tue, 5 Oct 2021 09:00:07 +0200 Subject: [PATCH] feat(scripts): improve code structure and performance --- scripts/background.js | 79 ++++++++++++------- scripts/content.js | 173 +++++++++++++++++++++++------------------- 2 files changed, 147 insertions(+), 105 deletions(-) diff --git a/scripts/background.js b/scripts/background.js index 0aeab2c..1143cb4 100644 --- a/scripts/background.js +++ b/scripts/background.js @@ -101,6 +101,54 @@ const getCache = (hostname, responseCallback) => { }); }; +/** + * @async + * @function getClasses + * @description Retrieves a selectors list + * + * @param {void} [responseCallback] + * @returns {Promise<{ matches: string[] }>} A selectors list + */ + +const getClasses = async (responseCallback) => { + try { + const url = + "https://raw.githubusercontent.com/wanhose/do-not-consent/master/data/classes.txt"; + const response = await fetch(url); + const data = await response.text(); + + if (response.status !== 200) throw new Error(); + + responseCallback({ classes: data.split("\n") }); + } catch { + responseCallback({ classes: [] }); + } +}; + +/** + * @async + * @function getSelectors + * @description Retrieves a selectors list + * + * @param {void} [responseCallback] + * @returns {Promise<{ matches: string[] }>} A selectors list + */ + +const getSelectors = async (responseCallback) => { + try { + const url = + "https://raw.githubusercontent.com/wanhose/do-not-consent/master/data/elements.txt"; + const response = await fetch(url); + const data = await response.text(); + + if (response.status !== 200) throw new Error(); + + responseCallback({ selectors: data.split("\n") }); + } catch { + responseCallback({ selectors: [] }); + } +}; + /** * @function getTab * @description Retrieves current tab information @@ -118,30 +166,6 @@ const getTab = (responseCallback) => { }); }; -/** - * @async - * @function getList - * @description Retrieves selectors list - * - * @param {void} [responseCallback] - * @returns {Promise<{ matches: string[] }>} A selectors list - */ - -const getList = async (responseCallback) => { - try { - const url = - "https://raw.githubusercontent.com/wanhose/do-not-consent/master/data/elements.txt"; - const response = await fetch(url); - const data = await response.text(); - - if (response.status !== 200) throw new Error(); - - responseCallback({ selectors: data.split("\n") }); - } catch { - responseCallback({ selectors: [] }); - } -}; - /** * @function updateCache * @description Update cache state @@ -200,8 +224,11 @@ chrome.runtime.onMessage.addListener((request, sender, responseCallback) => { case "GET_CACHE": getCache(request.hostname, responseCallback); break; - case "GET_LIST": - getList(responseCallback); + case "GET_CLASSES": + getClasses(responseCallback); + break; + case "GET_SELECTORS": + getSelectors(responseCallback); break; case "GET_TAB": getTab(responseCallback); diff --git a/scripts/content.js b/scripts/content.js index 6db05ec..6e8d578 100644 --- a/scripts/content.js +++ b/scripts/content.js @@ -1,5 +1,4 @@ /** - * @var attempts * @description Number of attempts * @type {number} */ @@ -7,7 +6,13 @@ let attempts = 1; /** - * @constant dispatch + * @description Array of selectors + * @type {string[]} + */ + +let classesFromNetwork = []; + +/** * @description Shortcut to send messages to background script * @type {void} */ @@ -15,15 +20,6 @@ let attempts = 1; const dispatch = chrome.runtime.sendMessage; /** - * @var intervalId - * @description Task interval identifier - * @type {number} - */ - -let intervalId = 0; - -/** - * @var selectorsFromCache * @description Array of selectors * @type {string[]} */ @@ -31,30 +27,92 @@ let intervalId = 0; let selectorsFromCache = []; /** - * @var selectorsFromNetwork * @description Array of selectors - * @type {string[]} + * @type {Promise[]} */ let selectorsFromNetwork = []; /** - * @function fix - * @description Fix scroll issues + * @description Split large arrays into promises + * @param {string[]} array + */ + +const chunkerize = (array) => + [...Array(Math.ceil(array.length / 300))].map( + (_, index) => () => + new Promise((resolve) => { + removeElements(array.slice(index * 300, (index + 1) * 300), true); + resolve(true); + }) + ); + +/** + * @description Fixes scroll issues */ const fix = () => { const body = document.body; - const classListToRemove = ["ta-cc-modal-open"]; + const classes = classesFromNetwork; const facebook = document.getElementsByClassName("_31e")[0]; const html = document.documentElement; - if (body) body.classList.remove(...classListToRemove); + if (body && classes.length > 0) body.classList.remove(...classes); if (body) body.style.setProperty("overflow-y", "unset", "important"); if (facebook) facebook.style.setProperty("position", "unset", "important"); if (html) html.style.setProperty("overflow-y", "unset", "important"); }; +/** + * @function removeElements + * @description Removes matched elements from a selectors array + * @param {string[]} selectors + * @param {boolean} updateCache + */ + +const removeElements = (selectors, updateCache) => { + for (let i = selectors.length; i--; ) { + const selector = selectors[i]; + const element = search(selector); + + if (element) { + const tagName = element.tagName.toUpperCase(); + + if (!["BODY", "HTML"].includes(tagName)) { + element.remove(); + + if (updateCache) { + selectorsFromCache = [...selectorsFromCache, selector]; + dispatch({ + hostname: document.location.hostname, + state: { matches: [selector] }, + type: "UPDATE_CACHE", + }); + } + } + } + } +}; + +/** + * @function runTasks + * @description Starts running tasks + */ + +const runTasks = async () => { + if (attempts <= 20) { + fix(); + removeElements(selectorsFromCache); + + if (selectorsFromNetwork.length > 0) { + const selectors = selectorsFromNetwork; + + if (attempts <= 5) await Promise.all(selectors.map((fn) => fn())); + if (document.readyState === "complete") attempts += 1; + } + } +}; + /** * @function search * @description Retrieves HTML element if selector exists @@ -80,83 +138,40 @@ const search = (selector) => { }; /** - * @function removeFromCache - * @description Removes matched elements from cache results + * @description Setups classes selectors + * @type {Promise} */ -const removeFromCache = () => { - for (let i = selectorsFromCache.length; i--; ) { - const selector = selectorsFromCache[i]; - const element = search(selector); - - if (element) { - const tagName = element.tagName.toUpperCase(); - - if (!["BODY", "HTML"].includes(tagName)) element.remove(); - } - } -}; +const setupClasses = new Promise((resolve) => { + dispatch({ type: "GET_CLASSES" }, null, ({ classes }) => { + classesFromNetwork = classes; + resolve(true); + }); +}); /** - * @function removeFromNetwork - * @description Removes matched elements from network results + * @description Setups elements selectors + * @type {Promise} */ -const removeFromNetwork = () => { - for (let i = selectorsFromNetwork.length; i--; ) { - const selector = selectorsFromNetwork[i]; - const element = search(selector); - - if (element) { - const tagName = element.tagName.toUpperCase(); - - if (!["BODY", "HTML"].includes(tagName)) { - element.remove(); - dispatch({ - hostname: document.location.hostname, - state: { matches: [selector] }, - type: "UPDATE_CACHE", - }); - } - } - } -}; - -/** - * @function runTasks - * @description Starts running tasks - */ - -const runTasks = () => { - if (attempts <= 20) { - fix(); - removeFromCache(); - if (attempts <= 5) removeFromNetwork(); - if (document.readyState === "complete") attempts += 1; - } - - if (attempts > 20) { - clearInterval(intervalId); - } -}; - -/** - * @description Setup extension context - */ +const setupSelectors = new Promise((resolve) => { + dispatch({ type: "GET_SELECTORS" }, null, ({ selectors }) => { + selectorsFromNetwork = chunkerize(selectors); + resolve(true); + }); +}); dispatch( { hostname: document.location.hostname, type: "GET_CACHE" }, null, - ({ enabled, matches }) => { + async ({ enabled, matches }) => { dispatch({ type: "ENABLE_POPUP" }); if (enabled) { selectorsFromCache = matches; dispatch({ type: "ENABLE_ICON" }); - dispatch({ type: "GET_LIST" }, null, ({ selectors }) => { - selectorsFromNetwork = selectors; - intervalId = setInterval(runTasks, 500); - }); + await Promise.all([setupClasses, setupSelectors]); + setInterval(runTasks, 500); } } );