Skip to main content

playwright_rs/protocol/
file_chooser.rs

1// Copyright 2026 Paul Adamson
2// Licensed under the Apache License, Version 2.0
3//
4// FileChooser - plain data struct constructed from "fileChooser" event params.
5//
6// FileChooser is NOT a ChannelOwner. It is constructed directly from
7// the event params when a "fileChooser" event is received on the Page channel.
8// The event params contain an ElementHandle GUID and an isMultiple flag.
9//
10// See: <https://playwright.dev/docs/api/class-filechooser>
11
12use crate::error::Result;
13use crate::protocol::ElementHandle;
14use std::path::PathBuf;
15use std::sync::Arc;
16
17/// Represents a file chooser dialog triggered by an `<input type="file">` element.
18///
19/// `FileChooser` objects are dispatched by the `"fileChooser"` event on
20/// [`Page`](crate::protocol::Page) via [`on_filechooser`](crate::protocol::Page::on_filechooser)
21/// and [`expect_file_chooser`](crate::protocol::Page::expect_file_chooser).
22///
23/// # Usage
24///
25/// Use [`set_files`](FileChooser::set_files) to satisfy the file chooser by providing
26/// file paths. The files are read from disk and sent to the browser.
27///
28/// # Example
29///
30/// ```ignore
31/// use playwright_rs::protocol::Playwright;
32/// use std::path::PathBuf;
33///
34/// #[tokio::main]
35/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
36///     let playwright = Playwright::launch().await?;
37///     let browser = playwright.chromium().launch().await?;
38///     let page = browser.new_page().await?;
39///
40///     page.set_content(
41///         r#"<input type="file" id="f" />"#,
42///         None
43///     ).await?;
44///
45///     // Set up the waiter BEFORE the action that triggers the file chooser
46///     let waiter = page.expect_file_chooser(None).await?;
47///
48///     // Click the file input to open the chooser
49///     page.locator("#f").await.click(None).await?;
50///
51///     // Resolve the waiter and set files
52///     let chooser = waiter.wait().await?;
53///     chooser.set_files(&[PathBuf::from("/tmp/test.txt")]).await?;
54///
55///     browser.close().await?;
56///     Ok(())
57/// }
58/// ```
59///
60/// See: <https://playwright.dev/docs/api/class-filechooser>
61#[derive(Clone)]
62pub struct FileChooser {
63    /// Back-reference to the page that owns this file chooser
64    page: crate::protocol::Page,
65    /// The `<input type="file">` element that triggered the chooser
66    element: Arc<ElementHandle>,
67    /// Whether the input accepts multiple files
68    is_multiple: bool,
69}
70
71impl FileChooser {
72    /// Creates a new `FileChooser` from event params.
73    ///
74    /// Called by `Page::on_event("fileChooser")` when a fileChooser event
75    /// is received from the Playwright server.
76    pub(crate) fn new(
77        page: crate::protocol::Page,
78        element: Arc<ElementHandle>,
79        is_multiple: bool,
80    ) -> Self {
81        Self {
82            page,
83            element,
84            is_multiple,
85        }
86    }
87
88    /// Returns the page that owns this file chooser.
89    ///
90    /// See: <https://playwright.dev/docs/api/class-filechooser#file-chooser-page>
91    pub fn page(&self) -> &crate::protocol::Page {
92        &self.page
93    }
94
95    /// Returns the `<input type="file">` element that triggered this chooser.
96    ///
97    /// See: <https://playwright.dev/docs/api/class-filechooser#file-chooser-element>
98    pub fn element(&self) -> Arc<ElementHandle> {
99        self.element.clone()
100    }
101
102    /// Returns `true` if the file input accepts multiple files.
103    ///
104    /// This reflects the `multiple` attribute on the underlying `<input type="file">`.
105    ///
106    /// See: <https://playwright.dev/docs/api/class-filechooser#file-chooser-is-multiple>
107    pub fn is_multiple(&self) -> bool {
108        self.is_multiple
109    }
110
111    /// Sets files on the associated `<input type="file">` element.
112    ///
113    /// Reads each file from disk, encodes as base64, and sends a `setInputFiles`
114    /// RPC directly on the ElementHandle channel. This satisfies the file chooser
115    /// dialog without requiring any OS-level file picker interaction.
116    ///
117    /// # Arguments
118    ///
119    /// * `files` - Slice of file paths to set on the input element
120    ///
121    /// # Errors
122    ///
123    /// Returns error if:
124    /// - Any file cannot be read from disk
125    /// - The RPC call to the browser fails
126    ///
127    /// # Example
128    ///
129    /// ```ignore
130    /// # use std::path::PathBuf;
131    /// let waiter = page.expect_file_chooser(None).await?;
132    /// page.locator("input[type=file]").await.click(None).await?;
133    /// let chooser = waiter.wait().await?;
134    /// chooser.set_files(&[PathBuf::from("/tmp/upload.txt")]).await?;
135    /// ```
136    ///
137    /// See: <https://playwright.dev/docs/api/class-filechooser#file-chooser-set-files>
138    pub async fn set_files(&self, files: &[PathBuf]) -> Result<()> {
139        self.element.set_input_files(files).await
140    }
141}
142
143impl std::fmt::Debug for FileChooser {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        f.debug_struct("FileChooser")
146            .field("is_multiple", &self.is_multiple)
147            .finish()
148    }
149}