diff --git a/lib/ExecutionContext.js b/lib/ExecutionContext.js index d66c0d76..d100d969 100644 --- a/lib/ExecutionContext.js +++ b/lib/ExecutionContext.js @@ -182,6 +182,18 @@ class ExecutionContext { return createJSHandle(this, response.objects); } + /** + * @param {Protocol.DOM.BackendNodeId} backendNodeId + * @return {Promise} + */ + async _adoptBackendNodeId(backendNodeId) { + const {object} = await this._client.send('DOM.resolveNode', { + backendNodeId: backendNodeId, + executionContextId: this._contextId, + }); + return /** @type {Puppeteer.ElementHandle}*/(createJSHandle(this, object)); + } + /** * @param {Puppeteer.ElementHandle} elementHandle * @return {Promise} @@ -192,11 +204,7 @@ class ExecutionContext { const nodeInfo = await this._client.send('DOM.describeNode', { objectId: elementHandle._remoteObject.objectId, }); - const {object} = await this._client.send('DOM.resolveNode', { - backendNodeId: nodeInfo.node.backendNodeId, - executionContextId: this._contextId, - }); - return /** @type {Puppeteer.ElementHandle}*/(createJSHandle(this, object)); + return this._adoptBackendNodeId(nodeInfo.node.backendNodeId); } } diff --git a/lib/JSHandle.js b/lib/JSHandle.js index 5465f5b1..e5ff0041 100644 --- a/lib/JSHandle.js +++ b/lib/JSHandle.js @@ -311,6 +311,8 @@ class ElementHandle extends JSHandle { * @param {!Array} filePaths */ async uploadFile(...filePaths) { + const isMultiple = await this.evaluate(element => element.multiple); + assert(filePaths.length <= 1 || isMultiple, 'Multiple file uploads only work with '); // These imports are only needed for `uploadFile`, so keep them // scoped here to avoid paying the cost unnecessarily. const path = require('path'); diff --git a/lib/Page.js b/lib/Page.js index 99aa4517..3fa522c6 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -15,7 +15,6 @@ */ const fs = require('fs'); -const path = require('path'); const EventEmitter = require('events'); const mime = require('mime'); const {Events} = require('./Events'); @@ -112,7 +111,6 @@ class Page extends EventEmitter { networkManager.on(Events.NetworkManager.Response, event => this.emit(Events.Page.Response, event)); networkManager.on(Events.NetworkManager.RequestFailed, event => this.emit(Events.Page.RequestFailed, event)); networkManager.on(Events.NetworkManager.RequestFinished, event => this.emit(Events.Page.RequestFinished, event)); - this._fileChooserInterceptionIsDisabled = false; this._fileChooserInterceptors = new Set(); client.on('Page.domContentEventFired', event => this.emit(Events.Page.DOMContentLoaded)); @@ -137,23 +135,22 @@ class Page extends EventEmitter { this._client.send('Target.setAutoAttach', {autoAttach: true, waitForDebuggerOnStart: false, flatten: true}), this._client.send('Performance.enable', {}), this._client.send('Log.enable', {}), - this._client.send('Page.setInterceptFileChooserDialog', {enabled: true}).catch(e => { - this._fileChooserInterceptionIsDisabled = true; - }), + this._client.send('Page.setInterceptFileChooserDialog', {enabled: true}), ]); } /** * @param {!Protocol.Page.fileChooserOpenedPayload} event */ - _onFileChooser(event) { - if (!this._fileChooserInterceptors.size) { - this._client.send('Page.handleFileChooser', { action: 'fallback' }).catch(debugError); + async _onFileChooser(event) { + if (!this._fileChooserInterceptors.size) return; - } + const frame = this._frameManager.frame(event.frameId); + const context = await frame.executionContext(); + const element = await context._adoptBackendNodeId(event.backendNodeId); const interceptors = Array.from(this._fileChooserInterceptors); this._fileChooserInterceptors.clear(); - const fileChooser = new FileChooser(this._client, event); + const fileChooser = new FileChooser(this._client, element, event); for (const interceptor of interceptors) interceptor.call(null, fileChooser); } @@ -163,8 +160,6 @@ class Page extends EventEmitter { * @return !Promise} */ async waitForFileChooser(options = {}) { - if (this._fileChooserInterceptionIsDisabled) - throw new Error('File chooser handling does not work with multiple connections to the same page'); const { timeout = this._timeoutSettings.timeout(), } = options; @@ -1351,10 +1346,12 @@ class ConsoleMessage { class FileChooser { /** * @param {Puppeteer.CDPSession} client + * @param {Puppeteer.ElementHandle} element * @param {!Protocol.Page.fileChooserOpenedPayload} event */ - constructor(client, event) { + constructor(client, element, event) { this._client = client; + this._element = element; this._multiple = event.mode !== 'selectSingle'; this._handled = false; } @@ -1373,11 +1370,7 @@ class FileChooser { async accept(filePaths) { assert(!this._handled, 'Cannot accept FileChooser which is already handled!'); this._handled = true; - const files = filePaths.map(filePath => path.resolve(filePath)); - await this._client.send('Page.handleFileChooser', { - action: 'accept', - files, - }); + await this._element.uploadFile(...filePaths); } /** @@ -1386,9 +1379,6 @@ class FileChooser { async cancel() { assert(!this._handled, 'Cannot cancel FileChooser which is already handled!'); this._handled = true; - await this._client.send('Page.handleFileChooser', { - action: 'cancel', - }); } } diff --git a/package.json b/package.json index 12c8c1c5..b3e2b591 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "node": ">=8.16.0" }, "puppeteer": { - "chromium_revision": "706915" + "chromium_revision": "722269" }, "scripts": { "unit": "node test/test.js", diff --git a/test/accessibility.spec.js b/test/accessibility.spec.js index b60fc691..7576ed9b 100644 --- a/test/accessibility.spec.js +++ b/test/accessibility.spec.js @@ -81,7 +81,7 @@ module.exports.addTests = function({testRunner, expect, FFOX}) { expect(await page.accessibility.snapshot()).toEqual(golden); }); it('should report uninteresting nodes', async function({page}) { - await page.setContent(``); + await page.setContent(``); await page.focus('textarea'); const golden = FFOX ? { role: 'entry', @@ -100,7 +100,7 @@ module.exports.addTests = function({testRunner, expect, FFOX}) { focused: true, multiline: true, children: [{ - role: 'GenericContainer', + role: 'generic', name: '', children: [{ role: 'text', name: 'hi' @@ -182,7 +182,7 @@ module.exports.addTests = function({testRunner, expect, FFOX}) { name: 'my fake image' }] } : { - role: 'GenericContainer', + role: 'generic', name: '', value: 'Edit this image: ', children: [{ @@ -241,7 +241,7 @@ module.exports.addTests = function({testRunner, expect, FFOX}) {
Edit this image:my fake image
`); const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual({ - role: 'GenericContainer', + role: 'generic', name: '' }); }); @@ -250,7 +250,7 @@ module.exports.addTests = function({testRunner, expect, FFOX}) {
Edit this image:my fake image
`); const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual({ - role: 'GenericContainer', + role: 'generic', name: '' }); }); @@ -360,10 +360,18 @@ module.exports.addTests = function({testRunner, expect, FFOX}) { const div = await page.$('div'); expect(await page.accessibility.snapshot({root: div})).toEqual(null); expect(await page.accessibility.snapshot({root: div, interestingOnly: false})).toEqual({ - role: 'GenericContainer', + role: 'generic', name: '', - children: [ { role: 'button', name: 'My Button' } ] } - ); + children: [ + { + role: 'button', + name: 'My Button', + children: [ + { role: 'text', name: 'My Button' }, + ], + }, + ], + }); }); }); }); diff --git a/test/chromiumonly.spec.js b/test/chromiumonly.spec.js index 360934f4..962a7b04 100644 --- a/test/chromiumonly.spec.js +++ b/test/chromiumonly.spec.js @@ -94,22 +94,6 @@ module.exports.addLauncherTests = function({testRunner, expect, defaultBrowserOp }); }); - describe('Page.waitForFileChooser', () => { - it('should fail gracefully when trying to work with filechoosers within multiple connections', async() => { - // 1. Launch a browser and connect to all pages. - const originalBrowser = await puppeteer.launch(defaultBrowserOptions); - await originalBrowser.pages(); - // 2. Connect a remote browser and connect to first page. - const remoteBrowser = await puppeteer.connect({browserWSEndpoint: originalBrowser.wsEndpoint()}); - const [page] = await remoteBrowser.pages(); - // 3. Make sure |page.waitForFileChooser()| does not work with multiclient. - let error = null; - await page.waitForFileChooser().catch(e => error = e); - expect(error.message).toBe('File chooser handling does not work with multiple connections to the same page'); - originalBrowser.close(); - }); - - }); }); };