2020-01-17 23:02:57 +01:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2020-01-07 19:39:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
import { BrowserContext } from '../browserContext';
|
|
|
|
|
import { Page } from '../page';
|
|
|
|
|
import { Protocol } from './protocol';
|
2020-01-10 00:14:35 +01:00
|
|
|
import { WKSession } from './wkConnection';
|
2020-01-07 19:39:01 +01:00
|
|
|
import { WKPage } from './wkPage';
|
|
|
|
|
import { RegisteredListener, helper, assert, debugError } from '../helper';
|
|
|
|
|
import { Events } from '../events';
|
|
|
|
|
|
2020-01-18 00:33:55 +01:00
|
|
|
const isPovisionalSymbol = Symbol('isPovisional');
|
2020-01-09 20:02:55 +01:00
|
|
|
|
2020-01-07 19:39:01 +01:00
|
|
|
export class WKPageProxy {
|
2020-01-10 00:14:35 +01:00
|
|
|
private readonly _pageProxySession: WKSession;
|
2020-01-07 19:39:01 +01:00
|
|
|
readonly _browserContext: BrowserContext;
|
|
|
|
|
private _pagePromise: Promise<Page> | null = null;
|
|
|
|
|
private _wkPage: WKPage | null = null;
|
|
|
|
|
private readonly _firstTargetPromise: Promise<void>;
|
2020-01-13 22:33:25 +01:00
|
|
|
private _firstTargetCallback?: () => void;
|
2020-01-28 23:00:36 +01:00
|
|
|
private _pagePausedOnStart: boolean = false;
|
2020-01-09 20:02:55 +01:00
|
|
|
private readonly _sessions = new Map<string, WKSession>();
|
2020-01-07 19:39:01 +01:00
|
|
|
private readonly _eventListeners: RegisteredListener[];
|
|
|
|
|
|
2020-01-10 00:14:35 +01:00
|
|
|
constructor(pageProxySession: WKSession, browserContext: BrowserContext) {
|
|
|
|
|
this._pageProxySession = pageProxySession;
|
2020-01-07 19:39:01 +01:00
|
|
|
this._browserContext = browserContext;
|
|
|
|
|
this._firstTargetPromise = new Promise(r => this._firstTargetCallback = r);
|
|
|
|
|
this._eventListeners = [
|
2020-01-09 20:02:55 +01:00
|
|
|
helper.addEventListener(this._pageProxySession, 'Target.targetCreated', this._onTargetCreated.bind(this)),
|
|
|
|
|
helper.addEventListener(this._pageProxySession, 'Target.targetDestroyed', this._onTargetDestroyed.bind(this)),
|
|
|
|
|
helper.addEventListener(this._pageProxySession, 'Target.dispatchMessageFromTarget', this._onDispatchMessageFromTarget.bind(this)),
|
|
|
|
|
helper.addEventListener(this._pageProxySession, 'Target.didCommitProvisionalTarget', this._onDidCommitProvisionalTarget.bind(this)),
|
2020-01-07 19:39:01 +01:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-10 00:14:35 +01:00
|
|
|
didClose() {
|
|
|
|
|
if (this._wkPage)
|
|
|
|
|
this._wkPage.didClose(false);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 19:39:01 +01:00
|
|
|
dispose() {
|
2020-01-10 00:14:35 +01:00
|
|
|
this._pageProxySession.dispose();
|
2020-01-07 19:39:01 +01:00
|
|
|
helper.removeEventListeners(this._eventListeners);
|
2020-01-09 20:02:55 +01:00
|
|
|
for (const session of this._sessions.values())
|
|
|
|
|
session.dispose();
|
|
|
|
|
this._sessions.clear();
|
2020-01-10 00:14:35 +01:00
|
|
|
if (this._wkPage)
|
2020-01-22 23:17:44 +01:00
|
|
|
this._wkPage.dispose();
|
2020-01-10 00:14:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatchMessageToSession(message: any) {
|
|
|
|
|
this._pageProxySession.dispatchMessage(message);
|
2020-01-07 19:39:01 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-17 02:00:02 +01:00
|
|
|
private _isProvisionalCrossProcessLoadInProgress() : boolean {
|
|
|
|
|
for (const anySession of this._sessions.values()) {
|
2020-01-18 00:33:55 +01:00
|
|
|
if ((anySession as any)[isPovisionalSymbol])
|
2020-01-17 02:00:02 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-14 20:46:08 +01:00
|
|
|
handleProvisionalLoadFailed(event: Protocol.Browser.provisionalLoadFailedPayload) {
|
|
|
|
|
if (!this._wkPage)
|
|
|
|
|
return;
|
2020-01-17 02:00:02 +01:00
|
|
|
if (!this._isProvisionalCrossProcessLoadInProgress())
|
|
|
|
|
return;
|
2020-01-16 20:10:46 +01:00
|
|
|
let errorText = event.error;
|
|
|
|
|
if (errorText.includes('cancelled'))
|
|
|
|
|
errorText += '; maybe frame was detached?';
|
|
|
|
|
this._wkPage._page._frameManager.provisionalLoadFailed(event.loaderId, errorText);
|
2020-01-14 20:46:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-07 19:39:01 +01:00
|
|
|
async page(): Promise<Page> {
|
|
|
|
|
if (!this._pagePromise)
|
|
|
|
|
this._pagePromise = this._initializeWKPage();
|
|
|
|
|
return this._pagePromise;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:43:43 +01:00
|
|
|
existingPage(): Page | undefined {
|
|
|
|
|
return this._wkPage ? this._wkPage._page : undefined;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 19:39:01 +01:00
|
|
|
onPopupCreated(popupPageProxy: WKPageProxy) {
|
2020-01-13 22:33:25 +01:00
|
|
|
const wkPage = this._wkPage;
|
|
|
|
|
if (!wkPage || !wkPage._page.listenerCount(Events.Page.Popup))
|
2020-01-07 19:39:01 +01:00
|
|
|
return;
|
2020-01-13 22:33:25 +01:00
|
|
|
popupPageProxy.page().then(page => wkPage._page.emit(Events.Page.Popup, page));
|
2020-01-07 19:39:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async _initializeWKPage(): Promise<Page> {
|
|
|
|
|
await this._firstTargetPromise;
|
2020-01-13 22:33:25 +01:00
|
|
|
let session: WKSession | undefined;
|
2020-01-09 20:02:55 +01:00
|
|
|
for (const anySession of this._sessions.values()) {
|
2020-01-18 00:33:55 +01:00
|
|
|
if (!(anySession as any)[isPovisionalSymbol]) {
|
2020-01-09 20:02:55 +01:00
|
|
|
session = anySession;
|
2020-01-07 19:39:01 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert(session, 'One non-provisional target session must exist');
|
2020-01-08 02:16:27 +01:00
|
|
|
this._wkPage = new WKPage(this._browserContext, this._pageProxySession);
|
2020-01-28 23:00:36 +01:00
|
|
|
await this._wkPage.initialize(session!, this._pagePausedOnStart);
|
|
|
|
|
if (this._pagePausedOnStart) {
|
|
|
|
|
this._resumeTarget(session!.sessionId);
|
|
|
|
|
this._pagePausedOnStart = false;
|
|
|
|
|
}
|
2020-01-07 19:39:01 +01:00
|
|
|
return this._wkPage._page;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 20:02:55 +01:00
|
|
|
private _onTargetCreated(event: Protocol.Target.targetCreatedPayload) {
|
|
|
|
|
const { targetInfo } = event;
|
2020-01-10 00:14:35 +01:00
|
|
|
const session = new WKSession(this._pageProxySession.connection, targetInfo.targetId, `The ${targetInfo.type} has been closed.`, (message: any) => {
|
2020-01-09 20:02:55 +01:00
|
|
|
this._pageProxySession.send('Target.sendMessageToTarget', {
|
|
|
|
|
message: JSON.stringify(message), targetId: targetInfo.targetId
|
|
|
|
|
}).catch(e => {
|
|
|
|
|
session.dispatchMessage({ id: message.id, error: { message: e.message } });
|
|
|
|
|
});
|
|
|
|
|
});
|
2020-01-07 19:39:01 +01:00
|
|
|
assert(targetInfo.type === 'page', 'Only page targets are expected in WebKit, received: ' + targetInfo.type);
|
2020-01-09 20:02:55 +01:00
|
|
|
this._sessions.set(targetInfo.targetId, session);
|
2020-01-07 19:39:01 +01:00
|
|
|
if (this._firstTargetCallback) {
|
|
|
|
|
this._firstTargetCallback();
|
2020-01-13 22:33:25 +01:00
|
|
|
this._firstTargetCallback = undefined;
|
2020-01-07 19:39:01 +01:00
|
|
|
}
|
2020-01-28 23:00:36 +01:00
|
|
|
if (targetInfo.isProvisional) {
|
2020-01-18 00:33:55 +01:00
|
|
|
(session as any)[isPovisionalSymbol] = true;
|
2020-01-28 23:00:36 +01:00
|
|
|
if (this._wkPage)
|
|
|
|
|
this._wkPage.onProvisionalLoadStarted(session);
|
|
|
|
|
if (targetInfo.isPaused)
|
|
|
|
|
this._resumeTarget(targetInfo.targetId);
|
|
|
|
|
} else if (this._pagePromise) {
|
|
|
|
|
assert(!this._pagePausedOnStart);
|
|
|
|
|
// This is the first time page target is created, will resume
|
|
|
|
|
// after finishing intialization.
|
|
|
|
|
this._pagePausedOnStart = !!targetInfo.isPaused;
|
|
|
|
|
} else if (targetInfo.isPaused) {
|
|
|
|
|
this._resumeTarget(targetInfo.targetId);
|
2020-01-28 20:07:35 +01:00
|
|
|
}
|
2020-01-07 19:39:01 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-28 23:00:36 +01:00
|
|
|
private _resumeTarget(targetId: string) {
|
|
|
|
|
this._pageProxySession.send('Target.resume', { targetId }).catch(debugError);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-09 20:02:55 +01:00
|
|
|
private _onTargetDestroyed(event: Protocol.Target.targetDestroyedPayload) {
|
|
|
|
|
const { targetId, crashed } = event;
|
|
|
|
|
const session = this._sessions.get(targetId);
|
2020-01-22 23:17:44 +01:00
|
|
|
assert(session, 'Unknown target destroyed: ' + targetId);
|
|
|
|
|
session!.dispose();
|
2020-01-09 20:02:55 +01:00
|
|
|
this._sessions.delete(targetId);
|
2020-01-22 23:17:44 +01:00
|
|
|
if (this._wkPage)
|
|
|
|
|
this._wkPage.onSessionDestroyed(session!, crashed);
|
2020-01-07 19:39:01 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-09 20:02:55 +01:00
|
|
|
private _onDispatchMessageFromTarget(event: Protocol.Target.dispatchMessageFromTargetPayload) {
|
|
|
|
|
const { targetId, message } = event;
|
|
|
|
|
const session = this._sessions.get(targetId);
|
|
|
|
|
assert(session, 'Unknown target: ' + targetId);
|
2020-01-18 00:33:55 +01:00
|
|
|
session!.dispatchMessage(JSON.parse(message));
|
2020-01-09 20:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _onDidCommitProvisionalTarget(event: Protocol.Target.didCommitProvisionalTargetPayload) {
|
|
|
|
|
const { oldTargetId, newTargetId } = event;
|
|
|
|
|
const newSession = this._sessions.get(newTargetId);
|
|
|
|
|
assert(newSession, 'Unknown new target: ' + newTargetId);
|
|
|
|
|
const oldSession = this._sessions.get(oldTargetId);
|
|
|
|
|
assert(oldSession, 'Unknown old target: ' + oldTargetId);
|
|
|
|
|
// TODO: make some calls like screenshot catch swapped out error and retry.
|
2020-01-13 22:33:25 +01:00
|
|
|
oldSession!.errorText = 'Target was swapped out.';
|
2020-01-18 00:33:55 +01:00
|
|
|
(newSession as any)[isPovisionalSymbol] = undefined;
|
|
|
|
|
if (this._wkPage)
|
2020-01-22 23:17:44 +01:00
|
|
|
this._wkPage.onProvisionalLoadCommitted(newSession!);
|
2020-01-07 19:39:01 +01:00
|
|
|
}
|
|
|
|
|
}
|