chore: reuse BrowserContext across browsers (#201)
This commit is contained in:
parent
0af3b9dfc8
commit
5ffb710d7d
|
|
@ -359,8 +359,8 @@ export const macEditingCommands: {[key: string]: string|string[]} = {
|
||||||
'Alt+Tab': 'insertTabIgnoringFieldEditor:',
|
'Alt+Tab': 'insertTabIgnoringFieldEditor:',
|
||||||
'Alt+Enter': 'insertNewlineIgnoringFieldEditor:',
|
'Alt+Enter': 'insertNewlineIgnoringFieldEditor:',
|
||||||
'Alt+Escape': 'complete:',
|
'Alt+Escape': 'complete:',
|
||||||
"Alt+ArrowUp": ['moveBackward:', 'moveToBeginningOfParagraph:'],
|
'Alt+ArrowUp': ['moveBackward:', 'moveToBeginningOfParagraph:'],
|
||||||
"Alt+ArrowDown": ['moveForward:', 'moveToEndOfParagraph:'],
|
'Alt+ArrowDown': ['moveForward:', 'moveToEndOfParagraph:'],
|
||||||
'Alt+ArrowLeft': 'moveWordLeft:',
|
'Alt+ArrowLeft': 'moveWordLeft:',
|
||||||
'Alt+ArrowRight': 'moveWordRight:',
|
'Alt+ArrowRight': 'moveWordRight:',
|
||||||
'Alt+Delete': 'deleteWordForward:',
|
'Alt+Delete': 'deleteWordForward:',
|
||||||
|
|
|
||||||
74
src/browserContext.ts
Normal file
74
src/browserContext.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { assert } from './helper';
|
||||||
|
import { Page } from './page';
|
||||||
|
import * as network from './network';
|
||||||
|
|
||||||
|
export interface BrowserDelegate<Browser> {
|
||||||
|
contextPages(): Promise<Page<Browser>[]>;
|
||||||
|
createPageInContext(): Promise<Page<Browser>>;
|
||||||
|
closeContext(): Promise<void>;
|
||||||
|
getContextCookies(): Promise<network.NetworkCookie[]>;
|
||||||
|
clearContextCookies(): Promise<void>;
|
||||||
|
setContextCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BrowserContext<Browser> {
|
||||||
|
private readonly _delegate: BrowserDelegate<Browser>;
|
||||||
|
private readonly _browser: Browser;
|
||||||
|
private readonly _isIncognito: boolean;
|
||||||
|
|
||||||
|
constructor(delegate: BrowserDelegate<Browser>, browser: Browser, isIncognito: boolean) {
|
||||||
|
this._delegate = delegate;
|
||||||
|
this._browser = browser;
|
||||||
|
this._isIncognito = isIncognito;
|
||||||
|
}
|
||||||
|
|
||||||
|
async pages(): Promise<Page<Browser>[]> {
|
||||||
|
return this._delegate.contextPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
isIncognito(): boolean {
|
||||||
|
return this._isIncognito;
|
||||||
|
}
|
||||||
|
|
||||||
|
async newPage(): Promise<Page<Browser>> {
|
||||||
|
return this._delegate.createPageInContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
browser(): Browser {
|
||||||
|
return this._browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
async cookies(...urls: string[]): Promise<network.NetworkCookie[]> {
|
||||||
|
return network.filterCookies(await this._delegate.getContextCookies(), urls);
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearCookies() {
|
||||||
|
await this._delegate.clearContextCookies();
|
||||||
|
}
|
||||||
|
|
||||||
|
async setCookies(cookies: network.SetNetworkCookieParam[]) {
|
||||||
|
await this._delegate.setContextCookies(network.rewriteCookies(cookies));
|
||||||
|
}
|
||||||
|
|
||||||
|
async close() {
|
||||||
|
assert(this._isIncognito, 'Non-incognito profiles cannot be closed!');
|
||||||
|
await this._delegate.closeContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ import * as childProcess from 'child_process';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { assert, helper } from '../helper';
|
import { assert, helper } from '../helper';
|
||||||
import { BrowserContext } from './BrowserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
import { Connection, ConnectionEvents, CDPSession } from './Connection';
|
import { Connection, ConnectionEvents, CDPSession } from './Connection';
|
||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { Target } from './Target';
|
import { Target } from './Target';
|
||||||
|
|
@ -27,6 +27,8 @@ import { Protocol } from './protocol';
|
||||||
import { Chromium } from './features/chromium';
|
import { Chromium } from './features/chromium';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { FrameManager } from './FrameManager';
|
import { FrameManager } from './FrameManager';
|
||||||
|
import * as network from '../network';
|
||||||
|
import { Permissions } from './features/permissions';
|
||||||
|
|
||||||
export class Browser extends EventEmitter {
|
export class Browser extends EventEmitter {
|
||||||
private _ignoreHTTPSErrors: boolean;
|
private _ignoreHTTPSErrors: boolean;
|
||||||
|
|
@ -35,8 +37,8 @@ export class Browser extends EventEmitter {
|
||||||
_connection: Connection;
|
_connection: Connection;
|
||||||
_client: CDPSession;
|
_client: CDPSession;
|
||||||
private _closeCallback: () => Promise<void>;
|
private _closeCallback: () => Promise<void>;
|
||||||
private _defaultContext: BrowserContext;
|
private _defaultContext: BrowserContext<Browser>;
|
||||||
private _contexts = new Map<string, BrowserContext>();
|
private _contexts = new Map<string, BrowserContext<Browser>>();
|
||||||
_targets = new Map<string, Target>();
|
_targets = new Map<string, Target>();
|
||||||
readonly chromium: Chromium;
|
readonly chromium: Chromium;
|
||||||
|
|
||||||
|
|
@ -68,9 +70,9 @@ export class Browser extends EventEmitter {
|
||||||
this._closeCallback = closeCallback || (() => Promise.resolve());
|
this._closeCallback = closeCallback || (() => Promise.resolve());
|
||||||
this.chromium = new Chromium(this);
|
this.chromium = new Chromium(this);
|
||||||
|
|
||||||
this._defaultContext = new BrowserContext(this._client, this, null);
|
this._defaultContext = this._createBrowserContext(null);
|
||||||
for (const contextId of contextIds)
|
for (const contextId of contextIds)
|
||||||
this._contexts.set(contextId, new BrowserContext(this._client, this, contextId));
|
this._contexts.set(contextId, this._createBrowserContext(contextId));
|
||||||
|
|
||||||
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
||||||
this._client.on('Target.targetCreated', this._targetCreated.bind(this));
|
this._client.on('Target.targetCreated', this._targetCreated.bind(this));
|
||||||
|
|
@ -78,30 +80,68 @@ export class Browser extends EventEmitter {
|
||||||
this._client.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
|
this._client.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_createBrowserContext(contextId: string | null): BrowserContext<Browser> {
|
||||||
|
const isIncognito = !!contextId;
|
||||||
|
const context = new BrowserContext({
|
||||||
|
contextPages: async (): Promise<Page<Browser>[]> => {
|
||||||
|
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
||||||
|
const pages = await Promise.all(targets.map(target => target.page()));
|
||||||
|
return pages.filter(page => !!page);
|
||||||
|
},
|
||||||
|
|
||||||
|
createPageInContext: async (): Promise<Page<Browser>> => {
|
||||||
|
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
|
||||||
|
const target = this._targets.get(targetId);
|
||||||
|
assert(await target._initializedPromise, 'Failed to create target for page');
|
||||||
|
const page = await target.page();
|
||||||
|
return page;
|
||||||
|
},
|
||||||
|
|
||||||
|
closeContext: async (): Promise<void> => {
|
||||||
|
await this._client.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
|
||||||
|
this._contexts.delete(contextId);
|
||||||
|
},
|
||||||
|
|
||||||
|
getContextCookies: async (): Promise<network.NetworkCookie[]> => {
|
||||||
|
const { cookies } = await this._client.send('Storage.getCookies', { browserContextId: contextId || undefined });
|
||||||
|
return cookies.map(c => {
|
||||||
|
const copy: any = { sameSite: 'None', ...c };
|
||||||
|
delete copy.size;
|
||||||
|
return copy as network.NetworkCookie;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearContextCookies: async (): Promise<void> => {
|
||||||
|
await this._client.send('Storage.clearCookies', { browserContextId: contextId || undefined });
|
||||||
|
},
|
||||||
|
|
||||||
|
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
|
||||||
|
await this._client.send('Storage.setCookies', { cookies, browserContextId: contextId || undefined });
|
||||||
|
},
|
||||||
|
}, this, isIncognito);
|
||||||
|
(context as any).permissions = new Permissions(this._client, contextId);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
process(): childProcess.ChildProcess | null {
|
process(): childProcess.ChildProcess | null {
|
||||||
return this._process;
|
return this._process;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createIncognitoBrowserContext(): Promise<BrowserContext> {
|
async createIncognitoBrowserContext(): Promise<BrowserContext<Browser>> {
|
||||||
const {browserContextId} = await this._client.send('Target.createBrowserContext');
|
const {browserContextId} = await this._client.send('Target.createBrowserContext');
|
||||||
const context = new BrowserContext(this._client, this, browserContextId);
|
const context = this._createBrowserContext(browserContextId);
|
||||||
this._contexts.set(browserContextId, context);
|
this._contexts.set(browserContextId, context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContexts(): BrowserContext[] {
|
browserContexts(): BrowserContext<Browser>[] {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultBrowserContext(): BrowserContext {
|
defaultBrowserContext(): BrowserContext<Browser> {
|
||||||
return this._defaultContext;
|
return this._defaultContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _disposeContext(contextId: string | null) {
|
|
||||||
await this._client.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
|
|
||||||
this._contexts.delete(contextId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _targetCreated(event: Protocol.Target.targetCreatedPayload) {
|
async _targetCreated(event: Protocol.Target.targetCreatedPayload) {
|
||||||
const targetInfo = event.targetInfo;
|
const targetInfo = event.targetInfo;
|
||||||
const {browserContextId} = targetInfo;
|
const {browserContextId} = targetInfo;
|
||||||
|
|
@ -134,19 +174,11 @@ export class Browser extends EventEmitter {
|
||||||
this.chromium.emit(Events.Chromium.TargetChanged, target);
|
this.chromium.emit(Events.Chromium.TargetChanged, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
async newPage(): Promise<Page<Browser, BrowserContext>> {
|
async newPage(): Promise<Page<Browser>> {
|
||||||
return this._defaultContext.newPage();
|
return this._defaultContext.newPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createPageInContext(contextId: string | null): Promise<Page<Browser, BrowserContext>> {
|
async _closePage(page: Page<Browser>) {
|
||||||
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
|
|
||||||
const target = this._targets.get(targetId);
|
|
||||||
assert(await target._initializedPromise, 'Failed to create target for page');
|
|
||||||
const page = await target.page();
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _closePage(page: Page<Browser, BrowserContext>) {
|
|
||||||
await this._client.send('Target.closeTarget', { targetId: Target.fromPage(page)._targetId });
|
await this._client.send('Target.closeTarget', { targetId: Target.fromPage(page)._targetId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,13 +186,7 @@ export class Browser extends EventEmitter {
|
||||||
return Array.from(this._targets.values()).filter(target => target._isInitialized);
|
return Array.from(this._targets.values()).filter(target => target._isInitialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _pages(context: BrowserContext): Promise<Page<Browser, BrowserContext>[]> {
|
async _activatePage(page: Page<Browser>) {
|
||||||
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
|
||||||
const pages = await Promise.all(targets.map(target => target.page()));
|
|
||||||
return pages.filter(page => !!page);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _activatePage(page: Page<Browser, BrowserContext>) {
|
|
||||||
await (page._delegate as FrameManager)._client.send('Target.activateTarget', {targetId: Target.fromPage(page)._targetId});
|
await (page._delegate as FrameManager)._client.send('Target.activateTarget', {targetId: Target.fromPage(page)._targetId});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +216,7 @@ export class Browser extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async pages(): Promise<Page<Browser, BrowserContext>[]> {
|
async pages(): Promise<Page<Browser>[]> {
|
||||||
const contextPages = await Promise.all(this.browserContexts().map(context => context.pages()));
|
const contextPages = await Promise.all(this.browserContexts().map(context => context.pages()));
|
||||||
// Flatten array.
|
// Flatten array.
|
||||||
return contextPages.reduce((acc, x) => acc.concat(x), []);
|
return contextPages.reduce((acc, x) => acc.concat(x), []);
|
||||||
|
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2017 Google Inc. All rights reserved.
|
|
||||||
* Modifications copyright (c) Microsoft Corporation.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { assert } from '../helper';
|
|
||||||
import { filterCookies, NetworkCookie, rewriteCookies, SetNetworkCookieParam } from '../network';
|
|
||||||
import { Browser } from './Browser';
|
|
||||||
import { CDPSession } from './Connection';
|
|
||||||
import { Permissions } from './features/permissions';
|
|
||||||
import { Page } from '../page';
|
|
||||||
|
|
||||||
export class BrowserContext {
|
|
||||||
readonly permissions: Permissions;
|
|
||||||
|
|
||||||
private _browser: Browser;
|
|
||||||
private _id: string;
|
|
||||||
|
|
||||||
constructor(client: CDPSession, browser: Browser, contextId: string | null) {
|
|
||||||
this._browser = browser;
|
|
||||||
this._id = contextId;
|
|
||||||
this.permissions = new Permissions(client, contextId);
|
|
||||||
}
|
|
||||||
|
|
||||||
pages(): Promise<Page<Browser, BrowserContext>[]> {
|
|
||||||
return this._browser._pages(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
isIncognito(): boolean {
|
|
||||||
return !!this._id;
|
|
||||||
}
|
|
||||||
|
|
||||||
newPage(): Promise<Page<Browser, BrowserContext>> {
|
|
||||||
return this._browser._createPageInContext(this._id);
|
|
||||||
}
|
|
||||||
|
|
||||||
browser(): Browser {
|
|
||||||
return this._browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
async cookies(...urls: string[]): Promise<NetworkCookie[]> {
|
|
||||||
const { cookies } = await this._browser._client.send('Storage.getCookies', { browserContextId: this._id || undefined });
|
|
||||||
return filterCookies(cookies.map(c => {
|
|
||||||
const copy: any = { sameSite: 'None', ...c };
|
|
||||||
delete copy.size;
|
|
||||||
return copy as NetworkCookie;
|
|
||||||
}), urls);
|
|
||||||
}
|
|
||||||
|
|
||||||
async clearCookies() {
|
|
||||||
await this._browser._client.send('Storage.clearCookies', { browserContextId: this._id || undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
async setCookies(cookies: SetNetworkCookieParam[]) {
|
|
||||||
cookies = rewriteCookies(cookies);
|
|
||||||
await this._browser._client.send('Storage.setCookies', { cookies, browserContextId: this._id || undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
async close() {
|
|
||||||
assert(this._id, 'Non-incognito profiles cannot be closed!');
|
|
||||||
await this._browser._disposeContext(this._id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -41,7 +41,7 @@ import { Workers } from './features/workers';
|
||||||
import { Overrides } from './features/overrides';
|
import { Overrides } from './features/overrides';
|
||||||
import { Interception } from './features/interception';
|
import { Interception } from './features/interception';
|
||||||
import { Browser } from './Browser';
|
import { Browser } from './Browser';
|
||||||
import { BrowserContext } from './BrowserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import * as input from '../input';
|
import * as input from '../input';
|
||||||
import { ConsoleMessage } from '../console';
|
import { ConsoleMessage } from '../console';
|
||||||
|
|
@ -64,7 +64,7 @@ type FrameData = {
|
||||||
|
|
||||||
export class FrameManager extends EventEmitter implements frames.FrameDelegate, PageDelegate {
|
export class FrameManager extends EventEmitter implements frames.FrameDelegate, PageDelegate {
|
||||||
_client: CDPSession;
|
_client: CDPSession;
|
||||||
private _page: Page<Browser, BrowserContext>;
|
private _page: Page<Browser>;
|
||||||
private _networkManager: NetworkManager;
|
private _networkManager: NetworkManager;
|
||||||
private _frames = new Map<string, frames.Frame>();
|
private _frames = new Map<string, frames.Frame>();
|
||||||
private _contextIdToContext = new Map<number, js.ExecutionContext>();
|
private _contextIdToContext = new Map<number, js.ExecutionContext>();
|
||||||
|
|
@ -74,7 +74,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||||
rawKeyboard: RawKeyboardImpl;
|
rawKeyboard: RawKeyboardImpl;
|
||||||
screenshotterDelegate: CRScreenshotDelegate;
|
screenshotterDelegate: CRScreenshotDelegate;
|
||||||
|
|
||||||
constructor(client: CDPSession, browserContext: BrowserContext, ignoreHTTPSErrors: boolean) {
|
constructor(client: CDPSession, browserContext: BrowserContext<Browser>, ignoreHTTPSErrors: boolean) {
|
||||||
super();
|
super();
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this.rawKeyboard = new RawKeyboardImpl(client);
|
this.rawKeyboard = new RawKeyboardImpl(client);
|
||||||
|
|
@ -254,7 +254,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||||
this._handleFrameTree(child);
|
this._handleFrameTree(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
page(): Page<Browser, BrowserContext> {
|
page(): Page<Browser> {
|
||||||
return this._page;
|
return this._page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { Browser } from './Browser';
|
import { Browser } from './Browser';
|
||||||
import { BrowserContext } from './BrowserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
import { CDPSession, CDPSessionEvents } from './Connection';
|
import { CDPSession, CDPSessionEvents } from './Connection';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { Worker } from './features/workers';
|
import { Worker } from './features/workers';
|
||||||
|
|
@ -30,25 +30,25 @@ const targetSymbol = Symbol('target');
|
||||||
|
|
||||||
export class Target {
|
export class Target {
|
||||||
private _targetInfo: Protocol.Target.TargetInfo;
|
private _targetInfo: Protocol.Target.TargetInfo;
|
||||||
private _browserContext: BrowserContext;
|
private _browserContext: BrowserContext<Browser>;
|
||||||
_targetId: string;
|
_targetId: string;
|
||||||
private _sessionFactory: () => Promise<CDPSession>;
|
private _sessionFactory: () => Promise<CDPSession>;
|
||||||
private _ignoreHTTPSErrors: boolean;
|
private _ignoreHTTPSErrors: boolean;
|
||||||
private _defaultViewport: types.Viewport;
|
private _defaultViewport: types.Viewport;
|
||||||
private _pagePromise: Promise<Page<Browser, BrowserContext>> | null = null;
|
private _pagePromise: Promise<Page<Browser>> | null = null;
|
||||||
private _page: Page<Browser, BrowserContext> | null = null;
|
private _page: Page<Browser> | null = null;
|
||||||
private _workerPromise: Promise<Worker> | null = null;
|
private _workerPromise: Promise<Worker> | null = null;
|
||||||
_initializedPromise: Promise<boolean>;
|
_initializedPromise: Promise<boolean>;
|
||||||
_initializedCallback: (value?: unknown) => void;
|
_initializedCallback: (value?: unknown) => void;
|
||||||
_isInitialized: boolean;
|
_isInitialized: boolean;
|
||||||
|
|
||||||
static fromPage(page: Page<Browser, BrowserContext>): Target {
|
static fromPage(page: Page<Browser>): Target {
|
||||||
return (page as any)[targetSymbol];
|
return (page as any)[targetSymbol];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
targetInfo: Protocol.Target.TargetInfo,
|
targetInfo: Protocol.Target.TargetInfo,
|
||||||
browserContext: BrowserContext,
|
browserContext: BrowserContext<Browser>,
|
||||||
sessionFactory: () => Promise<CDPSession>,
|
sessionFactory: () => Promise<CDPSession>,
|
||||||
ignoreHTTPSErrors: boolean,
|
ignoreHTTPSErrors: boolean,
|
||||||
defaultViewport: types.Viewport | null) {
|
defaultViewport: types.Viewport | null) {
|
||||||
|
|
@ -81,7 +81,7 @@ export class Target {
|
||||||
this._page._didClose();
|
this._page._didClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
async page(): Promise<Page<Browser, BrowserContext> | null> {
|
async page(): Promise<Page<Browser> | null> {
|
||||||
if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) {
|
if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) {
|
||||||
this._pagePromise = this._sessionFactory().then(async client => {
|
this._pagePromise = this._sessionFactory().then(async client => {
|
||||||
const frameManager = new FrameManager(client, this._browserContext, this._ignoreHTTPSErrors);
|
const frameManager = new FrameManager(client, this._browserContext, this._ignoreHTTPSErrors);
|
||||||
|
|
@ -131,7 +131,7 @@ export class Target {
|
||||||
return this._browserContext.browser();
|
return this._browserContext.browser();
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContext(): BrowserContext {
|
browserContext(): BrowserContext<Browser> {
|
||||||
return this._browserContext;
|
return this._browserContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export { Keyboard, Mouse } from '../input';
|
||||||
export { ExecutionContext, JSHandle } from '../javascript';
|
export { ExecutionContext, JSHandle } from '../javascript';
|
||||||
export { Request, Response } from '../network';
|
export { Request, Response } from '../network';
|
||||||
export { Browser } from './Browser';
|
export { Browser } from './Browser';
|
||||||
export { BrowserContext } from './BrowserContext';
|
export { BrowserContext } from '../browserContext';
|
||||||
export { BrowserFetcher } from '../browserFetcher';
|
export { BrowserFetcher } from '../browserFetcher';
|
||||||
export { CDPSession } from './Connection';
|
export { CDPSession } from './Connection';
|
||||||
export { Accessibility } from './features/accessibility';
|
export { Accessibility } from './features/accessibility';
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { assert } from '../../helper';
|
import { assert } from '../../helper';
|
||||||
import { Browser } from '../Browser';
|
import { Browser } from '../Browser';
|
||||||
import { BrowserContext } from '../BrowserContext';
|
import { BrowserContext } from '../../browserContext';
|
||||||
import { CDPSession, Connection } from '../Connection';
|
import { CDPSession, Connection } from '../Connection';
|
||||||
import { Page } from '../../page';
|
import { Page } from '../../page';
|
||||||
import { readProtocolStream } from '../protocolHelper';
|
import { readProtocolStream } from '../protocolHelper';
|
||||||
|
|
@ -48,7 +48,7 @@ export class Chromium extends EventEmitter {
|
||||||
return target._worker();
|
return target._worker();
|
||||||
}
|
}
|
||||||
|
|
||||||
async startTracing(page: Page<Browser, BrowserContext> | undefined, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) {
|
async startTracing(page: Page<Browser> | undefined, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) {
|
||||||
assert(!this._recording, 'Cannot start recording trace while already recording trace.');
|
assert(!this._recording, 'Cannot start recording trace while already recording trace.');
|
||||||
this._tracingClient = page ? (page._delegate as FrameManager)._client : this._client;
|
this._tracingClient = page ? (page._delegate as FrameManager)._client : this._client;
|
||||||
|
|
||||||
|
|
@ -87,12 +87,12 @@ export class Chromium extends EventEmitter {
|
||||||
return contentPromise;
|
return contentPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
targets(context?: BrowserContext): Target[] {
|
targets(context?: BrowserContext<Browser>): Target[] {
|
||||||
const targets = this._browser._allTargets();
|
const targets = this._browser._allTargets();
|
||||||
return context ? targets.filter(t => t.browserContext() === context) : targets;
|
return context ? targets.filter(t => t.browserContext() === context) : targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
pageTarget(page: Page<Browser, BrowserContext>): Target {
|
pageTarget(page: Page<Browser>): Target {
|
||||||
return Target.fromPage(page);
|
return Target.fromPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { assert, helper, RegisteredListener } from '../helper';
|
import { helper, RegisteredListener } from '../helper';
|
||||||
import { filterCookies, NetworkCookie, SetNetworkCookieParam, rewriteCookies } from '../network';
|
|
||||||
import { Connection, ConnectionEvents, JugglerSessionEvents } from './Connection';
|
import { Connection, ConnectionEvents, JugglerSessionEvents } from './Connection';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { Events as CommonEvents } from '../events';
|
import { Events as CommonEvents } from '../events';
|
||||||
|
|
@ -25,6 +24,8 @@ import { Permissions } from './features/permissions';
|
||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { FrameManager } from './FrameManager';
|
import { FrameManager } from './FrameManager';
|
||||||
|
import * as network from '../network';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
|
|
||||||
export class Browser extends EventEmitter {
|
export class Browser extends EventEmitter {
|
||||||
private _connection: Connection;
|
private _connection: Connection;
|
||||||
|
|
@ -32,8 +33,8 @@ export class Browser extends EventEmitter {
|
||||||
private _process: import('child_process').ChildProcess;
|
private _process: import('child_process').ChildProcess;
|
||||||
private _closeCallback: () => void;
|
private _closeCallback: () => void;
|
||||||
_targets: Map<string, Target>;
|
_targets: Map<string, Target>;
|
||||||
private _defaultContext: BrowserContext;
|
private _defaultContext: BrowserContext<Browser>;
|
||||||
private _contexts: Map<string, BrowserContext>;
|
private _contexts: Map<string, BrowserContext<Browser>>;
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
|
|
||||||
static async create(connection: Connection, defaultViewport: types.Viewport | null, process: import('child_process').ChildProcess | null, closeCallback: () => void) {
|
static async create(connection: Connection, defaultViewport: types.Viewport | null, process: import('child_process').ChildProcess | null, closeCallback: () => void) {
|
||||||
|
|
@ -52,10 +53,10 @@ export class Browser extends EventEmitter {
|
||||||
|
|
||||||
this._targets = new Map();
|
this._targets = new Map();
|
||||||
|
|
||||||
this._defaultContext = new BrowserContext(this._connection, this, null);
|
this._defaultContext = this._createBrowserContext(null);
|
||||||
this._contexts = new Map();
|
this._contexts = new Map();
|
||||||
for (const browserContextId of browserContextIds)
|
for (const browserContextId of browserContextIds)
|
||||||
this._contexts.set(browserContextId, new BrowserContext(this._connection, this, browserContextId));
|
this._contexts.set(browserContextId, this._createBrowserContext(browserContextId));
|
||||||
|
|
||||||
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
||||||
|
|
||||||
|
|
@ -74,14 +75,14 @@ export class Browser extends EventEmitter {
|
||||||
return !this._connection._closed;
|
return !this._connection._closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createIncognitoBrowserContext(): Promise<BrowserContext> {
|
async createIncognitoBrowserContext(): Promise<BrowserContext<Browser>> {
|
||||||
const {browserContextId} = await this._connection.send('Target.createBrowserContext');
|
const {browserContextId} = await this._connection.send('Target.createBrowserContext');
|
||||||
const context = new BrowserContext(this._connection, this, browserContextId);
|
const context = this._createBrowserContext(browserContextId);
|
||||||
this._contexts.set(browserContextId, context);
|
this._contexts.set(browserContextId, context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContexts(): Array<BrowserContext> {
|
browserContexts(): Array<BrowserContext<Browser>> {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,11 +90,6 @@ export class Browser extends EventEmitter {
|
||||||
return this._defaultContext;
|
return this._defaultContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _disposeContext(browserContextId) {
|
|
||||||
await this._connection.send('Target.removeBrowserContext', {browserContextId});
|
|
||||||
this._contexts.delete(browserContextId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async userAgent(): Promise<string> {
|
async userAgent(): Promise<string> {
|
||||||
const info = await this._connection.send('Browser.getInfo');
|
const info = await this._connection.send('Browser.getInfo');
|
||||||
return info.userAgent;
|
return info.userAgent;
|
||||||
|
|
@ -132,16 +128,8 @@ export class Browser extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newPage(): Promise<Page<Browser, BrowserContext>> {
|
newPage(): Promise<Page<Browser>> {
|
||||||
return this._createPageInContext(this._defaultContext._browserContextId);
|
return this._defaultContext.newPage();
|
||||||
}
|
|
||||||
|
|
||||||
async _createPageInContext(browserContextId: string | null): Promise<Page<Browser, BrowserContext>> {
|
|
||||||
const {targetId} = await this._connection.send('Target.newPage', {
|
|
||||||
browserContextId: browserContextId || undefined
|
|
||||||
});
|
|
||||||
const target = this._targets.get(targetId);
|
|
||||||
return await target.page();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async pages() {
|
async pages() {
|
||||||
|
|
@ -153,12 +141,6 @@ export class Browser extends EventEmitter {
|
||||||
return Array.from(this._targets.values());
|
return Array.from(this._targets.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
async _pages(context: BrowserContext): Promise<Page<Browser, BrowserContext>[]> {
|
|
||||||
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
|
||||||
const pages = await Promise.all(targets.map(target => target.page()));
|
|
||||||
return pages.filter(page => !!page);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _onTargetCreated({targetId, url, browserContextId, openerId, type}) {
|
async _onTargetCreated({targetId, url, browserContextId, openerId, type}) {
|
||||||
const context = browserContextId ? this._contexts.get(browserContextId) : this._defaultContext;
|
const context = browserContextId ? this._contexts.get(browserContextId) : this._defaultContext;
|
||||||
const target = new Target(this._connection, this, context, targetId, type, url, openerId);
|
const target = new Target(this._connection, this, context, targetId, type, url, openerId);
|
||||||
|
|
@ -187,20 +169,63 @@ export class Browser extends EventEmitter {
|
||||||
helper.removeEventListeners(this._eventListeners);
|
helper.removeEventListeners(this._eventListeners);
|
||||||
this._closeCallback();
|
this._closeCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_createBrowserContext(browserContextId: string | null): BrowserContext<Browser> {
|
||||||
|
const isIncognito = !!browserContextId;
|
||||||
|
const context = new BrowserContext({
|
||||||
|
contextPages: async (): Promise<Page<Browser>[]> => {
|
||||||
|
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
||||||
|
const pages = await Promise.all(targets.map(target => target.page()));
|
||||||
|
return pages.filter(page => !!page);
|
||||||
|
},
|
||||||
|
|
||||||
|
createPageInContext: async (): Promise<Page<Browser>> => {
|
||||||
|
const {targetId} = await this._connection.send('Target.newPage', {
|
||||||
|
browserContextId: browserContextId || undefined
|
||||||
|
});
|
||||||
|
const target = this._targets.get(targetId);
|
||||||
|
return await target.page();
|
||||||
|
},
|
||||||
|
|
||||||
|
closeContext: async (): Promise<void> => {
|
||||||
|
await this._connection.send('Target.removeBrowserContext', { browserContextId });
|
||||||
|
this._contexts.delete(browserContextId);
|
||||||
|
},
|
||||||
|
|
||||||
|
getContextCookies: async (): Promise<network.NetworkCookie[]> => {
|
||||||
|
const { cookies } = await this._connection.send('Browser.getCookies', { browserContextId: browserContextId || undefined });
|
||||||
|
return cookies.map(c => {
|
||||||
|
const copy: any = { ... c };
|
||||||
|
delete copy.size;
|
||||||
|
return copy as network.NetworkCookie;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearContextCookies: async (): Promise<void> => {
|
||||||
|
await this._connection.send('Browser.clearCookies', { browserContextId: browserContextId || undefined });
|
||||||
|
},
|
||||||
|
|
||||||
|
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
|
||||||
|
await this._connection.send('Browser.setCookies', { browserContextId: browserContextId || undefined, cookies });
|
||||||
|
},
|
||||||
|
}, this, isIncognito);
|
||||||
|
(context as any).permissions = new Permissions(this._connection, browserContextId);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Target {
|
export class Target {
|
||||||
_pagePromise?: Promise<Page<Browser, BrowserContext>>;
|
_pagePromise?: Promise<Page<Browser>>;
|
||||||
private _page: Page<Browser, BrowserContext> | null = null;
|
private _page: Page<Browser> | null = null;
|
||||||
private _browser: Browser;
|
private _browser: Browser;
|
||||||
_context: BrowserContext;
|
_context: BrowserContext<Browser>;
|
||||||
private _connection: Connection;
|
private _connection: Connection;
|
||||||
private _targetId: string;
|
private _targetId: string;
|
||||||
private _type: 'page' | 'browser';
|
private _type: 'page' | 'browser';
|
||||||
_url: string;
|
_url: string;
|
||||||
private _openerId: string;
|
private _openerId: string;
|
||||||
|
|
||||||
constructor(connection: any, browser: Browser, context: BrowserContext, targetId: string, type: 'page' | 'browser', url: string, openerId: string | undefined) {
|
constructor(connection: any, browser: Browser, context: BrowserContext<Browser>, targetId: string, type: 'page' | 'browser', url: string, openerId: string | undefined) {
|
||||||
this._browser = browser;
|
this._browser = browser;
|
||||||
this._context = context;
|
this._context = context;
|
||||||
this._connection = connection;
|
this._connection = connection;
|
||||||
|
|
@ -227,11 +252,11 @@ export class Target {
|
||||||
return this._url;
|
return this._url;
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContext(): BrowserContext {
|
browserContext(): BrowserContext<Browser> {
|
||||||
return this._context;
|
return this._context;
|
||||||
}
|
}
|
||||||
|
|
||||||
page(): Promise<Page<Browser, BrowserContext>> {
|
page(): Promise<Page<Browser>> {
|
||||||
if (this._type === 'page' && !this._pagePromise) {
|
if (this._type === 'page' && !this._pagePromise) {
|
||||||
this._pagePromise = new Promise(async f => {
|
this._pagePromise = new Promise(async f => {
|
||||||
const session = await this._connection.createSession(this._targetId);
|
const session = await this._connection.createSession(this._targetId);
|
||||||
|
|
@ -252,64 +277,3 @@ export class Target {
|
||||||
return this._browser;
|
return this._browser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BrowserContext {
|
|
||||||
_connection: Connection;
|
|
||||||
_browser: Browser;
|
|
||||||
_browserContextId: string;
|
|
||||||
readonly permissions: Permissions;
|
|
||||||
|
|
||||||
constructor(connection: Connection, browser: Browser, browserContextId: string | null) {
|
|
||||||
this._connection = connection;
|
|
||||||
this._browser = browser;
|
|
||||||
this._browserContextId = browserContextId;
|
|
||||||
this.permissions = new Permissions(connection, browserContextId);
|
|
||||||
}
|
|
||||||
|
|
||||||
pages(): Promise<Page<Browser, BrowserContext>[]> {
|
|
||||||
return this._browser._pages(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
isIncognito(): boolean {
|
|
||||||
return !!this._browserContextId;
|
|
||||||
}
|
|
||||||
|
|
||||||
newPage() {
|
|
||||||
return this._browser._createPageInContext(this._browserContextId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
browser(): Browser {
|
|
||||||
return this._browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
async cookies(...urls: string[]): Promise<NetworkCookie[]> {
|
|
||||||
const { cookies } = await this._connection.send('Browser.getCookies', {
|
|
||||||
browserContextId: this._browserContextId || undefined
|
|
||||||
});
|
|
||||||
return filterCookies(cookies, urls).map(c => {
|
|
||||||
const copy: any = { ... c };
|
|
||||||
delete copy.size;
|
|
||||||
return copy as NetworkCookie;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async clearCookies() {
|
|
||||||
await this._connection.send('Browser.clearCookies', {
|
|
||||||
browserContextId: this._browserContextId || undefined,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async setCookies(cookies: SetNetworkCookieParam[]) {
|
|
||||||
cookies = rewriteCookies(cookies);
|
|
||||||
await this._connection.send('Browser.setCookies', {
|
|
||||||
browserContextId: this._browserContextId || undefined,
|
|
||||||
cookies
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async close() {
|
|
||||||
assert(this._browserContextId, 'Non-incognito contexts cannot be closed!');
|
|
||||||
await this._browser._disposeContext(this._browserContextId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@ import { Protocol } from './protocol';
|
||||||
import * as input from '../input';
|
import * as input from '../input';
|
||||||
import { RawMouseImpl, RawKeyboardImpl } from './Input';
|
import { RawMouseImpl, RawKeyboardImpl } from './Input';
|
||||||
import { FFScreenshotDelegate } from './Screenshotter';
|
import { FFScreenshotDelegate } from './Screenshotter';
|
||||||
import { Browser, BrowserContext } from './Browser';
|
import { Browser } from './Browser';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
import { Interception } from './features/interception';
|
import { Interception } from './features/interception';
|
||||||
import { Accessibility } from './features/accessibility';
|
import { Accessibility } from './features/accessibility';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
|
|
@ -58,14 +59,14 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||||
readonly rawKeyboard: RawKeyboardImpl;
|
readonly rawKeyboard: RawKeyboardImpl;
|
||||||
readonly screenshotterDelegate: FFScreenshotDelegate;
|
readonly screenshotterDelegate: FFScreenshotDelegate;
|
||||||
readonly _session: JugglerSession;
|
readonly _session: JugglerSession;
|
||||||
readonly _page: Page<Browser, BrowserContext>;
|
readonly _page: Page<Browser>;
|
||||||
private readonly _networkManager: NetworkManager;
|
private readonly _networkManager: NetworkManager;
|
||||||
private _mainFrame: frames.Frame;
|
private _mainFrame: frames.Frame;
|
||||||
private readonly _frames: Map<string, frames.Frame>;
|
private readonly _frames: Map<string, frames.Frame>;
|
||||||
private readonly _contextIdToContext: Map<string, js.ExecutionContext>;
|
private readonly _contextIdToContext: Map<string, js.ExecutionContext>;
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
|
|
||||||
constructor(session: JugglerSession, browserContext: BrowserContext) {
|
constructor(session: JugglerSession, browserContext: BrowserContext<Browser>) {
|
||||||
super();
|
super();
|
||||||
this._session = session;
|
this._session = session;
|
||||||
this.rawKeyboard = new RawKeyboardImpl(session);
|
this.rawKeyboard = new RawKeyboardImpl(session);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
export { TimeoutError } from '../Errors';
|
export { TimeoutError } from '../Errors';
|
||||||
export { Keyboard, Mouse } from '../input';
|
export { Keyboard, Mouse } from '../input';
|
||||||
export { Browser, BrowserContext } from './Browser';
|
export { Browser } from './Browser';
|
||||||
|
export { BrowserContext } from '../browserContext';
|
||||||
export { BrowserFetcher } from '../browserFetcher';
|
export { BrowserFetcher } from '../browserFetcher';
|
||||||
export { Dialog } from '../dialog';
|
export { Dialog } from '../dialog';
|
||||||
export { ExecutionContext, JSHandle } from '../javascript';
|
export { ExecutionContext, JSHandle } from '../javascript';
|
||||||
|
|
|
||||||
13
src/page.ts
13
src/page.ts
|
|
@ -27,6 +27,7 @@ import { Screenshotter, ScreenshotterDelegate } from './screenshotter';
|
||||||
import { TimeoutSettings } from './TimeoutSettings';
|
import { TimeoutSettings } from './TimeoutSettings';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
|
import { BrowserContext } from './browserContext';
|
||||||
|
|
||||||
export interface PageDelegate {
|
export interface PageDelegate {
|
||||||
readonly rawMouse: input.RawMouse;
|
readonly rawMouse: input.RawMouse;
|
||||||
|
|
@ -52,10 +53,6 @@ export interface PageDelegate {
|
||||||
setCacheEnabled(enabled: boolean): Promise<void>;
|
setCacheEnabled(enabled: boolean): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BrowserContextInterface<Browser> {
|
|
||||||
browser(): Browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
type PageState = {
|
type PageState = {
|
||||||
viewport: types.Viewport | null;
|
viewport: types.Viewport | null;
|
||||||
userAgent: string | null;
|
userAgent: string | null;
|
||||||
|
|
@ -72,14 +69,14 @@ export type FileChooser = {
|
||||||
multiple: boolean
|
multiple: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Page<Browser, BrowserContext extends BrowserContextInterface<Browser>> extends EventEmitter {
|
export class Page<Browser> extends EventEmitter {
|
||||||
private _closed = false;
|
private _closed = false;
|
||||||
private _closedCallback: () => void;
|
private _closedCallback: () => void;
|
||||||
private _closedPromise: Promise<void>;
|
private _closedPromise: Promise<void>;
|
||||||
private _disconnected = false;
|
private _disconnected = false;
|
||||||
private _disconnectedCallback: (e: Error) => void;
|
private _disconnectedCallback: (e: Error) => void;
|
||||||
readonly _disconnectedPromise: Promise<Error>;
|
readonly _disconnectedPromise: Promise<Error>;
|
||||||
private _browserContext: BrowserContext;
|
private _browserContext: BrowserContext<Browser>;
|
||||||
readonly keyboard: input.Keyboard;
|
readonly keyboard: input.Keyboard;
|
||||||
readonly mouse: input.Mouse;
|
readonly mouse: input.Mouse;
|
||||||
readonly _timeoutSettings: TimeoutSettings;
|
readonly _timeoutSettings: TimeoutSettings;
|
||||||
|
|
@ -89,7 +86,7 @@ export class Page<Browser, BrowserContext extends BrowserContextInterface<Browse
|
||||||
readonly _screenshotter: Screenshotter;
|
readonly _screenshotter: Screenshotter;
|
||||||
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
|
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
|
||||||
|
|
||||||
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
constructor(delegate: PageDelegate, browserContext: BrowserContext<Browser>) {
|
||||||
super();
|
super();
|
||||||
this._delegate = delegate;
|
this._delegate = delegate;
|
||||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||||
|
|
@ -156,7 +153,7 @@ export class Page<Browser, BrowserContext extends BrowserContextInterface<Browse
|
||||||
return this._browserContext.browser();
|
return this._browserContext.browser();
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContext(): BrowserContext {
|
browserContext(): BrowserContext<Browser> {
|
||||||
return this._browserContext;
|
return this._browserContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,22 +17,23 @@
|
||||||
|
|
||||||
import * as childProcess from 'child_process';
|
import * as childProcess from 'child_process';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { assert, helper, RegisteredListener, debugError } from '../helper';
|
import { helper, RegisteredListener, debugError } from '../helper';
|
||||||
import { filterCookies, NetworkCookie, rewriteCookies, SetNetworkCookieParam } from '../network';
|
import * as network from '../network';
|
||||||
import { Connection, ConnectionEvents, TargetSession } from './Connection';
|
import { Connection, ConnectionEvents, TargetSession } from './Connection';
|
||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { Target } from './Target';
|
import { Target } from './Target';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
|
|
||||||
export class Browser extends EventEmitter {
|
export class Browser extends EventEmitter {
|
||||||
readonly _defaultViewport: types.Viewport;
|
readonly _defaultViewport: types.Viewport;
|
||||||
private readonly _process: childProcess.ChildProcess;
|
private readonly _process: childProcess.ChildProcess;
|
||||||
readonly _connection: Connection;
|
readonly _connection: Connection;
|
||||||
private _closeCallback: () => Promise<void>;
|
private _closeCallback: () => Promise<void>;
|
||||||
private readonly _defaultContext: BrowserContext;
|
private readonly _defaultContext: BrowserContext<Browser>;
|
||||||
private _contexts = new Map<string, BrowserContext>();
|
private _contexts = new Map<string, BrowserContext<Browser>>();
|
||||||
_targets = new Map<string, Target>();
|
_targets = new Map<string, Target>();
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
private _privateEvents = new EventEmitter();
|
private _privateEvents = new EventEmitter();
|
||||||
|
|
@ -51,7 +52,7 @@ export class Browser extends EventEmitter {
|
||||||
/** @type {!Map<string, !Target>} */
|
/** @type {!Map<string, !Target>} */
|
||||||
this._targets = new Map();
|
this._targets = new Map();
|
||||||
|
|
||||||
this._defaultContext = new BrowserContext(this);
|
this._defaultContext = this._createBrowserContext(undefined);
|
||||||
/** @type {!Map<string, !BrowserContext>} */
|
/** @type {!Map<string, !BrowserContext>} */
|
||||||
this._contexts = new Map();
|
this._contexts = new Map();
|
||||||
|
|
||||||
|
|
@ -85,34 +86,26 @@ export class Browser extends EventEmitter {
|
||||||
return this._process;
|
return this._process;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createIncognitoBrowserContext(): Promise<BrowserContext> {
|
async createIncognitoBrowserContext(): Promise<BrowserContext<Browser>> {
|
||||||
const {browserContextId} = await this._connection.send('Browser.createContext');
|
const {browserContextId} = await this._connection.send('Browser.createContext');
|
||||||
const context = new BrowserContext(this, browserContextId);
|
const context = this._createBrowserContext(browserContextId);
|
||||||
this._contexts.set(browserContextId, context);
|
this._contexts.set(browserContextId, context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContexts(): BrowserContext[] {
|
browserContexts(): BrowserContext<Browser>[] {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultBrowserContext(): BrowserContext {
|
defaultBrowserContext(): BrowserContext<Browser> {
|
||||||
return this._defaultContext;
|
return this._defaultContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _disposeContext(browserContextId: string | null) {
|
async _disposeContext(browserContextId: string | null) {
|
||||||
await this._connection.send('Browser.deleteContext', {browserContextId});
|
|
||||||
this._contexts.delete(browserContextId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async newPage(): Promise<Page<Browser, BrowserContext>> {
|
async newPage(): Promise<Page<Browser>> {
|
||||||
return this._createPageInContext(this._defaultContext._id);
|
return this._defaultContext.newPage();
|
||||||
}
|
|
||||||
|
|
||||||
async _createPageInContext(browserContextId?: string): Promise<Page<Browser, BrowserContext>> {
|
|
||||||
const { targetId } = await this._connection.send('Browser.createPage', { browserContextId });
|
|
||||||
const target = this._targets.get(targetId);
|
|
||||||
return await target.page();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
targets(): Target[] {
|
targets(): Target[] {
|
||||||
|
|
@ -143,7 +136,7 @@ export class Browser extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async pages(): Promise<Page<Browser, BrowserContext>[]> {
|
async pages(): Promise<Page<Browser>[]> {
|
||||||
const contextPages = await Promise.all(this.browserContexts().map(context => context.pages()));
|
const contextPages = await Promise.all(this.browserContexts().map(context => context.pages()));
|
||||||
// Flatten array.
|
// Flatten array.
|
||||||
return contextPages.reduce((acc, x) => acc.concat(x), []);
|
return contextPages.reduce((acc, x) => acc.concat(x), []);
|
||||||
|
|
@ -188,19 +181,13 @@ export class Browser extends EventEmitter {
|
||||||
target._didClose();
|
target._didClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_closePage(page: Page<Browser, BrowserContext>) {
|
_closePage(page: Page<Browser>) {
|
||||||
this._connection.send('Target.close', {
|
this._connection.send('Target.close', {
|
||||||
targetId: Target.fromPage(page)._targetId
|
targetId: Target.fromPage(page)._targetId
|
||||||
}).catch(debugError);
|
}).catch(debugError);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _pages(context: BrowserContext): Promise<Page<Browser, BrowserContext>[]> {
|
async _activatePage(page: Page<Browser>): Promise<void> {
|
||||||
const targets = this.targets().filter(target => target._browserContext === context && target._type === 'page');
|
|
||||||
const pages = await Promise.all(targets.map(target => target.page()));
|
|
||||||
return pages.filter(page => !!page);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _activatePage(page: Page<Browser, BrowserContext>): Promise<void> {
|
|
||||||
await this._connection.send('Target.activate', { targetId: Target.fromPage(page)._targetId });
|
await this._connection.send('Target.activate', { targetId: Target.fromPage(page)._targetId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,54 +209,45 @@ export class Browser extends EventEmitter {
|
||||||
helper.removeEventListeners(this._eventListeners);
|
helper.removeEventListeners(this._eventListeners);
|
||||||
await this._closeCallback.call(null);
|
await this._closeCallback.call(null);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export class BrowserContext {
|
_createBrowserContext(browserContextId: string | undefined): BrowserContext<Browser> {
|
||||||
private _browser: Browser;
|
const isIncognito = !!browserContextId;
|
||||||
_id: string;
|
const context = new BrowserContext({
|
||||||
|
contextPages: async (): Promise<Page<Browser>[]> => {
|
||||||
|
const targets = this.targets().filter(target => target._browserContext === context && target._type === 'page');
|
||||||
|
const pages = await Promise.all(targets.map(target => target.page()));
|
||||||
|
return pages.filter(page => !!page);
|
||||||
|
},
|
||||||
|
|
||||||
constructor(browser: Browser, contextId?: string) {
|
createPageInContext: async (): Promise<Page<Browser>> => {
|
||||||
this._browser = browser;
|
const { targetId } = await this._connection.send('Browser.createPage', { browserContextId });
|
||||||
this._id = contextId;
|
const target = this._targets.get(targetId);
|
||||||
}
|
return await target.page();
|
||||||
|
},
|
||||||
|
|
||||||
pages(): Promise<Page<Browser, BrowserContext>[]> {
|
closeContext: async (): Promise<void> => {
|
||||||
return this._browser._pages(this);
|
await this._connection.send('Browser.deleteContext', { browserContextId });
|
||||||
}
|
this._contexts.delete(browserContextId);
|
||||||
|
},
|
||||||
|
|
||||||
isIncognito(): boolean {
|
getContextCookies: async (): Promise<network.NetworkCookie[]> => {
|
||||||
return !!this._id;
|
const { cookies } = await this._connection.send('Browser.getAllCookies', { browserContextId });
|
||||||
}
|
return cookies.map((c: network.NetworkCookie) => ({
|
||||||
|
...c,
|
||||||
|
expires: c.expires === 0 ? -1 : c.expires
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
newPage(): Promise<Page<Browser, BrowserContext>> {
|
clearContextCookies: async (): Promise<void> => {
|
||||||
return this._browser._createPageInContext(this._id);
|
await this._connection.send('Browser.deleteAllCookies', { browserContextId });
|
||||||
}
|
},
|
||||||
|
|
||||||
browser(): Browser {
|
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
|
||||||
return this._browser;
|
const cc = cookies.map(c => ({ ...c, session: c.expires === -1 || c.expires === undefined })) as Protocol.Browser.SetCookieParam[];
|
||||||
}
|
await this._connection.send('Browser.setCookies', { cookies: cc, browserContextId });
|
||||||
|
},
|
||||||
async close() {
|
}, this, isIncognito);
|
||||||
assert(this._id, 'Non-incognito profiles cannot be closed!');
|
return context;
|
||||||
await this._browser._disposeContext(this._id);
|
|
||||||
}
|
|
||||||
|
|
||||||
async cookies(...urls: string[]): Promise<NetworkCookie[]> {
|
|
||||||
const { cookies } = await this._browser._connection.send('Browser.getAllCookies', { browserContextId: this._id });
|
|
||||||
return filterCookies(cookies.map((c: NetworkCookie) => ({
|
|
||||||
...c,
|
|
||||||
expires: c.expires === 0 ? -1 : c.expires
|
|
||||||
})), urls);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setCookies(cookies: SetNetworkCookieParam[]) {
|
|
||||||
cookies = rewriteCookies(cookies);
|
|
||||||
const cc = cookies.map(c => ({ ...c, session: c.expires === -1 || c.expires === undefined })) as Protocol.Browser.SetCookieParam[];
|
|
||||||
await this._browser._connection.send('Browser.setCookies', { cookies: cc, browserContextId: this._id });
|
|
||||||
}
|
|
||||||
|
|
||||||
async clearCookies() {
|
|
||||||
await this._browser._connection.send('Browser.deleteAllCookies', { browserContextId: this._id });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import * as debug from 'debug';
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import { ConnectionTransport } from '../types';
|
import { ConnectionTransport } from '../types';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { throws } from 'assert';
|
|
||||||
|
|
||||||
const debugProtocol = debug('playwright:protocol');
|
const debugProtocol = debug('playwright:protocol');
|
||||||
const debugWrappedMessage = require('debug')('wrapped');
|
const debugWrappedMessage = require('debug')('wrapped');
|
||||||
|
|
@ -96,7 +95,7 @@ export class Connection extends EventEmitter {
|
||||||
const delay = this._delay || 0;
|
const delay = this._delay || 0;
|
||||||
this._dispatchTimerId = setTimeout(() => {
|
this._dispatchTimerId = setTimeout(() => {
|
||||||
this._dispatchTimerId = undefined;
|
this._dispatchTimerId = undefined;
|
||||||
this._dispatchOneMessageFromQueue()
|
this._dispatchOneMessageFromQueue();
|
||||||
}, delay);
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@
|
||||||
import * as EventEmitter from 'events';
|
import * as EventEmitter from 'events';
|
||||||
import { TimeoutError } from '../Errors';
|
import { TimeoutError } from '../Errors';
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { assert, debugError, helper, RegisteredListener } from '../helper';
|
import { assert, helper, RegisteredListener } from '../helper';
|
||||||
import * as js from '../javascript';
|
import * as js from '../javascript';
|
||||||
import * as dom from '../dom';
|
import * as dom from '../dom';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { TargetSession, TargetSessionEvents } from './Connection';
|
import { TargetSession } from './Connection';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { ExecutionContextDelegate, EVALUATION_SCRIPT_URL } from './ExecutionContext';
|
import { ExecutionContextDelegate, EVALUATION_SCRIPT_URL } from './ExecutionContext';
|
||||||
import { NetworkManager, NetworkManagerEvents } from './NetworkManager';
|
import { NetworkManager, NetworkManagerEvents } from './NetworkManager';
|
||||||
|
|
@ -30,7 +30,8 @@ import { Page, PageDelegate } from '../page';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { DOMWorldDelegate } from './JSHandle';
|
import { DOMWorldDelegate } from './JSHandle';
|
||||||
import * as dialog from '../dialog';
|
import * as dialog from '../dialog';
|
||||||
import { Browser, BrowserContext } from './Browser';
|
import { Browser } from './Browser';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
import { RawMouseImpl, RawKeyboardImpl } from './Input';
|
import { RawMouseImpl, RawKeyboardImpl } from './Input';
|
||||||
import { WKScreenshotDelegate } from './Screenshotter';
|
import { WKScreenshotDelegate } from './Screenshotter';
|
||||||
import * as input from '../input';
|
import * as input from '../input';
|
||||||
|
|
@ -57,7 +58,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||||
readonly rawKeyboard: RawKeyboardImpl;
|
readonly rawKeyboard: RawKeyboardImpl;
|
||||||
readonly screenshotterDelegate: WKScreenshotDelegate;
|
readonly screenshotterDelegate: WKScreenshotDelegate;
|
||||||
_session: TargetSession;
|
_session: TargetSession;
|
||||||
readonly _page: Page<Browser, BrowserContext>;
|
readonly _page: Page<Browser>;
|
||||||
private readonly _networkManager: NetworkManager;
|
private readonly _networkManager: NetworkManager;
|
||||||
private readonly _frames: Map<string, frames.Frame>;
|
private readonly _frames: Map<string, frames.Frame>;
|
||||||
private readonly _contextIdToContext: Map<number, js.ExecutionContext>;
|
private readonly _contextIdToContext: Map<number, js.ExecutionContext>;
|
||||||
|
|
@ -66,7 +67,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||||
private _mainFrame: frames.Frame;
|
private _mainFrame: frames.Frame;
|
||||||
private readonly _bootstrapScripts: string[] = [];
|
private readonly _bootstrapScripts: string[] = [];
|
||||||
|
|
||||||
constructor(browserContext: BrowserContext) {
|
constructor(browserContext: BrowserContext<Browser>) {
|
||||||
super();
|
super();
|
||||||
this.rawKeyboard = new RawKeyboardImpl();
|
this.rawKeyboard = new RawKeyboardImpl();
|
||||||
this.rawMouse = new RawMouseImpl();
|
this.rawMouse = new RawMouseImpl();
|
||||||
|
|
@ -95,7 +96,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is called for provisional targets as well. The session passed as the parameter
|
// This method is called for provisional targets as well. The session passed as the parameter
|
||||||
// may be different from the current session and may be destroyed without becoming current.
|
// may be different from the current session and may be destroyed without becoming current.
|
||||||
async _initializeSession(session: TargetSession) {
|
async _initializeSession(session: TargetSession) {
|
||||||
const promises : Promise<any>[] = [
|
const promises : Promise<any>[] = [
|
||||||
// Page agent must be enabled before Runtime.
|
// Page agent must be enabled before Runtime.
|
||||||
|
|
@ -109,7 +110,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||||
];
|
];
|
||||||
if (!session.isProvisional()) {
|
if (!session.isProvisional()) {
|
||||||
// FIXME: move dialog agent to web process.
|
// FIXME: move dialog agent to web process.
|
||||||
// Dialog agent resides in the UI process and should not be re-enabled on navigation.
|
// Dialog agent resides in the UI process and should not be re-enabled on navigation.
|
||||||
promises.push(session.send('Dialog.enable'));
|
promises.push(session.send('Dialog.enable'));
|
||||||
}
|
}
|
||||||
if (this._page._state.userAgent !== null)
|
if (this._page._state.userAgent !== null)
|
||||||
|
|
@ -193,7 +194,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||||
this._handleFrameTree(child);
|
this._handleFrameTree(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
page(): Page<Browser, BrowserContext> {
|
page(): Page<Browser> {
|
||||||
return this._page;
|
return this._page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BrowserContext, Browser } from './Browser';
|
import { Browser } from './Browser';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { isSwappedOutError, TargetSession, TargetSessionEvents } from './Connection';
|
import { isSwappedOutError, TargetSession, TargetSessionEvents } from './Connection';
|
||||||
|
|
@ -24,18 +25,18 @@ import { FrameManager } from './FrameManager';
|
||||||
const targetSymbol = Symbol('target');
|
const targetSymbol = Symbol('target');
|
||||||
|
|
||||||
export class Target {
|
export class Target {
|
||||||
readonly _browserContext: BrowserContext;
|
readonly _browserContext: BrowserContext<Browser>;
|
||||||
readonly _targetId: string;
|
readonly _targetId: string;
|
||||||
readonly _type: 'page' | 'service-worker' | 'worker';
|
readonly _type: 'page' | 'service-worker' | 'worker';
|
||||||
private readonly _session: TargetSession;
|
private readonly _session: TargetSession;
|
||||||
private _pagePromise: Promise<Page<Browser, BrowserContext>> | null = null;
|
private _pagePromise: Promise<Page<Browser>> | null = null;
|
||||||
_page: Page<Browser, BrowserContext> | null = null;
|
_page: Page<Browser> | null = null;
|
||||||
|
|
||||||
static fromPage(page: Page<Browser, BrowserContext>): Target {
|
static fromPage(page: Page<Browser>): Target {
|
||||||
return (page as any)[targetSymbol];
|
return (page as any)[targetSymbol];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(session: TargetSession, targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext) {
|
constructor(session: TargetSession, targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext<Browser>) {
|
||||||
const {targetId, type} = targetInfo;
|
const {targetId, type} = targetInfo;
|
||||||
this._session = session;
|
this._session = session;
|
||||||
this._browserContext = browserContext;
|
this._browserContext = browserContext;
|
||||||
|
|
@ -83,7 +84,7 @@ export class Target {
|
||||||
(this._page._delegate as FrameManager).setSession(this._session);
|
(this._page._delegate as FrameManager).setSession(this._session);
|
||||||
}
|
}
|
||||||
|
|
||||||
async page(): Promise<Page<Browser, BrowserContext>> {
|
async page(): Promise<Page<Browser>> {
|
||||||
if (this._type === 'page' && !this._pagePromise) {
|
if (this._type === 'page' && !this._pagePromise) {
|
||||||
const browser = this._browserContext.browser();
|
const browser = this._browserContext.browser();
|
||||||
// Reference local page variable as _page may be
|
// Reference local page variable as _page may be
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
export { TimeoutError } from '../Errors';
|
export { TimeoutError } from '../Errors';
|
||||||
export { Browser, BrowserContext } from './Browser';
|
export { Browser } from './Browser';
|
||||||
|
export { BrowserContext } from '../browserContext';
|
||||||
export { BrowserFetcher } from '../browserFetcher';
|
export { BrowserFetcher } from '../browserFetcher';
|
||||||
export { ExecutionContext, JSHandle } from '../javascript';
|
export { ExecutionContext, JSHandle } from '../javascript';
|
||||||
export { ElementHandle } from '../dom';
|
export { ElementHandle } from '../dom';
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
|
||||||
describe('JSCoverage', function() {
|
describe('JSCoverage', function() {
|
||||||
it('should work', async function({page, server}) {
|
it('should work', async function({page, server}) {
|
||||||
await page.coverage.startJSCoverage();
|
await page.coverage.startJSCoverage();
|
||||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', {waitUntil: 'networkidle0'});
|
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
||||||
const coverage = await page.coverage.stopJSCoverage();
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
expect(coverage.length).toBe(1);
|
expect(coverage.length).toBe(1);
|
||||||
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue