diff --git a/docs/api.md b/docs/api.md index b3200c7fdc6..6c38fde6ee2 100644 --- a/docs/api.md +++ b/docs/api.md @@ -211,6 +211,7 @@ puppeteer.launch().then(async browser => { ``` #### browser.close() +- returns: <[Promise]> Closes browser with all the pages (if any were opened). The browser object itself is considered to be disposed and could not be used anymore. diff --git a/lib/Browser.js b/lib/Browser.js index 720cae22d87..adf5d13e320 100644 --- a/lib/Browser.js +++ b/lib/Browser.js @@ -16,14 +16,16 @@ const {helper} = require('./helper'); const Page = require('./Page'); +const EventEmitter = require('events'); -class Browser { +class Browser extends EventEmitter { /** * @param {!Connection} connection * @param {boolean} ignoreHTTPSErrors - * @param {function()=} closeCallback + * @param {function():Promise=} closeCallback */ constructor(connection, ignoreHTTPSErrors, closeCallback) { + super(); this._ignoreHTTPSErrors = ignoreHTTPSErrors; this._screenshotTaskQueue = new TaskQueue(); this._connection = connection; @@ -54,9 +56,9 @@ class Browser { return version.product; } - close() { + async close() { this._connection.dispose(); - this._closeCallback.call(null); + await this._closeCallback.call(null); } } diff --git a/lib/Launcher.js b/lib/Launcher.js index 63b4066dac8..5cca77267ec 100644 --- a/lib/Launcher.js +++ b/lib/Launcher.js @@ -84,9 +84,14 @@ class Launcher { chromeProcess.stderr.pipe(process.stderr); } - // Cleanup as processes exit. - if (temporaryUserDataDir) - chromeProcess.once('close', () => removeSync(temporaryUserDataDir)); + const waitForChromeToClose = new Promise(fulfill => { + chromeProcess.once('close', () => { + // Cleanup as processes exit. + if (temporaryUserDataDir) + removeSync(temporaryUserDataDir); + fulfill(); + }); + }); const listeners = [ helper.addEventListener(process, 'exit', killChrome) ]; if (options.handleSIGINT !== false) @@ -102,12 +107,29 @@ class Launcher { throw e; } + /** + * @return {Promise} + */ function killChrome() { helper.removeEventListeners(listeners); - if (process.platform === 'win32') - childProcess.execSync(`taskkill /pid ${chromeProcess.pid} /T /F`); - else - process.kill(-chromeProcess.pid); + if (temporaryUserDataDir) { + // Force kill chrome. + if (process.platform === 'win32') + childProcess.execSync(`taskkill /pid ${chromeProcess.pid} /T /F`); + else + process.kill(-chromeProcess.pid, 'SIGKILL'); + // Attempt to remove temporary profile directory to avoid littering. + try { + removeSync(temporaryUserDataDir); + } catch (e) { } + } else { + // Terminate chrome gracefully. + if (process.platform === 'win32') + chromeProcess.kill(); + else + process.kill(-chromeProcess.pid, 'SIGTERM'); + } + return waitForChromeToClose; } } diff --git a/test/test.js b/test/test.js index 5800bd1e91d..bb4ce2b0a68 100644 --- a/test/test.js +++ b/test/test.js @@ -107,7 +107,7 @@ describe('Puppeteer', function() { const options = Object.assign({userDataDir}, defaultBrowserOptions); const browser = await puppeteer.launch(options); expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0); - browser.close(); + await browser.close(); expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0); rm(userDataDir); })); @@ -117,7 +117,7 @@ describe('Puppeteer', function() { options.args = [`--user-data-dir=${userDataDir}`].concat(options.args); const browser = await puppeteer.launch(options); expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0); - browser.close(); + await browser.close(); expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0); rm(userDataDir); })); diff --git a/utils/doclint/check_public_api/index.js b/utils/doclint/check_public_api/index.js index d7205ce03f7..e93314c9a84 100644 --- a/utils/doclint/check_public_api/index.js +++ b/utils/doclint/check_public_api/index.js @@ -165,9 +165,9 @@ function checkDuplicates(doc) { classes.add(cls.name); const members = new Set(); for (const member of cls.membersArray) { - if (members.has(member.name)) - errors.push(`Duplicate declaration of method ${cls.name}.${member.name}()`); - members.add(member.name); + if (members.has(member.type + ' ' + member.name)) + errors.push(`Duplicate declaration of ${member.type} ${cls.name}.${member.name}()`); + members.add(member.type + ' ' + member.name); const args = new Set(); for (const arg of member.argsArray) { if (args.has(arg.name))