chore: pull common functionality into the BrowserTypeBase (#2312)

This commit is contained in:
Pavel Feldman 2020-05-20 16:30:04 -07:00 committed by GitHub
parent aa0d844c76
commit f9b437a49e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 118 additions and 184 deletions

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { BrowserContext, BrowserContextOptions } from './browserContext'; import { BrowserContext, BrowserContextOptions, BrowserContextBase } from './browserContext';
import { Page } from './page'; import { Page } from './page';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { Download } from './download'; import { Download } from './download';
@ -44,6 +44,7 @@ export interface Browser extends EventEmitter {
export abstract class BrowserBase extends EventEmitter implements Browser, InnerLogger { export abstract class BrowserBase extends EventEmitter implements Browser, InnerLogger {
readonly _options: BrowserOptions; readonly _options: BrowserOptions;
private _downloads = new Map<string, Download>(); private _downloads = new Map<string, Download>();
_defaultContext: BrowserContextBase | null = null;
constructor(options: BrowserOptions) { constructor(options: BrowserOptions) {
super(); super();
@ -102,4 +103,3 @@ export abstract class BrowserBase extends EventEmitter implements Browser, Inner
} }
} }
export type LaunchType = 'local' | 'server' | 'persistent';

View file

@ -36,7 +36,6 @@ export class CRBrowser extends BrowserBase {
readonly _connection: CRConnection; readonly _connection: CRConnection;
_session: CRSession; _session: CRSession;
private _clientRootSessionPromise: Promise<CRSession> | null = null; private _clientRootSessionPromise: Promise<CRSession> | null = null;
readonly _defaultContext: CRBrowserContext | null = null;
readonly _contexts = new Map<string, CRBrowserContext>(); readonly _contexts = new Map<string, CRBrowserContext>();
_crPages = new Map<string, CRPage>(); _crPages = new Map<string, CRPage>();
_backgroundPages = new Map<string, CRPage>(); _backgroundPages = new Map<string, CRPage>();
@ -123,7 +122,7 @@ export class CRBrowser extends BrowserBase {
if (!context) { if (!context) {
// TODO: auto attach only to pages from our contexts. // TODO: auto attach only to pages from our contexts.
// assert(this._defaultContext); // assert(this._defaultContext);
context = this._defaultContext; context = this._defaultContext as CRBrowserContext;
} }
if (targetInfo.type === 'other' && targetInfo.url.startsWith('devtools://devtools') && this._devtools) { if (targetInfo.type === 'other' && targetInfo.url.startsWith('devtools://devtools') && this._devtools) {

View file

@ -31,7 +31,6 @@ import { Protocol } from './protocol';
export class FFBrowser extends BrowserBase { export class FFBrowser extends BrowserBase {
_connection: FFConnection; _connection: FFConnection;
readonly _ffPages: Map<string, FFPage>; readonly _ffPages: Map<string, FFPage>;
readonly _defaultContext: FFBrowserContext | null = null;
readonly _contexts: Map<string, FFBrowserContext>; readonly _contexts: Map<string, FFBrowserContext>;
private _eventListeners: RegisteredListener[]; private _eventListeners: RegisteredListener[];
@ -91,7 +90,7 @@ export class FFBrowser extends BrowserBase {
_onAttachedToTarget(payload: Protocol.Browser.attachedToTargetPayload) { _onAttachedToTarget(payload: Protocol.Browser.attachedToTargetPayload) {
const {targetId, browserContextId, openerId, type} = payload.targetInfo; const {targetId, browserContextId, openerId, type} = payload.targetInfo;
assert(type === 'page'); assert(type === 'page');
const context = browserContextId ? this._contexts.get(browserContextId)! : this._defaultContext; const context = browserContextId ? this._contexts.get(browserContextId)! : this._defaultContext as FFBrowserContext;
assert(context, `Unknown context id:${browserContextId}, _defaultContext: ${this._defaultContext}`); assert(context, `Unknown context id:${browserContextId}, _defaultContext: ${this._defaultContext}`);
const session = this._connection.createSession(payload.sessionId, type); const session = this._connection.createSession(payload.sessionId, type);
const opener = openerId ? this._ffPages.get(openerId)! : null; const opener = openerId ? this._ffPages.get(openerId)! : null;

View file

@ -19,7 +19,7 @@ import { EventEmitter } from 'events';
import { helper } from '../helper'; import { helper } from '../helper';
import { RootLogger } from '../logger'; import { RootLogger } from '../logger';
import { TimeoutSettings } from '../timeoutSettings'; import { TimeoutSettings } from '../timeoutSettings';
import { LaunchOptionsBase } from './browserType'; import { LaunchOptions } from './browserType';
export class WebSocketWrapper { export class WebSocketWrapper {
readonly wsEndpoint: string; readonly wsEndpoint: string;
@ -54,11 +54,11 @@ export class BrowserServer extends EventEmitter {
private _process: ChildProcess | undefined; private _process: ChildProcess | undefined;
private _gracefullyClose: (() => Promise<void>) | undefined; private _gracefullyClose: (() => Promise<void>) | undefined;
private _webSocketWrapper: WebSocketWrapper | null = null; private _webSocketWrapper: WebSocketWrapper | null = null;
private _launchOptions: LaunchOptionsBase; readonly _launchOptions: LaunchOptions;
readonly _logger: RootLogger; readonly _logger: RootLogger;
readonly _launchDeadline: number; readonly _launchDeadline: number;
constructor(options: LaunchOptionsBase) { constructor(options: LaunchOptions) {
super(); super();
this._launchOptions = options; this._launchOptions = options;
this._logger = new RootLogger(options.logger); this._logger = new RootLogger(options.logger);

View file

@ -17,7 +17,10 @@
import { BrowserContext } from '../browserContext'; import { BrowserContext } from '../browserContext';
import { BrowserServer } from './browserServer'; import { BrowserServer } from './browserServer';
import * as browserPaths from '../install/browserPaths'; import * as browserPaths from '../install/browserPaths';
import { Logger } from '../logger'; import { Logger, RootLogger } from '../logger';
import { ConnectionTransport, WebSocketTransport } from '../transport';
import { BrowserBase, BrowserOptions, Browser } from '../browser';
import { assert } from '../helper';
export type BrowserArgOptions = { export type BrowserArgOptions = {
headless?: boolean, headless?: boolean,
@ -25,7 +28,7 @@ export type BrowserArgOptions = {
devtools?: boolean, devtools?: boolean,
}; };
export type LaunchOptionsBase = BrowserArgOptions & { type LaunchOptionsBase = BrowserArgOptions & {
executablePath?: string, executablePath?: string,
ignoreDefaultArgs?: boolean | string[], ignoreDefaultArgs?: boolean | string[],
handleSIGINT?: boolean, handleSIGINT?: boolean,
@ -46,9 +49,11 @@ export type ConnectOptions = {
slowMo?: number, slowMo?: number,
logger?: Logger, logger?: Logger,
}; };
export type LaunchType = 'local' | 'server' | 'persistent';
export type LaunchOptions = LaunchOptionsBase & { slowMo?: number }; export type LaunchOptions = LaunchOptionsBase & { slowMo?: number };
export type LaunchServerOptions = LaunchOptionsBase & { port?: number }; export type LaunchServerOptions = LaunchOptionsBase & { port?: number };
export interface BrowserType<Browser> {
export interface BrowserType {
executablePath(): string; executablePath(): string;
name(): string; name(): string;
launch(options?: LaunchOptions): Promise<Browser>; launch(options?: LaunchOptions): Promise<Browser>;
@ -57,7 +62,7 @@ export interface BrowserType<Browser> {
connect(options: ConnectOptions): Promise<Browser>; connect(options: ConnectOptions): Promise<Browser>;
} }
export abstract class AbstractBrowserType<Browser> implements BrowserType<Browser> { export abstract class BrowserTypeBase implements BrowserType {
private _name: string; private _name: string;
private _executablePath: string | undefined; private _executablePath: string | undefined;
readonly _browserPath: string; readonly _browserPath: string;
@ -79,8 +84,44 @@ export abstract class AbstractBrowserType<Browser> implements BrowserType<Browse
return this._name; return this._name;
} }
abstract launch(options?: LaunchOptions): Promise<Browser>; async launch(options: LaunchOptions = {}): Promise<Browser> {
abstract launchServer(options?: LaunchServerOptions): Promise<BrowserServer>; assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
abstract launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise<BrowserContext>; const browserServer = new BrowserServer(options);
abstract connect(options: ConnectOptions): Promise<Browser>; const { transport, downloadsPath } = await this._launchServer(options, 'local', browserServer);
return await browserServer._initializeOrClose(async () => {
return this._connectToServer(browserServer, false, transport!, downloadsPath);
});
}
async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> {
const browserServer = new BrowserServer(options);
const { transport, downloadsPath } = await this._launchServer(options, 'persistent', browserServer, userDataDir);
return await browserServer._initializeOrClose(async () => {
const browser = await this._connectToServer(browserServer, true, transport!, downloadsPath);
const context = browser._defaultContext!;
if (!options.ignoreDefaultArgs || Array.isArray(options.ignoreDefaultArgs))
await context._loadDefaultContext();
return context;
});
}
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
const browserServer = new BrowserServer(options);
await this._launchServer(options, 'server', browserServer);
return browserServer;
}
async connect(options: ConnectOptions): Promise<Browser> {
const logger = new RootLogger(options.logger);
return await WebSocketTransport.connect(options.wsEndpoint, async transport => {
if ((options as any).__testHookBeforeCreateBrowser)
await (options as any).__testHookBeforeCreateBrowser();
return this._connectToTransport(transport, { slowMo: options.slowMo, logger, downloadsPath: '' });
}, logger);
}
abstract _launchServer(options: LaunchServerOptions, launchType: LaunchType, browserServer: BrowserServer, userDataDir?: string): Promise<{ transport?: ConnectionTransport, downloadsPath: string }>;
abstract _connectToServer(browserServer: BrowserServer, persistent: boolean, transport: ConnectionTransport, downloadsPath: string): Promise<BrowserBase>;
abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BrowserBase>;
} }

View file

@ -25,17 +25,16 @@ import * as ws from 'ws';
import { launchProcess } from './processLauncher'; import { launchProcess } from './processLauncher';
import { kBrowserCloseMessageId } from '../chromium/crConnection'; import { kBrowserCloseMessageId } from '../chromium/crConnection';
import { PipeTransport } from './pipeTransport'; import { PipeTransport } from './pipeTransport';
import { LaunchOptions, BrowserArgOptions, ConnectOptions, LaunchServerOptions, AbstractBrowserType, processBrowserArgOptions } from './browserType'; import { BrowserArgOptions, LaunchServerOptions, BrowserTypeBase, processBrowserArgOptions, LaunchType } from './browserType';
import { LaunchType } from '../browser';
import { BrowserServer, WebSocketWrapper } from './browserServer'; import { BrowserServer, WebSocketWrapper } from './browserServer';
import { Events } from '../events'; import { Events } from '../events';
import { ConnectionTransport, ProtocolRequest, WebSocketTransport } from '../transport'; import { ConnectionTransport, ProtocolRequest } from '../transport';
import { BrowserContext } from '../browserContext'; import { InnerLogger, logError } from '../logger';
import { InnerLogger, logError, RootLogger } from '../logger';
import { BrowserDescriptor } from '../install/browserPaths'; import { BrowserDescriptor } from '../install/browserPaths';
import { CRDevTools } from '../chromium/crDevTools'; import { CRDevTools } from '../chromium/crDevTools';
import { BrowserBase, BrowserOptions } from '../browser';
export class Chromium extends AbstractBrowserType<CRBrowser> { export class Chromium extends BrowserTypeBase {
private _devtools: CRDevTools | undefined; private _devtools: CRDevTools | undefined;
constructor(packagePath: string, browser: BrowserDescriptor) { constructor(packagePath: string, browser: BrowserDescriptor) {
@ -48,52 +47,28 @@ export class Chromium extends AbstractBrowserType<CRBrowser> {
return new CRDevTools(path.join(this._browserPath, 'devtools-preferences.json')); return new CRDevTools(path.join(this._browserPath, 'devtools-preferences.json'));
} }
async launch(options: LaunchOptions = {}): Promise<CRBrowser> { async _connectToServer(browserServer: BrowserServer, persistent: boolean, transport: ConnectionTransport, downloadsPath: string): Promise<BrowserBase> {
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); const options = browserServer._launchOptions;
const browserServer = new BrowserServer(options); let devtools = this._devtools;
const { transport, downloadsPath } = await this._launchServer(options, 'local', browserServer); if ((options as any).__testHookForDevTools) {
return await browserServer._initializeOrClose(async () => { devtools = this._createDevTools();
let devtools = this._devtools; await (options as any).__testHookForDevTools(devtools);
if ((options as any).__testHookForDevTools) { }
devtools = this._createDevTools(); return await CRBrowser.connect(transport, {
await (options as any).__testHookForDevTools(devtools); slowMo: options.slowMo,
} persistent,
return await CRBrowser.connect(transport!, { headful: !processBrowserArgOptions(options).headless,
slowMo: options.slowMo, logger: browserServer._logger,
headful: !processBrowserArgOptions(options).headless, downloadsPath,
logger: browserServer._logger, ownedServer: browserServer,
downloadsPath, }, devtools);
ownedServer: browserServer,
}, devtools);
});
} }
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> { _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<CRBrowser> {
const browserServer = new BrowserServer(options); return CRBrowser.connect(transport, options);
await this._launchServer(options, 'server', browserServer);
return browserServer;
} }
async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> { async _launchServer(options: LaunchServerOptions, launchType: LaunchType, browserServer: BrowserServer, userDataDir?: string): Promise<{ transport?: ConnectionTransport, downloadsPath: string }> {
const browserServer = new BrowserServer(options);
const { transport, downloadsPath } = await this._launchServer(options, 'persistent', browserServer, userDataDir);
return await browserServer._initializeOrClose(async () => {
const browser = await CRBrowser.connect(transport!, {
slowMo: options.slowMo,
persistent: true,
logger: browserServer._logger,
downloadsPath,
headful: !processBrowserArgOptions(options).headless,
ownedServer: browserServer
}, this._devtools);
const context = browser._defaultContext!;
if (!options.ignoreDefaultArgs || Array.isArray(options.ignoreDefaultArgs))
await context._loadDefaultContext();
return context;
});
}
private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, browserServer: BrowserServer, userDataDir?: string): Promise<{ transport?: ConnectionTransport, downloadsPath: string }> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -163,15 +138,6 @@ export class Chromium extends AbstractBrowserType<CRBrowser> {
return { transport, downloadsPath }; return { transport, downloadsPath };
} }
async connect(options: ConnectOptions): Promise<CRBrowser> {
const logger = new RootLogger(options.logger);
return await WebSocketTransport.connect(options.wsEndpoint, async transport => {
if ((options as any).__testHookBeforeCreateBrowser)
await (options as any).__testHookBeforeCreateBrowser();
return CRBrowser.connect(transport, { slowMo: options.slowMo, logger, downloadsPath: '' });
}, logger);
}
private _defaultArgs(options: BrowserArgOptions = {}, launchType: LaunchType, userDataDir: string): string[] { private _defaultArgs(options: BrowserArgOptions = {}, launchType: LaunchType, userDataDir: string): string[] {
const { devtools, headless } = processBrowserArgOptions(options); const { devtools, headless } = processBrowserArgOptions(options);
const { args = [] } = options; const { args = [] } = options;

View file

@ -70,7 +70,7 @@ export class ElectronApplication extends ExtendedEventEmitter {
constructor(logger: InnerLogger, browser: CRBrowser, nodeConnection: CRConnection) { constructor(logger: InnerLogger, browser: CRBrowser, nodeConnection: CRConnection) {
super(); super();
this._logger = logger; this._logger = logger;
this._browserContext = browser._defaultContext!; this._browserContext = browser._defaultContext as CRBrowserContext;
this._browserContext.on(Events.BrowserContext.Close, () => this.emit(ElectronEvents.ElectronApplication.Close)); this._browserContext.on(Events.BrowserContext.Close, () => this.emit(ElectronEvents.ElectronApplication.Close));
this._browserContext.on(Events.BrowserContext.Page, event => this._onPage(event)); this._browserContext.on(Events.BrowserContext.Page, event => this._onPage(event));
this._nodeConnection = nodeConnection; this._nodeConnection = nodeConnection;

View file

@ -20,73 +20,46 @@ import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import * as util from 'util'; import * as util from 'util';
import * as ws from 'ws'; import * as ws from 'ws';
import { LaunchType } from '../browser';
import { BrowserContext } from '../browserContext';
import { TimeoutError } from '../errors'; import { TimeoutError } from '../errors';
import { Events } from '../events'; import { Events } from '../events';
import { FFBrowser } from '../firefox/ffBrowser'; import { FFBrowser } from '../firefox/ffBrowser';
import { kBrowserCloseMessageId } from '../firefox/ffConnection'; import { kBrowserCloseMessageId } from '../firefox/ffConnection';
import { helper, assert, debugAssert } from '../helper'; import { helper, assert, debugAssert } from '../helper';
import { BrowserServer, WebSocketWrapper } from './browserServer'; import { BrowserServer, WebSocketWrapper } from './browserServer';
import { BrowserArgOptions, LaunchOptions, LaunchServerOptions, ConnectOptions, AbstractBrowserType, processBrowserArgOptions } from './browserType'; import { BrowserArgOptions, LaunchServerOptions, BrowserTypeBase, processBrowserArgOptions, LaunchType } from './browserType';
import { launchProcess, waitForLine } from './processLauncher'; import { launchProcess, waitForLine } from './processLauncher';
import { ConnectionTransport, SequenceNumberMixer, WebSocketTransport } from '../transport'; import { ConnectionTransport, SequenceNumberMixer, WebSocketTransport } from '../transport';
import { RootLogger, InnerLogger, logError } from '../logger'; import { InnerLogger, logError } from '../logger';
import { BrowserDescriptor } from '../install/browserPaths'; import { BrowserDescriptor } from '../install/browserPaths';
import { BrowserBase, BrowserOptions } from '../browser';
const mkdtempAsync = util.promisify(fs.mkdtemp); const mkdtempAsync = util.promisify(fs.mkdtemp);
export class Firefox extends AbstractBrowserType<FFBrowser> { export class Firefox extends BrowserTypeBase {
constructor(packagePath: string, browser: BrowserDescriptor) { constructor(packagePath: string, browser: BrowserDescriptor) {
super(packagePath, browser); super(packagePath, browser);
} }
async launch(options: LaunchOptions = {}): Promise<FFBrowser> { _connectToServer(browserServer: BrowserServer, persistent: boolean, transport: ConnectionTransport, downloadsPath: string): Promise<BrowserBase> {
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); const options = browserServer._launchOptions;
const browserServer = new BrowserServer(options); // TODO: connect to the underlying socket.
const { downloadsPath } = await this._launchServer(options, 'local', browserServer); return WebSocketTransport.connect(browserServer.wsEndpoint()!, transport => {
return await browserServer._initializeOrClose(async () => { return FFBrowser.connect(transport, {
const browser = await WebSocketTransport.connect(browserServer.wsEndpoint()!, transport => { slowMo: options.slowMo,
return FFBrowser.connect(transport, { logger: browserServer._logger,
slowMo: options.slowMo, persistent,
logger: browserServer._logger, downloadsPath,
downloadsPath, headful: !processBrowserArgOptions(options).headless,
headful: !processBrowserArgOptions(options).headless, ownedServer: browserServer,
ownedServer: browserServer, });
}); }, browserServer._logger);
}, browserServer._logger);
return browser;
});
} }
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> { _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<FFBrowser> {
const browserServer = new BrowserServer(options); return FFBrowser.connect(transport, options);
await this._launchServer(options, 'server', browserServer);
return browserServer;
} }
async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> { async _launchServer(options: LaunchServerOptions, launchType: LaunchType, browserServer: BrowserServer, userDataDir?: string): Promise<{ downloadsPath: string }> {
const browserServer = new BrowserServer(options);
const { downloadsPath } = await this._launchServer(options, 'persistent', browserServer, userDataDir);
return await browserServer._initializeOrClose(async () => {
const browser = await WebSocketTransport.connect(browserServer.wsEndpoint()!, transport => {
return FFBrowser.connect(transport, {
slowMo: options.slowMo,
logger: browserServer._logger,
persistent: true,
downloadsPath,
ownedServer: browserServer,
headful: !processBrowserArgOptions(options).headless,
});
}, browserServer._logger);
const context = browser._defaultContext!;
if (!options.ignoreDefaultArgs || Array.isArray(options.ignoreDefaultArgs))
await context._loadDefaultContext();
return context;
});
}
private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, browserServer: BrowserServer, userDataDir?: string): Promise<{ downloadsPath: string }> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -143,7 +116,7 @@ export class Firefox extends AbstractBrowserType<FFBrowser> {
// We try to gracefully close to prevent crash reporting and core dumps. // We try to gracefully close to prevent crash reporting and core dumps.
const transport = await WebSocketTransport.connect(browserWSEndpoint!, async transport => transport); const transport = await WebSocketTransport.connect(browserWSEndpoint!, async transport => transport);
const message = { method: 'Browser.close', params: {}, id: kBrowserCloseMessageId }; const message = { method: 'Browser.close', params: {}, id: kBrowserCloseMessageId };
await transport.send(message); transport.send(message);
}, },
onkill: (exitCode, signal) => { onkill: (exitCode, signal) => {
browserServer.emit(Events.BrowserServer.Close, exitCode, signal); browserServer.emit(Events.BrowserServer.Close, exitCode, signal);
@ -162,15 +135,6 @@ export class Firefox extends AbstractBrowserType<FFBrowser> {
return { downloadsPath }; return { downloadsPath };
} }
async connect(options: ConnectOptions): Promise<FFBrowser> {
const logger = new RootLogger(options.logger);
return await WebSocketTransport.connect(options.wsEndpoint, async transport => {
if ((options as any).__testHookBeforeCreateBrowser)
await (options as any).__testHookBeforeCreateBrowser();
return FFBrowser.connect(transport, { slowMo: options.slowMo, logger, downloadsPath: '' });
}, logger);
}
private _defaultArgs(options: BrowserArgOptions = {}, launchType: LaunchType, userDataDir: string, port: number): string[] { private _defaultArgs(options: BrowserArgOptions = {}, launchType: LaunchType, userDataDir: string, port: number): string[] {
const { devtools, headless } = processBrowserArgOptions(options); const { devtools, headless } = processBrowserArgOptions(options);
const { args = [] } = options; const { args = [] } = options;

View file

@ -24,62 +24,37 @@ import * as os from 'os';
import * as util from 'util'; import * as util from 'util';
import { helper, assert } from '../helper'; import { helper, assert } from '../helper';
import { kBrowserCloseMessageId } from '../webkit/wkConnection'; import { kBrowserCloseMessageId } from '../webkit/wkConnection';
import { LaunchOptions, BrowserArgOptions, LaunchServerOptions, ConnectOptions, AbstractBrowserType, processBrowserArgOptions } from './browserType'; import { BrowserArgOptions, LaunchServerOptions, BrowserTypeBase, processBrowserArgOptions, LaunchType } from './browserType';
import { ConnectionTransport, SequenceNumberMixer, WebSocketTransport } from '../transport'; import { ConnectionTransport, SequenceNumberMixer } from '../transport';
import * as ws from 'ws'; import * as ws from 'ws';
import { LaunchType } from '../browser';
import { BrowserServer, WebSocketWrapper } from './browserServer'; import { BrowserServer, WebSocketWrapper } from './browserServer';
import { Events } from '../events'; import { Events } from '../events';
import { BrowserContext } from '../browserContext';
import { InnerLogger, logError, RootLogger } from '../logger'; import { InnerLogger, logError, RootLogger } from '../logger';
import { BrowserDescriptor } from '../install/browserPaths'; import { BrowserDescriptor } from '../install/browserPaths';
import { BrowserBase, BrowserOptions } from '../browser';
export class WebKit extends AbstractBrowserType<WKBrowser> { export class WebKit extends BrowserTypeBase {
constructor(packagePath: string, browser: BrowserDescriptor) { constructor(packagePath: string, browser: BrowserDescriptor) {
super(packagePath, browser); super(packagePath, browser);
} }
async launch(options: LaunchOptions = {}): Promise<WKBrowser> { _connectToServer(browserServer: BrowserServer, persistent: boolean, transport: ConnectionTransport, downloadsPath: string): Promise<BrowserBase> {
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); const options = browserServer._launchOptions;
const browserServer = new BrowserServer(options); return WKBrowser.connect(transport, {
const { transport, downloadsPath } = await this._launchServer(options, 'local', browserServer); slowMo: options.slowMo,
return await browserServer._initializeOrClose(async () => { headful: !processBrowserArgOptions(options).headless,
return await WKBrowser.connect(transport!, { logger: browserServer._logger,
slowMo: options.slowMo, persistent,
headful: !processBrowserArgOptions(options).headless, downloadsPath,
logger: browserServer._logger, ownedServer: browserServer
downloadsPath,
ownedServer: browserServer
});
}); });
} }
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> { _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<WKBrowser> {
const browserServer = new BrowserServer(options); return WKBrowser.connect(transport, options);
await this._launchServer(options, 'server', browserServer);
return browserServer;
} }
async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> { async _launchServer(options: LaunchServerOptions, launchType: LaunchType, browserServer: BrowserServer, userDataDir?: string): Promise<{ transport?: ConnectionTransport, downloadsPath: string, logger: RootLogger }> {
const browserServer = new BrowserServer(options);
const { transport, downloadsPath } = await this._launchServer(options, 'persistent', browserServer, userDataDir);
return await browserServer._initializeOrClose(async () => {
const browser = await WKBrowser.connect(transport!, {
slowMo: options.slowMo,
headful: !processBrowserArgOptions(options).headless,
logger: browserServer._logger,
persistent: true,
downloadsPath,
ownedServer: browserServer
});
const context = browser._defaultContext!;
if (!options.ignoreDefaultArgs || Array.isArray(options.ignoreDefaultArgs))
await context._loadDefaultContext();
return context;
});
}
private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, browserServer: BrowserServer, userDataDir?: string): Promise<{ transport?: ConnectionTransport, downloadsPath: string, logger: RootLogger }> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -144,15 +119,6 @@ export class WebKit extends AbstractBrowserType<WKBrowser> {
return { transport, downloadsPath, logger }; return { transport, downloadsPath, logger };
} }
async connect(options: ConnectOptions): Promise<WKBrowser> {
const logger = new RootLogger(options.logger);
return await WebSocketTransport.connect(options.wsEndpoint, async transport => {
if ((options as any).__testHookBeforeCreateBrowser)
await (options as any).__testHookBeforeCreateBrowser();
return WKBrowser.connect(transport, { slowMo: options.slowMo, logger, downloadsPath: '' });
}, logger);
}
_defaultArgs(options: BrowserArgOptions = {}, launchType: LaunchType, userDataDir: string, port: number): string[] { _defaultArgs(options: BrowserArgOptions = {}, launchType: LaunchType, userDataDir: string, port: number): string[] {
const { devtools, headless } = processBrowserArgOptions(options); const { devtools, headless } = processBrowserArgOptions(options);
const { args = [] } = options; const { args = [] } = options;

View file

@ -32,7 +32,6 @@ const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) Appl
export class WKBrowser extends BrowserBase { export class WKBrowser extends BrowserBase {
private readonly _connection: WKConnection; private readonly _connection: WKConnection;
readonly _browserSession: WKSession; readonly _browserSession: WKSession;
readonly _defaultContext: WKBrowserContext | null = null;
readonly _contexts = new Map<string, WKBrowserContext>(); readonly _contexts = new Map<string, WKBrowserContext>();
readonly _wkPages = new Map<string, WKPage>(); readonly _wkPages = new Map<string, WKPage>();
private readonly _eventListeners: RegisteredListener[]; private readonly _eventListeners: RegisteredListener[];
@ -132,7 +131,7 @@ export class WKBrowser extends BrowserBase {
context = this._contexts.get(pageProxyInfo.browserContextId) || null; context = this._contexts.get(pageProxyInfo.browserContextId) || null;
} }
if (!context) if (!context)
context = this._defaultContext; context = this._defaultContext as WKBrowserContext;
if (!context) if (!context)
return; return;
const pageProxySession = new WKSession(this._connection, pageProxyId, `The page has been closed.`, (message: any) => { const pageProxySession = new WKSession(this._connection, pageProxyId, `The page has been closed.`, (message: any) => {