fix(chromium): associate navigation requests with navigations (#1724)
This commit is contained in:
parent
42beb3784f
commit
3584205086
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue