diff --git a/packages/browser-extension/src/scripts/content.js b/packages/browser-extension/src/scripts/content.js index e5229ca..3f18d0a 100644 --- a/packages/browser-extension/src/scripts/content.js +++ b/packages/browser-extension/src/scripts/content.js @@ -3,7 +3,7 @@ * @type {string[]} */ -let classes = []; +const classes = []; /** * @description Shortcut to send messages to background script @@ -12,12 +12,19 @@ let classes = []; const dispatch = chrome.runtime.sendMessage; +/** + * @description Array of skips to skip + * @type {string[]} + */ + +const skips = []; + /** * @description Array of instructions * @type {string[]} */ -let fixes = []; +const fixes = []; /** * @description Hostname @@ -29,8 +36,7 @@ const hostname = document.location.hostname; * @description Is consent preview page? */ -const isPreview = - hostname.startsWith('consent.') || hostname.startsWith('myprivacy.'); +const preview = hostname.startsWith('consent.') || hostname.startsWith('myprivacy.'); /** * @description Options provided to observer @@ -43,7 +49,7 @@ const options = { childList: true, subtree: true }; * @type {string[]} */ -let selectors = []; +const selectors = []; /** * @description Target provided to observer @@ -53,42 +59,39 @@ const target = document.body || document.documentElement; /** * @description Checks if node element is removable - * @param {Element} node + * @param {any} node + * @returns {boolean} */ const check = (node) => node instanceof HTMLElement && node.parentElement && !['BODY', 'HTML'].includes(node.tagName) && - !(node.id && ['APP', 'ROOT'].includes(node.id.toUpperCase?.())); + !(node.id && ['APP', 'ROOT'].includes(node.id.toUpperCase?.())) && + node.matches(selectors); /** * @description Cleans DOM + * @param {HTMLElement[]} nodes + * @returns {void} */ -const clean = () => { - if (selectors.length) { - const nodes = Array.from(document.querySelectorAll(selectors)); - nodes.filter(check).forEach((node) => (node.outerHTML = '')); - } -}; +const clean = (nodes) => nodes.filter(check).forEach((node) => (node.outerHTML = '')); /** * @description Fixes scroll issues */ const fix = () => { - const body = document.body; - const html = document.documentElement; + if (skips.length && !skips.includes(hostname)) { + for (const item of [document.body, document.documentElement]) { + item?.classList.remove(...classes); + item?.style.setProperty('position', 'initial', 'important'); + item?.style.setProperty('overflow-y', 'initial', 'important'); + } + } - body?.classList.remove(...classes); - body?.style.setProperty('position', 'initial', 'important'); - body?.style.setProperty('overflow-y', 'initial', 'important'); - html?.classList.remove(...classes); - html?.style.setProperty('position', 'initial', 'important'); - html?.style.setProperty('overflow-y', 'initial', 'important'); - - fixes.forEach((fix) => { + for (const fix of fixes) { const [match, selector, action, property] = fix.split('##'); if (hostname.includes(match)) { @@ -96,73 +99,56 @@ const fix = () => { case 'click': { const node = document.querySelector(selector); node?.click(); + break; } case 'remove': { const node = document.querySelector(selector); node?.style?.removeProperty(property); + break; } case 'reset': { const node = document.querySelector(selector); node?.style?.setProperty(property, 'initial', 'important'); + break; } case 'resetAll': { const nodes = document.querySelectorAll(selector); - // prettier-ignore - nodes.forEach((node) => node?.style?.setProperty(property, "initial", "important")); + nodes.forEach((node) => node?.style?.setProperty(property, 'initial', 'important')); + break; } default: break; } } - }); + } }; +/** + * @description Mutation Observer instance + * @type {MutationObserver} + */ + const observer = new MutationObserver((mutations, instance) => { + const nodes = mutations.map((mutation) => Array.from(mutation.addedNodes)).flat(); + instance.disconnect(); fix(); - - if (!isPreview) { - for (const mutation of mutations) { - for (const node of mutation.addedNodes) { - const valid = check(node); - - if (valid && node.matches(selectors)) node.outerHTML = ''; - } - } - } - + if (!preview) clean(nodes); instance.observe(target, options); }); /** - * @description Queries classes selectors - * @returns {Promise<{ classes: string[] }>} + * @description Gets data + * @returns {Promise} */ -const queryClasses = () => - new Promise((resolve) => { - dispatch({ type: 'GET_CLASSES' }, null, resolve); - }); - -/** - * @description Queries fixes instructions - * @returns {Promise<{ fixes: string[] }>} - */ - -const queryFixes = () => - new Promise((resolve) => { - dispatch({ type: 'GET_FIXES' }, null, resolve); - }); - -/** - * @description Queries elements selectors - * @returns {Promise<{ selectors: string }>} - */ - -const querySelectors = () => - new Promise((resolve) => { - dispatch({ type: 'GET_SELECTORS' }, null, resolve); - }); +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 @@ -171,10 +157,12 @@ const querySelectors = () => document.addEventListener('readystatechange', () => { dispatch({ hostname, type: 'GET_CACHE' }, null, async ({ enabled }) => { - if (document.readyState === 'complete' && enabled && !isPreview) { + if (document.readyState === 'complete' && enabled && !preview) { + const nodes = Array.from(document.querySelectorAll(selectors)); + fix(); - clean(); - setTimeout(clean, 2000); + clean(nodes); + setTimeout(() => clean(nodes), 2000); } }); }); @@ -194,12 +182,12 @@ dispatch({ hostname, type: 'GET_CACHE' }, null, async ({ enabled }) => { dispatch({ type: 'ENABLE_POPUP' }); if (enabled) { - const promises = [queryClasses(), queryFixes(), querySelectors()]; - const results = await Promise.all(promises); + const results = await promiseAll(); - classes = results[0]?.classes ?? []; - fixes = results[1]?.fixes ?? []; - selectors = results[2]?.selectors ?? []; + 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' }); }