@@ -22,23 +22,35 @@ import { BrowserContext } from '../browserContext';
22
22
import { ArtifactDispatcher } from './artifactDispatcher' ;
23
23
24
24
import type { BrowserTypeDispatcher } from './browserTypeDispatcher' ;
25
- import type { RootDispatcher } from './dispatcher' ;
26
25
import type { PageDispatcher } from './pageDispatcher' ;
27
26
import type { CRBrowser } from '../chromium/crBrowser' ;
28
27
import type { CallMetadata } from '../instrumentation' ;
29
28
import type * as channels from '@protocol/channels' ;
30
29
30
+ type BrowserDispatcherOptions = {
31
+ // Do not allow to close this browser.
32
+ ignoreStopAndKill ?: boolean ,
33
+ // Only expose browser contexts created by this dispatcher. By default, all contexts are exposed.
34
+ isolateContexts ?: boolean ,
35
+ } ;
36
+
31
37
export class BrowserDispatcher extends Dispatcher < Browser , channels . BrowserChannel , BrowserTypeDispatcher > implements channels . BrowserChannel {
32
38
_type_Browser = true ;
39
+ private _options : BrowserDispatcherOptions ;
40
+ private _isolatedContexts = new Set < BrowserContext > ( ) ;
33
41
34
- constructor ( scope : BrowserTypeDispatcher , browser : Browser ) {
42
+ constructor ( scope : BrowserTypeDispatcher , browser : Browser , options : BrowserDispatcherOptions = { } ) {
35
43
super ( scope , browser , 'Browser' , { version : browser . version ( ) , name : browser . options . name } ) ;
36
- this . addObjectListener ( Browser . Events . Context , ( context : BrowserContext ) => this . _dispatchEvent ( 'context' , { context : BrowserContextDispatcher . from ( this , context ) } ) ) ;
37
- this . addObjectListener ( Browser . Events . Disconnected , ( ) => this . _didClose ( ) ) ;
38
- if ( browser . _defaultContext )
39
- this . _dispatchEvent ( 'context' , { context : BrowserContextDispatcher . from ( this , browser . _defaultContext ) } ) ;
40
- for ( const context of browser . contexts ( ) )
41
- this . _dispatchEvent ( 'context' , { context : BrowserContextDispatcher . from ( this , context ) } ) ;
44
+ this . _options = options ;
45
+
46
+ if ( ! options . isolateContexts ) {
47
+ this . addObjectListener ( Browser . Events . Context , ( context : BrowserContext ) => this . _dispatchEvent ( 'context' , { context : BrowserContextDispatcher . from ( this , context ) } ) ) ;
48
+ this . addObjectListener ( Browser . Events . Disconnected , ( ) => this . _didClose ( ) ) ;
49
+ if ( browser . _defaultContext )
50
+ this . _dispatchEvent ( 'context' , { context : BrowserContextDispatcher . from ( this , browser . _defaultContext ) } ) ;
51
+ for ( const context of browser . contexts ( ) )
52
+ this . _dispatchEvent ( 'context' , { context : BrowserContextDispatcher . from ( this , context ) } ) ;
53
+ }
42
54
}
43
55
44
56
_didClose ( ) {
@@ -47,24 +59,49 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
47
59
}
48
60
49
61
async newContext ( params : channels . BrowserNewContextParams , metadata : CallMetadata ) : Promise < channels . BrowserNewContextResult > {
62
+ if ( ! this . _options . isolateContexts ) {
63
+ const context = await this . _object . newContext ( metadata , params ) ;
64
+ const contextDispatcher = BrowserContextDispatcher . from ( this , context ) ;
65
+ return { context : contextDispatcher } ;
66
+ }
67
+
68
+ if ( params . recordVideo )
69
+ params . recordVideo . dir = this . _object . options . artifactsDir ;
50
70
const context = await this . _object . newContext ( metadata , params ) ;
51
- return { context : BrowserContextDispatcher . from ( this , context ) } ;
71
+ this . _isolatedContexts . add ( context ) ;
72
+ context . on ( BrowserContext . Events . Close , ( ) => this . _isolatedContexts . delete ( context ) ) ;
73
+ const contextDispatcher = BrowserContextDispatcher . from ( this , context ) ;
74
+ this . _dispatchEvent ( 'context' , { context : contextDispatcher } ) ;
75
+ return { context : contextDispatcher } ;
52
76
}
53
77
54
78
async newContextForReuse ( params : channels . BrowserNewContextForReuseParams , metadata : CallMetadata ) : Promise < channels . BrowserNewContextForReuseResult > {
55
- return await newContextForReuse ( this . _object , this , params , metadata ) ;
79
+ const { context, needsReset } = await this . _object . newContextForReuse ( params , metadata ) ;
80
+ if ( needsReset ) {
81
+ const oldContextDispatcher = this . connection . existingDispatcher < BrowserContextDispatcher > ( context ) ;
82
+ if ( oldContextDispatcher )
83
+ oldContextDispatcher . _dispose ( ) ;
84
+ await context . resetForReuse ( metadata , params ) ;
85
+ }
86
+ const contextDispatcher = BrowserContextDispatcher . from ( this , context ) ;
87
+ this . _dispatchEvent ( 'context' , { context : contextDispatcher } ) ;
88
+ return { context : contextDispatcher } ;
56
89
}
57
90
58
91
async stopPendingOperations ( params : channels . BrowserStopPendingOperationsParams , metadata : CallMetadata ) : Promise < channels . BrowserStopPendingOperationsResult > {
59
92
await this . _object . stopPendingOperations ( params . reason ) ;
60
93
}
61
94
62
95
async close ( params : channels . BrowserCloseParams , metadata : CallMetadata ) : Promise < void > {
96
+ if ( this . _options . ignoreStopAndKill )
97
+ return ;
63
98
metadata . potentiallyClosesScope = true ;
64
99
await this . _object . close ( params ) ;
65
100
}
66
101
67
102
async killForTests ( _ : any , metadata : CallMetadata ) : Promise < void > {
103
+ if ( this . _options . ignoreStopAndKill )
104
+ return ;
68
105
metadata . potentiallyClosesScope = true ;
69
106
await this . _object . killForTests ( ) ;
70
107
}
@@ -93,83 +130,8 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
93
130
const crBrowser = this . _object as CRBrowser ;
94
131
return { artifact : ArtifactDispatcher . from ( this , await crBrowser . stopTracing ( ) ) } ;
95
132
}
96
- }
97
-
98
- // This class implements multiplexing browser dispatchers over a single Browser instance.
99
- export class ConnectedBrowserDispatcher extends Dispatcher < Browser , channels . BrowserChannel , RootDispatcher > implements channels . BrowserChannel {
100
- _type_Browser = true ;
101
- private _contexts = new Set < BrowserContext > ( ) ;
102
-
103
- constructor ( scope : RootDispatcher , browser : Browser ) {
104
- super ( scope , browser , 'Browser' , { version : browser . version ( ) , name : browser . options . name } ) ;
105
- }
106
-
107
- async newContext ( params : channels . BrowserNewContextParams , metadata : CallMetadata ) : Promise < channels . BrowserNewContextResult > {
108
- if ( params . recordVideo )
109
- params . recordVideo . dir = this . _object . options . artifactsDir ;
110
- const context = await this . _object . newContext ( metadata , params ) ;
111
- this . _contexts . add ( context ) ;
112
- context . on ( BrowserContext . Events . Close , ( ) => this . _contexts . delete ( context ) ) ;
113
- const contextDispatcher = BrowserContextDispatcher . from ( this , context ) ;
114
- this . _dispatchEvent ( 'context' , { context : contextDispatcher } ) ;
115
- return { context : contextDispatcher } ;
116
- }
117
-
118
- async newContextForReuse ( params : channels . BrowserNewContextForReuseParams , metadata : CallMetadata ) : Promise < channels . BrowserNewContextForReuseResult > {
119
- return await newContextForReuse ( this . _object , this as any as BrowserDispatcher , params , metadata ) ;
120
- }
121
-
122
- async stopPendingOperations ( params : channels . BrowserStopPendingOperationsParams , metadata : CallMetadata ) : Promise < channels . BrowserStopPendingOperationsResult > {
123
- await this . _object . stopPendingOperations ( params . reason ) ;
124
- }
125
-
126
- async close ( ) : Promise < void > {
127
- // Client should not send us Browser.close.
128
- }
129
-
130
- async killForTests ( ) : Promise < void > {
131
- // Client should not send us Browser.killForTests.
132
- }
133
-
134
- async defaultUserAgentForTest ( ) : Promise < channels . BrowserDefaultUserAgentForTestResult > {
135
- throw new Error ( 'Client should not send us Browser.defaultUserAgentForTest' ) ;
136
- }
137
-
138
- async newBrowserCDPSession ( ) : Promise < channels . BrowserNewBrowserCDPSessionResult > {
139
- if ( ! this . _object . options . isChromium )
140
- throw new Error ( `CDP session is only available in Chromium` ) ;
141
- const crBrowser = this . _object as CRBrowser ;
142
- return { session : new CDPSessionDispatcher ( this as any as BrowserDispatcher , await crBrowser . newBrowserCDPSession ( ) ) } ;
143
- }
144
-
145
- async startTracing ( params : channels . BrowserStartTracingParams ) : Promise < void > {
146
- if ( ! this . _object . options . isChromium )
147
- throw new Error ( `Tracing is only available in Chromium` ) ;
148
- const crBrowser = this . _object as CRBrowser ;
149
- await crBrowser . startTracing ( params . page ? ( params . page as PageDispatcher ) . _object : undefined , params ) ;
150
- }
151
-
152
- async stopTracing ( ) : Promise < channels . BrowserStopTracingResult > {
153
- if ( ! this . _object . options . isChromium )
154
- throw new Error ( `Tracing is only available in Chromium` ) ;
155
- const crBrowser = this . _object as CRBrowser ;
156
- return { artifact : ArtifactDispatcher . from ( this , await crBrowser . stopTracing ( ) ) } ;
157
- }
158
133
159
134
async cleanupContexts ( ) {
160
- await Promise . all ( Array . from ( this . _contexts ) . map ( context => context . close ( { reason : 'Global context cleanup (connection terminated)' } ) ) ) ;
161
- }
162
- }
163
-
164
- async function newContextForReuse ( browser : Browser , scope : BrowserDispatcher , params : channels . BrowserNewContextForReuseParams , metadata : CallMetadata ) : Promise < channels . BrowserNewContextForReuseResult > {
165
- const { context, needsReset } = await browser . newContextForReuse ( params , metadata ) ;
166
- if ( needsReset ) {
167
- const oldContextDispatcher = scope . connection . existingDispatcher < BrowserContextDispatcher > ( context ) ;
168
- if ( oldContextDispatcher )
169
- oldContextDispatcher . _dispose ( ) ;
170
- await context . resetForReuse ( metadata , params ) ;
135
+ await Promise . all ( Array . from ( this . _isolatedContexts ) . map ( context => context . close ( { reason : 'Global context cleanup (connection terminated)' } ) ) ) ;
171
136
}
172
- const contextDispatcher = BrowserContextDispatcher . from ( scope , context ) ;
173
- scope . _dispatchEvent ( 'context' , { context : contextDispatcher } ) ;
174
- return { context : contextDispatcher } ;
175
137
}
0 commit comments