chore: prepare library types for rpc (#2706)
This commit is contained in:
Родитель
1865c5685a
Коммит
bc3050776e
|
@ -14,7 +14,8 @@
|
|||
* 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 { EventEmitter } from 'events';
|
||||
import { Download } from './download';
|
||||
|
@ -22,17 +23,20 @@ import type { BrowserServer } from './server/browserServer';
|
|||
import { Events } from './events';
|
||||
import { Loggers } from './logger';
|
||||
import { ProxySettings } from './types';
|
||||
import { LoggerSink } from './loggerSink';
|
||||
|
||||
export type BrowserOptions = {
|
||||
loggers: Loggers,
|
||||
downloadsPath?: string,
|
||||
headful?: boolean,
|
||||
persistent?: PersistentContextOptions, // Undefined means no persistent context.
|
||||
persistent?: types.BrowserContextOptions, // Undefined means no persistent context.
|
||||
slowMo?: number,
|
||||
ownedServer?: BrowserServer,
|
||||
proxy?: ProxySettings,
|
||||
};
|
||||
|
||||
export type BrowserContextOptions = types.BrowserContextOptions & { logger?: LoggerSink };
|
||||
|
||||
export interface Browser extends EventEmitter {
|
||||
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
|
||||
contexts(): BrowserContext[];
|
||||
|
|
|
@ -31,43 +31,18 @@ import { ProgressController } from './progress';
|
|||
import { DebugController } from './debug/debugController';
|
||||
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 {
|
||||
setDefaultNavigationTimeout(timeout: number): void;
|
||||
setDefaultTimeout(timeout: number): void;
|
||||
pages(): Page[];
|
||||
newPage(): Promise<Page>;
|
||||
cookies(urls?: string | string[]): Promise<network.NetworkCookie[]>;
|
||||
addCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
|
||||
cookies(urls?: string | string[]): Promise<types.NetworkCookie[]>;
|
||||
addCookies(cookies: types.SetNetworkCookieParam[]): Promise<void>;
|
||||
clearCookies(): Promise<void>;
|
||||
grantPermissions(permissions: string[], options?: { origin?: string }): Promise<void>;
|
||||
clearPermissions(): 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>;
|
||||
setHTTPCredentials(httpCredentials: types.Credentials | null): Promise<void>;
|
||||
addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any): Promise<void>;
|
||||
|
@ -79,6 +54,8 @@ export interface BrowserContext {
|
|||
close(): Promise<void>;
|
||||
}
|
||||
|
||||
type BrowserContextOptions = types.BrowserContextOptions & { logger?: LoggerSink };
|
||||
|
||||
export abstract class BrowserContextBase extends EventEmitter implements BrowserContext {
|
||||
readonly _timeoutSettings = new TimeoutSettings();
|
||||
readonly _pageBindings = new Map<string, PageBinding>();
|
||||
|
@ -141,21 +118,27 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
|
|||
// BrowserContext methods.
|
||||
abstract pages(): Page[];
|
||||
abstract newPage(): Promise<Page>;
|
||||
abstract cookies(...urls: string[]): Promise<network.NetworkCookie[]>;
|
||||
abstract addCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
|
||||
abstract _doCookies(urls: string[]): Promise<types.NetworkCookie[]>;
|
||||
abstract addCookies(cookies: types.SetNetworkCookieParam[]): Promise<void>;
|
||||
abstract clearCookies(): Promise<void>;
|
||||
abstract _doGrantPermissions(origin: string, permissions: string[]): Promise<void>;
|
||||
abstract _doClearPermissions(): Promise<void>;
|
||||
abstract setGeolocation(geolocation: types.Geolocation | 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 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 route(url: types.URLMatch, handler: network.RouteHandler): Promise<void>;
|
||||
abstract unroute(url: types.URLMatch, handler?: network.RouteHandler): 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> {
|
||||
await this.exposeBinding(name, (options, ...args: any) => playwrightFunction(...args));
|
||||
}
|
||||
|
@ -172,6 +155,11 @@ export abstract class BrowserContextBase extends EventEmitter implements Browser
|
|||
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 }) {
|
||||
let origin = '*';
|
||||
if (options && options.origin) {
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserBase, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, BrowserContextOptions, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { Events as CommonEvents } from '../events';
|
||||
import { assert, helper } from '../helper';
|
||||
import { assert } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding, Worker } from '../page';
|
||||
import { ConnectionTransport, SlowMoTransport } from '../transport';
|
||||
|
@ -280,7 +280,7 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||
readonly _browserContextId: string | null;
|
||||
readonly _evaluateOnNewDocumentSources: string[];
|
||||
|
||||
constructor(browser: CRBrowser, browserContextId: string | null, options: BrowserContextOptions) {
|
||||
constructor(browser: CRBrowser, browserContextId: string | null, options: types.BrowserContextOptions) {
|
||||
super(browser, options);
|
||||
this._browser = browser;
|
||||
this._browserContextId = browserContextId;
|
||||
|
@ -325,18 +325,18 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||
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 });
|
||||
return network.filterCookies(cookies.map(c => {
|
||||
const copy: any = { sameSite: 'None', ...c };
|
||||
delete copy.size;
|
||||
delete copy.priority;
|
||||
delete copy.session;
|
||||
return copy as network.NetworkCookie;
|
||||
return copy as types.NetworkCookie;
|
||||
}), 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 });
|
||||
}
|
||||
|
||||
|
@ -384,7 +384,7 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||
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);
|
||||
for (const page of this.pages())
|
||||
await (page._delegate as CRPage).updateExtraHTTPHeaders();
|
||||
|
@ -402,8 +402,7 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||
await (page._delegate as CRPage).updateHttpCredentials();
|
||||
}
|
||||
|
||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
||||
const source = await helper.evaluationScript(script, arg);
|
||||
async _doAddInitScript(source: string) {
|
||||
this._evaluateOnNewDocumentSources.push(source);
|
||||
for (const page of this.pages())
|
||||
await (page._delegate as CRPage).evaluateOnNewDocument(source);
|
||||
|
|
|
@ -21,7 +21,7 @@ import { assert, helper, RegisteredListener } from '../helper';
|
|||
import { Protocol } from './protocol';
|
||||
import * as network from '../network';
|
||||
import * as frames from '../frames';
|
||||
import { Credentials } from '../types';
|
||||
import * as types from '../types';
|
||||
import { CRPage } from './crPage';
|
||||
|
||||
export class CRNetworkManager {
|
||||
|
@ -63,7 +63,7 @@ export class CRNetworkManager {
|
|||
helper.removeEventListeners(this._eventListeners);
|
||||
}
|
||||
|
||||
async authenticate(credentials: Credentials | null) {
|
||||
async authenticate(credentials: types.Credentials | null) {
|
||||
this._credentials = credentials;
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
// or the page was closed. We should tolerate these errors.
|
||||
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 responseHeaders: { [s: string]: string; } = {};
|
||||
|
@ -423,8 +423,8 @@ function headersArray(headers: { [s: string]: string; }): { name: string; value:
|
|||
return result;
|
||||
}
|
||||
|
||||
function headersObject(headers: Protocol.Network.Headers): network.Headers {
|
||||
const result: network.Headers = {};
|
||||
function headersObject(headers: Protocol.Network.Headers): types.Headers {
|
||||
const result: types.Headers = {};
|
||||
for (const key of Object.keys(headers))
|
||||
result[key.toLowerCase()] = headers[key];
|
||||
return result;
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
|
||||
import * as program from 'commander';
|
||||
import { Playwright } from '../server/playwright';
|
||||
import { BrowserType, LaunchOptions } from '../server/browserType';
|
||||
import { BrowserType } from '../server/browserType';
|
||||
import { DeviceDescriptors } from '../deviceDescriptors';
|
||||
import { BrowserContextOptions } from '../browserContext';
|
||||
import { helper } from '../helper';
|
||||
import { LaunchOptions, BrowserContextOptions } from '../types';
|
||||
|
||||
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 util from 'util';
|
||||
import * as frames from './frames';
|
||||
import { assert, helper } from './helper';
|
||||
import { assert, helper, assertMaxArguments } from './helper';
|
||||
import InjectedScript from './injected/injectedScript';
|
||||
import * as injectedScriptSource from './generated/injectedScriptSource';
|
||||
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<Arg, R>(pageFunction: js.Func1<Arg, R>, arg: Arg): Promise<js.SmartHandle<R>>;
|
||||
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 {
|
||||
if (this.frame._page._delegate.isElementHandle(remoteObject))
|
||||
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>(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> {
|
||||
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);
|
||||
if (!handle)
|
||||
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();
|
||||
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>(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> {
|
||||
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 result = await arrayHandle.evaluate(pageFunction, arg);
|
||||
const result = await arrayHandle._evaluateExpression(expression, isFunction, true, arg);
|
||||
arrayHandle.dispose();
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserBase, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, BrowserContextOptions, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import * as network from '../network';
|
||||
|
@ -146,7 +146,7 @@ export class FFBrowserContext extends BrowserContextBase {
|
|||
readonly _browser: FFBrowser;
|
||||
readonly _browserContextId: string | null;
|
||||
|
||||
constructor(browser: FFBrowser, browserContextId: string | null, options: BrowserContextOptions) {
|
||||
constructor(browser: FFBrowser, browserContextId: string | null, options: types.BrowserContextOptions) {
|
||||
super(browser, options);
|
||||
this._browser = browser;
|
||||
this._browserContextId = browserContextId;
|
||||
|
@ -237,17 +237,17 @@ export class FFBrowserContext extends BrowserContextBase {
|
|||
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 });
|
||||
return network.filterCookies(cookies.map(c => {
|
||||
const copy: any = { ... c };
|
||||
delete copy.size;
|
||||
delete copy.session;
|
||||
return copy as network.NetworkCookie;
|
||||
return copy as types.NetworkCookie;
|
||||
}), 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) });
|
||||
}
|
||||
|
||||
|
@ -282,7 +282,7 @@ export class FFBrowserContext extends BrowserContextBase {
|
|||
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);
|
||||
const allHeaders = { ...this._options.extraHTTPHeaders };
|
||||
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 });
|
||||
}
|
||||
|
||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
||||
const source = await helper.evaluationScript(script, arg);
|
||||
async _doAddInitScript(source: string) {
|
||||
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 * as network from '../network';
|
||||
import * as frames from '../frames';
|
||||
import * as types from '../types';
|
||||
import { Protocol } from './protocol';
|
||||
|
||||
export class FFNetworkManager {
|
||||
|
@ -74,7 +75,7 @@ export class FFNetworkManager {
|
|||
throw new Error(`Response body for ${request.request.method()} ${request.request.url()} was evicted!`);
|
||||
return Buffer.from(response.base64body, 'base64');
|
||||
};
|
||||
const headers: network.Headers = {};
|
||||
const headers: types.Headers = {};
|
||||
for (const {name, value} of event.headers)
|
||||
headers[name.toLowerCase()] = value;
|
||||
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._session = session;
|
||||
|
||||
const headers: network.Headers = {};
|
||||
const headers: types.Headers = {};
|
||||
for (const {name, value} of payload.headers)
|
||||
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);
|
||||
}
|
||||
|
||||
async continue(overrides: { method?: string; headers?: network.Headers; postData?: string }) {
|
||||
async continue(overrides: { method?: string; headers?: types.Headers; postData?: string }) {
|
||||
const {
|
||||
method,
|
||||
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 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[] = [];
|
||||
for (const name in headers) {
|
||||
if (!Object.is(headers[name], undefined))
|
||||
|
|
|
@ -36,9 +36,6 @@ type ContextData = {
|
|||
rerunnableTasks: Set<RerunnableTask<any>>;
|
||||
};
|
||||
|
||||
export type GotoOptions = types.NavigateOptions & {
|
||||
referer?: string,
|
||||
};
|
||||
export type GotoResult = {
|
||||
newDocumentId?: string,
|
||||
};
|
||||
|
@ -347,7 +344,7 @@ export class Frame {
|
|||
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'));
|
||||
abortProgressOnFrameDetach(progressController, this);
|
||||
return progressController.run(async progress => {
|
||||
|
@ -439,6 +436,11 @@ export class Frame {
|
|||
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>(pageFunction: js.Func1<void, R>, arg?: any): 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);
|
||||
}
|
||||
|
||||
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> {
|
||||
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, 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 this.$(selector);
|
||||
if (!handle)
|
||||
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();
|
||||
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, 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, selector);
|
||||
const result = await arrayHandle.evaluate(pageFunction, arg);
|
||||
const result = await arrayHandle._evaluateExpression(expression, isFunction, true, arg);
|
||||
arrayHandle.dispose();
|
||||
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>(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>> {
|
||||
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;
|
||||
if (helper.isString(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);
|
||||
else
|
||||
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 injectedScript = await context.injectedScript();
|
||||
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');
|
||||
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 {
|
||||
if (Object.is(arg, undefined))
|
||||
return 'undefined';
|
||||
|
|
|
@ -18,7 +18,6 @@ import * as dom from './dom';
|
|||
import * as utilityScriptSource from './generated/utilityScriptSource';
|
||||
import * as sourceMap from './utils/sourceMap';
|
||||
import { serializeAsCallArgument } from './common/utilityScriptSerializers';
|
||||
import { helper } from './helper';
|
||||
import UtilityScript from './injected/utilityScript';
|
||||
|
||||
type ObjectId = string;
|
||||
|
@ -106,6 +105,10 @@ export class JSHandle<T = any> {
|
|||
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> {
|
||||
const objectHandle = await this.evaluateHandle((object: any, propertyName) => {
|
||||
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> {
|
||||
const utilityScript = await context.utilityScript();
|
||||
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.`);
|
||||
return evaluateExpression(context, returnByValue, String(pageFunction), typeof pageFunction === 'function', ...args);
|
||||
}
|
||||
|
||||
const originalText = pageFunction.toString();
|
||||
let functionText = originalText;
|
||||
export async function evaluateExpression(context: ExecutionContext, returnByValue: boolean, expression: string, isFunction: boolean, ...args: any[]): Promise<any> {
|
||||
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 {
|
||||
new Function('(' + functionText + ')');
|
||||
} catch (e1) {
|
||||
|
@ -203,7 +207,7 @@ export async function evaluate(context: ExecutionContext, returnByValue: boolean
|
|||
utilityScriptObjectIds.push(handle._objectId!);
|
||||
}
|
||||
|
||||
functionText += await sourceMap.generateSourceMapUrl(originalText, functionText);
|
||||
functionText += await sourceMap.generateSourceMapUrl(expression, functionText);
|
||||
// See UtilityScript for arguments.
|
||||
const utilityScriptValues = [returnByValue, functionText, args.length, ...args];
|
||||
|
||||
|
|
|
@ -18,35 +18,11 @@ import * as fs from 'fs';
|
|||
import * as mime from 'mime';
|
||||
import * as util from 'util';
|
||||
import * as frames from './frames';
|
||||
import * as types from './types';
|
||||
import { assert, helper } from './helper';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
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 function filterCookies(cookies: NetworkCookie[], urls: string | string[] = []): NetworkCookie[] {
|
||||
if (!Array.isArray(urls))
|
||||
urls = [ urls ];
|
||||
export function filterCookies(cookies: types.NetworkCookie[], urls: string[]): types.NetworkCookie[] {
|
||||
const parsedURLs = urls.map(s => new URL(s));
|
||||
// Chromiums's cookies are missing sameSite when it is 'None'
|
||||
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 => {
|
||||
assert(c.name, 'Cookie should have a name');
|
||||
assert(c.value, 'Cookie should have a value');
|
||||
|
@ -93,8 +69,6 @@ function stripFragmentFromUrl(url: string): string {
|
|||
return parsed.href;
|
||||
}
|
||||
|
||||
export type Headers = { [key: string]: string };
|
||||
|
||||
export class Request {
|
||||
readonly _routeDelegate: RouteDelegate | null;
|
||||
private _response: Response | null = null;
|
||||
|
@ -107,13 +81,13 @@ export class Request {
|
|||
private _resourceType: string;
|
||||
private _method: string;
|
||||
private _postData: string | null;
|
||||
private _headers: Headers;
|
||||
private _headers: types.Headers;
|
||||
private _frame: frames.Frame;
|
||||
private _waitForResponsePromise: Promise<Response | null>;
|
||||
private _waitForResponsePromiseCallback: (value: Response | null) => void = () => {};
|
||||
|
||||
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(!(routeDelegate && redirectedFrom), 'Should not be able to intercept redirects');
|
||||
this._routeDelegate = routeDelegate;
|
||||
|
@ -243,7 +217,7 @@ export class Route {
|
|||
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!');
|
||||
this._handled = true;
|
||||
if (response.path) {
|
||||
|
@ -257,7 +231,7 @@ export class Route {
|
|||
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!');
|
||||
await this._delegate.continue(overrides);
|
||||
}
|
||||
|
@ -275,10 +249,10 @@ export class Response {
|
|||
private _status: number;
|
||||
private _statusText: string;
|
||||
private _url: string;
|
||||
private _headers: Headers;
|
||||
private _headers: types.Headers;
|
||||
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._status = status;
|
||||
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 {
|
||||
abort(errorCode: string): Promise<void>;
|
||||
fulfill(response: FulfillResponse): Promise<void>;
|
||||
continue(overrides: { method?: string; headers?: Headers; postData?: string; }): Promise<void>;
|
||||
fulfill(response: types.FulfillResponse): 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.
|
||||
|
@ -429,8 +396,8 @@ export const STATUS_TEXTS: { [status: string]: string } = {
|
|||
'511': 'Network Authentication Required',
|
||||
};
|
||||
|
||||
export function verifyHeaders(headers: Headers): Headers {
|
||||
const result: Headers = {};
|
||||
export function verifyHeaders(headers: types.Headers): types.Headers {
|
||||
const result: types.Headers = {};
|
||||
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.`);
|
||||
|
@ -439,7 +406,7 @@ export function verifyHeaders(headers: Headers): Headers {
|
|||
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 lowerCaseToOriginalCase = new Map<string, string>();
|
||||
for (const h of headers) {
|
||||
|
@ -451,7 +418,7 @@ export function mergeHeaders(headers: (Headers | undefined | null)[]): Headers {
|
|||
lowerCaseToValue.set(lower, h[key]);
|
||||
}
|
||||
}
|
||||
const result: Headers = {};
|
||||
const result: types.Headers = {};
|
||||
for (const [lower, value] of lowerCaseToValue)
|
||||
result[lowerCaseToOriginalCase.get(lower)!] = value;
|
||||
return result;
|
||||
|
|
14
src/page.ts
14
src/page.ts
|
@ -84,7 +84,7 @@ type PageState = {
|
|||
viewportSize: types.Size | null;
|
||||
mediaType: types.MediaType | null;
|
||||
colorScheme: types.ColorScheme | null;
|
||||
extraHTTPHeaders: network.Headers | null;
|
||||
extraHTTPHeaders: types.Headers | null;
|
||||
};
|
||||
|
||||
export class Page extends EventEmitter {
|
||||
|
@ -265,7 +265,7 @@ export class Page extends EventEmitter {
|
|||
await this._delegate.exposeBinding(binding);
|
||||
}
|
||||
|
||||
setExtraHTTPHeaders(headers: network.Headers) {
|
||||
setExtraHTTPHeaders(headers: types.Headers) {
|
||||
this._state.extraHTTPHeaders = network.verifyHeaders(headers);
|
||||
return this._delegate.updateExtraHTTPHeaders();
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ export class Page extends EventEmitter {
|
|||
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));
|
||||
}
|
||||
|
||||
|
@ -386,6 +386,10 @@ export class Page extends EventEmitter {
|
|||
|
||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
||||
const source = await helper.evaluationScript(script, arg);
|
||||
await this._addInitScriptExpression(source);
|
||||
}
|
||||
|
||||
async _addInitScriptExpression(source: string) {
|
||||
this._evaluateOnNewDocumentSources.push(source);
|
||||
await this._delegate.evaluateOnNewDocument(source);
|
||||
}
|
||||
|
@ -445,11 +449,11 @@ export class Page extends EventEmitter {
|
|||
return this._attributeToPage(() => this.mainFrame().title());
|
||||
}
|
||||
|
||||
async close(options: { runBeforeUnload: (boolean | undefined); } = {runBeforeUnload: undefined}) {
|
||||
async close(options?: { runBeforeUnload?: boolean }) {
|
||||
if (this._closed)
|
||||
return;
|
||||
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);
|
||||
if (!runBeforeUnload)
|
||||
await this._closedPromise;
|
||||
|
|
|
@ -18,7 +18,7 @@ import * as fs from 'fs';
|
|||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
import { BrowserContext, PersistentContextOptions, verifyProxySettings, validateBrowserContextOptions } from '../browserContext';
|
||||
import { BrowserContext, verifyProxySettings, validateBrowserContextOptions } from '../browserContext';
|
||||
import { BrowserServer } from './browserServer';
|
||||
import * as browserPaths from '../install/browserPaths';
|
||||
import { Loggers, Logger } from '../logger';
|
||||
|
@ -34,41 +34,21 @@ import { TimeoutSettings } from '../timeoutSettings';
|
|||
import { WebSocketServer } from './webSocketServer';
|
||||
import { LoggerSink } from '../loggerSink';
|
||||
|
||||
export type FirefoxUserPrefsOptions = {
|
||||
firefoxUserPrefs?: { [key: string]: string | number | boolean },
|
||||
};
|
||||
type FirefoxPrefsOptions = { 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 = {
|
||||
wsEndpoint: string,
|
||||
slowMo?: number,
|
||||
logger?: LoggerSink,
|
||||
timeout?: number,
|
||||
};
|
||||
export type LaunchOptions = LaunchOptionsBase & { slowMo?: number };
|
||||
type LaunchServerOptions = LaunchOptionsBase & { port?: number };
|
||||
export type LaunchNonPersistentOptions = LaunchOptions & FirefoxPrefsOptions;
|
||||
type LaunchPersistentOptions = LaunchOptions & types.BrowserContextOptions;
|
||||
type LaunchServerOptions = types.LaunchServerOptions & { logger?: LoggerSink } & FirefoxPrefsOptions;
|
||||
|
||||
export interface BrowserType {
|
||||
executablePath(): string;
|
||||
name(): string;
|
||||
launch(options?: LaunchOptions & FirefoxUserPrefsOptions): Promise<Browser>;
|
||||
launchServer(options?: LaunchServerOptions & FirefoxUserPrefsOptions): Promise<BrowserServer>;
|
||||
launchPersistentContext(userDataDir: string, options?: LaunchOptions & PersistentContextOptions): Promise<BrowserContext>;
|
||||
launch(options?: LaunchNonPersistentOptions): Promise<Browser>;
|
||||
launchServer(options?: LaunchServerOptions): Promise<BrowserServer>;
|
||||
launchPersistentContext(userDataDir: string, options?: LaunchPersistentOptions): Promise<BrowserContext>;
|
||||
connect(options: ConnectOptions): Promise<Browser>;
|
||||
}
|
||||
|
||||
|
@ -102,7 +82,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
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).port, 'Cannot specify a port without launching as a server.');
|
||||
options = validateLaunchOptions(options);
|
||||
|
@ -111,7 +91,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
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.');
|
||||
options = validateLaunchOptions(options);
|
||||
const persistent = validateBrowserContextOptions(options);
|
||||
|
@ -120,7 +100,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
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;
|
||||
const { browserServer, downloadsPath, transport } = await this._launchServer(progress, options, !!persistent, logger, userDataDir);
|
||||
if ((options as any).__testHookBeforeCreateBrowser)
|
||||
|
@ -246,7 +226,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
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 _startWebSocketServer(transport: ConnectionTransport, logger: Logger, port: number): WebSocketServer;
|
||||
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;
|
||||
return { ...options, devtools, headless };
|
||||
}
|
||||
|
|
|
@ -21,13 +21,14 @@ import { CRBrowser } from '../chromium/crBrowser';
|
|||
import * as ws from 'ws';
|
||||
import { Env } from './processLauncher';
|
||||
import { kBrowserCloseMessageId } from '../chromium/crConnection';
|
||||
import { LaunchOptionsBase, BrowserTypeBase } from './browserType';
|
||||
import { BrowserTypeBase } from './browserType';
|
||||
import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
|
||||
import { Logger } from '../logger';
|
||||
import { BrowserDescriptor } from '../install/browserPaths';
|
||||
import { CRDevTools } from '../chromium/crDevTools';
|
||||
import { BrowserOptions } from '../browser';
|
||||
import { WebSocketServer } from './webSocketServer';
|
||||
import { LaunchOptionsBase } from '../types';
|
||||
|
||||
export class Chromium extends BrowserTypeBase {
|
||||
private _devtools: CRDevTools | undefined;
|
||||
|
|
|
@ -21,7 +21,7 @@ import * as path from 'path';
|
|||
import * as ws from 'ws';
|
||||
import { FFBrowser } from '../firefox/ffBrowser';
|
||||
import { kBrowserCloseMessageId } from '../firefox/ffConnection';
|
||||
import { LaunchOptionsBase, BrowserTypeBase, FirefoxUserPrefsOptions } from './browserType';
|
||||
import { BrowserTypeBase, LaunchNonPersistentOptions } from './browserType';
|
||||
import { Env } from './processLauncher';
|
||||
import { ConnectionTransport, ProtocolResponse, ProtocolRequest } from '../transport';
|
||||
import { Logger } from '../logger';
|
||||
|
@ -56,7 +56,7 @@ export class Firefox extends BrowserTypeBase {
|
|||
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;
|
||||
if (devtools)
|
||||
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 * as path from 'path';
|
||||
import { kBrowserCloseMessageId } from '../webkit/wkConnection';
|
||||
import { LaunchOptionsBase, BrowserTypeBase } from './browserType';
|
||||
import { BrowserTypeBase } from './browserType';
|
||||
import { ConnectionTransport, ProtocolResponse, ProtocolRequest } from '../transport';
|
||||
import * as ws from 'ws';
|
||||
import { Logger } from '../logger';
|
||||
|
@ -27,6 +27,7 @@ import { BrowserOptions } from '../browser';
|
|||
import { BrowserDescriptor } from '../install/browserPaths';
|
||||
import { WebSocketServer } from './webSocketServer';
|
||||
import { assert } from '../helper';
|
||||
import { LaunchOptionsBase } from '../types';
|
||||
|
||||
export class WebKit extends BrowserTypeBase {
|
||||
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 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.
|
||||
*/
|
||||
|
||||
import { BrowserBase, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, BrowserContextOptions, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import { helper, RegisteredListener, assert } from '../helper';
|
||||
import * as network from '../network';
|
||||
|
@ -202,7 +202,7 @@ export class WKBrowserContext extends BrowserContextBase {
|
|||
readonly _browserContextId: string | undefined;
|
||||
readonly _evaluateOnNewDocumentSources: string[];
|
||||
|
||||
constructor(browser: WKBrowser, browserContextId: string | undefined, options: BrowserContextOptions) {
|
||||
constructor(browser: WKBrowser, browserContextId: string | undefined, options: types.BrowserContextOptions) {
|
||||
super(browser, options);
|
||||
this._browser = browser;
|
||||
this._browserContextId = browserContextId;
|
||||
|
@ -257,17 +257,17 @@ export class WKBrowserContext extends BrowserContextBase {
|
|||
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 });
|
||||
return network.filterCookies(cookies.map((c: network.NetworkCookie) => {
|
||||
return network.filterCookies(cookies.map((c: types.NetworkCookie) => {
|
||||
const copy: any = { ... c };
|
||||
copy.expires = c.expires === -1 ? -1 : c.expires / 1000;
|
||||
delete copy.session;
|
||||
return copy as network.NetworkCookie;
|
||||
return copy as types.NetworkCookie;
|
||||
}), urls);
|
||||
}
|
||||
|
||||
async addCookies(cookies: network.SetNetworkCookieParam[]) {
|
||||
async addCookies(cookies: types.SetNetworkCookieParam[]) {
|
||||
const cc = network.rewriteCookies(cookies).map(c => ({
|
||||
...c,
|
||||
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 });
|
||||
}
|
||||
|
||||
async setExtraHTTPHeaders(headers: network.Headers): Promise<void> {
|
||||
async setExtraHTTPHeaders(headers: types.Headers): Promise<void> {
|
||||
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
|
||||
for (const page of this.pages())
|
||||
await (page._delegate as WKPage).updateExtraHTTPHeaders();
|
||||
|
@ -314,8 +314,7 @@ export class WKBrowserContext extends BrowserContextBase {
|
|||
await (page._delegate as WKPage).updateHttpCredentials();
|
||||
}
|
||||
|
||||
async addInitScript(script: Function | string | { path?: string, content?: string }, arg?: any) {
|
||||
const source = await helper.evaluationScript(script, arg);
|
||||
async _doAddInitScript(source: string) {
|
||||
this._evaluateOnNewDocumentSources.push(source);
|
||||
for (const page of this.pages())
|
||||
await (page._delegate as WKPage)._updateBootstrapScript();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
import * as frames from '../frames';
|
||||
import { assert, helper } from '../helper';
|
||||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
import { Protocol } from './protocol';
|
||||
import { WKSession } from './wkConnection';
|
||||
|
||||
|
@ -65,7 +66,7 @@ export class WKInterceptableRequest implements network.RouteDelegate {
|
|||
await this._session.sendMayFail('Network.interceptRequestWithError', { requestId: this._requestId, errorType });
|
||||
}
|
||||
|
||||
async fulfill(response: network.FulfillResponse) {
|
||||
async fulfill(response: types.FulfillResponse) {
|
||||
await this._interceptedPromise;
|
||||
|
||||
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;
|
||||
// In certain cases, protocol will return error if the request was already canceled
|
||||
// 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 {
|
||||
const result: network.Headers = {};
|
||||
function headersObject(headers: Protocol.Network.Headers): types.Headers {
|
||||
const result: types.Headers = {};
|
||||
for (const key of Object.keys(headers))
|
||||
result[key.toLowerCase()] = headers[key];
|
||||
return result;
|
||||
|
|
|
@ -552,7 +552,7 @@ export class WKPage implements PageDelegate {
|
|||
await this._updateState('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() });
|
||||
}
|
||||
|
||||
_calculateExtraHTTPHeaders(): network.Headers {
|
||||
_calculateExtraHTTPHeaders(): types.Headers {
|
||||
const headers = network.mergeHeaders([
|
||||
this._browserContext._options.extraHTTPHeaders,
|
||||
this._page._state.extraHTTPHeaders
|
||||
|
|
Загрузка…
Ссылка в новой задаче