chore: pull common functionality into the BrowserTypeBase (#2312)
This commit is contained in:
parent
aa0d844c76
commit
f9b437a49e
|
|
@ -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';
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue