[mv3] Mind id/class attribute changes in generic cosmetic filterer

Related issue:
https://github.com/uBlockOrigin/uBlock-issues/issues/3904
This commit is contained in:
Raymond Hill 2025-12-23 10:51:15 -05:00
parent 00b79d9023
commit c975fbb9d7
No known key found for this signature in database
GPG key ID: F5630CAE62A14316

View file

@ -74,7 +74,7 @@ const hashFromStr = (type, s) => {
// http://www.w3.org/TR/2014/REC-html5-20141028/infrastructure.html#space-separated-tokens
// http://jsperf.com/enumerate-classes/6
const uBOL_idFromNode = (node, out) => {
const uBOL_idFromNode = node => {
const raw = node.id;
if ( typeof raw !== 'string' || raw.length === 0 ) { return; }
const hash = hashFromStr(0x23 /* '#' */, raw.trim());
@ -82,15 +82,15 @@ const uBOL_idFromNode = (node, out) => {
if ( selectorList === undefined ) { return; }
genericSelectorMap.delete(hash);
if ( genericExceptionSieve.has(hash) ) {
applyExceptions(selectorList, out);
applyExceptions(selectorList);
} else {
out.push(selectorList);
styleSheetSelectors.push(selectorList);
}
};
// https://github.com/uBlockOrigin/uBlock-issues/discussions/2076
// Performance: avoid using Element.classList
const uBOL_classesFromNode = (node, out) => {
const uBOL_classesFromNode = node => {
const s = node.getAttribute('class');
if ( typeof s !== 'string' ) { return; }
const len = s.length;
@ -106,14 +106,14 @@ const uBOL_classesFromNode = (node, out) => {
if ( selectorList === undefined ) { continue; }
genericSelectorMap.delete(hash);
if ( genericExceptionSieve.has(hash) ) {
applyExceptions(selectorList, out);
applyExceptions(selectorList);
} else {
out.push(selectorList);
styleSheetSelectors.push(selectorList);
}
}
};
const applyExceptions = (selectorList, out) => {
const applyExceptions = selectorList => {
const selectors = new Set(selectorList.split(',\n'));
self.isolatedAPI.forEachHostname(hostname => {
const exceptions = genericExceptionMap.get(hostname);
@ -124,7 +124,7 @@ const applyExceptions = (selectorList, out) => {
if ( selectors.size === 0 ) { return true; }
}, { hasEntities: true });
if ( selectors.size === 0 ) { return; }
out.push(Array.from(selectors).join(',\n'));
styleSheetSelectors.push(Array.from(selectors).join(',\n'));
}
/******************************************************************************/
@ -138,9 +138,7 @@ const pendingNodes = {
next(out) {
for ( const added of this.addedNodes ) {
if ( this.nodeSet.has(added) ) { continue; }
if ( added.nodeType === 1 ) {
this.nodeSet.add(added);
}
this.nodeSet.add(added);
if ( added.firstElementChild === null ) { continue; }
for ( const descendant of added.querySelectorAll('[id],[class]') ) {
this.nodeSet.add(descendant);
@ -168,8 +166,8 @@ const uBOL_processNodes = ( ) => {
pendingNodes.next(nodes);
if ( nodes.length === 0 ) { break; }
for ( const node of nodes ) {
uBOL_idFromNode(node, styleSheetSelectors);
uBOL_classesFromNode(node, styleSheetSelectors);
uBOL_idFromNode(node);
uBOL_classesFromNode(node);
}
nodes.length = 0;
if ( performance.now() >= deadline ) { break; }
@ -196,14 +194,22 @@ const uBOL_processNodes = ( ) => {
/******************************************************************************/
const uBOL_processChanges = mutations => {
for ( let i = 0; i < mutations.length; i++ ) {
const mutation = mutations[i];
for ( const added of mutation.addedNodes ) {
if ( added.nodeType !== 1 ) { continue; }
pendingNodes.add(added);
for ( const mutation of mutations ) {
if ( mutation.type === 'childList' ) {
for ( const added of mutation.addedNodes ) {
if ( added.nodeType !== 1 ) { continue; }
if ( added.parentElement === null ) { continue; }
pendingNodes.add(added);
}
} else if ( mutation.attributeName === 'class' ) {
uBOL_classesFromNode(mutation.target);
} else {
uBOL_idFromNode(mutation.target);
}
}
if ( pendingNodes.hasNodes() === false ) { return; }
if ( pendingNodes.hasNodes() === false ) {
if ( styleSheetSelectors.length === 0 ) { return; }
}
lastDomChange = Date.now();
if ( processTimer !== undefined ) { return; }
processTimer = self.setTimeout(( ) => {
@ -227,11 +233,14 @@ const stopAll = ( ) => {
/******************************************************************************/
pendingNodes.add(document);
if ( document.documentElement === null ) { return; }
pendingNodes.add(document.documentElement);
uBOL_processNodes();
let domMutationObserver = new MutationObserver(uBOL_processChanges);
domMutationObserver.observe(document, {
attributeFilter: [ 'class', 'id' ],
attributes: true,
childList: true,
subtree: true,
});