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 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid(), bytes_len = tracing::field::Empty))]
70 pub async fn screenshot(
71 &self,
72 options: impl Into<Option<crate::protocol::ScreenshotOptions>>,
73 ) -> Result<Vec<u8>> {
74 let options = options.into();
75 let params = if let Some(opts) = options {
76 opts.to_json()
77 } else {
78 serde_json::json!({
80 "type": "png",
81 "timeout": crate::DEFAULT_TIMEOUT_MS
82 })
83 };
84
85 #[derive(Deserialize)]
86 struct ScreenshotResponse {
87 binary: String,
88 }
89
90 let response: ScreenshotResponse = self.base.channel().send("screenshot", params).await?;
91
92 let bytes = base64::prelude::BASE64_STANDARD
94 .decode(&response.binary)
95 .map_err(|e| {
96 crate::error::Error::ProtocolError(format!(
97 "Failed to decode element screenshot: {}",
98 e
99 ))
100 })?;
101 tracing::Span::current().record("bytes_len", bytes.len());
102
103 Ok(bytes)
104 }
105
106 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
112 pub async fn bounding_box(&self) -> Result<Option<BoundingBox>> {
113 #[derive(Deserialize)]
114 struct BoundingBoxResponse {
115 value: Option<BoundingBox>,
116 }
117
118 let response: BoundingBoxResponse = self
119 .base
120 .channel()
121 .send(
122 "boundingBox",
123 serde_json::json!({
124 "timeout": crate::DEFAULT_TIMEOUT_MS
125 }),
126 )
127 .await?;
128
129 Ok(response.value)
130 }
131
132 pub(crate) async fn set_input_files(
143 &self,
144 files: &[std::path::PathBuf],
145 ) -> crate::error::Result<()> {
146 use base64::{Engine as _, engine::general_purpose};
147
148 let payloads: Vec<serde_json::Value> = files
149 .iter()
150 .map(|path| {
151 let name = path
152 .file_name()
153 .map(|n| n.to_string_lossy().into_owned())
154 .unwrap_or_else(|| "file".to_string());
155 let mime_type = crate::protocol::mime::from_path(path);
156 let buffer = std::fs::read(path).unwrap_or_default();
157 let b64 = general_purpose::STANDARD.encode(&buffer);
158 serde_json::json!({
159 "name": name,
160 "mimeType": mime_type,
161 "buffer": b64
162 })
163 })
164 .collect();
165
166 self.base
167 .channel()
168 .send_no_result(
169 "setInputFiles",
170 serde_json::json!({
171 "payloads": payloads,
172 "timeout": crate::DEFAULT_TIMEOUT_MS
173 }),
174 )
175 .await
176 }
177
178 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
182 pub async fn scroll_into_view_if_needed(&self) -> Result<()> {
183 self.base
184 .channel()
185 .send_no_result(
186 "scrollIntoViewIfNeeded",
187 serde_json::json!({
188 "timeout": crate::DEFAULT_TIMEOUT_MS
189 }),
190 )
191 .await
192 }
193
194 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
199 pub async fn content_frame(&self) -> Result<Option<crate::protocol::Frame>> {
200 use crate::server::connection::ConnectionExt;
201
202 #[derive(Deserialize)]
203 struct FrameRef {
204 guid: String,
205 }
206 #[derive(Deserialize)]
207 struct ContentFrameResponse {
208 frame: Option<FrameRef>,
209 }
210
211 let response: ContentFrameResponse = self
212 .base
213 .channel()
214 .send("contentFrame", serde_json::json!({}))
215 .await?;
216
217 match response.frame {
218 None => Ok(None),
219 Some(frame_ref) => {
220 let connection = self.base.connection();
221 let frame = connection
222 .get_typed::<crate::protocol::Frame>(&frame_ref.guid)
223 .await?;
224 Ok(Some(frame))
225 }
226 }
227 }
228
229 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
235 pub async fn owner_frame(&self) -> Result<Option<crate::protocol::Frame>> {
236 use crate::server::connection::ConnectionExt;
237
238 #[derive(Deserialize)]
239 struct FrameRef {
240 guid: String,
241 }
242 #[derive(Deserialize)]
243 struct OwnerFrameResponse {
244 frame: Option<FrameRef>,
245 }
246
247 let response: OwnerFrameResponse = self
248 .base
249 .channel()
250 .send("ownerFrame", serde_json::json!({}))
251 .await?;
252
253 match response.frame {
254 None => Ok(None),
255 Some(frame_ref) => {
256 let connection = self.base.connection();
257 let frame = connection
258 .get_typed::<crate::protocol::Frame>(&frame_ref.guid)
259 .await?;
260 Ok(Some(frame))
261 }
262 }
263 }
264
265 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
276 pub async fn wait_for_element_state(&self, state: &str, timeout: Option<f64>) -> Result<()> {
277 let timeout_ms = timeout.unwrap_or(crate::DEFAULT_TIMEOUT_MS);
278 self.base
279 .channel()
280 .send_no_result(
281 "waitForElementState",
282 serde_json::json!({
283 "state": state,
284 "timeout": timeout_ms
285 }),
286 )
287 .await
288 }
289}
290
291impl ChannelOwner for ElementHandle {
292 fn guid(&self) -> &str {
293 self.base.guid()
294 }
295
296 fn type_name(&self) -> &str {
297 self.base.type_name()
298 }
299
300 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
301 self.base.parent()
302 }
303
304 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
305 self.base.connection()
306 }
307
308 fn initializer(&self) -> &Value {
309 self.base.initializer()
310 }
311
312 fn channel(&self) -> &crate::server::channel::Channel {
313 self.base.channel()
314 }
315
316 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
317 self.base.dispose(reason)
318 }
319
320 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
321 self.base.adopt(child)
322 }
323
324 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
325 self.base.add_child(guid, child)
326 }
327
328 fn remove_child(&self, guid: &str) {
329 self.base.remove_child(guid)
330 }
331
332 fn on_event(&self, _method: &str, _params: Value) {
333 }
335
336 fn was_collected(&self) -> bool {
337 self.base.was_collected()
338 }
339
340 fn as_any(&self) -> &dyn Any {
341 self
342 }
343}
344
345impl std::fmt::Debug for ElementHandle {
346 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347 f.debug_struct("ElementHandle")
348 .field("guid", &self.guid())
349 .finish()
350 }
351}