@@ -7,25 +7,29 @@ import (
7
7
"log"
8
8
"os"
9
9
"strings"
10
+ "sync"
11
+
12
+ "golang.org/x/exp/slices"
10
13
)
11
14
12
15
type browserContextImpl struct {
13
16
channelOwner
14
- timeoutSettings * timeoutSettings
15
- isClosedOrClosing bool
16
- options * BrowserNewContextOptions
17
- pages []Page
18
- routes []* routeHandlerEntry
19
- ownedPage Page
20
- browser * browserImpl
21
- serviceWorkers []Worker
22
- backgroundPages []Page
23
- bindings map [string ]BindingCallFunction
24
- tracing * tracingImpl
25
- request * apiRequestContextImpl
26
- harRecorders map [string ]harRecordingMetadata
27
- closed chan struct {}
28
- closeReason * string
17
+ timeoutSettings * timeoutSettings
18
+ closeWasCalled bool
19
+ options * BrowserNewContextOptions
20
+ pages []Page
21
+ routes []* routeHandlerEntry
22
+ ownedPage Page
23
+ browser * browserImpl
24
+ serviceWorkers []Worker
25
+ backgroundPages []Page
26
+ bindings map [string ]BindingCallFunction
27
+ tracing * tracingImpl
28
+ request * apiRequestContextImpl
29
+ harRecorders map [string ]harRecordingMetadata
30
+ closed chan struct {}
31
+ closeReason * string
32
+ harRouters []* harRouter
29
33
}
30
34
31
35
func (b * browserContextImpl ) SetDefaultNavigationTimeout (timeout float64 ) {
@@ -210,21 +214,56 @@ func (b *browserContextImpl) ExposeFunction(name string, binding ExposedFunction
210
214
}
211
215
212
216
func (b * browserContextImpl ) Route (url interface {}, handler routeHandler , times ... int ) error {
213
- b .routes = append (b .routes , newRouteHandlerEntry (newURLMatcher (url , b .options .BaseURL ), handler , times ... ))
217
+ b .Lock ()
218
+ defer b .Unlock ()
219
+ b .routes = slices .Insert (b .routes , 0 , newRouteHandlerEntry (newURLMatcher (url , b .options .BaseURL ), handler , times ... ))
214
220
return b .updateInterceptionPatterns ()
215
221
}
216
222
217
223
func (b * browserContextImpl ) Unroute (url interface {}, handlers ... routeHandler ) error {
224
+ removed , remaining , err := unroute (b .routes , url , handlers ... )
225
+ if err != nil {
226
+ return err
227
+ }
228
+ return b .unrouteInternal (removed , remaining , UnrouteBehaviorDefault )
229
+ }
230
+
231
+ func (b * browserContextImpl ) unrouteInternal (removed []* routeHandlerEntry , remaining []* routeHandlerEntry , behavior * UnrouteBehavior ) error {
218
232
b .Lock ()
219
233
defer b .Unlock ()
220
-
221
- routes , err := unroute (b .routes , url , handlers ... )
222
- if err != nil {
234
+ b .routes = remaining
235
+ if err := b .updateInterceptionPatterns (); err != nil {
223
236
return err
224
237
}
225
- b .routes = routes
238
+ if behavior == nil || behavior == UnrouteBehaviorDefault {
239
+ return nil
240
+ }
241
+ wg := & sync.WaitGroup {}
242
+ for _ , entry := range removed {
243
+ wg .Add (1 )
244
+ go func (entry * routeHandlerEntry ) {
245
+ defer wg .Done ()
246
+ entry .Stop (string (* behavior ))
247
+ }(entry )
248
+ }
249
+ wg .Wait ()
250
+ return nil
251
+ }
226
252
227
- return b .updateInterceptionPatterns ()
253
+ func (b * browserContextImpl ) UnrouteAll (options ... BrowserContextUnrouteAllOptions ) error {
254
+ var behavior * UnrouteBehavior
255
+ if len (options ) == 1 {
256
+ behavior = options [0 ].Behavior
257
+ }
258
+ defer b .disposeHarRouters ()
259
+ return b .unrouteInternal (b .routes , []* routeHandlerEntry {}, behavior )
260
+ }
261
+
262
+ func (b * browserContextImpl ) disposeHarRouters () {
263
+ for _ , router := range b .harRouters {
264
+ router .dispose ()
265
+ }
266
+ b .harRouters = make ([]* harRouter , 0 )
228
267
}
229
268
230
269
func (b * browserContextImpl ) Request () APIRequestContext {
@@ -255,6 +294,7 @@ func (b *browserContextImpl) RouteFromHAR(har string, options ...BrowserContextR
255
294
notFound = HarNotFoundAbort
256
295
}
257
296
router := newHarRouter (b .connection .localUtils , har , * notFound , opt .URL )
297
+ b .harRouters = append (b .harRouters , router )
258
298
return router .addContextRoute (b )
259
299
}
260
300
@@ -318,15 +358,13 @@ func (b *browserContextImpl) ExpectPage(cb func() error, options ...BrowserConte
318
358
}
319
359
320
360
func (b * browserContextImpl ) Close (options ... BrowserContextCloseOptions ) error {
321
- if b .isClosedOrClosing {
361
+ if b .closeWasCalled {
322
362
return nil
323
363
}
324
364
if len (options ) == 1 {
325
365
b .closeReason = options [0 ].Reason
326
366
}
327
- b .Lock ()
328
- b .isClosedOrClosing = true
329
- b .Unlock ()
367
+ b .closeWasCalled = true
330
368
innerClose := func () (interface {}, error ) {
331
369
for harId , harMetaData := range b .harRecorders {
332
370
overrides := map [string ]interface {}{}
@@ -456,6 +494,7 @@ func (b *browserContextImpl) onClose() {
456
494
b .browser .contexts = contexts
457
495
b .browser .Unlock ()
458
496
}
497
+ b .disposeHarRouters ()
459
498
b .Emit ("close" , b )
460
499
}
461
500
@@ -473,20 +512,15 @@ func (b *browserContextImpl) onPage(page Page) {
473
512
func (b * browserContextImpl ) onRoute (route * routeImpl ) {
474
513
go func () {
475
514
b .Lock ()
476
- defer b .Unlock ()
477
515
route .context = b
516
+ page := route .Request ().(* requestImpl ).safePage ()
478
517
routes := make ([]* routeHandlerEntry , len (b .routes ))
479
518
copy (routes , b .routes )
519
+ b .Unlock ()
480
520
481
- url := route .Request ().URL ()
482
- for i , handlerEntry := range routes {
483
- if ! handlerEntry .Matches (url ) {
484
- continue
485
- }
486
- if handlerEntry .WillExceed () {
487
- b .routes = append (b .routes [:i ], b .routes [i + 1 :]... )
488
- }
489
- handled := handlerEntry .Handle (route )
521
+ checkInterceptionIfNeeded := func () {
522
+ b .Lock ()
523
+ defer b .Unlock ()
490
524
if len (b .routes ) == 0 {
491
525
_ , err := b .connection .WrapAPICall (func () (interface {}, error ) {
492
526
err := b .updateInterceptionPatterns ()
@@ -496,14 +530,37 @@ func (b *browserContextImpl) onRoute(route *routeImpl) {
496
530
log .Printf ("could not update interception patterns: %v" , err )
497
531
}
498
532
}
533
+ }
534
+
535
+ url := route .Request ().URL ()
536
+ for _ , handlerEntry := range routes {
537
+ // If the page or the context was closed we stall all requests right away.
538
+ if (page != nil && page .closeWasCalled ) || b .closeWasCalled {
539
+ return
540
+ }
541
+ if ! handlerEntry .Matches (url ) {
542
+ continue
543
+ }
544
+ if ! slices .ContainsFunc (b .routes , func (entry * routeHandlerEntry ) bool {
545
+ return entry == handlerEntry
546
+ }) {
547
+ continue
548
+ }
549
+ if handlerEntry .WillExceed () {
550
+ b .routes = slices .DeleteFunc (b .routes , func (rhe * routeHandlerEntry ) bool {
551
+ return rhe == handlerEntry
552
+ })
553
+ }
554
+ handled := handlerEntry .Handle (route )
555
+ checkInterceptionIfNeeded ()
499
556
yes := <- handled
500
557
if yes {
501
558
return
502
559
}
503
560
}
504
- if err := route . internalContinue ( true ); err != nil {
505
- log . Printf ( "could not continue request: %v" , err )
506
- }
561
+ // If the page is closed or unrouteAll() was called without waiting and interception disabled,
562
+ // the method will throw an error - silence it.
563
+ _ = route . internalContinue ( true )
507
564
}()
508
565
}
509
566
@@ -624,6 +681,7 @@ func newBrowserContext(parent *channelOwner, objectType string, guid string, ini
624
681
bindings : make (map [string ]BindingCallFunction ),
625
682
harRecorders : make (map [string ]harRecordingMetadata ),
626
683
closed : make (chan struct {}, 1 ),
684
+ harRouters : make ([]* harRouter , 0 ),
627
685
}
628
686
bt .createChannelOwner (bt , parent , objectType , guid , initializer )
629
687
if parent .objectType == "Browser" {
0 commit comments