diff --git a/lib/Page.js b/lib/Page.js index c2637052cfa..89f343b9c51 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -517,6 +517,22 @@ class Page extends EventEmitter { * @param {!Protocol.Runtime.consoleAPICalledPayload} event */ async _onConsoleAPI(event) { + if (event.executionContextId === 0) { + // DevTools protocol stores the last 1000 console messages. These + // messages are always reported even for removed execution contexts. In + // this case, they are marked with executionContextId = 0 and are + // reported upon enabling Runtime agent. + // + // Ignore these messages since: + // - there's no execution context we can use to operate with message + // arguments + // - these messages are reported before Puppeteer clients can subscribe + // to the 'console' + // page event. + // + // @see https://github.com/GoogleChrome/puppeteer/issues/3865 + return; + } const context = this._frameManager.executionContextById(event.executionContextId); const values = event.args.map(arg => createJSHandle(context, arg)); this._addConsoleMessage(event.type, values, event.stackTrace); diff --git a/test/page.spec.js b/test/page.spec.js index 6dbf9c2549c..a50afe89bd6 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -350,6 +350,24 @@ module.exports.addTests = function({testRunner, expect, headless}) { columnNumber: 14, }); }); + // @see https://github.com/GoogleChrome/puppeteer/issues/3865 + it('should not throw when there are console messages in detached iframes', async({browser, page, server}) => { + await page.goto(server.EMPTY_PAGE); + await page.evaluate(async() => { + // 1. Create a popup that Puppeteer is not connected to. + const win = window.open(window.location.href, 'Title', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=200,top=0,left=0'); + await new Promise(x => win.onload = x); + // 2. In this popup, create an iframe that console.logs a message. + win.document.body.innerHTML = ``; + const frame = win.document.querySelector('iframe'); + await new Promise(x => frame.onload = x); + // 3. After that, remove the iframe. + frame.remove(); + }); + const popupTarget = page.browserContext().targets().find(target => target !== page.target()); + // 4. Connect to the popup and make sure it doesn't throw. + await popupTarget.page(); + }); }); describe('Page.Events.DOMContentLoaded', function() {