fix(webkit): forward network messages from provisional to committed page
This commit is contained in:
parent
fc9ddb7c3c
commit
6694c0e35b
|
|
@ -89,19 +89,23 @@ export class WKPage implements PageDelegate {
|
||||||
|
|
||||||
// This method is called for provisional targets as well. The session passed as the parameter
|
// This method is called for provisional targets as well. The session passed as the parameter
|
||||||
// may be different from the current session and may be destroyed without becoming current.
|
// may be different from the current session and may be destroyed without becoming current.
|
||||||
async _initializeSession(session: WKSession, isProvisional: boolean) {
|
async _initializeSession(session: WKSession) {
|
||||||
|
const isProvisional = this._session !== session;
|
||||||
const promises : Promise<any>[] = [
|
const promises : Promise<any>[] = [
|
||||||
// Page agent must be enabled before Runtime.
|
// Page agent must be enabled before Runtime.
|
||||||
session.send('Page.enable'),
|
session.send('Page.enable')
|
||||||
session.send('Page.getResourceTree').then(({frameTree}) => this._handleFrameTree(frameTree)),
|
|
||||||
// Resource tree should be received before first execution context.
|
|
||||||
session.send('Runtime.enable'),
|
|
||||||
session.send('Page.createIsolatedWorld', { name: UTILITY_WORLD_NAME, source: `//# sourceURL=${EVALUATION_SCRIPT_URL}` }),
|
|
||||||
session.send('Console.enable'),
|
|
||||||
session.send('Page.setInterceptFileChooserDialog', { enabled: true }),
|
|
||||||
this._networkManager.initializeSession(session, this._page._state.interceptNetwork, this._page._state.offlineMode),
|
|
||||||
this._workers.initializeSession(session),
|
|
||||||
];
|
];
|
||||||
|
if (!isProvisional)
|
||||||
|
promises.push(session.send('Page.getResourceTree').then(({frameTree}) => this._handleFrameTree(frameTree)));
|
||||||
|
promises.push(
|
||||||
|
// Resource tree should be received before first execution context.
|
||||||
|
session.send('Runtime.enable'),
|
||||||
|
session.send('Page.createIsolatedWorld', { name: UTILITY_WORLD_NAME, source: `//# sourceURL=${EVALUATION_SCRIPT_URL}` }),
|
||||||
|
session.send('Console.enable'),
|
||||||
|
session.send('Page.setInterceptFileChooserDialog', { enabled: true }),
|
||||||
|
this._networkManager.initializeSession(session, this._page._state.interceptNetwork, this._page._state.offlineMode),
|
||||||
|
this._workers.initializeSession(session)
|
||||||
|
);
|
||||||
const contextOptions = this._page.browserContext()._options;
|
const contextOptions = this._page.browserContext()._options;
|
||||||
if (contextOptions.userAgent)
|
if (contextOptions.userAgent)
|
||||||
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
|
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
|
||||||
|
|
@ -186,7 +190,10 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFrameNavigated(framePayload: Protocol.Page.Frame, initial: boolean) {
|
_onFrameNavigated(framePayload: Protocol.Page.Frame, initial: boolean) {
|
||||||
const frame = this._page._frameManager.frame(framePayload.id);
|
let frame = this._page._frameManager.frame(framePayload.id);
|
||||||
|
const newProcessMainFrame = !frame && !framePayload.parentId;
|
||||||
|
if (newProcessMainFrame)
|
||||||
|
frame = this._page._frameManager.mainFrame();
|
||||||
for (const [contextId, context] of this._contextIdToContext) {
|
for (const [contextId, context] of this._contextIdToContext) {
|
||||||
if (context.frame === frame) {
|
if (context.frame === frame) {
|
||||||
(context._delegate as WKExecutionContext)._dispose();
|
(context._delegate as WKExecutionContext)._dispose();
|
||||||
|
|
@ -194,6 +201,8 @@ export class WKPage implements PageDelegate {
|
||||||
frame._contextDestroyed(context);
|
frame._contextDestroyed(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (newProcessMainFrame)
|
||||||
|
this._onFrameAttached(framePayload.id, null);
|
||||||
this._page._frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url, framePayload.name || '', framePayload.loaderId, initial);
|
this._page._frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url, framePayload.name || '', framePayload.loaderId, initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,16 @@ import { WKSession } from './wkConnection';
|
||||||
import { WKPage } from './wkPage';
|
import { WKPage } from './wkPage';
|
||||||
import { RegisteredListener, helper, assert, debugError } from '../helper';
|
import { RegisteredListener, helper, assert, debugError } from '../helper';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
|
import { WKProvisionalPage } from './wkProvisionalPage';
|
||||||
|
|
||||||
// We keep provisional messages on the session instace until provisional
|
const isPovisionalSymbol = Symbol('isPovisional');
|
||||||
// target is committed. Non-provisional target (there should be just one)
|
|
||||||
// has undefined instead.
|
|
||||||
const provisionalMessagesSymbol = Symbol('provisionalMessages');
|
|
||||||
|
|
||||||
export class WKPageProxy {
|
export class WKPageProxy {
|
||||||
private readonly _pageProxySession: WKSession;
|
private readonly _pageProxySession: WKSession;
|
||||||
readonly _browserContext: BrowserContext;
|
readonly _browserContext: BrowserContext;
|
||||||
private _pagePromise: Promise<Page> | null = null;
|
private _pagePromise: Promise<Page> | null = null;
|
||||||
private _wkPage: WKPage | null = null;
|
private _wkPage: WKPage | null = null;
|
||||||
|
private _provisionalPage: WKProvisionalPage | null = null;
|
||||||
private readonly _firstTargetPromise: Promise<void>;
|
private readonly _firstTargetPromise: Promise<void>;
|
||||||
private _firstTargetCallback?: () => void;
|
private _firstTargetCallback?: () => void;
|
||||||
private readonly _sessions = new Map<string, WKSession>();
|
private readonly _sessions = new Map<string, WKSession>();
|
||||||
|
|
@ -79,7 +78,7 @@ export class WKPageProxy {
|
||||||
|
|
||||||
private _isProvisionalCrossProcessLoadInProgress() : boolean {
|
private _isProvisionalCrossProcessLoadInProgress() : boolean {
|
||||||
for (const anySession of this._sessions.values()) {
|
for (const anySession of this._sessions.values()) {
|
||||||
if ((anySession as any)[provisionalMessagesSymbol])
|
if ((anySession as any)[isPovisionalSymbol])
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -113,7 +112,7 @@ export class WKPageProxy {
|
||||||
await this._firstTargetPromise;
|
await this._firstTargetPromise;
|
||||||
let session: WKSession | undefined;
|
let session: WKSession | undefined;
|
||||||
for (const anySession of this._sessions.values()) {
|
for (const anySession of this._sessions.values()) {
|
||||||
if (!(anySession as any)[provisionalMessagesSymbol]) {
|
if (!(anySession as any)[isPovisionalSymbol]) {
|
||||||
session = anySession;
|
session = anySession;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +122,7 @@ export class WKPageProxy {
|
||||||
this._wkPage.setSession(session!);
|
this._wkPage.setSession(session!);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._wkPage._initializePageProxySession(),
|
this._wkPage._initializePageProxySession(),
|
||||||
this._wkPage._initializeSession(session!, false),
|
this._wkPage._initializeSession(session!),
|
||||||
]);
|
]);
|
||||||
return this._wkPage._page;
|
return this._wkPage._page;
|
||||||
}
|
}
|
||||||
|
|
@ -144,9 +143,12 @@ export class WKPageProxy {
|
||||||
this._firstTargetCallback = undefined;
|
this._firstTargetCallback = undefined;
|
||||||
}
|
}
|
||||||
if (targetInfo.isProvisional)
|
if (targetInfo.isProvisional)
|
||||||
(session as any)[provisionalMessagesSymbol] = [];
|
(session as any)[isPovisionalSymbol] = true;
|
||||||
if (targetInfo.isProvisional && this._wkPage)
|
if (targetInfo.isProvisional && this._wkPage) {
|
||||||
this._wkPage._initializeSession(session, true);
|
assert(!this._provisionalPage);
|
||||||
|
this._provisionalPage = new WKProvisionalPage(session, this._wkPage);
|
||||||
|
this._wkPage._initializeSession(session);
|
||||||
|
}
|
||||||
if (targetInfo.isPaused)
|
if (targetInfo.isPaused)
|
||||||
this._pageProxySession.send('Target.resume', { targetId: targetInfo.targetId }).catch(debugError);
|
this._pageProxySession.send('Target.resume', { targetId: targetInfo.targetId }).catch(debugError);
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +159,10 @@ export class WKPageProxy {
|
||||||
if (session)
|
if (session)
|
||||||
session.dispose();
|
session.dispose();
|
||||||
this._sessions.delete(targetId);
|
this._sessions.delete(targetId);
|
||||||
|
if (this._provisionalPage && this._provisionalPage._session === session) {
|
||||||
|
this._provisionalPage.dispose();
|
||||||
|
this._provisionalPage = null;
|
||||||
|
}
|
||||||
if (this._wkPage && this._wkPage._session === session && crashed)
|
if (this._wkPage && this._wkPage._session === session && crashed)
|
||||||
this._wkPage.didClose(crashed);
|
this._wkPage.didClose(crashed);
|
||||||
}
|
}
|
||||||
|
|
@ -165,11 +171,7 @@ export class WKPageProxy {
|
||||||
const { targetId, message } = event;
|
const { targetId, message } = event;
|
||||||
const session = this._sessions.get(targetId);
|
const session = this._sessions.get(targetId);
|
||||||
assert(session, 'Unknown target: ' + targetId);
|
assert(session, 'Unknown target: ' + targetId);
|
||||||
const provisionalMessages = (session as any)[provisionalMessagesSymbol];
|
session!.dispatchMessage(JSON.parse(message));
|
||||||
if (provisionalMessages)
|
|
||||||
provisionalMessages.push(message);
|
|
||||||
else
|
|
||||||
session!.dispatchMessage(JSON.parse(message));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onDidCommitProvisionalTarget(event: Protocol.Target.didCommitProvisionalTargetPayload) {
|
private _onDidCommitProvisionalTarget(event: Protocol.Target.didCommitProvisionalTargetPayload) {
|
||||||
|
|
@ -180,11 +182,12 @@ export class WKPageProxy {
|
||||||
assert(oldSession, 'Unknown old target: ' + oldTargetId);
|
assert(oldSession, 'Unknown old target: ' + oldTargetId);
|
||||||
// TODO: make some calls like screenshot catch swapped out error and retry.
|
// TODO: make some calls like screenshot catch swapped out error and retry.
|
||||||
oldSession!.errorText = 'Target was swapped out.';
|
oldSession!.errorText = 'Target was swapped out.';
|
||||||
const provisionalMessages = (newSession as any)[provisionalMessagesSymbol];
|
(newSession as any)[isPovisionalSymbol] = undefined;
|
||||||
assert(provisionalMessages, 'Committing target must be provisional');
|
if (this._provisionalPage) {
|
||||||
(newSession as any)[provisionalMessagesSymbol] = undefined;
|
this._provisionalPage.dispose();
|
||||||
for (const message of provisionalMessages)
|
this._provisionalPage = null;
|
||||||
newSession!.dispatchMessage(JSON.parse(message));
|
}
|
||||||
this._wkPage!.setSession(newSession!);
|
if (this._wkPage)
|
||||||
|
this._wkPage.setSession(newSession!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
src/webkit/wkProvisionalPage.ts
Normal file
48
src/webkit/wkProvisionalPage.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* 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 { WKSession } from './wkConnection';
|
||||||
|
import { WKPage } from './wkPage';
|
||||||
|
import { RegisteredListener, helper } from '../helper';
|
||||||
|
|
||||||
|
export class WKProvisionalPage {
|
||||||
|
readonly _session: WKSession;
|
||||||
|
private readonly _wkPage: WKPage;
|
||||||
|
private _sessionListeners: RegisteredListener[] = [];
|
||||||
|
|
||||||
|
constructor(session: WKSession, page: WKPage) {
|
||||||
|
this._session = session;
|
||||||
|
this._wkPage = page;
|
||||||
|
|
||||||
|
this._sessionListeners = [
|
||||||
|
'Network.requestWillBeSent',
|
||||||
|
'Network.requestIntercepted',
|
||||||
|
'Network.responseReceived',
|
||||||
|
'Network.loadingFinished',
|
||||||
|
'Network.loadingFailed',
|
||||||
|
].map(name => helper.addEventListener(this._session, name, args => this._onNetworkEvent(name, args)));
|
||||||
|
}
|
||||||
|
private _onNetworkEvent(eventName: string, payload: any) {
|
||||||
|
// Pretend that the events happened in the same process.
|
||||||
|
if (payload.frameId)
|
||||||
|
payload.frameId = this._wkPage._page._frameManager.mainFrame()._id;
|
||||||
|
this._wkPage._session.emit(eventName, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
helper.removeEventListeners(this._sessionListeners);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue