diff --git a/package.json b/package.json index 9e50325f3c..4982812c71 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "playwright": { "chromium_revision": "724623", "firefox_revision": "1009", - "webkit_revision": "1063" + "webkit_revision": "1065" }, "scripts": { "unit": "node test/test.js", diff --git a/src/webkit/wkBrowser.ts b/src/webkit/wkBrowser.ts index c465a4d553..2276329090 100644 --- a/src/webkit/wkBrowser.ts +++ b/src/webkit/wkBrowser.ts @@ -15,46 +15,39 @@ * limitations under the License. */ -import { helper, RegisteredListener, debugError, assert } from '../helper'; import * as browser from '../browser'; -import * as network from '../network'; -import * as types from '../types'; -import { WKConnection, WKConnectionEvents, WKTargetSession } from './wkConnection'; -import { Page } from '../page'; -import { WKTarget } from './wkTarget'; -import { Protocol } from './protocol'; -import { Events } from '../events'; import { BrowserContext, BrowserContextOptions } from '../browserContext'; +import { assert, debugError, helper, RegisteredListener } from '../helper'; +import * as network from '../network'; +import { Page } from '../page'; import { ConnectionTransport } from '../transport'; +import * as types from '../types'; +import { Protocol } from './protocol'; +import { WKConnection, WKConnectionEvents, WKPageProxySession } from './wkConnection'; +import { WKPageProxy } from './wkPageProxy'; export class WKBrowser extends browser.Browser { readonly _connection: WKConnection; private readonly _defaultContext: BrowserContext; private readonly _contexts = new Map(); - private readonly _targets = new Map(); + private readonly _pageProxies = new Map(); private readonly _eventListeners: RegisteredListener[]; - private _firstTargetCallback?: () => void; - private readonly _firstTargetPromise: Promise; + private _firstPageProxyCallback?: () => void; + private readonly _firstPageProxyPromise: Promise; constructor(transport: ConnectionTransport) { super(); this._connection = new WKConnection(transport); - /** @type {!Map} */ - this._targets = new Map(); - this._defaultContext = this._createBrowserContext(undefined, {}); - /** @type {!Map} */ - this._contexts = new Map(); this._eventListeners = [ - helper.addEventListener(this._connection, WKConnectionEvents.TargetCreated, this._onTargetCreated.bind(this)), - helper.addEventListener(this._connection, WKConnectionEvents.TargetDestroyed, this._onTargetDestroyed.bind(this)), - helper.addEventListener(this._connection, WKConnectionEvents.DidCommitProvisionalTarget, this._onProvisionalTargetCommitted.bind(this)), + helper.addEventListener(this._connection, WKConnectionEvents.PageProxyCreated, this._onPageProxyCreated.bind(this)), + helper.addEventListener(this._connection, WKConnectionEvents.PageProxyDestroyed, this._onPageProxyDestroyed.bind(this)) ]; - this._firstTargetPromise = new Promise(resolve => this._firstTargetCallback = resolve); + this._firstPageProxyPromise = new Promise(resolve => this._firstPageProxyCallback = resolve); // Intercept provisional targets during cross-process navigation. this._connection.send('Target.setPauseOnStart', { pauseOnStart: true }).catch(e => { @@ -81,65 +74,40 @@ export class WKBrowser extends browser.Browser { } async _waitForFirstPageTarget(timeout: number): Promise { - assert(!this._targets.size); - await helper.waitWithTimeout(this._firstTargetPromise, 'target', timeout); + assert(!this._pageProxies.size); + await helper.waitWithTimeout(this._firstPageProxyPromise, 'firstPageProxy', timeout); } - _onTargetCreated(session: WKTargetSession, targetInfo: Protocol.Target.TargetInfo) { - assert(targetInfo.type === 'page', 'Only page targets are expected in WebKit, received: ' + targetInfo.type); + _onPageProxyCreated(session: WKPageProxySession, pageProxyInfo: Protocol.Browser.PageProxyInfo) { let context = null; - if (targetInfo.browserContextId) { + if (pageProxyInfo.browserContextId) { // FIXME: we don't know about the default context id, so assume that all targets from // unknown contexts are created in the 'default' context which can in practice be represented // by multiple actual contexts in WebKit. Solving this properly will require adding context // lifecycle events. - context = this._contexts.get(targetInfo.browserContextId); - // if (!context) - // throw new Error(`Target ${targetId} created in unknown browser context ${browserContextId}.`); + context = this._contexts.get(pageProxyInfo.browserContextId); } if (!context) context = this._defaultContext; - const target = new WKTarget(this, session, targetInfo, context); - this._targets.set(targetInfo.targetId, target); - if (targetInfo.isProvisional) { - const oldTarget = this._targets.get(targetInfo.oldTargetId); - if (oldTarget) - oldTarget._initializeSession(session); + const pageProxy = new WKPageProxy(this, session, context); + this._pageProxies.set(pageProxyInfo.pageProxyId, pageProxy); + + if (pageProxyInfo.openerId) { + const opener = this._pageProxies.get(pageProxyInfo.openerId); + if (opener) + opener.onPopupCreated(pageProxy); } - if (this._firstTargetCallback) { - this._firstTargetCallback(); - this._firstTargetCallback = null; + + if (this._firstPageProxyCallback) { + this._firstPageProxyCallback(); + this._firstPageProxyCallback = null; } - if (!targetInfo.oldTargetId && targetInfo.openerId) { - const opener = this._targets.get(targetInfo.openerId); - if (!opener) - return; - const openerPage = opener._wkPage ? opener._wkPage._page : null; - if (!openerPage || !openerPage.listenerCount(Events.Page.Popup)) - return; - target.page().then(page => openerPage.emit(Events.Page.Popup, page)); - } - if (targetInfo.isPaused) - this._connection.send('Target.resume', { targetId: targetInfo.targetId }).catch(debugError); } - _onTargetDestroyed({targetId, crashed}) { - const target = this._targets.get(targetId); - this._targets.delete(targetId); - target._didClose(crashed); - } - - _closePage(targetId: string, runBeforeUnload: boolean) { - this._connection.send('Target.close', { - targetId, - runBeforeUnload - }).catch(debugError); - } - - async _onProvisionalTargetCommitted({oldTargetId, newTargetId}) { - const oldTarget = this._targets.get(oldTargetId); - const newTarget = this._targets.get(newTargetId); - newTarget._swapWith(oldTarget); + _onPageProxyDestroyed(pageProxyId: Protocol.Browser.PageProxyID) { + const pageProxy = this._pageProxies.get(pageProxyId); + pageProxy.dispose(); + this._pageProxies.delete(pageProxyId); } disconnect() { @@ -158,15 +126,15 @@ export class WKBrowser extends browser.Browser { _createBrowserContext(browserContextId: string | undefined, options: BrowserContextOptions): BrowserContext { const context = new BrowserContext({ pages: async (): Promise => { - const targets = Array.from(this._targets.values()).filter(target => target._browserContext === context && !target._session.isProvisional()); - const pages = await Promise.all(targets.map(target => target.page())); + const pageProxies = Array.from(this._pageProxies.values()).filter(proxy => proxy._browserContext === context); + const pages = await Promise.all(pageProxies.map(proxy => proxy.page())); return pages.filter(page => !!page); }, newPage: async (): Promise => { - const { targetId } = await this._connection.send('Browser.createPage', { browserContextId }); - const target = this._targets.get(targetId); - return await target.page(); + const { pageProxyId } = await this._connection.send('Browser.createPage', { browserContextId }); + const pageProxy = this._pageProxies.get(pageProxyId); + return await pageProxy.page(); }, close: async (): Promise => { diff --git a/src/webkit/wkConnection.ts b/src/webkit/wkConnection.ts index c96e0a435c..9489e678e8 100644 --- a/src/webkit/wkConnection.ts +++ b/src/webkit/wkConnection.ts @@ -25,18 +25,23 @@ const debugProtocol = debug('playwright:protocol'); const debugWrappedMessage = require('debug')('wrapped'); export const WKConnectionEvents = { - TargetCreated: Symbol('ConnectionEvents.TargetCreated'), - TargetDestroyed: Symbol('Connection.TargetDestroyed'), - DidCommitProvisionalTarget: Symbol('Connection.DidCommitProvisionalTarget') + PageProxyCreated: Symbol('ConnectionEvents.PageProxyCreated'), + PageProxyDestroyed: Symbol('Connection.PageProxyDestroyed') +}; + +export const WKPageProxySessionEvents = { + TargetCreated: Symbol('PageProxyEvents.TargetCreated'), + TargetDestroyed: Symbol('PageProxyEvents.TargetDestroyed'), + DidCommitProvisionalTarget: Symbol('PageProxyEvents.DidCommitProvisionalTarget'), }; export class WKConnection extends EventEmitter { - _lastId = 0; + private _lastId = 0; private readonly _callbacks = new Map void, reject: (e: Error) => void, error: Error, method: string}>(); private readonly _transport: ConnectionTransport; - private readonly _sessions = new Map(); + private readonly _pageProxySessions = new Map(); - _closed = false; + private _closed = false; constructor(transport: ConnectionTransport) { super(); @@ -45,18 +50,23 @@ export class WKConnection extends EventEmitter { this._transport.onclose = this._onClose.bind(this); } + nextMessageId(): number { + return ++this._lastId; + } + send( method: T, - params?: Protocol.CommandParameters[T] + params?: Protocol.CommandParameters[T], + pageProxyId?: string ): Promise { - const id = this._rawSend({method, params}); + const id = this._rawSend({pageProxyId, method, params}); return new Promise((resolve, reject) => { this._callbacks.set(id, {resolve, reject, error: new Error(), method}); }); } _rawSend(message: any): number { - const id = ++this._lastId; + const id = this.nextMessageId(); message = JSON.stringify(Object.assign({}, message, {id})); debugProtocol('SEND ► ' + message); this._transport.send(message); @@ -66,7 +76,7 @@ export class WKConnection extends EventEmitter { private _dispatchMessage(message: string) { debugProtocol('◀ RECV ' + message); const object = JSON.parse(message); - this._dispatchTargetMessageToSession(object, message); + this._dispatchPageProxyMessage(object, message); if (object.id) { const callback = this._callbacks.get(object.id); // Callbacks could be all rejected if someone has called `.dispose()`. @@ -84,40 +94,21 @@ export class WKConnection extends EventEmitter { } } - _dispatchTargetMessageToSession(object: {method: string, params: any}, wrappedMessage: string) { - if (object.method === 'Target.targetCreated') { - const targetInfo = object.params.targetInfo as Protocol.Target.TargetInfo; - const session = new WKTargetSession(this, targetInfo); - this._sessions.set(session._sessionId, session); - Promise.resolve().then(() => this.emit(WKConnectionEvents.TargetCreated, session, object.params.targetInfo)); - } else if (object.method === 'Target.targetDestroyed') { - const session = this._sessions.get(object.params.targetId); - if (session) { - session._onClosed(); - this._sessions.delete(object.params.targetId); - } - Promise.resolve().then(() => this.emit(WKConnectionEvents.TargetDestroyed, { targetId: object.params.targetId, crashed: object.params.crashed })); - } else if (object.method === 'Target.dispatchMessageFromTarget') { - const {targetId, message} = object.params as Protocol.Target.dispatchMessageFromTargetPayload; - const session = this._sessions.get(targetId); - if (!session) - throw new Error('Unknown target: ' + targetId); - if (session.isProvisional()) - session._addProvisionalMessage(message); - else - session._dispatchMessageFromTarget(message); - } else if (object.method === 'Target.didCommitProvisionalTarget') { - const {oldTargetId, newTargetId} = object.params as Protocol.Target.didCommitProvisionalTargetPayload; - Promise.resolve().then(() => this.emit(WKConnectionEvents.DidCommitProvisionalTarget, { oldTargetId, newTargetId })); - const newSession = this._sessions.get(newTargetId); - if (!newSession) - throw new Error('Unknown new target: ' + newTargetId); - const oldSession = this._sessions.get(oldTargetId); - if (!oldSession) - throw new Error('Unknown old target: ' + oldTargetId); - oldSession._swappedOut = true; - for (const message of newSession._takeProvisionalMessagesAndCommit()) - newSession._dispatchMessageFromTarget(message); + _dispatchPageProxyMessage(object: {method: string, params: any, id?: string, pageProxyId?: string}, message: string) { + if (object.method === 'Browser.pageProxyCreated') { + const pageProxyId = object.params.pageProxyInfo.pageProxyId; + const pageProxySession = new WKPageProxySession(this, pageProxyId); + this._pageProxySessions.set(pageProxyId, pageProxySession); + Promise.resolve().then(() => this.emit(WKConnectionEvents.PageProxyCreated, pageProxySession, object.params.pageProxyInfo)); + } else if (object.method === 'Browser.pageProxyDestroyed') { + const pageProxyId = object.params.pageProxyId as string; + const pageProxySession = this._pageProxySessions.get(pageProxyId); + this._pageProxySessions.delete(pageProxyId); + pageProxySession.dispose(); + Promise.resolve().then(() => this.emit(WKConnectionEvents.PageProxyDestroyed, pageProxyId)); + } else if (!object.id && object.pageProxyId) { + const pageProxySession = this._pageProxySessions.get(object.pageProxyId); + pageProxySession._dispatchEvent(object, message); } } @@ -130,9 +121,10 @@ export class WKConnection extends EventEmitter { for (const callback of this._callbacks.values()) callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`)); this._callbacks.clear(); - for (const session of this._sessions.values()) - session._onClosed(); - this._sessions.clear(); + + for (const pageProxySession of this._pageProxySessions.values()) + pageProxySession.dispose(); + this._pageProxySessions.clear(); } dispose() { @@ -145,11 +137,89 @@ export const WKTargetSessionEvents = { Disconnected: Symbol('TargetSessionEvents.Disconnected') }; +export class WKPageProxySession extends EventEmitter { + _connection: WKConnection; + private readonly _sessions = new Map(); + private readonly _callbacks = new Map void, reject: (e: Error) => void, error: Error, method: string}>(); + private readonly _pageProxyId: string; + on: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; + addListener: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; + off: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; + removeListener: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; + once: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; + + constructor(connection: WKConnection, pageProxyId: string) { + super(); + this._connection = connection; + this._pageProxyId = pageProxyId; + } + + send( + method: T, + params?: Protocol.CommandParameters[T] + ): Promise { + if (!this._connection) + return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the pageProxy has been closed.`)); + return this._connection.send(method, params, this._pageProxyId).catch(e => { + // There is a possible race of the connection closure. We may have received + // targetDestroyed notification before response for the command, in that + // case it's safe to swallow the exception. + }) as Promise; + } + + _dispatchEvent(object: {method: string, params: any, pageProxyId?: string}, wrappedMessage: string) { + if (object.method === 'Target.targetCreated') { + const targetInfo = object.params.targetInfo as Protocol.Target.TargetInfo; + const session = new WKTargetSession(this, targetInfo); + this._sessions.set(session._sessionId, session); + Promise.resolve().then(() => this.emit(WKPageProxySessionEvents.TargetCreated, session, object.params.targetInfo)); + } else if (object.method === 'Target.targetDestroyed') { + const session = this._sessions.get(object.params.targetId); + if (session) { + session._onClosed(); + this._sessions.delete(object.params.targetId); + } + Promise.resolve().then(() => this.emit(WKPageProxySessionEvents.TargetDestroyed, { targetId: object.params.targetId, crashed: object.params.crashed })); + } else if (object.method === 'Target.dispatchMessageFromTarget') { + const {targetId, message} = object.params as Protocol.Target.dispatchMessageFromTargetPayload; + const session = this._sessions.get(targetId); + if (!session) + throw new Error('Unknown target: ' + targetId); + if (session.isProvisional()) + session._addProvisionalMessage(message); + else + session._dispatchMessageFromTarget(message); + } else if (object.method === 'Target.didCommitProvisionalTarget') { + const {oldTargetId, newTargetId} = object.params as Protocol.Target.didCommitProvisionalTargetPayload; + Promise.resolve().then(() => this.emit(WKPageProxySessionEvents.DidCommitProvisionalTarget, { oldTargetId, newTargetId })); + const newSession = this._sessions.get(newTargetId); + if (!newSession) + throw new Error('Unknown new target: ' + newTargetId); + const oldSession = this._sessions.get(oldTargetId); + if (!oldSession) + throw new Error('Unknown old target: ' + oldTargetId); + oldSession._swappedOut = true; + for (const message of newSession._takeProvisionalMessagesAndCommit()) + newSession._dispatchMessageFromTarget(message); + } else { + Promise.resolve().then(() => this.emit(object.method, object.params)); + } + } + + dispose() { + for (const session of this._sessions.values()) + session._onClosed(); + this._sessions.clear(); + + this._connection = null; + } +} + export class WKTargetSession extends EventEmitter { - private _connection: WKConnection; - private _callbacks = new Map void, reject: (e: Error) => void, error: Error, method: string}>(); - private _targetType: string; - _sessionId: string; + _pageProxySession: WKPageProxySession; + private readonly _callbacks = new Map void, reject: (e: Error) => void, error: Error, method: string}>(); + private readonly _targetType: string; + readonly _sessionId: string; _swappedOut = false; private _provisionalMessages?: string[]; on: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; @@ -158,10 +228,10 @@ export class WKTargetSession extends EventEmitter { removeListener: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; once: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; - constructor(connection: WKConnection, targetInfo: Protocol.Target.TargetInfo) { + constructor(pageProxySession: WKPageProxySession, targetInfo: Protocol.Target.TargetInfo) { super(); const {targetId, type, isProvisional} = targetInfo; - this._connection = connection; + this._pageProxySession = pageProxySession; this._targetType = type; this._sessionId = targetId; if (isProvisional) @@ -172,13 +242,17 @@ export class WKTargetSession extends EventEmitter { return !!this._provisionalMessages; } + isClosed(): boolean { + return !this._pageProxySession; + } + send( method: T, params?: Protocol.CommandParameters[T] ): Promise { - if (!this._connection) + if (!this._pageProxySession) return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`)); - const innerId = ++this._connection._lastId; + const innerId = this._pageProxySession._connection.nextMessageId(); const messageObj = { id: innerId, method, @@ -190,7 +264,7 @@ export class WKTargetSession extends EventEmitter { const result = new Promise((resolve, reject) => { this._callbacks.set(innerId, {resolve, reject, error: new Error(), method}); }); - this._connection.send('Target.sendMessageToTarget', { + this._pageProxySession.send('Target.sendMessageToTarget', { message: message, targetId: this._sessionId }).catch(e => { // There is a possible race of the connection closure. We may have received @@ -238,7 +312,7 @@ export class WKTargetSession extends EventEmitter { callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`)); } this._callbacks.clear(); - this._connection = null; + this._pageProxySession = null; Promise.resolve().then(() => this.emit(WKTargetSessionEvents.Disconnected)); } } diff --git a/src/webkit/wkPage.ts b/src/webkit/wkPage.ts index 261d4463b5..1b8a865d09 100644 --- a/src/webkit/wkPage.ts +++ b/src/webkit/wkPage.ts @@ -367,7 +367,10 @@ export class WKPage implements PageDelegate { } async closePage(runBeforeUnload: boolean): Promise { - this._browser._closePage(this._session._sessionId, runBeforeUnload); + this._session._pageProxySession.send('Target.close', { + targetId: this._session._sessionId, + runBeforeUnload + }).catch(debugError); } getBoundingBoxForScreenshot(handle: dom.ElementHandle): Promise { diff --git a/src/webkit/wkPageProxy.ts b/src/webkit/wkPageProxy.ts new file mode 100644 index 0000000000..999096349c --- /dev/null +++ b/src/webkit/wkPageProxy.ts @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + + +import { BrowserContext } from '../browserContext'; +import { Page } from '../page'; +import { Protocol } from './protocol'; +import { WKPageProxySession, WKPageProxySessionEvents, WKTargetSession } from './wkConnection'; +import { WKPage } from './wkPage'; +import { WKBrowser } from './wkBrowser'; +import { RegisteredListener, helper, assert, debugError } from '../helper'; +import { Events } from '../events'; + +export class WKPageProxy { + private readonly _browser: WKBrowser; + private readonly _pageProxySession: WKPageProxySession; + readonly _browserContext: BrowserContext; + private _pagePromise: Promise | null = null; + private _wkPage: WKPage | null = null; + private readonly _firstTargetPromise: Promise; + private _firstTargetCallback: () => void; + private readonly _targetSessions = new Map(); + private readonly _eventListeners: RegisteredListener[]; + + constructor(browser: WKBrowser, session: WKPageProxySession, browserContext: BrowserContext) { + this._browser = browser; + this._pageProxySession = session; + this._browserContext = browserContext; + this._firstTargetPromise = new Promise(r => this._firstTargetCallback = r); + this._eventListeners = [ + helper.addEventListener(this._pageProxySession, WKPageProxySessionEvents.TargetCreated, this._onTargetCreated.bind(this)), + helper.addEventListener(this._pageProxySession, WKPageProxySessionEvents.TargetDestroyed, this._onTargetDestroyed.bind(this)), + helper.addEventListener(this._pageProxySession, WKPageProxySessionEvents.DidCommitProvisionalTarget, this._onProvisionalTargetCommitted.bind(this)) + ]; + } + + dispose() { + helper.removeEventListeners(this._eventListeners); + } + + async page(): Promise { + if (!this._pagePromise) + this._pagePromise = this._initializeWKPage(); + return this._pagePromise; + } + + onPopupCreated(popupPageProxy: WKPageProxy) { + if (!this._wkPage) + return; + if (!this._wkPage._page.listenerCount(Events.Page.Popup)) + return; + popupPageProxy.page().then(page => this._wkPage._page.emit(Events.Page.Popup, page)); + } + + private async _initializeWKPage(): Promise { + await this._firstTargetPromise; + let session: WKTargetSession; + for (const targetSession of this._targetSessions.values()) { + if (!targetSession.isProvisional()) { + session = targetSession; + break; + } + } + assert(session, 'One non-provisional target session must exist'); + this._wkPage = new WKPage(this._browser, this._browserContext); + this._wkPage.setSession(session); + await this._initializeSession(session); + return this._wkPage._page; + } + + private _initializeSession(session: WKTargetSession) : Promise { + return this._wkPage._initializeSession(session).catch(e => { + if (session.isClosed()) + return; + // Swallow initialization errors due to newer target swap in, + // since we will reinitialize again. + if (this._wkPage._session === session) + throw e; + }); + } + + private _onTargetCreated(session: WKTargetSession, targetInfo: Protocol.Target.TargetInfo) { + assert(targetInfo.type === 'page', 'Only page targets are expected in WebKit, received: ' + targetInfo.type); + this._targetSessions.set(targetInfo.targetId, session); + if (this._firstTargetCallback) { + this._firstTargetCallback(); + this._firstTargetCallback = null; + } + if (targetInfo.isProvisional && this._wkPage) + this._initializeSession(session); + if (targetInfo.isPaused) + this._pageProxySession.send('Target.resume', { targetId: targetInfo.targetId }).catch(debugError); + } + + private _onTargetDestroyed({targetId, crashed}) { + const targetSession = this._targetSessions.get(targetId); + this._targetSessions.delete(targetId); + if (!this._wkPage) + return; + if (this._wkPage._session === targetSession) + this._wkPage.didClose(crashed); + } + + private _onProvisionalTargetCommitted({oldTargetId, newTargetId}) { + const newTargetSession = this._targetSessions.get(newTargetId); + this._wkPage.setSession(newTargetSession); + } +} diff --git a/src/webkit/wkTarget.ts b/src/webkit/wkTarget.ts deleted file mode 100644 index 244dcce724..0000000000 --- a/src/webkit/wkTarget.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2019 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 { BrowserContext } from '../browserContext'; -import { Page } from '../page'; -import { Protocol } from './protocol'; -import { WKTargetSession } from './wkConnection'; -import { WKPage } from './wkPage'; -import { WKBrowser } from './wkBrowser'; - -export class WKTarget { - readonly _browserContext: BrowserContext; - readonly _targetId: string; - readonly _session: WKTargetSession; - private _pagePromise: Promise | null = null; - private _browser: WKBrowser; - _wkPage: WKPage | null = null; - - constructor(browser: WKBrowser, session: WKTargetSession, targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext) { - this._browser = browser; - this._session = session; - this._browserContext = browserContext; - this._targetId = targetInfo.targetId; - /** @type {?Promise} */ - this._pagePromise = null; - } - - _didClose(crashed: boolean) { - if (this._wkPage) - this._wkPage.didClose(crashed); - } - - async _initializeSession(session: WKTargetSession) { - if (!this._wkPage) - return; - await this._wkPage._initializeSession(session).catch(e => { - // Swallow initialization errors due to newer target swap in, - // since we will reinitialize again. - if (this._wkPage) - throw e; - }); - } - - async _swapWith(oldTarget: WKTarget) { - if (!oldTarget._pagePromise) - return; - this._pagePromise = oldTarget._pagePromise; - this._wkPage = oldTarget._wkPage; - // Swapped out 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; - oldTarget._wkPage = null; - this._wkPage.setSession(this._session); - } - - async page(): Promise { - if (!this._pagePromise) { - this._wkPage = new WKPage(this._browser, this._browserContext); - this._wkPage.setSession(this._session); - // Reference local page variable as |this._frameManager| may be - // cleared on swap. - const page = this._wkPage._page; - this._pagePromise = this._initializeSession(this._session).then(() => page); - } - return this._pagePromise; - } -}