From 58336d3eb9d41a4185241b55348ea89dc3308892 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 11 Dec 2019 07:18:43 -0800 Subject: [PATCH] chore: get rid of templating (#209) --- src/browserContext.ts | 33 +++++++++++++++++++++---------- src/chromium/Browser.ts | 28 +++++++++++++------------- src/chromium/FrameManager.ts | 8 ++++---- src/chromium/Target.ts | 16 +++++++-------- src/chromium/features/chromium.ts | 6 +++--- src/firefox/Browser.ts | 32 +++++++++++++++--------------- src/firefox/FrameManager.ts | 4 ++-- src/page.ts | 12 +++++------ src/webkit/Browser.ts | 28 +++++++++++++------------- src/webkit/FrameManager.ts | 10 +++++----- src/webkit/Target.ts | 14 ++++++------- 11 files changed, 102 insertions(+), 89 deletions(-) diff --git a/src/browserContext.ts b/src/browserContext.ts index 169d60fdf8..2145849c65 100644 --- a/src/browserContext.ts +++ b/src/browserContext.ts @@ -18,28 +18,41 @@ import { assert } from './helper'; import { Page } from './page'; import * as network from './network'; +import * as childProcess from 'child_process'; -export interface BrowserDelegate { - contextPages(): Promise[]>; - createPageInContext(): Promise>; +export interface BrowserInterface { + browserContexts(): BrowserContext[]; + close(): Promise; + createIncognitoBrowserContext(): Promise; + defaultBrowserContext(): BrowserContext; + newPage(): Promise; + pages(): Promise; + process(): childProcess.ChildProcess | null; + version(): Promise; + userAgent(): Promise; +} + +export interface BrowserDelegate { + contextPages(): Promise; + createPageInContext(): Promise; closeContext(): Promise; getContextCookies(): Promise; clearContextCookies(): Promise; setContextCookies(cookies: network.SetNetworkCookieParam[]): Promise; } -export class BrowserContext { - private readonly _delegate: BrowserDelegate; - private readonly _browser: Browser; +export class BrowserContext { + private readonly _delegate: BrowserDelegate; + private readonly _browser: BrowserInterface; private readonly _isIncognito: boolean; - constructor(delegate: BrowserDelegate, browser: Browser, isIncognito: boolean) { + constructor(delegate: BrowserDelegate, browser: BrowserInterface, isIncognito: boolean) { this._delegate = delegate; this._browser = browser; this._isIncognito = isIncognito; } - async pages(): Promise[]> { + async pages(): Promise { return this._delegate.contextPages(); } @@ -47,11 +60,11 @@ export class BrowserContext { return this._isIncognito; } - async newPage(): Promise> { + async newPage(): Promise { return this._delegate.createPageInContext(); } - browser(): Browser { + browser(): BrowserInterface { return this._browser; } diff --git a/src/chromium/Browser.ts b/src/chromium/Browser.ts index 0dbe477f13..7244eaf833 100644 --- a/src/chromium/Browser.ts +++ b/src/chromium/Browser.ts @@ -19,7 +19,7 @@ import * as childProcess from 'child_process'; import { EventEmitter } from 'events'; import { Events } from './events'; import { assert, helper } from '../helper'; -import { BrowserContext } from '../browserContext'; +import { BrowserContext, BrowserInterface } from '../browserContext'; import { Connection, ConnectionEvents, CDPSession } from './Connection'; import { Page } from '../page'; import { Target } from './Target'; @@ -30,15 +30,15 @@ import { FrameManager } from './FrameManager'; import * as network from '../network'; import { Permissions } from './features/permissions'; -export class Browser extends EventEmitter { +export class Browser extends EventEmitter implements BrowserInterface { private _ignoreHTTPSErrors: boolean; private _defaultViewport: types.Viewport; private _process: childProcess.ChildProcess; _connection: Connection; _client: CDPSession; private _closeCallback: () => Promise; - private _defaultContext: BrowserContext; - private _contexts = new Map>(); + private _defaultContext: BrowserContext; + private _contexts = new Map(); _targets = new Map(); readonly chromium: Chromium; @@ -80,16 +80,16 @@ export class Browser extends EventEmitter { this._client.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this)); } - _createBrowserContext(contextId: string | null): BrowserContext { + _createBrowserContext(contextId: string | null): BrowserContext { const isIncognito = !!contextId; const context = new BrowserContext({ - contextPages: async (): Promise[]> => { + contextPages: async (): Promise => { const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page'); const pages = await Promise.all(targets.map(target => target.page())); return pages.filter(page => !!page); }, - createPageInContext: async (): Promise> => { + createPageInContext: async (): Promise => { const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined }); const target = this._targets.get(targetId); assert(await target._initializedPromise, 'Failed to create target for page'); @@ -127,18 +127,18 @@ export class Browser extends EventEmitter { return this._process; } - async createIncognitoBrowserContext(): Promise> { + async createIncognitoBrowserContext(): Promise { const {browserContextId} = await this._client.send('Target.createBrowserContext'); const context = this._createBrowserContext(browserContextId); this._contexts.set(browserContextId, context); return context; } - browserContexts(): BrowserContext[] { + browserContexts(): BrowserContext[] { return [this._defaultContext, ...Array.from(this._contexts.values())]; } - defaultBrowserContext(): BrowserContext { + defaultBrowserContext(): BrowserContext { return this._defaultContext; } @@ -174,11 +174,11 @@ export class Browser extends EventEmitter { this.chromium.emit(Events.Chromium.TargetChanged, target); } - async newPage(): Promise> { + async newPage(): Promise { return this._defaultContext.newPage(); } - async _closePage(page: Page) { + async _closePage(page: Page) { await this._client.send('Target.closeTarget', { targetId: Target.fromPage(page)._targetId }); } @@ -186,7 +186,7 @@ export class Browser extends EventEmitter { return Array.from(this._targets.values()).filter(target => target._isInitialized); } - async _activatePage(page: Page) { + async _activatePage(page: Page) { await (page._delegate as FrameManager)._client.send('Target.activateTarget', {targetId: Target.fromPage(page)._targetId}); } @@ -216,7 +216,7 @@ export class Browser extends EventEmitter { } } - async pages(): Promise[]> { + async pages(): Promise { const contextPages = await Promise.all(this.browserContexts().map(context => context.pages())); // Flatten array. return contextPages.reduce((acc, x) => acc.concat(x), []); diff --git a/src/chromium/FrameManager.ts b/src/chromium/FrameManager.ts index 495cb40397..3e4a31313d 100644 --- a/src/chromium/FrameManager.ts +++ b/src/chromium/FrameManager.ts @@ -62,7 +62,7 @@ type FrameData = { export class FrameManager extends EventEmitter implements frames.FrameDelegate, PageDelegate { _client: CDPSession; - private _page: Page; + private _page: Page; private _networkManager: NetworkManager; private _frames = new Map(); private _contextIdToContext = new Map(); @@ -72,7 +72,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, rawKeyboard: RawKeyboardImpl; screenshotterDelegate: CRScreenshotDelegate; - constructor(client: CDPSession, browserContext: BrowserContext, ignoreHTTPSErrors: boolean) { + constructor(client: CDPSession, browserContext: BrowserContext, ignoreHTTPSErrors: boolean) { super(); this._client = client; this.rawKeyboard = new RawKeyboardImpl(client); @@ -251,7 +251,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, this._handleFrameTree(child); } - page(): Page { + page(): Page { return this._page; } @@ -540,7 +540,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, if (runBeforeUnload) await this._client.send('Page.close'); else - await this._page.browser()._closePage(this._page); + await (this._page.browser() as Browser)._closePage(this._page); } } diff --git a/src/chromium/Target.ts b/src/chromium/Target.ts index da9f1f0a21..d1737b97a1 100644 --- a/src/chromium/Target.ts +++ b/src/chromium/Target.ts @@ -30,25 +30,25 @@ const targetSymbol = Symbol('target'); export class Target { private _targetInfo: Protocol.Target.TargetInfo; - private _browserContext: BrowserContext; + private _browserContext: BrowserContext; _targetId: string; private _sessionFactory: () => Promise; private _ignoreHTTPSErrors: boolean; private _defaultViewport: types.Viewport; - private _pagePromise: Promise> | null = null; - private _page: Page | null = null; + private _pagePromise: Promise | null = null; + private _page: Page | null = null; private _workerPromise: Promise | null = null; _initializedPromise: Promise; _initializedCallback: (value?: unknown) => void; _isInitialized: boolean; - static fromPage(page: Page): Target { + static fromPage(page: Page): Target { return (page as any)[targetSymbol]; } constructor( targetInfo: Protocol.Target.TargetInfo, - browserContext: BrowserContext, + browserContext: BrowserContext, sessionFactory: () => Promise, ignoreHTTPSErrors: boolean, defaultViewport: types.Viewport | null) { @@ -81,7 +81,7 @@ export class Target { this._page._didClose(); } - async page(): Promise | null> { + async page(): Promise { if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) { this._pagePromise = this._sessionFactory().then(async client => { const frameManager = new FrameManager(client, this._browserContext, this._ignoreHTTPSErrors); @@ -128,10 +128,10 @@ export class Target { } browser(): Browser { - return this._browserContext.browser(); + return this._browserContext.browser() as Browser; } - browserContext(): BrowserContext { + browserContext(): BrowserContext { return this._browserContext; } diff --git a/src/chromium/features/chromium.ts b/src/chromium/features/chromium.ts index db8cda487a..b8df6f9fdb 100644 --- a/src/chromium/features/chromium.ts +++ b/src/chromium/features/chromium.ts @@ -48,7 +48,7 @@ export class Chromium extends EventEmitter { return target._worker(); } - async startTracing(page: Page | undefined, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) { + async startTracing(page: Page | undefined, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) { assert(!this._recording, 'Cannot start recording trace while already recording trace.'); this._tracingClient = page ? (page._delegate as FrameManager)._client : this._client; @@ -87,12 +87,12 @@ export class Chromium extends EventEmitter { return contentPromise; } - targets(context?: BrowserContext): Target[] { + targets(context?: BrowserContext): Target[] { const targets = this._browser._allTargets(); return context ? targets.filter(t => t.browserContext() === context) : targets; } - pageTarget(page: Page): Target { + pageTarget(page: Page): Target { return Target.fromPage(page); } diff --git a/src/firefox/Browser.ts b/src/firefox/Browser.ts index 07c8ca79cc..39bf89f80c 100644 --- a/src/firefox/Browser.ts +++ b/src/firefox/Browser.ts @@ -25,16 +25,16 @@ import { Page } from '../page'; import * as types from '../types'; import { FrameManager } from './FrameManager'; import * as network from '../network'; -import { BrowserContext } from '../browserContext'; +import { BrowserContext, BrowserInterface } from '../browserContext'; -export class Browser extends EventEmitter { +export class Browser extends EventEmitter implements BrowserInterface { private _connection: Connection; _defaultViewport: types.Viewport; private _process: import('child_process').ChildProcess; private _closeCallback: () => void; _targets: Map; - private _defaultContext: BrowserContext; - private _contexts: Map>; + private _defaultContext: BrowserContext; + private _contexts: Map; private _eventListeners: RegisteredListener[]; static async create(connection: Connection, defaultViewport: types.Viewport | null, process: import('child_process').ChildProcess | null, closeCallback: () => void) { @@ -75,14 +75,14 @@ export class Browser extends EventEmitter { return !this._connection._closed; } - async createIncognitoBrowserContext(): Promise> { + async createIncognitoBrowserContext(): Promise { const {browserContextId} = await this._connection.send('Target.createBrowserContext'); const context = this._createBrowserContext(browserContextId); this._contexts.set(browserContextId, context); return context; } - browserContexts(): Array> { + browserContexts(): Array { return [this._defaultContext, ...Array.from(this._contexts.values())]; } @@ -128,7 +128,7 @@ export class Browser extends EventEmitter { } } - newPage(): Promise> { + newPage(): Promise { return this._defaultContext.newPage(); } @@ -170,16 +170,16 @@ export class Browser extends EventEmitter { this._closeCallback(); } - _createBrowserContext(browserContextId: string | null): BrowserContext { + _createBrowserContext(browserContextId: string | null): BrowserContext { const isIncognito = !!browserContextId; const context = new BrowserContext({ - contextPages: async (): Promise[]> => { + contextPages: async (): Promise => { const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page'); const pages = await Promise.all(targets.map(target => target.page())); return pages.filter(page => !!page); }, - createPageInContext: async (): Promise> => { + createPageInContext: async (): Promise => { const {targetId} = await this._connection.send('Target.newPage', { browserContextId: browserContextId || undefined }); @@ -215,17 +215,17 @@ export class Browser extends EventEmitter { } export class Target { - _pagePromise?: Promise>; - private _page: Page | null = null; + _pagePromise?: Promise; + private _page: Page | null = null; private _browser: Browser; - _context: BrowserContext; + _context: BrowserContext; private _connection: Connection; private _targetId: string; private _type: 'page' | 'browser'; _url: string; private _openerId: string; - constructor(connection: any, browser: Browser, context: BrowserContext, targetId: string, type: 'page' | 'browser', url: string, openerId: string | undefined) { + constructor(connection: any, browser: Browser, context: BrowserContext, targetId: string, type: 'page' | 'browser', url: string, openerId: string | undefined) { this._browser = browser; this._context = context; this._connection = connection; @@ -252,11 +252,11 @@ export class Target { return this._url; } - browserContext(): BrowserContext { + browserContext(): BrowserContext { return this._context; } - page(): Promise> { + page(): Promise { if (this._type === 'page' && !this._pagePromise) { this._pagePromise = new Promise(async f => { const session = await this._connection.createSession(this._targetId); diff --git a/src/firefox/FrameManager.ts b/src/firefox/FrameManager.ts index 4ba8fa63ee..7f07f605db 100644 --- a/src/firefox/FrameManager.ts +++ b/src/firefox/FrameManager.ts @@ -56,14 +56,14 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, readonly rawKeyboard: RawKeyboardImpl; readonly screenshotterDelegate: FFScreenshotDelegate; readonly _session: JugglerSession; - readonly _page: Page; + readonly _page: Page; private readonly _networkManager: NetworkManager; private _mainFrame: frames.Frame; private readonly _frames: Map; private readonly _contextIdToContext: Map; private _eventListeners: RegisteredListener[]; - constructor(session: JugglerSession, browserContext: BrowserContext) { + constructor(session: JugglerSession, browserContext: BrowserContext) { super(); this._session = session; this.rawKeyboard = new RawKeyboardImpl(session); diff --git a/src/page.ts b/src/page.ts index 2a9246ca68..c308b1196a 100644 --- a/src/page.ts +++ b/src/page.ts @@ -26,7 +26,7 @@ import { Screenshotter, ScreenshotterDelegate } from './screenshotter'; import { TimeoutSettings } from './TimeoutSettings'; import * as types from './types'; import { Events } from './events'; -import { BrowserContext } from './browserContext'; +import { BrowserContext, BrowserInterface } from './browserContext'; import { ConsoleMessage, ConsoleMessageLocation } from './console'; export interface PageDelegate { @@ -69,14 +69,14 @@ export type FileChooser = { multiple: boolean }; -export class Page extends EventEmitter { +export class Page extends EventEmitter { private _closed = false; private _closedCallback: () => void; private _closedPromise: Promise; private _disconnected = false; private _disconnectedCallback: (e: Error) => void; readonly _disconnectedPromise: Promise; - private _browserContext: BrowserContext; + private _browserContext: BrowserContext; readonly keyboard: input.Keyboard; readonly mouse: input.Mouse; readonly _timeoutSettings: TimeoutSettings; @@ -87,7 +87,7 @@ export class Page extends EventEmitter { private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>(); readonly _lifecycleWatchers = new Set(); - constructor(delegate: PageDelegate, browserContext: BrowserContext) { + constructor(delegate: PageDelegate, browserContext: BrowserContext) { super(); this._delegate = delegate; this._closedPromise = new Promise(f => this._closedCallback = f); @@ -150,11 +150,11 @@ export class Page extends EventEmitter { }); } - browser(): Browser { + browser(): BrowserInterface { return this._browserContext.browser(); } - browserContext(): BrowserContext { + browserContext(): BrowserContext { return this._browserContext; } diff --git a/src/webkit/Browser.ts b/src/webkit/Browser.ts index 502a19fd38..cc3678e922 100644 --- a/src/webkit/Browser.ts +++ b/src/webkit/Browser.ts @@ -25,15 +25,15 @@ import { Target } from './Target'; import { Protocol } from './protocol'; import * as types from '../types'; import { Events } from '../events'; -import { BrowserContext } from '../browserContext'; +import { BrowserContext, BrowserInterface } from '../browserContext'; -export class Browser extends EventEmitter { +export class Browser extends EventEmitter implements BrowserInterface { readonly _defaultViewport: types.Viewport; private readonly _process: childProcess.ChildProcess; readonly _connection: Connection; private _closeCallback: () => Promise; - private readonly _defaultContext: BrowserContext; - private _contexts = new Map>(); + private readonly _defaultContext: BrowserContext; + private _contexts = new Map(); _targets = new Map(); private _eventListeners: RegisteredListener[]; private _privateEvents = new EventEmitter(); @@ -86,25 +86,25 @@ export class Browser extends EventEmitter { return this._process; } - async createIncognitoBrowserContext(): Promise> { + async createIncognitoBrowserContext(): Promise { const {browserContextId} = await this._connection.send('Browser.createContext'); const context = this._createBrowserContext(browserContextId); this._contexts.set(browserContextId, context); return context; } - browserContexts(): BrowserContext[] { + browserContexts(): BrowserContext[] { return [this._defaultContext, ...Array.from(this._contexts.values())]; } - defaultBrowserContext(): BrowserContext { + defaultBrowserContext(): BrowserContext { return this._defaultContext; } async _disposeContext(browserContextId: string | null) { } - async newPage(): Promise> { + async newPage(): Promise { return this._defaultContext.newPage(); } @@ -136,7 +136,7 @@ export class Browser extends EventEmitter { } } - async pages(): Promise[]> { + async pages(): Promise { const contextPages = await Promise.all(this.browserContexts().map(context => context.pages())); // Flatten array. return contextPages.reduce((acc, x) => acc.concat(x), []); @@ -181,13 +181,13 @@ export class Browser extends EventEmitter { target._didClose(); } - _closePage(page: Page) { + _closePage(page: Page) { this._connection.send('Target.close', { targetId: Target.fromPage(page)._targetId }).catch(debugError); } - async _activatePage(page: Page): Promise { + async _activatePage(page: Page): Promise { await this._connection.send('Target.activate', { targetId: Target.fromPage(page)._targetId }); } @@ -210,16 +210,16 @@ export class Browser extends EventEmitter { await this._closeCallback.call(null); } - _createBrowserContext(browserContextId: string | undefined): BrowserContext { + _createBrowserContext(browserContextId: string | undefined): BrowserContext { const isIncognito = !!browserContextId; const context = new BrowserContext({ - contextPages: async (): Promise[]> => { + contextPages: async (): Promise => { const targets = this.targets().filter(target => target._browserContext === context && target._type === 'page'); const pages = await Promise.all(targets.map(target => target.page())); return pages.filter(page => !!page); }, - createPageInContext: async (): Promise> => { + createPageInContext: async (): Promise => { const { targetId } = await this._connection.send('Browser.createPage', { browserContextId }); const target = this._targets.get(targetId); return await target.page(); diff --git a/src/webkit/FrameManager.ts b/src/webkit/FrameManager.ts index 64dea87cce..bfa5220bd2 100644 --- a/src/webkit/FrameManager.ts +++ b/src/webkit/FrameManager.ts @@ -59,7 +59,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, readonly rawKeyboard: RawKeyboardImpl; readonly screenshotterDelegate: WKScreenshotDelegate; _session: TargetSession; - readonly _page: Page; + readonly _page: Page; private readonly _networkManager: NetworkManager; private readonly _frames: Map; private readonly _contextIdToContext: Map; @@ -68,7 +68,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, private _mainFrame: frames.Frame; private readonly _bootstrapScripts: string[] = []; - constructor(browserContext: BrowserContext) { + constructor(browserContext: BrowserContext) { super(); this.rawKeyboard = new RawKeyboardImpl(); this.rawMouse = new RawMouseImpl(); @@ -197,7 +197,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, this._handleFrameTree(child); } - page(): Page { + page(): Page { return this._page; } @@ -403,7 +403,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, derivedType = level; else if (type === 'timing') derivedType = 'timeEnd'; - + const mainFrameContext = await this.mainFrame().executionContext(); const handles = (parameters || []).map(p => { let context: js.ExecutionContext | null = null; @@ -524,6 +524,6 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate, async closePage(runBeforeUnload: boolean): Promise { if (runBeforeUnload) throw new Error('Not implemented'); - this._page.browser()._closePage(this._page); + (this._page.browser() as Browser)._closePage(this._page); } } diff --git a/src/webkit/Target.ts b/src/webkit/Target.ts index 3149a311ed..338cf94caf 100644 --- a/src/webkit/Target.ts +++ b/src/webkit/Target.ts @@ -25,18 +25,18 @@ import { FrameManager } from './FrameManager'; const targetSymbol = Symbol('target'); export class Target { - readonly _browserContext: BrowserContext; + readonly _browserContext: BrowserContext; readonly _targetId: string; readonly _type: 'page' | 'service-worker' | 'worker'; private readonly _session: TargetSession; - private _pagePromise: Promise> | null = null; - _page: Page | null = null; + private _pagePromise: Promise | null = null; + _page: Page | null = null; - static fromPage(page: Page): Target { + static fromPage(page: Page): Target { return (page as any)[targetSymbol]; } - constructor(session: TargetSession, targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext) { + constructor(session: TargetSession, targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext) { const {targetId, type} = targetInfo; this._session = session; this._browserContext = browserContext; @@ -84,9 +84,9 @@ export class Target { (this._page._delegate as FrameManager).setSession(this._session); } - async page(): Promise> { + async page(): Promise { if (this._type === 'page' && !this._pagePromise) { - const browser = this._browserContext.browser(); + const browser = this._browserContext.browser() as Browser; // Reference local page variable as _page may be // cleared on swap. const frameManager = new FrameManager(this._browserContext);