Add ability to linger for remove-attr scriplet

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/1445

A third (optional) argument has been added to `remove-attr`
scriptlet, which can be one or more space-separated tokens
dictating the behavior of the scriptlet:

`stay`: This tells the scriplet to stay and act on DOM
changes, whiĺe the default behavior is to act only once
when the document becomes interactive.

`complete`: This tells the scriplet to start acting only
when the document is complete, i.e. once all secondary
resources have been loaded, while the default is to start
acting when the document is interactive -- which is earlier
than when the document is complete.

Example:

    ...##+js(remove-attr, class, .j-mini-player, stay)
This commit is contained in:
Raymond Hill 2021-01-08 10:45:35 -05:00
parent 0e3071dd50
commit 0f330c7359
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 36 additions and 4 deletions

View File

@ -665,7 +665,10 @@
if ( selector === '' || selector === '{{2}}' ) { if ( selector === '' || selector === '{{2}}' ) {
selector = `[${tokens.join('],[')}]`; selector = `[${tokens.join('],[')}]`;
} }
const rmattr = function() { let behavior = '{{3}}';
let timer;
const rmattr = ( ) => {
timer = undefined;
try { try {
const nodes = document.querySelectorAll(selector); const nodes = document.querySelectorAll(selector);
for ( const node of nodes ) { for ( const node of nodes ) {
@ -676,10 +679,39 @@
} catch(ex) { } catch(ex) {
} }
}; };
if ( document.readyState === 'loading' ) { const mutationHandler = mutations => {
window.addEventListener('DOMContentLoaded', rmattr, { once: true }); if ( timer !== undefined ) { return; }
} else { let skip = true;
for ( let i = 0; i < mutations.length && skip; i++ ) {
const { type, addedNodes, removedNodes } = mutations[i];
if ( type === 'attributes' ) { skip = false; }
for ( let j = 0; j < addedNodes.length && skip; j++ ) {
if ( addedNodes[j].nodeType === 1 ) { skip = false; break; }
}
for ( let j = 0; j < removedNodes.length && skip; j++ ) {
if ( removedNodes[j].nodeType === 1 ) { skip = false; break; }
}
}
if ( skip ) { return; }
timer = self.requestIdleCallback(rmattr, { timeout: 67 });
};
const start = ( ) => {
rmattr(); rmattr();
if ( /\bstay\b/.test(behavior) === false ) { return; }
const observer = new MutationObserver(mutationHandler);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: tokens,
childList: true,
subtree: true,
});
};
if ( document.readyState !== 'complete' && /\bcomplete\b/.test(behavior) ) {
document.addEventListener('load', start, { once: true });
} else if ( document.readyState === 'loading' ) {
document.addEventListener('DOMContentLoaded', start, { once: true });
} else {
start();
} }
})(); })();