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