@@ -30,10 +30,11 @@ import { launchProcess, waitForLine } from '../server/processLauncher';
30
30
import { kBrowserCloseMessageId } from '../chromium/crConnection' ;
31
31
import { PipeTransport } from './pipeTransport' ;
32
32
import { LaunchOptions , BrowserArgOptions , BrowserType } from './browserType' ;
33
- import { ConnectOptions } from '../browser' ;
34
- import { BrowserApp } from './browserApp ' ;
33
+ import { ConnectOptions , LaunchType } from '../browser' ;
34
+ import { BrowserServer } from './browserServer ' ;
35
35
import { Events } from '../events' ;
36
36
import { ConnectionTransport } from '../transport' ;
37
+ import { BrowserContext } from '../browserContext' ;
37
38
38
39
export class Chromium implements BrowserType {
39
40
private _projectRoot : string ;
@@ -49,19 +50,28 @@ export class Chromium implements BrowserType {
49
50
}
50
51
51
52
async launch ( options ?: LaunchOptions & { slowMo ?: number } ) : Promise < CRBrowser > {
52
- const { browserApp , transport } = await this . _launchBrowserApp ( options , false ) ;
53
+ const { browserServer , transport } = await this . _launchServer ( options , 'local' ) ;
53
54
const browser = await CRBrowser . connect ( transport ! , options && options . slowMo ) ;
54
55
// Hack: for typical launch scenario, ensure that close waits for actual process termination.
55
- browser . close = ( ) => browserApp . close ( ) ;
56
- ( browser as any ) [ '__app__ ' ] = browserApp ;
56
+ browser . close = ( ) => browserServer . close ( ) ;
57
+ ( browser as any ) [ '__server__ ' ] = browserServer ;
57
58
return browser ;
58
59
}
59
60
60
- async launchBrowserApp ( options ?: LaunchOptions ) : Promise < BrowserApp > {
61
- return ( await this . _launchBrowserApp ( options , true ) ) . browserApp ;
61
+ async launchServer ( options ?: LaunchOptions & { port ?: number } ) : Promise < BrowserServer > {
62
+ return ( await this . _launchServer ( options , 'server' , undefined , options && options . port ) ) . browserServer ;
62
63
}
63
64
64
- async _launchBrowserApp ( options : LaunchOptions = { } , isServer : boolean ) : Promise < { browserApp : BrowserApp , transport ?: ConnectionTransport } > {
65
+ async launchPersistent ( options ?: LaunchOptions & { userDataDir ?: string } ) : Promise < BrowserContext > {
66
+ const { browserServer, transport } = await this . _launchServer ( options , 'persistent' , options && options . userDataDir ) ;
67
+ const browser = await CRBrowser . connect ( transport ! ) ;
68
+ // Hack: for typical launch scenario, ensure that close waits for actual process termination.
69
+ const browserContext = browser . _defaultContext ;
70
+ browserContext . close = ( ) => browserServer . close ( ) ;
71
+ return browserContext ;
72
+ }
73
+
74
+ private async _launchServer ( options : LaunchOptions = { } , launchType : LaunchType , userDataDir ?: string , port ?: number ) : Promise < { browserServer : BrowserServer , transport ?: ConnectionTransport } > {
65
75
const {
66
76
ignoreDefaultArgs = false ,
67
77
args = [ ] ,
@@ -82,14 +92,19 @@ export class Chromium implements BrowserType {
82
92
else
83
93
chromeArguments . push ( ...args ) ;
84
94
85
- let temporaryUserDataDir : string | null = null ;
95
+ const userDataDirArg = chromeArguments . find ( arg => arg . startsWith ( '--user-data-dir=' ) ) ;
96
+ if ( userDataDirArg )
97
+ throw new Error ( 'Pass userDataDir parameter instead of specifying --user-data-dir argument' ) ;
98
+ if ( chromeArguments . find ( arg => arg . startsWith ( '--remote-debugging-' ) ) )
99
+ throw new Error ( 'Can\' use --remote-debugging-* args. Playwright manages remote debugging connection itself' ) ;
86
100
87
- if ( ! chromeArguments . some ( argument => argument . startsWith ( '--remote-debugging-' ) ) )
88
- chromeArguments . push ( isServer ? '--remote-debugging-port=0' : '--remote-debugging-pipe' ) ;
89
- if ( ! chromeArguments . some ( arg => arg . startsWith ( '--user-data-dir' ) ) ) {
101
+ let temporaryUserDataDir : string | null = null ;
102
+ if ( ! userDataDir ) {
103
+ userDataDir = await mkdtempAsync ( CHROMIUM_PROFILE_PATH ) ;
90
104
temporaryUserDataDir = await mkdtempAsync ( CHROMIUM_PROFILE_PATH ) ;
91
- chromeArguments . push ( `--user-data-dir=${ temporaryUserDataDir } ` ) ;
92
105
}
106
+ chromeArguments . push ( `--user-data-dir=${ userDataDir } ` ) ;
107
+ chromeArguments . push ( launchType === 'server' ? `--remote-debugging-port=${ port || 0 } ` : '--remote-debugging-pipe' ) ;
93
108
94
109
let chromeExecutable = executablePath ;
95
110
if ( ! executablePath ) {
@@ -99,11 +114,7 @@ export class Chromium implements BrowserType {
99
114
chromeExecutable = executablePath ;
100
115
}
101
116
102
- const usePipe = chromeArguments . includes ( '--remote-debugging-pipe' ) ;
103
- if ( usePipe && isServer )
104
- throw new Error ( `Argument "--remote-debugging-pipe" is not compatible with the launchBrowserApp.` ) ;
105
-
106
- let browserApp : BrowserApp | undefined = undefined ;
117
+ let browserServer : BrowserServer | undefined = undefined ;
107
118
const { launchedProcess, gracefullyClose } = await launchProcess ( {
108
119
executablePath : chromeExecutable ! ,
109
120
args : chromeArguments ,
@@ -112,10 +123,10 @@ export class Chromium implements BrowserType {
112
123
handleSIGTERM,
113
124
handleSIGHUP,
114
125
dumpio,
115
- pipe : usePipe ,
126
+ pipe : launchType !== 'server' ,
116
127
tempDir : temporaryUserDataDir || undefined ,
117
128
attemptToGracefullyClose : async ( ) => {
118
- if ( ! browserApp )
129
+ if ( ! browserServer )
119
130
return Promise . reject ( ) ;
120
131
// We try to gracefully close to prevent crash reporting and core dumps.
121
132
// Note that it's fine to reuse the pipe transport, since
@@ -125,23 +136,23 @@ export class Chromium implements BrowserType {
125
136
t . send ( JSON . stringify ( message ) ) ;
126
137
} ,
127
138
onkill : ( exitCode , signal ) => {
128
- if ( browserApp )
129
- browserApp . emit ( Events . BrowserApp . Close , exitCode , signal ) ;
139
+ if ( browserServer )
140
+ browserServer . emit ( Events . BrowserServer . Close , exitCode , signal ) ;
130
141
} ,
131
142
} ) ;
132
143
133
144
let transport : ConnectionTransport | undefined ;
134
145
let browserWSEndpoint : string | null ;
135
- if ( isServer ) {
146
+ if ( launchType === 'server' ) {
136
147
const timeoutError = new TimeoutError ( `Timed out after ${ timeout } ms while trying to connect to Chromium! The only Chromium revision guaranteed to work is r${ this . _revision } ` ) ;
137
148
const match = await waitForLine ( launchedProcess , launchedProcess . stderr , / ^ D e v T o o l s l i s t e n i n g o n ( w s : \/ \/ .* ) $ / , timeout , timeoutError ) ;
138
149
browserWSEndpoint = match [ 1 ] ;
139
150
} else {
140
151
transport = new PipeTransport ( launchedProcess . stdio [ 3 ] as NodeJS . WritableStream , launchedProcess . stdio [ 4 ] as NodeJS . ReadableStream ) ;
141
152
browserWSEndpoint = null ;
142
153
}
143
- browserApp = new BrowserApp ( launchedProcess , gracefullyClose , browserWSEndpoint ) ;
144
- return { browserApp , transport } ;
154
+ browserServer = new BrowserServer ( launchedProcess , gracefullyClose , browserWSEndpoint ) ;
155
+ return { browserServer , transport } ;
145
156
}
146
157
147
158
async connect ( options : ConnectOptions ) : Promise < CRBrowser > {
@@ -166,11 +177,8 @@ export class Chromium implements BrowserType {
166
177
devtools = false ,
167
178
headless = ! devtools ,
168
179
args = [ ] ,
169
- userDataDir = null
170
180
} = options ;
171
181
const chromeArguments = [ ...DEFAULT_ARGS ] ;
172
- if ( userDataDir )
173
- chromeArguments . push ( `--user-data-dir=${ userDataDir } ` ) ;
174
182
if ( devtools )
175
183
chromeArguments . push ( '--auto-open-devtools-for-tabs' ) ;
176
184
if ( headless ) {
0 commit comments