Skip to content

Commit a310d57

Browse files
authored
feat(Page): add option to run 'beforeunload' when closing the page (#2478)
Today, `page.close()` method doesn't run page's beforeunload listeners. This way users can be sure that `page.close()` actually closes the page. This patch adds an optional `runBeforeUnload` option to the `page.close()` method that would run beforeunload listeners. Note: running beforeunload handlers might cancel page closing. Fixes #2386.
1 parent 3760188 commit a310d57

4 files changed

Lines changed: 41 additions & 5 deletions

File tree

docs/api.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
* [page.bringToFront()](#pagebringtofront)
7070
* [page.browser()](#pagebrowser)
7171
* [page.click(selector[, options])](#pageclickselector-options)
72-
* [page.close()](#pageclose)
72+
* [page.close(options)](#pagecloseoptions)
7373
* [page.content()](#pagecontent)
7474
* [page.cookies(...urls)](#pagecookiesurls)
7575
* [page.coverage](#pagecoverage)
@@ -747,9 +747,18 @@ const [response] = await Promise.all([
747747

748748
Shortcut for [page.mainFrame().click(selector[, options])](#frameclickselector-options).
749749

750-
#### page.close()
750+
#### page.close(options)
751+
- `options` <[Object]>
752+
- `runBeforeUnload` <[boolean]> Defaults to `false`. Whether to run the
753+
[before unload](https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload)
754+
page handlers.
751755
- returns: <[Promise]>
752756

757+
By default, `page.close()` **does not** run beforeunload handlers.
758+
759+
> **NOTE** if `runBeforeUnload` is passed as true, a `beforeunload` dialog might be summoned
760+
> and should be handled manually via page's ['dialog'](#event-dialog) event.
761+
753762
#### page.content()
754763
- returns: <[Promise]<[String]>>
755764

lib/Page.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -823,10 +823,18 @@ class Page extends EventEmitter {
823823
return this.mainFrame().title();
824824
}
825825

826-
async close() {
826+
/**
827+
* @param {!{runBeforeUnload: (boolean|undefined)}=} options
828+
*/
829+
async close(options = {runBeforeUnload: undefined}) {
827830
console.assert(!!this._client._connection, 'Protocol error: Connection closed. Most likely the page has been closed.');
828-
await this._client._connection.send('Target.closeTarget', { targetId: this._target._targetId });
829-
await this._target._isClosedPromise;
831+
const runBeforeUnload = !!options.runBeforeUnload;
832+
if (runBeforeUnload) {
833+
await this._client.send('Page.close');
834+
} else {
835+
await this._client._connection.send('Target.closeTarget', { targetId: this._target._targetId });
836+
await this._target._isClosedPromise;
837+
}
830838
}
831839

832840
/**

test/assets/beforeunload.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
window.addEventListener('beforeunload', event => {
3+
event.returnValue = 'Leave?';
4+
});
5+
</script>

test/page.spec.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ module.exports.addTests = function({testRunner, expect, puppeteer, DeviceDescrip
4040
await newPage.close();
4141
expect(await browser.pages()).not.toContain(newPage);
4242
});
43+
it('should run beforeunload if asked for', async({browser, server}) => {
44+
const newPage = await browser.newPage();
45+
await newPage.goto(server.PREFIX + '/beforeunload.html');
46+
// We have to interact with a page so that 'beforeunload' handlers
47+
// fire.
48+
await newPage.click('body');
49+
newPage.close({ runBeforeUnload: true });
50+
const dialog = await waitEvent(newPage, 'dialog');
51+
expect(dialog.type()).toBe('beforeunload');
52+
expect(dialog.defaultValue()).toBe('');
53+
expect(dialog.message()).toBe('');
54+
dialog.accept();
55+
await waitEvent(newPage, 'close');
56+
});
4357
});
4458

4559
describe('Page.Events.error', function() {

0 commit comments

Comments
 (0)