mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 05:37:20 +03:00
chore: encapsulate target business in Browser class (#151)
Page and BrowserContext are now closer to be reused between browsers.
This commit is contained in:
parent
ed39499cea
commit
51ca756efe
@ -119,7 +119,7 @@ export class Browser extends EventEmitter {
|
||||
const target = this._targets.get(event.targetId);
|
||||
target._initializedCallback(false);
|
||||
this._targets.delete(event.targetId);
|
||||
target._closedCallback();
|
||||
target._didClose();
|
||||
if (await target._initializedPromise)
|
||||
this.chromium.emit(Events.Chromium.TargetDestroyed, target);
|
||||
}
|
||||
@ -146,14 +146,24 @@ export class Browser extends EventEmitter {
|
||||
return page;
|
||||
}
|
||||
|
||||
async _closeTarget(target: Target) {
|
||||
await this._client.send('Target.closeTarget', { targetId: target._targetId });
|
||||
async _closePage(page: Page) {
|
||||
await this._client.send('Target.closeTarget', { targetId: Target.fromPage(page)._targetId });
|
||||
}
|
||||
|
||||
_allTargets(): Target[] {
|
||||
return Array.from(this._targets.values()).filter(target => target._isInitialized);
|
||||
}
|
||||
|
||||
async _pages(context: BrowserContext): Promise<Page[]> {
|
||||
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) {
|
||||
await page._client.send('Target.activateTarget', {targetId: Target.fromPage(page)._targetId});
|
||||
}
|
||||
|
||||
async _waitForTarget(predicate: (arg0: Target) => boolean, options: { timeout?: number; } | undefined = {}): Promise<Target> {
|
||||
const {
|
||||
timeout = 30000
|
||||
|
@ -21,7 +21,6 @@ import { Browser } from './Browser';
|
||||
import { CDPSession } from './Connection';
|
||||
import { Permissions } from './features/permissions';
|
||||
import { Page } from './Page';
|
||||
import { Target } from './Target';
|
||||
|
||||
export class BrowserContext {
|
||||
readonly permissions: Permissions;
|
||||
@ -35,17 +34,8 @@ export class BrowserContext {
|
||||
this.permissions = new Permissions(client, contextId);
|
||||
}
|
||||
|
||||
_targets(): Target[] {
|
||||
return this._browser._allTargets().filter(target => target.browserContext() === this);
|
||||
}
|
||||
|
||||
async pages(): Promise<Page[]> {
|
||||
const pages = await Promise.all(
|
||||
this._targets()
|
||||
.filter(target => target.type() === 'page')
|
||||
.map(target => target.page())
|
||||
);
|
||||
return pages.filter(page => !!page);
|
||||
pages(): Promise<Page[]> {
|
||||
return this._browser._pages(this);
|
||||
}
|
||||
|
||||
isIncognito(): boolean {
|
||||
|
@ -35,7 +35,6 @@ import { RawMouseImpl, RawKeyboardImpl } from './Input';
|
||||
import { NetworkManagerEvents } from './NetworkManager';
|
||||
import { Protocol } from './protocol';
|
||||
import { getExceptionMessage, releaseObject } from './protocolHelper';
|
||||
import { Target } from './Target';
|
||||
import * as input from '../input';
|
||||
import * as types from '../types';
|
||||
import * as frames from '../frames';
|
||||
@ -58,8 +57,10 @@ export type Viewport = {
|
||||
|
||||
export class Page extends EventEmitter {
|
||||
private _closed = false;
|
||||
private _closedCallback: () => void;
|
||||
private _closedPromise: Promise<void>;
|
||||
_client: CDPSession;
|
||||
_target: Target;
|
||||
private _browserContext: BrowserContext;
|
||||
private _keyboard: input.Keyboard;
|
||||
private _mouse: input.Mouse;
|
||||
private _timeoutSettings: TimeoutSettings;
|
||||
@ -79,18 +80,19 @@ export class Page extends EventEmitter {
|
||||
private _disconnectPromise: Promise<Error> | undefined;
|
||||
private _emulatedMediaType: string | undefined;
|
||||
|
||||
static async create(client: CDPSession, target: Target, ignoreHTTPSErrors: boolean, defaultViewport: Viewport | null, screenshotter: Screenshotter): Promise<Page> {
|
||||
const page = new Page(client, target, ignoreHTTPSErrors, screenshotter);
|
||||
static async create(client: CDPSession, browserContext: BrowserContext, ignoreHTTPSErrors: boolean, defaultViewport: Viewport | null, screenshotter: Screenshotter): Promise<Page> {
|
||||
const page = new Page(client, browserContext, ignoreHTTPSErrors, screenshotter);
|
||||
await page._initialize();
|
||||
if (defaultViewport)
|
||||
await page.setViewport(defaultViewport);
|
||||
return page;
|
||||
}
|
||||
|
||||
constructor(client: CDPSession, target: Target, ignoreHTTPSErrors: boolean, screenshotter: Screenshotter) {
|
||||
constructor(client: CDPSession, browserContext: BrowserContext, ignoreHTTPSErrors: boolean, screenshotter: Screenshotter) {
|
||||
super();
|
||||
this._client = client;
|
||||
this._target = target;
|
||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||
this._browserContext = browserContext;
|
||||
this._keyboard = new input.Keyboard(new RawKeyboardImpl(client));
|
||||
this._mouse = new input.Mouse(new RawMouseImpl(client), this._keyboard);
|
||||
this._timeoutSettings = new TimeoutSettings();
|
||||
@ -134,10 +136,13 @@ export class Page extends EventEmitter {
|
||||
client.on('Inspector.targetCrashed', event => this._onTargetCrashed());
|
||||
client.on('Log.entryAdded', event => this._onLogEntryAdded(event));
|
||||
client.on('Page.fileChooserOpened', event => this._onFileChooserOpened(event));
|
||||
this._target._isClosedPromise.then(() => {
|
||||
this.emit(Events.Page.Close);
|
||||
this._closed = true;
|
||||
});
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
assert(!this._closed, 'Page closed twice');
|
||||
this._closed = true;
|
||||
this.emit(Events.Page.Close);
|
||||
this._closedCallback();
|
||||
}
|
||||
|
||||
async _initialize() {
|
||||
@ -179,11 +184,11 @@ export class Page extends EventEmitter {
|
||||
}
|
||||
|
||||
browser(): Browser {
|
||||
return this._target.browser();
|
||||
return this._browserContext.browser();
|
||||
}
|
||||
|
||||
browserContext(): BrowserContext {
|
||||
return this._target.browserContext();
|
||||
return this._browserContext;
|
||||
}
|
||||
|
||||
_onTargetCrashed() {
|
||||
@ -518,8 +523,8 @@ export class Page extends EventEmitter {
|
||||
if (runBeforeUnload) {
|
||||
await this._client.send('Page.close');
|
||||
} else {
|
||||
await this.browser()._closeTarget(this._target);
|
||||
await this._target._isClosedPromise;
|
||||
await this.browser()._closePage(this);
|
||||
await this._closedPromise;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ export class Screenshotter {
|
||||
}
|
||||
|
||||
private async _screenshot(page: Page, format: 'png' | 'jpeg', options: ScreenshotOptions): Promise<Buffer | string> {
|
||||
await page._client.send('Target.activateTarget', {targetId: page._target._targetId});
|
||||
await page.browser()._activatePage(page);
|
||||
let clip = options.clip ? processClip(options.clip) : undefined;
|
||||
const viewport = page.viewport();
|
||||
|
||||
|
@ -24,6 +24,8 @@ import { Page, Viewport } from './Page';
|
||||
import { Protocol } from './protocol';
|
||||
import { Screenshotter } from './Screenshotter';
|
||||
|
||||
const targetSymbol = Symbol('target');
|
||||
|
||||
export class Target {
|
||||
private _targetInfo: Protocol.Target.TargetInfo;
|
||||
private _browserContext: BrowserContext;
|
||||
@ -36,10 +38,12 @@ export class Target {
|
||||
private _workerPromise: Promise<Worker> | null = null;
|
||||
_initializedPromise: Promise<boolean>;
|
||||
_initializedCallback: (value?: unknown) => void;
|
||||
_isClosedPromise: Promise<void>;
|
||||
_closedCallback: (value?: unknown) => void;
|
||||
_isInitialized: boolean;
|
||||
|
||||
static fromPage(page: Page): Target {
|
||||
return (page as any)[targetSymbol];
|
||||
}
|
||||
|
||||
constructor(
|
||||
targetInfo: Protocol.Target.TargetInfo,
|
||||
browserContext: BrowserContext,
|
||||
@ -67,16 +71,23 @@ export class Target {
|
||||
openerPage.emit(Events.Page.Popup, popupPage);
|
||||
return true;
|
||||
});
|
||||
this._isClosedPromise = new Promise(fulfill => this._closedCallback = fulfill);
|
||||
this._isInitialized = this._targetInfo.type !== 'page' || this._targetInfo.url !== '';
|
||||
if (this._isInitialized)
|
||||
this._initializedCallback(true);
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
if (this._pagePromise)
|
||||
this._pagePromise.then(page => page._didClose());
|
||||
}
|
||||
|
||||
async page(): Promise<Page | null> {
|
||||
if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) {
|
||||
this._pagePromise = this._sessionFactory()
|
||||
.then(client => Page.create(client, this, this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotter));
|
||||
this._pagePromise = this._sessionFactory().then(async client => {
|
||||
const page = await Page.create(client, this._browserContext, this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotter);
|
||||
page[targetSymbol] = this;
|
||||
return page;
|
||||
});
|
||||
}
|
||||
return this._pagePromise;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ export class Chromium extends EventEmitter {
|
||||
}
|
||||
|
||||
pageTarget(page: Page): Target {
|
||||
return page._target;
|
||||
return Target.fromPage(page);
|
||||
}
|
||||
|
||||
waitForTarget(predicate: (arg0: Target) => boolean, options: { timeout?: number; } | undefined = {}): Promise<Target> {
|
||||
|
@ -150,6 +150,12 @@ export class Browser extends EventEmitter {
|
||||
return Array.from(this._targets.values());
|
||||
}
|
||||
|
||||
async _pages(context: BrowserContext): Promise<Page[]> {
|
||||
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);
|
||||
@ -166,7 +172,7 @@ export class Browser extends EventEmitter {
|
||||
_onTargetDestroyed({targetId}) {
|
||||
const target = this._targets.get(targetId);
|
||||
this._targets.delete(targetId);
|
||||
target._closedCallback();
|
||||
target._didClose();
|
||||
}
|
||||
|
||||
_onTargetInfoChanged({targetId, url}) {
|
||||
@ -189,8 +195,6 @@ export class Target {
|
||||
private _type: 'page' | 'browser';
|
||||
_url: string;
|
||||
private _openerId: string;
|
||||
_isClosedPromise: Promise<unknown>;
|
||||
_closedCallback: (value?: unknown) => void;
|
||||
|
||||
constructor(connection: any, browser: Browser, context: BrowserContext, targetId: string, type: 'page' | 'browser', url: string, openerId: string | undefined) {
|
||||
this._browser = browser;
|
||||
@ -200,9 +204,12 @@ export class Target {
|
||||
this._type = type;
|
||||
this._url = url;
|
||||
this._openerId = openerId;
|
||||
this._isClosedPromise = new Promise(fulfill => this._closedCallback = fulfill);
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
if (this._pagePromise)
|
||||
this._pagePromise.then(page => page._didClose());
|
||||
}
|
||||
|
||||
opener(): Target | null {
|
||||
return this._openerId ? this._browser._targets.get(this._openerId) : null;
|
||||
@ -225,7 +232,7 @@ export class Target {
|
||||
async page() {
|
||||
if (this._type === 'page' && !this._pagePromise) {
|
||||
const session = await this._connection.createSession(this._targetId);
|
||||
this._pagePromise = Page.create(session, this, this._browser._defaultViewport);
|
||||
this._pagePromise = Page.create(session, this._context, this._browser._defaultViewport);
|
||||
}
|
||||
return this._pagePromise;
|
||||
}
|
||||
@ -248,17 +255,8 @@ export class BrowserContext {
|
||||
this.permissions = new Permissions(connection, browserContextId);
|
||||
}
|
||||
|
||||
_targets(): Array<Target> {
|
||||
return this._browser._allTargets().filter(target => target.browserContext() === this);
|
||||
}
|
||||
|
||||
async pages(): Promise<Array<Page>> {
|
||||
const pages = await Promise.all(
|
||||
this._targets()
|
||||
.filter(target => target.type() === 'page')
|
||||
.map(target => target.page())
|
||||
);
|
||||
return pages.filter(page => !!page);
|
||||
pages(): Promise<Page[]> {
|
||||
return this._browser._pages(this);
|
||||
}
|
||||
|
||||
isIncognito(): boolean {
|
||||
|
@ -21,7 +21,7 @@ import * as mime from 'mime';
|
||||
import { TimeoutError } from '../Errors';
|
||||
import { assert, debugError, helper, RegisteredListener } from '../helper';
|
||||
import { TimeoutSettings } from '../TimeoutSettings';
|
||||
import { BrowserContext, Target } from './Browser';
|
||||
import { BrowserContext } from './Browser';
|
||||
import { JugglerSession, JugglerSessionEvents } from './Connection';
|
||||
import { Events } from './events';
|
||||
import { Accessibility } from './features/accessibility';
|
||||
@ -44,12 +44,14 @@ const writeFileAsync = helper.promisify(fs.writeFile);
|
||||
export class Page extends EventEmitter {
|
||||
private _timeoutSettings: TimeoutSettings;
|
||||
private _session: JugglerSession;
|
||||
private _target: Target;
|
||||
private _browserContext: BrowserContext;
|
||||
private _keyboard: input.Keyboard;
|
||||
private _mouse: input.Mouse;
|
||||
readonly accessibility: Accessibility;
|
||||
readonly interception: Interception;
|
||||
private _closed: boolean;
|
||||
private _closedCallback: () => void;
|
||||
private _closedPromise: Promise<void>;
|
||||
private _pageBindings: Map<string, Function>;
|
||||
private _networkManager: NetworkManager;
|
||||
_frameManager: FrameManager;
|
||||
@ -59,8 +61,8 @@ export class Page extends EventEmitter {
|
||||
private _disconnectPromise: Promise<Error>;
|
||||
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
|
||||
|
||||
static async create(session: JugglerSession, target: Target, defaultViewport: Viewport | null) {
|
||||
const page = new Page(session, target);
|
||||
static async create(session: JugglerSession, browserContext: BrowserContext, defaultViewport: Viewport | null) {
|
||||
const page = new Page(session, browserContext);
|
||||
await Promise.all([
|
||||
session.send('Runtime.enable'),
|
||||
session.send('Network.enable'),
|
||||
@ -73,15 +75,16 @@ export class Page extends EventEmitter {
|
||||
return page;
|
||||
}
|
||||
|
||||
constructor(session: JugglerSession, target: Target) {
|
||||
constructor(session: JugglerSession, browserContext: BrowserContext) {
|
||||
super();
|
||||
this._timeoutSettings = new TimeoutSettings();
|
||||
this._session = session;
|
||||
this._target = target;
|
||||
this._browserContext = browserContext;
|
||||
this._keyboard = new input.Keyboard(new RawKeyboardImpl(session));
|
||||
this._mouse = new input.Mouse(new RawMouseImpl(session), this._keyboard);
|
||||
this.accessibility = new Accessibility(session);
|
||||
this._closed = false;
|
||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||
this._pageBindings = new Map();
|
||||
this._networkManager = new NetworkManager(session);
|
||||
this._frameManager = new FrameManager(session, this, this._networkManager, this._timeoutSettings);
|
||||
@ -104,13 +107,16 @@ export class Page extends EventEmitter {
|
||||
helper.addEventListener(this._networkManager, NetworkManagerEvents.RequestFailed, request => this.emit(Events.Page.RequestFailed, request)),
|
||||
];
|
||||
this._viewport = null;
|
||||
this._target._isClosedPromise.then(() => {
|
||||
this._closed = true;
|
||||
this._frameManager.dispose();
|
||||
this._networkManager.dispose();
|
||||
helper.removeEventListeners(this._eventListeners);
|
||||
this.emit(Events.Page.Close);
|
||||
});
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
assert(!this._closed, 'Page closed twice');
|
||||
this._closed = true;
|
||||
this._frameManager.dispose();
|
||||
this._networkManager.dispose();
|
||||
helper.removeEventListeners(this._eventListeners);
|
||||
this.emit(Events.Page.Close);
|
||||
this._closedCallback();
|
||||
}
|
||||
|
||||
async setExtraHTTPHeaders(headers) {
|
||||
@ -250,7 +256,7 @@ export class Page extends EventEmitter {
|
||||
}
|
||||
|
||||
browserContext(): BrowserContext {
|
||||
return this._target.browserContext();
|
||||
return this._browserContext;
|
||||
}
|
||||
|
||||
_onUncaughtError(params) {
|
||||
@ -288,7 +294,7 @@ export class Page extends EventEmitter {
|
||||
}
|
||||
|
||||
browser() {
|
||||
return this._target.browser();
|
||||
return this._browserContext.browser();
|
||||
}
|
||||
|
||||
url() {
|
||||
@ -535,7 +541,7 @@ export class Page extends EventEmitter {
|
||||
} = options;
|
||||
await this._session.send('Page.close', { runBeforeUnload });
|
||||
if (!runBeforeUnload)
|
||||
await this._target._isClosedPromise;
|
||||
await this._closedPromise;
|
||||
}
|
||||
|
||||
async content() {
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
import * as childProcess from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import { assert, helper, RegisteredListener, debugError } from '../helper';
|
||||
import { filterCookies, NetworkCookie, rewriteCookies, SetNetworkCookieParam } from '../network';
|
||||
import { Connection } from './Connection';
|
||||
import { Page, Viewport } from './Page';
|
||||
@ -167,7 +167,23 @@ export class Browser extends EventEmitter {
|
||||
_onTargetDestroyed({targetId}) {
|
||||
const target = this._targets.get(targetId);
|
||||
this._targets.delete(targetId);
|
||||
target._closedCallback();
|
||||
target._didClose();
|
||||
}
|
||||
|
||||
_closePage(page: Page) {
|
||||
this._connection.send('Target.close', {
|
||||
targetId: Target.fromPage(page)._targetId
|
||||
}).catch(debugError);
|
||||
}
|
||||
|
||||
async _pages(context: BrowserContext): Promise<Page[]> {
|
||||
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): Promise<void> {
|
||||
await this._connection.send('Target.activate', { targetId: Target.fromPage(page)._targetId });
|
||||
}
|
||||
|
||||
async _onProvisionalTargetCommitted({oldTargetId, newTargetId}) {
|
||||
@ -177,8 +193,12 @@ export class Browser extends EventEmitter {
|
||||
const page = await oldTarget._pagePromise;
|
||||
const newTarget = this._targets.get(newTargetId);
|
||||
const newSession = this._connection.session(newTargetId);
|
||||
page._swapTargetOnNavigation(newSession, newTarget);
|
||||
page._swapSessionOnNavigation(newSession);
|
||||
newTarget._pagePromise = oldTarget._pagePromise;
|
||||
newTarget._adoptPage(page);
|
||||
// Old target should not be accessed by anyone. Reset page promise so that
|
||||
// old target does not close the page on connection reset.
|
||||
oldTarget._pagePromise = null;
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
@ -204,17 +224,8 @@ export class BrowserContext {
|
||||
this._id = contextId;
|
||||
}
|
||||
|
||||
_targets(): Target[] {
|
||||
return this._browser.targets().filter(target => target.browserContext() === this);
|
||||
}
|
||||
|
||||
async pages(): Promise<Page[]> {
|
||||
const pages = await Promise.all(
|
||||
this._targets()
|
||||
.filter(target => target.type() === 'page')
|
||||
.map(target => target.page())
|
||||
);
|
||||
return pages.filter(page => !!page);
|
||||
pages(): Promise<Page[]> {
|
||||
return this._browser._pages(this);
|
||||
}
|
||||
|
||||
isIncognito(): boolean {
|
||||
|
@ -91,7 +91,7 @@ export class FrameManager extends EventEmitter implements frames.FrameDelegate {
|
||||
];
|
||||
}
|
||||
|
||||
async _swapTargetOnNavigation(newSession) {
|
||||
async _swapSessionOnNavigation(newSession) {
|
||||
helper.removeEventListeners(this._sessionListeners);
|
||||
this.disconnectFromTarget();
|
||||
this._session = newSession;
|
||||
|
@ -28,7 +28,6 @@ import { FrameManager, FrameManagerEvents } from './FrameManager';
|
||||
import { RawKeyboardImpl, RawMouseImpl } from './Input';
|
||||
import { NetworkManagerEvents } from './NetworkManager';
|
||||
import { Protocol } from './protocol';
|
||||
import { Target } from './Target';
|
||||
import { TaskQueue } from './TaskQueue';
|
||||
import * as input from '../input';
|
||||
import * as types from '../types';
|
||||
@ -48,8 +47,10 @@ export type Viewport = {
|
||||
|
||||
export class Page extends EventEmitter {
|
||||
private _closed = false;
|
||||
private _closedCallback: () => void;
|
||||
private _closedPromise: Promise<void>;
|
||||
_session: TargetSession;
|
||||
private _target: Target;
|
||||
private _browserContext: BrowserContext;
|
||||
private _keyboard: input.Keyboard;
|
||||
private _mouse: input.Mouse;
|
||||
private _timeoutSettings: TimeoutSettings;
|
||||
@ -64,16 +65,17 @@ export class Page extends EventEmitter {
|
||||
private _emulatedMediaType: string | undefined;
|
||||
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
|
||||
|
||||
static async create(session: TargetSession, target: Target, defaultViewport: Viewport | null, screenshotTaskQueue: TaskQueue): Promise<Page> {
|
||||
const page = new Page(session, target, screenshotTaskQueue);
|
||||
static async create(session: TargetSession, browserContext: BrowserContext, defaultViewport: Viewport | null, screenshotTaskQueue: TaskQueue): Promise<Page> {
|
||||
const page = new Page(session, browserContext, screenshotTaskQueue);
|
||||
await page._initialize();
|
||||
if (defaultViewport)
|
||||
await page.setViewport(defaultViewport);
|
||||
return page;
|
||||
}
|
||||
|
||||
constructor(session: TargetSession, target: Target, screenshotTaskQueue: TaskQueue) {
|
||||
constructor(session: TargetSession, browserContext: BrowserContext, screenshotTaskQueue: TaskQueue) {
|
||||
super();
|
||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||
this._keyboard = new input.Keyboard(new RawKeyboardImpl(session));
|
||||
this._mouse = new input.Mouse(new RawMouseImpl(session), this._keyboard);
|
||||
this._timeoutSettings = new TimeoutSettings();
|
||||
@ -82,7 +84,7 @@ export class Page extends EventEmitter {
|
||||
this._screenshotTaskQueue = screenshotTaskQueue;
|
||||
|
||||
this._setSession(session);
|
||||
this._setTarget(target);
|
||||
this._browserContext = browserContext;
|
||||
|
||||
this._frameManager.on(FrameManagerEvents.FrameAttached, event => this.emit(Events.Page.FrameAttached, event));
|
||||
this._frameManager.on(FrameManagerEvents.FrameDetached, event => this.emit(Events.Page.FrameDetached, event));
|
||||
@ -95,6 +97,13 @@ export class Page extends EventEmitter {
|
||||
networkManager.on(NetworkManagerEvents.RequestFinished, event => this.emit(Events.Page.RequestFinished, event));
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
assert(!this._closed, 'Page closed twice');
|
||||
this._closed = true;
|
||||
this.emit(Events.Page.Close);
|
||||
this._closedCallback();
|
||||
}
|
||||
|
||||
async _initialize() {
|
||||
return Promise.all([
|
||||
this._frameManager.initialize(),
|
||||
@ -127,29 +136,18 @@ export class Page extends EventEmitter {
|
||||
event.defaultPrompt));
|
||||
}
|
||||
|
||||
_setTarget(newTarget: Target) {
|
||||
this._target = newTarget;
|
||||
this._target._isClosedPromise.then(() => {
|
||||
if (this._target !== newTarget)
|
||||
return;
|
||||
this.emit(Events.Page.Close);
|
||||
this._closed = true;
|
||||
});
|
||||
}
|
||||
|
||||
async _swapTargetOnNavigation(newSession : TargetSession, newTarget : Target) {
|
||||
async _swapSessionOnNavigation(newSession: TargetSession) {
|
||||
this._setSession(newSession);
|
||||
this._setTarget(newTarget);
|
||||
await this._frameManager._swapTargetOnNavigation(newSession);
|
||||
await this._frameManager._swapSessionOnNavigation(newSession);
|
||||
await this._initialize().catch(e => debugError('failed to enable agents after swap: ' + e));
|
||||
}
|
||||
|
||||
browser(): Browser {
|
||||
return this._target.browser();
|
||||
return this._browserContext.browser();
|
||||
}
|
||||
|
||||
browserContext(): BrowserContext {
|
||||
return this._target.browserContext();
|
||||
return this._browserContext;
|
||||
}
|
||||
|
||||
_onTargetCrashed() {
|
||||
@ -419,7 +417,7 @@ export class Page extends EventEmitter {
|
||||
Object.assign(params, this._viewport);
|
||||
}
|
||||
const [, result] = await Promise.all([
|
||||
this._session._connection.send('Target.activate', { targetId: this._target._targetId }),
|
||||
this.browser()._activatePage(this),
|
||||
this._session.send('Page.snapshotRect', params),
|
||||
]).catch(e => {
|
||||
debugError('Failed to take screenshot: ' + e);
|
||||
@ -437,12 +435,8 @@ export class Page extends EventEmitter {
|
||||
}
|
||||
|
||||
async close() {
|
||||
this.browser()._connection.send('Target.close', {
|
||||
targetId: this._target._targetId
|
||||
}).catch(e => {
|
||||
debugError(e);
|
||||
});
|
||||
await this._target._isClosedPromise;
|
||||
this.browser()._closePage(this);
|
||||
await this._closedPromise;
|
||||
}
|
||||
|
||||
isClosed(): boolean {
|
||||
|
@ -20,6 +20,8 @@ import { Browser, BrowserContext } from './Browser';
|
||||
import { Page } from './Page';
|
||||
import { Protocol } from './protocol';
|
||||
|
||||
const targetSymbol = Symbol('target');
|
||||
|
||||
export class Target {
|
||||
private _browserContext: BrowserContext;
|
||||
_targetId: string;
|
||||
@ -28,11 +30,13 @@ export class Target {
|
||||
private _url: string;
|
||||
_initializedPromise: Promise<boolean>;
|
||||
_initializedCallback: (value?: unknown) => void;
|
||||
_isClosedPromise: Promise<void>;
|
||||
_closedCallback: (value?: unknown) => void;
|
||||
_isInitialized: boolean;
|
||||
_eventListeners: RegisteredListener[];
|
||||
|
||||
static fromPage(page: Page): Target {
|
||||
return (page as any)[targetSymbol];
|
||||
}
|
||||
|
||||
constructor(targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext) {
|
||||
const {targetId, url, type} = targetInfo;
|
||||
this._browserContext = browserContext;
|
||||
@ -41,13 +45,24 @@ export class Target {
|
||||
/** @type {?Promise<!Page>} */
|
||||
this._pagePromise = null;
|
||||
this._url = url;
|
||||
this._isClosedPromise = new Promise(fulfill => this._closedCallback = fulfill);
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
if (this._pagePromise)
|
||||
this._pagePromise.then(page => page._didClose());
|
||||
}
|
||||
|
||||
_adoptPage(page: Page) {
|
||||
(page as any)[targetSymbol] = this;
|
||||
}
|
||||
|
||||
async page(): Promise<Page | null> {
|
||||
if (this._type === 'page' && !this._pagePromise) {
|
||||
const session = this.browser()._connection.session(this._targetId);
|
||||
this._pagePromise = Page.create(session, this, this.browser()._defaultViewport, this.browser()._screenshotTaskQueue);
|
||||
this._pagePromise = Page.create(session, this._browserContext, this.browser()._defaultViewport, this.browser()._screenshotTaskQueue).then(page => {
|
||||
this._adoptPage(page);
|
||||
return page;
|
||||
});
|
||||
}
|
||||
return this._pagePromise;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user