@@ -373,13 +373,11 @@ vAPI.SafeAnimationFrame = class {
373373 let i = mutations . length ;
374374 while ( i -- ) {
375375 const mutation = mutations [ i ] ;
376- let nodeList = mutation . addedNodes ;
377- if ( nodeList . length !== 0 ) {
378- addedNodeLists . push ( nodeList ) ;
379- }
380- nodeList = mutation . removedNodes ;
381- if ( nodeList . length !== 0 ) {
382- removedNodeLists . push ( nodeList ) ;
376+ if ( mutation . addedNodes . length !== 0 ) {
377+ addedNodeLists . push ( mutation . addedNodes ) ;
378+ }
379+ if ( mutation . removedNodes . length !== 0 ) {
380+ removedNodeLists . push ( mutation . removedNodes ) ;
383381 }
384382 }
385383 if ( addedNodeLists . length !== 0 || removedNodeLists . length !== 0 ) {
@@ -393,8 +391,6 @@ vAPI.SafeAnimationFrame = class {
393391 if ( domLayoutObserver !== undefined ) { return ; }
394392 domLayoutObserver = new MutationObserver ( observerHandler ) ;
395393 domLayoutObserver . observe ( document , {
396- //attributeFilter: [ 'class', 'id' ],
397- //attributes: true,
398394 childList : true ,
399395 subtree : true
400396 } ) ;
@@ -928,6 +924,22 @@ vAPI.DOMFilterer = class {
928924// vAPI.domSurveyor
929925
930926{
927+ const queriedHashes = new Set ( ) ;
928+ const newHashes = new Set ( ) ;
929+ const maxSurveyNodes = 65536 ;
930+ const pendingLists = [ ] ;
931+ const pendingNodes = [ ] ;
932+ const processedSet = new Set ( ) ;
933+ const ignoreTags = Object . assign ( Object . create ( null ) , {
934+ br : 1 , head : 1 , link : 1 , meta : 1 , script : 1 , style : 1
935+ } ) ;
936+ let domObserver ;
937+ let domFilterer ;
938+ let hostname = '' ;
939+ let domChanged = false ;
940+ let scannedCount = 0 ;
941+ let stopped = false ;
942+
931943 // http://www.cse.yorku.ca/~oz/hash.html#djb2
932944 // Must mirror cosmetic filtering compiler's version
933945 const hashFromStr = ( type , s ) => {
@@ -946,20 +958,12 @@ vAPI.DOMFilterer = class {
946958 }
947959 } ;
948960
949- const queriedHashes = new Set ( ) ;
950- const maxSurveyNodes = 65536 ;
951- const pendingLists = [ ] ;
952- const pendingNodes = [ ] ;
953- const processedSet = new Set ( ) ;
954- let domFilterer ;
955- let hostname = '' ;
956- let domChanged = false ;
957- let scannedCount = 0 ;
958- let stopped = false ;
961+ const qsa = ( context , selector ) =>
962+ Array . from ( context . querySelectorAll ( selector ) ) ;
959963
960964 const addPendingList = list => {
961965 if ( list . length === 0 ) { return ; }
962- pendingLists . push ( Array . from ( list ) ) ;
966+ pendingLists . push ( list ) ;
963967 } ;
964968
965969 const nextPendingNodes = ( ) => {
@@ -986,7 +990,7 @@ vAPI.DOMFilterer = class {
986990 } ;
987991
988992 const hasPendingNodes = ( ) => {
989- return pendingLists . length !== 0 ;
993+ return pendingLists . length !== 0 || newHashes . size !== 0 ;
990994 } ;
991995
992996 // Extract all classes/ids: these will be passed to the cosmetic
@@ -997,18 +1001,18 @@ vAPI.DOMFilterer = class {
9971001 // http://www.w3.org/TR/2014/REC-html5-20141028/infrastructure.html#space-separated-tokens
9981002 // http://jsperf.com/enumerate-classes/6
9991003
1000- const idFromNode = ( node , out ) => {
1004+ const idFromNode = node => {
10011005 const raw = node . id ;
10021006 if ( typeof raw !== 'string' || raw . length === 0 ) { return ; }
10031007 const hash = hashFromStr ( 0x23 /* '#' */ , raw . trim ( ) ) ;
10041008 if ( queriedHashes . has ( hash ) ) { return ; }
10051009 queriedHashes . add ( hash ) ;
1006- out . push ( hash ) ;
1010+ newHashes . add ( hash ) ;
10071011 } ;
10081012
10091013 // https://github.com/uBlockOrigin/uBlock-issues/discussions/2076
10101014 // Performance: avoid using Element.classList
1011- const classesFromNode = ( node , out ) => {
1015+ const classesFromNode = node => {
10121016 const s = node . getAttribute ( 'class' ) ;
10131017 if ( typeof s !== 'string' ) { return ; }
10141018 const len = s . length ;
@@ -1022,32 +1026,29 @@ vAPI.DOMFilterer = class {
10221026 const hash = hashFromStr ( 0x2E /* '.' */ , token ) ;
10231027 if ( queriedHashes . has ( hash ) ) { continue ; }
10241028 queriedHashes . add ( hash ) ;
1025- out . push ( hash ) ;
1029+ newHashes . add ( hash ) ;
10261030 }
10271031 } ;
10281032
1029- const getSurveyResults = ( hashes , safeOnly ) => {
1030- if ( self . vAPI . messaging instanceof Object === false ) {
1031- stop ( ) ; return ;
1032- }
1033- const promise = hashes . length === 0
1033+ const getSurveyResults = safeOnly => {
1034+ if ( Boolean ( self . vAPI ?. messaging ) === false ) { return stop ( ) ; }
1035+ const promise = newHashes . size === 0
10341036 ? Promise . resolve ( null )
10351037 : self . vAPI . messaging . send ( 'contentscript' , {
10361038 what : 'retrieveGenericCosmeticSelectors' ,
10371039 hostname,
1038- hashes,
1040+ hashes : Array . from ( newHashes ) ,
10391041 exceptions : domFilterer . exceptions ,
10401042 safeOnly,
10411043 } ) ;
10421044 promise . then ( response => {
10431045 processSurveyResults ( response ) ;
10441046 } ) ;
1047+ newHashes . clear ( ) ;
10451048 } ;
10461049
10471050 const doSurvey = ( ) => {
1048- if ( self . vAPI instanceof Object === false ) { return ; }
10491051 const t0 = performance . now ( ) ;
1050- const hashes = [ ] ;
10511052 const nodes = pendingNodes ;
10521053 const deadline = t0 + 4 ;
10531054 let scanned = 0 ;
@@ -1060,8 +1061,8 @@ vAPI.DOMFilterer = class {
10601061 if ( processedSet . has ( node ) ) { continue ; }
10611062 processedSet . add ( node ) ;
10621063 }
1063- idFromNode ( node , hashes ) ;
1064- classesFromNode ( node , hashes ) ;
1064+ idFromNode ( node ) ;
1065+ classesFromNode ( node ) ;
10651066 scanned += 1 ;
10661067 }
10671068 if ( performance . now ( ) >= deadline ) { break ; }
@@ -1071,7 +1072,7 @@ vAPI.DOMFilterer = class {
10711072 stop ( ) ;
10721073 }
10731074 processedSet . clear ( ) ;
1074- getSurveyResults ( hashes ) ;
1075+ getSurveyResults ( ) ;
10751076 } ;
10761077
10771078 const surveyTimer = new vAPI . SafeAnimationFrame ( doSurvey ) ;
@@ -1121,66 +1122,73 @@ vAPI.DOMFilterer = class {
11211122 } ) ;
11221123 } ;
11231124
1124- const domWatcherInterface = {
1125- onDOMCreated : function ( ) {
1126- domFilterer = vAPI . domFilterer ;
1127- // https://github.com/uBlockOrigin/uBlock-issues/issues/1692
1128- // Look-up safe-only selectors to mitigate probability of
1129- // html/body elements of erroneously being targeted.
1130- const hashes = [ ] ;
1131- if ( document . documentElement !== null ) {
1132- idFromNode ( document . documentElement , hashes ) ;
1133- classesFromNode ( document . documentElement , hashes ) ;
1134- }
1135- if ( document . body !== null ) {
1136- idFromNode ( document . body , hashes ) ;
1137- classesFromNode ( document . body , hashes ) ;
1138- }
1139- if ( hashes . length !== 0 ) {
1140- getSurveyResults ( hashes , true ) ;
1141- }
1142- addPendingList ( document . querySelectorAll (
1143- '[id]:not(html):not(body),[class]:not(html):not(body)'
1144- ) ) ;
1145- if ( hasPendingNodes ( ) ) {
1146- surveyTimer . start ( ) ;
1147- }
1148- } ,
1149- onDOMChanged : function ( addedNodes ) {
1150- if ( addedNodes . length === 0 ) { return ; }
1151- domChanged = true ;
1152- for ( const node of addedNodes ) {
1153- addPendingList ( [ node ] ) ;
1154- if ( node . firstElementChild === null ) { continue ; }
1155- addPendingList (
1156- node . querySelectorAll (
1157- '[id]:not(html):not(body),[class]:not(html):not(body)'
1158- )
1159- ) ;
1160- }
1161- if ( hasPendingNodes ( ) ) {
1162- surveyTimer . start ( 1 ) ;
1125+ const onDomChanged = mutations => {
1126+ domChanged = true ;
1127+ for ( const mutation of mutations ) {
1128+ if ( mutation . type === 'childList' ) {
1129+ const { addedNodes } = mutation ;
1130+ if ( addedNodes . length === 0 ) { continue ; }
1131+ for ( const node of addedNodes ) {
1132+ if ( node . nodeType !== 1 ) { continue ; }
1133+ if ( ignoreTags [ node . localName ] ) { continue ; }
1134+ if ( node . parentElement === null ) { continue ; }
1135+ addPendingList ( [ node ] ) ;
1136+ if ( node . firstElementChild === null ) { continue ; }
1137+ addPendingList ( qsa ( node , '[id],[class]' ) ) ;
1138+ }
1139+ } else if ( mutation . attributeName === 'class' ) {
1140+ classesFromNode ( mutation . target ) ;
1141+ } else {
1142+ idFromNode ( mutation . target ) ;
11631143 }
11641144 }
1145+ if ( hasPendingNodes ( ) ) {
1146+ surveyTimer . start ( ) ;
1147+ }
11651148 } ;
11661149
11671150 const start = details => {
1168- if ( self . vAPI instanceof Object === false ) { return ; }
1169- if ( self . vAPI . domFilterer instanceof Object === false ) { return ; }
1170- if ( self . vAPI . domWatcher instanceof Object === false ) { return ; }
1151+ if ( Boolean ( self . vAPI ?. domFilterer ) === false ) { return stop ( ) ; }
11711152 hostname = details . hostname ;
1172- self . vAPI . domWatcher . addListener ( domWatcherInterface ) ;
1153+ domFilterer = vAPI . domFilterer ;
1154+ // https://github.com/uBlockOrigin/uBlock-issues/issues/1692
1155+ // Look-up safe-only selectors to mitigate probability of
1156+ // html/body elements of erroneously being targeted.
1157+ if ( document . documentElement !== null ) {
1158+ idFromNode ( document . documentElement ) ;
1159+ classesFromNode ( document . documentElement ) ;
1160+ }
1161+ if ( document . body !== null ) {
1162+ idFromNode ( document . body ) ;
1163+ classesFromNode ( document . body ) ;
1164+ }
1165+ if ( newHashes . size !== 0 ) {
1166+ getSurveyResults ( newHashes , true ) ;
1167+ }
1168+ addPendingList ( qsa ( document , '[id],[class]' ) ) ;
1169+ if ( hasPendingNodes ( ) ) {
1170+ surveyTimer . start ( ) ;
1171+ }
1172+ domObserver = new MutationObserver ( onDomChanged ) ;
1173+ domObserver . observe ( document , {
1174+ attributeFilter : [ 'class' , 'id' ] ,
1175+ attributes : true ,
1176+ childList : true ,
1177+ subtree : true
1178+ } ) ;
11731179 } ;
11741180
11751181 const stop = ( ) => {
11761182 stopped = true ;
11771183 pendingLists . length = 0 ;
11781184 surveyTimer . clear ( ) ;
1179- if ( self . vAPI instanceof Object === false ) { return ; }
1180- if ( self . vAPI . domWatcher instanceof Object ) {
1181- self . vAPI . domWatcher . removeListener ( domWatcherInterface ) ;
1185+ if ( domObserver ) {
1186+ domObserver . disconnect ( ) ;
1187+ domObserver = undefined ;
1188+ }
1189+ if ( self . vAPI ?. domSurveyor ) {
1190+ self . vAPI . domSurveyor = null ;
11821191 }
1183- self . vAPI . domSurveyor = null ;
11841192 } ;
11851193
11861194 self . vAPI . domSurveyor = { start, addHashes } ;
0 commit comments