From 7f82f7a6e902d7afa0d39f761968fa2b513f2af5 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Sun, 17 Feb 2019 10:23:48 -0800 Subject: [PATCH] feat(firefox): support Browser.target() (#4028) Support browser target. Drive-by: switch over to a more devtools'ish protocol: - use `targetId` instead of `pageId` everywhere - use target events instead of tab events --- experimental/puppeteer-firefox/lib/Browser.js | 65 ++++++++++--------- experimental/puppeteer-firefox/lib/Page.js | 16 ++--- experimental/puppeteer-firefox/package.json | 2 +- test/browser.spec.js | 2 +- test/target.spec.js | 4 +- 5 files changed, 47 insertions(+), 42 deletions(-) diff --git a/experimental/puppeteer-firefox/lib/Browser.js b/experimental/puppeteer-firefox/lib/Browser.js index 5ade63376897e..084c863ddca1f 100644 --- a/experimental/puppeteer-firefox/lib/Browser.js +++ b/experimental/puppeteer-firefox/lib/Browser.js @@ -31,8 +31,8 @@ class Browser extends EventEmitter { this._process = process; this._closeCallback = closeCallback; - /** @type {!Map} */ - this._pageTargets = new Map(); + /** @type {!Map} */ + this._targets = new Map(); this._defaultContext = new BrowserContext(this._connection, this, null); /** @type {!Map} */ @@ -43,9 +43,9 @@ class Browser extends EventEmitter { this._connection.on(Events.Connection.Disconnected, () => this.emit(Events.Browser.Disconnected)); this._eventListeners = [ - helper.addEventListener(this._connection, 'Browser.tabOpened', this._onTabOpened.bind(this)), - helper.addEventListener(this._connection, 'Browser.tabClosed', this._onTabClosed.bind(this)), - helper.addEventListener(this._connection, 'Browser.tabNavigated', this._onTabNavigated.bind(this)), + helper.addEventListener(this._connection, 'Browser.targetCreated', this._onTargetCreated.bind(this)), + helper.addEventListener(this._connection, 'Browser.targetDestroyed', this._onTargetDestroyed.bind(this)), + helper.addEventListener(this._connection, 'Browser.targetInfoChanged', this._onTargetInfoChanged.bind(this)), ]; } @@ -152,29 +152,32 @@ class Browser extends EventEmitter { * @return {Promise} */ async _createPageInContext(browserContextId) { - const {pageId} = await this._connection.send('Browser.newPage', { + const {targetId} = await this._connection.send('Browser.newPage', { browserContextId: browserContextId || undefined }); - const target = this._pageTargets.get(pageId); + const target = this._targets.get(targetId); return await target.page(); } async pages() { - const pageTargets = Array.from(this._pageTargets.values()); + const pageTargets = Array.from(this._targets.values()).filter(target => target.type() === 'page'); return await Promise.all(pageTargets.map(target => target.page())); } targets() { - return Array.from(this._pageTargets.values()); + return Array.from(this._targets.values()); } - async _onTabOpened({pageId, url, browserContextId, openerId}) { + target() { + return this.targets().find(target => target.type() === 'browser'); + } + + async _onTargetCreated({targetId, url, browserContextId, openerId, type}) { const context = browserContextId ? this._contexts.get(browserContextId) : this._defaultContext; - const opener = openerId ? this._pageTargets.get(openerId) : null; - const target = new Target(this._connection, this, context, pageId, url, opener); - this._pageTargets.set(pageId, target); - if (opener && opener._pagePromise) { - const openerPage = await opener._pagePromise; + const target = new Target(this._connection, this, context, targetId, type, url, openerId); + this._targets.set(targetId, target); + if (target.opener() && target.opener()._pagePromise) { + const openerPage = await target.opener()._pagePromise; if (openerPage.listenerCount(Events.Page.Popup)) { const popupPage = await target.page(); openerPage.emit(Events.Page.Popup, popupPage); @@ -184,15 +187,15 @@ class Browser extends EventEmitter { context.emit(Events.BrowserContext.TargetCreated, target); } - _onTabClosed({pageId}) { - const target = this._pageTargets.get(pageId); - this._pageTargets.delete(pageId); + _onTargetDestroyed({targetId}) { + const target = this._targets.get(targetId); + this._targets.delete(targetId); this.emit(Events.Browser.TargetDestroyed, target); target.browserContext().emit(Events.BrowserContext.TargetDestroyed, target); } - _onTabNavigated({pageId, url}) { - const target = this._pageTargets.get(pageId); + _onTargetInfoChanged({targetId, url}) { + const target = this._targets.get(targetId); target._url = url; this.emit(Events.Browser.TargetChanged, target); target.browserContext().emit(Events.BrowserContext.TargetChanged, target); @@ -210,33 +213,35 @@ class Target { * @param {*} connection * @param {!Browser} browser * @param {!BrowserContext} context - * @param {string} pageId + * @param {string} targetId + * @param {string} type * @param {string} url - * @param {?Target} opener + * @param {string=} openerId */ - constructor(connection, browser, context, pageId, url, opener) { + constructor(connection, browser, context, targetId, type, url, openerId) { this._browser = browser; this._context = context; this._connection = connection; - this._pageId = pageId; + this._targetId = targetId; + this._type = type; /** @type {?Promise} */ this._pagePromise = null; this._url = url; - this._opener = opener; + this._openerId = openerId; } /** * @return {?Target} */ opener() { - return this._opener; + return this._openerId ? this._browser._targets.get(this._openerId) : null; } /** - * @return {"page"|"background_page"|"service_worker"|"other"|"browser"} + * @return {"page"|"browser"} */ type() { - return 'page'; + return this._type; } url() { @@ -251,8 +256,8 @@ class Target { } async page() { - if (!this._pagePromise) - this._pagePromise = Page.create(this._connection, this, this._pageId, this._browser._defaultViewport); + if (this._type === 'page' && !this._pagePromise) + this._pagePromise = Page.create(this._connection, this, this._targetId, this._browser._defaultViewport); return this._pagePromise; } diff --git a/experimental/puppeteer-firefox/lib/Page.js b/experimental/puppeteer-firefox/lib/Page.js index 1f9784502359f..5ab24924ee1df 100644 --- a/experimental/puppeteer-firefox/lib/Page.js +++ b/experimental/puppeteer-firefox/lib/Page.js @@ -19,14 +19,14 @@ const writeFileAsync = util.promisify(fs.writeFile); * @internal */ class PageSession extends EventEmitter { - constructor(connection, pageId) { + constructor(connection, targetId) { super(); this._connection = connection; - this._pageId = pageId; + this._targetId = targetId; const wrapperSymbol = Symbol('listenerWrapper'); function wrapperListener(listener, params) { - if (params.pageId === pageId) + if (params.targetId === targetId) listener.call(null, params); } @@ -41,7 +41,7 @@ class PageSession extends EventEmitter { } async send(method, params = {}) { - params = Object.assign({}, params, {pageId: this._pageId}); + params = Object.assign({}, params, {targetId: this._targetId}); return await this._connection.send(method, params); } } @@ -51,11 +51,11 @@ class Page extends EventEmitter { * * @param {!Puppeteer.Connection} connection * @param {!Puppeteer.Target} target - * @param {string} pageId + * @param {string} targetId * @param {?Puppeteer.Viewport} defaultViewport */ - static async create(connection, target, pageId, defaultViewport) { - const session = new PageSession(connection, pageId); + static async create(connection, target, targetId, defaultViewport) { + const session = new PageSession(connection, targetId); const page = new Page(session, target); await session.send('Page.enable'); if (defaultViewport) @@ -82,7 +82,7 @@ class Page extends EventEmitter { helper.addEventListener(this._session, 'Page.uncaughtError', this._onUncaughtError.bind(this)), helper.addEventListener(this._session, 'Page.console', this._onConsole.bind(this)), helper.addEventListener(this._session, 'Page.dialogOpened', this._onDialogOpened.bind(this)), - helper.addEventListener(this._session, 'Browser.tabClosed', this._onClosed.bind(this)), + helper.addEventListener(this._session, 'Browser.targetDestroyed', this._onClosed.bind(this)), helper.addEventListener(this._frameManager, Events.FrameManager.Load, () => this.emit(Events.Page.Load)), helper.addEventListener(this._frameManager, Events.FrameManager.DOMContentLoaded, () => this.emit(Events.Page.DOMContentLoaded)), helper.addEventListener(this._frameManager, Events.FrameManager.FrameAttached, frame => this.emit(Events.Page.FrameAttached, frame)), diff --git a/experimental/puppeteer-firefox/package.json b/experimental/puppeteer-firefox/package.json index b91f033d49976..6719af1240373 100644 --- a/experimental/puppeteer-firefox/package.json +++ b/experimental/puppeteer-firefox/package.json @@ -9,7 +9,7 @@ "node": ">=8.9.4" }, "puppeteer": { - "firefox_revision": "10282bfac697c69a6fdfeec4cddae7caf98e1969" + "firefox_revision": "2ede4ae19f39ec7a1b73162a6004235908260dfe" }, "scripts": { "install": "node install.js", diff --git a/test/browser.spec.js b/test/browser.spec.js index e516151eef91f..e49b3785992c8 100644 --- a/test/browser.spec.js +++ b/test/browser.spec.js @@ -20,7 +20,7 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer}) { const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; describe('Browser.target', function() { - it_fails_ffox('should return browser target', async({browser}) => { + it('should return browser target', async({browser}) => { const target = browser.target(); expect(target.type()).toBe('browser'); }); diff --git a/test/target.spec.js b/test/target.spec.js index 818eef4830ca4..68ba68caee034 100644 --- a/test/target.spec.js +++ b/test/target.spec.js @@ -24,7 +24,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer, Errors}) { const {TimeoutError} = Errors; describe('Target', function() { - it_fails_ffox('Browser.targets should return all of the targets', async({page, server, browser}) => { + it('Browser.targets should return all of the targets', async({page, server, browser}) => { // The pages will be the testing page and the original newtab page const targets = browser.targets(); expect(targets.some(target => target.type() === 'page' && @@ -38,7 +38,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer, Errors}) { expect(allPages).toContain(page); expect(allPages[0]).not.toBe(allPages[1]); }); - it_fails_ffox('should contain browser target', async({browser}) => { + it('should contain browser target', async({browser}) => { const targets = browser.targets(); const browserTarget = targets.find(target => target.type() === 'browser'); expect(browserTarget).toBeTruthy();