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}