fix(chromium): race between loadingFailed and requestPaused (#10289)

This commit is contained in:
Dmitry Gozman 2021-11-12 19:06:53 -08:00 committed by GitHub
parent 119a2e8f1b
commit 6a46711347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 2 deletions

View File

@ -56,7 +56,7 @@ export class CRNetworkManager {
eventsHelper.addEventListener(session, 'Network.responseReceived', this._onResponseReceived.bind(this)),
eventsHelper.addEventListener(session, 'Network.responseReceivedExtraInfo', this._onResponseReceivedExtraInfo.bind(this)),
eventsHelper.addEventListener(session, 'Network.loadingFinished', this._onLoadingFinished.bind(this)),
eventsHelper.addEventListener(session, 'Network.loadingFailed', this._onLoadingFailed.bind(this)),
eventsHelper.addEventListener(session, 'Network.loadingFailed', this._onLoadingFailed.bind(this, workerFrame)),
eventsHelper.addEventListener(session, 'Network.webSocketCreated', e => this._page._frameManager.onWebSocketCreated(e.requestId, e.url)),
eventsHelper.addEventListener(session, 'Network.webSocketWillSendHandshakeRequest', e => this._page._frameManager.onWebSocketRequest(e.requestId)),
eventsHelper.addEventListener(session, 'Network.webSocketHandshakeResponseReceived', e => this._page._frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)),
@ -368,12 +368,25 @@ export class CRNetworkManager {
this._page._frameManager.reportRequestFinished(request.request, response);
}
_onLoadingFailed(event: Protocol.Network.loadingFailedPayload) {
_onLoadingFailed(workerFrame: frames.Frame | undefined, event: Protocol.Network.loadingFailedPayload) {
this._responseExtraInfoTracker.loadingFailed(event);
let request = this._requestIdToRequest.get(event.requestId);
if (!request)
request = this._maybeAdoptMainRequest(event.requestId);
if (!request) {
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
if (requestWillBeSentEvent) {
// This is a case where request has failed before we had a chance to intercept it.
// We stop waiting for Fetch.requestPaused (it might never come), and dispatch request event
// right away, followed by requestfailed event.
this._requestIdToRequestWillBeSentEvent.delete(event.requestId);
this._onRequest(workerFrame, requestWillBeSentEvent, null);
request = this._requestIdToRequest.get(event.requestId);
}
}
// For certain requestIds we never receive requestWillBeSent event.
// @see https://crbug.com/750469
if (!request)

View File

@ -69,3 +69,56 @@ it('should return response body when Cross-Origin-Opener-Policy is set', async (
expect(response.request().failure()).toBeNull();
expect(await response.text()).toBe('Hello there!');
});
it('should fire requestfailed when intercepting race', async ({ page, server, browserName }) => {
const promsie = new Promise<void>(resolve => {
let counter = 0;
const failures = new Set();
const alive = new Set();
page.on('request', request => {
expect(alive.has(request)).toBe(false);
expect(failures.has(request)).toBe(false);
alive.add(request);
});
page.on('requestfailed', request => {
expect(failures.has(request)).toBe(false);
expect(alive.has(request)).toBe(true);
alive.delete(request);
failures.add(request);
if (++counter === 10)
resolve();
});
});
// Stall requests to make sure we don't get requestfinished.
await page.route('**', route => {});
const runFunc = {
chromium: 'abortAll()', // Fast in chromium to expose the race.
webkit: 'setTimeout(abortAll, 0)', // Async in webkit to let it issue requests.
firefox: 'setTimeout(abortAll, 1000)', // Slow in firefox to give it enough time to issue requests.
}[browserName];
await page.setContent(`
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<iframe src="${server.EMPTY_PAGE}"></iframe>
<script>
function abortAll() {
const frames = document.querySelectorAll("iframe");
for (const frame of frames)
frame.src = "about:blank";
}
${runFunc}
</script>
`);
await promsie;
});