diff --git a/docs/api.md b/docs/api.md
index 426bc41630..39c3a2e702 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -71,6 +71,7 @@
* [event: 'console'](#event-console)
* [event: 'dialog'](#event-dialog)
* [event: 'domcontentloaded'](#event-domcontentloaded)
+ * [event: 'filechooser'](#event-filechooser)
* [event: 'frameattached'](#event-frameattached)
* [event: 'framedetached'](#event-framedetached)
* [event: 'framenavigated'](#event-framenavigated)
@@ -176,10 +177,6 @@
* [chromium.startTracing(page, [options])](#chromiumstarttracingpage-options)
* [chromium.stopTracing()](#chromiumstoptracing)
* [chromium.wsEndpoint()](#chromiumwsendpoint)
-- [class: FileChooser](#class-filechooser)
- * [fileChooser.accept(filePaths)](#filechooseracceptfilepaths)
- * [fileChooser.cancel()](#filechoosercancel)
- * [fileChooser.isMultiple()](#filechooserismultiple)
- [class: Dialog](#class-dialog)
* [dialog.accept([promptText])](#dialogacceptprompttext)
* [dialog.defaultValue()](#dialogdefaultvalue)
@@ -972,6 +969,19 @@ Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` o
Emitted when the JavaScript [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched.
+#### event: 'filechooser'
+- <[Object]>
+ - `element` <[ElementHandle]> handle to the input element that was clicked
+ - `multiple` <[boolean]> Whether file chooser allow for [multiple](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#attr-multiple) file selection.
+
+Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can respond to it via setting the input files using [`elementHandle.setInputFiles`](#elementhandlesetinputfilesfiles).
+
+```js
+page.on('filechooser', async ({element, multiple}) => {
+ await element.setInputFiles('/tmp/myfile.pdf');
+});
+```
+
#### event: 'frameattached'
- <[Frame]>
@@ -1795,7 +1805,9 @@ Shortcut for [page.mainFrame().waitFor(selectorOrFunctionOrTimeout[, options[, .
#### page.waitForFileChooser([options])
- `options` <[Object]> Optional waiting parameters
- `timeout` <[number]> Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
-- returns: <[Promise]<[FileChooser]>> A promise that resolves after a page requests a file picker.
+- returns: <[Promise]<[Object]>>
+ - `element` <[ElementHandle]> handle to the input element that was clicked
+ - `multiple` <[boolean]> Whether file chooser allow for [multiple](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#attr-multiple) file selection.
> **NOTE** In non-headless Chromium, this method results in the native file picker dialog **not showing up** for the user.
@@ -1804,16 +1816,15 @@ The following example clicks a button that issues a file chooser, and then
responds with `/tmp/myfile.pdf` as if a user has selected this file.
```js
-const [fileChooser] = await Promise.all([
+const [{element, multiple}] = await Promise.all([
page.waitForFileChooser(),
page.click('#upload-file-button'), // some button that triggers file selection
]);
-await fileChooser.accept(['/tmp/myfile.pdf']);
+await element.setInputFiles('/tmp/myfile.pdf');
```
> **NOTE** This must be called *before* the file chooser is launched. It will not return a currently active file chooser.
-
#### page.waitForFunction(pageFunction[, options[, ...args]])
- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
- `options` <[Object]> Optional waiting parameters
@@ -2396,37 +2407,6 @@ Browser websocket endpoint which can be used as an argument to
You can find the `webSocketDebuggerUrl` from `http://${host}:${port}/json/version`. Learn more about the [devtools protocol](https://chromedevtools.github.io/devtools-protocol) and the [browser endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
-### class: FileChooser
-
-[FileChooser] objects are returned via the ['page.waitForFileChooser'](#pagewaitforfilechooseroptions) method.
-
-File choosers let you react to the page requesting for a file.
-
-An example of using [FileChooser]:
-
-```js
-const [fileChooser] = await Promise.all([
- page.waitForFileChooser(),
- page.click('#upload-file-button'), // some button that triggers file selection
-]);
-await fileChooser.accept(['/tmp/myfile.pdf']);
-```
-
-> **NOTE** In browsers, only one file chooser can be opened at a time.
-> All file choosers must be accepted or canceled. Not doing so will prevent subsequent file choosers from appearing.
-
-#### fileChooser.accept(filePaths)
-- `filePaths` <[Array]<[string]>> Accept the file chooser request with given paths. If some of the `filePaths` are relative paths, then they are resolved relative to the [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
-- returns: <[Promise]>
-
-#### fileChooser.cancel()
-- returns: <[Promise]>
-
-Closes the file chooser without selecting any files.
-
-#### fileChooser.isMultiple()
-- returns: <[boolean]> Whether file chooser allow for [multiple](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#attr-multiple) file selection.
-
### class: Dialog
[Dialog] objects are dispatched by page via the ['dialog'](#event-dialog) event.
diff --git a/src/chromium/Page.ts b/src/chromium/Page.ts
index e47dd2f5d9..e498eb7cb5 100644
--- a/src/chromium/Page.ts
+++ b/src/chromium/Page.ts
@@ -161,9 +161,10 @@ export class Page extends EventEmitter {
const interceptors = Array.from(this._fileChooserInterceptors);
this._fileChooserInterceptors.clear();
const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple);
- const fileChooser = new FileChooser(handle, multiple);
+ const fileChooser = { element: handle, multiple };
for (const interceptor of interceptors)
interceptor.call(null, fileChooser);
+ this.emit(Events.Page.FileChooser, fileChooser);
}
async waitForFileChooser(options: { timeout?: number; } = {}): Promise {
@@ -729,28 +730,7 @@ export class ConsoleMessage {
}
}
-export class FileChooser {
- private _element: ElementHandle;
- private _multiple: boolean;
- private _handled = false;
-
- constructor(element: ElementHandle, multiple: boolean) {
- this._element = element;
- this._multiple = multiple;
- }
-
- isMultiple(): boolean {
- return this._multiple;
- }
-
- async accept(filePaths: string[]): Promise {
- assert(!this._handled, 'Cannot accept FileChooser which is already handled!');
- this._handled = true;
- await this._element.setInputFiles(...filePaths);
- }
-
- async cancel(): Promise {
- assert(!this._handled, 'Cannot cancel FileChooser which is already handled!');
- this._handled = true;
- }
-}
+type FileChooser = {
+ element: ElementHandle,
+ multiple: boolean
+};
diff --git a/src/chromium/api.ts b/src/chromium/api.ts
index d15ccb9334..bcc99cb689 100644
--- a/src/chromium/api.ts
+++ b/src/chromium/api.ts
@@ -20,6 +20,6 @@ export { Frame } from '../frames';
export { Keyboard, Mouse } from '../input';
export { ElementHandle } from './JSHandle';
export { Request, Response } from '../network';
-export { ConsoleMessage, FileChooser, Page } from './Page';
+export { ConsoleMessage, Page } from './Page';
export { Playwright } from './Playwright';
export { Target } from './Target';
diff --git a/src/chromium/events.ts b/src/chromium/events.ts
index a23f209b0e..fd3a0e7c85 100644
--- a/src/chromium/events.ts
+++ b/src/chromium/events.ts
@@ -20,6 +20,7 @@ export const Events = {
Close: 'close',
Console: 'console',
Dialog: 'dialog',
+ FileChooser: 'filechooser',
DOMContentLoaded: 'domcontentloaded',
// Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
diff --git a/src/firefox/Page.ts b/src/firefox/Page.ts
index 15ec3936c9..5c127c2a0a 100644
--- a/src/firefox/Page.ts
+++ b/src/firefox/Page.ts
@@ -552,9 +552,10 @@ export class Page extends EventEmitter {
const interceptors = Array.from(this._fileChooserInterceptors);
this._fileChooserInterceptors.clear();
const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple);
- const fileChooser = new FileChooser(handle, multiple);
+ const fileChooser = { element: handle, multiple };
for (const interceptor of interceptors)
interceptor.call(null, fileChooser);
+ this.emit(Events.Page.FileChooser, fileChooser);
}
}
@@ -619,28 +620,7 @@ export type Viewport = {
hasTouch?: boolean;
}
-export class FileChooser {
- private _element: ElementHandle;
- private _multiple: boolean;
- private _handled = false;
-
- constructor(element: ElementHandle, multiple: boolean) {
- this._element = element;
- this._multiple = multiple;
- }
-
- isMultiple(): boolean {
- return this._multiple;
- }
-
- async accept(filePaths: string[]): Promise {
- assert(!this._handled, 'Cannot accept FileChooser which is already handled!');
- this._handled = true;
- await this._element.setInputFiles(...filePaths);
- }
-
- async cancel(): Promise {
- assert(!this._handled, 'Cannot cancel FileChooser which is already handled!');
- this._handled = true;
- }
-}
+type FileChooser = {
+ element: ElementHandle,
+ multiple: boolean
+};
diff --git a/src/firefox/api.ts b/src/firefox/api.ts
index f17df9e964..0a08f152d2 100644
--- a/src/firefox/api.ts
+++ b/src/firefox/api.ts
@@ -13,6 +13,6 @@ export { Permissions } from './features/permissions';
export { Frame } from './FrameManager';
export { ElementHandle } from './JSHandle';
export { Request, Response } from '../network';
-export { ConsoleMessage, FileChooser, Page } from './Page';
+export { ConsoleMessage, Page } from './Page';
export { Playwright } from './Playwright';
diff --git a/src/firefox/events.ts b/src/firefox/events.ts
index 276be50f7f..b9ea22b794 100644
--- a/src/firefox/events.ts
+++ b/src/firefox/events.ts
@@ -20,6 +20,7 @@ export const Events = {
Close: 'close',
Console: 'console',
Dialog: 'dialog',
+ FileChooser: 'filechooser',
DOMContentLoaded: 'domcontentloaded',
// Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
diff --git a/src/webkit/Page.ts b/src/webkit/Page.ts
index 25fc1710a1..bdd2fd6296 100644
--- a/src/webkit/Page.ts
+++ b/src/webkit/Page.ts
@@ -462,9 +462,10 @@ export class Page extends EventEmitter {
const interceptors = Array.from(this._fileChooserInterceptors);
this._fileChooserInterceptors.clear();
const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple);
- const fileChooser = new FileChooser(handle, multiple);
+ const fileChooser = { element: handle, multiple };
for (const interceptor of interceptors)
interceptor.call(null, fileChooser);
+ this.emit(Events.Page.FileChooser, fileChooser);
}
get mouse(): input.Mouse {
@@ -586,28 +587,7 @@ export class ConsoleMessage {
}
}
-export class FileChooser {
- private _element: ElementHandle;
- private _multiple: boolean;
- private _handled = false;
-
- constructor(element: ElementHandle, multiple: boolean) {
- this._element = element;
- this._multiple = multiple;
- }
-
- isMultiple(): boolean {
- return this._multiple;
- }
-
- async accept(filePaths: string[]): Promise {
- assert(!this._handled, 'Cannot accept FileChooser which is already handled!');
- this._handled = true;
- await this._element.setInputFiles(...filePaths);
- }
-
- async cancel(): Promise {
- assert(!this._handled, 'Cannot cancel FileChooser which is already handled!');
- this._handled = true;
- }
-}
+type FileChooser = {
+ element: ElementHandle,
+ multiple: boolean
+};
diff --git a/src/webkit/events.ts b/src/webkit/events.ts
index 54d33500e5..de060389b0 100644
--- a/src/webkit/events.ts
+++ b/src/webkit/events.ts
@@ -20,6 +20,7 @@ export const Events = {
Close: 'close',
Console: 'console',
Dialog: 'dialog',
+ FileChooser: 'filechooser',
DOMContentLoaded: 'domcontentloaded',
Request: 'request',
Response: 'response',