diff --git a/platform/mv3/extension/js/scripting/css-generic.js b/platform/mv3/extension/js/scripting/css-generic.js index 52c3781a2..7a7ed0c5e 100644 --- a/platform/mv3/extension/js/scripting/css-generic.js +++ b/platform/mv3/extension/js/scripting/css-generic.js @@ -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, });