@@ -16,47 +16,30 @@ let dom
1616let FloatingToolbar
1717const originalDescriptors = new Map ( )
1818const globalNames = [ 'window' , 'document' , 'Node' , 'HTMLElement' ]
19+ const resizeListeners = new Set ( )
20+ const selectionListeners = new Set ( )
1921const toolbarContainers = new Set ( )
2022
21- const getCapture = ( options ) =>
22- typeof options === 'boolean' ? options : Boolean ( options ?. capture )
23-
24- const createListenerTracker = ( ) => {
25- const registrations = [ ]
26- const matches = ( registration , listener , options ) =>
27- registration . listener === listener && registration . capture === getCapture ( options )
28-
29- return {
30- add ( listener , options ) {
31- if ( registrations . some ( ( registration ) => matches ( registration , listener , options ) ) ) return
32- registrations . push ( { listener, capture : getCapture ( options ) } )
33- } ,
34- remove ( listener , options ) {
35- const index = registrations . findIndex ( ( registration ) =>
36- matches ( registration , listener , options ) ,
37- )
38- if ( index !== - 1 ) registrations . splice ( index , 1 )
39- } ,
40- snapshot ( ) {
41- return registrations . map ( ( registration ) => ( { ...registration } ) )
42- } ,
43- has ( target ) {
44- return registrations . some (
45- ( registration ) =>
46- registration . listener === target . listener && registration . capture === target . capture ,
47- )
48- } ,
49- clear ( ) {
50- registrations . length = 0
51- } ,
52- get size ( ) {
53- return registrations . length
54- } ,
55- }
23+ const getCapture = ( options ) => ( typeof options === 'boolean' ? options : Boolean ( options ?. capture ) )
24+ const registrationKey = ( listener , capture ) => ( { listener, capture } )
25+ const sameRegistration = ( registration , listener , capture ) =>
26+ registration . listener === listener && registration . capture === capture
27+ const addRegistration = ( registrations , listener , options ) => {
28+ const capture = getCapture ( options )
29+ if ( ! [ ...registrations ] . some ( ( registration ) => sameRegistration ( registration , listener , capture ) ) )
30+ registrations . add ( registrationKey ( listener , capture ) )
5631}
57-
58- const resizeListeners = createListenerTracker ( )
59- const selectionListeners = createListenerTracker ( )
32+ const removeRegistration = ( registrations , listener , options ) => {
33+ const capture = getCapture ( options )
34+ const registration = [ ...registrations ] . find ( ( entry ) =>
35+ sameRegistration ( entry , listener , capture ) ,
36+ )
37+ if ( registration ) registrations . delete ( registration )
38+ }
39+ const hasRegistration = ( registrations , registration ) =>
40+ [ ...registrations ] . some ( ( entry ) =>
41+ sameRegistration ( entry , registration . listener , registration . capture ) ,
42+ )
6043
6144const waitFor = async ( predicate , message ) => {
6245 for ( let attempt = 0 ; attempt < 50 ; ++ attempt ) {
@@ -66,32 +49,18 @@ const waitFor = async (predicate, message) => {
6649 assert . fail ( message )
6750}
6851
69- const sameListenerRegistration = ( left , right ) =>
70- left . listener === right . listener && left . capture === right . capture
71-
72- const getNewListener = ( listeners , previousListeners , label ) => {
73- const addedListeners = listeners
74- . snapshot ( )
75- . filter (
76- ( listener ) =>
77- ! previousListeners . some ( ( previousListener ) =>
78- sameListenerRegistration ( listener , previousListener ) ,
79- ) ,
80- )
81- assert . equal ( addedListeners . length , 1 , `expected one new ${ label } listener` )
82- return addedListeners [ 0 ]
83- }
84-
85- const getNewSetEntry = ( entries , previousEntries , label ) => {
86- const addedEntries = [ ...entries ] . filter ( ( entry ) => ! previousEntries . has ( entry ) )
87- assert . equal ( addedEntries . length , 1 , `expected one new ${ label } listener` )
88- return addedEntries [ 0 ]
52+ const getNewRegistration = ( registrations , previousRegistrations , label ) => {
53+ const addedRegistrations = [ ...registrations ] . filter (
54+ ( registration ) => ! hasRegistration ( previousRegistrations , registration ) ,
55+ )
56+ assert . equal ( addedRegistrations . length , 1 , `expected one new ${ label } listener` )
57+ return addedRegistrations [ 0 ]
8958}
9059
9160const createToolbar = async ( id ) => {
9261 const state = globalThis . __FLOATING_SUBSCRIPTION_TEST__
93- const previousResizeListeners = resizeListeners . snapshot ( )
94- const previousSelectionListeners = selectionListeners . snapshot ( )
62+ const previousResizeListeners = new Set ( resizeListeners )
63+ const previousSelectionListeners = new Set ( selectionListeners )
9564 const previousStorageListeners = new Set ( state . storageListeners )
9665 const container = document . createElement ( 'div' )
9766 document . body . append ( container )
@@ -124,9 +93,9 @@ const createToolbar = async (id) => {
12493 return {
12594 container,
12695 listeners : {
127- resize : getNewListener ( resizeListeners , previousResizeListeners , 'resize' ) ,
128- selection : getNewListener ( selectionListeners , previousSelectionListeners , 'selection' ) ,
129- storage : getNewSetEntry ( state . storageListeners , previousStorageListeners , 'storage' ) ,
96+ resize : getNewRegistration ( resizeListeners , previousResizeListeners , 'resize' ) ,
97+ selection : getNewRegistration ( selectionListeners , previousSelectionListeners , 'selection' ) ,
98+ storage : getNewRegistration ( state . storageListeners , previousStorageListeners , 'storage' ) ,
13099 } ,
131100 close : ( ) => {
132101 onClose ( )
@@ -148,22 +117,22 @@ before(async () => {
148117 const originalWindowAddEventListener = window . addEventListener . bind ( window )
149118 const originalWindowRemoveEventListener = window . removeEventListener . bind ( window )
150119 window . addEventListener = ( type , listener , options ) => {
151- if ( type === 'resize' ) resizeListeners . add ( listener , options )
120+ if ( type === 'resize' ) addRegistration ( resizeListeners , listener , options )
152121 return originalWindowAddEventListener ( type , listener , options )
153122 }
154123 window . removeEventListener = ( type , listener , options ) => {
155- if ( type === 'resize' ) resizeListeners . remove ( listener , options )
124+ if ( type === 'resize' ) removeRegistration ( resizeListeners , listener , options )
156125 return originalWindowRemoveEventListener ( type , listener , options )
157126 }
158127
159128 const originalDocumentAddEventListener = document . addEventListener . bind ( document )
160129 const originalDocumentRemoveEventListener = document . removeEventListener . bind ( document )
161130 document . addEventListener = ( type , listener , options ) => {
162- if ( type === 'selectionchange' ) selectionListeners . add ( listener , options )
131+ if ( type === 'selectionchange' ) addRegistration ( selectionListeners , listener , options )
163132 return originalDocumentAddEventListener ( type , listener , options )
164133 }
165134 document . removeEventListener = ( type , listener , options ) => {
166- if ( type === 'selectionchange' ) selectionListeners . remove ( listener , options )
135+ if ( type === 'selectionchange' ) removeRegistration ( selectionListeners , listener , options )
167136 return originalDocumentRemoveEventListener ( type , listener , options )
168137 }
169138
@@ -199,14 +168,15 @@ after(() => {
199168 }
200169} )
201170
202- test ( 'listener tracking keeps registrations when capture does not match ' , ( ) => {
171+ test ( 'listener bookkeeping requires matching capture mode for removal ' , ( ) => {
203172 const listener = ( ) => { }
204- window . addEventListener ( 'resize' , listener , { capture : true } )
205- window . removeEventListener ( 'resize' , listener , { capture : false } )
173+ addRegistration ( resizeListeners , listener , { capture : true } )
174+ removeRegistration ( resizeListeners , listener , { capture : false } )
206175
207176 assert . equal ( resizeListeners . size , 1 )
177+ assert . equal ( hasRegistration ( resizeListeners , registrationKey ( listener , true ) ) , true )
208178
209- window . removeEventListener ( 'resize' , listener , { capture : true } )
179+ removeRegistration ( resizeListeners , listener , true )
210180 assert . equal ( resizeListeners . size , 0 )
211181} )
212182
@@ -217,8 +187,6 @@ test('closing a floating toolbar removes its real hook subscriptions', async ()
217187 assert . equal ( resizeListeners . size , 1 )
218188 assert . equal ( selectionListeners . size , 1 )
219189 assert . equal ( state . storageListeners . size , 1 )
220- assert . equal ( toolbar . listeners . resize . capture , false )
221- assert . equal ( toolbar . listeners . selection . capture , false )
222190
223191 act ( ( ) => toolbar . close ( ) )
224192
@@ -241,11 +209,11 @@ test('closing one toolbar leaves another toolbar subscriptions active', async ()
241209
242210 assert . equal ( first . container . isConnected , false )
243211 assert . equal ( second . container . isConnected , true )
244- assert . equal ( resizeListeners . has ( first . listeners . resize ) , false )
245- assert . equal ( selectionListeners . has ( first . listeners . selection ) , false )
212+ assert . equal ( hasRegistration ( resizeListeners , first . listeners . resize ) , false )
213+ assert . equal ( hasRegistration ( selectionListeners , first . listeners . selection ) , false )
246214 assert . equal ( state . storageListeners . has ( first . listeners . storage ) , false )
247- assert . equal ( resizeListeners . has ( second . listeners . resize ) , true )
248- assert . equal ( selectionListeners . has ( second . listeners . selection ) , true )
215+ assert . equal ( hasRegistration ( resizeListeners , second . listeners . resize ) , true )
216+ assert . equal ( hasRegistration ( selectionListeners , second . listeners . selection ) , true )
249217 assert . equal ( state . storageListeners . has ( second . listeners . storage ) , true )
250218
251219 act ( ( ) => second . close ( ) )
0 commit comments