diff --git a/packages/playwright-core/src/androidServerImpl.ts b/packages/playwright-core/src/androidServerImpl.ts index 9e89d7c83a..82a02856e2 100644 --- a/packages/playwright-core/src/androidServerImpl.ts +++ b/packages/playwright-core/src/androidServerImpl.ts @@ -49,7 +49,7 @@ export class AndroidServerLauncherImpl { const path = options.wsPath ? (options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}`) : `/${createGuid()}`; // 2. Start the server - const server = new PlaywrightServer({ path, maxConnections: 1, preLaunchedAndroidDevice: device }); + const server = new PlaywrightServer({ mode: 'launchServer', path, maxConnections: 1, preLaunchedAndroidDevice: device }); const wsEndpoint = await server.listen(options.port); // 3. Return the BrowserServer interface diff --git a/packages/playwright-core/src/browserServerImpl.ts b/packages/playwright-core/src/browserServerImpl.ts index 0d6ae54702..123d10b020 100644 --- a/packages/playwright-core/src/browserServerImpl.ts +++ b/packages/playwright-core/src/browserServerImpl.ts @@ -57,7 +57,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher { const path = options.wsPath ? (options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}`) : `/${createGuid()}`; // 2. Start the server - const server = new PlaywrightServer({ path, maxConnections: Infinity, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy }); + const server = new PlaywrightServer({ mode: 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy }); const wsEndpoint = await server.listen(options.port); // 3. Return the BrowserServer interface diff --git a/packages/playwright-core/src/cli/driver.ts b/packages/playwright-core/src/cli/driver.ts index 0c21e33c6b..3db878c0bc 100644 --- a/packages/playwright-core/src/cli/driver.ts +++ b/packages/playwright-core/src/cli/driver.ts @@ -54,6 +54,7 @@ export function runDriver() { export type RunServerOptions = { port?: number, path?: string, + extension?: boolean, maxConnections?: number, browserProxyMode?: 'client' | 'tether', ownedByTetherClient?: boolean, @@ -64,8 +65,9 @@ export async function runServer(options: RunServerOptions) { port, path = '/', maxConnections = Infinity, + extension, } = options; - const server = new PlaywrightServer({ path, maxConnections }); + const server = new PlaywrightServer({ mode: extension ? 'extension' : 'default', path, maxConnections }); const wsEndpoint = await server.listen(port); process.on('exit', () => server.close().catch(console.error)); console.log('Listening on ' + wsEndpoint); // eslint-disable-line no-console diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index c84241b45e..3726810c72 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -246,11 +246,13 @@ program .option('--port ', 'Server port') .option('--path ', 'Endpoint Path', '/') .option('--max-clients ', 'Maximum clients') + .option('--mode ', 'Server mode, either "default" or "extension"') .action(function(options) { runServer({ port: options.port ? +options.port : undefined, path: options.path, maxConnections: options.maxClients ? +options.maxClients : Infinity, + extension: options.mode === 'extension' || !!process.env.PW_EXTENSION_MODE, }).catch(logErrorAndExit); }); diff --git a/packages/playwright-core/src/remote/playwrightConnection.ts b/packages/playwright-core/src/remote/playwrightConnection.ts index 73cc651f53..2a3138eef5 100644 --- a/packages/playwright-core/src/remote/playwrightConnection.ts +++ b/packages/playwright-core/src/remote/playwrightConnection.ts @@ -28,7 +28,7 @@ import { startProfiling, stopProfiling } from '../utils'; import { monotonicTime } from '../utils'; import { debugLogger } from '../common/debugLogger'; -export type ClientType = 'controller' | 'playwright' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android'; +export type ClientType = 'controller' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android'; type Options = { socksProxyPattern: string | undefined, @@ -102,24 +102,10 @@ export class PlaywrightConnection { return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope) : await this._initPreLaunchedAndroidMode(scope); if (clientType === 'launch-browser') return await this._initLaunchBrowserMode(scope); - if (clientType === 'playwright') - return await this._initPlaywrightConnectMode(scope); throw new Error('Unsupported client type: ' + clientType); }); } - private async _initPlaywrightConnectMode(scope: RootDispatcher) { - debugLogger.log('server', `[${this._id}] engaged playwright.connect mode`); - const playwright = createPlaywright('javascript'); - // Close all launched browsers on disconnect. - this._cleanups.push(async () => { - await Promise.all(playwright.allBrowsers().map(browser => browser.close())); - }); - - const ownedSocksProxy = await this._createOwnedSocksProxy(playwright); - return new PlaywrightDispatcher(scope, playwright, ownedSocksProxy); - } - private async _initLaunchBrowserMode(scope: RootDispatcher) { debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`); const playwright = createPlaywright('javascript'); diff --git a/packages/playwright-core/src/remote/playwrightServer.ts b/packages/playwright-core/src/remote/playwrightServer.ts index e359494883..b2a22170e9 100644 --- a/packages/playwright-core/src/remote/playwrightServer.ts +++ b/packages/playwright-core/src/remote/playwrightServer.ts @@ -34,6 +34,7 @@ const kConnectionSymbol = Symbol('kConnection'); type ServerOptions = { path: string; maxConnections: number; + mode: 'default' | 'launchServer' | 'extension'; preLaunchedBrowser?: Browser; preLaunchedAndroidDevice?: AndroidDevice; preLaunchedSocksProxy?: SocksProxy; @@ -97,38 +98,35 @@ export class PlaywrightServer { const proxyValue = url.searchParams.get('proxy') || (Array.isArray(proxyHeader) ? proxyHeader[0] : proxyHeader); const launchOptionsHeader = request.headers['x-playwright-launch-options'] || ''; + const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader; + const launchOptionsParam = url.searchParams.get('launch-options'); let launchOptions: LaunchOptions = {}; try { - launchOptions = JSON.parse(Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader); + launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue); } catch (e) { } const id = String(++lastConnectionId); debugLogger.log('server', `[${id}] serving connection: ${request.url}`); - const isDebugControllerClient = !!request.headers['x-playwright-debug-controller']; - const shouldReuseBrowser = !!request.headers['x-playwright-reuse-context']; - // If we started in the legacy reuse-browser mode, create this._preLaunchedPlaywright. - // If we get a debug-controller request, create this._preLaunchedPlaywright. - if (isDebugControllerClient || shouldReuseBrowser) { + // Instantiate playwright for the extension modes. + const isExtension = this._options.mode === 'extension'; + if (isExtension) { if (!this._preLaunchedPlaywright) this._preLaunchedPlaywright = createPlaywright('javascript'); } - let clientType: ClientType = 'playwright'; + let clientType: ClientType = 'launch-browser'; let semaphore: Semaphore = browserSemaphore; - if (isDebugControllerClient) { + if (isExtension && url.searchParams.has('debug-controller')) { clientType = 'controller'; semaphore = controllerSemaphore; - } else if (shouldReuseBrowser) { + } else if (isExtension) { clientType = 'reuse-browser'; semaphore = reuseBrowserSemaphore; - } else if (this._options.preLaunchedBrowser || this._options.preLaunchedAndroidDevice) { + } else if (this._options.mode === 'launchServer') { clientType = 'pre-launched-browser-or-android'; semaphore = browserSemaphore; - } else if (browserName) { - clientType = 'launch-browser'; - semaphore = browserSemaphore; } const connection = new PlaywrightConnection( diff --git a/packages/playwright-test/src/runner/watchMode.ts b/packages/playwright-test/src/runner/watchMode.ts index c48122a227..48ba73f8a6 100644 --- a/packages/playwright-test/src/runner/watchMode.ts +++ b/packages/playwright-test/src/runner/watchMode.ts @@ -409,7 +409,7 @@ ${colors.dim('Waiting for file changes. Press')} ${colors.bold('enter')} ${color async function toggleShowBrowser(config: FullConfigInternal, originalWorkers: number) { if (!showBrowserServer) { config.config.workers = 1; - showBrowserServer = new PlaywrightServer({ path: '/' + createGuid(), maxConnections: 1 }); + showBrowserServer = new PlaywrightServer({ mode: 'extension', path: '/' + createGuid(), maxConnections: 1 }); const wsEndpoint = await showBrowserServer.listen(); process.env.PW_TEST_REUSE_CONTEXT = '1'; process.env.PW_TEST_CONNECT_WS_ENDPOINT = wsEndpoint; diff --git a/tests/config/browserTest.ts b/tests/config/browserTest.ts index a7e8b0ec74..e5c389e35f 100644 --- a/tests/config/browserTest.ts +++ b/tests/config/browserTest.ts @@ -132,7 +132,7 @@ const test = baseTest.extend server = remoteServer; } else { const runServer = new RunServer(); - await runServer._start(childProcess); + await runServer.start(childProcess); server = runServer; } return server; diff --git a/tests/config/debugControllerBackend.ts b/tests/config/debugControllerBackend.ts index 50408aa814..f4a772da4c 100644 --- a/tests/config/debugControllerBackend.ts +++ b/tests/config/debugControllerBackend.ts @@ -115,9 +115,7 @@ export class Backend extends EventEmitter { } async connect(wsEndpoint: string) { - this._transport = await WebSocketTransport.connect(wsEndpoint, { - 'x-playwright-debug-controller': 'true' - }); + this._transport = await WebSocketTransport.connect(wsEndpoint + '?debug-controller'); this._transport.onmessage = (message: any) => { if (!message.id) { this.emit(message.method, message.params); diff --git a/tests/config/remoteServer.ts b/tests/config/remoteServer.ts index 51c181cdc1..2419c8bd50 100644 --- a/tests/config/remoteServer.ts +++ b/tests/config/remoteServer.ts @@ -27,9 +27,12 @@ export class RunServer implements PlaywrightServer { private _process: TestChildProcess; _wsEndpoint: string; - async _start(childProcess: CommonFixtures['childProcess']) { + async start(childProcess: CommonFixtures['childProcess'], mode?: 'extension' | 'default') { + const command = ['node', path.join(__dirname, '..', '..', 'packages', 'playwright-core', 'lib', 'cli', 'cli.js'), 'run-server']; + if (mode === 'extension') + command.push('--mode=extension'); this._process = childProcess({ - command: ['node', path.join(__dirname, '..', '..', 'packages', 'playwright-core', 'lib', 'cli', 'cli.js'), 'run-server'], + command, env: { ...process.env, PWTEST_UNDER_TEST: '1', diff --git a/tests/library/debug-controller.spec.ts b/tests/library/debug-controller.spec.ts index 91442a4d9d..85bdd09049 100644 --- a/tests/library/debug-controller.spec.ts +++ b/tests/library/debug-controller.spec.ts @@ -31,7 +31,7 @@ type Fixtures = { const test = baseTest.extend({ wsEndpoint: async ({ }, use) => { process.env.PW_DEBUG_CONTROLLER_HEADLESS = '1'; - const server = new PlaywrightServer({ path: '/' + createGuid(), maxConnections: Number.MAX_VALUE, enableSocksProxy: false }); + const server = new PlaywrightServer({ mode: 'extension', path: '/' + createGuid(), maxConnections: Number.MAX_VALUE, enableSocksProxy: false }); const wsEndpoint = await server.listen(); await use(wsEndpoint); await server.close(); diff --git a/tests/playwright-test/playwright.reuse.browser.spec.ts b/tests/playwright-test/playwright.reuse.browser.spec.ts index 37a970bd8b..b75bc88df8 100644 --- a/tests/playwright-test/playwright.reuse.browser.spec.ts +++ b/tests/playwright-test/playwright.reuse.browser.spec.ts @@ -23,7 +23,7 @@ const test = baseTest.extend<{ runServer: () => Promise }>({ let server: PlaywrightServer | undefined; await use(async () => { const runServer = new RunServer(); - await runServer._start(childProcess); + await runServer.start(childProcess, 'extension'); server = runServer; return server; });