mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(networkidle): ignore favicons and keep track of requests (#368)
This counters Firefox not cancelling existing requests on navigation.
This commit is contained in:
parent
f1d6fe6bd8
commit
32edca7395
@ -117,6 +117,7 @@ export class FrameManager {
|
|||||||
frame._name = name;
|
frame._name = name;
|
||||||
frame._lastDocumentId = documentId;
|
frame._lastDocumentId = documentId;
|
||||||
this.frameLifecycleEvent(frameId, 'clear');
|
this.frameLifecycleEvent(frameId, 'clear');
|
||||||
|
this.clearInflightRequests(frame);
|
||||||
if (!initial) {
|
if (!initial) {
|
||||||
for (const watcher of this._lifecycleWatchers)
|
for (const watcher of this._lifecycleWatchers)
|
||||||
watcher._onCommittedNewDocumentNavigation(frame);
|
watcher._onCommittedNewDocumentNavigation(frame);
|
||||||
@ -162,12 +163,6 @@ export class FrameManager {
|
|||||||
return;
|
return;
|
||||||
if (event === 'clear') {
|
if (event === 'clear') {
|
||||||
frame._firedLifecycleEvents.clear();
|
frame._firedLifecycleEvents.clear();
|
||||||
this._stopNetworkIdleTimer(frame, 'networkidle0');
|
|
||||||
if (frame._inflightRequests === 0)
|
|
||||||
this._startNetworkIdleTimer(frame, 'networkidle0');
|
|
||||||
this._stopNetworkIdleTimer(frame, 'networkidle2');
|
|
||||||
if (frame._inflightRequests <= 2)
|
|
||||||
this._startNetworkIdleTimer(frame, 'networkidle2');
|
|
||||||
} else {
|
} else {
|
||||||
frame._firedLifecycleEvents.add(event);
|
frame._firedLifecycleEvents.add(event);
|
||||||
for (const watcher of this._lifecycleWatchers)
|
for (const watcher of this._lifecycleWatchers)
|
||||||
@ -179,9 +174,19 @@ export class FrameManager {
|
|||||||
this._page.emit(Events.Page.DOMContentLoaded);
|
this._page.emit(Events.Page.DOMContentLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearInflightRequests(frame: Frame) {
|
||||||
|
// Keep the current navigation request if any.
|
||||||
|
frame._inflightRequests = new Set(Array.from(frame._inflightRequests).filter(request => request._documentId === frame._lastDocumentId));
|
||||||
|
this._stopNetworkIdleTimer(frame, 'networkidle0');
|
||||||
|
if (frame._inflightRequests.size === 0)
|
||||||
|
this._startNetworkIdleTimer(frame, 'networkidle0');
|
||||||
|
this._stopNetworkIdleTimer(frame, 'networkidle2');
|
||||||
|
if (frame._inflightRequests.size <= 2)
|
||||||
|
this._startNetworkIdleTimer(frame, 'networkidle2');
|
||||||
|
}
|
||||||
|
|
||||||
requestStarted(request: network.Request) {
|
requestStarted(request: network.Request) {
|
||||||
if (request.frame())
|
this._inflightRequestStarted(request);
|
||||||
this._incrementRequestCount(request.frame());
|
|
||||||
if (request._documentId && request.frame() && !request.redirectChain().length) {
|
if (request._documentId && request.frame() && !request.redirectChain().length) {
|
||||||
for (const watcher of this._lifecycleWatchers)
|
for (const watcher of this._lifecycleWatchers)
|
||||||
watcher._onNavigationRequest(request.frame(), request);
|
watcher._onNavigationRequest(request.frame(), request);
|
||||||
@ -194,14 +199,12 @@ export class FrameManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
requestFinished(request: network.Request) {
|
requestFinished(request: network.Request) {
|
||||||
if (request.frame())
|
this._inflightRequestFinished(request);
|
||||||
this._decrementRequestCount(request.frame());
|
|
||||||
this._page.emit(Events.Page.RequestFinished, request);
|
this._page.emit(Events.Page.RequestFinished, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestFailed(request: network.Request, canceled: boolean) {
|
requestFailed(request: network.Request, canceled: boolean) {
|
||||||
if (request.frame())
|
this._inflightRequestFinished(request);
|
||||||
this._decrementRequestCount(request.frame());
|
|
||||||
if (request._documentId && request.frame()) {
|
if (request._documentId && request.frame()) {
|
||||||
const isCurrentDocument = request.frame()._lastDocumentId === request._documentId;
|
const isCurrentDocument = request.frame()._lastDocumentId === request._documentId;
|
||||||
if (!isCurrentDocument) {
|
if (!isCurrentDocument) {
|
||||||
@ -225,19 +228,27 @@ export class FrameManager {
|
|||||||
this._page.emit(Events.Page.FrameDetached, frame);
|
this._page.emit(Events.Page.FrameDetached, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _decrementRequestCount(frame: Frame) {
|
private _inflightRequestFinished(request: network.Request) {
|
||||||
frame._inflightRequests--;
|
if (!request.frame() || request.url().endsWith('favicon.ico'))
|
||||||
if (frame._inflightRequests === 0)
|
return;
|
||||||
|
const frame = request.frame();
|
||||||
|
if (!frame._inflightRequests.has(request))
|
||||||
|
return;
|
||||||
|
frame._inflightRequests.delete(request);
|
||||||
|
if (frame._inflightRequests.size === 0)
|
||||||
this._startNetworkIdleTimer(frame, 'networkidle0');
|
this._startNetworkIdleTimer(frame, 'networkidle0');
|
||||||
if (frame._inflightRequests === 2)
|
if (frame._inflightRequests.size === 2)
|
||||||
this._startNetworkIdleTimer(frame, 'networkidle2');
|
this._startNetworkIdleTimer(frame, 'networkidle2');
|
||||||
}
|
}
|
||||||
|
|
||||||
private _incrementRequestCount(frame: Frame) {
|
private _inflightRequestStarted(request: network.Request) {
|
||||||
frame._inflightRequests++;
|
if (!request.frame() || request.url().endsWith('favicon.ico'))
|
||||||
if (frame._inflightRequests === 1)
|
return;
|
||||||
|
const frame = request.frame();
|
||||||
|
frame._inflightRequests.add(request);
|
||||||
|
if (frame._inflightRequests.size === 1)
|
||||||
this._stopNetworkIdleTimer(frame, 'networkidle0');
|
this._stopNetworkIdleTimer(frame, 'networkidle0');
|
||||||
if (frame._inflightRequests === 3)
|
if (frame._inflightRequests.size === 3)
|
||||||
this._stopNetworkIdleTimer(frame, 'networkidle2');
|
this._stopNetworkIdleTimer(frame, 'networkidle2');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +278,7 @@ export class Frame {
|
|||||||
private _contextData = new Map<ContextType, ContextData>();
|
private _contextData = new Map<ContextType, ContextData>();
|
||||||
private _childFrames = new Set<Frame>();
|
private _childFrames = new Set<Frame>();
|
||||||
_name: string;
|
_name: string;
|
||||||
_inflightRequests = 0;
|
_inflightRequests = new Set<network.Request>();
|
||||||
readonly _networkIdleTimers = new Map<LifecycleEvent, NodeJS.Timer>();
|
readonly _networkIdleTimers = new Map<LifecycleEvent, NodeJS.Timer>();
|
||||||
|
|
||||||
constructor(page: Page, id: string, parentFrame: Frame | null) {
|
constructor(page: Page, id: string, parentFrame: Frame | null) {
|
||||||
@ -437,8 +448,10 @@ export class Frame {
|
|||||||
|
|
||||||
async setContent(html: string, options?: NavigateOptions): Promise<void> {
|
async setContent(html: string, options?: NavigateOptions): Promise<void> {
|
||||||
const context = await this._utilityContext();
|
const context = await this._utilityContext();
|
||||||
if (this._page._delegate.needsLifecycleResetOnSetContent())
|
if (this._page._delegate.needsLifecycleResetOnSetContent()) {
|
||||||
this._page._frameManager.frameLifecycleEvent(this._id, 'clear');
|
this._page._frameManager.frameLifecycleEvent(this._id, 'clear');
|
||||||
|
this._page._frameManager.clearInflightRequests(this);
|
||||||
|
}
|
||||||
await context.evaluate(html => {
|
await context.evaluate(html => {
|
||||||
window.stop();
|
window.stop();
|
||||||
document.open();
|
document.open();
|
||||||
|
@ -426,7 +426,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROME
|
|||||||
}
|
}
|
||||||
|
|
||||||
const response = await actionPromise;
|
const response = await actionPromise;
|
||||||
expect(performance.now() - lastResponseFinished).not.toBeLessThan(499);
|
expect(performance.now() - lastResponseFinished).not.toBeLessThan(450);
|
||||||
if (!isSetContent)
|
if (!isSetContent)
|
||||||
expect(response.ok()).toBe(true);
|
expect(response.ok()).toBe(true);
|
||||||
|
|
||||||
@ -490,12 +490,8 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROME
|
|||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should wait for networkidle0 in setContent with request from previous navigation', async({page, server}) => {
|
it.skip(FFOX)('should wait for networkidle0 in setContent with request from previous navigation', async({page, server}) => {
|
||||||
// TODO: there are two issues here which combined fail the test in firefox:
|
// TODO: in Firefox window.stop() does not cancel outstanding requests, and we also lack 'init' lifecycle,
|
||||||
// - calling window.stop() does not cancel all outstanding requests in firefox;
|
// therefore we don't clear inglight requests at the right time.
|
||||||
// - we do not reset inflight request counter on lifecycle clear, so we wait for
|
|
||||||
// the first request indefinitely.
|
|
||||||
// Note that we cannot just reset inflight request counter, because the current navigation
|
|
||||||
// request is already inflight at that moment.
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
server.setRoute('/foo.js', () => {});
|
server.setRoute('/foo.js', () => {});
|
||||||
await page.setContent(`<script>fetch('foo.js');</script>`);
|
await page.setContent(`<script>fetch('foo.js');</script>`);
|
||||||
@ -504,6 +500,8 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROME
|
|||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should wait for networkidle2 in setContent with request from previous navigation', async({page, server}) => {
|
it.skip(FFOX)('should wait for networkidle2 in setContent with request from previous navigation', async({page, server}) => {
|
||||||
|
// TODO: in Firefox window.stop() does not cancel outstanding requests, and we also lack 'init' lifecycle,
|
||||||
|
// therefore we don't clear inglight requests at the right time.
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
server.setRoute('/foo.js', () => {});
|
server.setRoute('/foo.js', () => {});
|
||||||
await page.setContent(`<script>fetch('foo.js');</script>`);
|
await page.setContent(`<script>fetch('foo.js');</script>`);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user