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