playwright_rs/protocol/
element_handle.rs1use crate::error::Result;
7use crate::protocol::locator::BoundingBox;
8use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
9use base64::Engine;
10use serde::Deserialize;
11use serde_json::Value;
12use std::any::Any;
13use std::sync::Arc;
14
15#[derive(Clone)]
22pub struct ElementHandle {
23 base: ChannelOwnerImpl,
24}
25
26impl ElementHandle {
27 pub fn new(
32 parent: Arc<dyn ChannelOwner>,
33 type_name: String,
34 guid: Arc<str>,
35 initializer: Value,
36 ) -> Result<Self> {
37 let base = ChannelOwnerImpl::new(
38 ParentOrConnection::Parent(parent),
39 type_name,
40 guid,
41 initializer,
42 );
43
44 Ok(Self { base })
45 }
46
47 pub async fn screenshot(
70 &self,
71 options: Option<crate::protocol::ScreenshotOptions>,
72 ) -> Result<Vec<u8>> {
73 let params = if let Some(opts) = options {
74 opts.to_json()
75 } else {
76 serde_json::json!({
78 "type": "png",
79 "timeout": crate::DEFAULT_TIMEOUT_MS
80 })
81 };
82
83 #[derive(Deserialize)]
84 struct ScreenshotResponse {
85 binary: String,
86 }
87
88 let response: ScreenshotResponse = self.base.channel().send("screenshot", params).await?;
89
90 let bytes = base64::prelude::BASE64_STANDARD
92 .decode(&response.binary)
93 .map_err(|e| {
94 crate::error::Error::ProtocolError(format!(
95 "Failed to decode element screenshot: {}",
96 e
97 ))
98 })?;
99
100 Ok(bytes)
101 }
102
103 pub async fn bounding_box(&self) -> Result<Option<BoundingBox>> {
109 #[derive(Deserialize)]
110 struct BoundingBoxResponse {
111 value: Option<BoundingBox>,
112 }
113
114 let response: BoundingBoxResponse = self
115 .base
116 .channel()
117 .send(
118 "boundingBox",
119 serde_json::json!({
120 "timeout": crate::DEFAULT_TIMEOUT_MS
121 }),
122 )
123 .await?;
124
125 Ok(response.value)
126 }
127
128 pub(crate) async fn set_input_files(
139 &self,
140 files: &[std::path::PathBuf],
141 ) -> crate::error::Result<()> {
142 use base64::{Engine as _, engine::general_purpose};
143
144 let payloads: Vec<serde_json::Value> = files
145 .iter()
146 .map(|path| {
147 let name = path
148 .file_name()
149 .map(|n| n.to_string_lossy().into_owned())
150 .unwrap_or_else(|| "file".to_string());
151 let mime_type = mime_guess::from_path(path)
152 .first_or_octet_stream()
153 .to_string();
154 let buffer = std::fs::read(path).unwrap_or_default();
155 let b64 = general_purpose::STANDARD.encode(&buffer);
156 serde_json::json!({
157 "name": name,
158 "mimeType": mime_type,
159 "buffer": b64
160 })
161 })
162 .collect();
163
164 self.base
165 .channel()
166 .send_no_result(
167 "setInputFiles",
168 serde_json::json!({
169 "payloads": payloads,
170 "timeout": crate::DEFAULT_TIMEOUT_MS
171 }),
172 )
173 .await
174 }
175
176 pub async fn scroll_into_view_if_needed(&self) -> Result<()> {
180 self.base
181 .channel()
182 .send_no_result(
183 "scrollIntoViewIfNeeded",
184 serde_json::json!({
185 "timeout": crate::DEFAULT_TIMEOUT_MS
186 }),
187 )
188 .await
189 }
190
191 pub async fn content_frame(&self) -> Result<Option<crate::protocol::Frame>> {
196 use crate::server::connection::ConnectionExt;
197
198 #[derive(Deserialize)]
199 struct FrameRef {
200 guid: String,
201 }
202 #[derive(Deserialize)]
203 struct ContentFrameResponse {
204 frame: Option<FrameRef>,
205 }
206
207 let response: ContentFrameResponse = self
208 .base
209 .channel()
210 .send("contentFrame", serde_json::json!({}))
211 .await?;
212
213 match response.frame {
214 None => Ok(None),
215 Some(frame_ref) => {
216 let connection = self.base.connection();
217 let frame = connection
218 .get_typed::<crate::protocol::Frame>(&frame_ref.guid)
219 .await?;
220 Ok(Some(frame))
221 }
222 }
223 }
224
225 pub async fn owner_frame(&self) -> Result<Option<crate::protocol::Frame>> {
231 use crate::server::connection::ConnectionExt;
232
233 #[derive(Deserialize)]
234 struct FrameRef {
235 guid: String,
236 }
237 #[derive(Deserialize)]
238 struct OwnerFrameResponse {
239 frame: Option<FrameRef>,
240 }
241
242 let response: OwnerFrameResponse = self
243 .base
244 .channel()
245 .send("ownerFrame", serde_json::json!({}))
246 .await?;
247
248 match response.frame {
249 None => Ok(None),
250 Some(frame_ref) => {
251 let connection = self.base.connection();
252 let frame = connection
253 .get_typed::<crate::protocol::Frame>(&frame_ref.guid)
254 .await?;
255 Ok(Some(frame))
256 }
257 }
258 }
259
260 pub async fn wait_for_element_state(&self, state: &str, timeout: Option<f64>) -> Result<()> {
271 let timeout_ms = timeout.unwrap_or(crate::DEFAULT_TIMEOUT_MS);
272 self.base
273 .channel()
274 .send_no_result(
275 "waitForElementState",
276 serde_json::json!({
277 "state": state,
278 "timeout": timeout_ms
279 }),
280 )
281 .await
282 }
283}
284
285impl ChannelOwner for ElementHandle {
286 fn guid(&self) -> &str {
287 self.base.guid()
288 }
289
290 fn type_name(&self) -> &str {
291 self.base.type_name()
292 }
293
294 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
295 self.base.parent()
296 }
297
298 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
299 self.base.connection()
300 }
301
302 fn initializer(&self) -> &Value {
303 self.base.initializer()
304 }
305
306 fn channel(&self) -> &crate::server::channel::Channel {
307 self.base.channel()
308 }
309
310 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
311 self.base.dispose(reason)
312 }
313
314 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
315 self.base.adopt(child)
316 }
317
318 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
319 self.base.add_child(guid, child)
320 }
321
322 fn remove_child(&self, guid: &str) {
323 self.base.remove_child(guid)
324 }
325
326 fn on_event(&self, _method: &str, _params: Value) {
327 }
329
330 fn was_collected(&self) -> bool {
331 self.base.was_collected()
332 }
333
334 fn as_any(&self) -> &dyn Any {
335 self
336 }
337}
338
339impl std::fmt::Debug for ElementHandle {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 f.debug_struct("ElementHandle")
342 .field("guid", &self.guid())
343 .finish()
344 }
345}