mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: prepare library types for rpc (#2706)
This commit is contained in:
parent
1865c5685a
commit
bc3050776e
@ -14,7 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BrowserContext, BrowserContextOptions, BrowserContextBase, PersistentContextOptions } from './browserContext';
|
import * as types from './types';
|
||||||
|
import { BrowserContext, BrowserContextBase } from './browserContext';
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { Download } from './download';
|
import { Download } from './download';
|
||||||
@ -22,17 +23,20 @@ import type { BrowserServer } from './server/browserServer';
|
|||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { Loggers } from './logger';
|
import { Loggers } from './logger';
|
||||||
import { ProxySettings } from './types';
|
import { ProxySettings } from './types';
|
||||||
|
import { LoggerSink } from './loggerSink';
|
||||||
|
|
||||||
export type BrowserOptions = {
|
export type BrowserOptions = {
|
||||||
loggers: Loggers,
|
loggers: Loggers,
|
||||||
downloadsPath?: string,
|
downloadsPath?: string,
|
||||||
headful?: boolean,
|
headful?: boolean,
|
||||||
persistent?: PersistentContextOptions, // Undefined means no persistent context.
|
persistent?: types.BrowserContextOptions, // Undefined means no persistent context.
|
||||||
slowMo?: number,
|
slowMo?: number,
|
||||||
ownedServer?: BrowserServer,
|
ownedServer?: BrowserServer,
|
||||||
proxy?: ProxySettings,
|
proxy?: ProxySettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BrowserContextOptions = types.BrowserContextOptions & { logger?: LoggerSink };
|
||||||
|
|
||||||
export interface Browser extends EventEmitter {
|
export interface Browser extends EventEmitter {
|
||||||
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
|
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
|
||||||
contexts(): BrowserContext[];
|
contexts(): BrowserContext[];
|
||||||
|
|||||||
@ -31,43 +31,18 @@ import { ProgressController } from './progress';
|
|||||||
import { DebugController } from './debug/debugController';
|
import { DebugController } from './debug/debugController';
|
||||||
import { LoggerSink } from './loggerSink';
|
import { LoggerSink } from './loggerSink';
|
||||||
|
|
||||||
type CommonContextOptions = {
|
|
||||||
viewport?: types.Size | null,
|
|
||||||
ignoreHTTPSErrors?: boolean,
|
|
||||||
javaScriptEnabled?: boolean,
|
|
||||||
bypassCSP?: boolean,
|
|
||||||
userAgent?: string,
|
|
||||||
locale?: string,
|
|
||||||
timezoneId?: string,
|
|
||||||
geolocation?: types.Geolocation,
|
|
||||||
permissions?: string[],
|
|
||||||
extraHTTPHeaders?: network.Headers,
|
|
||||||
offline?: boolean,
|
|
||||||
httpCredentials?: types.Credentials,
|
|
||||||
deviceScaleFactor?: number,
|
|
||||||
isMobile?: boolean,
|
|
||||||
hasTouch?: boolean,
|
|
||||||
colorScheme?: types.ColorScheme,
|
|
||||||
acceptDownloads?: boolean,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PersistentContextOptions = CommonContextOptions;
|
|
||||||
export type BrowserContextOptions = CommonContextOptions & {
|
|
||||||
logger?: LoggerSink,
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface BrowserContext {
|
export interface BrowserContext {
|
||||||
setDefaultNavigationTimeout(timeout: number): void;
|
setDefaultNavigationTimeout(timeout: number): void;
|
||||||
setDefaultTimeout(timeout: number): void;
|
setDefaultTimeout(timeout: number): void;
|
||||||
pages(): Page[];
|
pages(): Page[];
|
||||||
newPage(): Promise<Page>;
|
newPage(): Promise<Page>;
|
||||||
cookies(urls?: string | string[]): Promise<network.NetworkCookie[]>;
|
cookies(urls?: string | string[]): Promise<types.NetworkCookie[]>;
|
||||||
addCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
|
addCookies(cookies: types.SetNetworkCookieParam[]): Promise<void>;
|
||||||
clearCookies(): Promise<void>;
|
clearCookies(): Promise<void>;
|
||||||
grantPermissions(permissions: string[], options?: { origin?: string }): Promise<void>;
|
grantPermissions(permissions: string[], options?: { origin?: string }): Promise<void>;
|
||||||
clearPermissions(): Promise<void>;
|
clearPermissions(): Promise<void>;
|
||||||
setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
|
setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
|
||||||
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
|
setExtraHTTPHeaders(headers: types.Headers): Promise<void>;
|
||||||
setOffline(offline: boolean): Promise<void>;
|
setOffline(offline: boolean): Promise<void>;
|
||||||
setHTTPCredentials(httpCredentials: types.Credentials | null): Promise<void>;
|
setHTTPCredentials(httpCredentials: types.Credentials | null): Promise<void>;
|
||||||
addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void>;
|
addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void>;
|
||||||
@ -79,6 +54,8 @@ export interface BrowserContext {
|
|||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BrowserContextOptions = types.BrowserContextOptions & { logger?: LoggerSink };
|
||||||
|
|
||||||
export abstract class BrowserContextBase extends EventEmitter implements BrowserContext {
|
export abstract class BrowserContextBase extends EventEmitter implements BrowserContext {
|
||||||
readonly _timeoutSettings = new TimeoutSettings();
|
readonly _timeoutSettings = new TimeoutSettings();
|
||||||
readonly _pageBindings = new Map<string, PageBinding>();
|
readonly _pageBindings = new Map<string, PageBinding>();
|
||||||
@ -141,21 +118,27 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
|
|||||||
// BrowserContext methods.
|
// BrowserContext methods.
|
||||||
abstract pages(): Page[];
|
abstract pages(): Page[];
|
||||||
abstract newPage(): Promise<Page>;
|
abstract newPage(): Promise<Page>;
|
||||||
abstract cookies(...urls: string[]): Promise<network.NetworkCookie[]>;
|
abstract _doCookies(urls: string[]): Promise<types.NetworkCookie[]>;
|
||||||
abstract addCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
|
abstract addCookies(cookies: types.SetNetworkCookieParam[]): Promise<void>;
|
||||||
abstract clearCookies(): Promise<void>;
|
abstract clearCookies(): Promise<void>;
|
||||||
abstract _doGrantPermissions(origin: string, permissions: string[]): Promise<void>;
|
abstract _doGrantPermissions(origin: string, permissions: string[]): Promise<void>;
|
||||||
abstract _doClearPermissions(): Promise<void>;
|
abstract _doClearPermissions(): Promise<void>;
|
||||||
abstract setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
|
abstract setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
|
||||||
abstract setHTTPCredentials(httpCredentials: types.Credentials | null): Promise<void>;
|
abstract setHTTPCredentials(httpCredentials: types.Credentials | null): Promise<void>;
|
||||||
abstract setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
|
abstract setExtraHTTPHeaders(headers: types.Headers): Promise<void>;
|
||||||
abstract setOffline(offline: boolean): Promise<void>;
|
abstract setOffline(offline: boolean): Promise<void>;
|
||||||
abstract addInitScript(script: string | Function | { path?: string | undefined; content?: string | undefined; }, arg?: any): Promise<void>;
|
abstract _doAddInitScript(expression: string): Promise<void>;
|
||||||
abstract _doExposeBinding(binding: PageBinding): Promise<void>;
|
abstract _doExposeBinding(binding: PageBinding): Promise<void>;
|
||||||
abstract route(url: types.URLMatch, handler: network.RouteHandler): Promise<void>;
|
abstract route(url: types.URLMatch, handler: network.RouteHandler): Promise<void>;
|
||||||
abstract unroute(url: types.URLMatch, handler?: network.RouteHandler): Promise<void>;
|
abstract unroute(url: types.URLMatch, handler?: network.RouteHandler): Promise<void>;
|
||||||
abstract close(): Promise<void>;
|
abstract close(): Promise<void>;
|
||||||
|
|
||||||
|
async cookies(urls: string | string[] | undefined = []): Promise<types.NetworkCookie[]> {
|
||||||
|
if (urls && !Array.isArray(urls))
|
||||||
|
urls = [ urls ];
|
||||||
|
return await this._doCookies(urls as string[]);
|
||||||
|
}
|
||||||
|
|
||||||
async exposeFunction(name: string, playwrightFunction: Function): Promise<void> {
|
async exposeFunction(name: string, playwrightFunction: Function): Promise<void> {
|
||||||
await this.exposeBinding(name, (options, ...args: any) => playwrightFunction(...args));
|
await this.exposeBinding(name, (options, ...args: any) => playwrightFunction(...args));
|
||||||
}
|
}
|
||||||
@ -172,6 +155,11 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
|
|||||||
this._doExposeBinding(binding);
|
this._doExposeBinding(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addInitScript(script: string | Function | { path?: string | undefined; content?: string | undefined; }, arg?: any): Promise<void> {
|
||||||
|
const source = await helper.evaluationScript(script, arg);
|
||||||
|
await this._doAddInitScript(source);
|
||||||
|
}
|
||||||
|
|
||||||
async grantPermissions(permissions: string[], options?: { origin?: string }) {
|
async grantPermissions(permissions: string[], options?: { origin?: string }) {
|
||||||
let origin = '*';
|
let origin = '*';
|
||||||
if (options && options.origin) {
|
if (options && options.origin) {
|
||||||
|
|||||||
@ -15,10 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BrowserBase, BrowserOptions } from '../browser';
|
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
|
||||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, BrowserContextOptions, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||||
import { Events as CommonEvents } from '../events';
|
import { Events as CommonEvents } from '../events';
|
||||||
import { assert, helper } from '../helper';
|
import { assert } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { Page, PageBinding, Worker } from '../page';
|
import { Page, PageBinding, Worker } from '../page';
|
||||||
import { ConnectionTransport, SlowMoTransport } from '../transport';
|
import { ConnectionTransport, SlowMoTransport } from '../transport';
|
||||||
@ -280,7 +280,7 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||||||
readonly _browserContextId: string | null;
|
readonly _browserContextId: string | null;
|
||||||
readonly _evaluateOnNewDocumentSources: string[];
|
readonly _evaluateOnNewDocumentSources: string[];
|
||||||
|
|
||||||
constructor(browser: CRBrowser, browserContextId: string | null, options: BrowserContextOptions) {
|
constructor(browser: CRBrowser, browserContextId: string | null, options: types.BrowserContextOptions) {
|
||||||
super(browser, options);
|
super(browser, options);
|
||||||
this._browser = browser;
|
this._browser = browser;
|
||||||
this._browserContextId = browserContextId;
|
this._browserContextId = browserContextId;
|
||||||
@ -325,18 +325,18 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||||||
throw result;
|
throw result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async cookies(urls?: string | string[]): Promise<network.NetworkCookie[]> {
|
async _doCookies(urls: string[]): Promise<types.NetworkCookie[]> {
|
||||||
const { cookies } = await this._browser._session.send('Storage.getCookies', { browserContextId: this._browserContextId || undefined });
|
const { cookies } = await this._browser._session.send('Storage.getCookies', { browserContextId: this._browserContextId || undefined });
|
||||||
return network.filterCookies(cookies.map(c => {
|
return network.filterCookies(cookies.map(c => {
|
||||||
const copy: any = { sameSite: 'None', ...c };
|
const copy: any = { sameSite: 'None', ...c };
|
||||||
delete copy.size;
|
delete copy.size;
|
||||||
delete copy.priority;
|
delete copy.priority;
|
||||||
delete copy.session;
|
delete copy.session;
|
||||||
return copy as network.NetworkCookie;
|
return copy as types.NetworkCookie;
|
||||||
}), urls);
|
}), urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addCookies(cookies: network.SetNetworkCookieParam[]) {
|
async addCookies(cookies: types.SetNetworkCookieParam[]) {
|
||||||
await this._browser._session.send('Storage.setCookies', { cookies: network.rewriteCookies(cookies), browserContextId: this._browserContextId || undefined });
|
await this._browser._session.send('Storage.setCookies', { cookies: network.rewriteCookies(cookies), browserContextId: this._browserContextId || undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +384,7 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||||||
await (page._delegate as CRPage).updateGeolocation();
|
await (page._delegate as CRPage).updateGeolocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
async setExtraHTTPHeaders(headers: network.Headers): Promise<void> {
|
async setExtraHTTPHeaders(headers: types.Headers): Promise<void> {
|
||||||
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
|
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
|
||||||
for (const page of this.pages())
|
for (const page of this.pages())
|
||||||
await (page._delegate as CRPage).updateExtraHTTPHeaders();
|
await (page._delegate as CRPage).updateExtraHTTPHeaders();
|
||||||
@ -402,8 +402,7 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||||||
await (page._delegate as CRPage).updateHttpCredentials();
|
await (page._delegate as CRPage).updateHttpCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
async _doAddInitScript(source: string) {
|
||||||
const source = await helper.evaluationScript(script, arg);
|
|
||||||
this._evaluateOnNewDocumentSources.push(source);
|
this._evaluateOnNewDocumentSources.push(source);
|
||||||
for (const page of this.pages())
|
for (const page of this.pages())
|
||||||
await (page._delegate as CRPage).evaluateOnNewDocument(source);
|
await (page._delegate as CRPage).evaluateOnNewDocument(source);
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import { assert, helper, RegisteredListener } from '../helper';
|
|||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { Credentials } from '../types';
|
import * as types from '../types';
|
||||||
import { CRPage } from './crPage';
|
import { CRPage } from './crPage';
|
||||||
|
|
||||||
export class CRNetworkManager {
|
export class CRNetworkManager {
|
||||||
@ -63,7 +63,7 @@ export class CRNetworkManager {
|
|||||||
helper.removeEventListeners(this._eventListeners);
|
helper.removeEventListeners(this._eventListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticate(credentials: Credentials | null) {
|
async authenticate(credentials: types.Credentials | null) {
|
||||||
this._credentials = credentials;
|
this._credentials = credentials;
|
||||||
await this._updateProtocolRequestInterception();
|
await this._updateProtocolRequestInterception();
|
||||||
}
|
}
|
||||||
@ -350,7 +350,7 @@ class InterceptableRequest implements network.RouteDelegate {
|
|||||||
this.request = new network.Request(allowInterception ? this : null, frame, redirectedFrom, documentId, url, type, method, postData, headersObject(headers));
|
this.request = new network.Request(allowInterception ? this : null, frame, redirectedFrom, documentId, url, type, method, postData, headersObject(headers));
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string } = {}) {
|
async continue(overrides: { method?: string; headers?: types.Headers; postData?: string } = {}) {
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
// or the page was closed. We should tolerate these errors.
|
// or the page was closed. We should tolerate these errors.
|
||||||
await this._client._sendMayFail('Fetch.continueRequest', {
|
await this._client._sendMayFail('Fetch.continueRequest', {
|
||||||
@ -361,7 +361,7 @@ class InterceptableRequest implements network.RouteDelegate {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: network.FulfillResponse) {
|
async fulfill(response: types.FulfillResponse) {
|
||||||
const responseBody = response.body && helper.isString(response.body) ? Buffer.from(response.body) : (response.body || null);
|
const responseBody = response.body && helper.isString(response.body) ? Buffer.from(response.body) : (response.body || null);
|
||||||
|
|
||||||
const responseHeaders: { [s: string]: string; } = {};
|
const responseHeaders: { [s: string]: string; } = {};
|
||||||
@ -423,8 +423,8 @@ function headersArray(headers: { [s: string]: string; }): { name: string; value:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function headersObject(headers: Protocol.Network.Headers): network.Headers {
|
function headersObject(headers: Protocol.Network.Headers): types.Headers {
|
||||||
const result: network.Headers = {};
|
const result: types.Headers = {};
|
||||||
for (const key of Object.keys(headers))
|
for (const key of Object.keys(headers))
|
||||||
result[key.toLowerCase()] = headers[key];
|
result[key.toLowerCase()] = headers[key];
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -20,10 +20,10 @@
|
|||||||
|
|
||||||
import * as program from 'commander';
|
import * as program from 'commander';
|
||||||
import { Playwright } from '../server/playwright';
|
import { Playwright } from '../server/playwright';
|
||||||
import { BrowserType, LaunchOptions } from '../server/browserType';
|
import { BrowserType } from '../server/browserType';
|
||||||
import { DeviceDescriptors } from '../deviceDescriptors';
|
import { DeviceDescriptors } from '../deviceDescriptors';
|
||||||
import { BrowserContextOptions } from '../browserContext';
|
|
||||||
import { helper } from '../helper';
|
import { helper } from '../helper';
|
||||||
|
import { LaunchOptions, BrowserContextOptions } from '../types';
|
||||||
|
|
||||||
const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']);
|
const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']);
|
||||||
|
|
||||||
|
|||||||
28
src/dom.ts
28
src/dom.ts
@ -19,7 +19,7 @@ import * as mime from 'mime';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import * as frames from './frames';
|
import * as frames from './frames';
|
||||||
import { assert, helper } from './helper';
|
import { assert, helper, assertMaxArguments } from './helper';
|
||||||
import InjectedScript from './injected/injectedScript';
|
import InjectedScript from './injected/injectedScript';
|
||||||
import * as injectedScriptSource from './generated/injectedScriptSource';
|
import * as injectedScriptSource from './generated/injectedScriptSource';
|
||||||
import * as debugScriptSource from './generated/debugScriptSource';
|
import * as debugScriptSource from './generated/debugScriptSource';
|
||||||
@ -55,6 +55,12 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async evaluateExpressionInternal(expression: string, isFunction: boolean, ...args: any[]): Promise<any> {
|
||||||
|
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
|
||||||
|
return js.evaluateExpression(this, true /* returnByValue */, expression, isFunction, ...args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async evaluateHandleInternal<R>(pageFunction: js.Func0<R>): Promise<js.SmartHandle<R>>;
|
async evaluateHandleInternal<R>(pageFunction: js.Func0<R>): Promise<js.SmartHandle<R>>;
|
||||||
async evaluateHandleInternal<Arg, R>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>>;
|
async evaluateHandleInternal<Arg, R>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>>;
|
||||||
async evaluateHandleInternal(pageFunction: never, ...args: never[]): Promise<any> {
|
async evaluateHandleInternal(pageFunction: never, ...args: never[]): Promise<any> {
|
||||||
@ -63,6 +69,12 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async evaluateExpressionHandleInternal(expression: string, isFunction: boolean, ...args: any[]): Promise<any> {
|
||||||
|
return await this.frame._page._frameManager.waitForSignalsCreatedBy(null, false /* noWaitFor */, async () => {
|
||||||
|
return js.evaluateExpression(this, false /* returnByValue */, expression, isFunction, ...args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
createHandle(remoteObject: js.RemoteObject): js.JSHandle {
|
createHandle(remoteObject: js.RemoteObject): js.JSHandle {
|
||||||
if (this.frame._page._delegate.isElementHandle(remoteObject))
|
if (this.frame._page._delegate.isElementHandle(remoteObject))
|
||||||
return new ElementHandle(this, remoteObject.objectId!);
|
return new ElementHandle(this, remoteObject.objectId!);
|
||||||
@ -618,10 +630,15 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||||||
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
|
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
|
||||||
async $eval<R>(selector: string, pageFunction: js.FuncOn<Element, void, R>, arg?: any): Promise<R>;
|
async $eval<R>(selector: string, pageFunction: js.FuncOn<Element, void, R>, arg?: any): Promise<R>;
|
||||||
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
|
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
|
||||||
|
assertMaxArguments(arguments.length, 3);
|
||||||
|
return this._$evalExpression(selector, String(pageFunction), typeof pageFunction === 'function', arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||||
const handle = await selectors._query(this._context.frame, selector, this);
|
const handle = await selectors._query(this._context.frame, selector, this);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||||
const result = await handle.evaluate(pageFunction, arg);
|
const result = await handle._evaluateExpression(expression, isFunction, true, arg);
|
||||||
handle.dispose();
|
handle.dispose();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -629,8 +646,13 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||||||
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R>;
|
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R>;
|
||||||
async $$eval<R>(selector: string, pageFunction: js.FuncOn<Element[], void, R>, arg?: any): Promise<R>;
|
async $$eval<R>(selector: string, pageFunction: js.FuncOn<Element[], void, R>, arg?: any): Promise<R>;
|
||||||
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
|
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
|
||||||
|
assertMaxArguments(arguments.length, 3);
|
||||||
|
return this._$$evalExpression(selector, String(pageFunction), typeof pageFunction === 'function', arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _$$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||||
const arrayHandle = await selectors._queryArray(this._context.frame, selector, this);
|
const arrayHandle = await selectors._queryArray(this._context.frame, selector, this);
|
||||||
const result = await arrayHandle.evaluate(pageFunction, arg);
|
const result = await arrayHandle._evaluateExpression(expression, isFunction, true, arg);
|
||||||
arrayHandle.dispose();
|
arrayHandle.dispose();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,8 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BrowserBase, BrowserOptions } from '../browser';
|
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
|
||||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, BrowserContextOptions, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { assert, helper, RegisteredListener } from '../helper';
|
import { assert, helper, RegisteredListener } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
@ -146,7 +146,7 @@ export class FFBrowserContext extends BrowserContextBase {
|
|||||||
readonly _browser: FFBrowser;
|
readonly _browser: FFBrowser;
|
||||||
readonly _browserContextId: string | null;
|
readonly _browserContextId: string | null;
|
||||||
|
|
||||||
constructor(browser: FFBrowser, browserContextId: string | null, options: BrowserContextOptions) {
|
constructor(browser: FFBrowser, browserContextId: string | null, options: types.BrowserContextOptions) {
|
||||||
super(browser, options);
|
super(browser, options);
|
||||||
this._browser = browser;
|
this._browser = browser;
|
||||||
this._browserContextId = browserContextId;
|
this._browserContextId = browserContextId;
|
||||||
@ -237,17 +237,17 @@ export class FFBrowserContext extends BrowserContextBase {
|
|||||||
throw pageOrError;
|
throw pageOrError;
|
||||||
}
|
}
|
||||||
|
|
||||||
async cookies(urls?: string | string[]): Promise<network.NetworkCookie[]> {
|
async _doCookies(urls: string[]): Promise<types.NetworkCookie[]> {
|
||||||
const { cookies } = await this._browser._connection.send('Browser.getCookies', { browserContextId: this._browserContextId || undefined });
|
const { cookies } = await this._browser._connection.send('Browser.getCookies', { browserContextId: this._browserContextId || undefined });
|
||||||
return network.filterCookies(cookies.map(c => {
|
return network.filterCookies(cookies.map(c => {
|
||||||
const copy: any = { ... c };
|
const copy: any = { ... c };
|
||||||
delete copy.size;
|
delete copy.size;
|
||||||
delete copy.session;
|
delete copy.session;
|
||||||
return copy as network.NetworkCookie;
|
return copy as types.NetworkCookie;
|
||||||
}), urls);
|
}), urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addCookies(cookies: network.SetNetworkCookieParam[]) {
|
async addCookies(cookies: types.SetNetworkCookieParam[]) {
|
||||||
await this._browser._connection.send('Browser.setCookies', { browserContextId: this._browserContextId || undefined, cookies: network.rewriteCookies(cookies) });
|
await this._browser._connection.send('Browser.setCookies', { browserContextId: this._browserContextId || undefined, cookies: network.rewriteCookies(cookies) });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +282,7 @@ export class FFBrowserContext extends BrowserContextBase {
|
|||||||
await this._browser._connection.send('Browser.setGeolocationOverride', { browserContextId: this._browserContextId || undefined, geolocation });
|
await this._browser._connection.send('Browser.setGeolocationOverride', { browserContextId: this._browserContextId || undefined, geolocation });
|
||||||
}
|
}
|
||||||
|
|
||||||
async setExtraHTTPHeaders(headers: network.Headers): Promise<void> {
|
async setExtraHTTPHeaders(headers: types.Headers): Promise<void> {
|
||||||
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
|
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
|
||||||
const allHeaders = { ...this._options.extraHTTPHeaders };
|
const allHeaders = { ...this._options.extraHTTPHeaders };
|
||||||
if (this._options.locale)
|
if (this._options.locale)
|
||||||
@ -300,8 +300,7 @@ export class FFBrowserContext extends BrowserContextBase {
|
|||||||
await this._browser._connection.send('Browser.setHTTPCredentials', { browserContextId: this._browserContextId || undefined, credentials: httpCredentials });
|
await this._browser._connection.send('Browser.setHTTPCredentials', { browserContextId: this._browserContextId || undefined, credentials: httpCredentials });
|
||||||
}
|
}
|
||||||
|
|
||||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
async _doAddInitScript(source: string) {
|
||||||
const source = await helper.evaluationScript(script, arg);
|
|
||||||
await this._browser._connection.send('Browser.addScriptToEvaluateOnNewDocument', { browserContextId: this._browserContextId || undefined, script: source });
|
await this._browser._connection.send('Browser.addScriptToEvaluateOnNewDocument', { browserContextId: this._browserContextId || undefined, script: source });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import { FFSession } from './ffConnection';
|
|||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
|
import * as types from '../types';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
|
|
||||||
export class FFNetworkManager {
|
export class FFNetworkManager {
|
||||||
@ -74,7 +75,7 @@ export class FFNetworkManager {
|
|||||||
throw new Error(`Response body for ${request.request.method()} ${request.request.url()} was evicted!`);
|
throw new Error(`Response body for ${request.request.method()} ${request.request.url()} was evicted!`);
|
||||||
return Buffer.from(response.base64body, 'base64');
|
return Buffer.from(response.base64body, 'base64');
|
||||||
};
|
};
|
||||||
const headers: network.Headers = {};
|
const headers: types.Headers = {};
|
||||||
for (const {name, value} of event.headers)
|
for (const {name, value} of event.headers)
|
||||||
headers[name.toLowerCase()] = value;
|
headers[name.toLowerCase()] = value;
|
||||||
const response = new network.Response(request.request, event.status, event.statusText, headers, getResponseBody);
|
const response = new network.Response(request.request, event.status, event.statusText, headers, getResponseBody);
|
||||||
@ -149,7 +150,7 @@ class InterceptableRequest implements network.RouteDelegate {
|
|||||||
this._id = payload.requestId;
|
this._id = payload.requestId;
|
||||||
this._session = session;
|
this._session = session;
|
||||||
|
|
||||||
const headers: network.Headers = {};
|
const headers: types.Headers = {};
|
||||||
for (const {name, value} of payload.headers)
|
for (const {name, value} of payload.headers)
|
||||||
headers[name.toLowerCase()] = value;
|
headers[name.toLowerCase()] = value;
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ class InterceptableRequest implements network.RouteDelegate {
|
|||||||
payload.url, internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || 'other', payload.method, payload.postData || null, headers);
|
payload.url, internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || 'other', payload.method, payload.postData || null, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string }) {
|
async continue(overrides: { method?: string; headers?: types.Headers; postData?: string }) {
|
||||||
const {
|
const {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
@ -171,7 +172,7 @@ class InterceptableRequest implements network.RouteDelegate {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: network.FulfillResponse) {
|
async fulfill(response: types.FulfillResponse) {
|
||||||
const responseBody = response.body && helper.isString(response.body) ? Buffer.from(response.body) : (response.body || null);
|
const responseBody = response.body && helper.isString(response.body) ? Buffer.from(response.body) : (response.body || null);
|
||||||
|
|
||||||
const responseHeaders: { [s: string]: string; } = {};
|
const responseHeaders: { [s: string]: string; } = {};
|
||||||
@ -201,7 +202,7 @@ class InterceptableRequest implements network.RouteDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function headersArray(headers: network.Headers): Protocol.Network.HTTPHeader[] {
|
export function headersArray(headers: types.Headers): Protocol.Network.HTTPHeader[] {
|
||||||
const result: Protocol.Network.HTTPHeader[] = [];
|
const result: Protocol.Network.HTTPHeader[] = [];
|
||||||
for (const name in headers) {
|
for (const name in headers) {
|
||||||
if (!Object.is(headers[name], undefined))
|
if (!Object.is(headers[name], undefined))
|
||||||
|
|||||||
@ -36,9 +36,6 @@ type ContextData = {
|
|||||||
rerunnableTasks: Set<RerunnableTask<any>>;
|
rerunnableTasks: Set<RerunnableTask<any>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GotoOptions = types.NavigateOptions & {
|
|
||||||
referer?: string,
|
|
||||||
};
|
|
||||||
export type GotoResult = {
|
export type GotoResult = {
|
||||||
newDocumentId?: string,
|
newDocumentId?: string,
|
||||||
};
|
};
|
||||||
@ -347,7 +344,7 @@ export class Frame {
|
|||||||
return `${subject}.${method}`;
|
return `${subject}.${method}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async goto(url: string, options: GotoOptions = {}): Promise<network.Response | null> {
|
async goto(url: string, options: types.GotoOptions = {}): Promise<network.Response | null> {
|
||||||
const progressController = new ProgressController(this._page._logger, this._page._timeoutSettings.navigationTimeout(options), this._apiName('goto'));
|
const progressController = new ProgressController(this._page._logger, this._page._timeoutSettings.navigationTimeout(options), this._apiName('goto'));
|
||||||
abortProgressOnFrameDetach(progressController, this);
|
abortProgressOnFrameDetach(progressController, this);
|
||||||
return progressController.run(async progress => {
|
return progressController.run(async progress => {
|
||||||
@ -439,6 +436,11 @@ export class Frame {
|
|||||||
return context.evaluateHandleInternal(pageFunction, arg);
|
return context.evaluateHandleInternal(pageFunction, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _evaluateExpressionHandle(expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||||
|
const context = await this._mainContext();
|
||||||
|
return context.evaluateExpressionHandleInternal(expression, isFunction, arg);
|
||||||
|
}
|
||||||
|
|
||||||
async evaluate<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R>;
|
async evaluate<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R>;
|
||||||
async evaluate<R>(pageFunction: js.Func1<void, R>, arg?: any): Promise<R>;
|
async evaluate<R>(pageFunction: js.Func1<void, R>, arg?: any): Promise<R>;
|
||||||
async evaluate<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R> {
|
async evaluate<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<R> {
|
||||||
@ -447,6 +449,11 @@ export class Frame {
|
|||||||
return context.evaluateInternal(pageFunction, arg);
|
return context.evaluateInternal(pageFunction, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _evaluateExpression(expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||||
|
const context = await this._mainContext();
|
||||||
|
return context.evaluateExpressionHandleInternal(expression, isFunction, arg);
|
||||||
|
}
|
||||||
|
|
||||||
async $(selector: string): Promise<dom.ElementHandle<Element> | null> {
|
async $(selector: string): Promise<dom.ElementHandle<Element> | null> {
|
||||||
return selectors._query(this, selector);
|
return selectors._query(this, selector);
|
||||||
}
|
}
|
||||||
@ -493,10 +500,14 @@ export class Frame {
|
|||||||
async $eval<R>(selector: string, pageFunction: js.FuncOn<Element, void, R>, arg?: any): Promise<R>;
|
async $eval<R>(selector: string, pageFunction: js.FuncOn<Element, void, R>, arg?: any): Promise<R>;
|
||||||
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
|
async $eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
|
||||||
assertMaxArguments(arguments.length, 3);
|
assertMaxArguments(arguments.length, 3);
|
||||||
|
return this._$evalExpression(selector, String(pageFunction), typeof pageFunction === 'function', arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||||
const handle = await this.$(selector);
|
const handle = await this.$(selector);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||||
const result = await handle.evaluate(pageFunction, arg);
|
const result = await handle._evaluateExpression(expression, isFunction, true, arg);
|
||||||
handle.dispose();
|
handle.dispose();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -505,8 +516,12 @@ export class Frame {
|
|||||||
async $$eval<R>(selector: string, pageFunction: js.FuncOn<Element[], void, R>, arg?: any): Promise<R>;
|
async $$eval<R>(selector: string, pageFunction: js.FuncOn<Element[], void, R>, arg?: any): Promise<R>;
|
||||||
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
|
async $$eval<R, Arg>(selector: string, pageFunction: js.FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
|
||||||
assertMaxArguments(arguments.length, 3);
|
assertMaxArguments(arguments.length, 3);
|
||||||
|
return this._$$evalExpression(selector, String(pageFunction), typeof pageFunction === 'function', arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _$$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||||
const arrayHandle = await selectors._queryArray(this, selector);
|
const arrayHandle = await selectors._queryArray(this, selector);
|
||||||
const result = await arrayHandle.evaluate(pageFunction, arg);
|
const result = await arrayHandle._evaluateExpression(expression, isFunction, true, arg);
|
||||||
arrayHandle.dispose();
|
arrayHandle.dispose();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -804,6 +819,11 @@ export class Frame {
|
|||||||
async waitForFunction<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>>;
|
async waitForFunction<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>>;
|
||||||
async waitForFunction<R>(pageFunction: js.Func1<void, R>, arg?: any, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>>;
|
async waitForFunction<R>(pageFunction: js.Func1<void, R>, arg?: any, options?: types.WaitForFunctionOptions): Promise<js.SmartHandle<R>>;
|
||||||
async waitForFunction<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
|
async waitForFunction<R, Arg>(pageFunction: js.Func1<Arg, R>, arg: Arg, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
|
||||||
|
return this._waitForFunctionExpression(String(pageFunction), typeof pageFunction === 'function', arg, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async _waitForFunctionExpression<R>(expression: string, isFunction: boolean, arg: any, options: types.WaitForFunctionOptions = {}): Promise<js.SmartHandle<R>> {
|
||||||
const { polling = 'raf' } = options;
|
const { polling = 'raf' } = options;
|
||||||
if (helper.isString(polling))
|
if (helper.isString(polling))
|
||||||
assert(polling === 'raf', 'Unknown polling option: ' + polling);
|
assert(polling === 'raf', 'Unknown polling option: ' + polling);
|
||||||
@ -811,7 +831,7 @@ export class Frame {
|
|||||||
assert(polling > 0, 'Cannot poll with non-positive interval: ' + polling);
|
assert(polling > 0, 'Cannot poll with non-positive interval: ' + polling);
|
||||||
else
|
else
|
||||||
throw new Error('Unknown polling option: ' + polling);
|
throw new Error('Unknown polling option: ' + polling);
|
||||||
const predicateBody = helper.isString(pageFunction) ? 'return (' + pageFunction + ')' : 'return (' + pageFunction + ')(arg)';
|
const predicateBody = isFunction ? 'return (' + expression + ')(arg)' : 'return (' + expression + ')';
|
||||||
const task = async (context: dom.FrameExecutionContext) => {
|
const task = async (context: dom.FrameExecutionContext) => {
|
||||||
const injectedScript = await context.injectedScript();
|
const injectedScript = await context.injectedScript();
|
||||||
return context.evaluateHandleInternal(({ injectedScript, predicateBody, polling, arg }) => {
|
return context.evaluateHandleInternal(({ injectedScript, predicateBody, polling, arg }) => {
|
||||||
|
|||||||
@ -42,8 +42,11 @@ class Helper {
|
|||||||
assert(args.length === 0 || (args.length === 1 && args[0] === undefined), 'Cannot evaluate a string with arguments');
|
assert(args.length === 0 || (args.length === 1 && args[0] === undefined), 'Cannot evaluate a string with arguments');
|
||||||
return fun;
|
return fun;
|
||||||
}
|
}
|
||||||
return `(${fun})(${args.map(serializeArgument).join(',')})`;
|
return Helper.evaluationStringForFunctionBody(String(fun), ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static evaluationStringForFunctionBody(functionBody: string, ...args: any[]): string {
|
||||||
|
return `(${functionBody})(${args.map(serializeArgument).join(',')})`;
|
||||||
function serializeArgument(arg: any): string {
|
function serializeArgument(arg: any): string {
|
||||||
if (Object.is(arg, undefined))
|
if (Object.is(arg, undefined))
|
||||||
return 'undefined';
|
return 'undefined';
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import * as dom from './dom';
|
|||||||
import * as utilityScriptSource from './generated/utilityScriptSource';
|
import * as utilityScriptSource from './generated/utilityScriptSource';
|
||||||
import * as sourceMap from './utils/sourceMap';
|
import * as sourceMap from './utils/sourceMap';
|
||||||
import { serializeAsCallArgument } from './common/utilityScriptSerializers';
|
import { serializeAsCallArgument } from './common/utilityScriptSerializers';
|
||||||
import { helper } from './helper';
|
|
||||||
import UtilityScript from './injected/utilityScript';
|
import UtilityScript from './injected/utilityScript';
|
||||||
|
|
||||||
type ObjectId = string;
|
type ObjectId = string;
|
||||||
@ -106,6 +105,10 @@ export class JSHandle<T = any> {
|
|||||||
return evaluate(this._context, false /* returnByValue */, pageFunction, this, arg);
|
return evaluate(this._context, false /* returnByValue */, pageFunction, this, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_evaluateExpression(expression: string, isFunction: boolean, returnByValue: boolean, arg: any) {
|
||||||
|
return evaluateExpression(this._context, returnByValue, expression, isFunction, this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
async getProperty(propertyName: string): Promise<JSHandle> {
|
async getProperty(propertyName: string): Promise<JSHandle> {
|
||||||
const objectHandle = await this.evaluateHandle((object: any, propertyName) => {
|
const objectHandle = await this.evaluateHandle((object: any, propertyName) => {
|
||||||
const result: any = {__proto__: null};
|
const result: any = {__proto__: null};
|
||||||
@ -147,16 +150,17 @@ export class JSHandle<T = any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {
|
export async function evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||||
const utilityScript = await context.utilityScript();
|
return evaluateExpression(context, returnByValue, String(pageFunction), typeof pageFunction === 'function', ...args);
|
||||||
if (helper.isString(pageFunction)) {
|
}
|
||||||
const script = `(utilityScript, ...args) => utilityScript.evaluate(...args)` + sourceMap.generateSourceUrl();
|
|
||||||
return context._delegate.evaluateWithArguments(script, returnByValue, utilityScript, [returnByValue, sourceMap.ensureSourceUrl(pageFunction)], []);
|
|
||||||
}
|
|
||||||
if (typeof pageFunction !== 'function')
|
|
||||||
throw new Error(`Expected to get |string| or |function| as the first argument, but got "${pageFunction}" instead.`);
|
|
||||||
|
|
||||||
const originalText = pageFunction.toString();
|
export async function evaluateExpression(context: ExecutionContext, returnByValue: boolean, expression: string, isFunction: boolean, ...args: any[]): Promise<any> {
|
||||||
let functionText = originalText;
|
const utilityScript = await context.utilityScript();
|
||||||
|
if (!isFunction) {
|
||||||
|
const script = `(utilityScript, ...args) => utilityScript.evaluate(...args)` + sourceMap.generateSourceUrl();
|
||||||
|
return context._delegate.evaluateWithArguments(script, returnByValue, utilityScript, [returnByValue, sourceMap.ensureSourceUrl(expression)], []);
|
||||||
|
}
|
||||||
|
|
||||||
|
let functionText = expression;
|
||||||
try {
|
try {
|
||||||
new Function('(' + functionText + ')');
|
new Function('(' + functionText + ')');
|
||||||
} catch (e1) {
|
} catch (e1) {
|
||||||
@ -203,7 +207,7 @@ export async function evaluate(context: ExecutionContext, returnByValue: boolean
|
|||||||
utilityScriptObjectIds.push(handle._objectId!);
|
utilityScriptObjectIds.push(handle._objectId!);
|
||||||
}
|
}
|
||||||
|
|
||||||
functionText += await sourceMap.generateSourceMapUrl(originalText, functionText);
|
functionText += await sourceMap.generateSourceMapUrl(expression, functionText);
|
||||||
// See UtilityScript for arguments.
|
// See UtilityScript for arguments.
|
||||||
const utilityScriptValues = [returnByValue, functionText, args.length, ...args];
|
const utilityScriptValues = [returnByValue, functionText, args.length, ...args];
|
||||||
|
|
||||||
|
|||||||
@ -18,35 +18,11 @@ import * as fs from 'fs';
|
|||||||
import * as mime from 'mime';
|
import * as mime from 'mime';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import * as frames from './frames';
|
import * as frames from './frames';
|
||||||
|
import * as types from './types';
|
||||||
import { assert, helper } from './helper';
|
import { assert, helper } from './helper';
|
||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
export type NetworkCookie = {
|
export function filterCookies(cookies: types.NetworkCookie[], urls: string[]): types.NetworkCookie[] {
|
||||||
name: string,
|
|
||||||
value: string,
|
|
||||||
domain: string,
|
|
||||||
path: string,
|
|
||||||
expires: number,
|
|
||||||
httpOnly: boolean,
|
|
||||||
secure: boolean,
|
|
||||||
sameSite: 'Strict' | 'Lax' | 'None'
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SetNetworkCookieParam = {
|
|
||||||
name: string,
|
|
||||||
value: string,
|
|
||||||
url?: string,
|
|
||||||
domain?: string,
|
|
||||||
path?: string,
|
|
||||||
expires?: number,
|
|
||||||
httpOnly?: boolean,
|
|
||||||
secure?: boolean,
|
|
||||||
sameSite?: 'Strict' | 'Lax' | 'None'
|
|
||||||
};
|
|
||||||
|
|
||||||
export function filterCookies(cookies: NetworkCookie[], urls: string | string[] = []): NetworkCookie[] {
|
|
||||||
if (!Array.isArray(urls))
|
|
||||||
urls = [ urls ];
|
|
||||||
const parsedURLs = urls.map(s => new URL(s));
|
const parsedURLs = urls.map(s => new URL(s));
|
||||||
// Chromiums's cookies are missing sameSite when it is 'None'
|
// Chromiums's cookies are missing sameSite when it is 'None'
|
||||||
return cookies.filter(c => {
|
return cookies.filter(c => {
|
||||||
@ -65,7 +41,7 @@ export function filterCookies(cookies: NetworkCookie[], urls: string | string[]
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rewriteCookies(cookies: SetNetworkCookieParam[]): SetNetworkCookieParam[] {
|
export function rewriteCookies(cookies: types.SetNetworkCookieParam[]): types.SetNetworkCookieParam[] {
|
||||||
return cookies.map(c => {
|
return cookies.map(c => {
|
||||||
assert(c.name, 'Cookie should have a name');
|
assert(c.name, 'Cookie should have a name');
|
||||||
assert(c.value, 'Cookie should have a value');
|
assert(c.value, 'Cookie should have a value');
|
||||||
@ -93,8 +69,6 @@ function stripFragmentFromUrl(url: string): string {
|
|||||||
return parsed.href;
|
return parsed.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Headers = { [key: string]: string };
|
|
||||||
|
|
||||||
export class Request {
|
export class Request {
|
||||||
readonly _routeDelegate: RouteDelegate | null;
|
readonly _routeDelegate: RouteDelegate | null;
|
||||||
private _response: Response | null = null;
|
private _response: Response | null = null;
|
||||||
@ -107,13 +81,13 @@ export class Request {
|
|||||||
private _resourceType: string;
|
private _resourceType: string;
|
||||||
private _method: string;
|
private _method: string;
|
||||||
private _postData: string | null;
|
private _postData: string | null;
|
||||||
private _headers: Headers;
|
private _headers: types.Headers;
|
||||||
private _frame: frames.Frame;
|
private _frame: frames.Frame;
|
||||||
private _waitForResponsePromise: Promise<Response | null>;
|
private _waitForResponsePromise: Promise<Response | null>;
|
||||||
private _waitForResponsePromiseCallback: (value: Response | null) => void = () => {};
|
private _waitForResponsePromiseCallback: (value: Response | null) => void = () => {};
|
||||||
|
|
||||||
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined,
|
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined,
|
||||||
url: string, resourceType: string, method: string, postData: string | null, headers: Headers) {
|
url: string, resourceType: string, method: string, postData: string | null, headers: types.Headers) {
|
||||||
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
|
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
|
||||||
assert(!(routeDelegate && redirectedFrom), 'Should not be able to intercept redirects');
|
assert(!(routeDelegate && redirectedFrom), 'Should not be able to intercept redirects');
|
||||||
this._routeDelegate = routeDelegate;
|
this._routeDelegate = routeDelegate;
|
||||||
@ -243,7 +217,7 @@ export class Route {
|
|||||||
await this._delegate.abort(errorCode);
|
await this._delegate.abort(errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: FulfillResponse & { path?: string }) {
|
async fulfill(response: types.FulfillResponse & { path?: string }) {
|
||||||
assert(!this._handled, 'Route is already handled!');
|
assert(!this._handled, 'Route is already handled!');
|
||||||
this._handled = true;
|
this._handled = true;
|
||||||
if (response.path) {
|
if (response.path) {
|
||||||
@ -257,7 +231,7 @@ export class Route {
|
|||||||
await this._delegate.fulfill(response);
|
await this._delegate.fulfill(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { method?: string; headers?: Headers; postData?: string } = {}) {
|
async continue(overrides: { method?: string; headers?: types.Headers; postData?: string } = {}) {
|
||||||
assert(!this._handled, 'Route is already handled!');
|
assert(!this._handled, 'Route is already handled!');
|
||||||
await this._delegate.continue(overrides);
|
await this._delegate.continue(overrides);
|
||||||
}
|
}
|
||||||
@ -275,10 +249,10 @@ export class Response {
|
|||||||
private _status: number;
|
private _status: number;
|
||||||
private _statusText: string;
|
private _statusText: string;
|
||||||
private _url: string;
|
private _url: string;
|
||||||
private _headers: Headers;
|
private _headers: types.Headers;
|
||||||
private _getResponseBodyCallback: GetResponseBodyCallback;
|
private _getResponseBodyCallback: GetResponseBodyCallback;
|
||||||
|
|
||||||
constructor(request: Request, status: number, statusText: string, headers: Headers, getResponseBodyCallback: GetResponseBodyCallback) {
|
constructor(request: Request, status: number, statusText: string, headers: types.Headers, getResponseBodyCallback: GetResponseBodyCallback) {
|
||||||
this._request = request;
|
this._request = request;
|
||||||
this._status = status;
|
this._status = status;
|
||||||
this._statusText = statusText;
|
this._statusText = statusText;
|
||||||
@ -349,17 +323,10 @@ export class Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FulfillResponse = {
|
|
||||||
status?: number,
|
|
||||||
headers?: Headers,
|
|
||||||
contentType?: string,
|
|
||||||
body?: string | Buffer,
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface RouteDelegate {
|
export interface RouteDelegate {
|
||||||
abort(errorCode: string): Promise<void>;
|
abort(errorCode: string): Promise<void>;
|
||||||
fulfill(response: FulfillResponse): Promise<void>;
|
fulfill(response: types.FulfillResponse): Promise<void>;
|
||||||
continue(overrides: { method?: string; headers?: Headers; postData?: string; }): Promise<void>;
|
continue(overrides: { method?: string; headers?: types.Headers; postData?: string; }): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
|
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
|
||||||
@ -429,8 +396,8 @@ export const STATUS_TEXTS: { [status: string]: string } = {
|
|||||||
'511': 'Network Authentication Required',
|
'511': 'Network Authentication Required',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function verifyHeaders(headers: Headers): Headers {
|
export function verifyHeaders(headers: types.Headers): types.Headers {
|
||||||
const result: Headers = {};
|
const result: types.Headers = {};
|
||||||
for (const key of Object.keys(headers)) {
|
for (const key of Object.keys(headers)) {
|
||||||
const value = headers[key];
|
const value = headers[key];
|
||||||
assert(helper.isString(value), `Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
|
assert(helper.isString(value), `Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
|
||||||
@ -439,7 +406,7 @@ export function verifyHeaders(headers: Headers): Headers {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeHeaders(headers: (Headers | undefined | null)[]): Headers {
|
export function mergeHeaders(headers: (types.Headers | undefined | null)[]): types.Headers {
|
||||||
const lowerCaseToValue = new Map<string, string>();
|
const lowerCaseToValue = new Map<string, string>();
|
||||||
const lowerCaseToOriginalCase = new Map<string, string>();
|
const lowerCaseToOriginalCase = new Map<string, string>();
|
||||||
for (const h of headers) {
|
for (const h of headers) {
|
||||||
@ -451,7 +418,7 @@ export function mergeHeaders(headers: (Headers | undefined | null)[]): Headers {
|
|||||||
lowerCaseToValue.set(lower, h[key]);
|
lowerCaseToValue.set(lower, h[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const result: Headers = {};
|
const result: types.Headers = {};
|
||||||
for (const [lower, value] of lowerCaseToValue)
|
for (const [lower, value] of lowerCaseToValue)
|
||||||
result[lowerCaseToOriginalCase.get(lower)!] = value;
|
result[lowerCaseToOriginalCase.get(lower)!] = value;
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
14
src/page.ts
14
src/page.ts
@ -84,7 +84,7 @@ type PageState = {
|
|||||||
viewportSize: types.Size | null;
|
viewportSize: types.Size | null;
|
||||||
mediaType: types.MediaType | null;
|
mediaType: types.MediaType | null;
|
||||||
colorScheme: types.ColorScheme | null;
|
colorScheme: types.ColorScheme | null;
|
||||||
extraHTTPHeaders: network.Headers | null;
|
extraHTTPHeaders: types.Headers | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Page extends EventEmitter {
|
export class Page extends EventEmitter {
|
||||||
@ -265,7 +265,7 @@ export class Page extends EventEmitter {
|
|||||||
await this._delegate.exposeBinding(binding);
|
await this._delegate.exposeBinding(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
setExtraHTTPHeaders(headers: network.Headers) {
|
setExtraHTTPHeaders(headers: types.Headers) {
|
||||||
this._state.extraHTTPHeaders = network.verifyHeaders(headers);
|
this._state.extraHTTPHeaders = network.verifyHeaders(headers);
|
||||||
return this._delegate.updateExtraHTTPHeaders();
|
return this._delegate.updateExtraHTTPHeaders();
|
||||||
}
|
}
|
||||||
@ -295,7 +295,7 @@ export class Page extends EventEmitter {
|
|||||||
return this._attributeToPage(() => this.mainFrame().setContent(html, options));
|
return this._attributeToPage(() => this.mainFrame().setContent(html, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
async goto(url: string, options?: frames.GotoOptions): Promise<network.Response | null> {
|
async goto(url: string, options?: types.GotoOptions): Promise<network.Response | null> {
|
||||||
return this._attributeToPage(() => this.mainFrame().goto(url, options));
|
return this._attributeToPage(() => this.mainFrame().goto(url, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,6 +386,10 @@ export class Page extends EventEmitter {
|
|||||||
|
|
||||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
||||||
const source = await helper.evaluationScript(script, arg);
|
const source = await helper.evaluationScript(script, arg);
|
||||||
|
await this._addInitScriptExpression(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _addInitScriptExpression(source: string) {
|
||||||
this._evaluateOnNewDocumentSources.push(source);
|
this._evaluateOnNewDocumentSources.push(source);
|
||||||
await this._delegate.evaluateOnNewDocument(source);
|
await this._delegate.evaluateOnNewDocument(source);
|
||||||
}
|
}
|
||||||
@ -445,11 +449,11 @@ export class Page extends EventEmitter {
|
|||||||
return this._attributeToPage(() => this.mainFrame().title());
|
return this._attributeToPage(() => this.mainFrame().title());
|
||||||
}
|
}
|
||||||
|
|
||||||
async close(options: { runBeforeUnload: (boolean | undefined); } = {runBeforeUnload: undefined}) {
|
async close(options?: { runBeforeUnload?: boolean }) {
|
||||||
if (this._closed)
|
if (this._closed)
|
||||||
return;
|
return;
|
||||||
assert(!this._disconnected, 'Protocol error: Connection closed. Most likely the page has been closed.');
|
assert(!this._disconnected, 'Protocol error: Connection closed. Most likely the page has been closed.');
|
||||||
const runBeforeUnload = !!options.runBeforeUnload;
|
const runBeforeUnload = !!options && !!options.runBeforeUnload;
|
||||||
await this._delegate.closePage(runBeforeUnload);
|
await this._delegate.closePage(runBeforeUnload);
|
||||||
if (!runBeforeUnload)
|
if (!runBeforeUnload)
|
||||||
await this._closedPromise;
|
await this._closedPromise;
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import * as fs from 'fs';
|
|||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import { BrowserContext, PersistentContextOptions, verifyProxySettings, validateBrowserContextOptions } from '../browserContext';
|
import { BrowserContext, verifyProxySettings, validateBrowserContextOptions } from '../browserContext';
|
||||||
import { BrowserServer } from './browserServer';
|
import { BrowserServer } from './browserServer';
|
||||||
import * as browserPaths from '../install/browserPaths';
|
import * as browserPaths from '../install/browserPaths';
|
||||||
import { Loggers, Logger } from '../logger';
|
import { Loggers, Logger } from '../logger';
|
||||||
@ -34,41 +34,21 @@ import { TimeoutSettings } from '../timeoutSettings';
|
|||||||
import { WebSocketServer } from './webSocketServer';
|
import { WebSocketServer } from './webSocketServer';
|
||||||
import { LoggerSink } from '../loggerSink';
|
import { LoggerSink } from '../loggerSink';
|
||||||
|
|
||||||
export type FirefoxUserPrefsOptions = {
|
type FirefoxPrefsOptions = { firefoxUserPrefs?: { [key: string]: string | number | boolean } };
|
||||||
firefoxUserPrefs?: { [key: string]: string | number | boolean },
|
type LaunchOptions = types.LaunchOptions & { logger?: LoggerSink };
|
||||||
};
|
type ConnectOptions = types.ConnectOptions & { logger?: LoggerSink };
|
||||||
|
|
||||||
export type LaunchOptionsBase = {
|
|
||||||
executablePath?: string,
|
|
||||||
args?: string[],
|
|
||||||
ignoreDefaultArgs?: boolean | string[],
|
|
||||||
handleSIGINT?: boolean,
|
|
||||||
handleSIGTERM?: boolean,
|
|
||||||
handleSIGHUP?: boolean,
|
|
||||||
timeout?: number,
|
|
||||||
logger?: LoggerSink,
|
|
||||||
env?: Env,
|
|
||||||
headless?: boolean,
|
|
||||||
devtools?: boolean,
|
|
||||||
proxy?: types.ProxySettings,
|
|
||||||
downloadsPath?: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
type ConnectOptions = {
|
export type LaunchNonPersistentOptions = LaunchOptions & FirefoxPrefsOptions;
|
||||||
wsEndpoint: string,
|
type LaunchPersistentOptions = LaunchOptions & types.BrowserContextOptions;
|
||||||
slowMo?: number,
|
type LaunchServerOptions = types.LaunchServerOptions & { logger?: LoggerSink } & FirefoxPrefsOptions;
|
||||||
logger?: LoggerSink,
|
|
||||||
timeout?: number,
|
|
||||||
};
|
|
||||||
export type LaunchOptions = LaunchOptionsBase & { slowMo?: number };
|
|
||||||
type LaunchServerOptions = LaunchOptionsBase & { port?: number };
|
|
||||||
|
|
||||||
export interface BrowserType {
|
export interface BrowserType {
|
||||||
executablePath(): string;
|
executablePath(): string;
|
||||||
name(): string;
|
name(): string;
|
||||||
launch(options?: LaunchOptions & FirefoxUserPrefsOptions): Promise<Browser>;
|
launch(options?: LaunchNonPersistentOptions): Promise<Browser>;
|
||||||
launchServer(options?: LaunchServerOptions & FirefoxUserPrefsOptions): Promise<BrowserServer>;
|
launchServer(options?: LaunchServerOptions): Promise<BrowserServer>;
|
||||||
launchPersistentContext(userDataDir: string, options?: LaunchOptions & PersistentContextOptions): Promise<BrowserContext>;
|
launchPersistentContext(userDataDir: string, options?: LaunchPersistentOptions): Promise<BrowserContext>;
|
||||||
connect(options: ConnectOptions): Promise<Browser>;
|
connect(options: ConnectOptions): Promise<Browser>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +82,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||||||
return this._name;
|
return this._name;
|
||||||
}
|
}
|
||||||
|
|
||||||
async launch(options: LaunchOptions = {}): Promise<Browser> {
|
async launch(options: LaunchNonPersistentOptions = {}): Promise<Browser> {
|
||||||
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
|
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
|
||||||
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
||||||
options = validateLaunchOptions(options);
|
options = validateLaunchOptions(options);
|
||||||
@ -111,7 +91,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
async launchPersistentContext(userDataDir: string, options: LaunchOptions & PersistentContextOptions = {}): Promise<BrowserContext> {
|
async launchPersistentContext(userDataDir: string, options: LaunchPersistentOptions = {}): Promise<BrowserContext> {
|
||||||
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
|
||||||
options = validateLaunchOptions(options);
|
options = validateLaunchOptions(options);
|
||||||
const persistent = validateBrowserContextOptions(options);
|
const persistent = validateBrowserContextOptions(options);
|
||||||
@ -120,7 +100,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||||||
return browser._defaultContext!;
|
return browser._defaultContext!;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _innerLaunch(progress: Progress, options: LaunchOptions, logger: Loggers, persistent: PersistentContextOptions | undefined, userDataDir?: string): Promise<BrowserBase> {
|
async _innerLaunch(progress: Progress, options: LaunchOptions, logger: Loggers, persistent: types.BrowserContextOptions | undefined, userDataDir?: string): Promise<BrowserBase> {
|
||||||
options.proxy = options.proxy ? verifyProxySettings(options.proxy) : undefined;
|
options.proxy = options.proxy ? verifyProxySettings(options.proxy) : undefined;
|
||||||
const { browserServer, downloadsPath, transport } = await this._launchServer(progress, options, !!persistent, logger, userDataDir);
|
const { browserServer, downloadsPath, transport } = await this._launchServer(progress, options, !!persistent, logger, userDataDir);
|
||||||
if ((options as any).__testHookBeforeCreateBrowser)
|
if ((options as any).__testHookBeforeCreateBrowser)
|
||||||
@ -246,7 +226,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||||||
return { browserServer, downloadsPath, transport };
|
return { browserServer, downloadsPath, transport };
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract _defaultArgs(options: LaunchOptionsBase, isPersistent: boolean, userDataDir: string): string[];
|
abstract _defaultArgs(options: types.LaunchOptionsBase, isPersistent: boolean, userDataDir: string): string[];
|
||||||
abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BrowserBase>;
|
abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BrowserBase>;
|
||||||
abstract _startWebSocketServer(transport: ConnectionTransport, logger: Logger, port: number): WebSocketServer;
|
abstract _startWebSocketServer(transport: ConnectionTransport, logger: Logger, port: number): WebSocketServer;
|
||||||
abstract _amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env;
|
abstract _amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env;
|
||||||
@ -260,7 +240,7 @@ function copyTestHooks(from: object, to: object) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateLaunchOptions<Options extends LaunchOptionsBase>(options: Options): Options {
|
function validateLaunchOptions<Options extends types.LaunchOptionsBase>(options: Options): Options {
|
||||||
const { devtools = false, headless = !helper.isDebugMode() && !devtools } = options;
|
const { devtools = false, headless = !helper.isDebugMode() && !devtools } = options;
|
||||||
return { ...options, devtools, headless };
|
return { ...options, devtools, headless };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,13 +21,14 @@ import { CRBrowser } from '../chromium/crBrowser';
|
|||||||
import * as ws from 'ws';
|
import * as ws from 'ws';
|
||||||
import { Env } from './processLauncher';
|
import { Env } from './processLauncher';
|
||||||
import { kBrowserCloseMessageId } from '../chromium/crConnection';
|
import { kBrowserCloseMessageId } from '../chromium/crConnection';
|
||||||
import { LaunchOptionsBase, BrowserTypeBase } from './browserType';
|
import { BrowserTypeBase } from './browserType';
|
||||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
|
import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import { BrowserDescriptor } from '../install/browserPaths';
|
import { BrowserDescriptor } from '../install/browserPaths';
|
||||||
import { CRDevTools } from '../chromium/crDevTools';
|
import { CRDevTools } from '../chromium/crDevTools';
|
||||||
import { BrowserOptions } from '../browser';
|
import { BrowserOptions } from '../browser';
|
||||||
import { WebSocketServer } from './webSocketServer';
|
import { WebSocketServer } from './webSocketServer';
|
||||||
|
import { LaunchOptionsBase } from '../types';
|
||||||
|
|
||||||
export class Chromium extends BrowserTypeBase {
|
export class Chromium extends BrowserTypeBase {
|
||||||
private _devtools: CRDevTools | undefined;
|
private _devtools: CRDevTools | undefined;
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import * as path from 'path';
|
|||||||
import * as ws from 'ws';
|
import * as ws from 'ws';
|
||||||
import { FFBrowser } from '../firefox/ffBrowser';
|
import { FFBrowser } from '../firefox/ffBrowser';
|
||||||
import { kBrowserCloseMessageId } from '../firefox/ffConnection';
|
import { kBrowserCloseMessageId } from '../firefox/ffConnection';
|
||||||
import { LaunchOptionsBase, BrowserTypeBase, FirefoxUserPrefsOptions } from './browserType';
|
import { BrowserTypeBase, LaunchNonPersistentOptions } from './browserType';
|
||||||
import { Env } from './processLauncher';
|
import { Env } from './processLauncher';
|
||||||
import { ConnectionTransport, ProtocolResponse, ProtocolRequest } from '../transport';
|
import { ConnectionTransport, ProtocolResponse, ProtocolRequest } from '../transport';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
@ -56,7 +56,7 @@ export class Firefox extends BrowserTypeBase {
|
|||||||
return startWebSocketServer(transport, logger, port);
|
return startWebSocketServer(transport, logger, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaultArgs(options: LaunchOptionsBase & FirefoxUserPrefsOptions, isPersistent: boolean, userDataDir: string): string[] {
|
_defaultArgs(options: LaunchNonPersistentOptions, isPersistent: boolean, userDataDir: string): string[] {
|
||||||
const { args = [], proxy, devtools, headless } = options;
|
const { args = [], proxy, devtools, headless } = options;
|
||||||
if (devtools)
|
if (devtools)
|
||||||
console.warn('devtools parameter is not supported as a launch argument in Firefox. You can launch the devtools window manually.');
|
console.warn('devtools parameter is not supported as a launch argument in Firefox. You can launch the devtools window manually.');
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { WKBrowser } from '../webkit/wkBrowser';
|
|||||||
import { Env } from './processLauncher';
|
import { Env } from './processLauncher';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { kBrowserCloseMessageId } from '../webkit/wkConnection';
|
import { kBrowserCloseMessageId } from '../webkit/wkConnection';
|
||||||
import { LaunchOptionsBase, BrowserTypeBase } from './browserType';
|
import { BrowserTypeBase } from './browserType';
|
||||||
import { ConnectionTransport, ProtocolResponse, ProtocolRequest } from '../transport';
|
import { ConnectionTransport, ProtocolResponse, ProtocolRequest } from '../transport';
|
||||||
import * as ws from 'ws';
|
import * as ws from 'ws';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
@ -27,6 +27,7 @@ import { BrowserOptions } from '../browser';
|
|||||||
import { BrowserDescriptor } from '../install/browserPaths';
|
import { BrowserDescriptor } from '../install/browserPaths';
|
||||||
import { WebSocketServer } from './webSocketServer';
|
import { WebSocketServer } from './webSocketServer';
|
||||||
import { assert } from '../helper';
|
import { assert } from '../helper';
|
||||||
|
import { LaunchOptionsBase } from '../types';
|
||||||
|
|
||||||
export class WebKit extends BrowserTypeBase {
|
export class WebKit extends BrowserTypeBase {
|
||||||
constructor(packagePath: string, browser: BrowserDescriptor) {
|
constructor(packagePath: string, browser: BrowserDescriptor) {
|
||||||
|
|||||||
82
src/types.ts
82
src/types.ts
@ -180,3 +180,85 @@ export type MouseMultiClickOptions = PointerActionOptions & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type World = 'main' | 'utility';
|
export type World = 'main' | 'utility';
|
||||||
|
|
||||||
|
export type Headers = { [key: string]: string };
|
||||||
|
|
||||||
|
export type GotoOptions = NavigateOptions & {
|
||||||
|
referer?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FulfillResponse = {
|
||||||
|
status?: number,
|
||||||
|
headers?: Headers,
|
||||||
|
contentType?: string,
|
||||||
|
body?: string | Buffer,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NetworkCookie = {
|
||||||
|
name: string,
|
||||||
|
value: string,
|
||||||
|
domain: string,
|
||||||
|
path: string,
|
||||||
|
expires: number,
|
||||||
|
httpOnly: boolean,
|
||||||
|
secure: boolean,
|
||||||
|
sameSite: 'Strict' | 'Lax' | 'None'
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SetNetworkCookieParam = {
|
||||||
|
name: string,
|
||||||
|
value: string,
|
||||||
|
url?: string,
|
||||||
|
domain?: string,
|
||||||
|
path?: string,
|
||||||
|
expires?: number,
|
||||||
|
httpOnly?: boolean,
|
||||||
|
secure?: boolean,
|
||||||
|
sameSite?: 'Strict' | 'Lax' | 'None'
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BrowserContextOptions = {
|
||||||
|
viewport?: Size | null,
|
||||||
|
ignoreHTTPSErrors?: boolean,
|
||||||
|
javaScriptEnabled?: boolean,
|
||||||
|
bypassCSP?: boolean,
|
||||||
|
userAgent?: string,
|
||||||
|
locale?: string,
|
||||||
|
timezoneId?: string,
|
||||||
|
geolocation?: Geolocation,
|
||||||
|
permissions?: string[],
|
||||||
|
extraHTTPHeaders?: Headers,
|
||||||
|
offline?: boolean,
|
||||||
|
httpCredentials?: Credentials,
|
||||||
|
deviceScaleFactor?: number,
|
||||||
|
isMobile?: boolean,
|
||||||
|
hasTouch?: boolean,
|
||||||
|
colorScheme?: ColorScheme,
|
||||||
|
acceptDownloads?: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Env = {[key: string]: string | number | boolean | undefined};
|
||||||
|
|
||||||
|
export type LaunchOptionsBase = {
|
||||||
|
executablePath?: string,
|
||||||
|
args?: string[],
|
||||||
|
ignoreDefaultArgs?: boolean | string[],
|
||||||
|
handleSIGINT?: boolean,
|
||||||
|
handleSIGTERM?: boolean,
|
||||||
|
handleSIGHUP?: boolean,
|
||||||
|
timeout?: number,
|
||||||
|
env?: Env,
|
||||||
|
headless?: boolean,
|
||||||
|
devtools?: boolean,
|
||||||
|
proxy?: ProxySettings,
|
||||||
|
downloadsPath?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LaunchOptions = LaunchOptionsBase & { slowMo?: number };
|
||||||
|
export type LaunchServerOptions = LaunchOptionsBase & { port?: number };
|
||||||
|
|
||||||
|
export type ConnectOptions = {
|
||||||
|
wsEndpoint: string,
|
||||||
|
slowMo?: number,
|
||||||
|
timeout?: number,
|
||||||
|
};
|
||||||
|
|||||||
@ -15,8 +15,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BrowserBase, BrowserOptions } from '../browser';
|
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
|
||||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, BrowserContextOptions, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { helper, RegisteredListener, assert } from '../helper';
|
import { helper, RegisteredListener, assert } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
@ -202,7 +202,7 @@ export class WKBrowserContext extends BrowserContextBase {
|
|||||||
readonly _browserContextId: string | undefined;
|
readonly _browserContextId: string | undefined;
|
||||||
readonly _evaluateOnNewDocumentSources: string[];
|
readonly _evaluateOnNewDocumentSources: string[];
|
||||||
|
|
||||||
constructor(browser: WKBrowser, browserContextId: string | undefined, options: BrowserContextOptions) {
|
constructor(browser: WKBrowser, browserContextId: string | undefined, options: types.BrowserContextOptions) {
|
||||||
super(browser, options);
|
super(browser, options);
|
||||||
this._browser = browser;
|
this._browser = browser;
|
||||||
this._browserContextId = browserContextId;
|
this._browserContextId = browserContextId;
|
||||||
@ -257,17 +257,17 @@ export class WKBrowserContext extends BrowserContextBase {
|
|||||||
throw result;
|
throw result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async cookies(urls?: string | string[]): Promise<network.NetworkCookie[]> {
|
async _doCookies(urls: string[]): Promise<types.NetworkCookie[]> {
|
||||||
const { cookies } = await this._browser._browserSession.send('Playwright.getAllCookies', { browserContextId: this._browserContextId });
|
const { cookies } = await this._browser._browserSession.send('Playwright.getAllCookies', { browserContextId: this._browserContextId });
|
||||||
return network.filterCookies(cookies.map((c: network.NetworkCookie) => {
|
return network.filterCookies(cookies.map((c: types.NetworkCookie) => {
|
||||||
const copy: any = { ... c };
|
const copy: any = { ... c };
|
||||||
copy.expires = c.expires === -1 ? -1 : c.expires / 1000;
|
copy.expires = c.expires === -1 ? -1 : c.expires / 1000;
|
||||||
delete copy.session;
|
delete copy.session;
|
||||||
return copy as network.NetworkCookie;
|
return copy as types.NetworkCookie;
|
||||||
}), urls);
|
}), urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addCookies(cookies: network.SetNetworkCookieParam[]) {
|
async addCookies(cookies: types.SetNetworkCookieParam[]) {
|
||||||
const cc = network.rewriteCookies(cookies).map(c => ({
|
const cc = network.rewriteCookies(cookies).map(c => ({
|
||||||
...c,
|
...c,
|
||||||
session: c.expires === -1 || c.expires === undefined,
|
session: c.expires === -1 || c.expires === undefined,
|
||||||
@ -296,7 +296,7 @@ export class WKBrowserContext extends BrowserContextBase {
|
|||||||
await this._browser._browserSession.send('Playwright.setGeolocationOverride', { browserContextId: this._browserContextId, geolocation: payload });
|
await this._browser._browserSession.send('Playwright.setGeolocationOverride', { browserContextId: this._browserContextId, geolocation: payload });
|
||||||
}
|
}
|
||||||
|
|
||||||
async setExtraHTTPHeaders(headers: network.Headers): Promise<void> {
|
async setExtraHTTPHeaders(headers: types.Headers): Promise<void> {
|
||||||
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
|
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
|
||||||
for (const page of this.pages())
|
for (const page of this.pages())
|
||||||
await (page._delegate as WKPage).updateExtraHTTPHeaders();
|
await (page._delegate as WKPage).updateExtraHTTPHeaders();
|
||||||
@ -314,8 +314,7 @@ export class WKBrowserContext extends BrowserContextBase {
|
|||||||
await (page._delegate as WKPage).updateHttpCredentials();
|
await (page._delegate as WKPage).updateHttpCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
async _doAddInitScript(source: string) {
|
||||||
const source = await helper.evaluationScript(script, arg);
|
|
||||||
this._evaluateOnNewDocumentSources.push(source);
|
this._evaluateOnNewDocumentSources.push(source);
|
||||||
for (const page of this.pages())
|
for (const page of this.pages())
|
||||||
await (page._delegate as WKPage)._updateBootstrapScript();
|
await (page._delegate as WKPage)._updateBootstrapScript();
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { assert, helper } from '../helper';
|
import { assert, helper } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
|
import * as types from '../types';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { WKSession } from './wkConnection';
|
import { WKSession } from './wkConnection';
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ export class WKInterceptableRequest implements network.RouteDelegate {
|
|||||||
await this._session.sendMayFail('Network.interceptRequestWithError', { requestId: this._requestId, errorType });
|
await this._session.sendMayFail('Network.interceptRequestWithError', { requestId: this._requestId, errorType });
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: network.FulfillResponse) {
|
async fulfill(response: types.FulfillResponse) {
|
||||||
await this._interceptedPromise;
|
await this._interceptedPromise;
|
||||||
|
|
||||||
const base64Encoded = !!response.body && !helper.isString(response.body);
|
const base64Encoded = !!response.body && !helper.isString(response.body);
|
||||||
@ -101,7 +102,7 @@ export class WKInterceptableRequest implements network.RouteDelegate {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string }) {
|
async continue(overrides: { method?: string; headers?: types.Headers; postData?: string }) {
|
||||||
await this._interceptedPromise;
|
await this._interceptedPromise;
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
// or the page was closed. We should tolerate these errors.
|
// or the page was closed. We should tolerate these errors.
|
||||||
@ -122,8 +123,8 @@ export class WKInterceptableRequest implements network.RouteDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function headersObject(headers: Protocol.Network.Headers): network.Headers {
|
function headersObject(headers: Protocol.Network.Headers): types.Headers {
|
||||||
const result: network.Headers = {};
|
const result: types.Headers = {};
|
||||||
for (const key of Object.keys(headers))
|
for (const key of Object.keys(headers))
|
||||||
result[key.toLowerCase()] = headers[key];
|
result[key.toLowerCase()] = headers[key];
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -552,7 +552,7 @@ export class WKPage implements PageDelegate {
|
|||||||
await this._updateState('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() });
|
await this._updateState('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() });
|
||||||
}
|
}
|
||||||
|
|
||||||
_calculateExtraHTTPHeaders(): network.Headers {
|
_calculateExtraHTTPHeaders(): types.Headers {
|
||||||
const headers = network.mergeHeaders([
|
const headers = network.mergeHeaders([
|
||||||
this._browserContext._options.extraHTTPHeaders,
|
this._browserContext._options.extraHTTPHeaders,
|
||||||
this._page._state.extraHTTPHeaders
|
this._page._state.extraHTTPHeaders
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user