From 02f7501725733614fd2a117f81ed4a034900eae5 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 26 Jun 2020 12:28:27 -0700 Subject: [PATCH] chore(rpc): strongly-type the initializer, remove __init__ phase (#2729) --- src/rpc/channels.ts | 49 ++++++++++ src/rpc/client/browser.ts | 10 +- src/rpc/client/browserContext.ts | 19 ++-- src/rpc/client/browserType.ts | 20 ++-- src/rpc/client/channelOwner.ts | 8 +- .../client/{console.ts => consoleMessage.ts} | 28 ++---- src/rpc/client/elementHandle.ts | 6 +- src/rpc/client/frame.ts | 18 ++-- src/rpc/client/jsHandle.ts | 28 ++---- src/rpc/client/network.ts | 96 ++++++------------- src/rpc/client/page.ts | 78 +++++++-------- src/rpc/connection.ts | 49 ++++++---- src/rpc/dispatcher.ts | 14 +-- src/rpc/server/browserContextDispatcher.ts | 10 +- src/rpc/server/browserDispatcher.ts | 7 +- src/rpc/server/browserTypeDispatcher.ts | 10 +- src/rpc/server/consoleMessageDispatcher.ts | 9 +- src/rpc/server/elementHandlerDispatcher.ts | 3 +- src/rpc/server/frameDispatcher.ts | 12 +-- src/rpc/server/jsHandleDispatcher.ts | 12 +-- src/rpc/server/networkDispatchers.ts | 29 +++--- src/rpc/server/pageDispatcher.ts | 20 ++-- test/test.js | 13 ++- 23 files changed, 256 insertions(+), 292 deletions(-) rename src/rpc/client/{console.ts => consoleMessage.ts} (66%) diff --git a/src/rpc/channels.ts b/src/rpc/channels.ts index dc779c9a28..9480ca08e0 100644 --- a/src/rpc/channels.ts +++ b/src/rpc/channels.ts @@ -28,12 +28,17 @@ export interface BrowserTypeChannel extends Channel { launch(params: { options?: types.LaunchOptions }): Promise; launchPersistentContext(params: { userDataDir: string, options?: types.LaunchOptions & types.BrowserContextOptions }): Promise; } +export type BrowserTypeInitializer = { + executablePath: string, + name: string +}; export interface BrowserChannel extends Channel { close(): Promise; newContext(params: { options?: types.BrowserContextOptions }): Promise; newPage(params: { options?: types.BrowserContextOptions }): Promise; } +export type BrowserInitializer = {}; export interface BrowserContextChannel extends Channel { addCookies(params: { cookies: types.SetNetworkCookieParam[] }): Promise; @@ -54,6 +59,7 @@ export interface BrowserContextChannel extends Channel { setOffline(params: { offline: boolean }): Promise; waitForEvent(params: { event: string }): Promise; } +export type BrowserContextInitializer = {}; export interface PageChannel extends Channel { on(event: 'bindingCall', callback: (params: BindingCallChannel) => void): this; @@ -101,6 +107,10 @@ export interface PageChannel extends Channel { // A11Y accessibilitySnapshot(params: { options: { interestingOnly?: boolean, root?: ElementHandleChannel } }): Promise; } +export type PageInitializer = { + mainFrame: FrameChannel, + viewportSize: types.Size | null +}; export interface FrameChannel extends Channel { $$eval(params: { selector: string; expression: string, isFunction: boolean, arg: any }): Promise; @@ -137,6 +147,11 @@ export interface FrameChannel extends Channel { waitForNavigation(params: { options: types.WaitForNavigationOptions }): Promise; waitForSelector(params: { selector: string, options: types.WaitForElementOptions }): Promise; } +export type FrameInitializer = { + url: string, + name: string, + parentFrame: FrameChannel | null +}; export interface JSHandleChannel extends Channel { dispose(): Promise; @@ -145,6 +160,9 @@ export interface JSHandleChannel extends Channel { getPropertyList(): Promise<{ name: string, value: JSHandleChannel}[]>; jsonValue(): Promise; } +export type JSHandleInitializer = { + preview: string, +}; export interface ElementHandleChannel extends JSHandleChannel { $$eval(params: { selector: string; expression: string, isFunction: boolean, arg: any }): Promise; @@ -178,22 +196,53 @@ export interface ElementHandleChannel extends JSHandleChannel { export interface RequestChannel extends Channel { response(): Promise; } +export type RequestInitializer = { + frame: FrameChannel, + url: string, + resourceType: string, + method: string, + postData: string | null, + headers: types.Headers, + isNavigationRequest: boolean, + redirectedFrom: RequestChannel | null, +}; export interface RouteChannel extends Channel { abort(params: { errorCode: string }): Promise; continue(params: { overrides: { method?: string, headers?: types.Headers, postData?: string } }): Promise; fulfill(params: { response: types.FulfillResponse & { path?: string } }): Promise; } +export type RouteInitializer = { + request: RequestChannel, +}; export interface ResponseChannel extends Channel { body(): Promise; finished(): Promise; } +export type ResponseInitializer = { + request: RequestChannel, + url: string, + status: number, + statusText: string, + headers: types.Headers, +}; export interface ConsoleMessageChannel extends Channel { } +export type ConsoleMessageInitializer = { + type: string, + text: string, + args: JSHandleChannel[], + location: types.ConsoleMessageLocation, +}; export interface BindingCallChannel extends Channel { reject(params: { error: types.Error }): void; resolve(params: { result: any }): void; } +export type BindingCallInitializer = { + frame: FrameChannel, + name: string, + args: any[] +}; diff --git a/src/rpc/client/browser.ts b/src/rpc/client/browser.ts index 41445ae725..898d25c3cf 100644 --- a/src/rpc/client/browser.ts +++ b/src/rpc/client/browser.ts @@ -15,13 +15,13 @@ */ import * as types from '../../types'; -import { BrowserChannel } from '../channels'; +import { BrowserChannel, BrowserInitializer } from '../channels'; import { BrowserContext } from './browserContext'; import { Page } from './page'; import { ChannelOwner } from './channelOwner'; import { Connection } from '../connection'; -export class Browser extends ChannelOwner { +export class Browser extends ChannelOwner { readonly _contexts = new Set(); private _isConnected = true; @@ -34,12 +34,10 @@ export class Browser extends ChannelOwner { return browser ? Browser.from(browser) : null; } - constructor(connection: Connection, channel: BrowserChannel) { - super(connection, channel); + constructor(connection: Connection, channel: BrowserChannel, initializer: BrowserInitializer) { + super(connection, channel, initializer); } - _initialize() {} - async newContext(options?: types.BrowserContextOptions): Promise { const context = BrowserContext.from(await this._channel.newContext({ options })); this._contexts.add(context); diff --git a/src/rpc/client/browserContext.ts b/src/rpc/client/browserContext.ts index 51f7c19146..d24e5675e7 100644 --- a/src/rpc/client/browserContext.ts +++ b/src/rpc/client/browserContext.ts @@ -19,13 +19,14 @@ import * as frames from './frame'; import { Page, BindingCall, waitForEvent } from './page'; import * as types from '../../types'; import * as network from './network'; -import { BrowserContextChannel } from '../channels'; +import { BrowserContextChannel, BrowserContextInitializer } from '../channels'; import { ChannelOwner } from './channelOwner'; import { helper } from '../../helper'; import { Browser } from './browser'; import { Connection } from '../connection'; +import { Events } from '../../events'; -export class BrowserContext extends ChannelOwner { +export class BrowserContext extends ChannelOwner { _pages = new Set(); private _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = []; _browser: Browser | undefined; @@ -39,11 +40,16 @@ export class BrowserContext extends ChannelOwner { return context ? BrowserContext.from(context) : null; } - constructor(connection: Connection, channel: BrowserContextChannel) { - super(connection, channel); + constructor(connection: Connection, channel: BrowserContextChannel, initializer: BrowserContextInitializer) { + super(connection, channel, initializer); + channel.on('page', page => this._onPage(Page.from(page))); } - _initialize() {} + private _onPage(page: Page): void { + page._browserContext = this; + this._pages.add(page); + this.emit(Events.BrowserContext.Page, page); + } _onRoute(route: network.Route, request: network.Request) { for (const {url, handler} of this._routes) { @@ -56,7 +62,7 @@ export class BrowserContext extends ChannelOwner { } async _onBinding(bindingCall: BindingCall) { - const func = this._bindings.get(bindingCall.name); + const func = this._bindings.get(bindingCall._initializer.name); if (!func) return; bindingCall.call(func); @@ -157,5 +163,6 @@ export class BrowserContext extends ChannelOwner { async close(): Promise { await this._channel.close(); this._browser!._contexts.delete(this); + this.emit(Events.BrowserContext.Close); } } diff --git a/src/rpc/client/browserType.ts b/src/rpc/client/browserType.ts index 9f37b31d97..329c2aa3a6 100644 --- a/src/rpc/client/browserType.ts +++ b/src/rpc/client/browserType.ts @@ -15,31 +15,23 @@ */ import * as types from '../../types'; -import { BrowserTypeChannel } from '../channels'; +import { BrowserTypeChannel, BrowserTypeInitializer } from '../channels'; import { Browser } from './browser'; import { BrowserContext } from './browserContext'; import { ChannelOwner } from './channelOwner'; import { Connection } from '../connection'; -export class BrowserType extends ChannelOwner { - private _executablePath: string = ''; - private _name: string = ''; - - constructor(connection: Connection, channel: BrowserTypeChannel) { - super(connection, channel); - } - - _initialize(payload: { executablePath: string, name: string }) { - this._executablePath = payload.executablePath; - this._name = payload.name; +export class BrowserType extends ChannelOwner { + constructor(connection: Connection, channel: BrowserTypeChannel, initializer: BrowserTypeInitializer) { + super(connection, channel, initializer); } executablePath(): string { - return this._executablePath; + return this._initializer.executablePath; } name(): string { - return this._name; + return this._initializer.name; } async launch(options?: types.LaunchOptions): Promise { diff --git a/src/rpc/client/channelOwner.ts b/src/rpc/client/channelOwner.ts index c41956e247..5491ed60b5 100644 --- a/src/rpc/client/channelOwner.ts +++ b/src/rpc/client/channelOwner.ts @@ -18,17 +18,17 @@ import { EventEmitter } from 'events'; import { Channel } from '../channels'; import { Connection } from '../connection'; -export abstract class ChannelOwner extends EventEmitter { +export abstract class ChannelOwner extends EventEmitter { readonly _channel: T; + readonly _initializer: Initializer; readonly _connection: Connection; static clientSymbol = Symbol('client'); - constructor(connection: Connection, channel: T) { + constructor(connection: Connection, channel: T, initializer: Initializer) { super(); this._connection = connection; this._channel = channel; + this._initializer = initializer; (channel as any)[ChannelOwner.clientSymbol] = this; } - - abstract _initialize(payload: any): void; } diff --git a/src/rpc/client/console.ts b/src/rpc/client/consoleMessage.ts similarity index 66% rename from src/rpc/client/console.ts rename to src/rpc/client/consoleMessage.ts index 6bb109666a..bb82f4ecd0 100644 --- a/src/rpc/client/console.ts +++ b/src/rpc/client/consoleMessage.ts @@ -17,45 +17,33 @@ import * as util from 'util'; import { ConsoleMessageLocation } from '../../types'; import { JSHandle } from './jsHandle'; -import { ConsoleMessageChannel, JSHandleChannel } from '../channels'; +import { ConsoleMessageChannel, ConsoleMessageInitializer } from '../channels'; import { ChannelOwner } from './channelOwner'; import { Connection } from '../connection'; -export class ConsoleMessage extends ChannelOwner { - private _type: string = ''; - private _text: string = ''; - private _args: JSHandle[] = []; - private _location: ConsoleMessageLocation = {}; - +export class ConsoleMessage extends ChannelOwner { static from(request: ConsoleMessageChannel): ConsoleMessage { return request._object; } - constructor(connection: Connection, channel: ConsoleMessageChannel) { - super(connection, channel); - } - - _initialize(params: { type: string, text: string, args: JSHandleChannel[], location: ConsoleMessageLocation }) { - this._type = params.type; - this._text = params.text; - this._args = params.args.map(JSHandle.from); - this._location = params.location; + constructor(connection: Connection, channel: ConsoleMessageChannel, initializer: ConsoleMessageInitializer) { + super(connection, channel, initializer); } type(): string { - return this._type; + return this._initializer.type; } text(): string { - return this._text; + return this._initializer.text; } args(): JSHandle[] { - return this._args; + return this._initializer.args.map(JSHandle.from); } location(): ConsoleMessageLocation { - return this._location; + return this._initializer.location; } [util.inspect.custom]() { diff --git a/src/rpc/client/elementHandle.ts b/src/rpc/client/elementHandle.ts index 5f0151ec4e..b0a118f6c8 100644 --- a/src/rpc/client/elementHandle.ts +++ b/src/rpc/client/elementHandle.ts @@ -15,7 +15,7 @@ */ import * as types from '../../types'; -import { ElementHandleChannel } from '../channels'; +import { ElementHandleChannel, JSHandleInitializer } from '../channels'; import { Frame } from './frame'; import { FuncOn, JSHandle, convertArg } from './jsHandle'; import { Connection } from '../connection'; @@ -31,8 +31,8 @@ export class ElementHandle extends JSHandle { return handle ? ElementHandle.from(handle) : null; } - constructor(connection: Connection, channel: ElementHandleChannel) { - super(connection, channel); + constructor(connection: Connection, channel: ElementHandleChannel, initializer: JSHandleInitializer) { + super(connection, channel, initializer); this._elementChannel = channel; } diff --git a/src/rpc/client/frame.ts b/src/rpc/client/frame.ts index b9f7d9ca04..2031319432 100644 --- a/src/rpc/client/frame.ts +++ b/src/rpc/client/frame.ts @@ -17,7 +17,7 @@ import { assertMaxArguments } from '../../helper'; import * as types from '../../types'; -import { FrameChannel } from '../channels'; +import { FrameChannel, FrameInitializer } from '../channels'; import { BrowserContext } from './browserContext'; import { ChannelOwner } from './channelOwner'; import { ElementHandle, convertSelectOptionValues } from './elementHandle'; @@ -33,12 +33,13 @@ export type GotoOptions = types.NavigateOptions & { export type FunctionWithSource = (source: { context: BrowserContext, page: Page, frame: Frame }, ...args: any) => any; -export class Frame extends ChannelOwner { +export class Frame extends ChannelOwner { _parentFrame: Frame | null = null; _url = ''; _name = ''; private _detached = false; _childFrames = new Set(); + _page: Page | undefined; static from(frame: FrameChannel): Frame { return frame._object; @@ -48,16 +49,13 @@ export class Frame extends ChannelOwner { return frame ? Frame.from(frame) : null; } - constructor(connection: Connection, channel: FrameChannel) { - super(connection, channel); - } - - _initialize(params: { name: string, url: string, parentFrame: FrameChannel | null }) { - this._parentFrame = params.parentFrame ? params.parentFrame._object : null; + constructor(connection: Connection, channel: FrameChannel, initializer: FrameInitializer) { + super(connection, channel, initializer); + this._parentFrame = Frame.fromNullable(initializer.parentFrame); if (this._parentFrame) this._parentFrame._childFrames.add(this); - this._name = params.name; - this._url = params.url; + this._name = initializer.name; + this._url = initializer.url; } async goto(url: string, options: GotoOptions = {}): Promise { diff --git a/src/rpc/client/jsHandle.ts b/src/rpc/client/jsHandle.ts index 932cdffe0e..2a1384459a 100644 --- a/src/rpc/client/jsHandle.ts +++ b/src/rpc/client/jsHandle.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { JSHandleChannel } from '../channels'; +import { JSHandleChannel, JSHandleInitializer } from '../channels'; import { ElementHandle } from './elementHandle'; import { ChannelOwner } from './channelOwner'; import { Connection } from '../connection'; @@ -35,10 +35,7 @@ export type Func1 = string | ((arg: Unboxed) => R | Promise); export type FuncOn = string | ((on: On, arg2: Unboxed) => R | Promise); export type SmartHandle = T extends Node ? ElementHandle : JSHandle; -export class JSHandle extends ChannelOwner { - protected _jsChannel: JSHandleChannel; - private _preview: string | undefined; - +export class JSHandle extends ChannelOwner { static from(handle: JSHandleChannel): JSHandle { return handle._object; } @@ -47,25 +44,20 @@ export class JSHandle extends ChannelOwner { return handle ? JSHandle.from(handle) : null; } - constructor(conection: Connection, channel: JSHandleChannel) { - super(conection, channel); - this._jsChannel = channel; - } - - _initialize(params: { preview: string }) { - this._preview = params.preview; + constructor(conection: Connection, channel: JSHandleChannel, initializer: JSHandleInitializer) { + super(conection, channel, initializer); } async evaluate(pageFunction: FuncOn, arg: Arg): Promise; async evaluate(pageFunction: FuncOn, arg?: any): Promise; async evaluate(pageFunction: FuncOn, arg: Arg): Promise { - return await this._jsChannel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: convertArg(arg) }); + return await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: convertArg(arg) }); } async evaluateHandle(pageFunction: FuncOn, arg: Arg): Promise>; async evaluateHandle(pageFunction: FuncOn, arg?: any): Promise>; async evaluateHandle(pageFunction: FuncOn, arg: Arg): Promise> { - const handleChannel = await this._jsChannel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: convertArg(arg) }); + const handleChannel = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: convertArg(arg) }); return JSHandle.from(handleChannel) as SmartHandle; } @@ -83,13 +75,13 @@ export class JSHandle extends ChannelOwner { async getProperties(): Promise> { const map = new Map(); - for (const { name, value } of await this._jsChannel.getPropertyList()) + for (const { name, value } of await this._channel.getPropertyList()) map.set(name, JSHandle.from(value)); return map; } async jsonValue(): Promise { - return await this._jsChannel.jsonValue(); + return await this._channel.jsonValue(); } asElement(): ElementHandle | null { @@ -97,11 +89,11 @@ export class JSHandle extends ChannelOwner { } async dispose() { - return await this._jsChannel.dispose(); + return await this._channel.dispose(); } toString(): string { - return this._preview!; + return this._initializer.preview; } } diff --git a/src/rpc/client/network.ts b/src/rpc/client/network.ts index de329d6108..08ee61907a 100644 --- a/src/rpc/client/network.ts +++ b/src/rpc/client/network.ts @@ -16,7 +16,7 @@ import { URLSearchParams } from 'url'; import * as types from '../../types'; -import { RequestChannel, ResponseChannel, FrameChannel, RouteChannel } from '../channels'; +import { RequestChannel, ResponseChannel, RouteChannel, RequestInitializer, ResponseInitializer, RouteInitializer } from '../channels'; import { ChannelOwner } from './channelOwner'; import { Frame } from './frame'; import { Connection } from '../connection'; @@ -44,17 +44,10 @@ export type SetNetworkCookieParam = { sameSite?: 'Strict' | 'Lax' | 'None' }; -export class Request extends ChannelOwner { +export class Request extends ChannelOwner { private _redirectedFrom: Request | null = null; private _redirectedTo: Request | null = null; - private _isNavigationRequest = false; _failureText: string | null = null; - private _url: string = ''; - private _resourceType = ''; - private _method = ''; - private _postData: string | null = null; - private _headers: types.Headers = {}; - private _frame: Frame | undefined; static from(request: RequestChannel): Request { return request._object; @@ -64,42 +57,31 @@ export class Request extends ChannelOwner { return request ? Request.from(request) : null; } - constructor(connection: Connection, channel: RequestChannel) { - super(connection, channel); - } - - _initialize(payload: { frame: FrameChannel, redirectedFrom: RequestChannel | null, isNavigationRequest: boolean, - url: string, resourceType: string, method: string, postData: string | null, headers: types.Headers }) { - this._frame = payload.frame._object as Frame; - this._isNavigationRequest = payload.isNavigationRequest; - this._redirectedFrom = Request.fromNullable(payload.redirectedFrom); + constructor(connection: Connection, channel: RequestChannel, initializer: RequestInitializer) { + super(connection, channel, initializer); + this._redirectedFrom = Request.fromNullable(initializer.redirectedFrom); if (this._redirectedFrom) this._redirectedFrom._redirectedTo = this; - this._url = payload.url; - this._resourceType = payload.resourceType; - this._method = payload.method; - this._postData = payload.postData; - this._headers = payload.headers; } url(): string { - return this._url; + return this._initializer.url; } resourceType(): string { - return this._resourceType; + return this._initializer.resourceType; } method(): string { - return this._method; + return this._initializer.method; } postData(): string | null { - return this._postData; + return this._initializer.postData; } postDataJSON(): Object | null { - if (!this._postData) + if (!this._initializer.postData) return null; const contentType = this.headers()['content-type']; @@ -108,17 +90,17 @@ export class Request extends ChannelOwner { if (contentType === 'application/x-www-form-urlencoded') { const entries: Record = {}; - const parsed = new URLSearchParams(this._postData); + const parsed = new URLSearchParams(this._initializer.postData); for (const [k, v] of parsed.entries()) entries[k] = v; return entries; } - return JSON.parse(this._postData); + return JSON.parse(this._initializer.postData); } headers(): {[key: string]: string} { - return { ...this._headers }; + return { ...this._initializer.headers }; } async response(): Promise { @@ -126,11 +108,11 @@ export class Request extends ChannelOwner { } frame(): Frame { - return this._frame!; + return Frame.from(this._initializer.frame); } isNavigationRequest(): boolean { - return this._isNavigationRequest; + return this._initializer.isNavigationRequest; } redirectedFrom(): Request | null { @@ -150,23 +132,17 @@ export class Request extends ChannelOwner { } } -export class Route extends ChannelOwner { - private _request: Request | undefined; - +export class Route extends ChannelOwner { static from(route: RouteChannel): Route { return route._object; } - constructor(connection: Connection, channel: RouteChannel) { - super(connection, channel); - } - - _initialize(params: { request: RequestChannel }) { - this._request = Request.from(params.request); + constructor(connection: Connection, channel: RouteChannel, initializer: RouteInitializer) { + super(connection, channel, initializer); } request(): Request { - return this._request!; + return Request.from(this._initializer.request); } async abort(errorCode: string = 'failed') { @@ -184,13 +160,7 @@ export class Route extends ChannelOwner { export type RouteHandler = (route: Route, request: Request) => void; -export class Response extends ChannelOwner { - private _request: Request | undefined; - private _status: number = 0; - private _statusText: string = ''; - private _url: string = ''; - private _headers: types.Headers = {}; - +export class Response extends ChannelOwner { static from(response: ResponseChannel): Response { return response._object; } @@ -199,36 +169,28 @@ export class Response extends ChannelOwner { return response ? Response.from(response) : null; } - constructor(connection: Connection, channel: ResponseChannel) { - super(connection, channel); - } - - _initialize(payload: { request: RequestChannel, url: string, status: number, statusText: string, headers: types.Headers }) { - this._request = Request.from(payload.request); - this._status = payload.status; - this._statusText = payload.statusText; - this._url = payload.url; - this._headers = payload.headers; + constructor(connection: Connection, channel: ResponseChannel, initializer: ResponseInitializer) { + super(connection, channel, initializer); } url(): string { - return this._url; + return this._initializer.url; } ok(): boolean { - return this._status === 0 || (this._status >= 200 && this._status <= 299); + return this._initializer.status === 0 || (this._initializer.status >= 200 && this._initializer.status <= 299); } status(): number { - return this._status; + return this._initializer.status; } statusText(): string { - return this._statusText; + return this._initializer.statusText; } headers(): object { - return { ...this._headers }; + return { ...this._initializer.headers }; } async finished(): Promise { @@ -250,10 +212,10 @@ export class Response extends ChannelOwner { } request(): Request { - return this._request!; + return Request.from(this._initializer.request); } frame(): Frame { - return this._request!.frame(); + return Request.from(this._initializer.request).frame(); } } diff --git a/src/rpc/client/page.ts b/src/rpc/client/page.ts index 7c67647b44..6e88d02a7a 100644 --- a/src/rpc/client/page.ts +++ b/src/rpc/client/page.ts @@ -19,7 +19,7 @@ import { EventEmitter } from 'events'; import { Events } from '../../events'; import { assert, assertMaxArguments, helper, Listener, serializeError, parseError } from '../../helper'; import * as types from '../../types'; -import { BrowserContextChannel, FrameChannel, PageChannel, BindingCallChannel, Channel } from '../channels'; +import { PageChannel, BindingCallChannel, Channel, PageInitializer, BindingCallInitializer } from '../channels'; import { BrowserContext } from './browserContext'; import { ChannelOwner } from './channelOwner'; import { ElementHandle } from './elementHandle'; @@ -29,17 +29,17 @@ import { Request, Response, RouteHandler, Route } from './network'; import { Connection } from '../connection'; import { Keyboard, Mouse } from './input'; import { Accessibility } from './accessibility'; -import { ConsoleMessage } from './console'; +import { ConsoleMessage } from './consoleMessage'; -export class Page extends ChannelOwner { +export class Page extends ChannelOwner { readonly pdf: ((options?: types.PDFOptions) => Promise) | undefined; - private _browserContext: BrowserContext | undefined; - private _mainFrame: Frame | undefined; + _browserContext: BrowserContext | undefined; + private _mainFrame: Frame; private _frames = new Set(); private _workers: Worker[] = []; private _closed = false; - private _viewportSize: types.Size | null = null; + private _viewportSize: types.Size | null; private _routes: { url: types.URLMatch, handler: RouteHandler }[] = []; readonly accessibility: Accessibility; @@ -55,18 +55,16 @@ export class Page extends ChannelOwner { return page ? Page.from(page) : null; } - constructor(connection: Connection, channel: PageChannel) { - super(connection, channel); + constructor(connection: Connection, channel: PageChannel, initializer: PageInitializer) { + super(connection, channel, initializer); this.accessibility = new Accessibility(channel); this.keyboard = new Keyboard(channel); this.mouse = new Mouse(channel); - } - _initialize(payload: { browserContext: BrowserContextChannel, mainFrame: FrameChannel, viewportSize: types.Size }) { - this._browserContext = BrowserContext.from(payload.browserContext); - this._mainFrame = Frame.from(payload.mainFrame); + this._mainFrame = Frame.from(initializer.mainFrame); + this._mainFrame._page = this; this._frames.add(this._mainFrame); - this._viewportSize = payload.viewportSize; + this._viewportSize = initializer.viewportSize; this._channel.on('bindingCall', bindingCall => this._onBinding(BindingCall.from(bindingCall))); this._channel.on('close', () => this._onClose()); @@ -88,6 +86,7 @@ export class Page extends ChannelOwner { } private _onFrameAttached(frame: Frame) { + frame._page = this; this._frames.add(frame); if (frame._parentFrame) frame._parentFrame._childFrames.add(frame); @@ -118,7 +117,7 @@ export class Page extends ChannelOwner { } async _onBinding(bindingCall: BindingCall) { - const func = this._bindings.get(bindingCall.name); + const func = this._bindings.get(bindingCall._initializer.name); if (func) bindingCall.call(func); this._browserContext!._onBinding(bindingCall); @@ -138,7 +137,7 @@ export class Page extends ChannelOwner { } mainFrame(): Frame { - return this._mainFrame!; + return this._mainFrame; } frame(options: string | { name?: string, url?: types.URLMatch }): Frame | null { @@ -165,48 +164,48 @@ export class Page extends ChannelOwner { } async $(selector: string): Promise | null> { - return await this._mainFrame!.$(selector); + return await this._mainFrame.$(selector); } async waitForSelector(selector: string, options?: types.WaitForElementOptions): Promise | null> { - return await this._mainFrame!.waitForSelector(selector, options); + return await this._mainFrame.waitForSelector(selector, options); } async dispatchEvent(selector: string, type: string, eventInit?: Object, options?: types.TimeoutOptions): Promise { - return await this._mainFrame!.dispatchEvent(selector, type, eventInit, options); + return await this._mainFrame.dispatchEvent(selector, type, eventInit, options); } async evaluateHandle(pageFunction: Func1, arg: Arg): Promise>; async evaluateHandle(pageFunction: Func1, arg?: any): Promise>; async evaluateHandle(pageFunction: Func1, arg: Arg): Promise> { assertMaxArguments(arguments.length, 2); - return await this._mainFrame!.evaluateHandle(pageFunction, arg); + return await this._mainFrame.evaluateHandle(pageFunction, arg); } async $eval(selector: string, pageFunction: FuncOn, arg: Arg): Promise; async $eval(selector: string, pageFunction: FuncOn, arg?: any): Promise; async $eval(selector: string, pageFunction: FuncOn, arg: Arg): Promise { assertMaxArguments(arguments.length, 3); - return await this._mainFrame!.$eval(selector, pageFunction, arg); + return await this._mainFrame.$eval(selector, pageFunction, arg); } async $$eval(selector: string, pageFunction: FuncOn, arg: Arg): Promise; async $$eval(selector: string, pageFunction: FuncOn, arg?: any): Promise; async $$eval(selector: string, pageFunction: FuncOn, arg: Arg): Promise { assertMaxArguments(arguments.length, 3); - return await this._mainFrame!.$$eval(selector, pageFunction, arg); + return await this._mainFrame.$$eval(selector, pageFunction, arg); } async $$(selector: string): Promise[]> { - return await this._mainFrame!.$$(selector); + return await this._mainFrame.$$(selector); } async addScriptTag(options: { url?: string; path?: string; content?: string; type?: string; }): Promise { - return await this._mainFrame!.addScriptTag(options); + return await this._mainFrame.addScriptTag(options); } async addStyleTag(options: { url?: string; path?: string; content?: string; }): Promise { - return await this._mainFrame!.addStyleTag(options); + return await this._mainFrame.addStyleTag(options); } async exposeFunction(name: string, playwrightFunction: Function) { @@ -247,11 +246,11 @@ export class Page extends ChannelOwner { } async waitForLoadState(state?: types.LifecycleEvent, options?: types.TimeoutOptions): Promise { - return this._mainFrame!.waitForLoadState(state, options); + return this._mainFrame.waitForLoadState(state, options); } async waitForNavigation(options?: types.WaitForNavigationOptions): Promise { - return this._mainFrame!.waitForNavigation(options); + return this._mainFrame.waitForNavigation(options); } async waitForRequest(urlOrPredicate: string | RegExp | ((r: Request) => boolean), options: types.TimeoutOptions = {}): Promise { @@ -455,31 +454,24 @@ export class Worker extends EventEmitter { } } -export class BindingCall extends ChannelOwner { - name: string = ''; - source: { context: BrowserContext, page: Page, frame: Frame } | undefined; - args: any[] = []; +export class BindingCall extends ChannelOwner { static from(channel: BindingCallChannel): BindingCall { return channel._object; } - constructor(connection: Connection, channel: BindingCallChannel) { - super(connection, channel); - } - - _initialize(params: { name: string, context: BrowserContextChannel, page: PageChannel, frame: FrameChannel, args: any[] }) { - this.name = params.name; - this.source = { - context: BrowserContext.from(params.context), - page: Page.from(params.page), - frame: Frame.from(params.frame) - }; - this.args = params.args; + constructor(connection: Connection, channel: BindingCallChannel, initializer: BindingCallInitializer) { + super(connection, channel, initializer); } async call(func: FunctionWithSource) { try { - this._channel.resolve({ result: await func(this.source!, ...this.args) }); + const frame = Frame.from(this._initializer.frame); + const source = { + context: frame._page!._browserContext!, + page: frame._page!, + frame + }; + this._channel.resolve({ result: await func(source, ...this._initializer.args) }); } catch (e) { this._channel.reject({ error: serializeError(e) }); } diff --git a/src/rpc/connection.ts b/src/rpc/connection.ts index ba6dac4d7e..a3b5c18873 100644 --- a/src/rpc/connection.ts +++ b/src/rpc/connection.ts @@ -26,62 +26,75 @@ import { Request, Response, Route } from './client/network'; import { Page, BindingCall } from './client/page'; import debug = require('debug'); import { Channel } from './channels'; -import { ConsoleMessage } from './client/console'; +import { ConsoleMessage } from './client/consoleMessage'; export class Connection { private _channels = new Map(); + private _waitingForObject = new Map(); sendMessageToServerTransport = (message: any): Promise => Promise.resolve(); constructor() {} - createRemoteObject(type: string, guid: string): any { + private _createRemoteObject(type: string, guid: string, initializer: any): any { const channel = this._createChannel(guid) as any; this._channels.set(guid, channel); - let result: ChannelOwner; + let result: ChannelOwner; + initializer = this._replaceGuidsWithChannels(initializer); switch (type) { case 'browserType': - result = new BrowserType(this, channel); + result = new BrowserType(this, channel, initializer); break; case 'browser': - result = new Browser(this, channel); + result = new Browser(this, channel, initializer); break; case 'context': - result = new BrowserContext(this, channel); + result = new BrowserContext(this, channel, initializer); break; case 'page': - result = new Page(this, channel); + result = new Page(this, channel, initializer); break; case 'frame': - result = new Frame(this, channel); + result = new Frame(this, channel, initializer); break; case 'request': - result = new Request(this, channel); + result = new Request(this, channel, initializer); break; case 'response': - result = new Response(this, channel); + result = new Response(this, channel, initializer); break; case 'route': - result = new Route(this, channel); + result = new Route(this, channel, initializer); break; case 'bindingCall': - result = new BindingCall(this, channel); + result = new BindingCall(this, channel, initializer); break; case 'jsHandle': - result = new JSHandle(this, channel); + result = new JSHandle(this, channel, initializer); break; case 'elementHandle': - result = new ElementHandle(this, channel); + result = new ElementHandle(this, channel, initializer); break; case 'consoleMessage': - result = new ConsoleMessage(this, channel); + result = new ConsoleMessage(this, channel, initializer); break; default: throw new Error('Missing type ' + type); } channel._object = result; + const callback = this._waitingForObject.get(guid); + if (callback) { + callback(result); + this._waitingForObject.delete(guid); + } return result; } + waitForObjectWithKnownName(guid: string): Promise { + if (this._channels.has(guid)) + return this._channels.get(guid)!._object; + return new Promise(f => this._waitingForObject.set(guid, f)); + } + async sendMessageToServer(message: { guid: string, method: string, params: any }) { const converted = {...message, params: this._replaceChannelsWithGuids(message.params)}; debug('pw:channel:command')(converted); @@ -95,15 +108,11 @@ export class Connection { const { guid, method, params } = message; if (method === '__create__') { - this.createRemoteObject(params.type, guid); + this._createRemoteObject(params.type, guid, params.initializer); return; } const channel = this._channels.get(guid)!; - if (message.method === '__init__') { - channel._object._initialize(this._replaceGuidsWithChannels(params)); - return; - } channel.emit(method, this._replaceGuidsWithChannels(params)); } diff --git a/src/rpc/dispatcher.ts b/src/rpc/dispatcher.ts index c414893e64..1c5b450a49 100644 --- a/src/rpc/dispatcher.ts +++ b/src/rpc/dispatcher.ts @@ -18,13 +18,13 @@ import { EventEmitter } from 'events'; import { helper } from '../helper'; import { Channel } from './channels'; -export class Dispatcher extends EventEmitter implements Channel { +export class Dispatcher extends EventEmitter implements Channel { readonly _guid: string; readonly _type: string; protected _scope: DispatcherScope; _object: any; - constructor(scope: DispatcherScope, object: any, type: string, guid = type + '@' + helper.guid()) { + constructor(scope: DispatcherScope, object: any, type: string, initializer: Initializer, guid = type + '@' + helper.guid()) { super(); this._type = type; this._guid = guid; @@ -32,20 +32,16 @@ export class Dispatcher extends EventEmitter implements Channel { this._scope = scope; scope.dispatchers.set(this._guid, this); object[scope.dispatcherSymbol] = this; - this._scope.sendMessageToClient(this._guid, '__create__', { type }); + this._scope.sendMessageToClient(this._guid, '__create__', { type, initializer }); } - _initialize(payload: any) { - this._scope.sendMessageToClient(this._guid, '__init__', payload); - } - - _dispatchEvent(method: string, params: Dispatcher | any = {}) { + _dispatchEvent(method: string, params: Dispatcher | any = {}) { this._scope.sendMessageToClient(this._guid, method, params); } } export class DispatcherScope { - readonly dispatchers = new Map(); + readonly dispatchers = new Map>(); readonly dispatcherSymbol = Symbol('dispatcher'); sendMessageToClientTransport = (message: any) => {}; diff --git a/src/rpc/server/browserContextDispatcher.ts b/src/rpc/server/browserContextDispatcher.ts index 4694cf3194..5a5c45d5cb 100644 --- a/src/rpc/server/browserContextDispatcher.ts +++ b/src/rpc/server/browserContextDispatcher.ts @@ -17,14 +17,13 @@ import * as types from '../../types'; import { BrowserContextBase, BrowserContext } from '../../browserContext'; import { Events } from '../../events'; -import { BrowserDispatcher } from './browserDispatcher'; import { Dispatcher, DispatcherScope } from '../dispatcher'; import { PageDispatcher, BindingCallDispatcher } from './pageDispatcher'; -import { PageChannel, BrowserContextChannel } from '../channels'; +import { PageChannel, BrowserContextChannel, BrowserContextInitializer } from '../channels'; import { RouteDispatcher, RequestDispatcher } from './networkDispatchers'; import { Page } from '../../page'; -export class BrowserContextDispatcher extends Dispatcher implements BrowserContextChannel { +export class BrowserContextDispatcher extends Dispatcher implements BrowserContextChannel { private _context: BrowserContextBase; static from(scope: DispatcherScope, browserContext: BrowserContext): BrowserContextDispatcher { @@ -34,10 +33,7 @@ export class BrowserContextDispatcher extends Dispatcher implements BrowserConte } constructor(scope: DispatcherScope, context: BrowserContextBase) { - super(scope, context, 'context'); - this._initialize({ - browser: BrowserDispatcher.from(scope, context._browserBase) - }); + super(scope, context, 'context', {}); this._context = context; context.on(Events.BrowserContext.Page, page => this._dispatchEvent('page', PageDispatcher.from(this._scope, page))); context.on(Events.BrowserContext.Close, () => { diff --git a/src/rpc/server/browserDispatcher.ts b/src/rpc/server/browserDispatcher.ts index 1ac9f081d8..53ccaf3a18 100644 --- a/src/rpc/server/browserDispatcher.ts +++ b/src/rpc/server/browserDispatcher.ts @@ -18,11 +18,11 @@ import { BrowserBase } from '../../browser'; import { BrowserContextBase } from '../../browserContext'; import * as types from '../../types'; import { BrowserContextDispatcher } from './browserContextDispatcher'; -import { BrowserChannel, BrowserContextChannel, PageChannel } from '../channels'; +import { BrowserChannel, BrowserContextChannel, PageChannel, BrowserInitializer } from '../channels'; import { Dispatcher, DispatcherScope } from '../dispatcher'; import { PageDispatcher } from './pageDispatcher'; -export class BrowserDispatcher extends Dispatcher implements BrowserChannel { +export class BrowserDispatcher extends Dispatcher implements BrowserChannel { private _browser: BrowserBase; static from(scope: DispatcherScope, browser: BrowserBase): BrowserDispatcher { @@ -38,8 +38,7 @@ export class BrowserDispatcher extends Dispatcher implements BrowserChannel { } constructor(scope: DispatcherScope, browser: BrowserBase) { - super(scope, browser, 'browser'); - this._initialize({}); + super(scope, browser, 'browser', {}); this._browser = browser; } diff --git a/src/rpc/server/browserTypeDispatcher.ts b/src/rpc/server/browserTypeDispatcher.ts index 234d64e89b..0e29bdba02 100644 --- a/src/rpc/server/browserTypeDispatcher.ts +++ b/src/rpc/server/browserTypeDispatcher.ts @@ -18,12 +18,12 @@ import { BrowserBase } from '../../browser'; import { BrowserTypeBase } from '../../server/browserType'; import * as types from '../../types'; import { BrowserDispatcher } from './browserDispatcher'; -import { BrowserChannel, BrowserTypeChannel, BrowserContextChannel } from '../channels'; +import { BrowserChannel, BrowserTypeChannel, BrowserContextChannel, BrowserTypeInitializer } from '../channels'; import { Dispatcher, DispatcherScope } from '../dispatcher'; import { BrowserContextBase } from '../../browserContext'; import { BrowserContextDispatcher } from './browserContextDispatcher'; -export class BrowserTypeDispatcher extends Dispatcher implements BrowserTypeChannel { +export class BrowserTypeDispatcher extends Dispatcher implements BrowserTypeChannel { private _browserType: BrowserTypeBase; static from(scope: DispatcherScope, browserType: BrowserTypeBase): BrowserTypeDispatcher { @@ -33,8 +33,10 @@ export class BrowserTypeDispatcher extends Dispatcher implements BrowserTypeChan } constructor(scope: DispatcherScope, browserType: BrowserTypeBase) { - super(scope, browserType, 'browserType', browserType.name()); - this._initialize({ executablePath: browserType.executablePath(), name: browserType.name() }); + super(scope, browserType, 'browserType', { + executablePath: browserType.executablePath(), + name: browserType.name() + }, browserType.name()); this._browserType = browserType; } diff --git a/src/rpc/server/consoleMessageDispatcher.ts b/src/rpc/server/consoleMessageDispatcher.ts index 85e042a879..88a630f729 100644 --- a/src/rpc/server/consoleMessageDispatcher.ts +++ b/src/rpc/server/consoleMessageDispatcher.ts @@ -15,11 +15,11 @@ */ import { ConsoleMessage } from '../../console'; -import { ConsoleMessageChannel } from '../channels'; +import { ConsoleMessageChannel, ConsoleMessageInitializer } from '../channels'; import { Dispatcher, DispatcherScope } from '../dispatcher'; import { ElementHandleDispatcher } from './elementHandlerDispatcher'; -export class ConsoleMessageDispatcher extends Dispatcher implements ConsoleMessageChannel { +export class ConsoleMessageDispatcher extends Dispatcher implements ConsoleMessageChannel { static from(scope: DispatcherScope, message: ConsoleMessage): ConsoleMessageDispatcher { if ((message as any)[scope.dispatcherSymbol]) return (message as any)[scope.dispatcherSymbol]; @@ -27,11 +27,10 @@ export class ConsoleMessageDispatcher extends Dispatcher implements ConsoleMessa } constructor(scope: DispatcherScope, message: ConsoleMessage) { - super(scope, message, 'consoleMessage'); - this._initialize({ + super(scope, message, 'consoleMessage', { type: message.type(), text: message.text(), - args: message.args().map(a => ElementHandleDispatcher.from(this._scope, a)), + args: message.args().map(a => ElementHandleDispatcher.from(scope, a)), location: message.location(), }); } diff --git a/src/rpc/server/elementHandlerDispatcher.ts b/src/rpc/server/elementHandlerDispatcher.ts index 19b1c91012..1ecdd8f523 100644 --- a/src/rpc/server/elementHandlerDispatcher.ts +++ b/src/rpc/server/elementHandlerDispatcher.ts @@ -51,9 +51,8 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements Eleme constructor(scope: DispatcherScope, elementHandle: ElementHandle) { - super(scope, elementHandle, true); + super(scope, elementHandle); this._elementHandle = elementHandle; - this._initialize({ preview: elementHandle.toString(), frame: FrameDispatcher.from(scope, elementHandle._context.frame) }); this._elementHandle = elementHandle; } diff --git a/src/rpc/server/frameDispatcher.ts b/src/rpc/server/frameDispatcher.ts index ef1f3e36b6..4104eeffef 100644 --- a/src/rpc/server/frameDispatcher.ts +++ b/src/rpc/server/frameDispatcher.ts @@ -16,13 +16,13 @@ import { Frame } from '../../frames'; import * as types from '../../types'; -import { ElementHandleChannel, FrameChannel, JSHandleChannel, ResponseChannel } from '../channels'; +import { ElementHandleChannel, FrameChannel, JSHandleChannel, ResponseChannel, FrameInitializer } from '../channels'; import { Dispatcher, DispatcherScope } from '../dispatcher'; import { ElementHandleDispatcher, convertSelectOptionValues } from './elementHandlerDispatcher'; import { JSHandleDispatcher } from './jsHandleDispatcher'; import { ResponseDispatcher } from './networkDispatchers'; -export class FrameDispatcher extends Dispatcher implements FrameChannel { +export class FrameDispatcher extends Dispatcher implements FrameChannel { private _frame: Frame; static from(scope: DispatcherScope, frame: Frame): FrameDispatcher { @@ -38,14 +38,12 @@ export class FrameDispatcher extends Dispatcher implements FrameChannel { } constructor(scope: DispatcherScope, frame: Frame) { - super(scope, frame, 'frame'); - this._frame = frame; - const parentFrame = frame.parentFrame(); - this._initialize({ + super(scope, frame, 'frame', { url: frame.url(), name: frame.name(), - parentFrame: FrameDispatcher.fromNullable(this._scope, parentFrame) + parentFrame: FrameDispatcher.fromNullable(scope, frame.parentFrame()) }); + this._frame = frame; } async goto(params: { url: string, options: types.GotoOptions }): Promise { diff --git a/src/rpc/server/jsHandleDispatcher.ts b/src/rpc/server/jsHandleDispatcher.ts index 4ec0a0c70a..e6175dba06 100644 --- a/src/rpc/server/jsHandleDispatcher.ts +++ b/src/rpc/server/jsHandleDispatcher.ts @@ -15,17 +15,17 @@ */ import * as js from '../../javascript'; -import { JSHandleChannel } from '../channels'; +import { JSHandleChannel, JSHandleInitializer } from '../channels'; import { Dispatcher, DispatcherScope } from '../dispatcher'; import { convertArg } from './frameDispatcher'; -export class JSHandleDispatcher extends Dispatcher implements JSHandleChannel { +export class JSHandleDispatcher extends Dispatcher implements JSHandleChannel { readonly _jsHandle: js.JSHandle; - constructor(scope: DispatcherScope, jsHandle: js.JSHandle, omitInit?: boolean) { - super(scope, jsHandle, jsHandle.asElement() ? 'elementHandle' : 'jsHandle'); - if (!omitInit) - this._initialize({ preview: jsHandle.toString() }); + constructor(scope: DispatcherScope, jsHandle: js.JSHandle) { + super(scope, jsHandle, jsHandle.asElement() ? 'elementHandle' : 'jsHandle', { + preview: jsHandle.toString(), + }); this._jsHandle = jsHandle; } diff --git a/src/rpc/server/networkDispatchers.ts b/src/rpc/server/networkDispatchers.ts index 9111bdb673..fcb7a067c5 100644 --- a/src/rpc/server/networkDispatchers.ts +++ b/src/rpc/server/networkDispatchers.ts @@ -16,11 +16,11 @@ import { Request, Response, Route } from '../../network'; import * as types from '../../types'; -import { RequestChannel, ResponseChannel, RouteChannel } from '../channels'; +import { RequestChannel, ResponseChannel, RouteChannel, ResponseInitializer, RequestInitializer, RouteInitializer } from '../channels'; import { Dispatcher, DispatcherScope } from '../dispatcher'; import { FrameDispatcher } from './frameDispatcher'; -export class RequestDispatcher extends Dispatcher implements RequestChannel { +export class RequestDispatcher extends Dispatcher implements RequestChannel { private _request: Request; static from(scope: DispatcherScope, request: Request): RequestDispatcher { @@ -34,18 +34,15 @@ export class RequestDispatcher extends Dispatcher implements RequestChannel { } constructor(scope: DispatcherScope, request: Request) { - super(scope, request, 'request'); - this._initialize({ + super(scope, request, 'request', { + frame: FrameDispatcher.from(scope, request.frame()), url: request.url(), resourceType: request.resourceType(), method: request.method(), postData: request.postData(), headers: request.headers(), isNavigationRequest: request.isNavigationRequest(), - failure: request.failure(), - frame: FrameDispatcher.from(this._scope, request.frame()), - redirectedFrom: RequestDispatcher.fromNullable(this._scope, request.redirectedFrom()), - redirectedTo: RequestDispatcher.fromNullable(this._scope, request.redirectedTo()), + redirectedFrom: RequestDispatcher.fromNullable(scope, request.redirectedFrom()), }); this._request = request; } @@ -55,7 +52,7 @@ export class RequestDispatcher extends Dispatcher implements RequestChannel { } } -export class ResponseDispatcher extends Dispatcher implements ResponseChannel { +export class ResponseDispatcher extends Dispatcher implements ResponseChannel { private _response: Response; static from(scope: DispatcherScope, response: Response): ResponseDispatcher { @@ -69,12 +66,9 @@ export class ResponseDispatcher extends Dispatcher implements ResponseChannel { } constructor(scope: DispatcherScope, response: Response) { - super(scope, response, 'response'); - this._initialize({ - frame: FrameDispatcher.from(this._scope, response.frame()), - request: RequestDispatcher.from(this._scope, response.request())!, + super(scope, response, 'response', { + request: RequestDispatcher.from(scope, response.request())!, url: response.url(), - ok: response.ok(), status: response.status(), statusText: response.statusText(), headers: response.headers(), @@ -91,7 +85,7 @@ export class ResponseDispatcher extends Dispatcher implements ResponseChannel { } } -export class RouteDispatcher extends Dispatcher implements RouteChannel { +export class RouteDispatcher extends Dispatcher implements RouteChannel { private _route: Route; static from(scope: DispatcherScope, route: Route): RouteDispatcher { @@ -105,8 +99,9 @@ export class RouteDispatcher extends Dispatcher implements RouteChannel { } constructor(scope: DispatcherScope, route: Route) { - super(scope, route, 'route'); - this._initialize({ request: RequestDispatcher.from(this._scope, route.request()) }); + super(scope, route, 'route', { + request: RequestDispatcher.from(scope, route.request()) + }); this._route = route; } diff --git a/src/rpc/server/pageDispatcher.ts b/src/rpc/server/pageDispatcher.ts index 4d1f7a59b0..84f9ba1477 100644 --- a/src/rpc/server/pageDispatcher.ts +++ b/src/rpc/server/pageDispatcher.ts @@ -19,16 +19,15 @@ import { Request } from '../../network'; import { Frame } from '../../frames'; import { Page } from '../../page'; import * as types from '../../types'; -import { ElementHandleChannel, PageChannel, ResponseChannel, BindingCallChannel } from '../channels'; +import { ElementHandleChannel, PageChannel, ResponseChannel, BindingCallChannel, PageInitializer, BindingCallInitializer } from '../channels'; import { Dispatcher, DispatcherScope } from '../dispatcher'; -import { BrowserContextDispatcher } from './browserContextDispatcher'; import { FrameDispatcher } from './frameDispatcher'; import { RequestDispatcher, ResponseDispatcher, RouteDispatcher } from './networkDispatchers'; import { ConsoleMessageDispatcher } from './consoleMessageDispatcher'; import { BrowserContext } from '../../browserContext'; import { serializeError, parseError } from '../../helper'; -export class PageDispatcher extends Dispatcher implements PageChannel { +export class PageDispatcher extends Dispatcher implements PageChannel { private _page: Page; static from(scope: DispatcherScope, page: Page): PageDispatcher { @@ -44,11 +43,9 @@ export class PageDispatcher extends Dispatcher implements PageChannel { } constructor(scope: DispatcherScope, page: Page) { - super(scope, page, 'page'); - this._initialize({ - browserContext: BrowserContextDispatcher.from(scope, page._browserContext), + super(scope, page, 'page', { mainFrame: FrameDispatcher.from(scope, page.mainFrame()), - frames: page.frames().map(f => FrameDispatcher.from(this._scope, f)), + viewportSize: page.viewportSize() }); this._page = page; page.on(Events.Page.Close, () => this._dispatchEvent('close')); @@ -196,18 +193,15 @@ export class PageDispatcher extends Dispatcher implements PageChannel { } -export class BindingCallDispatcher extends Dispatcher implements BindingCallChannel { +export class BindingCallDispatcher extends Dispatcher implements BindingCallChannel { private _resolve: ((arg: any) => void) | undefined; private _reject: ((error: any) => void) | undefined; private _promise: Promise; constructor(scope: DispatcherScope, name: string, source: { context: BrowserContext, page: Page, frame: Frame }, args: any[]) { - super(scope, {}, 'bindingCall'); - this._initialize({ - name, - context: BrowserContextDispatcher.from(scope, source.context), - page: PageDispatcher.from(scope, source.page), + super(scope, {}, 'bindingCall', { frame: FrameDispatcher.from(scope, source.frame), + name, args }); this._promise = new Promise((resolve, reject) => { diff --git a/test/test.js b/test/test.js index fe85ade175..74138a3e04 100644 --- a/test/test.js +++ b/test/test.js @@ -113,16 +113,15 @@ function collect(browserNames) { for (const browserName of browserNames) { const browserType = playwright[browserName]; - let overridenBrowserType = browserType; - - // Channel substitute - if (process.env.PWCHANNEL) { - BrowserTypeDispatcher.from(dispatcherScope, browserType); - overridenBrowserType = connection.createRemoteObject('browserType', browserType.name()); - } const browserTypeEnvironment = new Environment('BrowserType'); browserTypeEnvironment.beforeAll(async state => { + // Channel substitute + let overridenBrowserType = browserType; + if (process.env.PWCHANNEL) { + BrowserTypeDispatcher.from(dispatcherScope, browserType); + overridenBrowserType = await connection.waitForObjectWithKnownName(browserType.name()); + } state.browserType = overridenBrowserType; }); browserTypeEnvironment.afterAll(async state => {