fix(lifecycle): recalculate lifecycle on iframe detach (#15812)

It could be that iframe was blocking some event, like load or networkidle,
and we never updated the lifecycle after the iframe was detached. This
lead to goto and other navigation commands to never resolve.
This commit is contained in:
Dmitry Gozman 2022-07-22 19:44:02 -07:00 committed by GitHub
parent fd21852b01
commit 4bec6309df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 2 deletions

View File

@ -277,8 +277,11 @@ export class FrameManager {
frameDetached(frameId: string) {
const frame = this._frames.get(frameId);
if (frame)
if (frame) {
this._removeFramesRecursively(frame);
// Recalculate subtree lifecycle for the whole tree - it should not be that big.
this._page.mainFrame()._recalculateLifecycle();
}
}
frameStoppedLoading(frameId: string) {
@ -590,7 +593,7 @@ export class Frame extends SdkObject {
});
}
private _recalculateLifecycle() {
_recalculateLifecycle() {
const events = new Set<types.LifecycleEvent>(this._firedLifecycleEvents);
for (const child of this._childFrames) {
child._recalculateLifecycle();

View File

@ -663,3 +663,35 @@ it('should return when navigation is committed if commit is specified', async ({
const response = await page.goto(server.EMPTY_PAGE, { waitUntil: 'commit' });
expect(response.status()).toBe(200);
});
it('should wait for load when iframe attaches and detaches', async ({ page, server }) => {
server.setRoute('/empty.html', (req, res) => {
res.writeHead(200, { 'content-type': 'text/html' });
res.end(`
<body>
<script>
const iframe = document.createElement('iframe');
iframe.src = './iframe.html';
document.body.appendChild(iframe);
setTimeout(() => iframe.remove(), 1000);
</script>
</body>
`);
});
server.setRoute('/iframe.html', (req, res) => {
res.writeHead(200, { 'content-type': 'text/html' });
res.end(`
<link rel="stylesheet" href="./style2.css">
`);
});
// Stall the css so that 'load' does not fire.
server.setRoute('/style2.css', () => {});
const frameDetached = page.waitForEvent('framedetached');
const done = page.goto(server.EMPTY_PAGE, { waitUntil: 'load' });
await frameDetached; // Make sure that iframe is gone.
await done;
expect(await page.$('iframe')).toBe(null);
});

View File

@ -157,3 +157,18 @@ it('should wait for networkidle from the popup', async ({ page, server, isAndroi
await popup.waitForLoadState('networkidle');
}
});
it('should wait for networkidle when iframe attaches and detaches', async ({ page }) => {
await page.setContent(`
<body>
<script>
setTimeout(() => {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
setTimeout(() => iframe.remove(), 400);
}, 400);
</script>
</body>
`, { waitUntil: 'networkidle' });
expect(await page.$('iframe')).toBe(null);
});