diff --git a/docs/api.md b/docs/api.md index 81198d0f4a086..13d723d3bb339 100644 --- a/docs/api.md +++ b/docs/api.md @@ -149,10 +149,10 @@ An example of launching a browser executable and connecting to a [Browser] later const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'. (async () => { - const browserApp = await webkit.launchBrowserApp({ webSocket: true }); - const connectOptions = browserApp.connectOptions(); - // Use connect options later to establish a connection. - const browser = await webkit.connect(connectOptions); + const browserApp = await webkit.launchBrowserApp(); + const wsEndpoint = browserApp.wsEndpoint(); + // Use web socket endpoint later to establish a connection. + const browser = await webkit.connect({ wsEndpoint }); // Close browser instance. await browserApp.close(); })(); @@ -3413,7 +3413,6 @@ If the function passed to the `worker.evaluateHandle` returns a [Promise], then - [event: 'close'](#event-close-2) - [browserApp.close()](#browserappclose) -- [browserApp.connectOptions()](#browserappconnectoptions) - [browserApp.kill()](#browserappkill) - [browserApp.process()](#browserappprocess) - [browserApp.wsEndpoint()](#browserappwsendpoint) @@ -3428,14 +3427,6 @@ Emitted when the browser app closes. Closes the browser gracefully and makes sure the process is terminated. -#### browserApp.connectOptions() -- returns: <[Object]> - - `browserWSEndpoint` a browser websocket endpoint to connect to. - - `slowMo` <[number]> - - `transport` <[ConnectionTransport]> **Experimental** A custom transport object which should be used to connect. - -This options object can be passed to [browserType.connect(options)](#browsertypeconnectoptions) to establish connection to the browser. - #### browserApp.kill() Kills the browser process. @@ -3444,10 +3435,9 @@ Kills the browser process. - returns: Spawned browser application process. #### browserApp.wsEndpoint() -- returns: Browser websocket url. - -Browser websocket endpoint which can be used as an argument to [browserType.connect(options)] to establish connection to the browser. Requires browser app to be launched with `browserType.launchBrowserApp({ webSocket: true, ... })`. +- returns: <[string]> Browser websocket url. +Browser websocket endpoint which can be used as an argument to [browserType.connect(options)](#browsertypeconnectoptions) to establish connection to the browser. ### class: BrowserType @@ -3478,10 +3468,8 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'. #### browserType.connect(options) - `options` <[Object]> - - `browserWSEndpoint` A browser websocket endpoint to connect to. + - `wsEndpoint` A browser websocket endpoint to connect to. - `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. - - `browserURL` **Chromium-only** A browser url to connect to, in format `http://${host}:${port}`. Use interchangeably with `browserWSEndpoint` to let Playwright fetch it from [metadata endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target). - - `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use. - returns: <[Promise]<[Browser]>> This methods attaches Playwright to an existing browser instance. @@ -3547,7 +3535,6 @@ try { - `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields: - `headless` <[boolean]> Whether to run browser in headless mode. More details for [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the `devtools` option is `true`. - `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only [guaranteed to work](https://github.com/Microsoft/playwright/#q-why-doesnt-playwright-vxxx-work-with-chromium-vyyy) with the bundled Chromium, Firefox or WebKit, use at your own risk. - - `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. - `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/). - `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use [`browserType.defaultArgs()`](#browsertypedefaultargsoptions). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`. - `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`. @@ -3557,7 +3544,6 @@ try { - `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`. - `userDataDir` <[string]> Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile). - `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`. - - `webSocket` <[boolean]> Connects to the browser over a WebSocket instead of a pipe. Defaults to `false`. - `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`. - returns: <[Promise]<[Browser]>> Promise which resolves to browser instance. @@ -3591,7 +3577,6 @@ const browser = await chromium.launch({ // Or 'firefox' or 'webkit'. - `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`. - `userDataDir` <[string]> Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile). - `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`. - - `webSocket` <[boolean]> Connects to the browser over a WebSocket instead of a pipe. Defaults to `false`. - `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`. - returns: <[Promise]<[BrowserApp]>> Promise which resolves to the browser app instance. @@ -3898,7 +3883,6 @@ const { chromium } = require('playwright'); [ChromiumBrowser]: #class-chromiumbrowser "ChromiumBrowser" [ChromiumSession]: #class-chromiumsession "ChromiumSession" [ChromiumTarget]: #class-chromiumtarget "ChromiumTarget" -[ConnectionTransport]: ../lib/WebSocketTransport.js "ConnectionTransport" [ConsoleMessage]: #class-consolemessage "ConsoleMessage" [Coverage]: #class-coverage "Coverage" [Dialog]: #class-dialog "Dialog" diff --git a/src/browser.ts b/src/browser.ts index f7d32ed1b2514..31328a15409b9 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -15,9 +15,7 @@ */ import { BrowserContext, BrowserContextOptions } from './browserContext'; -import { ConnectionTransport, SlowMoTransport } from './transport'; import * as platform from './platform'; -import { assert } from './helper'; export interface Browser extends platform.EventEmitterType { newContext(options?: BrowserContextOptions): Promise; @@ -31,16 +29,5 @@ export interface Browser extends platform.EventEmitterType { export type ConnectOptions = { slowMo?: number, - browserWSEndpoint?: string; - transport?: ConnectionTransport; + wsEndpoint: string }; - -export async function createTransport(options: ConnectOptions): Promise { - assert(Number(!!options.browserWSEndpoint) + Number(!!options.transport) === 1, 'Exactly one of browserWSEndpoint or transport must be passed to connect'); - let transport: ConnectionTransport | undefined; - if (options.transport) - transport = options.transport; - else if (options.browserWSEndpoint) - transport = await platform.createWebSocketTransport(options.browserWSEndpoint); - return SlowMoTransport.wrap(transport!, options.slowMo); -} diff --git a/src/chromium/crBrowser.ts b/src/chromium/crBrowser.ts index 97034c165e274..bce9febac967a 100644 --- a/src/chromium/crBrowser.ts +++ b/src/chromium/crBrowser.ts @@ -24,11 +24,12 @@ import { Page, Worker } from '../page'; import { CRTarget } from './crTarget'; import { Protocol } from './protocol'; import { CRPage } from './crPage'; -import { Browser, createTransport, ConnectOptions } from '../browser'; +import { Browser } from '../browser'; import * as network from '../network'; import * as types from '../types'; import * as platform from '../platform'; import { readProtocolStream } from './crProtocolHelper'; +import { ConnectionTransport, SlowMoTransport } from '../transport'; export class CRBrowser extends platform.EventEmitter implements Browser { _connection: CRConnection; @@ -41,9 +42,8 @@ export class CRBrowser extends platform.EventEmitter implements Browser { private _tracingPath: string | null = ''; private _tracingClient: CRSession | undefined; - static async connect(options: ConnectOptions): Promise { - const transport = await createTransport(options); - const connection = new CRConnection(transport); + static async connect(transport: ConnectionTransport, slowMo?: number): Promise { + const connection = new CRConnection(SlowMoTransport.wrap(transport, slowMo)); const { browserContextIds } = await connection.rootSession.send('Target.getBrowserContexts'); const browser = new CRBrowser(connection, browserContextIds); await connection.rootSession.send('Target.setDiscoverTargets', { discover: true }); diff --git a/src/firefox/ffBrowser.ts b/src/firefox/ffBrowser.ts index 35f0b3348b46f..70d2843558b57 100644 --- a/src/firefox/ffBrowser.ts +++ b/src/firefox/ffBrowser.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Browser, createTransport, ConnectOptions } from '../browser'; +import { Browser } from '../browser'; import { BrowserContext, BrowserContextOptions } from '../browserContext'; import { Events } from '../events'; import { assert, helper, RegisteredListener } from '../helper'; @@ -26,6 +26,7 @@ import { ConnectionEvents, FFConnection, FFSessionEvents } from './ffConnection' import { FFPage } from './ffPage'; import * as platform from '../platform'; import { Protocol } from './protocol'; +import { ConnectionTransport, SlowMoTransport } from '../transport'; export class FFBrowser extends platform.EventEmitter implements Browser { _connection: FFConnection; @@ -34,10 +35,9 @@ export class FFBrowser extends platform.EventEmitter implements Browser { private _contexts: Map; private _eventListeners: RegisteredListener[]; - static async connect(options: ConnectOptions): Promise { - const transport = await createTransport(options); - const connection = new FFConnection(transport); - const {browserContextIds} = await connection.send('Target.getBrowserContexts'); + static async connect(transport: ConnectionTransport, slowMo?: number): Promise { + const connection = new FFConnection(SlowMoTransport.wrap(transport, slowMo)); + const { browserContextIds } = await connection.send('Target.getBrowserContexts'); const browser = new FFBrowser(connection, browserContextIds); await connection.send('Target.enable'); await browser._waitForTarget(t => t.type() === 'page'); diff --git a/src/server/browserApp.ts b/src/server/browserApp.ts index b2fe486fa5361..0d8528038745a 100644 --- a/src/server/browserApp.ts +++ b/src/server/browserApp.ts @@ -15,19 +15,18 @@ */ import { ChildProcess, execSync } from 'child_process'; -import { ConnectOptions } from '../browser'; import * as platform from '../platform'; export class BrowserApp extends platform.EventEmitter { private _process: ChildProcess; private _gracefullyClose: () => Promise; - private _connectOptions: ConnectOptions; + private _browserWSEndpoint: string | null = null; - constructor(process: ChildProcess, gracefullyClose: () => Promise, connectOptions: ConnectOptions) { + constructor(process: ChildProcess, gracefullyClose: () => Promise, wsEndpoint: string | null) { super(); this._process = process; this._gracefullyClose = gracefullyClose; - this._connectOptions = connectOptions; + this._browserWSEndpoint = wsEndpoint; } process(): ChildProcess { @@ -35,11 +34,7 @@ export class BrowserApp extends platform.EventEmitter { } wsEndpoint(): string | null { - return this._connectOptions.browserWSEndpoint || null; - } - - connectOptions(): ConnectOptions { - return this._connectOptions; + return this._browserWSEndpoint; } kill() { diff --git a/src/server/browserType.ts b/src/server/browserType.ts index 5a062c6b0617f..48e4776ea4ef5 100644 --- a/src/server/browserType.ts +++ b/src/server/browserType.ts @@ -34,18 +34,16 @@ export type LaunchOptions = BrowserArgOptions & { handleSIGHUP?: boolean, timeout?: number, dumpio?: boolean, - env?: {[key: string]: string} | undefined, - webSocket?: boolean, - slowMo?: number, // TODO: we probably don't want this in launchBrowserApp. + env?: {[key: string]: string} | undefined }; export interface BrowserType { executablePath(): string; name(): string; launchBrowserApp(options?: LaunchOptions): Promise; - launch(options?: LaunchOptions): Promise; + launch(options?: LaunchOptions & { slowMo?: number }): Promise; defaultArgs(options?: BrowserArgOptions): string[]; - connect(options: ConnectOptions & { browserURL?: string }): Promise; + connect(options: ConnectOptions): Promise; devices: types.Devices; errors: { TimeoutError: typeof TimeoutError }; } diff --git a/src/server/chromium.ts b/src/server/chromium.ts index 9d40da90b6630..c1a635a18bf6c 100644 --- a/src/server/chromium.ts +++ b/src/server/chromium.ts @@ -30,9 +30,10 @@ import { launchProcess, waitForLine } from '../server/processLauncher'; import { kBrowserCloseMessageId } from '../chromium/crConnection'; import { PipeTransport } from './pipeTransport'; import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType'; -import { createTransport, ConnectOptions } from '../browser'; +import { ConnectOptions } from '../browser'; import { BrowserApp } from './browserApp'; import { Events } from '../events'; +import { ConnectionTransport } from '../transport'; export class Chromium implements BrowserType { private _projectRoot: string; @@ -47,26 +48,29 @@ export class Chromium implements BrowserType { return 'chromium'; } - async launch(options?: LaunchOptions): Promise { - const app = await this.launchBrowserApp(options); - const browser = await CRBrowser.connect(app.connectOptions()); + async launch(options?: LaunchOptions & { slowMo?: number }): Promise { + const { browserApp, transport } = await this._launchBrowserApp(options, false); + const browser = await CRBrowser.connect(transport!, options && options.slowMo); // Hack: for typical launch scenario, ensure that close waits for actual process termination. - browser.close = () => app.close(); + browser.close = () => browserApp.close(); + (browser as any)['__app__'] = browserApp; return browser; } - async launchBrowserApp(options: LaunchOptions = {}): Promise { + async launchBrowserApp(options?: LaunchOptions): Promise { + return (await this._launchBrowserApp(options, true)).browserApp; + } + + async _launchBrowserApp(options: LaunchOptions = {}, isServer: boolean): Promise<{ browserApp: BrowserApp, transport?: ConnectionTransport }> { const { ignoreDefaultArgs = false, args = [], dumpio = false, executablePath = null, - webSocket = false, env = process.env, handleSIGINT = true, handleSIGTERM = true, handleSIGHUP = true, - slowMo = 0, timeout = 30000 } = options; @@ -81,7 +85,7 @@ export class Chromium implements BrowserType { let temporaryUserDataDir: string | null = null; if (!chromeArguments.some(argument => argument.startsWith('--remote-debugging-'))) - chromeArguments.push(webSocket ? '--remote-debugging-port=0' : '--remote-debugging-pipe'); + chromeArguments.push(isServer ? '--remote-debugging-port=0' : '--remote-debugging-pipe'); if (!chromeArguments.some(arg => arg.startsWith('--user-data-dir'))) { temporaryUserDataDir = await mkdtempAsync(CHROMIUM_PROFILE_PATH); chromeArguments.push(`--user-data-dir=${temporaryUserDataDir}`); @@ -96,8 +100,8 @@ export class Chromium implements BrowserType { } const usePipe = chromeArguments.includes('--remote-debugging-pipe'); - if (usePipe && webSocket) - throw new Error(`Argument "--remote-debugging-pipe" is not compatible with "webSocket" launch option.`); + if (usePipe && isServer) + throw new Error(`Argument "--remote-debugging-pipe" is not compatible with the launchBrowserApp.`); let browserApp: BrowserApp | undefined = undefined; const { launchedProcess, gracefullyClose } = await launchProcess({ @@ -116,9 +120,9 @@ export class Chromium implements BrowserType { // We try to gracefully close to prevent crash reporting and core dumps. // Note that it's fine to reuse the pipe transport, since // our connection ignores kBrowserCloseMessageId. - const transport = await createTransport(browserApp.connectOptions()); + const t = transport || await platform.createWebSocketTransport(browserWSEndpoint!); const message = { method: 'Browser.close', id: kBrowserCloseMessageId }; - transport.send(JSON.stringify(message)); + t.send(JSON.stringify(message)); }, onkill: (exitCode, signal) => { if (browserApp) @@ -126,37 +130,23 @@ export class Chromium implements BrowserType { }, }); - let connectOptions: ConnectOptions; - if (!usePipe) { + let transport: ConnectionTransport | undefined; + let browserWSEndpoint: string | null; + if (isServer) { 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}`); const match = await waitForLine(launchedProcess, launchedProcess.stderr, /^DevTools listening on (ws:\/\/.*)$/, timeout, timeoutError); - const browserWSEndpoint = match[1]; - connectOptions = { browserWSEndpoint, slowMo }; + browserWSEndpoint = match[1]; } else { - const transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream); - connectOptions = { slowMo, transport }; + transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream); + browserWSEndpoint = null; } - browserApp = new BrowserApp(launchedProcess, gracefullyClose, connectOptions); - return browserApp; + browserApp = new BrowserApp(launchedProcess, gracefullyClose, browserWSEndpoint); + return { browserApp, transport }; } - async connect(options: ConnectOptions & { browserURL?: string }): Promise { - if (options.transport && options.transport.onmessage) - throw new Error('Transport is already in use'); - if (options.browserURL) { - assert(!options.browserWSEndpoint && !options.transport, 'Exactly one of browserWSEndpoint, browserURL or transport must be passed to connect'); - let connectionURL: string; - try { - const data = await platform.fetchUrl(new URL('/json/version', options.browserURL).href); - connectionURL = JSON.parse(data).webSocketDebuggerUrl; - } catch (e) { - e.message = `Failed to fetch browser webSocket url from ${options.browserURL}: ` + e.message; - throw e; - } - const transport = await platform.createWebSocketTransport(connectionURL); - options = { ...options, transport }; - } - return CRBrowser.connect(options); + async connect(options: ConnectOptions): Promise { + const transport = await platform.createWebSocketTransport(options.wsEndpoint); + return CRBrowser.connect(transport, options.slowMo); } executablePath(): string { diff --git a/src/server/firefox.ts b/src/server/firefox.ts index d349ccf6ee84d..e5151a2ed8dd4 100644 --- a/src/server/firefox.ts +++ b/src/server/firefox.ts @@ -29,9 +29,10 @@ import * as util from 'util'; import { TimeoutError } from '../errors'; import { assert } from '../helper'; import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType'; -import { createTransport, ConnectOptions } from '../browser'; +import { ConnectOptions } from '../browser'; import { BrowserApp } from './browserApp'; import { Events } from '../events'; +import { ConnectionTransport } from '../transport'; export class Firefox implements BrowserType { private _projectRoot: string; @@ -46,15 +47,20 @@ export class Firefox implements BrowserType { return 'firefox'; } - async launch(options?: LaunchOptions): Promise { - const app = await this.launchBrowserApp(options); - const browser = await FFBrowser.connect(app.connectOptions()); + async launch(options?: LaunchOptions & { slowMo?: number }): Promise { + const { browserApp, transport } = await this._launchBrowserApp(options, false); + const browser = await FFBrowser.connect(transport!, options && options.slowMo); // Hack: for typical launch scenario, ensure that close waits for actual process termination. - browser.close = () => app.close(); + browser.close = () => browserApp.close(); + (browser as any)['__app__'] = browserApp; return browser; } - async launchBrowserApp(options: LaunchOptions = {}): Promise { + async launchBrowserApp(options?: LaunchOptions): Promise { + return (await this._launchBrowserApp(options, true)).browserApp; + } + + private async _launchBrowserApp(options: LaunchOptions = {}, isServer: boolean): Promise<{ browserApp: BrowserApp, transport?: ConnectionTransport }> { const { ignoreDefaultArgs = false, args = [], @@ -64,9 +70,7 @@ export class Firefox implements BrowserType { handleSIGHUP = true, handleSIGINT = true, handleSIGTERM = true, - slowMo = 0, timeout = 30000, - webSocket = false, } = options; const firefoxArguments = []; @@ -115,7 +119,7 @@ export class Firefox implements BrowserType { // We try to gracefully close to prevent crash reporting and core dumps. // Note that it's fine to reuse the pipe transport, since // our connection ignores kBrowserCloseMessageId. - const transport = await createTransport(browserApp.connectOptions()); + const transport = await platform.createWebSocketTransport(browserWSEndpoint); const message = { method: 'Browser.close', params: {}, id: kBrowserCloseMessageId }; transport.send(JSON.stringify(message)); }, @@ -128,23 +132,13 @@ export class Firefox implements BrowserType { const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Firefox!`); const match = await waitForLine(launchedProcess, launchedProcess.stdout, /^Juggler listening on (ws:\/\/.*)$/, timeout, timeoutError); const browserWSEndpoint = match[1]; - let connectOptions: ConnectOptions; - if (webSocket) { - connectOptions = { browserWSEndpoint, slowMo }; - } else { - const transport = await platform.createWebSocketTransport(browserWSEndpoint); - connectOptions = { transport, slowMo }; - } - browserApp = new BrowserApp(launchedProcess, gracefullyClose, connectOptions); - return browserApp; + browserApp = new BrowserApp(launchedProcess, gracefullyClose, isServer ? browserWSEndpoint : null); + return { browserApp, transport: isServer ? undefined : await platform.createWebSocketTransport(browserWSEndpoint) }; } - async connect(options: ConnectOptions & { browserURL?: string }): Promise { - if (options.browserURL) - throw new Error('Option "browserURL" is not supported by Firefox'); - if (options.transport && options.transport.onmessage) - throw new Error('Transport is already in use'); - return FFBrowser.connect(options); + async connect(options: ConnectOptions): Promise { + const transport = await platform.createWebSocketTransport(options.wsEndpoint); + return FFBrowser.connect(transport, options.slowMo); } executablePath(): string { diff --git a/src/server/webkit.ts b/src/server/webkit.ts index ecae8464e7647..463ce0c79ae75 100644 --- a/src/server/webkit.ts +++ b/src/server/webkit.ts @@ -51,15 +51,20 @@ export class WebKit implements BrowserType { return 'webkit'; } - async launch(options?: LaunchOptions): Promise { - const app = await this.launchBrowserApp(options); - const browser = await WKBrowser.connect(app.connectOptions()); + async launch(options?: LaunchOptions & { slowMo?: number }): Promise { + const { browserApp, transport } = await this._launchBrowserApp(options, false); + const browser = await WKBrowser.connect(transport!, options && options.slowMo); // Hack: for typical launch scenario, ensure that close waits for actual process termination. - browser.close = () => app.close(); + browser.close = () => browserApp.close(); + (browser as any)['__app__'] = browserApp; return browser; } - async launchBrowserApp(options: LaunchOptions = {}): Promise { + async launchBrowserApp(options?: LaunchOptions): Promise { + return (await this._launchBrowserApp(options, true)).browserApp; + } + + private async _launchBrowserApp(options: LaunchOptions = {}, isServer: boolean): Promise<{ browserApp: BrowserApp, transport?: ConnectionTransport }> { const { ignoreDefaultArgs = false, args = [], @@ -69,8 +74,6 @@ export class WebKit implements BrowserType { handleSIGINT = true, handleSIGTERM = true, handleSIGHUP = true, - slowMo = 0, - webSocket = false, } = options; const webkitArguments = []; @@ -128,24 +131,13 @@ export class WebKit implements BrowserType { }); transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream); - - let connectOptions: ConnectOptions; - if (webSocket) { - const browserWSEndpoint = wrapTransportWithWebSocket(transport); - connectOptions = { browserWSEndpoint, slowMo }; - } else { - connectOptions = { transport, slowMo }; - } - browserApp = new BrowserApp(launchedProcess, gracefullyClose, connectOptions); - return browserApp; + browserApp = new BrowserApp(launchedProcess, gracefullyClose, isServer ? wrapTransportWithWebSocket(transport) : null); + return { browserApp, transport }; } - async connect(options: ConnectOptions & { browserURL?: string }): Promise { - if (options.browserURL) - throw new Error('Option "browserURL" is not supported by Firefox'); - if (options.transport && options.transport.onmessage) - throw new Error('Transport is already in use'); - return WKBrowser.connect(options); + async connect(options: ConnectOptions): Promise { + const transport = await platform.createWebSocketTransport(options.wsEndpoint); + return WKBrowser.connect(transport, options.slowMo); } executablePath(): string { diff --git a/src/web.ts b/src/web.ts index 8ea52344a6c17..b2d78e37aadc3 100644 --- a/src/web.ts +++ b/src/web.ts @@ -17,10 +17,26 @@ import { CRBrowser as ChromiumBrowser } from './chromium/crBrowser'; import { FFBrowser as FirefoxBrowser } from './firefox/ffBrowser'; import { WKBrowser as WebKitBrowser } from './webkit/wkBrowser'; +import * as platform from './platform'; const connect = { - chromium: { connect: ChromiumBrowser.connect }, - firefox: { connect: FirefoxBrowser.connect }, - webkit: { connect: WebKitBrowser.connect }, + chromium: { + connect: async (url: string) => { + const transport = await platform.createWebSocketTransport(url); + return ChromiumBrowser.connect(transport); + } + }, + webkit: { + connect: async (url: string) => { + const transport = await platform.createWebSocketTransport(url); + return WebKitBrowser.connect(transport); + } + }, + firefox: { + connect: async (url: string) => { + const transport = await platform.createWebSocketTransport(url); + return FirefoxBrowser.connect(transport); + } + } }; export = connect; diff --git a/src/webkit/wkBrowser.ts b/src/webkit/wkBrowser.ts index d49f9b56a46e5..47914debd395a 100644 --- a/src/webkit/wkBrowser.ts +++ b/src/webkit/wkBrowser.ts @@ -15,12 +15,12 @@ * limitations under the License. */ -import { Browser, createTransport, ConnectOptions } from '../browser'; +import { Browser } from '../browser'; import { BrowserContext, BrowserContextOptions } from '../browserContext'; import { assert, helper, RegisteredListener } from '../helper'; import * as network from '../network'; import { Page } from '../page'; -import { ConnectionTransport } from '../transport'; +import { ConnectionTransport, SlowMoTransport } from '../transport'; import * as types from '../types'; import { Events } from '../events'; import { Protocol } from './protocol'; @@ -41,9 +41,8 @@ export class WKBrowser extends platform.EventEmitter implements Browser { private _firstPageProxyCallback?: () => void; private readonly _firstPageProxyPromise: Promise; - static async connect(options: ConnectOptions): Promise { - const transport = await createTransport(options); - const browser = new WKBrowser(transport); + static async connect(transport: ConnectionTransport, slowMo: number = 0): Promise { + const browser = new WKBrowser(SlowMoTransport.wrap(transport, slowMo)); // TODO: figure out the timeout. await browser._waitForFirstPageTarget(30000); return browser; diff --git a/test/assets/playwrightweb.html b/test/assets/playwrightweb.html index a81814a87c47e..1946dfac200d0 100644 --- a/test/assets/playwrightweb.html +++ b/test/assets/playwrightweb.html @@ -1,7 +1,7 @@