@@ -14,33 +14,33 @@ function debugLog(...args) {
14
14
class TabShareExtension {
15
15
constructor ( ) {
16
16
this . activeConnections = new Map ( ) ; // tabId -> connection info
17
-
17
+
18
18
// Remove page action click handler since we now use popup
19
19
chrome . tabs . onRemoved . addListener ( this . onTabRemoved . bind ( this ) ) ;
20
-
20
+
21
21
// Handle messages from popup
22
22
chrome . runtime . onMessage . addListener ( this . onMessage . bind ( this ) ) ;
23
23
}
24
24
25
25
/**
26
26
* Handle messages from popup
27
- * @param {any } message
28
- * @param {chrome.runtime.MessageSender } sender
29
- * @param {Function } sendResponse
27
+ * @param {any } message
28
+ * @param {chrome.runtime.MessageSender } sender
29
+ * @param {Function } sendResponse
30
30
*/
31
31
onMessage ( message , sender , sendResponse ) {
32
32
switch ( message . type ) {
33
33
case 'getStatus' :
34
34
this . getStatus ( message . tabId , sendResponse ) ;
35
35
return true ; // Will respond asynchronously
36
-
36
+
37
37
case 'connect' :
38
38
this . connectTab ( message . tabId , message . bridgeUrl ) . then (
39
39
( ) => sendResponse ( { success : true } ) ,
40
40
( error ) => sendResponse ( { success : false , error : error . message } )
41
41
) ;
42
42
return true ; // Will respond asynchronously
43
-
43
+
44
44
case 'disconnect' :
45
45
this . disconnectTab ( message . tabId ) . then (
46
46
( ) => sendResponse ( { success : true } ) ,
@@ -53,24 +53,24 @@ class TabShareExtension {
53
53
54
54
/**
55
55
* Get connection status for popup
56
- * @param {number } requestedTabId
57
- * @param {Function } sendResponse
56
+ * @param {number } requestedTabId
57
+ * @param {Function } sendResponse
58
58
*/
59
59
getStatus ( requestedTabId , sendResponse ) {
60
60
const isConnected = this . activeConnections . size > 0 ;
61
61
let activeTabId = null ;
62
62
let activeTabInfo = null ;
63
-
63
+
64
64
if ( isConnected ) {
65
65
const [ tabId , connection ] = this . activeConnections . entries ( ) . next ( ) . value ;
66
66
activeTabId = tabId ;
67
-
67
+
68
68
// Get tab info
69
69
chrome . tabs . get ( tabId , ( tab ) => {
70
70
if ( chrome . runtime . lastError ) {
71
- sendResponse ( {
72
- isConnected : false ,
73
- error : 'Active tab not found'
71
+ sendResponse ( {
72
+ isConnected : false ,
73
+ error : 'Active tab not found'
74
74
} ) ;
75
75
} else {
76
76
sendResponse ( {
@@ -94,8 +94,8 @@ class TabShareExtension {
94
94
95
95
/**
96
96
* Connect a tab to the bridge server
97
- * @param {number } tabId
98
- * @param {string } bridgeUrl
97
+ * @param {number } tabId
98
+ * @param {string } bridgeUrl
99
99
*/
100
100
async connectTab ( tabId , bridgeUrl ) {
101
101
try {
@@ -104,15 +104,15 @@ class TabShareExtension {
104
104
// Attach chrome debugger
105
105
const debuggee = { tabId } ;
106
106
await chrome . debugger . attach ( debuggee , '1.3' ) ;
107
-
108
- if ( chrome . runtime . lastError )
107
+
108
+ if ( chrome . runtime . lastError )
109
109
throw new Error ( chrome . runtime . lastError . message ) ;
110
110
const targetInfo = /** @type {any } */ ( await chrome . debugger . sendCommand ( debuggee , 'Target.getTargetInfo' ) ) ;
111
111
debugLog ( 'Target info:' , targetInfo ) ;
112
112
113
113
// Connect to bridge server
114
114
const socket = new WebSocket ( bridgeUrl ) ;
115
-
115
+
116
116
const connection = {
117
117
debuggee,
118
118
socket,
@@ -137,36 +137,36 @@ class TabShareExtension {
137
137
138
138
// Set up message handling
139
139
this . setupMessageHandling ( connection ) ;
140
-
140
+
141
141
// Store connection
142
142
this . activeConnections . set ( tabId , connection ) ;
143
-
143
+
144
144
// Update UI
145
145
chrome . action . setBadgeText ( { tabId, text : '●' } ) ;
146
146
chrome . action . setBadgeBackgroundColor ( { tabId, color : '#4CAF50' } ) ;
147
147
chrome . action . setTitle ( { tabId, title : 'Disconnect from Playwright MCP' } ) ;
148
-
148
+
149
149
debugLog ( `Tab ${ tabId } connected successfully` ) ;
150
-
150
+
151
151
} catch ( error ) {
152
152
debugLog ( `Failed to connect tab ${ tabId } :` , error . message ) ;
153
153
await this . cleanupConnection ( tabId ) ;
154
-
154
+
155
155
// Show error to user
156
156
chrome . action . setBadgeText ( { tabId, text : '!' } ) ;
157
157
chrome . action . setBadgeBackgroundColor ( { tabId, color : '#F44336' } ) ;
158
158
chrome . action . setTitle ( { tabId, title : `Connection failed: ${ error . message } ` } ) ;
159
-
159
+
160
160
throw error ; // Re-throw for popup to handle
161
161
}
162
162
}
163
163
164
164
/**
165
165
* Set up bidirectional message handling between debugger and WebSocket
166
- * @param {Object } connection
166
+ * @param {Object } connection
167
167
*/
168
168
setupMessageHandling ( connection ) {
169
- const { debuggee, socket, tabId, sessionId } = connection ;
169
+ const { debuggee, socket, tabId, sessionId : rootSessionId } = connection ;
170
170
171
171
// WebSocket -> chrome.debugger
172
172
socket . onmessage = async ( event ) => {
@@ -186,31 +186,35 @@ class TabShareExtension {
186
186
187
187
try {
188
188
debugLog ( 'Received from bridge:' , message ) ;
189
-
189
+
190
+ const debuggerSession = { ...debuggee } ;
191
+ const sessionId = message . sessionId ;
192
+ // Pass session id, unless it's the root session.
193
+ if ( sessionId && sessionId !== rootSessionId )
194
+ debuggerSession . sessionId = sessionId ;
195
+
190
196
// Forward CDP command to chrome.debugger
191
- if ( message . method ) {
192
- const result = await chrome . debugger . sendCommand (
193
- debuggee ,
194
- message . method ,
195
- message . params || { }
196
- ) ;
197
-
198
- // Send response back to bridge
199
- const response = {
200
- id : message . id ,
201
- sessionId : message . sessionId ,
202
- result
197
+ const result = await chrome . debugger . sendCommand (
198
+ debuggerSession ,
199
+ message . method ,
200
+ message . params || { }
201
+ ) ;
202
+
203
+ // Send response back to bridge
204
+ const response = {
205
+ id : message . id ,
206
+ sessionId,
207
+ result
208
+ } ;
209
+
210
+ if ( chrome . runtime . lastError ) {
211
+ response . error = {
212
+ code : - 32000 ,
213
+ message : chrome . runtime . lastError . message ,
203
214
} ;
204
-
205
- if ( chrome . runtime . lastError ) {
206
- response . error = {
207
- code : - 32000 ,
208
- message : chrome . runtime . lastError . message ,
209
- } ;
210
- }
211
-
212
- socket . send ( JSON . stringify ( response ) ) ;
213
215
}
216
+
217
+ socket . send ( JSON . stringify ( response ) ) ;
214
218
} catch ( error ) {
215
219
debugLog ( 'Error processing WebSocket message:' , error ) ;
216
220
const response = {
@@ -228,10 +232,9 @@ class TabShareExtension {
228
232
// chrome.debugger events -> WebSocket
229
233
const eventListener = ( source , method , params ) => {
230
234
if ( source . tabId === tabId && socket . readyState === WebSocket . OPEN ) {
231
- // There is only one session per tab, and the relay server will
232
- // has a connection info for each tab.
235
+ // If the sessionId is not provided, use the root sessionId.
233
236
const event = {
234
- sessionId,
237
+ sessionId : source . sessionId || rootSessionId ,
235
238
method,
236
239
params,
237
240
} ;
@@ -268,21 +271,21 @@ class TabShareExtension {
268
271
269
272
/**
270
273
* Disconnect a tab from the bridge
271
- * @param {number } tabId
274
+ * @param {number } tabId
272
275
*/
273
276
async disconnectTab ( tabId ) {
274
277
await this . cleanupConnection ( tabId ) ;
275
-
278
+
276
279
// Update UI
277
280
chrome . action . setBadgeText ( { tabId, text : '' } ) ;
278
281
chrome . action . setTitle ( { tabId, title : 'Share tab with Playwright MCP' } ) ;
279
-
282
+
280
283
debugLog ( `Tab ${ tabId } disconnected` ) ;
281
284
}
282
285
283
286
/**
284
287
* Clean up connection resources
285
- * @param {number } tabId
288
+ * @param {number } tabId
286
289
*/
287
290
async cleanupConnection ( tabId ) {
288
291
const connection = this . activeConnections . get ( tabId ) ;
@@ -313,7 +316,7 @@ class TabShareExtension {
313
316
314
317
/**
315
318
* Handle tab removal
316
- * @param {number } tabId
319
+ * @param {number } tabId
317
320
*/
318
321
async onTabRemoved ( tabId ) {
319
322
if ( this . activeConnections . has ( tabId ) ) {
0 commit comments