@@ -28,11 +28,13 @@ type EnqueuedCommand = {
28
28
command : CdpCommand
29
29
params ?: object
30
30
p : DeferredPromise
31
+ sessionId ?: string
31
32
}
32
33
33
34
type EnableCommand = {
34
35
command : CdpCommand
35
36
params ?: object
37
+ sessionId ?: string
36
38
}
37
39
38
40
type Subscription = {
@@ -45,6 +47,12 @@ interface CDPClient extends CDP.Client {
45
47
_ws : WebSocket
46
48
}
47
49
50
+ export const DEFAULT_NETWORK_ENABLE_OPTIONS = {
51
+ maxTotalBufferSize : 0 ,
52
+ maxResourceBufferSize : 0 ,
53
+ maxPostDataSize : 0 ,
54
+ }
55
+
48
56
export interface CriClient {
49
57
/**
50
58
* The target id attached to by this client
@@ -138,6 +146,8 @@ type CreateParams = {
138
146
port ?: number
139
147
onReconnect ?: ( client : CriClient ) => void
140
148
protocolManager ?: ProtocolManagerShape
149
+ fullyManageTabs ?: boolean
150
+ browserClient ?: CriClient
141
151
}
142
152
143
153
export const create = async ( {
@@ -147,6 +157,8 @@ export const create = async ({
147
157
port,
148
158
onReconnect,
149
159
protocolManager,
160
+ fullyManageTabs,
161
+ browserClient,
150
162
} : CreateParams ) : Promise < CriClient > => {
151
163
const subscriptions : Subscription [ ] = [ ]
152
164
const enableCommands : EnableCommand [ ] = [ ]
@@ -183,12 +195,12 @@ export const create = async ({
183
195
184
196
// '*.enable' commands need to be resent on reconnect or any events in
185
197
// that namespace will no longer be received
186
- await Promise . all ( enableCommands . map ( ( { command, params } ) => {
187
- return cri . send ( command , params )
198
+ await Promise . all ( enableCommands . map ( ( { command, params, sessionId } ) => {
199
+ return cri . send ( command , params , sessionId )
188
200
} ) )
189
201
190
202
enqueuedCommands . forEach ( ( cmd ) => {
191
- cri . send ( cmd . command , cmd . params ) . then ( cmd . p . resolve as any , cmd . p . reject as any )
203
+ cri . send ( cmd . command , cmd . params , cmd . sessionId ) . then ( cmd . p . resolve as any , cmd . p . reject as any )
192
204
} )
193
205
194
206
enqueuedCommands = [ ]
@@ -258,35 +270,35 @@ export const create = async ({
258
270
cri . on ( 'disconnect' , retryReconnect )
259
271
}
260
272
261
- cri . on ( 'Inspector.targetCrashed' , async ( ) => {
262
- debug ( 'crash detected' )
263
- crashed = true
264
- } )
265
-
266
- // We only want to try and add service worker traffic if we have a host set. This indicates that this is the child cri client.
273
+ // We only want to try and add child target traffic if we have a host set. This indicates that this is the child cri client.
274
+ // Browser cri traffic is handled in browser-cri-client.ts. The basic approach here is we attach to targets and enable network traffic
275
+ // We must attach in a paused state so that we can enable network traffic before the target starts running.
267
276
if ( host ) {
268
- cri . on ( 'Target.targetCreated' , async ( event ) => {
269
- if ( event . targetInfo . type === 'service_worker' ) {
270
- const networkEnabledOptions = protocolManager ?. protocolEnabled ? {
271
- maxTotalBufferSize : 0 ,
272
- maxResourceBufferSize : 0 ,
273
- maxPostDataSize : 64 * 1024 ,
274
- } : {
275
- maxTotalBufferSize : 0 ,
276
- maxResourceBufferSize : 0 ,
277
- maxPostDataSize : 0 ,
278
- }
279
-
280
- const { sessionId } = await cri . send ( 'Target.attachToTarget' , {
281
- targetId : event . targetInfo . targetId ,
282
- flatten : true ,
283
- } )
284
-
285
- await cri . send ( 'Network.enable' , networkEnabledOptions , sessionId )
277
+ cri . on ( 'Target.targetCrashed' , async ( event ) => {
278
+ if ( event . targetId !== target ) {
279
+ return
286
280
}
281
+
282
+ debug ( 'crash detected' )
283
+ crashed = true
287
284
} )
288
285
289
- await cri . send ( 'Target.setDiscoverTargets' , { discover : true } )
286
+ if ( fullyManageTabs ) {
287
+ cri . on ( 'Target.attachedToTarget' , async ( event ) => {
288
+ // Service workers get attached at the page and browser level. We only want to handle them at the browser level
289
+ if ( event . targetInfo . type !== 'service_worker' && event . targetInfo . type !== 'page' ) {
290
+ await cri . send ( 'Network.enable' , protocolManager ?. networkEnableOptions ?? DEFAULT_NETWORK_ENABLE_OPTIONS , event . sessionId )
291
+ }
292
+
293
+ if ( event . waitingForDebugger ) {
294
+ await cri . send ( 'Runtime.runIfWaitingForDebugger' , undefined , event . sessionId )
295
+ }
296
+ } )
297
+
298
+ // Ideally we could use filter rather than checking the type above, but that was added relatively recently
299
+ await cri . send ( 'Target.setAutoAttach' , { autoAttach : true , waitForDebuggerOnStart : true , flatten : true } )
300
+ await cri . send ( 'Target.setDiscoverTargets' , { discover : true } )
301
+ }
290
302
}
291
303
}
292
304
@@ -295,7 +307,7 @@ export const create = async ({
295
307
client = {
296
308
targetId : target ,
297
309
298
- async send ( command : CdpCommand , params ?: object ) {
310
+ async send ( command : CdpCommand , params ?: object , sessionId ?: string ) {
299
311
if ( crashed ) {
300
312
return Promise . reject ( new Error ( `${ command } will not run as the target browser or tab CRI connection has crashed` ) )
301
313
}
@@ -313,6 +325,10 @@ export const create = async ({
313
325
obj . params = params
314
326
}
315
327
328
+ if ( sessionId ) {
329
+ obj . sessionId = sessionId
330
+ }
331
+
316
332
enqueuedCommands . push ( obj )
317
333
} )
318
334
}
@@ -328,12 +344,16 @@ export const create = async ({
328
344
obj . params = params
329
345
}
330
346
347
+ if ( sessionId ) {
348
+ obj . sessionId = sessionId
349
+ }
350
+
331
351
enableCommands . push ( obj )
332
352
}
333
353
334
354
if ( connected ) {
335
355
try {
336
- return await cri . send ( command , params )
356
+ return await cri . send ( command , params , sessionId )
337
357
} catch ( err ) {
338
358
// This error occurs when the browser has been left open for a long
339
359
// time and/or the user's computer has been put to sleep. The
@@ -343,7 +363,7 @@ export const create = async ({
343
363
throw err
344
364
}
345
365
346
- debug ( 'encountered closed websocket on send %o' , { command, params, err } )
366
+ debug ( 'encountered closed websocket on send %o' , { command, params, sessionId , err } )
347
367
348
368
const p = enqueue ( ) as Promise < any >
349
369
@@ -367,15 +387,25 @@ export const create = async ({
367
387
subscriptions . push ( { eventName, cb } )
368
388
debug ( 'registering CDP on event %o' , { eventName } )
369
389
370
- return cri . on ( eventName , cb )
390
+ cri . on ( eventName , cb )
391
+ // This ensures that we are notified about the browser's network events that have been registered (e.g. service workers)
392
+ // Long term we should use flat mode entirely across all of chrome remote interface
393
+ if ( eventName . startsWith ( 'Network.' ) ) {
394
+ browserClient ?. on ( eventName , cb )
395
+ }
371
396
} ,
372
397
373
398
off ( eventName , cb ) {
374
399
subscriptions . splice ( subscriptions . findIndex ( ( sub ) => {
375
400
return sub . eventName === eventName && sub . cb === cb
376
401
} ) , 1 )
377
402
378
- return cri . off ( eventName , cb )
403
+ cri . off ( eventName , cb )
404
+ // This ensures that we are notified about the browser's network events that have been registered (e.g. service workers)
405
+ // Long term we should use flat mode entirely across all of chrome remote interface
406
+ if ( eventName . startsWith ( 'Network.' ) ) {
407
+ browserClient ?. off ( eventName , cb )
408
+ }
379
409
} ,
380
410
381
411
get ws ( ) {
0 commit comments