feat(browser-extension): improve performance when triggering events on DOMContentLoad instead of load, visibilitychange just the first time and minor code improvements
This commit is contained in:
parent
7743a37de3
commit
1ea189525b
@ -1,20 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {Object} ExtensionData
|
||||||
|
* @property {string[] | undefined} commonWords
|
||||||
|
* @property {string[] | undefined} fixes
|
||||||
|
* @property {{ domains: string[] | undefined, tags: string[] | undefined } | undefined} skips
|
||||||
|
* @property {{ classes: string[] | undefined, selectors: string[] | undefined } | undefined} tokens
|
||||||
|
*/
|
||||||
|
|
||||||
if (typeof browser === 'undefined') {
|
if (typeof browser === 'undefined') {
|
||||||
browser = chrome;
|
browser = chrome;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} ExtensionData
|
|
||||||
* @property {string[]} commonWords
|
|
||||||
* @property {string[]} fixes
|
|
||||||
* @property {{ domains: string[], tags: string[] }} skips
|
|
||||||
* @property {{ classes: string[], selectors: string[] }} tokens
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Attribute name
|
|
||||||
*/
|
|
||||||
const dataAttributeName = 'data-cookie-dialog-monster';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Matched elements count
|
* @description Matched elements count
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@ -27,16 +22,16 @@ let count = 0;
|
|||||||
*/
|
*/
|
||||||
let { commonWords, fixes = [], skips, tokens } = {};
|
let { commonWords, fixes = [], skips, tokens } = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Attribute name
|
||||||
|
*/
|
||||||
|
const dataAttributeName = 'data-cookie-dialog-monster';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Shortcut to send messages to background script
|
* @description Shortcut to send messages to background script
|
||||||
*/
|
*/
|
||||||
const dispatch = browser.runtime.sendMessage;
|
const dispatch = browser.runtime.sendMessage;
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Event name
|
|
||||||
*/
|
|
||||||
const setupEventName = 'cookie-dialog-monster';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Current hostname
|
* @description Current hostname
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -44,10 +39,10 @@ const setupEventName = 'cookie-dialog-monster';
|
|||||||
const hostname = getHostname();
|
const hostname = getHostname();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Elements that were already matched and are removable
|
* @description Initial visibility state
|
||||||
* @type {HTMLElement[]}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
const removables = [];
|
let initiallyVisible = document.visibilityState === 'visible';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Options provided to observer
|
* @description Options provided to observer
|
||||||
@ -60,6 +55,12 @@ const options = { childList: true, subtree: true };
|
|||||||
*/
|
*/
|
||||||
const preview = hostname.startsWith('consent.') || hostname.startsWith('myprivacy.');
|
const preview = hostname.startsWith('consent.') || hostname.startsWith('myprivacy.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Elements that were already matched and are removable
|
||||||
|
* @type {HTMLElement[]}
|
||||||
|
*/
|
||||||
|
const removables = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Elements that were already seen
|
* @description Elements that were already seen
|
||||||
* @type {HTMLElement[]}
|
* @type {HTMLElement[]}
|
||||||
@ -72,6 +73,11 @@ const seen = [];
|
|||||||
*/
|
*/
|
||||||
let state = { enabled: true };
|
let state = { enabled: true };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Event name to trigger the cleaning process
|
||||||
|
*/
|
||||||
|
const triggerEventName = 'cookie-dialog-monster';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Clean DOM
|
* @description Clean DOM
|
||||||
* @param {Element[]} elements
|
* @param {Element[]} elements
|
||||||
@ -108,8 +114,10 @@ function clean(elements, skipMatch) {
|
|||||||
function forceClean(element) {
|
function forceClean(element) {
|
||||||
const elements = [...element.querySelectorAll(tokens.selectors)];
|
const elements = [...element.querySelectorAll(tokens.selectors)];
|
||||||
|
|
||||||
fix();
|
if (elements.length) {
|
||||||
if (elements.length && !preview) clean(elements, true);
|
fix();
|
||||||
|
clean(elements, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,7 +177,12 @@ function isInViewport(element) {
|
|||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function match(element, skipMatch) {
|
function match(element, skipMatch) {
|
||||||
if (!tokens?.classes.length || !tokens?.selectors.length) {
|
if (
|
||||||
|
!commonWords.length ||
|
||||||
|
!tokens?.classes?.length ||
|
||||||
|
!tokens?.selectors?.length ||
|
||||||
|
!skips?.tags?.length
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +228,7 @@ function match(element, skipMatch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Fix data, consent page and scroll issues
|
* @description Fix data, middle consent page and scroll issues
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function fix() {
|
function fix() {
|
||||||
@ -310,9 +323,8 @@ function restoreDOM() {
|
|||||||
/**
|
/**
|
||||||
* @async
|
* @async
|
||||||
* @description Set up everything
|
* @description Set up everything
|
||||||
* @param {boolean} skipReadyStateHack
|
|
||||||
*/
|
*/
|
||||||
async function setup(skipReadyStateHack) {
|
async function setup() {
|
||||||
state = (await dispatch({ hostname, type: 'GET_HOSTNAME_STATE' })) ?? state;
|
state = (await dispatch({ hostname, type: 'GET_HOSTNAME_STATE' })) ?? state;
|
||||||
dispatch({ type: 'ENABLE_POPUP' });
|
dispatch({ type: 'ENABLE_POPUP' });
|
||||||
|
|
||||||
@ -328,11 +340,6 @@ async function setup(skipReadyStateHack) {
|
|||||||
dispatch({ type: 'SET_BADGE', value: `${count}` });
|
dispatch({ type: 'SET_BADGE', value: `${count}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2023-06-13: hack to force clean when data request takes too long and there are no changes later
|
|
||||||
if (document.readyState === 'complete' && !skipReadyStateHack) {
|
|
||||||
window.dispatchEvent(new Event(setupEventName));
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({ type: 'ENABLE_ICON' });
|
dispatch({ type: 'ENABLE_ICON' });
|
||||||
observer.observe(document.body ?? document.documentElement, options);
|
observer.observe(document.body ?? document.documentElement, options);
|
||||||
} else {
|
} else {
|
||||||
@ -352,15 +359,14 @@ const observer = new MutationObserver((mutations) => {
|
|||||||
|
|
||||||
const elements = mutations.flatMap((mutation) => Array.from(mutation.addedNodes));
|
const elements = mutations.flatMap((mutation) => Array.from(mutation.addedNodes));
|
||||||
|
|
||||||
fix();
|
window.dispatchEvent(new Event(triggerEventName, { detail: { elements } }));
|
||||||
clean(elements);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Listen to messages from any other scripts
|
* @description Listen to messages from any other scripts
|
||||||
* @listens browser.runtime#onMessage
|
* @listens browser.runtime#onMessage
|
||||||
*/
|
*/
|
||||||
browser.runtime.onMessage.addListener((message) => {
|
browser.runtime.onMessage.addListener(async (message) => {
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case 'RESTORE': {
|
case 'RESTORE': {
|
||||||
restoreDOM();
|
restoreDOM();
|
||||||
@ -372,7 +378,55 @@ browser.runtime.onMessage.addListener((message) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setup();
|
fix();
|
||||||
|
await setup();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @async
|
||||||
|
* @description Fix still existing elements when page loads
|
||||||
|
* @listens window#DOMContentLoaded
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
window.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
if (document.visibilityState === 'visible') {
|
||||||
|
await setup();
|
||||||
|
window.dispatchEvent(new Event(triggerEventName));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Fix bfcache issues
|
||||||
|
* @listens window#pageshow
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
window.addEventListener('pageshow', async (event) => {
|
||||||
|
if (document.visibilityState === 'visible' && event.persisted) {
|
||||||
|
await setup();
|
||||||
|
window.dispatchEvent(new Event(triggerEventName));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Force clean when this event is fired
|
||||||
|
* @listens window#cookie-dialog-monster
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
window.addEventListener(triggerEventName, (event) => {
|
||||||
|
if (document.body?.children.length && !preview && state.enabled && tokens?.selectors?.length) {
|
||||||
|
fix();
|
||||||
|
|
||||||
|
if (event.detail?.elements) {
|
||||||
|
clean(event.detail.elements);
|
||||||
|
} else {
|
||||||
|
if (readingTime() < 4) {
|
||||||
|
forceClean(document.body);
|
||||||
|
} else {
|
||||||
|
// 2023-06-13: look into the first level of the document body, there are dialogs there very often
|
||||||
|
clean([...document.body.children]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -382,51 +436,9 @@ browser.runtime.onMessage.addListener((message) => {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
window.addEventListener('visibilitychange', async () => {
|
window.addEventListener('visibilitychange', async () => {
|
||||||
if (document.body?.children.length && !tokens) {
|
if (document.visibilityState === 'visible' && !initiallyVisible) {
|
||||||
await setup(true);
|
initiallyVisible = true;
|
||||||
clean([...document.body.children]);
|
await setup();
|
||||||
|
window.dispatchEvent(new Event(triggerEventName));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Fix still existing elements when page fully load
|
|
||||||
* @listens window#load
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
if (document.visibilityState === 'visible') {
|
|
||||||
window.dispatchEvent(new Event(setupEventName));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Fix bfcache issues
|
|
||||||
* @listens window#pageshow
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
window.addEventListener('pageshow', (event) => {
|
|
||||||
if (document.visibilityState === 'visible' && event.persisted) {
|
|
||||||
setup(true);
|
|
||||||
window.dispatchEvent(new Event(setupEventName));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Force clean when this event is fired
|
|
||||||
* @listens window#cookie-dialog-monster
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
window.addEventListener(setupEventName, () => {
|
|
||||||
if (document.body?.children.length && state.enabled && tokens?.selectors.length && !preview) {
|
|
||||||
if (readingTime() < 4) {
|
|
||||||
forceClean(document.body);
|
|
||||||
} else {
|
|
||||||
// 2023-06-13: look into the first level of the document body, there are dialogs there very often
|
|
||||||
clean([...document.body.children]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (document.visibilityState === 'visible') {
|
|
||||||
setup();
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user