fix(chromium): associate navigation requests with navigations (#1724)

This commit is contained in:
Pavel Feldman 2020-04-09 19:03:06 -07:00 committed by GitHub
parent 42beb3784f
commit 3584205086
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 31 additions and 16 deletions

View file

@ -121,6 +121,7 @@ export class CRSession extends EventEmitter {
private readonly _targetType: string;
private readonly _sessionId: string;
private readonly _rootSessionId: string;
private _unhandledException: Error | undefined;
on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
addListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
@ -145,6 +146,8 @@ export class CRSession extends EventEmitter {
method: T,
params?: Protocol.CommandParameters[T]
): Promise<Protocol.CommandReturnValues[T]> {
if (this._unhandledException)
throw this._unhandledException;
if (!this._connection)
throw new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`);
const id = this._connection._rawSend(this._sessionId, method, params);
@ -163,7 +166,7 @@ export class CRSession extends EventEmitter {
callback.resolve(object.result);
} else {
assert(!object.id);
Promise.resolve().then(() => this.emit(object.method!, object.params));
Promise.resolve().then(() => this.emit(object.method!, object.params)).catch(e => this._unhandledException = this._unhandledException || e);
}
}

View file

@ -182,6 +182,8 @@ export class CRNetworkManager {
}
const isNavigationRequest = event.requestId === event.loaderId && event.type === 'Document';
const documentId = isNavigationRequest ? event.loaderId : undefined;
if (isNavigationRequest)
this._page._frameManager.frameUpdatedDocumentIdForNavigation(event.frameId!, documentId!);
const request = new InterceptableRequest(this._client, frame, interceptionId, documentId, this._userRequestInterceptionEnabled, event, redirectedFrom);
this._requestIdToRequest.set(event.requestId, request);
this._page._frameManager.requestStarted(request.request);

View file

@ -482,7 +482,7 @@ class FrameSession {
}
_onFrameRequestedNavigation(payload: Protocol.Page.frameRequestedNavigationPayload) {
this._page._frameManager.frameRequestedNavigation(payload.frameId);
this._page._frameManager.frameRequestedNavigation(payload.frameId, '');
}
_onFrameNavigatedWithinDocument(frameId: string, url: string) {

View file

@ -65,7 +65,7 @@ export class FFPage implements PageDelegate {
helper.addEventListener(this._session, 'Page.frameDetached', this._onFrameDetached.bind(this)),
helper.addEventListener(this._session, 'Page.navigationAborted', this._onNavigationAborted.bind(this)),
helper.addEventListener(this._session, 'Page.navigationCommitted', this._onNavigationCommitted.bind(this)),
helper.addEventListener(this._session, 'Page.navigationStarted', event => this._onNavigationStarted(event.frameId)),
helper.addEventListener(this._session, 'Page.navigationStarted', this._onNavigationStarted.bind(this)),
helper.addEventListener(this._session, 'Page.sameDocumentNavigation', this._onSameDocumentNavigation.bind(this)),
helper.addEventListener(this._session, 'Runtime.executionContextCreated', this._onExecutionContextCreated.bind(this)),
helper.addEventListener(this._session, 'Runtime.executionContextDestroyed', this._onExecutionContextDestroyed.bind(this)),
@ -136,8 +136,8 @@ export class FFPage implements PageDelegate {
this._page._frameManager.frameDidPotentiallyRequestNavigation();
}
_onNavigationStarted(frameId: string) {
this._page._frameManager.frameRequestedNavigation(frameId);
_onNavigationStarted(params: Protocol.Page.navigationStartedPayload) {
this._page._frameManager.frameRequestedNavigation(params.frameId, params.navigationId);
}
_onNavigationAborted(params: Protocol.Page.navigationAbortedPayload) {

View file

@ -127,12 +127,20 @@ export class FrameManager {
barrier.release();
}
frameRequestedNavigation(frameId: string) {
frameRequestedNavigation(frameId: string, documentId: string) {
const frame = this._frames.get(frameId);
if (!frame)
return;
for (const barrier of this._pendingNavigationBarriers)
barrier.addFrame(frame);
frame._pendingDocumentId = documentId;
}
frameUpdatedDocumentIdForNavigation(frameId: string, documentId: string) {
const frame = this._frames.get(frameId);
if (!frame)
return;
frame._pendingDocumentId = documentId;
}
frameCommittedNewDocumentNavigation(frameId: string, url: string, name: string, documentId: string, initial: boolean) {
@ -140,7 +148,9 @@ export class FrameManager {
this.removeChildFramesRecursively(frame);
frame._url = url;
frame._name = name;
assert(!frame._pendingDocumentId || frame._pendingDocumentId === documentId);
frame._lastDocumentId = documentId;
frame._pendingDocumentId = '';
for (const task of frame._frameTasks)
task.onNewDocument(documentId);
this.clearFrameLifecycle(frame);
@ -225,8 +235,9 @@ export class FrameManager {
requestFailed(request: network.Request, canceled: boolean) {
this._inflightRequestFinished(request);
if (request._documentId) {
const isCurrentDocument = request.frame()._lastDocumentId === request._documentId;
if (!isCurrentDocument) {
const isPendingDocument = request.frame()._pendingDocumentId === request._documentId;
if (isPendingDocument) {
request.frame()._pendingDocumentId = '';
let errorText = request.failure()!.errorText;
if (canceled)
errorText += '; maybe frame was detached?';
@ -319,6 +330,7 @@ export class Frame {
_id: string;
readonly _firedLifecycleEvents: Set<types.LifecycleEvent>;
_lastDocumentId = '';
_pendingDocumentId = '';
_frameTasks = new Set<FrameTask>();
readonly _page: Page;
private _parentFrame: Frame | null;

View file

@ -329,7 +329,7 @@ export class WKPage implements PageDelegate {
}
private _onFrameScheduledNavigation(frameId: string) {
this._page._frameManager.frameRequestedNavigation(frameId);
this._page._frameManager.frameRequestedNavigation(frameId, '');
}
private _onFrameStoppedLoading(frameId: string) {
@ -761,6 +761,8 @@ export class WKPage implements PageDelegate {
// TODO(einbinder) this will fail if we are an XHR document request
const isNavigationRequest = event.type === 'Document';
const documentId = isNavigationRequest ? event.loaderId : undefined;
if (isNavigationRequest)
this._page._frameManager.frameUpdatedDocumentIdForNavigation(event.frameId, documentId!);
const request = new WKInterceptableRequest(session, this._page._needsRequestInterception(), frame, event, redirectedFrom, documentId);
this._requestIdToRequest.set(event.requestId, request);
this._page._frameManager.requestStarted(request.request);

View file

@ -103,7 +103,7 @@ describe('Auto waiting', () => {
]);
expect(messages.join('|')).toBe('route|domcontentloaded|evaluate');
});
it.fail(CHROMIUM)('should await navigation when assigning location twice', async({page, server}) => {
it('should await navigation when assigning location twice', async({page, server}) => {
const messages = [];
server.setRoute('/empty.html?cancel', async (req, res) => { res.end('done'); });
server.setRoute('/empty.html?override', async (req, res) => { messages.push('routeoverride'); res.end('done'); });
@ -196,10 +196,7 @@ describe('Auto waiting should not hang when', () => {
setTimeout(() => window.stop(), 100);
}, server.EMPTY_PAGE);
});
it.fail(CHROMIUM)('calling window.stop sync', async({page, server, httpsServer}) => {
// Flaky, see https://github.com/microsoft/playwright/pull/1630/checks?check_run_id=553475173.
// We only get Page.frameStoppedLoading, but do not know that navigation was aborted or
// that navigation request was cancelled.
it('calling window.stop sync', async({page, server, httpsServer}) => {
await page.evaluate((url) => {
window.location.href = url;
window.stop();

View file

@ -316,8 +316,7 @@ describe('browserType.launchPersistentContext', function() {
await removeUserDataDir(userDataDir);
await removeUserDataDir(userDataDir2);
});
// See https://github.com/microsoft/playwright/issues/717
it.slow().fail(WIN && CHROMIUM)('userDataDir option should restore cookies', async({browserType, server}) => {
it.slow()('userDataDir option should restore cookies', async({browserType, server}) => {
const userDataDir = await makeUserDataDir();
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
const page = await browserContext.newPage();