@@ -34,7 +34,7 @@ function setup(options: { adapter?: boolean, activate?: (id: string) => Promise<
3434 // The browser adapter binds this port to its known viewer and inspected
3535 // document. Production adapter identity checks live in packages/webext.
3636 if ( event . source !== viewer . window || event . origin !== viewer . win . location . origin
37- || event . data ?. session !== 'session-a' ) {
37+ || event . data ?. session !== new URLSearchParams ( viewer . win . location . search ) . get ( 'devframe-inspected-page' ) ) {
3838 return
3939 }
4040 page . win . dispatch ( 'message' , {
@@ -69,14 +69,16 @@ describe('inspected page bridge', () => {
6969 listener ( null )
7070 await vi . waitFor ( ( ) => expect ( changed ) . toHaveBeenCalledExactlyOnceWith ( null ) )
7171 unsubscribe ( )
72- await expect ( target . deactivate ( 'tracer' ) ) . resolves . toBe ( true )
73- expect ( s . host . deactivate ) . toHaveBeenCalledExactlyOnceWith ( 'tracer' )
72+ await expect ( target . deactivate ( 'tracer' ) ) . resolves . toBe ( false )
73+ expect ( s . host . deactivate ) . not . toHaveBeenCalled ( )
7474 } )
7575
7676 it ( 'carries an existing panel channel through the dedicated bridge port' , async ( ) => {
7777 interface Protocol extends InPageChannelProtocol {
78- pageScript : { route : ( ) => string }
79- panel : Record < string , never >
78+ functions : {
79+ pageScript : { route : ( ) => string }
80+ panel : Record < string , never >
81+ }
8082 }
8183 const s = setup ( )
8284 const pageScript = createPageScriptChannel < Protocol > ( {
@@ -169,6 +171,39 @@ describe('inspected page bridge', () => {
169171 expect ( s . host . deactivate ) . toHaveBeenCalledExactlyOnceWith ( 'tracer' )
170172 } )
171173
174+ it . each ( [ 'disconnect' , 'deactivate' ] ) ( 'preserves a newer session activation when the old session requests %s' , async ( method ) => {
175+ const s = setup ( )
176+ const first = await connect ( s )
177+ await first . activate ( 'tracer' )
178+ s . viewer . win . location . search = s . viewer . win . location . search . replace ( 'session-a' , 'session-b' )
179+ const second = await connect ( s )
180+ await second . activate ( 'tracer' )
181+
182+ if ( method === 'disconnect' )
183+ first . close ( )
184+ else
185+ await expect ( first . deactivate ( 'tracer' ) ) . resolves . toBe ( false )
186+ // A later request is a barrier for the host's shared operation queue.
187+ await second . prepare ( 'a11y' )
188+ expect ( s . host . deactivate ) . not . toHaveBeenCalled ( )
189+ second . close ( )
190+ await vi . waitFor ( ( ) => expect ( s . host . deactivate ) . toHaveBeenCalledExactlyOnceWith ( 'tracer' ) )
191+ } )
192+
193+ it ( 'releases ownership when inspection ends before an embedded activation' , async ( ) => {
194+ const s = setup ( )
195+ const target = await connect ( s )
196+ await target . activate ( 'tracer' )
197+ for ( const id of [ null , 'tracer' ] ) {
198+ for ( const listener of s . selectionListeners )
199+ listener ( id )
200+ }
201+ target . close ( )
202+ const replacement = await connect ( s )
203+ await replacement . prepare ( 'a11y' )
204+ expect ( s . host . deactivate ) . not . toHaveBeenCalled ( )
205+ } )
206+
172207 it ( 'closes a timed-out activation and tears down when the pending action finishes' , async ( ) => {
173208 let finish ! : ( value : boolean ) => void
174209 let started ! : ( ) => void
0 commit comments