@@ -68,11 +68,10 @@ export class CDPRelayServer {
6868 private _wss : WebSocketServer ;
6969 private _playwrightConnection : WebSocket | null = null ;
7070 private _extensionConnection : ExtensionConnection | null = null ;
71- private _connectedTabInfo : {
72- targetInfo : any ;
73- // Page sessionId that should be used by this connection.
74- sessionId : string ;
75- } | undefined ;
71+ // Map from relay sessionId (pw-tab-N) to tab info.
72+ private _tabSessions = new Map < string , { targetInfo : any ; tabId ?: number } > ( ) ;
73+ // Map from extension tabId to relay sessionId, for routing CDP events.
74+ private _tabIdToSessionId = new Map < number , string > ( ) ;
7675 private _nextSessionId : number = 1 ;
7776 private _extensionConnectionPromise ! : ManualPromise < void > ;
7877
@@ -214,7 +213,8 @@ export class CDPRelayServer {
214213 }
215214
216215 private _resetExtensionConnection ( ) {
217- this . _connectedTabInfo = undefined ;
216+ this . _tabSessions . clear ( ) ;
217+ this . _tabIdToSessionId . clear ( ) ;
218218 this . _extensionConnection = null ;
219219 this . _extensionConnectionPromise = new ManualPromise ( ) ;
220220 void this . _extensionConnectionPromise . catch ( logUnhandledError ) ;
@@ -245,14 +245,22 @@ export class CDPRelayServer {
245245
246246 private _handleExtensionMessage < M extends keyof ExtensionEvents > ( method : M , params : ExtensionEvents [ M ] [ 'params' ] ) {
247247 switch ( method ) {
248- case 'forwardCDPEvent' :
249- const sessionId = params . sessionId || this . _connectedTabInfo ?. sessionId ;
248+ case 'forwardCDPEvent' : {
249+ let sessionId = params . sessionId ;
250+ if ( ! sessionId ) {
251+ // Route event to the correct relay session by tabId, or fall back to the first session.
252+ if ( params . tabId !== undefined )
253+ sessionId = this . _tabIdToSessionId . get ( params . tabId ) ;
254+ else
255+ sessionId = [ ...this . _tabSessions . keys ( ) ] [ 0 ] ;
256+ }
250257 this . _sendToPlaywright ( {
251258 sessionId,
252259 method : params . method ,
253- params : params . params
260+ params : params . params ,
254261 } ) ;
255262 break ;
263+ }
256264 }
257265 }
258266
@@ -288,28 +296,47 @@ export class CDPRelayServer {
288296 // Forward child session handling.
289297 if ( sessionId )
290298 break ;
291- // Simulate auto-attach behavior with real target info
292- const { targetInfo } = await this . _extensionConnection ! . send ( 'attachToTab' , { } ) ;
293- this . _connectedTabInfo = {
294- targetInfo,
295- sessionId : `pw-tab- ${ this . _nextSessionId ++ } ` ,
296- } ;
299+ // Simulate auto-attach behavior with real target info.
300+ const { targetInfo, tabId } = await this . _extensionConnection ! . send ( 'attachToTab' , { } ) ;
301+ const tabSessionId = `pw-tab- ${ this . _nextSessionId ++ } ` ;
302+ this . _tabSessions . set ( tabSessionId , { targetInfo, tabId } ) ;
303+ if ( tabId !== undefined )
304+ this . _tabIdToSessionId . set ( tabId , tabSessionId ) ;
297305 debugLogger ( 'Simulating auto-attach' ) ;
298306 this . _sendToPlaywright ( {
299307 method : 'Target.attachedToTarget' ,
300308 params : {
301- sessionId : this . _connectedTabInfo . sessionId ,
302- targetInfo : {
303- ...this . _connectedTabInfo . targetInfo ,
304- attached : true ,
305- } ,
306- waitingForDebugger : false
309+ sessionId : tabSessionId ,
310+ targetInfo : { ...targetInfo , attached : true } ,
311+ waitingForDebugger : false ,
307312 }
308313 } ) ;
309314 return { } ;
310315 }
316+ case 'Target.createTarget' : {
317+ const { targetInfo, tabId } = await this . _extensionConnection ! . send ( 'createTab' , { url : params ?. url } ) ;
318+ const tabSessionId = `pw-tab-${ this . _nextSessionId ++ } ` ;
319+ this . _tabSessions . set ( tabSessionId , { targetInfo, tabId } ) ;
320+ if ( tabId !== undefined )
321+ this . _tabIdToSessionId . set ( tabId , tabSessionId ) ;
322+ this . _sendToPlaywright ( {
323+ method : 'Target.attachedToTarget' ,
324+ params : {
325+ sessionId : tabSessionId ,
326+ targetInfo : { ...targetInfo , attached : true } ,
327+ waitingForDebugger : false ,
328+ }
329+ } ) ;
330+ return { targetId : targetInfo . targetId } ;
331+ }
332+ case 'Target.getTargets' : {
333+ return {
334+ targetInfos : [ ...this . _tabSessions . values ( ) ] . map ( s => ( { ...s . targetInfo , attached : true } ) ) ,
335+ } ;
336+ }
311337 case 'Target.getTargetInfo' : {
312- return this . _connectedTabInfo ?. targetInfo ;
338+ const tabSession = sessionId ? this . _tabSessions . get ( sessionId ) : undefined ;
339+ return ( tabSession ?? [ ...this . _tabSessions . values ( ) ] [ 0 ] ) ?. targetInfo ;
313340 }
314341 }
315342 return await this . _forwardToExtension ( method , params , sessionId ) ;
@@ -318,10 +345,13 @@ export class CDPRelayServer {
318345 private async _forwardToExtension ( method : string , params : any , sessionId : string | undefined ) : Promise < any > {
319346 if ( ! this . _extensionConnection )
320347 throw new Error ( 'Extension not connected' ) ;
321- // Top level sessionId is only passed between the relay and the client.
322- if ( this . _connectedTabInfo ?. sessionId === sessionId )
348+ // Relay sessionIds (pw-tab-N) are internal — resolve them to tabId for routing.
349+ let tabId : number | undefined ;
350+ if ( sessionId && this . _tabSessions . has ( sessionId ) ) {
351+ tabId = this . _tabSessions . get ( sessionId ) ! . tabId ;
323352 sessionId = undefined ;
324- return await this . _extensionConnection . send ( 'forwardCDPCommand' , { sessionId, method, params } ) ;
353+ }
354+ return await this . _extensionConnection . send ( 'forwardCDPCommand' , { sessionId, tabId, method, params } ) ;
325355 }
326356
327357 private _sendToPlaywright ( message : CDPResponse ) : void {
0 commit comments