@@ -72,7 +72,11 @@ function fakeFrame(initialHref: string, options: { navigation?: boolean, crossOr
7272 }
7373 : undefined ,
7474 addEventListener : ( type , listener ) => void listeners . get ( type ) ! . add ( listener ) ,
75- removeEventListener : ( type , listener ) => void listeners . get ( type ) ! . delete ( listener ) ,
75+ get removeEventListener ( ) {
76+ if ( options . crossOrigin )
77+ throw new DOMException ( 'cross-origin' , 'SecurityError' )
78+ return ( type : 'popstate' | 'hashchange' , listener : Listener ) => void listeners . get ( type ) ! . delete ( listener )
79+ } ,
7680 }
7781 }
7882
@@ -122,6 +126,38 @@ function fakeFrame(initialHref: string, options: { navigation?: boolean, crossOr
122126}
123127
124128describe ( 'watchFrameLocation' , ( ) => {
129+ it ( 'resumes location tracking after navigating through a foreign origin' , ( ) => {
130+ expect . assertions ( 5 )
131+ const options = { crossOrigin : false }
132+ const frame = fakeFrame ( 'http://localhost/app/' , options )
133+ const onChange = vi . fn ( )
134+ const dispose = watchFrameLocation ( { iframe : frame . iframe , onChange } )
135+
136+ options . crossOrigin = true
137+ expect ( ( ) => frame . load ( 'http://example.test/' ) ) . not . toThrow ( )
138+ expect ( onChange ) . toHaveBeenCalledTimes ( 1 )
139+ options . crossOrigin = false
140+ frame . load ( 'http://localhost/returned' )
141+ expect ( onChange ) . toHaveBeenLastCalledWith ( 'http://localhost/returned' )
142+ frame . pushState ( 'http://localhost/next' )
143+ expect ( onChange ) . toHaveBeenLastCalledWith ( 'http://localhost/next' )
144+ dispose ( )
145+ expect ( frame . isDetached ( ) ) . toBe ( true )
146+ } )
147+
148+ it ( 'releases remaining subscriptions when the previous window is inaccessible' , ( ) => {
149+ expect . assertions ( 4 )
150+ const options = { crossOrigin : false , navigation : true }
151+ const frame = fakeFrame ( 'http://localhost/app/' , options )
152+ const dispose = watchFrameLocation ( { iframe : frame . iframe , onChange : vi . fn ( ) } )
153+
154+ expect ( frame . isHistoryPristine ( ) ) . toBe ( false )
155+ options . crossOrigin = true
156+ expect ( dispose ) . not . toThrow ( )
157+ expect ( frame . isHistoryPristine ( ) ) . toBe ( true )
158+ expect ( dispose ) . not . toThrow ( )
159+ } )
160+
125161 it ( 'reports pushState and replaceState by wrapping them, and restores them on dispose' , ( ) => {
126162 const frame = fakeFrame ( 'http://localhost/app/' )
127163 const onChange = vi . fn ( )
0 commit comments