2019-11-18 18:18:28 -08:00
|
|
|
/**
|
|
|
|
* Copyright 2017 Google Inc. All rights reserved.
|
|
|
|
* Modifications 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.
|
|
|
|
*/
|
|
|
|
|
2019-12-09 13:08:21 -08:00
|
|
|
import * as dom from './dom';
|
|
|
|
import * as frames from './frames';
|
|
|
|
import { assert, debugError, helper } from './helper';
|
|
|
|
import * as input from './input';
|
|
|
|
import * as js from './javascript';
|
|
|
|
import * as network from './network';
|
2019-12-11 12:36:42 -08:00
|
|
|
import { Screenshotter } from './screenshotter';
|
2019-12-12 20:54:40 -08:00
|
|
|
import { TimeoutSettings } from './timeoutSettings';
|
2019-12-09 13:08:21 -08:00
|
|
|
import * as types from './types';
|
2019-11-22 14:46:34 -08:00
|
|
|
import { Events } from './events';
|
2019-12-18 16:23:05 -08:00
|
|
|
import { BrowserContext } from './browserContext';
|
2019-12-10 15:17:42 -08:00
|
|
|
import { ConsoleMessage, ConsoleMessageLocation } from './console';
|
2019-12-12 09:02:37 -08:00
|
|
|
import Injected from './injected/injected';
|
2020-01-03 11:15:43 -08:00
|
|
|
import * as accessibility from './accessibility';
|
2020-01-07 11:55:24 -08:00
|
|
|
import * as platform from './platform';
|
2019-12-09 13:08:21 -08:00
|
|
|
|
|
|
|
export interface PageDelegate {
|
|
|
|
readonly rawMouse: input.RawMouse;
|
|
|
|
readonly rawKeyboard: input.RawKeyboard;
|
2019-12-11 12:36:42 -08:00
|
|
|
|
2019-12-17 11:28:09 -08:00
|
|
|
reload(): Promise<void>;
|
|
|
|
goBack(): Promise<boolean>;
|
|
|
|
goForward(): Promise<boolean>;
|
2019-12-09 13:08:21 -08:00
|
|
|
exposeBinding(name: string, bindingFunction: string): Promise<void>;
|
|
|
|
evaluateOnNewDocument(source: string): Promise<void>;
|
|
|
|
closePage(runBeforeUnload: boolean): Promise<void>;
|
|
|
|
|
2019-12-16 22:02:33 -08:00
|
|
|
navigateFrame(frame: frames.Frame, url: string, referrer: string | undefined): Promise<frames.GotoResult>;
|
|
|
|
needsLifecycleResetOnSetContent(): boolean;
|
2019-12-11 12:36:42 -08:00
|
|
|
|
2019-12-09 13:08:21 -08:00
|
|
|
setExtraHTTPHeaders(extraHTTPHeaders: network.Headers): Promise<void>;
|
|
|
|
setViewport(viewport: types.Viewport): Promise<void>;
|
2020-01-03 12:59:06 -08:00
|
|
|
setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void>;
|
2019-12-09 13:08:21 -08:00
|
|
|
setCacheEnabled(enabled: boolean): Promise<void>;
|
2019-12-30 14:05:28 -08:00
|
|
|
setRequestInterception(enabled: boolean): Promise<void>;
|
2019-12-30 14:09:54 -08:00
|
|
|
setOfflineMode(enabled: boolean): Promise<void>;
|
|
|
|
authenticate(credentials: types.Credentials | null): Promise<void>;
|
2019-12-11 12:36:42 -08:00
|
|
|
|
|
|
|
getBoundingBoxForScreenshot(handle: dom.ElementHandle<Node>): Promise<types.Rect | null>;
|
|
|
|
canScreenshotOutsideViewport(): boolean;
|
|
|
|
setBackgroundColor(color?: { r: number; g: number; b: number; a: number; }): Promise<void>;
|
2020-01-07 11:55:24 -08:00
|
|
|
takeScreenshot(format: string, options: types.ScreenshotOptions, viewport: types.Viewport): Promise<platform.BufferType>;
|
2019-12-11 12:36:42 -08:00
|
|
|
resetViewport(oldSize: types.Size): Promise<void>;
|
2019-12-12 17:51:05 -08:00
|
|
|
|
|
|
|
isElementHandle(remoteObject: any): boolean;
|
2019-12-12 21:11:52 -08:00
|
|
|
adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>>;
|
2019-12-19 15:19:22 -08:00
|
|
|
getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>; // Only called for frame owner elements.
|
|
|
|
getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>;
|
2019-12-12 17:51:05 -08:00
|
|
|
getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null>;
|
|
|
|
layoutViewport(): Promise<{ width: number, height: number }>;
|
2020-01-03 12:59:06 -08:00
|
|
|
setInputFiles(handle: dom.ElementHandle, files: types.FilePayload[]): Promise<void>;
|
2019-12-12 17:51:05 -08:00
|
|
|
getBoundingBox(handle: dom.ElementHandle): Promise<types.Rect | null>;
|
2020-01-03 11:15:43 -08:00
|
|
|
|
|
|
|
getAccessibilityTree(): Promise<accessibility.AXNode>;
|
2020-01-07 13:57:37 -08:00
|
|
|
pdf?: (options?: types.PDFOptions) => Promise<platform.BufferType>;
|
|
|
|
coverage(): Coverage | undefined;
|
2019-12-09 13:08:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
type PageState = {
|
|
|
|
viewport: types.Viewport | null;
|
2020-01-03 12:59:06 -08:00
|
|
|
mediaType: types.MediaType | null;
|
|
|
|
colorScheme: types.ColorScheme | null;
|
2019-12-09 13:08:21 -08:00
|
|
|
extraHTTPHeaders: network.Headers | null;
|
|
|
|
cacheEnabled: boolean | null;
|
2019-12-30 14:05:28 -08:00
|
|
|
interceptNetwork: boolean | null;
|
2019-12-30 14:09:54 -08:00
|
|
|
offlineMode: boolean | null;
|
|
|
|
credentials: types.Credentials | null;
|
2020-01-09 17:06:06 -08:00
|
|
|
hasTouch: boolean | null;
|
2019-12-09 13:08:21 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
export type FileChooser = {
|
|
|
|
element: dom.ElementHandle,
|
|
|
|
multiple: boolean
|
|
|
|
};
|
|
|
|
|
2020-01-07 11:55:24 -08:00
|
|
|
export class Page extends platform.EventEmitter {
|
2019-11-18 18:18:28 -08:00
|
|
|
private _closed = false;
|
2019-12-05 14:11:48 -08:00
|
|
|
private _closedCallback: () => void;
|
|
|
|
private _closedPromise: Promise<void>;
|
2019-12-06 13:36:47 -08:00
|
|
|
private _disconnected = false;
|
|
|
|
private _disconnectedCallback: (e: Error) => void;
|
2019-12-10 15:34:36 -07:00
|
|
|
readonly _disconnectedPromise: Promise<Error>;
|
2019-12-11 07:18:43 -08:00
|
|
|
private _browserContext: BrowserContext;
|
2019-12-06 13:36:47 -08:00
|
|
|
readonly keyboard: input.Keyboard;
|
|
|
|
readonly mouse: input.Mouse;
|
2019-12-09 13:08:21 -08:00
|
|
|
readonly _timeoutSettings: TimeoutSettings;
|
|
|
|
readonly _delegate: PageDelegate;
|
|
|
|
readonly _state: PageState;
|
2019-11-18 18:18:28 -08:00
|
|
|
private _pageBindings = new Map<string, Function>();
|
2019-12-09 13:08:21 -08:00
|
|
|
readonly _screenshotter: Screenshotter;
|
2019-12-16 15:56:11 -08:00
|
|
|
readonly _frameManager: frames.FrameManager;
|
2020-01-03 11:15:43 -08:00
|
|
|
readonly accessibility: accessibility.Accessibility;
|
2020-01-07 12:59:01 -08:00
|
|
|
private _workers = new Map<string, Worker>();
|
2020-01-07 13:57:37 -08:00
|
|
|
readonly pdf: ((options?: types.PDFOptions) => Promise<platform.BufferType>) | undefined;
|
|
|
|
readonly coverage: Coverage | undefined;
|
2019-11-18 18:18:28 -08:00
|
|
|
|
2019-12-11 07:18:43 -08:00
|
|
|
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
2019-11-18 18:18:28 -08:00
|
|
|
super();
|
2019-12-09 13:08:21 -08:00
|
|
|
this._delegate = delegate;
|
2019-12-05 14:11:48 -08:00
|
|
|
this._closedPromise = new Promise(f => this._closedCallback = f);
|
2019-12-06 13:36:47 -08:00
|
|
|
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
|
2019-12-05 14:11:48 -08:00
|
|
|
this._browserContext = browserContext;
|
2019-12-09 13:08:21 -08:00
|
|
|
this._state = {
|
2019-12-18 12:23:33 -08:00
|
|
|
viewport: browserContext._options.viewport || null,
|
2020-01-05 14:39:16 -08:00
|
|
|
mediaType: null,
|
|
|
|
colorScheme: null,
|
2019-12-09 13:08:21 -08:00
|
|
|
extraHTTPHeaders: null,
|
|
|
|
cacheEnabled: null,
|
2019-12-30 14:09:54 -08:00
|
|
|
interceptNetwork: null,
|
|
|
|
offlineMode: null,
|
2020-01-09 17:06:06 -08:00
|
|
|
credentials: null,
|
|
|
|
hasTouch: null
|
2019-12-09 13:08:21 -08:00
|
|
|
};
|
2020-01-03 11:15:43 -08:00
|
|
|
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate));
|
2019-12-09 13:08:21 -08:00
|
|
|
this.keyboard = new input.Keyboard(delegate.rawKeyboard);
|
|
|
|
this.mouse = new input.Mouse(delegate.rawMouse, this.keyboard);
|
2019-11-18 18:18:28 -08:00
|
|
|
this._timeoutSettings = new TimeoutSettings();
|
2019-12-11 12:36:42 -08:00
|
|
|
this._screenshotter = new Screenshotter(this);
|
2019-12-16 15:56:11 -08:00
|
|
|
this._frameManager = new frames.FrameManager(this);
|
2020-01-07 13:57:37 -08:00
|
|
|
if (delegate.pdf)
|
|
|
|
this.pdf = delegate.pdf.bind(delegate);
|
|
|
|
this.coverage = delegate.coverage();
|
2020-01-13 09:14:28 -08:00
|
|
|
return helper.logPublicApiCalls('page', this);
|
2019-12-05 14:11:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
_didClose() {
|
|
|
|
assert(!this._closed, 'Page closed twice');
|
|
|
|
this._closed = true;
|
|
|
|
this.emit(Events.Page.Close);
|
|
|
|
this._closedCallback();
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2020-01-03 11:10:10 -08:00
|
|
|
_didCrash() {
|
|
|
|
const error = new Error('Page crashed!');
|
|
|
|
error.stack = '';
|
|
|
|
this.emit('error', error);
|
|
|
|
}
|
|
|
|
|
2019-12-06 13:36:47 -08:00
|
|
|
_didDisconnect() {
|
|
|
|
assert(!this._disconnected, 'Page disconnected twice');
|
|
|
|
this._disconnected = true;
|
|
|
|
this._disconnectedCallback(new Error('Target closed'));
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-06 13:36:47 -08:00
|
|
|
async _onFileChooserOpened(handle: dom.ElementHandle) {
|
2019-12-17 17:34:32 -08:00
|
|
|
const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple);
|
|
|
|
if (!this.listenerCount(Events.Page.FileChooser)) {
|
2019-12-06 13:36:47 -08:00
|
|
|
await handle.dispose();
|
2019-11-18 18:18:28 -08:00
|
|
|
return;
|
2019-12-06 13:36:47 -08:00
|
|
|
}
|
2019-12-17 17:34:32 -08:00
|
|
|
const fileChooser: FileChooser = { element: handle, multiple };
|
2019-11-27 14:26:30 -08:00
|
|
|
this.emit(Events.Page.FileChooser, fileChooser);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-11 07:18:43 -08:00
|
|
|
browserContext(): BrowserContext {
|
2019-12-05 14:11:48 -08:00
|
|
|
return this._browserContext;
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-11-27 16:03:51 -08:00
|
|
|
mainFrame(): frames.Frame {
|
2019-12-16 15:56:11 -08:00
|
|
|
return this._frameManager.mainFrame();
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-11-27 16:03:51 -08:00
|
|
|
frames(): frames.Frame[] {
|
2019-12-16 15:56:11 -08:00
|
|
|
return this._frameManager.frames();
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
setDefaultNavigationTimeout(timeout: number) {
|
|
|
|
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
setDefaultTimeout(timeout: number) {
|
|
|
|
this._timeoutSettings.setDefaultTimeout(timeout);
|
|
|
|
}
|
|
|
|
|
2019-12-18 14:28:16 -08:00
|
|
|
async $(selector: string): Promise<dom.ElementHandle<Element> | null> {
|
|
|
|
return this.mainFrame().$(selector);
|
|
|
|
}
|
|
|
|
|
2019-12-20 16:57:21 -08:00
|
|
|
async waitForSelector(selector: string, options?: types.TimeoutOptions & { visibility?: types.Visibility }): Promise<dom.ElementHandle<Element> | null> {
|
2019-12-18 14:28:16 -08:00
|
|
|
return this.mainFrame().waitForSelector(selector, options);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-12 09:02:37 -08:00
|
|
|
async _createSelector(name: string, handle: dom.ElementHandle<Element>): Promise<string> {
|
2019-12-12 21:11:52 -08:00
|
|
|
const mainContext = await this.mainFrame()._mainContext();
|
|
|
|
return mainContext.evaluate((injected: Injected, target: Element, name: string) => {
|
2019-12-12 09:02:37 -08:00
|
|
|
return injected.engines.get(name).create(document.documentElement, target);
|
2019-12-12 21:11:52 -08:00
|
|
|
}, await mainContext._injected(), handle, name);
|
2019-12-12 09:02:37 -08:00
|
|
|
}
|
|
|
|
|
2019-11-27 16:03:51 -08:00
|
|
|
evaluateHandle: types.EvaluateHandle = async (pageFunction, ...args) => {
|
2019-12-18 13:51:45 -08:00
|
|
|
return this.mainFrame().evaluateHandle(pageFunction, ...args as any);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
$eval: types.$Eval = async (selector, pageFunction, ...args) => {
|
2019-11-25 15:06:52 -08:00
|
|
|
return this.mainFrame().$eval(selector, pageFunction, ...args as any);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
$$eval: types.$$Eval = async (selector, pageFunction, ...args) => {
|
2019-11-25 15:06:52 -08:00
|
|
|
return this.mainFrame().$$eval(selector, pageFunction, ...args as any);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-17 14:30:02 -08:00
|
|
|
async $$(selector: string): Promise<dom.ElementHandle<Element>[]> {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().$$(selector);
|
|
|
|
}
|
|
|
|
|
2019-12-05 16:26:09 -08:00
|
|
|
async $x(expression: string): Promise<dom.ElementHandle<Element>[]> {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().$x(expression);
|
|
|
|
}
|
|
|
|
|
2019-11-27 16:02:31 -08:00
|
|
|
async addScriptTag(options: { url?: string; path?: string; content?: string; type?: string; }): Promise<dom.ElementHandle> {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().addScriptTag(options);
|
|
|
|
}
|
|
|
|
|
2019-11-27 16:02:31 -08:00
|
|
|
async addStyleTag(options: { url?: string; path?: string; content?: string; }): Promise<dom.ElementHandle> {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().addStyleTag(options);
|
|
|
|
}
|
|
|
|
|
|
|
|
async exposeFunction(name: string, playwrightFunction: Function) {
|
|
|
|
if (this._pageBindings.has(name))
|
|
|
|
throw new Error(`Failed to add page binding with name ${name}: window['${name}'] already exists!`);
|
|
|
|
this._pageBindings.set(name, playwrightFunction);
|
2019-12-09 13:08:21 -08:00
|
|
|
await this._delegate.exposeBinding(name, helper.evaluationString(addPageBinding, name));
|
2019-11-18 18:18:28 -08:00
|
|
|
|
|
|
|
function addPageBinding(bindingName: string) {
|
2019-12-21 09:03:52 -08:00
|
|
|
const binding = (window as any)[bindingName];
|
|
|
|
(window as any)[bindingName] = (...args: any[]) => {
|
|
|
|
const me = (window as any)[bindingName];
|
2019-11-18 18:18:28 -08:00
|
|
|
let callbacks = me['callbacks'];
|
|
|
|
if (!callbacks) {
|
|
|
|
callbacks = new Map();
|
|
|
|
me['callbacks'] = callbacks;
|
|
|
|
}
|
|
|
|
const seq = (me['lastSeq'] || 0) + 1;
|
|
|
|
me['lastSeq'] = seq;
|
|
|
|
const promise = new Promise((resolve, reject) => callbacks.set(seq, {resolve, reject}));
|
|
|
|
binding(JSON.stringify({name: bindingName, seq, args}));
|
|
|
|
return promise;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 13:08:21 -08:00
|
|
|
setExtraHTTPHeaders(headers: network.Headers) {
|
2019-12-17 11:31:52 -08:00
|
|
|
this._state.extraHTTPHeaders = {};
|
|
|
|
for (const key of Object.keys(headers)) {
|
|
|
|
const value = headers[key];
|
|
|
|
assert(helper.isString(value), `Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
|
|
|
|
this._state.extraHTTPHeaders[key.toLowerCase()] = value;
|
|
|
|
}
|
2019-12-09 13:08:21 -08:00
|
|
|
return this._delegate.setExtraHTTPHeaders(headers);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-06 13:36:47 -08:00
|
|
|
async _onBindingCalled(payload: string, context: js.ExecutionContext) {
|
|
|
|
const {name, seq, args} = JSON.parse(payload);
|
2019-11-18 18:18:28 -08:00
|
|
|
let expression = null;
|
|
|
|
try {
|
|
|
|
const result = await this._pageBindings.get(name)(...args);
|
|
|
|
expression = helper.evaluationString(deliverResult, name, seq, result);
|
|
|
|
} catch (error) {
|
|
|
|
if (error instanceof Error)
|
|
|
|
expression = helper.evaluationString(deliverError, name, seq, error.message, error.stack);
|
|
|
|
else
|
|
|
|
expression = helper.evaluationString(deliverErrorValue, name, seq, error);
|
|
|
|
}
|
2019-12-06 13:36:47 -08:00
|
|
|
context.evaluate(expression).catch(debugError);
|
2019-11-18 18:18:28 -08:00
|
|
|
|
|
|
|
function deliverResult(name: string, seq: number, result: any) {
|
2019-12-21 09:03:52 -08:00
|
|
|
(window as any)[name]['callbacks'].get(seq).resolve(result);
|
|
|
|
(window as any)[name]['callbacks'].delete(seq);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
function deliverError(name: string, seq: number, message: string, stack: string) {
|
|
|
|
const error = new Error(message);
|
|
|
|
error.stack = stack;
|
2019-12-21 09:03:52 -08:00
|
|
|
(window as any)[name]['callbacks'].get(seq).reject(error);
|
|
|
|
(window as any)[name]['callbacks'].delete(seq);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
function deliverErrorValue(name: string, seq: number, value: any) {
|
2019-12-21 09:03:52 -08:00
|
|
|
(window as any)[name]['callbacks'].get(seq).reject(value);
|
|
|
|
(window as any)[name]['callbacks'].delete(seq);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 15:17:42 -08:00
|
|
|
_addConsoleMessage(type: string, args: js.JSHandle[], location: ConsoleMessageLocation, text?: string) {
|
2019-11-18 18:18:28 -08:00
|
|
|
if (!this.listenerCount(Events.Page.Console)) {
|
|
|
|
args.forEach(arg => arg.dispose());
|
|
|
|
return;
|
|
|
|
}
|
2019-12-10 15:17:42 -08:00
|
|
|
this.emit(Events.Page.Console, new ConsoleMessage(type, text, args, location));
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
url(): string {
|
|
|
|
return this.mainFrame().url();
|
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async content(): Promise<string> {
|
2019-12-09 13:08:21 -08:00
|
|
|
return this.mainFrame().content();
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async setContent(html: string, options?: frames.NavigateOptions): Promise<void> {
|
2019-12-09 13:08:21 -08:00
|
|
|
return this.mainFrame().setContent(html, options);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async goto(url: string, options?: frames.GotoOptions): Promise<network.Response | null> {
|
2019-12-09 13:08:21 -08:00
|
|
|
return this.mainFrame().goto(url, options);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-09 14:53:17 -08:00
|
|
|
async reload(options?: frames.NavigateOptions): Promise<network.Response | null> {
|
2019-12-17 11:28:09 -08:00
|
|
|
const waitPromise = this.waitForNavigation(options);
|
|
|
|
await this._delegate.reload();
|
|
|
|
return waitPromise;
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-18 18:03:02 -08:00
|
|
|
async waitForNavigation(options?: frames.WaitForNavigationOptions): Promise<network.Response | null> {
|
2019-12-09 13:08:21 -08:00
|
|
|
return this.mainFrame().waitForNavigation(options);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:32:30 -08:00
|
|
|
async waitForLoadState(options?: frames.NavigateOptions): Promise<void> {
|
|
|
|
return this.mainFrame().waitForLoadState(options);
|
|
|
|
}
|
|
|
|
|
2019-12-20 20:28:35 -08:00
|
|
|
async waitForEvent(event: string, optionsOrPredicate: Function | (types.TimeoutOptions & { predicate?: Function }) = {}): Promise<any> {
|
|
|
|
if (typeof optionsOrPredicate === 'function')
|
|
|
|
optionsOrPredicate = { predicate: optionsOrPredicate };
|
|
|
|
const { timeout = this._timeoutSettings.timeout(), predicate = () => true } = optionsOrPredicate;
|
2019-12-17 14:00:39 -08:00
|
|
|
return helper.waitForEvent(this, event, (...args: any[]) => !!predicate(...args), timeout, this._disconnectedPromise);
|
|
|
|
}
|
|
|
|
|
2019-12-20 14:38:54 -08:00
|
|
|
async waitForRequest(urlOrPredicate: string | RegExp | ((r: network.Request) => boolean), options: types.TimeoutOptions = {}): Promise<network.Request> {
|
2019-12-17 14:00:39 -08:00
|
|
|
const { timeout = this._timeoutSettings.timeout() } = options;
|
2019-12-06 13:36:47 -08:00
|
|
|
return helper.waitForEvent(this, Events.Page.Request, (request: network.Request) => {
|
2019-12-20 14:38:54 -08:00
|
|
|
if (helper.isString(urlOrPredicate) || urlOrPredicate instanceof RegExp)
|
2020-01-07 11:55:24 -08:00
|
|
|
return platform.urlMatches(request.url(), urlOrPredicate);
|
2019-12-20 14:38:54 -08:00
|
|
|
return urlOrPredicate(request);
|
2019-12-06 13:36:47 -08:00
|
|
|
}, timeout, this._disconnectedPromise);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 14:38:54 -08:00
|
|
|
async waitForResponse(urlOrPredicate: string | RegExp | ((r: network.Response) => boolean), options: types.TimeoutOptions = {}): Promise<network.Response> {
|
2019-12-17 14:00:39 -08:00
|
|
|
const { timeout = this._timeoutSettings.timeout() } = options;
|
2019-12-06 13:36:47 -08:00
|
|
|
return helper.waitForEvent(this, Events.Page.Response, (response: network.Response) => {
|
2019-12-20 14:38:54 -08:00
|
|
|
if (helper.isString(urlOrPredicate) || urlOrPredicate instanceof RegExp)
|
2020-01-07 11:55:24 -08:00
|
|
|
return platform.urlMatches(response.url(), urlOrPredicate);
|
2019-12-20 14:38:54 -08:00
|
|
|
return urlOrPredicate(response);
|
2019-12-06 13:36:47 -08:00
|
|
|
}, timeout, this._disconnectedPromise);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-09 14:53:17 -08:00
|
|
|
async goBack(options?: frames.NavigateOptions): Promise<network.Response | null> {
|
2019-12-17 11:28:09 -08:00
|
|
|
const waitPromise = this.waitForNavigation(options);
|
|
|
|
const result = await this._delegate.goBack();
|
|
|
|
if (!result) {
|
|
|
|
waitPromise.catch(() => {});
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return waitPromise;
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-09 14:53:17 -08:00
|
|
|
async goForward(options?: frames.NavigateOptions): Promise<network.Response | null> {
|
2019-12-17 11:28:09 -08:00
|
|
|
const waitPromise = this.waitForNavigation(options);
|
|
|
|
const result = await this._delegate.goForward();
|
|
|
|
if (!result) {
|
|
|
|
waitPromise.catch(() => {});
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return waitPromise;
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2020-01-05 14:39:16 -08:00
|
|
|
async emulateMedia(options: { media?: types.MediaType, colorScheme?: types.ColorScheme }) {
|
|
|
|
assert(!options.media || types.mediaTypes.has(options.media), 'Unsupported media: ' + options.media);
|
2020-01-03 12:59:06 -08:00
|
|
|
assert(!options.colorScheme || types.colorSchemes.has(options.colorScheme), 'Unsupported color scheme: ' + options.colorScheme);
|
2020-01-05 14:39:16 -08:00
|
|
|
if (options.media !== undefined)
|
|
|
|
this._state.mediaType = options.media;
|
2019-12-09 13:08:21 -08:00
|
|
|
if (options.colorScheme !== undefined)
|
2019-12-18 12:23:33 -08:00
|
|
|
this._state.colorScheme = options.colorScheme;
|
|
|
|
await this._delegate.setEmulateMedia(this._state.mediaType, this._state.colorScheme);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-06 11:33:24 -08:00
|
|
|
async setViewport(viewport: types.Viewport) {
|
2019-12-09 13:08:21 -08:00
|
|
|
const oldIsMobile = this._state.viewport ? !!this._state.viewport.isMobile : false;
|
|
|
|
const newIsMobile = !!viewport.isMobile;
|
|
|
|
this._state.viewport = { ...viewport };
|
|
|
|
await this._delegate.setViewport(viewport);
|
2020-01-13 13:32:44 -08:00
|
|
|
if (oldIsMobile !== newIsMobile)
|
2019-11-18 18:18:28 -08:00
|
|
|
await this.reload();
|
|
|
|
}
|
|
|
|
|
2019-12-06 11:33:24 -08:00
|
|
|
viewport(): types.Viewport | null {
|
2019-12-09 13:08:21 -08:00
|
|
|
return this._state.viewport;
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
evaluate: types.Evaluate = async (pageFunction, ...args) => {
|
2019-12-06 13:36:47 -08:00
|
|
|
return this.mainFrame().evaluate(pageFunction, ...args as any);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
async evaluateOnNewDocument(pageFunction: Function | string, ...args: any[]) {
|
|
|
|
const source = helper.evaluationString(pageFunction, ...args);
|
2019-12-09 13:08:21 -08:00
|
|
|
await this._delegate.evaluateOnNewDocument(source);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
async setCacheEnabled(enabled: boolean = true) {
|
2019-12-09 13:08:21 -08:00
|
|
|
if (this._state.cacheEnabled === enabled)
|
|
|
|
return;
|
|
|
|
this._state.cacheEnabled = enabled;
|
|
|
|
await this._delegate.setCacheEnabled(enabled);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-30 14:05:28 -08:00
|
|
|
async setRequestInterception(enabled: boolean) {
|
|
|
|
if (this._state.interceptNetwork === enabled)
|
|
|
|
return;
|
|
|
|
this._state.interceptNetwork = enabled;
|
|
|
|
await this._delegate.setRequestInterception(enabled);
|
|
|
|
}
|
|
|
|
|
2019-12-30 14:09:54 -08:00
|
|
|
async setOfflineMode(enabled: boolean) {
|
|
|
|
if (this._state.offlineMode === enabled)
|
|
|
|
return;
|
|
|
|
this._state.offlineMode = enabled;
|
|
|
|
await this._delegate.setOfflineMode(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
async authenticate(credentials: types.Credentials | null) {
|
|
|
|
this._state.credentials = credentials;
|
|
|
|
await this._delegate.authenticate(credentials);
|
|
|
|
}
|
|
|
|
|
2020-01-07 11:55:24 -08:00
|
|
|
async screenshot(options?: types.ScreenshotOptions): Promise<platform.BufferType> {
|
2019-12-06 11:33:24 -08:00
|
|
|
return this._screenshotter.screenshotPage(options);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async title(): Promise<string> {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().title();
|
|
|
|
}
|
|
|
|
|
|
|
|
async close(options: { runBeforeUnload: (boolean | undefined); } = {runBeforeUnload: undefined}) {
|
2019-12-18 16:23:05 -08:00
|
|
|
if (this._closed)
|
|
|
|
return;
|
2019-12-06 13:36:47 -08:00
|
|
|
assert(!this._disconnected, 'Protocol error: Connection closed. Most likely the page has been closed.');
|
2019-11-18 18:18:28 -08:00
|
|
|
const runBeforeUnload = !!options.runBeforeUnload;
|
2019-12-09 13:08:21 -08:00
|
|
|
await this._delegate.closePage(runBeforeUnload);
|
|
|
|
if (!runBeforeUnload)
|
2019-12-05 14:11:48 -08:00
|
|
|
await this._closedPromise;
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
isClosed(): boolean {
|
|
|
|
return this._closed;
|
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async click(selector: string, options?: frames.WaitForOptions & input.ClickOptions) {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().click(selector, options);
|
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async dblclick(selector: string, options?: frames.WaitForOptions & input.MultiClickOptions) {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().dblclick(selector, options);
|
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async tripleclick(selector: string, options?: frames.WaitForOptions & input.MultiClickOptions) {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().tripleclick(selector, options);
|
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async fill(selector: string, value: string, options?: frames.WaitForOptions) {
|
2019-12-14 19:13:22 -08:00
|
|
|
return this.mainFrame().fill(selector, value, options);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async focus(selector: string, options?: frames.WaitForOptions) {
|
2019-12-14 19:13:22 -08:00
|
|
|
return this.mainFrame().focus(selector, options);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async hover(selector: string, options?: frames.WaitForOptions & input.PointerActionOptions) {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().hover(selector, options);
|
|
|
|
}
|
|
|
|
|
2020-01-03 12:59:06 -08:00
|
|
|
async select(selector: string, value: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | undefined, options?: frames.WaitForOptions): Promise<string[]> {
|
2019-12-14 19:13:22 -08:00
|
|
|
return this.mainFrame().select(selector, value, options);
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:31:20 -08:00
|
|
|
async type(selector: string, text: string, options?: frames.WaitForOptions & { delay?: number }) {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().type(selector, text, options);
|
|
|
|
}
|
|
|
|
|
2019-12-20 16:57:21 -08:00
|
|
|
async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & { visibility?: types.Visibility }, ...args: any[]): Promise<js.JSHandle> {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
|
|
|
|
}
|
|
|
|
|
2019-12-18 18:11:02 -08:00
|
|
|
async waitForFunction(pageFunction: Function | string, options?: types.WaitForFunctionOptions, ...args: any[]): Promise<js.JSHandle> {
|
2019-11-18 18:18:28 -08:00
|
|
|
return this.mainFrame().waitForFunction(pageFunction, options, ...args);
|
|
|
|
}
|
2019-12-18 18:11:02 -08:00
|
|
|
|
|
|
|
async $wait(selector: string, pageFunction: Function | string, options?: types.WaitForFunctionOptions, ...args: any[]): Promise<js.JSHandle> {
|
|
|
|
return this.mainFrame().$wait(selector, pageFunction, options, ...args);
|
|
|
|
}
|
2020-01-07 12:59:01 -08:00
|
|
|
|
|
|
|
workers(): Worker[] {
|
|
|
|
return [...this._workers.values()];
|
|
|
|
}
|
|
|
|
|
|
|
|
_addWorker(workerId: string, worker: Worker) {
|
|
|
|
this._workers.set(workerId, worker);
|
|
|
|
this.emit(Events.Page.WorkerCreated, worker);
|
|
|
|
}
|
|
|
|
|
|
|
|
_removeWorker(workerId: string) {
|
|
|
|
const worker = this._workers.get(workerId);
|
|
|
|
if (!worker)
|
|
|
|
return;
|
|
|
|
this.emit(Events.Page.WorkerDestroyed, worker);
|
|
|
|
this._workers.delete(workerId);
|
|
|
|
}
|
|
|
|
|
|
|
|
_clearWorkers() {
|
|
|
|
this._workers.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Worker {
|
|
|
|
private _url: string;
|
|
|
|
private _executionContextPromise: Promise<js.ExecutionContext>;
|
|
|
|
private _executionContextCallback: (value?: js.ExecutionContext) => void;
|
|
|
|
_existingExecutionContext: js.ExecutionContext | null;
|
|
|
|
|
|
|
|
constructor(url: string) {
|
|
|
|
this._url = url;
|
|
|
|
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);
|
2020-01-13 09:14:28 -08:00
|
|
|
return helper.logPublicApiCalls('worker', this);
|
2020-01-07 12:59:01 -08:00
|
|
|
}
|
2020-01-13 09:14:28 -08:00
|
|
|
|
2020-01-07 12:59:01 -08:00
|
|
|
_createExecutionContext(delegate: js.ExecutionContextDelegate) {
|
|
|
|
this._existingExecutionContext = new js.ExecutionContext(delegate);
|
|
|
|
this._executionContextCallback(this._existingExecutionContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
url(): string {
|
|
|
|
return this._url;
|
|
|
|
}
|
|
|
|
|
|
|
|
evaluate: types.Evaluate = async (pageFunction, ...args) => {
|
|
|
|
return (await this._executionContextPromise).evaluate(pageFunction, ...args as any);
|
|
|
|
}
|
|
|
|
|
|
|
|
evaluateHandle: types.EvaluateHandle = async (pageFunction, ...args) => {
|
|
|
|
return (await this._executionContextPromise).evaluateHandle(pageFunction, ...args as any);
|
|
|
|
}
|
2019-11-18 18:18:28 -08:00
|
|
|
}
|
2020-01-07 13:57:37 -08:00
|
|
|
|
|
|
|
export class Coverage {
|
|
|
|
startJSCoverage(options: {
|
|
|
|
resetOnNavigation?: boolean;
|
|
|
|
reportAnonymousScripts?: boolean;
|
|
|
|
}): Promise<void> {
|
|
|
|
throw new Error('not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
stopJSCoverage(): Promise<types.CoverageEntry[]> {
|
|
|
|
throw new Error('not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
startCSSCoverage(options: { resetOnNavigation?: boolean; } = {}): Promise<void> {
|
|
|
|
throw new Error('not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
stopCSSCoverage(): Promise<types.CoverageEntry[]> {
|
|
|
|
throw new Error('not implemented');
|
|
|
|
}
|
|
|
|
}
|