1use crate::error::{Error, Result};
7use crate::protocol::page::{GotoOptions, Response, WaitUntil};
8use crate::protocol::{parse_result, serialize_argument, serialize_null};
9use crate::server::channel::Channel;
10use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
11use crate::server::connection::ConnectionExt;
12use serde::Deserialize;
13use serde_json::Value;
14use std::any::Any;
15use std::sync::{Arc, Mutex, RwLock};
16
17#[derive(Clone)]
26pub struct Frame {
27 base: ChannelOwnerImpl,
28 url: Arc<RwLock<String>>,
31 name: Arc<str>,
34 parent_frame_guid: Option<Arc<str>>,
37 is_detached: Arc<RwLock<bool>>,
40 page: Arc<Mutex<Option<crate::protocol::Page>>>,
45}
46
47impl Frame {
48 pub fn new(
53 parent: Arc<dyn ChannelOwner>,
54 type_name: String,
55 guid: Arc<str>,
56 initializer: Value,
57 ) -> Result<Self> {
58 let base = ChannelOwnerImpl::new(
59 ParentOrConnection::Parent(parent),
60 type_name,
61 guid,
62 initializer.clone(),
63 );
64
65 let initial_url = initializer
67 .get("url")
68 .and_then(|v| v.as_str())
69 .unwrap_or("about:blank")
70 .to_string();
71
72 let url = Arc::new(RwLock::new(initial_url));
73
74 let name: Arc<str> = Arc::from(
76 initializer
77 .get("name")
78 .and_then(|v| v.as_str())
79 .unwrap_or(""),
80 );
81
82 let parent_frame_guid: Option<Arc<str>> = initializer
84 .get("parentFrame")
85 .and_then(|v| v.get("guid"))
86 .and_then(|v| v.as_str())
87 .map(Arc::from);
88
89 Ok(Self {
90 base,
91 url,
92 name,
93 parent_frame_guid,
94 is_detached: Arc::new(RwLock::new(false)),
95 page: Arc::new(Mutex::new(None)),
96 })
97 }
98
99 pub(crate) fn set_page(&self, page: crate::protocol::Page) {
104 if let Ok(mut guard) = self.page.lock() {
105 *guard = Some(page);
106 }
107 }
108
109 pub fn page(&self) -> Option<crate::protocol::Page> {
118 if let Some(page) = self.page.lock().ok().and_then(|guard| guard.clone()) {
119 return Some(page);
120 }
121 let page = self.resolve_page()?;
127 self.set_page(page.clone());
128 Some(page)
129 }
130
131 fn resolve_page(&self) -> Option<crate::protocol::Page> {
134 let mut root = self.clone();
135 while let Some(parent) = root.parent_frame() {
136 root = parent;
137 }
138 let root_guid = root.guid().to_string();
139
140 self.base
141 .connection()
142 .all_objects_sync()
143 .into_iter()
144 .filter(|object| object.type_name() == "Page")
145 .find(|object| {
146 object.initializer()["mainFrame"]["guid"].as_str() == Some(root_guid.as_str())
147 })?
148 .as_any()
149 .downcast_ref::<crate::protocol::Page>()
150 .cloned()
151 }
152
153 pub fn name(&self) -> &str {
159 &self.name
160 }
161
162 pub fn parent_frame(&self) -> Option<crate::protocol::Frame> {
166 self.frame_by_guid(self.parent_frame_guid.as_deref()?)
172 }
173
174 fn frame_by_guid(&self, guid: &str) -> Option<crate::protocol::Frame> {
176 self.base
177 .connection()
178 .all_objects_sync()
179 .into_iter()
180 .find(|object| object.type_name() == "Frame" && object.guid() == guid)?
181 .as_any()
182 .downcast_ref::<crate::protocol::Frame>()
183 .cloned()
184 }
185
186 pub fn is_detached(&self) -> bool {
193 self.is_detached.read().map(|v| *v).unwrap_or(false)
194 }
195
196 pub fn child_frames(&self) -> Vec<crate::protocol::Frame> {
209 let my_guid = self.guid().to_string();
210 let conn = self.base.connection();
211
212 conn.all_objects_sync()
215 .into_iter()
216 .filter_map(|obj| {
217 if obj.type_name() != "Frame" {
219 return None;
220 }
221 let parent_guid = obj
223 .initializer()
224 .get("parentFrame")
225 .and_then(|v| v.get("guid"))
226 .and_then(|v| v.as_str())?;
227
228 if parent_guid == my_guid {
229 obj.as_any()
230 .downcast_ref::<crate::protocol::Frame>()
231 .cloned()
232 } else {
233 None
234 }
235 })
236 .collect()
237 }
238
239 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
280 pub async fn evaluate_handle(
281 &self,
282 expression: &str,
283 ) -> Result<Arc<crate::protocol::ElementHandle>> {
284 let params = serde_json::json!({
288 "expression": expression,
289 "arg": {"value": {"v": "undefined"}, "handles": []}
290 });
291
292 #[derive(Deserialize)]
294 struct HandleRef {
295 guid: String,
296 }
297 #[derive(Deserialize)]
298 struct EvaluateHandleResponse {
299 handle: HandleRef,
300 }
301
302 let response: EvaluateHandleResponse = self
303 .channel()
304 .send("evaluateExpressionHandle", params)
305 .await?;
306
307 let guid = &response.handle.guid;
308
309 let handle = self
311 .base
312 .connection()
313 .wait_for_typed::<crate::protocol::ElementHandle>(guid)
314 .await?;
315
316 Ok(Arc::new(handle))
317 }
318
319 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
342 pub async fn evaluate_handle_js(
343 &self,
344 expression: &str,
345 ) -> Result<std::sync::Arc<crate::protocol::JSHandle>> {
346 let params = serde_json::json!({
351 "expression": expression,
352 "arg": {"value": {"v": "undefined"}, "handles": []}
353 });
354
355 #[derive(Deserialize)]
357 struct HandleRef {
358 guid: String,
359 }
360 #[derive(Deserialize)]
361 struct EvaluateHandleResponse {
362 handle: HandleRef,
363 }
364
365 let response: EvaluateHandleResponse = self
366 .channel()
367 .send("evaluateExpressionHandle", params)
368 .await?;
369
370 let guid = &response.handle.guid;
371
372 let handle = crate::protocol::JSHandle::wait_for(&self.base.connection(), guid).await?;
373
374 Ok(std::sync::Arc::new(handle))
375 }
376
377 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
389 pub(crate) async fn wait_for_function_internal(
390 &self,
391 expression: &str,
392 selector: Option<&str>,
393 options: impl Into<Option<crate::protocol::WaitForFunctionOptions>>,
394 ) -> Result<Option<std::sync::Arc<crate::protocol::JSHandle>>> {
395 let options = options.into().unwrap_or_default();
396
397 let timeout = options
401 .timeout
402 .or_else(|| self.page().map(|p| p.default_timeout_ms()))
403 .unwrap_or(crate::DEFAULT_TIMEOUT_MS);
404
405 let mut params = serde_json::json!({
406 "expression": expression,
407 "arg": {"value": {"v": "undefined"}, "handles": []},
408 "timeout": timeout,
409 });
410 if let Some(interval) = options.polling_interval {
411 params["pollingInterval"] = serde_json::json!(interval);
412 }
413 if let Some(selector) = selector {
414 params["selector"] = serde_json::json!(selector);
415 params["strict"] = serde_json::json!(true);
416 }
417
418 #[derive(Deserialize)]
419 struct HandleRef {
420 guid: String,
421 }
422 #[derive(Deserialize)]
423 struct WaitForFunctionResponse {
424 handle: Option<HandleRef>,
425 }
426
427 let response: WaitForFunctionResponse =
428 self.channel().send("waitForFunction", params).await?;
429
430 let Some(handle_ref) = response.handle else {
431 return Ok(None);
432 };
433
434 let handle =
435 crate::protocol::JSHandle::wait_for(&self.base.connection(), &handle_ref.guid).await?;
436 Ok(Some(std::sync::Arc::new(handle)))
437 }
438
439 pub async fn wait_for_function(
451 &self,
452 expression: &str,
453 options: impl Into<Option<crate::protocol::WaitForFunctionOptions>>,
454 ) -> Result<std::sync::Arc<crate::protocol::JSHandle>> {
455 self.wait_for_function_internal(expression, None, options)
456 .await?
457 .ok_or_else(|| {
458 crate::error::Error::ProtocolError(
459 "waitForFunction returned no handle for a selector-less wait".to_string(),
460 )
461 })
462 }
463
464 pub(crate) async fn evaluate_with_fn_arg(
472 &self,
473 expression: &str,
474 binding_name: &str,
475 ) -> Result<Value> {
476 let params = serde_json::json!({
477 "expression": expression,
478 "arg": { "value": { "fn": binding_name }, "handles": [] },
479 });
480
481 #[derive(Deserialize)]
482 struct EvaluateResult {
483 value: serde_json::Value,
484 }
485
486 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
487 Ok(parse_result(&result.value))
488 }
489
490 pub fn locator(&self, selector: impl Into<String>) -> crate::protocol::Locator {
505 let page = self
506 .page()
507 .expect("Frame::locator(): the frame's page is gone (was it closed?)");
508 crate::protocol::Locator::new(Arc::new(self.clone()), selector.into(), page)
509 }
510
511 pub fn frame_locator<'a>(
524 &self,
525 selector: impl Into<Option<&'a str>>,
526 ) -> crate::protocol::FrameLocator {
527 let page = self
528 .page()
529 .expect("Frame::frame_locator(): the frame's page is gone (was it closed?)");
530 let frame = Arc::new(self.clone());
531 match selector.into() {
532 Some(selector) => crate::protocol::FrameLocator::new(frame, selector.to_string(), page),
533 None => crate::protocol::FrameLocator::any_frame(frame, page),
534 }
535 }
536
537 pub fn get_by_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
541 self.locator(crate::protocol::locator::get_by_text_selector(text, exact))
542 }
543
544 pub fn get_by_label(&self, text: &str, exact: bool) -> crate::protocol::Locator {
548 self.locator(crate::protocol::locator::get_by_label_selector(text, exact))
549 }
550
551 pub fn get_by_placeholder(&self, text: &str, exact: bool) -> crate::protocol::Locator {
555 self.locator(crate::protocol::locator::get_by_placeholder_selector(
556 text, exact,
557 ))
558 }
559
560 pub fn get_by_alt_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
564 self.locator(crate::protocol::locator::get_by_alt_text_selector(
565 text, exact,
566 ))
567 }
568
569 pub fn get_by_title(&self, text: &str, exact: bool) -> crate::protocol::Locator {
573 self.locator(crate::protocol::locator::get_by_title_selector(text, exact))
574 }
575
576 pub fn get_by_test_id(&self, test_id: &str) -> crate::protocol::Locator {
583 use crate::server::channel_owner::ChannelOwner;
584 let attr = self.connection().selectors().test_id_attribute();
585 self.locator(crate::protocol::locator::get_by_test_id_selector_with_attr(
586 test_id, &attr,
587 ))
588 }
589
590 pub fn get_by_role(
594 &self,
595 role: crate::protocol::locator::AriaRole,
596 options: Option<crate::protocol::locator::GetByRoleOptions>,
597 ) -> crate::protocol::Locator {
598 self.locator(crate::protocol::locator::get_by_role_selector(
599 role, options,
600 ))
601 }
602
603 fn channel(&self) -> &Channel {
605 self.base.channel()
606 }
607
608 pub fn url(&self) -> String {
614 self.url.read().unwrap().clone()
615 }
616
617 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid(), url = %url, status = tracing::field::Empty))]
631 pub async fn goto(
632 &self,
633 url: &str,
634 options: impl Into<Option<GotoOptions>>,
635 ) -> Result<Option<Response>> {
636 let options = options.into();
637 let mut params = serde_json::json!({
639 "url": url,
640 });
641
642 if let Some(opts) = options {
644 if let Some(timeout) = opts.timeout {
645 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
646 } else {
647 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
649 }
650 if let Some(wait_until) = opts.wait_until {
651 params["waitUntil"] = serde_json::json!(wait_until.as_str());
652 }
653 } else {
654 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
656 }
657
658 #[derive(Deserialize)]
661 struct GotoResponse {
662 response: Option<ResponseReference>,
663 }
664
665 #[derive(Deserialize)]
666 struct ResponseReference {
667 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
668 guid: Arc<str>,
669 }
670
671 let goto_result: GotoResponse = self.channel().send("goto", params).await?;
672
673 if let Some(response_ref) = goto_result.response {
675 let response_arc = self
677 .connection()
678 .wait_for_object(&response_ref.guid)
679 .await?;
680
681 let initializer = response_arc.initializer();
684
685 let status = initializer["status"].as_u64().ok_or_else(|| {
687 crate::error::Error::ProtocolError("Response missing status".to_string())
688 })? as u16;
689
690 let headers = initializer["headers"]
692 .as_array()
693 .ok_or_else(|| {
694 crate::error::Error::ProtocolError("Response missing headers".to_string())
695 })?
696 .iter()
697 .filter_map(|h| {
698 let name = h["name"].as_str()?;
699 let value = h["value"].as_str()?;
700 Some((name.to_string(), value.to_string()))
701 })
702 .collect();
703
704 tracing::Span::current().record("status", status);
705 Ok(Some(Response::new(
706 initializer["url"]
707 .as_str()
708 .ok_or_else(|| {
709 crate::error::Error::ProtocolError("Response missing url".to_string())
710 })?
711 .to_string(),
712 status,
713 initializer["statusText"].as_str().unwrap_or("").to_string(),
714 headers,
715 Some(response_arc),
716 )))
717 } else {
718 Ok(None)
721 }
722 }
723
724 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
728 pub async fn title(&self) -> Result<String> {
729 #[derive(Deserialize)]
730 struct TitleResponse {
731 value: String,
732 }
733
734 let response: TitleResponse = self.channel().send("title", serde_json::json!({})).await?;
735 Ok(response.value)
736 }
737
738 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
742 pub async fn content(&self) -> Result<String> {
743 #[derive(Deserialize)]
744 struct ContentResponse {
745 value: String,
746 }
747
748 let response: ContentResponse = self
749 .channel()
750 .send("content", serde_json::json!({}))
751 .await?;
752 Ok(response.value)
753 }
754
755 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
759 pub async fn set_content(
760 &self,
761 html: &str,
762 options: impl Into<Option<GotoOptions>>,
763 ) -> Result<()> {
764 let options = options.into();
765 let mut params = serde_json::json!({
766 "html": html,
767 });
768
769 if let Some(opts) = options {
770 if let Some(timeout) = opts.timeout {
771 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
772 } else {
773 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
774 }
775 if let Some(wait_until) = opts.wait_until {
776 params["waitUntil"] = serde_json::json!(wait_until.as_str());
777 }
778 } else {
779 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
780 }
781
782 self.channel().send_no_result("setContent", params).await
783 }
784
785 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
793 pub async fn wait_for_load_state(&self, state: Option<WaitUntil>) -> Result<()> {
794 let target_state = state.unwrap_or(WaitUntil::Load);
795
796 let js_check = match target_state {
797 WaitUntil::Load => "document.readyState === 'complete'",
799 WaitUntil::DomContentLoaded => "document.readyState !== 'loading'",
801 WaitUntil::NetworkIdle => "document.readyState === 'complete'",
804 WaitUntil::Commit => "document.readyState !== 'loading'",
806 };
807
808 let timeout_ms = crate::DEFAULT_TIMEOUT_MS as u64;
809 let poll_interval = std::time::Duration::from_millis(50);
810 let start = std::time::Instant::now();
811
812 loop {
813 #[derive(Deserialize)]
814 struct EvalResponse {
815 value: serde_json::Value,
816 }
817
818 let result: EvalResponse = self
819 .channel()
820 .send(
821 "evaluateExpression",
822 serde_json::json!({
823 "expression": js_check,
824 "isFunction": false,
825 "arg": crate::protocol::serialize_null(),
826 }),
827 )
828 .await?;
829
830 let is_ready = result
832 .value
833 .as_object()
834 .and_then(|m| m.get("b"))
835 .and_then(|v| v.as_bool())
836 .unwrap_or(false);
837
838 if is_ready {
839 return Ok(());
840 }
841
842 if start.elapsed().as_millis() as u64 >= timeout_ms {
843 return Err(crate::error::Error::Timeout(format!(
844 "wait_for_load_state({}) timed out after {}ms",
845 target_state.as_str(),
846 timeout_ms
847 )));
848 }
849
850 tokio::time::sleep(poll_interval).await;
851 }
852 }
853
854 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid(), url = %url))]
861 pub async fn wait_for_url(
862 &self,
863 url: &str,
864 options: impl Into<Option<GotoOptions>>,
865 ) -> Result<()> {
866 let options = options.into();
867 let timeout_ms = options
868 .as_ref()
869 .and_then(|o| o.timeout)
870 .map(|d| d.as_millis() as u64)
871 .unwrap_or(crate::DEFAULT_TIMEOUT_MS as u64);
872
873 let matcher = if url.contains('*') {
877 Some(crate::protocol::glob::GlobMatcher::new(url))
878 } else {
879 None
880 };
881
882 let poll_interval = std::time::Duration::from_millis(50);
883 let start = std::time::Instant::now();
884
885 loop {
886 let current_url = self.url();
887
888 let matches = match &matcher {
889 Some(matcher) => matcher.as_ref().is_some_and(|m| m.matches(¤t_url)),
891 None => current_url == url,
892 };
893
894 if matches {
895 if let Some(ref opts) = options
897 && let Some(wait_until) = opts.wait_until
898 {
899 self.wait_for_load_state(Some(wait_until)).await?;
900 }
901 return Ok(());
902 }
903
904 if start.elapsed().as_millis() as u64 >= timeout_ms {
905 return Err(crate::error::Error::Timeout(format!(
906 "wait_for_url({}) timed out after {}ms, current URL: {}",
907 url, timeout_ms, current_url
908 )));
909 }
910
911 tokio::time::sleep(poll_interval).await;
912 }
913 }
914
915 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
919 pub async fn query_selector(
920 &self,
921 selector: &str,
922 ) -> Result<Option<Arc<crate::protocol::ElementHandle>>> {
923 let response: serde_json::Value = self
924 .channel()
925 .send(
926 "querySelector",
927 serde_json::json!({
928 "selector": selector
929 }),
930 )
931 .await?;
932
933 if response.as_object().map(|o| o.is_empty()).unwrap_or(true) {
935 return Ok(None);
936 }
937
938 let element_value = if let Some(elem) = response.get("element") {
940 elem
941 } else if let Some(elem) = response.get("handle") {
942 elem
943 } else {
944 &response
946 };
947
948 if element_value.is_null() {
949 return Ok(None);
950 }
951
952 let guid = element_value["guid"].as_str().ok_or_else(|| {
954 crate::error::Error::ProtocolError("Element GUID missing".to_string())
955 })?;
956
957 let connection = self.base.connection();
959 let handle: crate::protocol::ElementHandle = connection
960 .get_typed::<crate::protocol::ElementHandle>(guid)
961 .await?;
962
963 Ok(Some(Arc::new(handle)))
964 }
965
966 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
970 pub async fn query_selector_all(
971 &self,
972 selector: &str,
973 ) -> Result<Vec<Arc<crate::protocol::ElementHandle>>> {
974 #[derive(Deserialize)]
975 struct QueryAllResponse {
976 elements: Vec<serde_json::Value>,
977 }
978
979 let response: QueryAllResponse = self
980 .channel()
981 .send(
982 "querySelectorAll",
983 serde_json::json!({
984 "selector": selector
985 }),
986 )
987 .await?;
988
989 let connection = self.base.connection();
991 let mut handles = Vec::new();
992
993 for element_value in response.elements {
994 let guid = element_value["guid"].as_str().ok_or_else(|| {
995 crate::error::Error::ProtocolError("Element GUID missing".to_string())
996 })?;
997
998 let handle: crate::protocol::ElementHandle = connection
999 .get_typed::<crate::protocol::ElementHandle>(guid)
1000 .await?;
1001
1002 handles.push(Arc::new(handle));
1003 }
1004
1005 Ok(handles)
1006 }
1007
1008 pub(crate) async fn locator_count(&self, selector: &str) -> Result<usize> {
1013 #[derive(Deserialize)]
1015 struct QueryAllResponse {
1016 elements: Vec<serde_json::Value>,
1017 }
1018
1019 let response: QueryAllResponse = self
1020 .channel()
1021 .send(
1022 "querySelectorAll",
1023 serde_json::json!({
1024 "selector": selector
1025 }),
1026 )
1027 .await?;
1028
1029 Ok(response.elements.len())
1030 }
1031
1032 pub(crate) async fn locator_text_content(&self, selector: &str) -> Result<Option<String>> {
1034 #[derive(Deserialize)]
1035 struct TextContentResponse {
1036 value: Option<String>,
1037 }
1038
1039 let response: TextContentResponse = self
1040 .channel()
1041 .send(
1042 "textContent",
1043 serde_json::json!({
1044 "selector": selector,
1045 "strict": true,
1046 "timeout": crate::DEFAULT_TIMEOUT_MS
1047 }),
1048 )
1049 .await?;
1050
1051 Ok(response.value)
1052 }
1053
1054 pub(crate) async fn locator_inner_text(&self, selector: &str) -> Result<String> {
1056 #[derive(Deserialize)]
1057 struct InnerTextResponse {
1058 value: String,
1059 }
1060
1061 let response: InnerTextResponse = self
1062 .channel()
1063 .send(
1064 "innerText",
1065 serde_json::json!({
1066 "selector": selector,
1067 "strict": true,
1068 "timeout": crate::DEFAULT_TIMEOUT_MS
1069 }),
1070 )
1071 .await?;
1072
1073 Ok(response.value)
1074 }
1075
1076 pub(crate) async fn locator_inner_html(&self, selector: &str) -> Result<String> {
1078 #[derive(Deserialize)]
1079 struct InnerHTMLResponse {
1080 value: String,
1081 }
1082
1083 let response: InnerHTMLResponse = self
1084 .channel()
1085 .send(
1086 "innerHTML",
1087 serde_json::json!({
1088 "selector": selector,
1089 "strict": true,
1090 "timeout": crate::DEFAULT_TIMEOUT_MS
1091 }),
1092 )
1093 .await?;
1094
1095 Ok(response.value)
1096 }
1097
1098 pub(crate) async fn locator_get_attribute(
1100 &self,
1101 selector: &str,
1102 name: &str,
1103 ) -> Result<Option<String>> {
1104 #[derive(Deserialize)]
1105 struct GetAttributeResponse {
1106 value: Option<String>,
1107 }
1108
1109 let response: GetAttributeResponse = self
1110 .channel()
1111 .send(
1112 "getAttribute",
1113 serde_json::json!({
1114 "selector": selector,
1115 "name": name,
1116 "strict": true,
1117 "timeout": crate::DEFAULT_TIMEOUT_MS
1118 }),
1119 )
1120 .await?;
1121
1122 Ok(response.value)
1123 }
1124
1125 pub(crate) async fn locator_is_visible(&self, selector: &str) -> Result<bool> {
1127 #[derive(Deserialize)]
1128 struct IsVisibleResponse {
1129 value: bool,
1130 }
1131
1132 let response: IsVisibleResponse = self
1133 .channel()
1134 .send(
1135 "isVisible",
1136 serde_json::json!({
1137 "selector": selector,
1138 "strict": true,
1139 "timeout": crate::DEFAULT_TIMEOUT_MS
1140 }),
1141 )
1142 .await?;
1143
1144 Ok(response.value)
1145 }
1146
1147 pub(crate) async fn locator_is_enabled(&self, selector: &str) -> Result<bool> {
1149 #[derive(Deserialize)]
1150 struct IsEnabledResponse {
1151 value: bool,
1152 }
1153
1154 let response: IsEnabledResponse = self
1155 .channel()
1156 .send(
1157 "isEnabled",
1158 serde_json::json!({
1159 "selector": selector,
1160 "strict": true,
1161 "timeout": crate::DEFAULT_TIMEOUT_MS
1162 }),
1163 )
1164 .await?;
1165
1166 Ok(response.value)
1167 }
1168
1169 pub(crate) async fn locator_is_checked(&self, selector: &str) -> Result<bool> {
1171 #[derive(Deserialize)]
1172 struct IsCheckedResponse {
1173 value: bool,
1174 }
1175
1176 let response: IsCheckedResponse = self
1177 .channel()
1178 .send(
1179 "isChecked",
1180 serde_json::json!({
1181 "selector": selector,
1182 "strict": true,
1183 "timeout": crate::DEFAULT_TIMEOUT_MS
1184 }),
1185 )
1186 .await?;
1187
1188 Ok(response.value)
1189 }
1190
1191 pub(crate) async fn locator_is_editable(&self, selector: &str) -> Result<bool> {
1193 #[derive(Deserialize)]
1194 struct IsEditableResponse {
1195 value: bool,
1196 }
1197
1198 let response: IsEditableResponse = self
1199 .channel()
1200 .send(
1201 "isEditable",
1202 serde_json::json!({
1203 "selector": selector,
1204 "strict": true,
1205 "timeout": crate::DEFAULT_TIMEOUT_MS
1206 }),
1207 )
1208 .await?;
1209
1210 Ok(response.value)
1211 }
1212
1213 pub(crate) async fn locator_is_hidden(&self, selector: &str) -> Result<bool> {
1215 #[derive(Deserialize)]
1216 struct IsHiddenResponse {
1217 value: bool,
1218 }
1219
1220 let response: IsHiddenResponse = self
1221 .channel()
1222 .send(
1223 "isHidden",
1224 serde_json::json!({
1225 "selector": selector,
1226 "strict": true,
1227 "timeout": crate::DEFAULT_TIMEOUT_MS
1228 }),
1229 )
1230 .await?;
1231
1232 Ok(response.value)
1233 }
1234
1235 pub(crate) async fn locator_is_disabled(&self, selector: &str) -> Result<bool> {
1237 #[derive(Deserialize)]
1238 struct IsDisabledResponse {
1239 value: bool,
1240 }
1241
1242 let response: IsDisabledResponse = self
1243 .channel()
1244 .send(
1245 "isDisabled",
1246 serde_json::json!({
1247 "selector": selector,
1248 "strict": true,
1249 "timeout": crate::DEFAULT_TIMEOUT_MS
1250 }),
1251 )
1252 .await?;
1253
1254 Ok(response.value)
1255 }
1256
1257 pub(crate) async fn locator_is_focused(&self, selector: &str) -> Result<bool> {
1263 #[derive(Deserialize)]
1264 struct EvaluateResult {
1265 value: serde_json::Value,
1266 }
1267
1268 let script = r#"selector => {
1271 const elements = document.querySelectorAll(selector);
1272 if (elements.length === 0) return false;
1273 const element = elements[0];
1274 return document.activeElement === element;
1275 }"#;
1276
1277 let params = serde_json::json!({
1278 "expression": script,
1279 "arg": {
1280 "value": {"s": selector},
1281 "handles": []
1282 }
1283 });
1284
1285 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1286
1287 if let serde_json::Value::Object(map) = &result.value
1289 && let Some(b) = map.get("b").and_then(|v| v.as_bool())
1290 {
1291 return Ok(b);
1292 }
1293
1294 Ok(result.value.to_string().to_lowercase().contains("true"))
1296 }
1297
1298 pub(crate) async fn locator_click(
1302 &self,
1303 selector: &str,
1304 options: Option<crate::protocol::ClickOptions>,
1305 ) -> Result<()> {
1306 let mut params = serde_json::json!({
1307 "selector": selector,
1308 "strict": true
1309 });
1310
1311 if let Some(opts) = options {
1312 let opts_json = opts.to_json();
1313 if let Some(obj) = params.as_object_mut()
1314 && let Some(opts_obj) = opts_json.as_object()
1315 {
1316 obj.extend(opts_obj.clone());
1317 }
1318 } else {
1319 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1320 }
1321
1322 self.channel()
1323 .send_no_result("click", params)
1324 .await
1325 .map_err(|e| match e {
1326 Error::Timeout(msg) => {
1327 Error::Timeout(format!("{} (selector: '{}')", msg, selector))
1328 }
1329 other => other,
1330 })
1331 }
1332
1333 pub(crate) async fn locator_dblclick(
1335 &self,
1336 selector: &str,
1337 options: Option<crate::protocol::ClickOptions>,
1338 ) -> Result<()> {
1339 let mut params = serde_json::json!({
1340 "selector": selector,
1341 "strict": true
1342 });
1343
1344 if let Some(opts) = options {
1345 let opts_json = opts.to_json();
1346 if let Some(obj) = params.as_object_mut()
1347 && let Some(opts_obj) = opts_json.as_object()
1348 {
1349 obj.extend(opts_obj.clone());
1350 }
1351 } else {
1352 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1353 }
1354
1355 self.channel().send_no_result("dblclick", params).await
1356 }
1357
1358 pub(crate) async fn locator_fill(
1360 &self,
1361 selector: &str,
1362 text: &str,
1363 options: Option<crate::protocol::FillOptions>,
1364 ) -> Result<()> {
1365 let mut params = serde_json::json!({
1366 "selector": selector,
1367 "value": text,
1368 "strict": true
1369 });
1370
1371 if let Some(opts) = options {
1372 let opts_json = opts.to_json();
1373 if let Some(obj) = params.as_object_mut()
1374 && let Some(opts_obj) = opts_json.as_object()
1375 {
1376 obj.extend(opts_obj.clone());
1377 }
1378 } else {
1379 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1380 }
1381
1382 self.channel().send_no_result("fill", params).await
1383 }
1384
1385 pub(crate) async fn locator_clear(
1387 &self,
1388 selector: &str,
1389 options: Option<crate::protocol::FillOptions>,
1390 ) -> Result<()> {
1391 let mut params = serde_json::json!({
1392 "selector": selector,
1393 "value": "",
1394 "strict": true
1395 });
1396
1397 if let Some(opts) = options {
1398 let opts_json = opts.to_json();
1399 if let Some(obj) = params.as_object_mut()
1400 && let Some(opts_obj) = opts_json.as_object()
1401 {
1402 obj.extend(opts_obj.clone());
1403 }
1404 } else {
1405 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1406 }
1407
1408 self.channel().send_no_result("fill", params).await
1409 }
1410
1411 pub(crate) async fn locator_press(
1413 &self,
1414 selector: &str,
1415 key: &str,
1416 options: Option<crate::protocol::PressOptions>,
1417 ) -> Result<()> {
1418 let mut params = serde_json::json!({
1419 "selector": selector,
1420 "key": key,
1421 "strict": true
1422 });
1423
1424 if let Some(opts) = options {
1425 let opts_json = opts.to_json();
1426 if let Some(obj) = params.as_object_mut()
1427 && let Some(opts_obj) = opts_json.as_object()
1428 {
1429 obj.extend(opts_obj.clone());
1430 }
1431 } else {
1432 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1433 }
1434
1435 self.channel().send_no_result("press", params).await
1436 }
1437
1438 pub(crate) async fn locator_focus(&self, selector: &str) -> Result<()> {
1440 self.channel()
1441 .send_no_result(
1442 "focus",
1443 serde_json::json!({
1444 "selector": selector,
1445 "strict": true,
1446 "timeout": crate::DEFAULT_TIMEOUT_MS
1447 }),
1448 )
1449 .await
1450 }
1451
1452 pub(crate) async fn locator_blur(&self, selector: &str) -> Result<()> {
1454 self.channel()
1455 .send_no_result(
1456 "blur",
1457 serde_json::json!({
1458 "selector": selector,
1459 "strict": true,
1460 "timeout": crate::DEFAULT_TIMEOUT_MS
1461 }),
1462 )
1463 .await
1464 }
1465
1466 pub(crate) async fn locator_press_sequentially(
1470 &self,
1471 selector: &str,
1472 text: &str,
1473 options: Option<crate::protocol::PressSequentiallyOptions>,
1474 ) -> Result<()> {
1475 let mut params = serde_json::json!({
1476 "selector": selector,
1477 "text": text,
1478 "strict": true
1479 });
1480
1481 if let Some(opts) = options {
1482 let opts_json = opts.to_json();
1483 if let Some(obj) = params.as_object_mut()
1484 && let Some(opts_obj) = opts_json.as_object()
1485 {
1486 obj.extend(opts_obj.clone());
1487 }
1488 } else {
1489 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1490 }
1491
1492 self.channel().send_no_result("type", params).await
1493 }
1494
1495 pub(crate) async fn locator_all_inner_texts(&self, selector: &str) -> Result<Vec<String>> {
1497 #[derive(serde::Deserialize)]
1498 struct EvaluateResult {
1499 value: serde_json::Value,
1500 }
1501
1502 let params = serde_json::json!({
1505 "selector": selector,
1506 "expression": "ee => ee.map(e => e.innerText)",
1507 "isFunction": true,
1508 "arg": {
1509 "value": {"v": "null"},
1510 "handles": []
1511 }
1512 });
1513
1514 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1515
1516 Self::parse_string_array(result.value)
1517 }
1518
1519 pub(crate) async fn locator_all_text_contents(&self, selector: &str) -> Result<Vec<String>> {
1521 #[derive(serde::Deserialize)]
1522 struct EvaluateResult {
1523 value: serde_json::Value,
1524 }
1525
1526 let params = serde_json::json!({
1529 "selector": selector,
1530 "expression": "ee => ee.map(e => e.textContent || '')",
1531 "isFunction": true,
1532 "arg": {
1533 "value": {"v": "null"},
1534 "handles": []
1535 }
1536 });
1537
1538 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1539
1540 Self::parse_string_array(result.value)
1541 }
1542
1543 pub(crate) async fn locator_tap(
1550 &self,
1551 selector: &str,
1552 options: Option<crate::protocol::TapOptions>,
1553 ) -> Result<()> {
1554 let mut params = serde_json::json!({
1555 "selector": selector,
1556 "strict": true
1557 });
1558
1559 if let Some(opts) = options {
1560 let opts_json = opts.to_json();
1561 if let Some(obj) = params.as_object_mut()
1562 && let Some(opts_obj) = opts_json.as_object()
1563 {
1564 obj.extend(opts_obj.clone());
1565 }
1566 } else {
1567 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1568 }
1569
1570 self.channel().send_no_result("tap", params).await
1571 }
1572
1573 pub(crate) async fn locator_drag_to(
1579 &self,
1580 source_selector: &str,
1581 target_selector: &str,
1582 options: Option<crate::protocol::DragToOptions>,
1583 ) -> Result<()> {
1584 let mut params = serde_json::json!({
1585 "source": source_selector,
1586 "target": target_selector,
1587 "strict": true
1588 });
1589
1590 if let Some(opts) = options {
1591 let opts_json = opts.to_json();
1592 if let Some(obj) = params.as_object_mut()
1593 && let Some(opts_obj) = opts_json.as_object()
1594 {
1595 obj.extend(opts_obj.clone());
1596 }
1597 } else {
1598 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1599 }
1600
1601 self.channel().send_no_result("dragAndDrop", params).await
1602 }
1603
1604 pub(crate) async fn locator_drop(
1608 &self,
1609 selector: &str,
1610 options: crate::protocol::DropOptions,
1611 ) -> Result<()> {
1612 let mut params = serde_json::json!({
1613 "selector": selector,
1614 "strict": true,
1615 });
1616
1617 let opts_json = options.to_json();
1618 if let Some(obj) = params.as_object_mut()
1619 && let Some(opts_obj) = opts_json.as_object()
1620 {
1621 obj.extend(opts_obj.clone());
1622 }
1623
1624 self.channel().send_no_result("drop", params).await
1625 }
1626
1627 pub(crate) async fn locator_wait_for(
1634 &self,
1635 selector: &str,
1636 options: Option<crate::protocol::WaitForOptions>,
1637 ) -> Result<()> {
1638 let mut params = serde_json::json!({
1639 "selector": selector,
1640 "strict": true
1641 });
1642
1643 if let Some(opts) = options {
1644 let opts_json = opts.to_json();
1645 if let Some(obj) = params.as_object_mut()
1646 && let Some(opts_obj) = opts_json.as_object()
1647 {
1648 obj.extend(opts_obj.clone());
1649 }
1650 } else {
1651 params["state"] = serde_json::json!("visible");
1653 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1654 }
1655
1656 let _: serde_json::Value = self.channel().send("waitForSelector", params).await?;
1658 Ok(())
1659 }
1660
1661 pub(crate) async fn locator_evaluate<T: serde::Serialize>(
1668 &self,
1669 selector: &str,
1670 expression: &str,
1671 arg: Option<T>,
1672 ) -> Result<serde_json::Value> {
1673 let serialized_arg = match arg {
1674 Some(a) => serialize_argument(&a),
1675 None => serialize_null(),
1676 };
1677
1678 let params = serde_json::json!({
1679 "selector": selector,
1680 "expression": expression,
1681 "isFunction": true,
1682 "arg": serialized_arg,
1683 "strict": true
1684 });
1685
1686 #[derive(Deserialize)]
1687 struct EvaluateResult {
1688 value: serde_json::Value,
1689 }
1690
1691 let result: EvaluateResult = self.channel().send("evalOnSelector", params).await?;
1692 Ok(parse_result(&result.value))
1693 }
1694
1695 pub(crate) async fn locator_evaluate_all<T: serde::Serialize>(
1702 &self,
1703 selector: &str,
1704 expression: &str,
1705 arg: Option<T>,
1706 ) -> Result<serde_json::Value> {
1707 let serialized_arg = match arg {
1708 Some(a) => serialize_argument(&a),
1709 None => serialize_null(),
1710 };
1711
1712 let params = serde_json::json!({
1713 "selector": selector,
1714 "expression": expression,
1715 "isFunction": true,
1716 "arg": serialized_arg
1717 });
1718
1719 #[derive(Deserialize)]
1720 struct EvaluateResult {
1721 value: serde_json::Value,
1722 }
1723
1724 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1725 Ok(parse_result(&result.value))
1726 }
1727
1728 fn parse_string_array(value: serde_json::Value) -> Result<Vec<String>> {
1733 let array = if let Some(arr) = value.get("a").and_then(|v| v.as_array()) {
1735 arr.clone()
1736 } else if let Some(arr) = value.as_array() {
1737 arr.clone()
1738 } else {
1739 return Ok(Vec::new());
1740 };
1741
1742 let mut result = Vec::with_capacity(array.len());
1743 for item in &array {
1744 let s = if let Some(s) = item.get("s").and_then(|v| v.as_str()) {
1746 s.to_string()
1747 } else if let Some(s) = item.as_str() {
1748 s.to_string()
1749 } else if item.is_null() {
1750 String::new()
1751 } else {
1752 item.to_string()
1753 };
1754 result.push(s);
1755 }
1756 Ok(result)
1757 }
1758
1759 pub(crate) async fn locator_check(
1760 &self,
1761 selector: &str,
1762 options: Option<crate::protocol::CheckOptions>,
1763 ) -> Result<()> {
1764 let mut params = serde_json::json!({
1765 "selector": selector,
1766 "strict": true
1767 });
1768
1769 if let Some(opts) = options {
1770 let opts_json = opts.to_json();
1771 if let Some(obj) = params.as_object_mut()
1772 && let Some(opts_obj) = opts_json.as_object()
1773 {
1774 obj.extend(opts_obj.clone());
1775 }
1776 } else {
1777 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1778 }
1779
1780 self.channel().send_no_result("check", params).await
1781 }
1782
1783 pub(crate) async fn locator_uncheck(
1784 &self,
1785 selector: &str,
1786 options: Option<crate::protocol::CheckOptions>,
1787 ) -> Result<()> {
1788 let mut params = serde_json::json!({
1789 "selector": selector,
1790 "strict": true
1791 });
1792
1793 if let Some(opts) = options {
1794 let opts_json = opts.to_json();
1795 if let Some(obj) = params.as_object_mut()
1796 && let Some(opts_obj) = opts_json.as_object()
1797 {
1798 obj.extend(opts_obj.clone());
1799 }
1800 } else {
1801 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1802 }
1803
1804 self.channel().send_no_result("uncheck", params).await
1805 }
1806
1807 pub(crate) async fn locator_hover(
1808 &self,
1809 selector: &str,
1810 options: Option<crate::protocol::HoverOptions>,
1811 ) -> Result<()> {
1812 let mut params = serde_json::json!({
1813 "selector": selector,
1814 "strict": true
1815 });
1816
1817 if let Some(opts) = options {
1818 let opts_json = opts.to_json();
1819 if let Some(obj) = params.as_object_mut()
1820 && let Some(opts_obj) = opts_json.as_object()
1821 {
1822 obj.extend(opts_obj.clone());
1823 }
1824 } else {
1825 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1826 }
1827
1828 self.channel().send_no_result("hover", params).await
1829 }
1830
1831 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
1832 #[derive(Deserialize)]
1833 struct InputValueResponse {
1834 value: String,
1835 }
1836
1837 let response: InputValueResponse = self
1838 .channel()
1839 .send(
1840 "inputValue",
1841 serde_json::json!({
1842 "selector": selector,
1843 "strict": true,
1844 "timeout": crate::DEFAULT_TIMEOUT_MS }),
1846 )
1847 .await?;
1848
1849 Ok(response.value)
1850 }
1851
1852 pub(crate) async fn locator_select_option(
1853 &self,
1854 selector: &str,
1855 value: crate::protocol::SelectOption,
1856 options: Option<crate::protocol::SelectOptions>,
1857 ) -> Result<Vec<String>> {
1858 #[derive(Deserialize)]
1859 struct SelectOptionResponse {
1860 values: Vec<String>,
1861 }
1862
1863 let mut params = serde_json::json!({
1864 "selector": selector,
1865 "strict": true,
1866 "options": [value.to_json()]
1867 });
1868
1869 if let Some(opts) = options {
1870 let opts_json = opts.to_json();
1871 if let Some(obj) = params.as_object_mut()
1872 && let Some(opts_obj) = opts_json.as_object()
1873 {
1874 obj.extend(opts_obj.clone());
1875 }
1876 } else {
1877 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1879 }
1880
1881 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1882
1883 Ok(response.values)
1884 }
1885
1886 pub(crate) async fn locator_select_option_multiple(
1887 &self,
1888 selector: &str,
1889 values: Vec<crate::protocol::SelectOption>,
1890 options: Option<crate::protocol::SelectOptions>,
1891 ) -> Result<Vec<String>> {
1892 #[derive(Deserialize)]
1893 struct SelectOptionResponse {
1894 values: Vec<String>,
1895 }
1896
1897 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
1898
1899 let mut params = serde_json::json!({
1900 "selector": selector,
1901 "strict": true,
1902 "options": values_array
1903 });
1904
1905 if let Some(opts) = options {
1906 let opts_json = opts.to_json();
1907 if let Some(obj) = params.as_object_mut()
1908 && let Some(opts_obj) = opts_json.as_object()
1909 {
1910 obj.extend(opts_obj.clone());
1911 }
1912 } else {
1913 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1915 }
1916
1917 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1918
1919 Ok(response.values)
1920 }
1921
1922 pub(crate) async fn locator_set_input_files(
1923 &self,
1924 selector: &str,
1925 file: &std::path::PathBuf,
1926 ) -> Result<()> {
1927 use base64::{Engine as _, engine::general_purpose};
1928 use std::io::Read;
1929
1930 let mut file_handle = std::fs::File::open(file)?;
1932 let mut buffer = Vec::new();
1933 file_handle.read_to_end(&mut buffer)?;
1934
1935 let base64_content = general_purpose::STANDARD.encode(&buffer);
1937
1938 let file_name = file
1940 .file_name()
1941 .and_then(|n| n.to_str())
1942 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
1943
1944 self.channel()
1945 .send_no_result(
1946 "setInputFiles",
1947 serde_json::json!({
1948 "selector": selector,
1949 "strict": true,
1950 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
1952 "name": file_name,
1953 "buffer": base64_content
1954 }]
1955 }),
1956 )
1957 .await
1958 }
1959
1960 pub(crate) async fn locator_set_input_files_multiple(
1961 &self,
1962 selector: &str,
1963 files: &[&std::path::PathBuf],
1964 ) -> Result<()> {
1965 use base64::{Engine as _, engine::general_purpose};
1966 use std::io::Read;
1967
1968 if files.is_empty() {
1970 return self
1971 .channel()
1972 .send_no_result(
1973 "setInputFiles",
1974 serde_json::json!({
1975 "selector": selector,
1976 "strict": true,
1977 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
1979 }),
1980 )
1981 .await;
1982 }
1983
1984 let mut file_objects = Vec::new();
1986 for file_path in files {
1987 let mut file_handle = std::fs::File::open(file_path)?;
1988 let mut buffer = Vec::new();
1989 file_handle.read_to_end(&mut buffer)?;
1990
1991 let base64_content = general_purpose::STANDARD.encode(&buffer);
1992 let file_name = file_path
1993 .file_name()
1994 .and_then(|n| n.to_str())
1995 .ok_or_else(|| {
1996 crate::error::Error::InvalidArgument("Invalid file path".to_string())
1997 })?;
1998
1999 file_objects.push(serde_json::json!({
2000 "name": file_name,
2001 "buffer": base64_content
2002 }));
2003 }
2004
2005 self.channel()
2006 .send_no_result(
2007 "setInputFiles",
2008 serde_json::json!({
2009 "selector": selector,
2010 "strict": true,
2011 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": file_objects
2013 }),
2014 )
2015 .await
2016 }
2017
2018 pub(crate) async fn locator_set_input_files_payload(
2019 &self,
2020 selector: &str,
2021 file: crate::protocol::FilePayload,
2022 ) -> Result<()> {
2023 use base64::{Engine as _, engine::general_purpose};
2024
2025 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
2027
2028 self.channel()
2029 .send_no_result(
2030 "setInputFiles",
2031 serde_json::json!({
2032 "selector": selector,
2033 "strict": true,
2034 "timeout": crate::DEFAULT_TIMEOUT_MS,
2035 "payloads": [{
2036 "name": file.name,
2037 "mimeType": file.mime_type,
2038 "buffer": base64_content
2039 }]
2040 }),
2041 )
2042 .await
2043 }
2044
2045 pub(crate) async fn locator_set_input_files_payload_multiple(
2046 &self,
2047 selector: &str,
2048 files: &[crate::protocol::FilePayload],
2049 ) -> Result<()> {
2050 use base64::{Engine as _, engine::general_purpose};
2051
2052 if files.is_empty() {
2054 return self
2055 .channel()
2056 .send_no_result(
2057 "setInputFiles",
2058 serde_json::json!({
2059 "selector": selector,
2060 "strict": true,
2061 "timeout": crate::DEFAULT_TIMEOUT_MS,
2062 "payloads": []
2063 }),
2064 )
2065 .await;
2066 }
2067
2068 let file_objects: Vec<_> = files
2070 .iter()
2071 .map(|file| {
2072 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
2073 serde_json::json!({
2074 "name": file.name,
2075 "mimeType": file.mime_type,
2076 "buffer": base64_content
2077 })
2078 })
2079 .collect();
2080
2081 self.channel()
2082 .send_no_result(
2083 "setInputFiles",
2084 serde_json::json!({
2085 "selector": selector,
2086 "strict": true,
2087 "timeout": crate::DEFAULT_TIMEOUT_MS,
2088 "payloads": file_objects
2089 }),
2090 )
2091 .await
2092 }
2093
2094 pub(crate) async fn locator_aria_snapshot(
2101 &self,
2102 selector: &str,
2103 options: Option<&crate::protocol::AriaSnapshotOptions>,
2104 ) -> Result<String> {
2105 let timeout = options
2106 .and_then(|o| o.timeout)
2107 .unwrap_or(crate::DEFAULT_TIMEOUT_MS);
2108 self.aria_snapshot_raw(selector, timeout, options).await
2109 }
2110
2111 pub(crate) async fn aria_snapshot_raw(
2112 &self,
2113 selector: &str,
2114 timeout: f64,
2115 options: Option<&crate::protocol::AriaSnapshotOptions>,
2116 ) -> Result<String> {
2117 #[derive(Deserialize)]
2118 struct AriaSnapshotResponse {
2119 snapshot: String,
2120 }
2121
2122 let mut params = serde_json::json!({
2123 "selector": selector,
2124 "timeout": timeout,
2125 });
2126 if let Some(opts) = options {
2127 if let Some(mode) = opts.mode {
2128 params["mode"] = serde_json::Value::String(mode.as_str().to_string());
2129 }
2130 if let Some(depth) = opts.depth {
2131 params["depth"] = serde_json::Value::from(depth);
2132 }
2133 if let Some(boxes) = opts.boxes {
2134 params["boxes"] = serde_json::Value::Bool(boxes);
2135 }
2136 }
2137
2138 let response: AriaSnapshotResponse = self.channel().send("ariaSnapshot", params).await?;
2139 Ok(response.snapshot)
2140 }
2141
2142 pub(crate) async fn locator_aria_snapshot_json(
2143 &self,
2144 selector: &str,
2145 options: Option<&crate::protocol::AriaSnapshotOptions>,
2146 ) -> Result<serde_json::Value> {
2147 let timeout = options
2148 .and_then(|o| o.timeout)
2149 .unwrap_or(crate::DEFAULT_TIMEOUT_MS);
2150 self.aria_snapshot_json_raw(selector, timeout, options)
2151 .await
2152 }
2153
2154 pub(crate) async fn aria_snapshot_json_raw(
2155 &self,
2156 selector: &str,
2157 timeout: f64,
2158 options: Option<&crate::protocol::AriaSnapshotOptions>,
2159 ) -> Result<serde_json::Value> {
2160 #[derive(Deserialize)]
2161 struct AriaSnapshotJsonResponse {
2162 snapshot: serde_json::Value,
2163 }
2164
2165 let mut params = serde_json::json!({
2166 "selector": selector,
2167 "timeout": timeout,
2168 });
2169 if let Some(opts) = options {
2170 if let Some(mode) = opts.mode {
2171 params["mode"] = serde_json::Value::String(mode.as_str().to_string());
2172 }
2173 if let Some(depth) = opts.depth {
2174 params["depth"] = serde_json::Value::from(depth);
2175 }
2176 if let Some(boxes) = opts.boxes {
2177 params["boxes"] = serde_json::Value::Bool(boxes);
2178 }
2179 }
2180
2181 let response: AriaSnapshotJsonResponse =
2182 self.channel().send("ariaSnapshotJSON", params).await?;
2183 Ok(response.snapshot)
2184 }
2185
2186 pub(crate) async fn frame_resolve_selector(&self, selector: &str) -> Result<String> {
2192 #[derive(Deserialize)]
2193 struct ResolveSelectorResponse {
2194 #[serde(rename = "resolvedSelector")]
2195 resolved_selector: String,
2196 }
2197
2198 let response: ResolveSelectorResponse = self
2199 .channel()
2200 .send(
2201 "resolveSelector",
2202 serde_json::json!({
2203 "selector": selector,
2204 }),
2205 )
2206 .await?;
2207
2208 Ok(response.resolved_selector)
2209 }
2210
2211 pub(crate) async fn locator_highlight(
2218 &self,
2219 selector: &str,
2220 style: Option<&str>,
2221 ) -> Result<()> {
2222 let mut params = serde_json::json!({ "selector": selector });
2223 if let Some(style) = style {
2224 params["style"] = serde_json::Value::String(style.to_string());
2225 }
2226 self.channel().send_no_result("highlight", params).await
2227 }
2228
2229 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
2233 let params = serde_json::json!({
2234 "expression": expression,
2235 "arg": {
2236 "value": {"v": "null"},
2237 "handles": []
2238 }
2239 });
2240
2241 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
2242 Ok(())
2243 }
2244
2245 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
2257 let params = serde_json::json!({
2258 "expression": expression,
2259 "arg": {
2260 "value": {"v": "null"},
2261 "handles": []
2262 }
2263 });
2264
2265 #[derive(Deserialize)]
2266 struct EvaluateResult {
2267 value: serde_json::Value,
2268 }
2269
2270 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2271
2272 match &result.value {
2279 Value::Object(map) => {
2280 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
2281 Ok(s.to_string())
2283 } else if let Some(n) = map.get("n") {
2284 Ok(n.to_string())
2286 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
2287 Ok(b.to_string())
2289 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
2290 Ok(v.to_string())
2292 } else {
2293 Ok(result.value.to_string())
2295 }
2296 }
2297 _ => {
2298 Ok(result.value.to_string())
2300 }
2301 }
2302 }
2303
2304 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid()))]
2344 pub async fn evaluate<T: serde::Serialize>(
2345 &self,
2346 expression: &str,
2347 arg: Option<&T>,
2348 ) -> Result<Value> {
2349 let serialized_arg = match arg {
2351 Some(a) => serialize_argument(a),
2352 None => serialize_null(),
2353 };
2354
2355 let params = serde_json::json!({
2357 "expression": expression,
2358 "arg": serialized_arg
2359 });
2360
2361 #[derive(Deserialize)]
2363 struct EvaluateResult {
2364 value: serde_json::Value,
2365 }
2366
2367 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2368
2369 Ok(parse_result(&result.value))
2371 }
2372
2373 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2413 pub async fn add_style_tag(
2414 &self,
2415 options: crate::protocol::page::AddStyleTagOptions,
2416 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2417 options.validate()?;
2419
2420 let mut params = serde_json::json!({});
2422
2423 if let Some(content) = &options.content {
2424 params["content"] = serde_json::json!(content);
2425 }
2426
2427 if let Some(url) = &options.url {
2428 params["url"] = serde_json::json!(url);
2429 }
2430
2431 if let Some(path) = &options.path {
2432 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2434 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
2435 })?;
2436 params["content"] = serde_json::json!(css_content);
2437 }
2438
2439 #[derive(Deserialize)]
2440 struct AddStyleTagResponse {
2441 element: serde_json::Value,
2442 }
2443
2444 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
2445
2446 let guid = response.element["guid"].as_str().ok_or_else(|| {
2447 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
2448 })?;
2449
2450 let connection = self.base.connection();
2451 let handle: crate::protocol::ElementHandle = connection
2452 .get_typed::<crate::protocol::ElementHandle>(guid)
2453 .await?;
2454
2455 Ok(Arc::new(handle))
2456 }
2457
2458 pub(crate) async fn locator_dispatch_event(
2466 &self,
2467 selector: &str,
2468 type_: &str,
2469 event_init: Option<serde_json::Value>,
2470 ) -> Result<()> {
2471 let event_init_serialized = match event_init {
2474 Some(v) => serialize_argument(&v),
2475 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
2476 };
2477
2478 let params = serde_json::json!({
2479 "selector": selector,
2480 "type": type_,
2481 "eventInit": event_init_serialized,
2482 "strict": true,
2483 "timeout": crate::DEFAULT_TIMEOUT_MS
2484 });
2485
2486 self.channel().send_no_result("dispatchEvent", params).await
2487 }
2488
2489 pub(crate) async fn locator_bounding_box(
2499 &self,
2500 selector: &str,
2501 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
2502 let element = self.query_selector(selector).await?;
2503 match element {
2504 Some(handle) => handle.bounding_box().await,
2505 None => Ok(None),
2506 }
2507 }
2508
2509 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
2516 let element = self.query_selector(selector).await?;
2517 match element {
2518 Some(handle) => handle.scroll_into_view_if_needed().await,
2519 None => Err(crate::error::Error::ElementNotFound(format!(
2520 "Element not found: {}",
2521 selector
2522 ))),
2523 }
2524 }
2525
2526 pub(crate) async fn frame_expect(
2532 &self,
2533 selector: &str,
2534 expression: &str,
2535 expected_value: serde_json::Value,
2536 is_not: bool,
2537 timeout_ms: f64,
2538 ) -> Result<()> {
2539 let params = serde_json::json!({
2540 "selector": selector,
2541 "expression": expression,
2542 "expectedValue": expected_value,
2543 "isNot": is_not,
2544 "timeout": timeout_ms
2545 });
2546
2547 let result: serde_json::Value = self.channel().send("expect", params).await?;
2555
2556 if crate::server::error_parsing::legacy_expect_verdict(&result, is_not) == Some(false) {
2564 return Err(crate::error::Error::AssertionFailed(format!(
2565 "Assertion failed for selector '{selector}' ({expression}). \
2566 Reported by a pre-1.61 Playwright server, which does not send \
2567 assertion details; connect to a version-matched server for a \
2568 fuller diagnostic."
2569 )));
2570 }
2571 Ok(())
2572 }
2573
2574 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2584 pub async fn add_script_tag(
2585 &self,
2586 options: crate::protocol::page::AddScriptTagOptions,
2587 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2588 options.validate()?;
2590
2591 let mut params = serde_json::json!({});
2593
2594 if let Some(content) = &options.content {
2595 params["content"] = serde_json::json!(content);
2596 }
2597
2598 if let Some(url) = &options.url {
2599 params["url"] = serde_json::json!(url);
2600 }
2601
2602 if let Some(path) = &options.path {
2603 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2605 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
2606 })?;
2607 params["content"] = serde_json::json!(js_content);
2608 }
2609
2610 if let Some(type_) = &options.type_ {
2611 params["type"] = serde_json::json!(type_);
2612 }
2613
2614 #[derive(Deserialize)]
2615 struct AddScriptTagResponse {
2616 element: serde_json::Value,
2617 }
2618
2619 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
2620
2621 let guid = response.element["guid"].as_str().ok_or_else(|| {
2622 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
2623 })?;
2624
2625 let connection = self.base.connection();
2626 let handle: crate::protocol::ElementHandle = connection
2627 .get_typed::<crate::protocol::ElementHandle>(guid)
2628 .await?;
2629
2630 Ok(Arc::new(handle))
2631 }
2632}
2633
2634impl ChannelOwner for Frame {
2635 fn guid(&self) -> &str {
2636 self.base.guid()
2637 }
2638
2639 fn type_name(&self) -> &str {
2640 self.base.type_name()
2641 }
2642
2643 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
2644 self.base.parent()
2645 }
2646
2647 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
2648 self.base.connection()
2649 }
2650
2651 fn initializer(&self) -> &Value {
2652 self.base.initializer()
2653 }
2654
2655 fn channel(&self) -> &Channel {
2656 self.base.channel()
2657 }
2658
2659 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
2660 if let Ok(mut guard) = self.page.lock() {
2664 *guard = None;
2665 }
2666 self.base.dispose(reason)
2667 }
2668
2669 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
2670 self.base.adopt(child)
2671 }
2672
2673 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
2674 self.base.add_child(guid, child)
2675 }
2676
2677 fn remove_child(&self, guid: &str) {
2678 self.base.remove_child(guid)
2679 }
2680
2681 fn on_event(&self, method: &str, params: Value) {
2682 match method {
2683 "navigated" => {
2684 if let Some(url_value) = params.get("url")
2686 && let Some(url_str) = url_value.as_str()
2687 {
2688 if let Ok(mut url) = self.url.write() {
2690 *url = url_str.to_string();
2691 }
2692 }
2693 let self_clone = self.clone();
2695 tokio::spawn(async move {
2696 if let Some(page) = self_clone.page() {
2697 page.trigger_framenavigated_event(self_clone).await;
2698 }
2699 });
2700 }
2701 "loadstate" => {
2702 if let Some(add) = params.get("add").and_then(|v| v.as_str())
2707 && add == "load"
2708 && self.parent_frame_guid.is_none()
2709 {
2710 let self_clone = self.clone();
2711 tokio::spawn(async move {
2712 if let Some(page) = self_clone.page() {
2713 page.trigger_load_event().await;
2714 }
2715 });
2716 }
2717 }
2718 "detached" => {
2719 if let Ok(mut flag) = self.is_detached.write() {
2721 *flag = true;
2722 }
2723 }
2724 _ => {
2725 }
2727 }
2728 }
2729
2730 fn was_collected(&self) -> bool {
2731 self.base.was_collected()
2732 }
2733
2734 fn as_any(&self) -> &dyn Any {
2735 self
2736 }
2737}
2738
2739impl std::fmt::Debug for Frame {
2740 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2741 f.debug_struct("Frame").field("guid", &self.guid()).finish()
2742 }
2743}