mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-16 07:33:35 +03:00
chore(rpc): support downloads, dialogs, persistent context (#2733)
This commit is contained in:
parent
b54303a386
commit
6393407a6a
@ -33,6 +33,7 @@ export type BrowserTypeInitializer = {
|
||||
name: string
|
||||
};
|
||||
|
||||
|
||||
export interface BrowserChannel extends Channel {
|
||||
close(): Promise<void>;
|
||||
newContext(params: { options?: types.BrowserContextOptions }): Promise<BrowserContextChannel>;
|
||||
@ -40,6 +41,7 @@ export interface BrowserChannel extends Channel {
|
||||
}
|
||||
export type BrowserInitializer = {};
|
||||
|
||||
|
||||
export interface BrowserContextChannel extends Channel {
|
||||
addCookies(params: { cookies: types.SetNetworkCookieParam[] }): Promise<void>;
|
||||
addInitScript(params: { source: string }): Promise<void>;
|
||||
@ -59,17 +61,23 @@ export interface BrowserContextChannel extends Channel {
|
||||
setOffline(params: { offline: boolean }): Promise<void>;
|
||||
waitForEvent(params: { event: string }): Promise<any>;
|
||||
}
|
||||
export type BrowserContextInitializer = {};
|
||||
export type BrowserContextInitializer = {
|
||||
pages: PageChannel[]
|
||||
};
|
||||
|
||||
|
||||
export interface PageChannel extends Channel {
|
||||
on(event: 'bindingCall', callback: (params: BindingCallChannel) => void): this;
|
||||
on(event: 'close', callback: () => void): this;
|
||||
on(event: 'console', callback: (params: ConsoleMessageChannel) => void): this;
|
||||
on(event: 'dialog', callback: (params: DialogChannel) => void): this;
|
||||
on(event: 'download', callback: (params: DownloadChannel) => void): this;
|
||||
on(event: 'frameAttached', callback: (params: FrameChannel) => void): this;
|
||||
on(event: 'frameDetached', callback: (params: FrameChannel) => void): this;
|
||||
on(event: 'frameNavigated', callback: (params: { frame: FrameChannel, url: string, name: string }) => void): this;
|
||||
on(event: 'frameNavigated', callback: (params: { frame: FrameChannel, url: string, name: string }) => void): this;
|
||||
on(event: 'pageError', callback: (params: { error: types.Error }) => void): this;
|
||||
on(event: 'popup', callback: (params: PageChannel) => void): this;
|
||||
on(event: 'request', callback: (params: RequestChannel) => void): this;
|
||||
on(event: 'requestFailed', callback: (params: { request: RequestChannel, failureText: string | null }) => void): this;
|
||||
on(event: 'requestFinished', callback: (params: RequestChannel) => void): this;
|
||||
@ -112,6 +120,7 @@ export type PageInitializer = {
|
||||
viewportSize: types.Size | null
|
||||
};
|
||||
|
||||
|
||||
export interface FrameChannel extends Channel {
|
||||
$$eval(params: { selector: string; expression: string, isFunction: boolean, arg: any }): Promise<any>;
|
||||
$eval(params: { selector: string; expression: string, isFunction: boolean, arg: any }): Promise<any>;
|
||||
@ -153,6 +162,7 @@ export type FrameInitializer = {
|
||||
parentFrame: FrameChannel | null
|
||||
};
|
||||
|
||||
|
||||
export interface JSHandleChannel extends Channel {
|
||||
dispose(): Promise<void>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<any>;
|
||||
@ -164,6 +174,7 @@ export type JSHandleInitializer = {
|
||||
preview: string,
|
||||
};
|
||||
|
||||
|
||||
export interface ElementHandleChannel extends JSHandleChannel {
|
||||
$$eval(params: { selector: string; expression: string, isFunction: boolean, arg: any }): Promise<any>;
|
||||
$eval(params: { selector: string; expression: string, isFunction: boolean, arg: any }): Promise<any>;
|
||||
@ -193,6 +204,7 @@ export interface ElementHandleChannel extends JSHandleChannel {
|
||||
uncheck(params: { options?: types.TimeoutOptions & { force?: boolean } & { noWaitAfter?: boolean } }): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
export interface RequestChannel extends Channel {
|
||||
response(): Promise<ResponseChannel | null>;
|
||||
}
|
||||
@ -207,6 +219,7 @@ export type RequestInitializer = {
|
||||
redirectedFrom: RequestChannel | null,
|
||||
};
|
||||
|
||||
|
||||
export interface RouteChannel extends Channel {
|
||||
abort(params: { errorCode: string }): Promise<void>;
|
||||
continue(params: { overrides: { method?: string, headers?: types.Headers, postData?: string } }): Promise<void>;
|
||||
@ -216,6 +229,7 @@ export type RouteInitializer = {
|
||||
request: RequestChannel,
|
||||
};
|
||||
|
||||
|
||||
export interface ResponseChannel extends Channel {
|
||||
body(): Promise<Buffer>;
|
||||
finished(): Promise<Error | null>;
|
||||
@ -228,6 +242,7 @@ export type ResponseInitializer = {
|
||||
headers: types.Headers,
|
||||
};
|
||||
|
||||
|
||||
export interface ConsoleMessageChannel extends Channel {
|
||||
}
|
||||
export type ConsoleMessageInitializer = {
|
||||
@ -237,6 +252,7 @@ export type ConsoleMessageInitializer = {
|
||||
location: types.ConsoleMessageLocation,
|
||||
};
|
||||
|
||||
|
||||
export interface BindingCallChannel extends Channel {
|
||||
reject(params: { error: types.Error }): void;
|
||||
resolve(params: { result: any }): void;
|
||||
@ -246,3 +262,25 @@ export type BindingCallInitializer = {
|
||||
name: string,
|
||||
args: any[]
|
||||
};
|
||||
|
||||
|
||||
export interface DialogChannel extends Channel {
|
||||
accept(params: { promptText?: string }): Promise<void>;
|
||||
dismiss(): Promise<void>;
|
||||
}
|
||||
export type DialogInitializer = {
|
||||
type: string,
|
||||
message: string,
|
||||
defaultValue: string,
|
||||
};
|
||||
|
||||
|
||||
export interface DownloadChannel extends Channel {
|
||||
path(): Promise<string>;
|
||||
failure(): Promise<string | null>;
|
||||
delete(): Promise<void>;
|
||||
}
|
||||
export type DownloadInitializer = {
|
||||
url: string,
|
||||
suggestedFilename: string,
|
||||
};
|
||||
|
@ -50,7 +50,10 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
|
||||
}
|
||||
|
||||
async newPage(options?: types.BrowserContextOptions): Promise<Page> {
|
||||
return Page.from(await this._channel.newPage({ options }));
|
||||
const context = await this.newContext(options);
|
||||
const page = await context.newPage();
|
||||
page._ownedContext = context;
|
||||
return page;
|
||||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
|
@ -42,6 +42,11 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
|
||||
|
||||
constructor(connection: Connection, channel: BrowserContextChannel, initializer: BrowserContextInitializer) {
|
||||
super(connection, channel, initializer);
|
||||
initializer.pages.map(p => {
|
||||
const page = Page.from(p);
|
||||
this._pages.add(page);
|
||||
page._browserContext = this;
|
||||
});
|
||||
channel.on('page', page => this._onPage(Page.from(page)));
|
||||
}
|
||||
|
||||
@ -162,7 +167,8 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this._channel.close();
|
||||
this._browser!._contexts.delete(this);
|
||||
if (this._browser)
|
||||
this._browser._contexts.delete(this);
|
||||
this.emit(Events.BrowserContext.Close);
|
||||
}
|
||||
}
|
||||
|
49
src/rpc/client/dialog.ts
Normal file
49
src/rpc/client/dialog.ts
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DialogChannel, DialogInitializer } from '../channels';
|
||||
import { Connection } from '../connection';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
|
||||
export class Dialog extends ChannelOwner<DialogChannel, DialogInitializer> {
|
||||
static from(request: DialogChannel): Dialog {
|
||||
return request._object;
|
||||
}
|
||||
|
||||
constructor(connection: Connection, channel: DialogChannel, initializer: DialogInitializer) {
|
||||
super(connection, channel, initializer);
|
||||
}
|
||||
|
||||
type(): string {
|
||||
return this._initializer.type;
|
||||
}
|
||||
|
||||
message(): string {
|
||||
return this._initializer.message;
|
||||
}
|
||||
|
||||
defaultValue(): string {
|
||||
return this._initializer.defaultValue;
|
||||
}
|
||||
|
||||
async accept(promptText: string | undefined) {
|
||||
await this._channel.accept({ promptText });
|
||||
}
|
||||
|
||||
async dismiss() {
|
||||
await this._channel.dismiss();
|
||||
}
|
||||
}
|
56
src/rpc/client/download.ts
Normal file
56
src/rpc/client/download.ts
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { DownloadChannel, DownloadInitializer } from '../channels';
|
||||
import { Connection } from '../connection';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
export class Download extends ChannelOwner<DownloadChannel, DownloadInitializer> {
|
||||
static from(request: DownloadChannel): Download {
|
||||
return request._object;
|
||||
}
|
||||
|
||||
constructor(connection: Connection, channel: DownloadChannel, initializer: DownloadInitializer) {
|
||||
super(connection, channel, initializer);
|
||||
}
|
||||
|
||||
url(): string {
|
||||
return this._initializer.url;
|
||||
}
|
||||
|
||||
suggestedFilename(): string {
|
||||
return this._initializer.suggestedFilename;
|
||||
}
|
||||
|
||||
async path(): Promise<string | null> {
|
||||
return this._channel.path();
|
||||
}
|
||||
|
||||
async failure(): Promise<string | null> {
|
||||
return this._channel.failure();
|
||||
}
|
||||
|
||||
async createReadStream(): Promise<Readable | null> {
|
||||
const fileName = await this.path();
|
||||
return fileName ? fs.createReadStream(fileName) : null;
|
||||
}
|
||||
|
||||
async delete(): Promise<void> {
|
||||
return this._channel.delete();
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ export class Frame extends ChannelOwner<FrameChannel, FrameInitializer> {
|
||||
_parentFrame: Frame | null = null;
|
||||
_url = '';
|
||||
_name = '';
|
||||
private _detached = false;
|
||||
_detached = false;
|
||||
_childFrames = new Set<Frame>();
|
||||
_page: Page | undefined;
|
||||
|
||||
|
@ -30,11 +30,15 @@ import { Connection } from '../connection';
|
||||
import { Keyboard, Mouse } from './input';
|
||||
import { Accessibility } from './accessibility';
|
||||
import { ConsoleMessage } from './consoleMessage';
|
||||
import { Dialog } from './dialog';
|
||||
import { Download } from './download';
|
||||
|
||||
export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
readonly pdf: ((options?: types.PDFOptions) => Promise<Buffer>) | undefined;
|
||||
|
||||
_browserContext: BrowserContext | undefined;
|
||||
_ownedContext: BrowserContext | undefined;
|
||||
|
||||
private _mainFrame: Frame;
|
||||
private _frames = new Set<Frame>();
|
||||
private _workers: Worker[] = [];
|
||||
@ -69,10 +73,13 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
this._channel.on('bindingCall', bindingCall => this._onBinding(BindingCall.from(bindingCall)));
|
||||
this._channel.on('close', () => this._onClose());
|
||||
this._channel.on('console', message => this.emit(Events.Page.Console, ConsoleMessage.from(message)));
|
||||
this._channel.on('dialog', dialog => this.emit(Events.Page.Dialog, Dialog.from(dialog)));
|
||||
this._channel.on('download', download => this.emit(Events.Page.Download, Download.from(download)));
|
||||
this._channel.on('frameAttached', frame => this._onFrameAttached(Frame.from(frame)));
|
||||
this._channel.on('frameDetached', frame => this._onFrameDetached(Frame.from(frame)));
|
||||
this._channel.on('frameNavigated', ({ frame, url, name }) => this._onFrameNavigated(Frame.from(frame), url, name));
|
||||
this._channel.on('pageError', ({ error }) => this.emit(Events.Page.PageError, parseError(error)));
|
||||
this._channel.on('popup', popup => this.emit(Events.Page.Popup, Page.from(popup)));
|
||||
this._channel.on('request', request => this.emit(Events.Page.Request, Request.from(request)));
|
||||
this._channel.on('requestFailed', ({ request, failureText }) => this._onRequestFailed(Request.from(request), failureText));
|
||||
this._channel.on('requestFinished', request => this.emit(Events.Page.RequestFinished, Request.from(request)));
|
||||
@ -95,6 +102,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
|
||||
private _onFrameDetached(frame: Frame) {
|
||||
this._frames.delete(frame);
|
||||
frame._detached = true;
|
||||
if (frame._parentFrame)
|
||||
frame._parentFrame._childFrames.delete(frame);
|
||||
this.emit(Events.Page.FrameDetached, frame);
|
||||
@ -329,6 +337,8 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
|
||||
async close(options: { runBeforeUnload?: boolean } = {runBeforeUnload: undefined}) {
|
||||
await this._channel.close({ options });
|
||||
if (this._ownedContext)
|
||||
await this._ownedContext.close();
|
||||
}
|
||||
|
||||
isClosed(): boolean {
|
||||
|
@ -27,6 +27,8 @@ import { Page, BindingCall } from './client/page';
|
||||
import debug = require('debug');
|
||||
import { Channel } from './channels';
|
||||
import { ConsoleMessage } from './client/consoleMessage';
|
||||
import { Dialog } from './client/dialog';
|
||||
import { Download } from './client/download';
|
||||
|
||||
export class Connection {
|
||||
private _channels = new Map<string, Channel>();
|
||||
@ -41,21 +43,39 @@ export class Connection {
|
||||
let result: ChannelOwner<any, any>;
|
||||
initializer = this._replaceGuidsWithChannels(initializer);
|
||||
switch (type) {
|
||||
case 'browserType':
|
||||
result = new BrowserType(this, channel, initializer);
|
||||
case 'bindingCall':
|
||||
result = new BindingCall(this, channel, initializer);
|
||||
break;
|
||||
case 'browser':
|
||||
result = new Browser(this, channel, initializer);
|
||||
break;
|
||||
case 'browserType':
|
||||
result = new BrowserType(this, channel, initializer);
|
||||
break;
|
||||
case 'context':
|
||||
result = new BrowserContext(this, channel, initializer);
|
||||
break;
|
||||
case 'page':
|
||||
result = new Page(this, channel, initializer);
|
||||
case 'consoleMessage':
|
||||
result = new ConsoleMessage(this, channel, initializer);
|
||||
break;
|
||||
case 'dialog':
|
||||
result = new Dialog(this, channel, initializer);
|
||||
break;
|
||||
case 'download':
|
||||
result = new Download(this, channel, initializer);
|
||||
break;
|
||||
case 'elementHandle':
|
||||
result = new ElementHandle(this, channel, initializer);
|
||||
break;
|
||||
case 'frame':
|
||||
result = new Frame(this, channel, initializer);
|
||||
break;
|
||||
case 'jsHandle':
|
||||
result = new JSHandle(this, channel, initializer);
|
||||
break;
|
||||
case 'page':
|
||||
result = new Page(this, channel, initializer);
|
||||
break;
|
||||
case 'request':
|
||||
result = new Request(this, channel, initializer);
|
||||
break;
|
||||
@ -65,18 +85,6 @@ export class Connection {
|
||||
case 'route':
|
||||
result = new Route(this, channel, initializer);
|
||||
break;
|
||||
case 'bindingCall':
|
||||
result = new BindingCall(this, channel, initializer);
|
||||
break;
|
||||
case 'jsHandle':
|
||||
result = new JSHandle(this, channel, initializer);
|
||||
break;
|
||||
case 'elementHandle':
|
||||
result = new ElementHandle(this, channel, initializer);
|
||||
break;
|
||||
case 'consoleMessage':
|
||||
result = new ConsoleMessage(this, channel, initializer);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Missing type ' + type);
|
||||
}
|
||||
@ -150,8 +158,12 @@ export class Connection {
|
||||
// TODO: send base64
|
||||
if (payload instanceof Buffer)
|
||||
return payload;
|
||||
if (typeof payload === 'object')
|
||||
return Object.fromEntries([...Object.entries(payload)].map(([n,v]) => [n, this._replaceChannelsWithGuids(v)]));
|
||||
if (typeof payload === 'object') {
|
||||
const result: any = {};
|
||||
for (const key of Object.keys(payload))
|
||||
result[key] = this._replaceChannelsWithGuids(payload[key]);
|
||||
return result;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
@ -165,8 +177,12 @@ export class Connection {
|
||||
// TODO: send base64
|
||||
if (payload instanceof Buffer)
|
||||
return payload;
|
||||
if (typeof payload === 'object')
|
||||
return Object.fromEntries([...Object.entries(payload)].map(([n,v]) => [n, this._replaceGuidsWithChannels(v)]));
|
||||
if (typeof payload === 'object') {
|
||||
const result: any = {};
|
||||
for (const key of Object.keys(payload))
|
||||
result[key] = this._replaceGuidsWithChannels(payload[key]);
|
||||
return result;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
|
@ -18,30 +18,30 @@ import { EventEmitter } from 'events';
|
||||
import { helper } from '../helper';
|
||||
import { Channel } from './channels';
|
||||
|
||||
export class Dispatcher<Initializer> extends EventEmitter implements Channel {
|
||||
export class Dispatcher<Type, Initializer> extends EventEmitter implements Channel {
|
||||
readonly _guid: string;
|
||||
readonly _type: string;
|
||||
protected _scope: DispatcherScope;
|
||||
_object: any;
|
||||
|
||||
constructor(scope: DispatcherScope, object: any, type: string, initializer: Initializer, guid = type + '@' + helper.guid()) {
|
||||
constructor(scope: DispatcherScope, object: Type, type: string, initializer: Initializer, guid = type + '@' + helper.guid()) {
|
||||
super();
|
||||
this._type = type;
|
||||
this._guid = guid;
|
||||
this._object = object;
|
||||
this._scope = scope;
|
||||
scope.dispatchers.set(this._guid, this);
|
||||
object[scope.dispatcherSymbol] = this;
|
||||
(object as any)[scope.dispatcherSymbol] = this;
|
||||
this._scope.sendMessageToClient(this._guid, '__create__', { type, initializer });
|
||||
}
|
||||
|
||||
_dispatchEvent(method: string, params: Dispatcher<any> | any = {}) {
|
||||
_dispatchEvent(method: string, params: Dispatcher<any, any> | any = {}) {
|
||||
this._scope.sendMessageToClient(this._guid, method, params);
|
||||
}
|
||||
}
|
||||
|
||||
export class DispatcherScope {
|
||||
readonly dispatchers = new Map<string, Dispatcher<any>>();
|
||||
readonly dispatchers = new Map<string, Dispatcher<any, any>>();
|
||||
readonly dispatcherSymbol = Symbol('dispatcher');
|
||||
sendMessageToClientTransport = (message: any) => {};
|
||||
|
||||
@ -65,8 +65,12 @@ export class DispatcherScope {
|
||||
// TODO: send base64
|
||||
if (payload instanceof Buffer)
|
||||
return payload;
|
||||
if (typeof payload === 'object')
|
||||
return Object.fromEntries([...Object.entries(payload)].map(([n,v]) => [n, this._replaceDispatchersWithGuids(v)]));
|
||||
if (typeof payload === 'object') {
|
||||
const result: any = {};
|
||||
for (const key of Object.keys(payload))
|
||||
result[key] = this._replaceDispatchersWithGuids(payload[key]);
|
||||
return result;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
@ -80,8 +84,12 @@ export class DispatcherScope {
|
||||
// TODO: send base64
|
||||
if (payload instanceof Buffer)
|
||||
return payload;
|
||||
if (typeof payload === 'object')
|
||||
return Object.fromEntries([...Object.entries(payload)].map(([n,v]) => [n, this._replaceGuidsWithDispatchers(v)]));
|
||||
if (typeof payload === 'object') {
|
||||
const result: any = {};
|
||||
for (const key of Object.keys(payload))
|
||||
result[key] = this._replaceGuidsWithDispatchers(payload[key]);
|
||||
return result;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,8 @@ import { PageChannel, BrowserContextChannel, BrowserContextInitializer } from '.
|
||||
import { RouteDispatcher, RequestDispatcher } from './networkDispatchers';
|
||||
import { Page } from '../../page';
|
||||
|
||||
export class BrowserContextDispatcher extends Dispatcher<BrowserContextInitializer> implements BrowserContextChannel {
|
||||
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, BrowserContextInitializer> implements BrowserContextChannel {
|
||||
private _context: BrowserContextBase;
|
||||
|
||||
static from(scope: DispatcherScope, browserContext: BrowserContext): BrowserContextDispatcher {
|
||||
if ((browserContext as any)[scope.dispatcherSymbol])
|
||||
return (browserContext as any)[scope.dispatcherSymbol];
|
||||
@ -33,7 +32,9 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContextInitializ
|
||||
}
|
||||
|
||||
constructor(scope: DispatcherScope, context: BrowserContextBase) {
|
||||
super(scope, context, 'context', {});
|
||||
super(scope, context, 'context', {
|
||||
pages: context.pages().map(p => PageDispatcher.from(scope, p))
|
||||
});
|
||||
this._context = context;
|
||||
context.on(Events.BrowserContext.Page, page => this._dispatchEvent('page', PageDispatcher.from(this._scope, page)));
|
||||
context.on(Events.BrowserContext.Close, () => {
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserBase } from '../../browser';
|
||||
import { BrowserBase, Browser } from '../../browser';
|
||||
import { BrowserContextBase } from '../../browserContext';
|
||||
import * as types from '../../types';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
@ -22,9 +22,7 @@ import { BrowserChannel, BrowserContextChannel, PageChannel, BrowserInitializer
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { PageDispatcher } from './pageDispatcher';
|
||||
|
||||
export class BrowserDispatcher extends Dispatcher<BrowserInitializer> implements BrowserChannel {
|
||||
private _browser: BrowserBase;
|
||||
|
||||
export class BrowserDispatcher extends Dispatcher<Browser, BrowserInitializer> implements BrowserChannel {
|
||||
static from(scope: DispatcherScope, browser: BrowserBase): BrowserDispatcher {
|
||||
if ((browser as any)[scope.dispatcherSymbol])
|
||||
return (browser as any)[scope.dispatcherSymbol];
|
||||
@ -39,18 +37,17 @@ export class BrowserDispatcher extends Dispatcher<BrowserInitializer> implements
|
||||
|
||||
constructor(scope: DispatcherScope, browser: BrowserBase) {
|
||||
super(scope, browser, 'browser', {});
|
||||
this._browser = browser;
|
||||
}
|
||||
|
||||
async newContext(params: { options?: types.BrowserContextOptions }): Promise<BrowserContextChannel> {
|
||||
return BrowserContextDispatcher.from(this._scope, await this._browser.newContext(params.options) as BrowserContextBase);
|
||||
return BrowserContextDispatcher.from(this._scope, await this._object.newContext(params.options) as BrowserContextBase);
|
||||
}
|
||||
|
||||
async newPage(params: { options?: types.BrowserContextOptions }): Promise<PageChannel> {
|
||||
return PageDispatcher.from(this._scope, await this._browser.newPage(params.options));
|
||||
return PageDispatcher.from(this._scope, await this._object.newPage(params.options));
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this._browser.close();
|
||||
await this._object.close();
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { BrowserBase } from '../../browser';
|
||||
import { BrowserTypeBase } from '../../server/browserType';
|
||||
import { BrowserTypeBase, BrowserType } from '../../server/browserType';
|
||||
import * as types from '../../types';
|
||||
import { BrowserDispatcher } from './browserDispatcher';
|
||||
import { BrowserChannel, BrowserTypeChannel, BrowserContextChannel, BrowserTypeInitializer } from '../channels';
|
||||
@ -23,9 +23,7 @@ import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { BrowserContextBase } from '../../browserContext';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
|
||||
export class BrowserTypeDispatcher extends Dispatcher<BrowserTypeInitializer> implements BrowserTypeChannel {
|
||||
private _browserType: BrowserTypeBase;
|
||||
|
||||
export class BrowserTypeDispatcher extends Dispatcher<BrowserType, BrowserTypeInitializer> implements BrowserTypeChannel {
|
||||
static from(scope: DispatcherScope, browserType: BrowserTypeBase): BrowserTypeDispatcher {
|
||||
if ((browserType as any)[scope.dispatcherSymbol])
|
||||
return (browserType as any)[scope.dispatcherSymbol];
|
||||
@ -37,21 +35,20 @@ export class BrowserTypeDispatcher extends Dispatcher<BrowserTypeInitializer> im
|
||||
executablePath: browserType.executablePath(),
|
||||
name: browserType.name()
|
||||
}, browserType.name());
|
||||
this._browserType = browserType;
|
||||
}
|
||||
|
||||
async launch(params: { options?: types.LaunchOptions }): Promise<BrowserChannel> {
|
||||
const browser = await this._browserType.launch(params.options || undefined);
|
||||
const browser = await this._object.launch(params.options || undefined);
|
||||
return BrowserDispatcher.from(this._scope, browser as BrowserBase);
|
||||
}
|
||||
|
||||
async launchPersistentContext(params: { userDataDir: string, options?: types.LaunchOptions & types.BrowserContextOptions }): Promise<BrowserContextChannel> {
|
||||
const browserContext = await this._browserType.launchPersistentContext(params.userDataDir, params.options);
|
||||
const browserContext = await this._object.launchPersistentContext(params.userDataDir, params.options);
|
||||
return BrowserContextDispatcher.from(this._scope, browserContext as BrowserContextBase);
|
||||
}
|
||||
|
||||
async connect(params: { options: types.ConnectOptions }): Promise<BrowserChannel> {
|
||||
const browser = await this._browserType.connect(params.options);
|
||||
const browser = await this._object.connect(params.options);
|
||||
return BrowserDispatcher.from(this._scope, browser as BrowserBase);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import { ConsoleMessageChannel, ConsoleMessageInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
|
||||
export class ConsoleMessageDispatcher extends Dispatcher<ConsoleMessageInitializer> implements ConsoleMessageChannel {
|
||||
export class ConsoleMessageDispatcher extends Dispatcher<ConsoleMessage, ConsoleMessageInitializer> implements ConsoleMessageChannel {
|
||||
static from(scope: DispatcherScope, message: ConsoleMessage): ConsoleMessageDispatcher {
|
||||
if ((message as any)[scope.dispatcherSymbol])
|
||||
return (message as any)[scope.dispatcherSymbol];
|
||||
|
43
src/rpc/server/dialogDispatcher.ts
Normal file
43
src/rpc/server/dialogDispatcher.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Dialog } from '../../dialog';
|
||||
import { DialogChannel, DialogInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
|
||||
export class DialogDispatcher extends Dispatcher<Dialog, DialogInitializer> implements DialogChannel {
|
||||
static from(scope: DispatcherScope, dialog: Dialog): DialogDispatcher {
|
||||
if ((dialog as any)[scope.dispatcherSymbol])
|
||||
return (dialog as any)[scope.dispatcherSymbol];
|
||||
return new DialogDispatcher(scope, dialog);
|
||||
}
|
||||
|
||||
constructor(scope: DispatcherScope, dialog: Dialog) {
|
||||
super(scope, dialog, 'dialog', {
|
||||
type: dialog.type(),
|
||||
message: dialog.message(),
|
||||
defaultValue: dialog.defaultValue(),
|
||||
});
|
||||
}
|
||||
|
||||
async accept(params: { promptText?: string }): Promise<void> {
|
||||
await this._object.accept(params.promptText);
|
||||
}
|
||||
|
||||
async dismiss(): Promise<void> {
|
||||
await this._object.dismiss();
|
||||
}
|
||||
}
|
46
src/rpc/server/downloadDispatcher.ts
Normal file
46
src/rpc/server/downloadDispatcher.ts
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Download } from '../../download';
|
||||
import { DownloadChannel, DownloadInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
|
||||
export class DownloadDispatcher extends Dispatcher<Download, DownloadInitializer> implements DownloadChannel {
|
||||
static from(scope: DispatcherScope, Download: Download): DownloadDispatcher {
|
||||
if ((Download as any)[scope.dispatcherSymbol])
|
||||
return (Download as any)[scope.dispatcherSymbol];
|
||||
return new DownloadDispatcher(scope, Download);
|
||||
}
|
||||
|
||||
constructor(scope: DispatcherScope, download: Download) {
|
||||
super(scope, download, 'download', {
|
||||
url: download.url(),
|
||||
suggestedFilename: download.suggestedFilename(),
|
||||
});
|
||||
}
|
||||
|
||||
async path(): Promise<string> {
|
||||
return this._object.path();
|
||||
}
|
||||
|
||||
async failure(): Promise<string | null> {
|
||||
return this._object.failure();
|
||||
}
|
||||
|
||||
async delete(): Promise<void> {
|
||||
await this._object.delete();
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ import { ElementHandleDispatcher, convertSelectOptionValues } from './elementHan
|
||||
import { JSHandleDispatcher } from './jsHandleDispatcher';
|
||||
import { ResponseDispatcher } from './networkDispatchers';
|
||||
|
||||
export class FrameDispatcher extends Dispatcher<FrameInitializer> implements FrameChannel {
|
||||
export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> implements FrameChannel {
|
||||
private _frame: Frame;
|
||||
|
||||
static from(scope: DispatcherScope, frame: Frame): FrameDispatcher {
|
||||
|
@ -19,27 +19,25 @@ import { JSHandleChannel, JSHandleInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { convertArg } from './frameDispatcher';
|
||||
|
||||
export class JSHandleDispatcher extends Dispatcher<JSHandleInitializer> implements JSHandleChannel {
|
||||
readonly _jsHandle: js.JSHandle<any>;
|
||||
export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitializer> implements JSHandleChannel {
|
||||
|
||||
constructor(scope: DispatcherScope, jsHandle: js.JSHandle) {
|
||||
super(scope, jsHandle, jsHandle.asElement() ? 'elementHandle' : 'jsHandle', {
|
||||
preview: jsHandle.toString(),
|
||||
});
|
||||
this._jsHandle = jsHandle;
|
||||
}
|
||||
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<any> {
|
||||
return this._jsHandle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, convertArg(this._scope, params.arg));
|
||||
return this._object._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, convertArg(this._scope, params.arg));
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any}): Promise<JSHandleChannel> {
|
||||
const jsHandle = await this._jsHandle._evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, convertArg(this._scope, params.arg));
|
||||
const jsHandle = await this._object._evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, convertArg(this._scope, params.arg));
|
||||
return new JSHandleDispatcher(this._scope, jsHandle);
|
||||
}
|
||||
|
||||
async getPropertyList(): Promise<{ name: string, value: JSHandleChannel }[]> {
|
||||
const map = await this._jsHandle.getProperties();
|
||||
const map = await this._object.getProperties();
|
||||
const result = [];
|
||||
for (const [name, value] of map)
|
||||
result.push({ name, value: new JSHandleDispatcher(this._scope, value) });
|
||||
@ -47,10 +45,10 @@ export class JSHandleDispatcher extends Dispatcher<JSHandleInitializer> implemen
|
||||
}
|
||||
|
||||
async jsonValue(): Promise<any> {
|
||||
return this._jsHandle.jsonValue();
|
||||
return this._object.jsonValue();
|
||||
}
|
||||
|
||||
async dispose() {
|
||||
await this._jsHandle.dispose();
|
||||
await this._object.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,7 @@ import { RequestChannel, ResponseChannel, RouteChannel, ResponseInitializer, Req
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { FrameDispatcher } from './frameDispatcher';
|
||||
|
||||
export class RequestDispatcher extends Dispatcher<RequestInitializer> implements RequestChannel {
|
||||
private _request: Request;
|
||||
|
||||
export class RequestDispatcher extends Dispatcher<Request, RequestInitializer> implements RequestChannel {
|
||||
static from(scope: DispatcherScope, request: Request): RequestDispatcher {
|
||||
if ((request as any)[scope.dispatcherSymbol])
|
||||
return (request as any)[scope.dispatcherSymbol];
|
||||
@ -44,16 +42,14 @@ export class RequestDispatcher extends Dispatcher<RequestInitializer> implements
|
||||
isNavigationRequest: request.isNavigationRequest(),
|
||||
redirectedFrom: RequestDispatcher.fromNullable(scope, request.redirectedFrom()),
|
||||
});
|
||||
this._request = request;
|
||||
}
|
||||
|
||||
async response(): Promise<ResponseChannel | null> {
|
||||
return ResponseDispatcher.fromNullable(this._scope, await this._request.response());
|
||||
return ResponseDispatcher.fromNullable(this._scope, await this._object.response());
|
||||
}
|
||||
}
|
||||
|
||||
export class ResponseDispatcher extends Dispatcher<ResponseInitializer> implements ResponseChannel {
|
||||
private _response: Response;
|
||||
export class ResponseDispatcher extends Dispatcher<Response, ResponseInitializer> implements ResponseChannel {
|
||||
|
||||
static from(scope: DispatcherScope, response: Response): ResponseDispatcher {
|
||||
if ((response as any)[scope.dispatcherSymbol])
|
||||
@ -73,20 +69,18 @@ export class ResponseDispatcher extends Dispatcher<ResponseInitializer> implemen
|
||||
statusText: response.statusText(),
|
||||
headers: response.headers(),
|
||||
});
|
||||
this._response = response;
|
||||
}
|
||||
|
||||
async finished(): Promise<Error | null> {
|
||||
return await this._response.finished();
|
||||
return await this._object.finished();
|
||||
}
|
||||
|
||||
async body(): Promise<Buffer> {
|
||||
return await this._response.body();
|
||||
return await this._object.body();
|
||||
}
|
||||
}
|
||||
|
||||
export class RouteDispatcher extends Dispatcher<RouteInitializer> implements RouteChannel {
|
||||
private _route: Route;
|
||||
export class RouteDispatcher extends Dispatcher<Route, RouteInitializer> implements RouteChannel {
|
||||
|
||||
static from(scope: DispatcherScope, route: Route): RouteDispatcher {
|
||||
if ((route as any)[scope.dispatcherSymbol])
|
||||
@ -102,18 +96,17 @@ export class RouteDispatcher extends Dispatcher<RouteInitializer> implements Rou
|
||||
super(scope, route, 'route', {
|
||||
request: RequestDispatcher.from(scope, route.request())
|
||||
});
|
||||
this._route = route;
|
||||
}
|
||||
|
||||
async continue(params: { overrides: { method?: string, headers?: types.Headers, postData?: string } }): Promise<void> {
|
||||
await this._route.continue(params.overrides);
|
||||
await this._object.continue(params.overrides);
|
||||
}
|
||||
|
||||
async fulfill(params: { response: types.FulfillResponse & { path?: string } }): Promise<void> {
|
||||
await this._route.fulfill(params.response);
|
||||
await this._object.fulfill(params.response);
|
||||
}
|
||||
|
||||
async abort(params: { errorCode: string }): Promise<void> {
|
||||
await this._route.abort(params.errorCode);
|
||||
await this._object.abort(params.errorCode);
|
||||
}
|
||||
}
|
||||
|
@ -14,20 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserContext } from '../../browserContext';
|
||||
import { Events } from '../../events';
|
||||
import { Request } from '../../network';
|
||||
import { Frame } from '../../frames';
|
||||
import { parseError, serializeError } from '../../helper';
|
||||
import { Request } from '../../network';
|
||||
import { Page } from '../../page';
|
||||
import * as types from '../../types';
|
||||
import { ElementHandleChannel, PageChannel, ResponseChannel, BindingCallChannel, PageInitializer, BindingCallInitializer } from '../channels';
|
||||
import { BindingCallChannel, BindingCallInitializer, ElementHandleChannel, PageChannel, PageInitializer, ResponseChannel } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { ConsoleMessageDispatcher } from './consoleMessageDispatcher';
|
||||
import { DialogDispatcher } from './dialogDispatcher';
|
||||
import { DownloadDispatcher } from './downloadDispatcher';
|
||||
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<PageInitializer> implements PageChannel {
|
||||
export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements PageChannel {
|
||||
private _page: Page;
|
||||
|
||||
static from(scope: DispatcherScope, page: Page): PageDispatcher {
|
||||
@ -50,10 +52,13 @@ export class PageDispatcher extends Dispatcher<PageInitializer> implements PageC
|
||||
this._page = page;
|
||||
page.on(Events.Page.Close, () => this._dispatchEvent('close'));
|
||||
page.on(Events.Page.Console, message => this._dispatchEvent('console', ConsoleMessageDispatcher.from(this._scope, message)));
|
||||
page.on(Events.Page.Dialog, dialog => this._dispatchEvent('dialog', DialogDispatcher.from(this._scope, dialog)));
|
||||
page.on(Events.Page.Download, dialog => this._dispatchEvent('download', DownloadDispatcher.from(this._scope, dialog)));
|
||||
page.on(Events.Page.FrameAttached, frame => this._onFrameAttached(frame));
|
||||
page.on(Events.Page.FrameDetached, frame => this._onFrameDetached(frame));
|
||||
page.on(Events.Page.FrameNavigated, frame => this._onFrameNavigated(frame));
|
||||
page.on(Events.Page.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) }));
|
||||
page.on(Events.Page.Popup, page => this._dispatchEvent('popup', PageDispatcher.from(this._scope, page)));
|
||||
page.on(Events.Page.Request, request => this._dispatchEvent('request', RequestDispatcher.from(this._scope, request)));
|
||||
page.on(Events.Page.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', {
|
||||
request: RequestDispatcher.from(this._scope, request),
|
||||
@ -193,7 +198,7 @@ export class PageDispatcher extends Dispatcher<PageInitializer> implements PageC
|
||||
}
|
||||
|
||||
|
||||
export class BindingCallDispatcher extends Dispatcher<BindingCallInitializer> implements BindingCallChannel {
|
||||
export class BindingCallDispatcher extends Dispatcher<{}, BindingCallInitializer> implements BindingCallChannel {
|
||||
private _resolve: ((arg: any) => void) | undefined;
|
||||
private _reject: ((error: any) => void) | undefined;
|
||||
private _promise: Promise<any>;
|
||||
|
Loading…
Reference in New Issue
Block a user