mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 21:53:35 +03:00
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+Enter': 'insertNewlineIgnoringFieldEditor:',
|
||||
'Alt+Escape': 'complete:',
|
||||
"Alt+ArrowUp": ['moveBackward:', 'moveToBeginningOfParagraph:'],
|
||||
"Alt+ArrowDown": ['moveForward:', 'moveToEndOfParagraph:'],
|
||||
'Alt+ArrowUp': ['moveBackward:', 'moveToBeginningOfParagraph:'],
|
||||
'Alt+ArrowDown': ['moveForward:', 'moveToEndOfParagraph:'],
|
||||
'Alt+ArrowLeft': 'moveWordLeft:',
|
||||
'Alt+ArrowRight': 'moveWordRight:',
|
||||
'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 { Events } from './events';
|
||||
import { assert, helper } from '../helper';
|
||||
import { BrowserContext } from './BrowserContext';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import { Connection, ConnectionEvents, CDPSession } from './Connection';
|
||||
import { Page } from '../page';
|
||||
import { Target } from './Target';
|
||||
@ -27,6 +27,8 @@ import { Protocol } from './protocol';
|
||||
import { Chromium } from './features/chromium';
|
||||
import * as types from '../types';
|
||||
import { FrameManager } from './FrameManager';
|
||||
import * as network from '../network';
|
||||
import { Permissions } from './features/permissions';
|
||||
|
||||
export class Browser extends EventEmitter {
|
||||
private _ignoreHTTPSErrors: boolean;
|
||||
@ -35,8 +37,8 @@ export class Browser extends EventEmitter {
|
||||
_connection: Connection;
|
||||
_client: CDPSession;
|
||||
private _closeCallback: () => Promise<void>;
|
||||
private _defaultContext: BrowserContext;
|
||||
private _contexts = new Map<string, BrowserContext>();
|
||||
private _defaultContext: BrowserContext<Browser>;
|
||||
private _contexts = new Map<string, BrowserContext<Browser>>();
|
||||
_targets = new Map<string, Target>();
|
||||
readonly chromium: Chromium;
|
||||
|
||||
@ -68,9 +70,9 @@ export class Browser extends EventEmitter {
|
||||
this._closeCallback = closeCallback || (() => Promise.resolve());
|
||||
this.chromium = new Chromium(this);
|
||||
|
||||
this._defaultContext = new BrowserContext(this._client, this, null);
|
||||
this._defaultContext = this._createBrowserContext(null);
|
||||
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._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));
|
||||
}
|
||||
|
||||
_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 {
|
||||
return this._process;
|
||||
}
|
||||
|
||||
async createIncognitoBrowserContext(): Promise<BrowserContext> {
|
||||
async createIncognitoBrowserContext(): Promise<BrowserContext<Browser>> {
|
||||
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);
|
||||
return context;
|
||||
}
|
||||
|
||||
browserContexts(): BrowserContext[] {
|
||||
browserContexts(): BrowserContext<Browser>[] {
|
||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||
}
|
||||
|
||||
defaultBrowserContext(): BrowserContext {
|
||||
defaultBrowserContext(): BrowserContext<Browser> {
|
||||
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) {
|
||||
const targetInfo = event.targetInfo;
|
||||
const {browserContextId} = targetInfo;
|
||||
@ -134,19 +174,11 @@ export class Browser extends EventEmitter {
|
||||
this.chromium.emit(Events.Chromium.TargetChanged, target);
|
||||
}
|
||||
|
||||
async newPage(): Promise<Page<Browser, BrowserContext>> {
|
||||
async newPage(): Promise<Page<Browser>> {
|
||||
return this._defaultContext.newPage();
|
||||
}
|
||||
|
||||
async _createPageInContext(contextId: string | null): Promise<Page<Browser, BrowserContext>> {
|
||||
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>) {
|
||||
async _closePage(page: Page<Browser>) {
|
||||
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);
|
||||
}
|
||||
|
||||
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 _activatePage(page: Page<Browser, BrowserContext>) {
|
||||
async _activatePage(page: Page<Browser>) {
|
||||
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()));
|
||||
// Flatten array.
|
||||
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 { Interception } from './features/interception';
|
||||
import { Browser } from './Browser';
|
||||
import { BrowserContext } from './BrowserContext';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import * as types from '../types';
|
||||
import * as input from '../input';
|
||||
import { ConsoleMessage } from '../console';
|
||||
@ -64,7 +64,7 @@ type FrameData = {
|
||||
|
||||
export class FrameManager extends EventEmitter implements frames.FrameDelegate, PageDelegate {
|
||||
_client: CDPSession;
|
||||
private _page: Page<Browser, BrowserContext>;
|
||||
private _page: Page<Browser>;
|
||||
private _networkManager: NetworkManager;
|
||||
private _frames = new Map<string, frames.Frame>();
|
||||
private _contextIdToContext = new Map<number, js.ExecutionContext>();
|
||||
@ -74,7 +74,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||
rawKeyboard: RawKeyboardImpl;
|
||||
screenshotterDelegate: CRScreenshotDelegate;
|
||||
|
||||
constructor(client: CDPSession, browserContext: BrowserContext, ignoreHTTPSErrors: boolean) {
|
||||
constructor(client: CDPSession, browserContext: BrowserContext<Browser>, ignoreHTTPSErrors: boolean) {
|
||||
super();
|
||||
this._client = client;
|
||||
this.rawKeyboard = new RawKeyboardImpl(client);
|
||||
@ -254,7 +254,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||
this._handleFrameTree(child);
|
||||
}
|
||||
|
||||
page(): Page<Browser, BrowserContext> {
|
||||
page(): Page<Browser> {
|
||||
return this._page;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
import * as types from '../types';
|
||||
import { Browser } from './Browser';
|
||||
import { BrowserContext } from './BrowserContext';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import { CDPSession, CDPSessionEvents } from './Connection';
|
||||
import { Events } from '../events';
|
||||
import { Worker } from './features/workers';
|
||||
@ -30,25 +30,25 @@ const targetSymbol = Symbol('target');
|
||||
|
||||
export class Target {
|
||||
private _targetInfo: Protocol.Target.TargetInfo;
|
||||
private _browserContext: BrowserContext;
|
||||
private _browserContext: BrowserContext<Browser>;
|
||||
_targetId: string;
|
||||
private _sessionFactory: () => Promise<CDPSession>;
|
||||
private _ignoreHTTPSErrors: boolean;
|
||||
private _defaultViewport: types.Viewport;
|
||||
private _pagePromise: Promise<Page<Browser, BrowserContext>> | null = null;
|
||||
private _page: Page<Browser, BrowserContext> | null = null;
|
||||
private _pagePromise: Promise<Page<Browser>> | null = null;
|
||||
private _page: Page<Browser> | null = null;
|
||||
private _workerPromise: Promise<Worker> | null = null;
|
||||
_initializedPromise: Promise<boolean>;
|
||||
_initializedCallback: (value?: unknown) => void;
|
||||
_isInitialized: boolean;
|
||||
|
||||
static fromPage(page: Page<Browser, BrowserContext>): Target {
|
||||
static fromPage(page: Page<Browser>): Target {
|
||||
return (page as any)[targetSymbol];
|
||||
}
|
||||
|
||||
constructor(
|
||||
targetInfo: Protocol.Target.TargetInfo,
|
||||
browserContext: BrowserContext,
|
||||
browserContext: BrowserContext<Browser>,
|
||||
sessionFactory: () => Promise<CDPSession>,
|
||||
ignoreHTTPSErrors: boolean,
|
||||
defaultViewport: types.Viewport | null) {
|
||||
@ -81,7 +81,7 @@ export class Target {
|
||||
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) {
|
||||
this._pagePromise = this._sessionFactory().then(async client => {
|
||||
const frameManager = new FrameManager(client, this._browserContext, this._ignoreHTTPSErrors);
|
||||
@ -131,7 +131,7 @@ export class Target {
|
||||
return this._browserContext.browser();
|
||||
}
|
||||
|
||||
browserContext(): BrowserContext {
|
||||
browserContext(): BrowserContext<Browser> {
|
||||
return this._browserContext;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ export { Keyboard, Mouse } from '../input';
|
||||
export { ExecutionContext, JSHandle } from '../javascript';
|
||||
export { Request, Response } from '../network';
|
||||
export { Browser } from './Browser';
|
||||
export { BrowserContext } from './BrowserContext';
|
||||
export { BrowserContext } from '../browserContext';
|
||||
export { BrowserFetcher } from '../browserFetcher';
|
||||
export { CDPSession } from './Connection';
|
||||
export { Accessibility } from './features/accessibility';
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { assert } from '../../helper';
|
||||
import { Browser } from '../Browser';
|
||||
import { BrowserContext } from '../BrowserContext';
|
||||
import { BrowserContext } from '../../browserContext';
|
||||
import { CDPSession, Connection } from '../Connection';
|
||||
import { Page } from '../../page';
|
||||
import { readProtocolStream } from '../protocolHelper';
|
||||
@ -48,7 +48,7 @@ export class Chromium extends EventEmitter {
|
||||
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.');
|
||||
this._tracingClient = page ? (page._delegate as FrameManager)._client : this._client;
|
||||
|
||||
@ -87,12 +87,12 @@ export class Chromium extends EventEmitter {
|
||||
return contentPromise;
|
||||
}
|
||||
|
||||
targets(context?: BrowserContext): Target[] {
|
||||
targets(context?: BrowserContext<Browser>): Target[] {
|
||||
const targets = this._browser._allTargets();
|
||||
return context ? targets.filter(t => t.browserContext() === context) : targets;
|
||||
}
|
||||
|
||||
pageTarget(page: Page<Browser, BrowserContext>): Target {
|
||||
pageTarget(page: Page<Browser>): Target {
|
||||
return Target.fromPage(page);
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import { filterCookies, NetworkCookie, SetNetworkCookieParam, rewriteCookies } from '../network';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import { Connection, ConnectionEvents, JugglerSessionEvents } from './Connection';
|
||||
import { Events } from './events';
|
||||
import { Events as CommonEvents } from '../events';
|
||||
@ -25,6 +24,8 @@ import { Permissions } from './features/permissions';
|
||||
import { Page } from '../page';
|
||||
import * as types from '../types';
|
||||
import { FrameManager } from './FrameManager';
|
||||
import * as network from '../network';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
|
||||
export class Browser extends EventEmitter {
|
||||
private _connection: Connection;
|
||||
@ -32,8 +33,8 @@ export class Browser extends EventEmitter {
|
||||
private _process: import('child_process').ChildProcess;
|
||||
private _closeCallback: () => void;
|
||||
_targets: Map<string, Target>;
|
||||
private _defaultContext: BrowserContext;
|
||||
private _contexts: Map<string, BrowserContext>;
|
||||
private _defaultContext: BrowserContext<Browser>;
|
||||
private _contexts: Map<string, BrowserContext<Browser>>;
|
||||
private _eventListeners: RegisteredListener[];
|
||||
|
||||
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._defaultContext = new BrowserContext(this._connection, this, null);
|
||||
this._defaultContext = this._createBrowserContext(null);
|
||||
this._contexts = new Map();
|
||||
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));
|
||||
|
||||
@ -74,14 +75,14 @@ export class Browser extends EventEmitter {
|
||||
return !this._connection._closed;
|
||||
}
|
||||
|
||||
async createIncognitoBrowserContext(): Promise<BrowserContext> {
|
||||
async createIncognitoBrowserContext(): Promise<BrowserContext<Browser>> {
|
||||
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);
|
||||
return context;
|
||||
}
|
||||
|
||||
browserContexts(): Array<BrowserContext> {
|
||||
browserContexts(): Array<BrowserContext<Browser>> {
|
||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||
}
|
||||
|
||||
@ -89,11 +90,6 @@ export class Browser extends EventEmitter {
|
||||
return this._defaultContext;
|
||||
}
|
||||
|
||||
async _disposeContext(browserContextId) {
|
||||
await this._connection.send('Target.removeBrowserContext', {browserContextId});
|
||||
this._contexts.delete(browserContextId);
|
||||
}
|
||||
|
||||
async userAgent(): Promise<string> {
|
||||
const info = await this._connection.send('Browser.getInfo');
|
||||
return info.userAgent;
|
||||
@ -132,16 +128,8 @@ export class Browser extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
newPage(): Promise<Page<Browser, BrowserContext>> {
|
||||
return this._createPageInContext(this._defaultContext._browserContextId);
|
||||
}
|
||||
|
||||
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();
|
||||
newPage(): Promise<Page<Browser>> {
|
||||
return this._defaultContext.newPage();
|
||||
}
|
||||
|
||||
async pages() {
|
||||
@ -153,12 +141,6 @@ export class Browser extends EventEmitter {
|
||||
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}) {
|
||||
const context = browserContextId ? this._contexts.get(browserContextId) : this._defaultContext;
|
||||
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);
|
||||
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 {
|
||||
_pagePromise?: Promise<Page<Browser, BrowserContext>>;
|
||||
private _page: Page<Browser, BrowserContext> | null = null;
|
||||
_pagePromise?: Promise<Page<Browser>>;
|
||||
private _page: Page<Browser> | null = null;
|
||||
private _browser: Browser;
|
||||
_context: BrowserContext;
|
||||
_context: BrowserContext<Browser>;
|
||||
private _connection: Connection;
|
||||
private _targetId: string;
|
||||
private _type: 'page' | 'browser';
|
||||
_url: 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._context = context;
|
||||
this._connection = connection;
|
||||
@ -227,11 +252,11 @@ export class Target {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
browserContext(): BrowserContext {
|
||||
browserContext(): BrowserContext<Browser> {
|
||||
return this._context;
|
||||
}
|
||||
|
||||
page(): Promise<Page<Browser, BrowserContext>> {
|
||||
page(): Promise<Page<Browser>> {
|
||||
if (this._type === 'page' && !this._pagePromise) {
|
||||
this._pagePromise = new Promise(async f => {
|
||||
const session = await this._connection.createSession(this._targetId);
|
||||
@ -252,64 +277,3 @@ export class Target {
|
||||
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 { RawMouseImpl, RawKeyboardImpl } from './Input';
|
||||
import { FFScreenshotDelegate } from './Screenshotter';
|
||||
import { Browser, BrowserContext } from './Browser';
|
||||
import { Browser } from './Browser';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import { Interception } from './features/interception';
|
||||
import { Accessibility } from './features/accessibility';
|
||||
import * as network from '../network';
|
||||
@ -58,14 +59,14 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||
readonly rawKeyboard: RawKeyboardImpl;
|
||||
readonly screenshotterDelegate: FFScreenshotDelegate;
|
||||
readonly _session: JugglerSession;
|
||||
readonly _page: Page<Browser, BrowserContext>;
|
||||
readonly _page: Page<Browser>;
|
||||
private readonly _networkManager: NetworkManager;
|
||||
private _mainFrame: frames.Frame;
|
||||
private readonly _frames: Map<string, frames.Frame>;
|
||||
private readonly _contextIdToContext: Map<string, js.ExecutionContext>;
|
||||
private _eventListeners: RegisteredListener[];
|
||||
|
||||
constructor(session: JugglerSession, browserContext: BrowserContext) {
|
||||
constructor(session: JugglerSession, browserContext: BrowserContext<Browser>) {
|
||||
super();
|
||||
this._session = session;
|
||||
this.rawKeyboard = new RawKeyboardImpl(session);
|
||||
|
@ -3,7 +3,8 @@
|
||||
|
||||
export { TimeoutError } from '../Errors';
|
||||
export { Keyboard, Mouse } from '../input';
|
||||
export { Browser, BrowserContext } from './Browser';
|
||||
export { Browser } from './Browser';
|
||||
export { BrowserContext } from '../browserContext';
|
||||
export { BrowserFetcher } from '../browserFetcher';
|
||||
export { Dialog } from '../dialog';
|
||||
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 * as types from './types';
|
||||
import { Events } from './events';
|
||||
import { BrowserContext } from './browserContext';
|
||||
|
||||
export interface PageDelegate {
|
||||
readonly rawMouse: input.RawMouse;
|
||||
@ -52,10 +53,6 @@ export interface PageDelegate {
|
||||
setCacheEnabled(enabled: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
interface BrowserContextInterface<Browser> {
|
||||
browser(): Browser;
|
||||
}
|
||||
|
||||
type PageState = {
|
||||
viewport: types.Viewport | null;
|
||||
userAgent: string | null;
|
||||
@ -72,14 +69,14 @@ export type FileChooser = {
|
||||
multiple: boolean
|
||||
};
|
||||
|
||||
export class Page<Browser, BrowserContext extends BrowserContextInterface<Browser>> extends EventEmitter {
|
||||
export class Page<Browser> extends EventEmitter {
|
||||
private _closed = false;
|
||||
private _closedCallback: () => void;
|
||||
private _closedPromise: Promise<void>;
|
||||
private _disconnected = false;
|
||||
private _disconnectedCallback: (e: Error) => void;
|
||||
readonly _disconnectedPromise: Promise<Error>;
|
||||
private _browserContext: BrowserContext;
|
||||
private _browserContext: BrowserContext<Browser>;
|
||||
readonly keyboard: input.Keyboard;
|
||||
readonly mouse: input.Mouse;
|
||||
readonly _timeoutSettings: TimeoutSettings;
|
||||
@ -89,7 +86,7 @@ export class Page<Browser, BrowserContext extends BrowserContextInterface<Browse
|
||||
readonly _screenshotter: Screenshotter;
|
||||
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
|
||||
|
||||
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
||||
constructor(delegate: PageDelegate, browserContext: BrowserContext<Browser>) {
|
||||
super();
|
||||
this._delegate = delegate;
|
||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||
@ -156,7 +153,7 @@ export class Page<Browser, BrowserContext extends BrowserContextInterface<Browse
|
||||
return this._browserContext.browser();
|
||||
}
|
||||
|
||||
browserContext(): BrowserContext {
|
||||
browserContext(): BrowserContext<Browser> {
|
||||
return this._browserContext;
|
||||
}
|
||||
|
||||
|
@ -17,22 +17,23 @@
|
||||
|
||||
import * as childProcess from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
import { assert, helper, RegisteredListener, debugError } from '../helper';
|
||||
import { filterCookies, NetworkCookie, rewriteCookies, SetNetworkCookieParam } from '../network';
|
||||
import { helper, RegisteredListener, debugError } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Connection, ConnectionEvents, TargetSession } from './Connection';
|
||||
import { Page } from '../page';
|
||||
import { Target } from './Target';
|
||||
import { Protocol } from './protocol';
|
||||
import * as types from '../types';
|
||||
import { Events } from '../events';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
|
||||
export class Browser extends EventEmitter {
|
||||
readonly _defaultViewport: types.Viewport;
|
||||
private readonly _process: childProcess.ChildProcess;
|
||||
readonly _connection: Connection;
|
||||
private _closeCallback: () => Promise<void>;
|
||||
private readonly _defaultContext: BrowserContext;
|
||||
private _contexts = new Map<string, BrowserContext>();
|
||||
private readonly _defaultContext: BrowserContext<Browser>;
|
||||
private _contexts = new Map<string, BrowserContext<Browser>>();
|
||||
_targets = new Map<string, Target>();
|
||||
private _eventListeners: RegisteredListener[];
|
||||
private _privateEvents = new EventEmitter();
|
||||
@ -51,7 +52,7 @@ export class Browser extends EventEmitter {
|
||||
/** @type {!Map<string, !Target>} */
|
||||
this._targets = new Map();
|
||||
|
||||
this._defaultContext = new BrowserContext(this);
|
||||
this._defaultContext = this._createBrowserContext(undefined);
|
||||
/** @type {!Map<string, !BrowserContext>} */
|
||||
this._contexts = new Map();
|
||||
|
||||
@ -85,34 +86,26 @@ export class Browser extends EventEmitter {
|
||||
return this._process;
|
||||
}
|
||||
|
||||
async createIncognitoBrowserContext(): Promise<BrowserContext> {
|
||||
async createIncognitoBrowserContext(): Promise<BrowserContext<Browser>> {
|
||||
const {browserContextId} = await this._connection.send('Browser.createContext');
|
||||
const context = new BrowserContext(this, browserContextId);
|
||||
const context = this._createBrowserContext(browserContextId);
|
||||
this._contexts.set(browserContextId, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
browserContexts(): BrowserContext[] {
|
||||
browserContexts(): BrowserContext<Browser>[] {
|
||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||
}
|
||||
|
||||
defaultBrowserContext(): BrowserContext {
|
||||
defaultBrowserContext(): BrowserContext<Browser> {
|
||||
return this._defaultContext;
|
||||
}
|
||||
|
||||
async _disposeContext(browserContextId: string | null) {
|
||||
await this._connection.send('Browser.deleteContext', {browserContextId});
|
||||
this._contexts.delete(browserContextId);
|
||||
}
|
||||
|
||||
async newPage(): Promise<Page<Browser, BrowserContext>> {
|
||||
return this._createPageInContext(this._defaultContext._id);
|
||||
}
|
||||
|
||||
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();
|
||||
async newPage(): Promise<Page<Browser>> {
|
||||
return this._defaultContext.newPage();
|
||||
}
|
||||
|
||||
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()));
|
||||
// Flatten array.
|
||||
return contextPages.reduce((acc, x) => acc.concat(x), []);
|
||||
@ -188,19 +181,13 @@ export class Browser extends EventEmitter {
|
||||
target._didClose();
|
||||
}
|
||||
|
||||
_closePage(page: Page<Browser, BrowserContext>) {
|
||||
_closePage(page: Page<Browser>) {
|
||||
this._connection.send('Target.close', {
|
||||
targetId: Target.fromPage(page)._targetId
|
||||
}).catch(debugError);
|
||||
}
|
||||
|
||||
async _pages(context: BrowserContext): Promise<Page<Browser, BrowserContext>[]> {
|
||||
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> {
|
||||
async _activatePage(page: Page<Browser>): Promise<void> {
|
||||
await this._connection.send('Target.activate', { targetId: Target.fromPage(page)._targetId });
|
||||
}
|
||||
|
||||
@ -222,54 +209,45 @@ export class Browser extends EventEmitter {
|
||||
helper.removeEventListeners(this._eventListeners);
|
||||
await this._closeCallback.call(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowserContext {
|
||||
private _browser: Browser;
|
||||
_id: string;
|
||||
_createBrowserContext(browserContextId: string | undefined): BrowserContext<Browser> {
|
||||
const isIncognito = !!browserContextId;
|
||||
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) {
|
||||
this._browser = browser;
|
||||
this._id = contextId;
|
||||
}
|
||||
createPageInContext: async (): Promise<Page<Browser>> => {
|
||||
const { targetId } = await this._connection.send('Browser.createPage', { browserContextId });
|
||||
const target = this._targets.get(targetId);
|
||||
return await target.page();
|
||||
},
|
||||
|
||||
pages(): Promise<Page<Browser, BrowserContext>[]> {
|
||||
return this._browser._pages(this);
|
||||
}
|
||||
closeContext: async (): Promise<void> => {
|
||||
await this._connection.send('Browser.deleteContext', { browserContextId });
|
||||
this._contexts.delete(browserContextId);
|
||||
},
|
||||
|
||||
isIncognito(): boolean {
|
||||
return !!this._id;
|
||||
}
|
||||
getContextCookies: async (): Promise<network.NetworkCookie[]> => {
|
||||
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>> {
|
||||
return this._browser._createPageInContext(this._id);
|
||||
}
|
||||
clearContextCookies: async (): Promise<void> => {
|
||||
await this._connection.send('Browser.deleteAllCookies', { browserContextId });
|
||||
},
|
||||
|
||||
browser(): Browser {
|
||||
return this._browser;
|
||||
}
|
||||
|
||||
async close() {
|
||||
assert(this._id, 'Non-incognito profiles cannot be closed!');
|
||||
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 });
|
||||
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
|
||||
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 });
|
||||
},
|
||||
}, this, isIncognito);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ import * as debug from 'debug';
|
||||
import {EventEmitter} from 'events';
|
||||
import { ConnectionTransport } from '../types';
|
||||
import { Protocol } from './protocol';
|
||||
import { throws } from 'assert';
|
||||
|
||||
const debugProtocol = debug('playwright:protocol');
|
||||
const debugWrappedMessage = require('debug')('wrapped');
|
||||
@ -96,7 +95,7 @@ export class Connection extends EventEmitter {
|
||||
const delay = this._delay || 0;
|
||||
this._dispatchTimerId = setTimeout(() => {
|
||||
this._dispatchTimerId = undefined;
|
||||
this._dispatchOneMessageFromQueue()
|
||||
this._dispatchOneMessageFromQueue();
|
||||
}, delay);
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,11 @@
|
||||
import * as EventEmitter from 'events';
|
||||
import { TimeoutError } from '../Errors';
|
||||
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 dom from '../dom';
|
||||
import * as network from '../network';
|
||||
import { TargetSession, TargetSessionEvents } from './Connection';
|
||||
import { TargetSession } from './Connection';
|
||||
import { Events } from '../events';
|
||||
import { ExecutionContextDelegate, EVALUATION_SCRIPT_URL } from './ExecutionContext';
|
||||
import { NetworkManager, NetworkManagerEvents } from './NetworkManager';
|
||||
@ -30,7 +30,8 @@ import { Page, PageDelegate } from '../page';
|
||||
import { Protocol } from './protocol';
|
||||
import { DOMWorldDelegate } from './JSHandle';
|
||||
import * as dialog from '../dialog';
|
||||
import { Browser, BrowserContext } from './Browser';
|
||||
import { Browser } from './Browser';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import { RawMouseImpl, RawKeyboardImpl } from './Input';
|
||||
import { WKScreenshotDelegate } from './Screenshotter';
|
||||
import * as input from '../input';
|
||||
@ -57,7 +58,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||
readonly rawKeyboard: RawKeyboardImpl;
|
||||
readonly screenshotterDelegate: WKScreenshotDelegate;
|
||||
_session: TargetSession;
|
||||
readonly _page: Page<Browser, BrowserContext>;
|
||||
readonly _page: Page<Browser>;
|
||||
private readonly _networkManager: NetworkManager;
|
||||
private readonly _frames: Map<string, frames.Frame>;
|
||||
private readonly _contextIdToContext: Map<number, js.ExecutionContext>;
|
||||
@ -66,7 +67,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||
private _mainFrame: frames.Frame;
|
||||
private readonly _bootstrapScripts: string[] = [];
|
||||
|
||||
constructor(browserContext: BrowserContext) {
|
||||
constructor(browserContext: BrowserContext<Browser>) {
|
||||
super();
|
||||
this.rawKeyboard = new RawKeyboardImpl();
|
||||
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
|
||||
// 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) {
|
||||
const promises : Promise<any>[] = [
|
||||
// Page agent must be enabled before Runtime.
|
||||
@ -109,7 +110,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||
];
|
||||
if (!session.isProvisional()) {
|
||||
// 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'));
|
||||
}
|
||||
if (this._page._state.userAgent !== null)
|
||||
@ -193,7 +194,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate,
|
||||
this._handleFrameTree(child);
|
||||
}
|
||||
|
||||
page(): Page<Browser, BrowserContext> {
|
||||
page(): Page<Browser> {
|
||||
return this._page;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserContext, Browser } from './Browser';
|
||||
import { Browser } from './Browser';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import { Page } from '../page';
|
||||
import { Protocol } from './protocol';
|
||||
import { isSwappedOutError, TargetSession, TargetSessionEvents } from './Connection';
|
||||
@ -24,18 +25,18 @@ import { FrameManager } from './FrameManager';
|
||||
const targetSymbol = Symbol('target');
|
||||
|
||||
export class Target {
|
||||
readonly _browserContext: BrowserContext;
|
||||
readonly _browserContext: BrowserContext<Browser>;
|
||||
readonly _targetId: string;
|
||||
readonly _type: 'page' | 'service-worker' | 'worker';
|
||||
private readonly _session: TargetSession;
|
||||
private _pagePromise: Promise<Page<Browser, BrowserContext>> | null = null;
|
||||
_page: Page<Browser, BrowserContext> | null = null;
|
||||
private _pagePromise: Promise<Page<Browser>> | 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];
|
||||
}
|
||||
|
||||
constructor(session: TargetSession, targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext) {
|
||||
constructor(session: TargetSession, targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext<Browser>) {
|
||||
const {targetId, type} = targetInfo;
|
||||
this._session = session;
|
||||
this._browserContext = browserContext;
|
||||
@ -83,7 +84,7 @@ export class Target {
|
||||
(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) {
|
||||
const browser = this._browserContext.browser();
|
||||
// Reference local page variable as _page may be
|
||||
|
@ -2,7 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export { TimeoutError } from '../Errors';
|
||||
export { Browser, BrowserContext } from './Browser';
|
||||
export { Browser } from './Browser';
|
||||
export { BrowserContext } from '../browserContext';
|
||||
export { BrowserFetcher } from '../browserFetcher';
|
||||
export { ExecutionContext, JSHandle } from '../javascript';
|
||||
export { ElementHandle } from '../dom';
|
||||
|
@ -22,7 +22,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
|
||||
describe('JSCoverage', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
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();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||
|
Loading…
Reference in New Issue
Block a user