mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 13:45:36 +03:00
chore(rpc): support workers, file chooser, browser server (#2766)
This commit is contained in:
parent
5bb018e0e5
commit
e29f7b9f58
@ -121,7 +121,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
|
||||
async _initializePreview() {
|
||||
const utility = await this._context.injectedScript();
|
||||
this._preview = await utility.evaluate((injected, e) => 'JSHandle@' + injected.previewNode(e), this);
|
||||
this._setPreview(await utility.evaluate((injected, e) => 'JSHandle@' + injected.previewNode(e), this));
|
||||
}
|
||||
|
||||
asElement(): ElementHandle<T> | null {
|
||||
|
@ -82,6 +82,7 @@ export class JSHandle<T = any> {
|
||||
readonly _value: any;
|
||||
private _objectType: string;
|
||||
protected _preview: string;
|
||||
private _previewCallback: ((preview: string) => void) | undefined;
|
||||
|
||||
constructor(context: ExecutionContext, type: string, objectId?: ObjectId, value?: any) {
|
||||
this._context = context;
|
||||
@ -147,6 +148,16 @@ export class JSHandle<T = any> {
|
||||
toString(): string {
|
||||
return this._preview;
|
||||
}
|
||||
|
||||
_setPreviewCallback(callback: (preview: string) => void) {
|
||||
this._previewCallback = callback;
|
||||
}
|
||||
|
||||
_setPreview(preview: string) {
|
||||
this._preview = preview;
|
||||
if (this._previewCallback)
|
||||
this._previewCallback(preview);
|
||||
}
|
||||
}
|
||||
|
||||
export async function evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||
|
@ -646,12 +646,20 @@ export class Worker extends EventEmitter {
|
||||
return js.evaluate(await this._executionContextPromise, true /* returnByValue */, pageFunction, arg);
|
||||
}
|
||||
|
||||
async _evaluateExpression(expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
return js.evaluateExpression(await this._executionContextPromise, true /* returnByValue */, expression, isFunction, arg);
|
||||
}
|
||||
|
||||
async evaluateHandle<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>>;
|
||||
async evaluateHandle<R>(pageFunction: js.Func1<void, R>, arg?: any): Promise<js.SmartHandle<R>>;
|
||||
async evaluateHandle<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>> {
|
||||
assertMaxArguments(arguments.length, 2);
|
||||
return js.evaluate(await this._executionContextPromise, false /* returnByValue */, pageFunction, arg);
|
||||
}
|
||||
|
||||
async _evaluateExpressionHandle(expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
return js.evaluateExpression(await this._executionContextPromise, false /* returnByValue */, expression, isFunction, arg);
|
||||
}
|
||||
}
|
||||
|
||||
export class PageBinding {
|
||||
|
@ -25,9 +25,11 @@ export interface Channel extends EventEmitter {
|
||||
_object: any;
|
||||
}
|
||||
|
||||
|
||||
export interface BrowserTypeChannel extends Channel {
|
||||
connect(params: { options: types.ConnectOptions }): Promise<BrowserChannel>;
|
||||
launch(params: { options?: types.LaunchOptions }): Promise<BrowserChannel>;
|
||||
launchServer(params: { options?: types.LaunchServerOptions }): Promise<BrowserServerChannel>;
|
||||
launchPersistentContext(params: { userDataDir: string, options?: types.LaunchOptions & types.BrowserContextOptions }): Promise<BrowserContextChannel>;
|
||||
}
|
||||
export type BrowserTypeInitializer = {
|
||||
@ -36,7 +38,21 @@ export type BrowserTypeInitializer = {
|
||||
};
|
||||
|
||||
|
||||
export interface BrowserServerChannel extends Channel {
|
||||
on(event: 'close', callback: () => void): this;
|
||||
|
||||
close(): Promise<void>;
|
||||
kill(): Promise<void>;
|
||||
}
|
||||
export type BrowserServerInitializer = {
|
||||
wsEndpoint: string,
|
||||
pid: number
|
||||
};
|
||||
|
||||
|
||||
export interface BrowserChannel extends Channel {
|
||||
on(event: 'close', callback: () => void): this;
|
||||
|
||||
close(): Promise<void>;
|
||||
newContext(params: { options?: types.BrowserContextOptions }): Promise<BrowserContextChannel>;
|
||||
newPage(params: { options?: types.BrowserContextOptions }): Promise<PageChannel>;
|
||||
@ -46,7 +62,10 @@ export type BrowserInitializer = {};
|
||||
|
||||
export interface BrowserContextChannel extends Channel {
|
||||
on(event: 'bindingCall', callback: (params: BindingCallChannel) => void): this;
|
||||
on(event: 'close', callback: () => void): this;
|
||||
on(event: 'page', callback: (params: PageChannel) => void): this;
|
||||
on(event: 'route', callback: (params: { route: RouteChannel, request: RequestChannel }) => void): this;
|
||||
|
||||
addCookies(params: { cookies: types.SetNetworkCookieParam[] }): Promise<void>;
|
||||
addInitScript(params: { source: string }): Promise<void>;
|
||||
clearCookies(): Promise<void>;
|
||||
@ -78,6 +97,7 @@ export interface PageChannel extends Channel {
|
||||
on(event: 'dialog', callback: (params: DialogChannel) => void): this;
|
||||
on(event: 'download', callback: (params: DownloadChannel) => void): this;
|
||||
on(event: 'domcontentloaded', callback: () => void): this;
|
||||
on(event: 'fileChooser', callback: (params: { element: ElementHandleChannel, isMultiple: boolean }) => 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;
|
||||
@ -90,6 +110,7 @@ export interface PageChannel extends Channel {
|
||||
on(event: 'requestFinished', callback: (params: RequestChannel) => void): this;
|
||||
on(event: 'response', callback: (params: ResponseChannel) => void): this;
|
||||
on(event: 'route', callback: (params: { route: RouteChannel, request: RequestChannel }) => void): this;
|
||||
on(event: 'worker', callback: (params: WorkerChannel) => void): this;
|
||||
|
||||
setDefaultNavigationTimeoutNoReply(params: { timeout: number }): void;
|
||||
setDefaultTimeoutNoReply(params: { timeout: number }): Promise<void>;
|
||||
@ -121,7 +142,9 @@ export interface PageChannel extends Channel {
|
||||
|
||||
// A11Y
|
||||
accessibilitySnapshot(params: { options: { interestingOnly?: boolean, root?: ElementHandleChannel } }): Promise<types.SerializedAXNode | null>;
|
||||
pdf: (params: { options?: types.PDFOptions }) => Promise<Binary>;
|
||||
}
|
||||
|
||||
export type PageInitializer = {
|
||||
mainFrame: FrameChannel,
|
||||
viewportSize: types.Size | null
|
||||
@ -137,7 +160,7 @@ export interface FrameChannel extends Channel {
|
||||
click(params: { selector: string, options: types.PointerActionOptions & types.MouseClickOptions & types.TimeoutOptions & { force?: boolean } & { noWaitAfter?: boolean }, isPage?: boolean }): Promise<void>;
|
||||
content(): Promise<string>;
|
||||
dblclick(params: { selector: string, options: types.PointerActionOptions & types.MouseMultiClickOptions & types.TimeoutOptions & { force?: boolean }, isPage?: boolean}): Promise<void>;
|
||||
dispatchEvent(params: { selector: string, type: string, eventInit: Object | undefined, options: types.TimeoutOptions, isPage?: boolean }): Promise<void>;
|
||||
dispatchEvent(params: { selector: string, type: string, eventInit: any, options: types.TimeoutOptions, isPage?: boolean }): Promise<void>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any, isPage?: boolean }): Promise<any>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any, isPage?: boolean }): Promise<JSHandleChannel>;
|
||||
fill(params: { selector: string, value: string, options: types.NavigatingActionWaitOptions, isPage?: boolean }): Promise<void>;
|
||||
@ -170,7 +193,18 @@ export type FrameInitializer = {
|
||||
};
|
||||
|
||||
|
||||
export interface WorkerChannel extends Channel {
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<any>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any }): Promise<JSHandleChannel>;
|
||||
}
|
||||
export type WorkerInitializer = {
|
||||
url: string,
|
||||
};
|
||||
|
||||
|
||||
export interface JSHandleChannel extends Channel {
|
||||
on(event: 'previewUpdated', callback: (preview: string) => void): this;
|
||||
|
||||
dispose(): Promise<void>;
|
||||
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<any>;
|
||||
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any}): Promise<JSHandleChannel>;
|
||||
|
@ -20,6 +20,7 @@ import { BrowserContext } from './browserContext';
|
||||
import { Page } from './page';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Connection } from '../connection';
|
||||
import { Events } from '../../events';
|
||||
|
||||
export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
|
||||
readonly _contexts = new Set<BrowserContext>();
|
||||
@ -36,6 +37,10 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
|
||||
|
||||
constructor(connection: Connection, channel: BrowserChannel, initializer: BrowserInitializer) {
|
||||
super(connection, channel, initializer);
|
||||
channel.on('close', () => {
|
||||
this._isConnected = false;
|
||||
this.emit(Events.Browser.Disconnected);
|
||||
});
|
||||
}
|
||||
|
||||
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||
|
@ -52,7 +52,9 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
|
||||
page._setBrowserContext(this);
|
||||
});
|
||||
this._channel.on('bindingCall', bindingCall => this._onBinding(BindingCall.from(bindingCall)));
|
||||
this._channel.on('close', () => this._onClose());
|
||||
this._channel.on('page', page => this._onPage(Page.from(page)));
|
||||
this._channel.on('route', ({ route, request }) => this._onRoute(network.Route.from(route), network.Request.from(request)));
|
||||
}
|
||||
|
||||
private _onPage(page: Page): void {
|
||||
@ -180,8 +182,7 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
|
||||
return result;
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this._channel.close();
|
||||
private async _onClose() {
|
||||
if (this._browser)
|
||||
this._browser._contexts.delete(this);
|
||||
|
||||
@ -193,4 +194,8 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
|
||||
this._pendingWaitForEvents.clear();
|
||||
this.emit(Events.BrowserContext.Close);
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this._channel.close();
|
||||
}
|
||||
}
|
||||
|
50
src/rpc/client/browserServer.ts
Normal file
50
src/rpc/client/browserServer.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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 { ChildProcess } from 'child_process';
|
||||
import { BrowserServerChannel, BrowserServerInitializer } from '../channels';
|
||||
import { Connection } from '../connection';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Events } from '../../events';
|
||||
|
||||
export class BrowserServer extends ChannelOwner<BrowserServerChannel, BrowserServerInitializer> {
|
||||
static from(request: BrowserServerChannel): BrowserServer {
|
||||
return request._object;
|
||||
}
|
||||
|
||||
constructor(connection: Connection, channel: BrowserServerChannel, initializer: BrowserServerInitializer) {
|
||||
super(connection, channel, initializer);
|
||||
channel.on('close', () => this.emit(Events.BrowserServer.Close));
|
||||
}
|
||||
|
||||
process(): ChildProcess {
|
||||
return { pid: this._initializer.pid } as any;
|
||||
}
|
||||
|
||||
wsEndpoint(): string {
|
||||
return this._initializer.wsEndpoint;
|
||||
}
|
||||
|
||||
async kill(): Promise<void> {
|
||||
await this._channel.kill();
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this._channel.close();
|
||||
}
|
||||
|
||||
_checkLeaks() {}
|
||||
}
|
@ -20,6 +20,7 @@ import { Browser } from './browser';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Connection } from '../connection';
|
||||
import { BrowserServer } from './browserServer';
|
||||
|
||||
export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeInitializer> {
|
||||
constructor(connection: Connection, channel: BrowserTypeChannel, initializer: BrowserTypeInitializer) {
|
||||
@ -39,6 +40,11 @@ export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeIni
|
||||
return Browser.from(await this._channel.launch({ options }));
|
||||
}
|
||||
|
||||
async launchServer(options?: types.LaunchServerOptions): Promise<BrowserServer> {
|
||||
delete (options as any).logger;
|
||||
return BrowserServer.from(await this._channel.launchServer({ options }));
|
||||
}
|
||||
|
||||
async launchPersistentContext(userDataDir: string, options: types.LaunchOptions & types.BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||
delete (options as any).logger;
|
||||
return BrowserContext.from(await this._channel.launchPersistentContext({ userDataDir, options }));
|
||||
|
47
src/rpc/client/fileChooser.ts
Normal file
47
src/rpc/client/fileChooser.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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 types from '../../types';
|
||||
import { ElementHandle } from './elementHandle';
|
||||
import { Page } from './page';
|
||||
|
||||
export class FileChooser {
|
||||
private _page: Page;
|
||||
private _elementHandle: ElementHandle<Node>;
|
||||
private _isMultiple: boolean;
|
||||
|
||||
constructor(page: Page, elementHandle: ElementHandle, isMultiple: boolean) {
|
||||
this._page = page;
|
||||
this._elementHandle = elementHandle;
|
||||
this._isMultiple = isMultiple;
|
||||
}
|
||||
|
||||
element(): ElementHandle {
|
||||
return this._elementHandle;
|
||||
}
|
||||
|
||||
isMultiple(): boolean {
|
||||
return this._isMultiple;
|
||||
}
|
||||
|
||||
page(): Page {
|
||||
return this._page;
|
||||
}
|
||||
|
||||
async setFiles(files: string | types.FilePayload | string[] | types.FilePayload[], options?: types.NavigatingActionWaitOptions) {
|
||||
return this._elementHandle.setInputFiles(files, options);
|
||||
}
|
||||
}
|
@ -97,8 +97,8 @@ export class Frame extends ChannelOwner<FrameChannel, FrameInitializer> {
|
||||
return ElementHandle.fromNullable(await this._channel.waitForSelector({ selector, options, isPage: this._page!._isPageCall })) as ElementHandle<Element> | null;
|
||||
}
|
||||
|
||||
async dispatchEvent(selector: string, type: string, eventInit?: Object, options: types.TimeoutOptions = {}): Promise<void> {
|
||||
await this._channel.dispatchEvent({ selector, type, eventInit, options, isPage: this._page!._isPageCall });
|
||||
async dispatchEvent(selector: string, type: string, eventInit?: any, options: types.TimeoutOptions = {}): Promise<void> {
|
||||
await this._channel.dispatchEvent({ selector, type, eventInit: serializeArgument(eventInit), options, isPage: this._page!._isPageCall });
|
||||
}
|
||||
|
||||
async $eval<R, Arg>(selector: string, pageFunction: FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
|
||||
|
@ -37,6 +37,8 @@ export type FuncOn<On, Arg2, R> = string | ((on: On, arg2: Unboxed<Arg2>) => R |
|
||||
export type SmartHandle<T> = T extends Node ? ElementHandle<T> : JSHandle<T>;
|
||||
|
||||
export class JSHandle<T = any> extends ChannelOwner<JSHandleChannel, JSHandleInitializer> {
|
||||
private _preview: string;
|
||||
|
||||
static from(handle: JSHandleChannel): JSHandle {
|
||||
return handle._object;
|
||||
}
|
||||
@ -47,6 +49,8 @@ export class JSHandle<T = any> extends ChannelOwner<JSHandleChannel, JSHandleIni
|
||||
|
||||
constructor(conection: Connection, channel: JSHandleChannel, initializer: JSHandleInitializer) {
|
||||
super(conection, channel, initializer);
|
||||
this._preview = this._initializer.preview;
|
||||
channel.on('previewUpdated', preview => this._preview = preview);
|
||||
}
|
||||
|
||||
async evaluate<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<R>;
|
||||
@ -94,7 +98,7 @@ export class JSHandle<T = any> extends ChannelOwner<JSHandleChannel, JSHandleIni
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this._initializer.preview;
|
||||
return this._preview;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,35 +16,37 @@
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { TimeoutError } from '../../errors';
|
||||
import { Events } from '../../events';
|
||||
import { assert, assertMaxArguments, helper, Listener } from '../../helper';
|
||||
import { TimeoutSettings } from '../../timeoutSettings';
|
||||
import * as types from '../../types';
|
||||
import { PageChannel, BindingCallChannel, Channel, PageInitializer, BindingCallInitializer } from '../channels';
|
||||
import { BindingCallChannel, BindingCallInitializer, Channel, PageChannel, PageInitializer } from '../channels';
|
||||
import { Connection } from '../connection';
|
||||
import { parseError, serializeError } from '../serializers';
|
||||
import { Accessibility } from './accessibility';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { ElementHandle } from './elementHandle';
|
||||
import { Frame, FunctionWithSource, GotoOptions } from './frame';
|
||||
import { Func1, FuncOn, SmartHandle } from './jsHandle';
|
||||
import { Request, Response, RouteHandler, Route } from './network';
|
||||
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';
|
||||
import { TimeoutError } from '../../errors';
|
||||
import { TimeoutSettings } from '../../timeoutSettings';
|
||||
import { parseError, serializeError } from '../serializers';
|
||||
import { ElementHandle } from './elementHandle';
|
||||
import { Worker } from './worker';
|
||||
import { Frame, FunctionWithSource, GotoOptions } from './frame';
|
||||
import { Keyboard, Mouse } from './input';
|
||||
import { Func1, FuncOn, SmartHandle } from './jsHandle';
|
||||
import { Request, Response, Route, RouteHandler } from './network';
|
||||
import { FileChooser } from './fileChooser';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
readonly pdf: ((options?: types.PDFOptions) => Promise<Buffer>) | undefined;
|
||||
|
||||
private _browserContext: BrowserContext | undefined;
|
||||
_ownedContext: BrowserContext | undefined;
|
||||
|
||||
private _mainFrame: Frame;
|
||||
private _frames = new Set<Frame>();
|
||||
private _workers: Worker[] = [];
|
||||
_workers = new Set<Worker>();
|
||||
private _closed = false;
|
||||
private _viewportSize: types.Size | null;
|
||||
private _routes: { url: types.URLMatch, handler: RouteHandler }[] = [];
|
||||
@ -83,6 +85,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
this._channel.on('dialog', dialog => this.emit(Events.Page.Dialog, Dialog.from(dialog)));
|
||||
this._channel.on('domcontentloaded', () => this.emit(Events.Page.DOMContentLoaded));
|
||||
this._channel.on('download', download => this.emit(Events.Page.Download, Download.from(download)));
|
||||
this._channel.on('fileChooser', ({ element, isMultiple }) => this.emit(Events.Page.FileChooser, new FileChooser(this, ElementHandle.from(element), isMultiple)));
|
||||
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));
|
||||
@ -94,6 +97,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
this._channel.on('requestFinished', request => this.emit(Events.Page.RequestFinished, Request.from(request)));
|
||||
this._channel.on('response', response => this.emit(Events.Page.Response, Response.from(response)));
|
||||
this._channel.on('route', ({ route, request }) => this._onRoute(Route.from(route), Request.from(request)));
|
||||
this._channel.on('worker', worker => this._onWorker(Worker.from(worker)));
|
||||
}
|
||||
|
||||
_setBrowserContext(context: BrowserContext) {
|
||||
@ -145,6 +149,12 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
this._browserContext!._onBinding(bindingCall);
|
||||
}
|
||||
|
||||
_onWorker(worker: Worker): void {
|
||||
this._workers.add(worker);
|
||||
worker._page = this;
|
||||
this.emit(Events.Page.Worker, worker);
|
||||
}
|
||||
|
||||
private _onClose() {
|
||||
this._closed = true;
|
||||
this._browserContext!._pages.delete(this);
|
||||
@ -221,7 +231,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
return this._attributeToPage(() => this._mainFrame.waitForSelector(selector, options));
|
||||
}
|
||||
|
||||
async dispatchEvent(selector: string, type: string, eventInit?: Object, options?: types.TimeoutOptions): Promise<void> {
|
||||
async dispatchEvent(selector: string, type: string, eventInit?: any, options?: types.TimeoutOptions): Promise<void> {
|
||||
return this._attributeToPage(() => this._mainFrame.dispatchEvent(selector, type, eventInit, options));
|
||||
}
|
||||
|
||||
@ -465,7 +475,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
}
|
||||
|
||||
workers(): Worker[] {
|
||||
return this._workers;
|
||||
return [...this._workers];
|
||||
}
|
||||
|
||||
on(event: string | symbol, listener: Listener): this {
|
||||
@ -483,33 +493,10 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
this._channel.setFileChooserInterceptedNoReply({ intercepted: false });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class Worker extends EventEmitter {
|
||||
private _url: string;
|
||||
private _channel: any;
|
||||
|
||||
constructor(url: string) {
|
||||
super();
|
||||
this._url = url;
|
||||
}
|
||||
|
||||
url(): string {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R>;
|
||||
async evaluate<R>(pageFunction: Func1<void, R>, arg?: any): Promise<R>;
|
||||
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R> {
|
||||
assertMaxArguments(arguments.length, 2);
|
||||
return await this._channel.evaluate({ pageFunction, arg });
|
||||
}
|
||||
|
||||
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
|
||||
async evaluateHandle<R>(pageFunction: Func1<void, R>, arg?: any): Promise<SmartHandle<R>>;
|
||||
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
|
||||
assertMaxArguments(arguments.length, 2);
|
||||
return await this._channel.evaluateHandle({ pageFunction, arg });
|
||||
async pdf(options?: types.PDFOptions): Promise<Buffer> {
|
||||
const binary = await this._channel.pdf({ options });
|
||||
return Buffer.from(binary, 'base64');
|
||||
}
|
||||
}
|
||||
|
||||
|
57
src/rpc/client/worker.ts
Normal file
57
src/rpc/client/worker.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 { Events } from '../../events';
|
||||
import { assertMaxArguments } from '../../helper';
|
||||
import { WorkerChannel, WorkerInitializer } from '../channels';
|
||||
import { Connection } from '../connection';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Func1, JSHandle, parseResult, serializeArgument, SmartHandle } from './jsHandle';
|
||||
import { Page } from './page';
|
||||
|
||||
export class Worker extends ChannelOwner<WorkerChannel, WorkerInitializer> {
|
||||
_page: Page | undefined;
|
||||
|
||||
static from(worker: WorkerChannel): Worker {
|
||||
return worker._object;
|
||||
}
|
||||
|
||||
constructor(connection: Connection, channel: WorkerChannel, initializer: WorkerInitializer) {
|
||||
super(connection, channel, initializer);
|
||||
channel.on('close', () => {
|
||||
this._page!._workers.delete(this);
|
||||
this.emit(Events.Worker.Close, this);
|
||||
});
|
||||
}
|
||||
|
||||
url(): string {
|
||||
return this._initializer.url;
|
||||
}
|
||||
|
||||
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R>;
|
||||
async evaluate<R>(pageFunction: Func1<void, R>, arg?: any): Promise<R>;
|
||||
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R> {
|
||||
assertMaxArguments(arguments.length, 2);
|
||||
return parseResult(await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }));
|
||||
}
|
||||
|
||||
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
|
||||
async evaluateHandle<R>(pageFunction: Func1<void, R>, arg?: any): Promise<SmartHandle<R>>;
|
||||
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
|
||||
assertMaxArguments(arguments.length, 2);
|
||||
return JSHandle.from(await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) })) as SmartHandle<R>;
|
||||
}
|
||||
}
|
@ -24,12 +24,14 @@ import { Frame } from './client/frame';
|
||||
import { JSHandle } from './client/jsHandle';
|
||||
import { Request, Response, Route } from './client/network';
|
||||
import { Page, BindingCall } from './client/page';
|
||||
import { Worker } from './client/worker';
|
||||
import debug = require('debug');
|
||||
import { Channel } from './channels';
|
||||
import { ConsoleMessage } from './client/consoleMessage';
|
||||
import { Dialog } from './client/dialog';
|
||||
import { Download } from './client/download';
|
||||
import { parseError } from './serializers';
|
||||
import { BrowserServer } from './client/browserServer';
|
||||
|
||||
export class Connection {
|
||||
private _channels = new Map<string, Channel>();
|
||||
@ -52,6 +54,9 @@ export class Connection {
|
||||
case 'browser':
|
||||
result = new Browser(this, channel, initializer);
|
||||
break;
|
||||
case 'browserServer':
|
||||
result = new BrowserServer(this, channel, initializer);
|
||||
break;
|
||||
case 'browserType':
|
||||
result = new BrowserType(this, channel, initializer);
|
||||
break;
|
||||
@ -88,6 +93,9 @@ export class Connection {
|
||||
case 'route':
|
||||
result = new Route(this, channel, initializer);
|
||||
break;
|
||||
case 'worker':
|
||||
result = new Worker(this, channel, initializer);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Missing type ' + type);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
import { BrowserChannel, BrowserContextChannel, PageChannel, BrowserInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { PageDispatcher } from './pageDispatcher';
|
||||
import { Events } from '../../events';
|
||||
|
||||
export class BrowserDispatcher extends Dispatcher<Browser, BrowserInitializer> implements BrowserChannel {
|
||||
static from(scope: DispatcherScope, browser: BrowserBase): BrowserDispatcher {
|
||||
@ -37,6 +38,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, BrowserInitializer> i
|
||||
|
||||
constructor(scope: DispatcherScope, browser: BrowserBase) {
|
||||
super(scope, browser, 'browser', {});
|
||||
browser.on(Events.Browser.Disconnected, () => this._dispatchEvent('close'));
|
||||
}
|
||||
|
||||
async newContext(params: { options?: types.BrowserContextOptions }): Promise<BrowserContextChannel> {
|
||||
|
44
src/rpc/server/browserServerDispatcher.ts
Normal file
44
src/rpc/server/browserServerDispatcher.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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 { BrowserServer } from '../../server/browserServer';
|
||||
import { BrowserServerChannel, BrowserServerInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { Events } from '../../events';
|
||||
|
||||
export class BrowserServerDispatcher extends Dispatcher<BrowserServer, BrowserServerInitializer> implements BrowserServerChannel {
|
||||
static from(scope: DispatcherScope, browserServer: BrowserServer): BrowserServerDispatcher {
|
||||
if ((browserServer as any)[scope.dispatcherSymbol])
|
||||
return (browserServer as any)[scope.dispatcherSymbol];
|
||||
return new BrowserServerDispatcher(scope, browserServer);
|
||||
}
|
||||
|
||||
constructor(scope: DispatcherScope, browserServer: BrowserServer) {
|
||||
super(scope, browserServer, 'browserServer', {
|
||||
wsEndpoint: browserServer.wsEndpoint(),
|
||||
pid: browserServer.process().pid
|
||||
});
|
||||
browserServer.on(Events.BrowserServer.Close, () => this._dispatchEvent('close'));
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this._object.close();
|
||||
}
|
||||
|
||||
async kill(): Promise<void> {
|
||||
await this._object.close();
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ import { BrowserBase } from '../../browser';
|
||||
import { BrowserTypeBase, BrowserType } from '../../server/browserType';
|
||||
import * as types from '../../types';
|
||||
import { BrowserDispatcher } from './browserDispatcher';
|
||||
import { BrowserChannel, BrowserTypeChannel, BrowserContextChannel, BrowserTypeInitializer } from '../channels';
|
||||
import { BrowserChannel, BrowserTypeChannel, BrowserContextChannel, BrowserTypeInitializer, BrowserServerChannel } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { BrowserContextBase } from '../../browserContext';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
import { BrowserServerDispatcher } from './browserServerDispatcher';
|
||||
|
||||
export class BrowserTypeDispatcher extends Dispatcher<BrowserType, BrowserTypeInitializer> implements BrowserTypeChannel {
|
||||
static from(scope: DispatcherScope, browserType: BrowserTypeBase): BrowserTypeDispatcher {
|
||||
@ -47,6 +48,10 @@ export class BrowserTypeDispatcher extends Dispatcher<BrowserType, BrowserTypeIn
|
||||
return BrowserContextDispatcher.from(this._scope, browserContext as BrowserContextBase);
|
||||
}
|
||||
|
||||
async launchServer(params: { options?: types.LaunchServerOptions }): Promise<BrowserServerChannel> {
|
||||
return BrowserServerDispatcher.from(this._scope, await this._object.launchServer(params.options));
|
||||
}
|
||||
|
||||
async connect(params: { options: types.ConnectOptions }): Promise<BrowserChannel> {
|
||||
const browser = await this._object.connect(params.options);
|
||||
return BrowserDispatcher.from(this._scope, browser as BrowserBase);
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { ConsoleMessage } from '../../console';
|
||||
import { ConsoleMessageChannel, ConsoleMessageInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
import { fromHandle } from './elementHandlerDispatcher';
|
||||
|
||||
export class ConsoleMessageDispatcher extends Dispatcher<ConsoleMessage, ConsoleMessageInitializer> implements ConsoleMessageChannel {
|
||||
static from(scope: DispatcherScope, message: ConsoleMessage): ConsoleMessageDispatcher {
|
||||
@ -30,7 +30,7 @@ export class ConsoleMessageDispatcher extends Dispatcher<ConsoleMessage, Console
|
||||
super(scope, message, 'consoleMessage', {
|
||||
type: message.type(),
|
||||
text: message.text(),
|
||||
args: message.args().map(a => ElementHandleDispatcher.from(scope, a)),
|
||||
args: message.args().map(a => fromHandle(scope, a)),
|
||||
location: message.location(),
|
||||
});
|
||||
}
|
||||
|
@ -22,21 +22,21 @@ import { DispatcherScope } from '../dispatcher';
|
||||
import { JSHandleDispatcher, serializeResult, parseArgument } from './jsHandleDispatcher';
|
||||
import { FrameDispatcher } from './frameDispatcher';
|
||||
|
||||
export function fromHandle(scope: DispatcherScope, handle: js.JSHandle): JSHandleDispatcher {
|
||||
if ((handle as any)[scope.dispatcherSymbol])
|
||||
return (handle as any)[scope.dispatcherSymbol];
|
||||
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle);
|
||||
}
|
||||
|
||||
export function fromNullableHandle(scope: DispatcherScope, handle: js.JSHandle | null): JSHandleDispatcher | null {
|
||||
if (!handle)
|
||||
return null;
|
||||
return fromHandle(scope, handle);
|
||||
}
|
||||
|
||||
export class ElementHandleDispatcher extends JSHandleDispatcher implements ElementHandleChannel {
|
||||
readonly _elementHandle: ElementHandle;
|
||||
|
||||
static from(scope: DispatcherScope, handle: js.JSHandle): JSHandleDispatcher {
|
||||
if ((handle as any)[scope.dispatcherSymbol])
|
||||
return (handle as any)[scope.dispatcherSymbol];
|
||||
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle);
|
||||
}
|
||||
|
||||
static fromNullable(scope: DispatcherScope, handle: js.JSHandle | null): JSHandleDispatcher | null {
|
||||
if (!handle)
|
||||
return null;
|
||||
return ElementHandleDispatcher.from(scope, handle);
|
||||
}
|
||||
|
||||
static fromElement(scope: DispatcherScope, handle: ElementHandle): ElementHandleDispatcher {
|
||||
if ((handle as any)[scope.dispatcherSymbol])
|
||||
return (handle as any)[scope.dispatcherSymbol];
|
||||
@ -125,7 +125,7 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements Eleme
|
||||
}
|
||||
|
||||
async press(params: { key: string, options: { delay?: number } & types.NavigatingActionWaitOptions }) {
|
||||
await this._elementHandle.type(params.key, params.options);
|
||||
await this._elementHandle.press(params.key, params.options);
|
||||
}
|
||||
|
||||
async check(params: { options?: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions }) {
|
||||
|
@ -18,7 +18,7 @@ import { Frame } from '../../frames';
|
||||
import * as types from '../../types';
|
||||
import { ElementHandleChannel, FrameChannel, FrameInitializer, JSHandleChannel, ResponseChannel } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { convertSelectOptionValues, ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
import { convertSelectOptionValues, ElementHandleDispatcher, fromHandle } from './elementHandlerDispatcher';
|
||||
import { parseArgument, serializeResult } from './jsHandleDispatcher';
|
||||
import { ResponseDispatcher } from './networkDispatchers';
|
||||
|
||||
@ -72,7 +72,7 @@ export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> impleme
|
||||
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any, isPage?: boolean }): Promise<JSHandleChannel> {
|
||||
const target = params.isPage ? this._frame._page : this._frame;
|
||||
return ElementHandleDispatcher.fromElement(this._scope, await target._evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg)));
|
||||
return fromHandle(this._scope, await target._evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg)));
|
||||
}
|
||||
|
||||
async waitForSelector(params: { selector: string, options: types.WaitForElementOptions, isPage?: boolean }): Promise<ElementHandleChannel | null> {
|
||||
@ -80,9 +80,9 @@ export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> impleme
|
||||
return ElementHandleDispatcher.fromNullableElement(this._scope, await target.waitForSelector(params.selector, params.options));
|
||||
}
|
||||
|
||||
async dispatchEvent(params: { selector: string, type: string, eventInit: Object | undefined, options: types.TimeoutOptions, isPage?: boolean }): Promise<void> {
|
||||
async dispatchEvent(params: { selector: string, type: string, eventInit: any, options: types.TimeoutOptions, isPage?: boolean }): Promise<void> {
|
||||
const target = params.isPage ? this._frame._page : this._frame;
|
||||
return target.dispatchEvent(params.selector, params.type, params.eventInit, params.options);
|
||||
return target.dispatchEvent(params.selector, params.type, parseArgument(params.eventInit), params.options);
|
||||
}
|
||||
|
||||
async $eval(params: { selector: string, expression: string, isFunction: boolean, arg: any, isPage?: boolean }): Promise<any> {
|
||||
@ -202,7 +202,7 @@ export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> impleme
|
||||
|
||||
async waitForFunction(params: { expression: string, isFunction: boolean, arg: any; options: types.WaitForFunctionOptions, isPage?: boolean }): Promise<JSHandleChannel> {
|
||||
const target = params.isPage ? this._frame._page : this._frame;
|
||||
return ElementHandleDispatcher.from(this._scope, await target._waitForFunctionExpression(params.expression, params.isFunction, parseArgument(params.arg), params.options));
|
||||
return fromHandle(this._scope, await target._waitForFunctionExpression(params.expression, params.isFunction, parseArgument(params.arg), params.options));
|
||||
}
|
||||
|
||||
async title(): Promise<string> {
|
||||
|
@ -17,8 +17,8 @@
|
||||
import * as js from '../../javascript';
|
||||
import { JSHandleChannel, JSHandleInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
import { parseEvaluationResultValue, serializeAsCallArgument } from '../../common/utilityScriptSerializers';
|
||||
import { fromHandle } from './elementHandlerDispatcher';
|
||||
|
||||
export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitializer> implements JSHandleChannel {
|
||||
|
||||
@ -26,6 +26,7 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitiali
|
||||
super(scope, jsHandle, jsHandle.asElement() ? 'elementHandle' : 'jsHandle', {
|
||||
preview: jsHandle.toString(),
|
||||
});
|
||||
jsHandle._setPreviewCallback(preview => this._dispatchEvent('previewUpdated', preview));
|
||||
}
|
||||
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<any> {
|
||||
@ -34,7 +35,7 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitiali
|
||||
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any}): Promise<JSHandleChannel> {
|
||||
const jsHandle = await this._object._evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
|
||||
return ElementHandleDispatcher.from(this._scope, jsHandle);
|
||||
return fromHandle(this._scope, jsHandle);
|
||||
}
|
||||
|
||||
async getPropertyList(): Promise<{ name: string, value: JSHandleChannel }[]> {
|
||||
|
@ -18,9 +18,9 @@ import { BrowserContext } from '../../browserContext';
|
||||
import { Events } from '../../events';
|
||||
import { Frame } from '../../frames';
|
||||
import { Request } from '../../network';
|
||||
import { Page } from '../../page';
|
||||
import { Page, Worker } from '../../page';
|
||||
import * as types from '../../types';
|
||||
import { BindingCallChannel, BindingCallInitializer, ElementHandleChannel, PageChannel, PageInitializer, ResponseChannel } from '../channels';
|
||||
import { BindingCallChannel, BindingCallInitializer, ElementHandleChannel, PageChannel, PageInitializer, ResponseChannel, WorkerInitializer, WorkerChannel, JSHandleChannel, Binary } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from '../dispatcher';
|
||||
import { parseError, serializeError } from '../serializers';
|
||||
import { ConsoleMessageDispatcher } from './consoleMessageDispatcher';
|
||||
@ -28,6 +28,9 @@ import { DialogDispatcher } from './dialogDispatcher';
|
||||
import { DownloadDispatcher } from './downloadDispatcher';
|
||||
import { FrameDispatcher } from './frameDispatcher';
|
||||
import { RequestDispatcher, ResponseDispatcher, RouteDispatcher } from './networkDispatchers';
|
||||
import { serializeResult, parseArgument } from './jsHandleDispatcher';
|
||||
import { fromHandle, ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||
import { FileChooser } from '../../fileChooser';
|
||||
|
||||
export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements PageChannel {
|
||||
private _page: Page;
|
||||
@ -56,6 +59,10 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
|
||||
page.on(Events.Page.DOMContentLoaded, () => this._dispatchEvent('domcontentloaded'));
|
||||
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.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
|
||||
element: ElementHandleDispatcher.fromElement(this._scope, fileChooser.element()),
|
||||
isMultiple: fileChooser.isMultiple()
|
||||
}));
|
||||
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));
|
||||
@ -69,6 +76,7 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
|
||||
}));
|
||||
page.on(Events.Page.RequestFinished, request => this._dispatchEvent('requestFinished', RequestDispatcher.from(this._scope, request)));
|
||||
page.on(Events.Page.Response, response => this._dispatchEvent('response', ResponseDispatcher.from(this._scope, response)));
|
||||
page.on(Events.Page.Worker, worker => this._dispatchEvent('worker', WorkerDispatcher.from(this._scope, worker)));
|
||||
}
|
||||
|
||||
async setDefaultNavigationTimeoutNoReply(params: { timeout: number }) {
|
||||
@ -187,6 +195,13 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
|
||||
});
|
||||
}
|
||||
|
||||
async pdf(params: { options?: types.PDFOptions }): Promise<Binary> {
|
||||
if (!this._page.pdf)
|
||||
throw new Error('PDF generation is only supported for Headless Chromium');
|
||||
const binary = await this._page.pdf(params.options);
|
||||
return binary.toString('base64');
|
||||
}
|
||||
|
||||
_onFrameAttached(frame: Frame) {
|
||||
this._dispatchEvent('frameAttached', FrameDispatcher.from(this._scope, frame));
|
||||
}
|
||||
@ -201,6 +216,29 @@ export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements
|
||||
}
|
||||
|
||||
|
||||
export class WorkerDispatcher extends Dispatcher<Worker, WorkerInitializer> implements WorkerChannel {
|
||||
static from(scope: DispatcherScope, worker: Worker): WorkerDispatcher {
|
||||
if ((worker as any)[scope.dispatcherSymbol])
|
||||
return (worker as any)[scope.dispatcherSymbol];
|
||||
return new WorkerDispatcher(scope, worker);
|
||||
}
|
||||
|
||||
constructor(scope: DispatcherScope, worker: Worker) {
|
||||
super(scope, worker, 'worker', {
|
||||
url: worker.url()
|
||||
});
|
||||
worker.on(Events.Worker.Close, () => this._dispatchEvent('close'));
|
||||
}
|
||||
|
||||
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: any, isPage?: boolean }): Promise<any> {
|
||||
return serializeResult(await this._object._evaluateExpression(params.expression, params.isFunction, parseArgument(params.arg)));
|
||||
}
|
||||
|
||||
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any, isPage?: boolean }): Promise<JSHandleChannel> {
|
||||
return fromHandle(this._scope, await this._object._evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg)));
|
||||
}
|
||||
}
|
||||
|
||||
export class BindingCallDispatcher extends Dispatcher<{}, BindingCallInitializer> implements BindingCallChannel {
|
||||
private _resolve: ((arg: any) => void) | undefined;
|
||||
private _reject: ((error: any) => void) | undefined;
|
||||
|
@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('../utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = require('../utils').testOptions(browserType);
|
||||
|
||||
describe('ChromiumBrowserContext', function() {
|
||||
describe.skip(CHANNEL)('ChromiumBrowserContext', function() {
|
||||
it('should create a worker from a service worker', async({browser, page, server, context}) => {
|
||||
const [worker] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
|
@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('../utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = require('../utils').testOptions(browserType);
|
||||
|
||||
describe('JSCoverage', function() {
|
||||
describe.skip(CHANNEL)('JSCoverage', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
||||
@ -88,7 +88,7 @@ describe('JSCoverage', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('CSSCoverage', function() {
|
||||
describe.skip(CHANNEL)('CSSCoverage', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/simple.html');
|
||||
|
@ -17,9 +17,9 @@
|
||||
const path = require('path');
|
||||
const utils = require('../utils');
|
||||
const {makeUserDataDir, removeUserDataDir} = utils;
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = utils.testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, CHANNEL} = utils.testOptions(browserType);
|
||||
|
||||
describe('launcher', function() {
|
||||
describe.skip(CHANNEL)('launcher', function() {
|
||||
it('should throw with remote-debugging-pipe argument', async({browserType, defaultBrowserOptions}) => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
||||
@ -32,7 +32,7 @@ describe('launcher', function() {
|
||||
const browser = await browserType.launchServer(options);
|
||||
await browser.close();
|
||||
});
|
||||
it.skip(USES_HOOKS)('should open devtools when "devtools: true" option is given', async({browserType, defaultBrowserOptions}) => {
|
||||
it('should open devtools when "devtools: true" option is given', async({browserType, defaultBrowserOptions}) => {
|
||||
let devtoolsCallback;
|
||||
const devtoolsPromise = new Promise(f => devtoolsCallback = f);
|
||||
const __testHookForDevTools = devtools => devtools.__testHookOnBinding = parsed => {
|
||||
@ -49,7 +49,7 @@ describe('launcher', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('extensions', () => {
|
||||
describe.skip(CHANNEL)('extensions', () => {
|
||||
it('should return background pages', async({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
|
||||
@ -73,7 +73,7 @@ describe('extensions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext', function() {
|
||||
describe.skip(CHANNEL)('BrowserContext', function() {
|
||||
it('should not create pages automatically', async ({browserType, defaultBrowserOptions}) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const browserSession = await browser.newBrowserCDPSession();
|
||||
|
@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('../utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = require('../utils').testOptions(browserType);
|
||||
|
||||
describe('OOPIF', function() {
|
||||
describe.skip(CHANNEL)('OOPIF', function() {
|
||||
beforeAll(async function(state) {
|
||||
state.browser = await state.browserType.launch(Object.assign({}, state.defaultBrowserOptions, {
|
||||
args: (state.defaultBrowserOptions.args || []).concat(['--site-per-process']),
|
||||
|
@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('../utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = require('../utils').testOptions(browserType);
|
||||
|
||||
describe('ChromiumBrowserContext.createSession', function() {
|
||||
describe.skip(CHANNEL)('ChromiumBrowserContext.createSession', function() {
|
||||
it('should work', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
|
||||
@ -95,7 +95,7 @@ describe('ChromiumBrowserContext.createSession', function() {
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
describe('ChromiumBrowser.newBrowserCDPSession', function() {
|
||||
describe.skip(CHANNEL)('ChromiumBrowser.newBrowserCDPSession', function() {
|
||||
it('should work', async function({page, browser, server}) {
|
||||
const session = await browser.newBrowserCDPSession();
|
||||
const version = await session.send('Browser.getVersion');
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {FFOX, CHROMIUM, WEBKIT, OUTPUT_DIR} = require('../utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, OUTPUT_DIR, CHANNEL} = require('../utils').testOptions(browserType);
|
||||
|
||||
describe('Chromium.startTracing', function() {
|
||||
describe.skip(CHANNEL)('Chromium.startTracing', function() {
|
||||
beforeEach(async function(state) {
|
||||
state.outputFile = path.join(OUTPUT_DIR, `trace-${state.parallelIndex}.json`);
|
||||
state.browser = await state.browserType.launch(state.defaultBrowserOptions);
|
||||
|
@ -336,7 +336,7 @@ describe('launchPersistentContext()', function() {
|
||||
expect(error.message).toContain('can not specify page');
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should have passed URL when launching with ignoreDefaultArgs: true', async ({browserType, defaultBrowserOptions, server}) => {
|
||||
it.skip(USES_HOOKS)('should have passed URL when launching with ignoreDefaultArgs: true', async ({browserType, defaultBrowserOptions, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const args = browserType._defaultArgs(defaultBrowserOptions, 'persistent', userDataDir, 0).filter(a => a !== 'about:blank');
|
||||
const options = {
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Logger', function() {
|
||||
describe.skip(CHANNEL)('Logger', function() {
|
||||
it('should log', async({browserType, defaultBrowserOptions}) => {
|
||||
const log = [];
|
||||
const browser = await browserType.launch({...defaultBrowserOptions, logger: {
|
||||
|
@ -18,7 +18,7 @@
|
||||
const utils = require('./utils');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = utils.testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN, CHANNEL} = utils.testOptions(browserType);
|
||||
|
||||
describe('Page.goto', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
@ -541,7 +541,7 @@ describe('Page.goto', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.waitForNavigation', function() {
|
||||
describe.skip(CHANNEL)('Page.waitForNavigation', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [response] = await Promise.all([
|
||||
|
@ -24,6 +24,8 @@ const { Connection } = require('../lib/rpc/connection');
|
||||
const { helper } = require('../lib/helper');
|
||||
const { BrowserTypeDispatcher } = require('../lib/rpc/server/browserTypeDispatcher');
|
||||
|
||||
Error.stackTraceLimit = 15;
|
||||
|
||||
function getCLIArgument(argName) {
|
||||
for (let i = 0; i < process.argv.length; ++i) {
|
||||
// Support `./test.js --foo bar
|
||||
|
@ -200,7 +200,8 @@ const utils = module.exports = {
|
||||
browserType,
|
||||
GOLDEN_DIR,
|
||||
OUTPUT_DIR,
|
||||
USES_HOOKS: !!process.env.PWCHANNEL
|
||||
USES_HOOKS: !!process.env.PWCHANNEL,
|
||||
CHANNEL: !!process.env.PWCHANNEL,
|
||||
};
|
||||
},
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user