@@ -85,21 +85,16 @@ type Browser struct {
85
85
// NewBrowser creates a new browser
86
86
func NewBrowser (ctx context.Context , cancelFn context.CancelFunc , browserProc * BrowserProcess , launchOpts * LaunchOptions , logger * Logger ) (* Browser , error ) {
87
87
b := Browser {
88
- BaseEventEmitter : NewBaseEventEmitter (ctx ),
89
- ctx : ctx ,
90
- cancelFn : cancelFn ,
91
- state : int64 (BrowserStateOpen ),
92
- browserProc : browserProc ,
93
- conn : nil ,
94
- connected : false ,
95
- launchOpts : launchOpts ,
96
- contexts : make (map [cdp.BrowserContextID ]* BrowserContext ),
97
- defaultContext : nil ,
98
- pagesMu : sync.RWMutex {},
99
- pages : make (map [target.ID ]* Page ),
100
- sessionIDtoTargetIDMu : sync.RWMutex {},
101
- sessionIDtoTargetID : make (map [target.SessionID ]target.ID ),
102
- logger : logger ,
88
+ BaseEventEmitter : NewBaseEventEmitter (ctx ),
89
+ ctx : ctx ,
90
+ cancelFn : cancelFn ,
91
+ state : int64 (BrowserStateOpen ),
92
+ browserProc : browserProc ,
93
+ launchOpts : launchOpts ,
94
+ contexts : make (map [cdp.BrowserContextID ]* BrowserContext ),
95
+ pages : make (map [target.ID ]* Page ),
96
+ sessionIDtoTargetID : make (map [target.SessionID ]target.ID ),
97
+ logger : logger ,
103
98
}
104
99
if err := b .connect (); err != nil {
105
100
return nil , err
@@ -116,21 +111,27 @@ func (b *Browser) connect() error {
116
111
}
117
112
118
113
b .connMu .Lock ()
119
- defer b .connMu .Unlock ()
120
114
b .connected = true
115
+ b .connMu .Unlock ()
116
+
117
+ // We don't need to lock this because `connect()` is called only in NewBrowser
121
118
b .defaultContext = NewBrowserContext (b .ctx , b .conn , b , "" , NewBrowserContextOptions (), b .logger )
119
+
122
120
return b .initEvents ()
123
121
}
124
122
125
123
func (b * Browser ) disposeContext (id cdp.BrowserContextID ) error {
126
124
b .logger .Debugf ("Browser:disposeContext" , "bctxid:%v" , id )
125
+
127
126
action := target .DisposeBrowserContext (id )
128
127
if err := action .Do (cdp .WithExecutor (b .ctx , b .conn )); err != nil {
129
128
return fmt .Errorf ("unable to dispose browser context %T: %w" , action , err )
130
129
}
130
+
131
131
b .contextsMu .Lock ()
132
132
defer b .contextsMu .Unlock ()
133
133
delete (b .contexts , id )
134
+
134
135
return nil
135
136
}
136
137
@@ -197,69 +198,84 @@ func (b *Browser) initEvents() error {
197
198
}
198
199
199
200
func (b * Browser ) onAttachedToTarget (ev * target.EventAttachedToTarget ) {
201
+ evti := ev .TargetInfo
202
+
200
203
b .contextsMu .RLock ()
201
- var browserCtx * BrowserContext = b .defaultContext
202
- bctx , ok := b .contexts [ev . TargetInfo .BrowserContextID ]
204
+ browserCtx : = b .defaultContext
205
+ bctx , ok := b .contexts [evti .BrowserContextID ]
203
206
if ok {
204
207
browserCtx = bctx
205
208
}
206
- b .logger .Debugf ("Browser:onAttachedToTarget" , "sid:%v tid:%v bctxid:%v bctx nil=%t" , ev .SessionID , ev .TargetInfo .TargetID , ev .TargetInfo .BrowserContextID , bctx == nil )
207
209
b .contextsMu .RUnlock ()
208
210
211
+ b .logger .Debugf ("Browser:onAttachedToTarget" , "sid:%v tid:%v bctxid:%v bctx nil:%t" ,
212
+ ev .SessionID , evti .TargetID , evti .BrowserContextID , browserCtx == nil )
213
+
209
214
// We're not interested in the top-level browser target, other targets or DevTools targets right now.
210
- isDevTools := strings .HasPrefix (ev . TargetInfo .URL , "devtools://devtools" )
211
- if ev . TargetInfo . Type == "browser" || ev . TargetInfo .Type == "other" || isDevTools {
212
- b .logger .Debugf ("Browser:onAttachedToTarget:return" , "sid:%v tid:%v (devtools)" , ev .SessionID , ev . TargetInfo .TargetID )
215
+ isDevTools := strings .HasPrefix (evti .URL , "devtools://devtools" )
216
+ if evti . Type == "browser" || evti .Type == "other" || isDevTools {
217
+ b .logger .Debugf ("Browser:onAttachedToTarget:return" , "sid:%v tid:%v (devtools)" , ev .SessionID , evti .TargetID )
213
218
return
214
219
}
215
220
216
- if ev .TargetInfo .Type == "background_page" {
217
- p , err := NewPage (b .ctx , b .conn .getSession (ev .SessionID ), browserCtx , ev .TargetInfo .TargetID , nil , false , b .logger )
221
+ switch evti .Type {
222
+ case "background_page" :
223
+ p , err := NewPage (b .ctx , b .conn .getSession (ev .SessionID ), browserCtx , evti .TargetID , nil , false , b .logger )
218
224
if err != nil {
219
225
isRunning := atomic .LoadInt64 (& b .state ) == BrowserStateOpen && b .IsConnected () //b.conn.isConnected()
220
226
if _ , ok := err .(* websocket.CloseError ); ! ok && ! isRunning {
221
227
// If we're no longer connected to browser, then ignore WebSocket errors
222
228
b .logger .Debugf ("Browser:onAttachedToTarget:background_page:return" , "sid:%v tid:%v websocket err:%v" ,
223
- ev .SessionID , ev . TargetInfo .TargetID , err )
229
+ ev .SessionID , evti .TargetID , err )
224
230
return
225
231
}
226
232
k6Throw (b .ctx , "cannot create NewPage for background_page event: %w" , err )
227
233
}
234
+
228
235
b .pagesMu .Lock ()
229
- b .logger .Debugf ("Browser:onAttachedToTarget:background_page:addTid" , "sid:%v tid:%v" , ev .SessionID , ev . TargetInfo .TargetID )
230
- b .pages [ev . TargetInfo .TargetID ] = p
236
+ b .logger .Debugf ("Browser:onAttachedToTarget:background_page:addTid" , "sid:%v tid:%v" , ev .SessionID , evti .TargetID )
237
+ b .pages [evti .TargetID ] = p
231
238
b .pagesMu .Unlock ()
239
+
232
240
b .sessionIDtoTargetIDMu .Lock ()
233
- b .logger .Debugf ("Browser:onAttachedToTarget:background_page:addSid" , "sid:%v tid:%v" , ev .SessionID , ev . TargetInfo .TargetID )
234
- b .sessionIDtoTargetID [ev .SessionID ] = ev . TargetInfo .TargetID
241
+ b .logger .Debugf ("Browser:onAttachedToTarget:background_page:addSid" , "sid:%v tid:%v" , ev .SessionID , evti .TargetID )
242
+ b .sessionIDtoTargetID [ev .SessionID ] = evti .TargetID
235
243
b .sessionIDtoTargetIDMu .Unlock ()
236
- } else if ev . TargetInfo . Type == "page" {
244
+ case "page" :
237
245
var opener * Page = nil
238
246
b .pagesMu .RLock ()
239
- if t , ok := b .pages [ev . TargetInfo .OpenerID ]; ok {
247
+ if t , ok := b .pages [evti .OpenerID ]; ok {
240
248
opener = t
241
249
}
242
- b .logger .Debugf ("Browser:onAttachedToTarget:page" , "sid:%v tid:%v opener nil:%t" , ev .SessionID , ev . TargetInfo .TargetID , opener == nil )
250
+ b .logger .Debugf ("Browser:onAttachedToTarget:page" , "sid:%v tid:%v opener nil:%t" , ev .SessionID , evti .TargetID , opener == nil )
243
251
b .pagesMu .RUnlock ()
244
- p , err := NewPage (b .ctx , b .conn .getSession (ev .SessionID ), browserCtx , ev .TargetInfo .TargetID , opener , true , b .logger )
252
+
253
+ p , err := NewPage (b .ctx , b .conn .getSession (ev .SessionID ), browserCtx , evti .TargetID , opener , true , b .logger )
245
254
if err != nil {
246
255
isRunning := atomic .LoadInt64 (& b .state ) == BrowserStateOpen && b .IsConnected () //b.conn.isConnected()
247
256
if _ , ok := err .(* websocket.CloseError ); ! ok && ! isRunning {
248
257
// If we're no longer connected to browser, then ignore WebSocket errors
249
- b .logger .Debugf ("Browser:onAttachedToTarget:page:return" , "sid:%v tid:%v websocket err:" , ev .SessionID , ev . TargetInfo .TargetID )
258
+ b .logger .Debugf ("Browser:onAttachedToTarget:page:return" , "sid:%v tid:%v websocket err:" , ev .SessionID , evti .TargetID )
250
259
return
251
260
}
252
261
k6Throw (b .ctx , "cannot create NewPage for page event: %w" , err )
253
262
}
263
+
254
264
b .pagesMu .Lock ()
255
- b .logger .Debugf ("Browser:onAttachedToTarget:page:addTarget" , "sid:%v tid:%v" , ev .SessionID , ev . TargetInfo .TargetID )
256
- b .pages [ev . TargetInfo .TargetID ] = p
265
+ b .logger .Debugf ("Browser:onAttachedToTarget:page:addTarget" , "sid:%v tid:%v" , ev .SessionID , evti .TargetID )
266
+ b .pages [evti .TargetID ] = p
257
267
b .pagesMu .Unlock ()
268
+
258
269
b .sessionIDtoTargetIDMu .Lock ()
259
- b .logger .Debugf ("Browser:onAttachedToTarget:page:sidToTid" , "sid:%v tid:%v" , ev .SessionID , ev . TargetInfo .TargetID )
260
- b .sessionIDtoTargetID [ev .SessionID ] = ev . TargetInfo .TargetID
270
+ b .logger .Debugf ("Browser:onAttachedToTarget:page:sidToTid" , "sid:%v tid:%v" , ev .SessionID , evti .TargetID )
271
+ b .sessionIDtoTargetID [ev .SessionID ] = evti .TargetID
261
272
b .sessionIDtoTargetIDMu .Unlock ()
273
+
262
274
browserCtx .emit (EventBrowserContextPage , p )
275
+ default :
276
+ b .logger .Warnf (
277
+ "Browser:onAttachedToTarget" , "sid:%v tid:%v bctxid:%v bctx nil:%t, unknown target type: %q" ,
278
+ ev .SessionID , evti .TargetID , evti .BrowserContextID , browserCtx == nil , evti .Type )
263
279
}
264
280
}
265
281
@@ -346,16 +362,21 @@ func (b *Browser) Close() {
346
362
b .logger .Debugf ("Browser:Close" , "already in a closing state" )
347
363
return
348
364
}
349
- b . browserProc . GracefulClose ()
350
- defer b .browserProc . Terminate ( )
365
+
366
+ atomic . CompareAndSwapInt64 ( & b . state , b .state , BrowserStateClosed )
351
367
352
368
action := cdpbrowser .Close ()
353
369
if err := action .Do (cdp .WithExecutor (b .ctx , b .conn )); err != nil {
354
370
if _ , ok := err .(* websocket.CloseError ); ! ok {
355
371
k6Throw (b .ctx , "unable to execute %T: %v" , action , err )
356
372
}
357
373
}
358
- atomic .CompareAndSwapInt64 (& b .state , b .state , BrowserStateClosed )
374
+
375
+ // terminate the browser process early on, then tell the CDP
376
+ // afterwards. this will take a little bit of time, and CDP
377
+ // will stop emitting events.
378
+ b .browserProc .GracefulClose ()
379
+ b .browserProc .Terminate ()
359
380
}
360
381
361
382
// Contexts returns list of browser contexts
@@ -374,6 +395,7 @@ func (b *Browser) Contexts() []api.BrowserContext {
374
395
func (b * Browser ) IsConnected () bool {
375
396
b .connMu .RLock ()
376
397
defer b .connMu .RUnlock ()
398
+
377
399
return b .connected
378
400
}
379
401
0 commit comments