2525// Isolate from global scope
2626( function uBOL_cssGeneric ( ) {
2727
28- const genericSelectorMap = self . genericSelectorMap || new Map ( ) ;
29- self . genericSelectorMap = undefined ;
30- if ( genericSelectorMap . size === 0 ) { return ; }
28+ const genericSelectorMaps = self . genericSelectorMaps ?? [ ] ;
29+ self . genericSelectorMaps = undefined ;
3130
32- const genericExceptionSieve = self . genericExceptionSieve || new Set ( ) ;
33- self . genericExceptionSieve = undefined ;
31+ const genericDetails = self . genericDetails ?? [ ] ;
32+ self . genericDetails = undefined ;
3433
35- const genericExceptionMap = self . genericExceptionMap || new Map ( ) ;
36- self . genericExceptionMap = undefined ;
34+ if ( genericDetails . length === 0 ) { return ; }
35+ if ( document . documentElement === null ) { return ; }
3736
3837/******************************************************************************/
3938
4039const maxSurveyTimeSlice = 4 ;
4140const maxSurveyNodeSlice = 64 ;
42- const styleSheetSelectors = [ ] ;
41+ const seenHashes = new Set ( ) ;
42+ const pendingHashes = new Set ( ) ;
43+ const pendingSelectors = [ ] ;
4344const stopAllRatio = 0.95 ; // To be investigated
4445
4546let surveyCount = 0 ;
@@ -51,6 +52,35 @@ let lastDomChange = Date.now();
5152
5253/******************************************************************************/
5354
55+ const pendingNodes = {
56+ addedNodes : [ ] ,
57+ nodeSet : new Set ( ) ,
58+ add ( node ) {
59+ this . addedNodes . push ( node ) ;
60+ } ,
61+ next ( out ) {
62+ for ( const added of this . addedNodes ) {
63+ if ( this . nodeSet . has ( added ) ) { continue ; }
64+ this . nodeSet . add ( added ) ;
65+ if ( added . firstElementChild === null ) { continue ; }
66+ for ( const descendant of added . querySelectorAll ( '[id],[class]' ) ) {
67+ this . nodeSet . add ( descendant ) ;
68+ }
69+ }
70+ this . addedNodes . length = 0 ;
71+ for ( const node of this . nodeSet ) {
72+ this . nodeSet . delete ( node ) ;
73+ out . push ( node ) ;
74+ if ( out . length === maxSurveyNodeSlice ) { break ; }
75+ }
76+ } ,
77+ hasNodes ( ) {
78+ return this . addedNodes . length !== 0 || this . nodeSet . size !== 0 ;
79+ } ,
80+ } ;
81+
82+ /******************************************************************************/
83+
5484// http://www.cse.yorku.ca/~oz/hash.html#djb2
5585// Must mirror dnrRulesetFromRawLists's version
5686
@@ -61,7 +91,7 @@ const hashFromStr = (type, s) => {
6191 for ( let i = 0 ; i < len ; i += step ) {
6292 hash = ( hash << 5 ) + hash ^ s . charCodeAt ( i ) ;
6393 }
64- return hash & 0xFFF ;
94+ return hash & 0xFFFF ;
6595} ;
6696
6797/******************************************************************************/
@@ -78,14 +108,9 @@ const uBOL_idFromNode = node => {
78108 const raw = node . id ;
79109 if ( typeof raw !== 'string' || raw . length === 0 ) { return ; }
80110 const hash = hashFromStr ( 0x23 /* '#' */ , raw . trim ( ) ) ;
81- const selectorList = genericSelectorMap . get ( hash ) ;
82- if ( selectorList === undefined ) { return ; }
83- genericSelectorMap . delete ( hash ) ;
84- if ( genericExceptionSieve . has ( hash ) ) {
85- applyExceptions ( selectorList ) ;
86- } else {
87- styleSheetSelectors . push ( selectorList ) ;
88- }
111+ if ( seenHashes . has ( hash ) ) { return ; }
112+ seenHashes . add ( hash ) ;
113+ pendingHashes . add ( hash ) ;
89114} ;
90115
91116// https://github.com/uBlockOrigin/uBlock-issues/discussions/2076
@@ -102,58 +127,31 @@ const uBOL_classesFromNode = node => {
102127 beg = end ;
103128 if ( token . length === 0 ) { continue ; }
104129 const hash = hashFromStr ( 0x2E /* '.' */ , token ) ;
105- const selectorList = genericSelectorMap . get ( hash ) ;
106- if ( selectorList === undefined ) { continue ; }
107- genericSelectorMap . delete ( hash ) ;
108- if ( genericExceptionSieve . has ( hash ) ) {
109- applyExceptions ( selectorList ) ;
110- } else {
111- styleSheetSelectors . push ( selectorList ) ;
112- }
130+ if ( seenHashes . has ( hash ) ) { continue ; }
131+ seenHashes . add ( hash ) ;
132+ pendingHashes . add ( hash ) ;
113133 }
114134} ;
115135
116- const applyExceptions = selectorList => {
117- const selectors = new Set ( selectorList . split ( ',\n' ) ) ;
118- self . isolatedAPI . forEachHostname ( hostname => {
119- const exceptions = genericExceptionMap . get ( hostname ) ;
120- if ( exceptions === undefined ) { return ; }
121- for ( const exception of exceptions . split ( '\n' ) ) {
122- selectors . delete ( exception ) ;
136+ /******************************************************************************/
137+
138+ const processPendingHashes = ( ) => {
139+ for ( const hash of pendingHashes ) {
140+ for ( const selectorMap of genericSelectorMaps ) {
141+ const selectors = selectorMap . get ( hash ) ;
142+ if ( selectors === undefined ) { continue ; }
143+ selectorMap . delete ( hash ) ;
144+ pendingSelectors . push ( selectors ) ;
123145 }
124- if ( selectors . size === 0 ) { return true ; }
125- } , { hasEntities : true } ) ;
126- if ( selectors . size === 0 ) { return ; }
127- styleSheetSelectors . push ( Array . from ( selectors ) . join ( ',\n' ) ) ;
128- }
146+ }
147+ } ;
129148
130149/******************************************************************************/
131150
132- const pendingNodes = {
133- addedNodes : [ ] ,
134- nodeSet : new Set ( ) ,
135- add ( node ) {
136- this . addedNodes . push ( node ) ;
137- } ,
138- next ( out ) {
139- for ( const added of this . addedNodes ) {
140- if ( this . nodeSet . has ( added ) ) { continue ; }
141- this . nodeSet . add ( added ) ;
142- if ( added . firstElementChild === null ) { continue ; }
143- for ( const descendant of added . querySelectorAll ( '[id],[class]' ) ) {
144- this . nodeSet . add ( descendant ) ;
145- }
146- }
147- this . addedNodes . length = 0 ;
148- for ( const node of this . nodeSet ) {
149- this . nodeSet . delete ( node ) ;
150- out . push ( node ) ;
151- if ( out . length === maxSurveyNodeSlice ) { break ; }
152- }
153- } ,
154- hasNodes ( ) {
155- return this . addedNodes . length !== 0 || this . nodeSet . size !== 0 ;
156- } ,
151+ const exceptPendingSelectors = ( ) => {
152+ if ( exceptionSet . size === 0 ) { return pendingSelectors . join ( ',\n' ) ; }
153+ const selectorSet = new Set ( pendingSelectors . map ( a => a . split ( ',\n' ) ) . flat ( ) ) ;
154+ return Array . from ( selectorSet . difference ( exceptionSet ) ) . join ( ',\n' ) ;
157155} ;
158156
159157/******************************************************************************/
@@ -173,21 +171,24 @@ const uBOL_processNodes = ( ) => {
173171 if ( performance . now ( ) >= deadline ) { break ; }
174172 }
175173 surveyCount += 1 ;
176- if ( styleSheetSelectors . length === 0 ) {
174+ processPendingHashes ( ) ;
175+ const styleSheetSelectors = exceptPendingSelectors ( ) ;
176+ pendingHashes . clear ( ) ;
177+ pendingSelectors . length = 0 ;
178+ if ( styleSheetSelectors === '' ) {
177179 surveyMissCount += 1 ;
178- if (
179- surveyCount >= 100 &&
180- ( surveyMissCount / surveyCount ) >= stopAllRatio
181- ) {
182- stopAll ( `too many misses in surveyor (${ surveyMissCount } /${ surveyCount } )` ) ;
180+ if ( surveyCount >= 64 ) {
181+ if ( ( surveyMissCount / surveyCount ) >= stopAllRatio ) {
182+ stopAll ( `too many misses in surveyor (${ surveyMissCount } /${ surveyCount } )` ) ;
183+ }
183184 }
184185 return ;
185186 }
186187 if ( styleSheetTimer !== undefined ) { return ; }
188+ surveyMissCount = 0 ;
187189 styleSheetTimer = self . requestAnimationFrame ( ( ) => {
188190 styleSheetTimer = undefined ;
189- self . cssAPI . insert ( `${ styleSheetSelectors . join ( ',' ) } {display:none!important;}` ) ;
190- styleSheetSelectors . length = 0 ;
191+ self . cssAPI . insert ( `${ styleSheetSelectors } {display:none!important;}` ) ;
191192 } ) ;
192193} ;
193194
@@ -208,7 +209,7 @@ const uBOL_processChanges = mutations => {
208209 }
209210 }
210211 if ( pendingNodes . hasNodes ( ) === false ) {
211- if ( styleSheetSelectors . length === 0 ) { return ; }
212+ if ( pendingHashes . size === 0 ) { return ; }
212213 }
213214 lastDomChange = Date . now ( ) ;
214215 if ( processTimer !== undefined ) { return ; }
@@ -225,15 +226,54 @@ const stopAll = ( ) => {
225226 self . clearTimeout ( domChangeTimer ) ;
226227 domChangeTimer = undefined ;
227228 }
228- domMutationObserver . disconnect ( ) ;
229- domMutationObserver . takeRecords ( ) ;
230- domMutationObserver = undefined ;
231- genericSelectorMap . clear ( ) ;
229+ if ( domMutationObserver ) {
230+ domMutationObserver . disconnect ( ) ;
231+ domMutationObserver . takeRecords ( ) ;
232+ domMutationObserver = undefined ;
233+ }
234+ genericSelectorMaps . length = 0 ;
232235} ;
233236
234237/******************************************************************************/
235238
236- if ( document . documentElement === null ) { return ; }
239+ // Perform once:
240+ // - Inject highly generics
241+ // - Collate exceptions matching current context
242+
243+ const exceptionSet = new Set ( ) ;
244+ for ( const entry of genericDetails ) {
245+ const { highlyGeneric, exceptions, hostnames } = entry ;
246+ if ( highlyGeneric ) {
247+ pendingSelectors . push ( highlyGeneric ) ;
248+ }
249+ if ( hostnames . length === 0 ) { continue ; }
250+ let i = - 1 ;
251+ for ( const hostname of self . isolatedAPI . contexts . hostnames ) {
252+ i = self . isolatedAPI . binarySearch ( hostnames , hostname , i ) ;
253+ if ( i >= 0 ) {
254+ exceptions [ i ] . split ( '\n' ) . forEach ( a => exceptionSet . add ( a ) ) ;
255+ } else {
256+ i = ~ i ;
257+ }
258+ }
259+ if ( entry . hasEntities ) {
260+ i = - 1 ;
261+ for ( const entity of self . isolatedAPI . contexts . entities ) {
262+ i = self . isolatedAPI . binarySearch ( hostnames , entity , i ) ;
263+ if ( i >= 0 ) {
264+ exceptions [ i ] . split ( '\n' ) . forEach ( a => exceptionSet . add ( a ) ) ;
265+ } else {
266+ i = ~ i ;
267+ }
268+ }
269+ }
270+ }
271+ genericDetails . length = 0 ;
272+
273+ /******************************************************************************/
274+
275+ // Start applying generic cosmetic filters
276+
237277pendingNodes . add ( document . documentElement ) ;
238278uBOL_processNodes ( ) ;
239279
@@ -248,10 +288,10 @@ domMutationObserver.observe(document, {
248288const needDomChangeObserver = ( ) => {
249289 domChangeTimer = undefined ;
250290 if ( domMutationObserver === undefined ) { return ; }
251- if ( ( Date . now ( ) - lastDomChange ) > 20000 ) {
291+ if ( ( Date . now ( ) - lastDomChange ) > 30000 ) {
252292 return stopAll ( 'no more DOM changes' ) ;
253293 }
254- domChangeTimer = self . setTimeout ( needDomChangeObserver , 20000 ) ;
294+ domChangeTimer = self . setTimeout ( needDomChangeObserver , 30000 ) ;
255295} ;
256296
257297needDomChangeObserver ( ) ;
0 commit comments