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> {
116 self.page.lock().ok().and_then(|g| g.clone())
117 }
118
119 pub fn name(&self) -> &str {
125 &self.name
126 }
127
128 pub fn parent_frame(&self) -> Option<crate::protocol::Frame> {
132 let guid = self.parent_frame_guid.as_ref()?;
133 let conn = self.base.connection();
136 tokio::task::block_in_place(|| {
139 tokio::runtime::Handle::current()
140 .block_on(conn.get_typed::<crate::protocol::Frame>(guid))
141 .ok()
142 })
143 }
144
145 pub fn is_detached(&self) -> bool {
152 self.is_detached.read().map(|v| *v).unwrap_or(false)
153 }
154
155 pub fn child_frames(&self) -> Vec<crate::protocol::Frame> {
168 let my_guid = self.guid().to_string();
169 let conn = self.base.connection();
170
171 conn.all_objects_sync()
174 .into_iter()
175 .filter_map(|obj| {
176 if obj.type_name() != "Frame" {
178 return None;
179 }
180 let parent_guid = obj
182 .initializer()
183 .get("parentFrame")
184 .and_then(|v| v.get("guid"))
185 .and_then(|v| v.as_str())?;
186
187 if parent_guid == my_guid {
188 obj.as_any()
189 .downcast_ref::<crate::protocol::Frame>()
190 .cloned()
191 } else {
192 None
193 }
194 })
195 .collect()
196 }
197
198 pub async fn evaluate_handle(
239 &self,
240 expression: &str,
241 ) -> Result<Arc<crate::protocol::ElementHandle>> {
242 let params = serde_json::json!({
243 "expression": expression,
244 "isFunction": false,
245 "arg": {"value": {"v": "undefined"}, "handles": []}
246 });
247
248 #[derive(Deserialize)]
250 struct HandleRef {
251 guid: String,
252 }
253 #[derive(Deserialize)]
254 struct EvaluateHandleResponse {
255 handle: HandleRef,
256 }
257
258 let response: EvaluateHandleResponse = self
259 .channel()
260 .send("evaluateExpressionHandle", params)
261 .await?;
262
263 let guid = &response.handle.guid;
264
265 let connection = self.base.connection();
267 let mut attempts = 0;
268 let max_attempts = 20;
269 let handle = loop {
270 match connection
271 .get_typed::<crate::protocol::ElementHandle>(guid)
272 .await
273 {
274 Ok(h) => break h,
275 Err(_) if attempts < max_attempts => {
276 attempts += 1;
277 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
278 }
279 Err(e) => return Err(e),
280 }
281 };
282
283 Ok(Arc::new(handle))
284 }
285
286 pub async fn evaluate_handle_js(
309 &self,
310 expression: &str,
311 ) -> Result<std::sync::Arc<crate::protocol::JSHandle>> {
312 let trimmed = expression.trim();
316 let is_function = trimmed.starts_with("(")
317 || trimmed.starts_with("function")
318 || trimmed.starts_with("async ");
319
320 let params = serde_json::json!({
321 "expression": expression,
322 "isFunction": is_function,
323 "arg": {"value": {"v": "undefined"}, "handles": []}
324 });
325
326 #[derive(Deserialize)]
328 struct HandleRef {
329 guid: String,
330 }
331 #[derive(Deserialize)]
332 struct EvaluateHandleResponse {
333 handle: HandleRef,
334 }
335
336 let response: EvaluateHandleResponse = self
337 .channel()
338 .send("evaluateExpressionHandle", params)
339 .await?;
340
341 let guid = &response.handle.guid;
342
343 let connection = self.base.connection();
345 let mut attempts = 0;
346 let max_attempts = 20;
347 let handle = loop {
348 match connection
349 .get_typed::<crate::protocol::JSHandle>(guid)
350 .await
351 {
352 Ok(h) => break h,
353 Err(_) if attempts < max_attempts => {
354 attempts += 1;
355 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
356 }
357 Err(e) => return Err(e),
358 }
359 };
360
361 Ok(std::sync::Arc::new(handle))
362 }
363
364 pub fn locator(&self, selector: &str) -> crate::protocol::Locator {
379 let page = self
380 .page()
381 .expect("Frame::locator() called before set_page(); call page.main_frame() first");
382 crate::protocol::Locator::new(Arc::new(self.clone()), selector.to_string(), page)
383 }
384
385 pub fn get_by_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
389 self.locator(&crate::protocol::locator::get_by_text_selector(text, exact))
390 }
391
392 pub fn get_by_label(&self, text: &str, exact: bool) -> crate::protocol::Locator {
396 self.locator(&crate::protocol::locator::get_by_label_selector(
397 text, exact,
398 ))
399 }
400
401 pub fn get_by_placeholder(&self, text: &str, exact: bool) -> crate::protocol::Locator {
405 self.locator(&crate::protocol::locator::get_by_placeholder_selector(
406 text, exact,
407 ))
408 }
409
410 pub fn get_by_alt_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
414 self.locator(&crate::protocol::locator::get_by_alt_text_selector(
415 text, exact,
416 ))
417 }
418
419 pub fn get_by_title(&self, text: &str, exact: bool) -> crate::protocol::Locator {
423 self.locator(&crate::protocol::locator::get_by_title_selector(
424 text, exact,
425 ))
426 }
427
428 pub fn get_by_test_id(&self, test_id: &str) -> crate::protocol::Locator {
435 use crate::server::channel_owner::ChannelOwner;
436 let attr = self.connection().selectors().test_id_attribute();
437 self.locator(&crate::protocol::locator::get_by_test_id_selector_with_attr(test_id, &attr))
438 }
439
440 pub fn get_by_role(
444 &self,
445 role: crate::protocol::locator::AriaRole,
446 options: Option<crate::protocol::locator::GetByRoleOptions>,
447 ) -> crate::protocol::Locator {
448 self.locator(&crate::protocol::locator::get_by_role_selector(
449 role, options,
450 ))
451 }
452
453 fn channel(&self) -> &Channel {
455 self.base.channel()
456 }
457
458 pub fn url(&self) -> String {
464 self.url.read().unwrap().clone()
465 }
466
467 pub async fn goto(&self, url: &str, options: Option<GotoOptions>) -> Result<Option<Response>> {
481 let mut params = serde_json::json!({
483 "url": url,
484 });
485
486 if let Some(opts) = options {
488 if let Some(timeout) = opts.timeout {
489 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
490 } else {
491 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
493 }
494 if let Some(wait_until) = opts.wait_until {
495 params["waitUntil"] = serde_json::json!(wait_until.as_str());
496 }
497 } else {
498 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
500 }
501
502 #[derive(Deserialize)]
505 struct GotoResponse {
506 response: Option<ResponseReference>,
507 }
508
509 #[derive(Deserialize)]
510 struct ResponseReference {
511 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
512 guid: Arc<str>,
513 }
514
515 let goto_result: GotoResponse = self.channel().send("goto", params).await?;
516
517 if let Some(response_ref) = goto_result.response {
519 let response_arc = {
525 let mut attempts = 0;
526 let max_attempts = 20; loop {
528 match self.connection().get_object(&response_ref.guid).await {
529 Ok(obj) => break obj,
530 Err(_) if attempts < max_attempts => {
531 attempts += 1;
532 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
533 }
534 Err(e) => return Err(e),
535 }
536 }
537 };
538
539 let initializer = response_arc.initializer();
542
543 let status = initializer["status"].as_u64().ok_or_else(|| {
545 crate::error::Error::ProtocolError("Response missing status".to_string())
546 })? as u16;
547
548 let headers = initializer["headers"]
550 .as_array()
551 .ok_or_else(|| {
552 crate::error::Error::ProtocolError("Response missing headers".to_string())
553 })?
554 .iter()
555 .filter_map(|h| {
556 let name = h["name"].as_str()?;
557 let value = h["value"].as_str()?;
558 Some((name.to_string(), value.to_string()))
559 })
560 .collect();
561
562 Ok(Some(Response::new(
563 initializer["url"]
564 .as_str()
565 .ok_or_else(|| {
566 crate::error::Error::ProtocolError("Response missing url".to_string())
567 })?
568 .to_string(),
569 status,
570 initializer["statusText"].as_str().unwrap_or("").to_string(),
571 headers,
572 Some(response_arc),
573 )))
574 } else {
575 Ok(None)
578 }
579 }
580
581 pub async fn title(&self) -> Result<String> {
585 #[derive(Deserialize)]
586 struct TitleResponse {
587 value: String,
588 }
589
590 let response: TitleResponse = self.channel().send("title", serde_json::json!({})).await?;
591 Ok(response.value)
592 }
593
594 pub async fn content(&self) -> Result<String> {
598 #[derive(Deserialize)]
599 struct ContentResponse {
600 value: String,
601 }
602
603 let response: ContentResponse = self
604 .channel()
605 .send("content", serde_json::json!({}))
606 .await?;
607 Ok(response.value)
608 }
609
610 pub async fn set_content(&self, html: &str, options: Option<GotoOptions>) -> Result<()> {
614 let mut params = serde_json::json!({
615 "html": html,
616 });
617
618 if let Some(opts) = options {
619 if let Some(timeout) = opts.timeout {
620 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
621 } else {
622 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
623 }
624 if let Some(wait_until) = opts.wait_until {
625 params["waitUntil"] = serde_json::json!(wait_until.as_str());
626 }
627 } else {
628 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
629 }
630
631 self.channel().send_no_result("setContent", params).await
632 }
633
634 pub async fn wait_for_load_state(&self, state: Option<WaitUntil>) -> Result<()> {
642 let target_state = state.unwrap_or(WaitUntil::Load);
643
644 let js_check = match target_state {
645 WaitUntil::Load => "document.readyState === 'complete'",
647 WaitUntil::DomContentLoaded => "document.readyState !== 'loading'",
649 WaitUntil::NetworkIdle => "document.readyState === 'complete'",
652 WaitUntil::Commit => "document.readyState !== 'loading'",
654 };
655
656 let timeout_ms = crate::DEFAULT_TIMEOUT_MS as u64;
657 let poll_interval = std::time::Duration::from_millis(50);
658 let start = std::time::Instant::now();
659
660 loop {
661 #[derive(Deserialize)]
662 struct EvalResponse {
663 value: serde_json::Value,
664 }
665
666 let result: EvalResponse = self
667 .channel()
668 .send(
669 "evaluateExpression",
670 serde_json::json!({
671 "expression": js_check,
672 "isFunction": false,
673 "arg": crate::protocol::serialize_null(),
674 }),
675 )
676 .await?;
677
678 let is_ready = result
680 .value
681 .as_object()
682 .and_then(|m| m.get("b"))
683 .and_then(|v| v.as_bool())
684 .unwrap_or(false);
685
686 if is_ready {
687 return Ok(());
688 }
689
690 if start.elapsed().as_millis() as u64 >= timeout_ms {
691 return Err(crate::error::Error::Timeout(format!(
692 "wait_for_load_state({}) timed out after {}ms",
693 target_state.as_str(),
694 timeout_ms
695 )));
696 }
697
698 tokio::time::sleep(poll_interval).await;
699 }
700 }
701
702 pub async fn wait_for_url(&self, url: &str, options: Option<GotoOptions>) -> Result<()> {
709 let timeout_ms = options
710 .as_ref()
711 .and_then(|o| o.timeout)
712 .map(|d| d.as_millis() as u64)
713 .unwrap_or(crate::DEFAULT_TIMEOUT_MS as u64);
714
715 let is_glob = url.contains('*');
719
720 let poll_interval = std::time::Duration::from_millis(50);
721 let start = std::time::Instant::now();
722
723 loop {
724 let current_url = self.url();
725
726 let matches = if is_glob {
727 glob_match(url, ¤t_url)
728 } else {
729 current_url == url
730 };
731
732 if matches {
733 if let Some(ref opts) = options
735 && let Some(wait_until) = opts.wait_until
736 {
737 self.wait_for_load_state(Some(wait_until)).await?;
738 }
739 return Ok(());
740 }
741
742 if start.elapsed().as_millis() as u64 >= timeout_ms {
743 return Err(crate::error::Error::Timeout(format!(
744 "wait_for_url({}) timed out after {}ms, current URL: {}",
745 url, timeout_ms, current_url
746 )));
747 }
748
749 tokio::time::sleep(poll_interval).await;
750 }
751 }
752
753 pub async fn query_selector(
757 &self,
758 selector: &str,
759 ) -> Result<Option<Arc<crate::protocol::ElementHandle>>> {
760 let response: serde_json::Value = self
761 .channel()
762 .send(
763 "querySelector",
764 serde_json::json!({
765 "selector": selector
766 }),
767 )
768 .await?;
769
770 if response.as_object().map(|o| o.is_empty()).unwrap_or(true) {
772 return Ok(None);
773 }
774
775 let element_value = if let Some(elem) = response.get("element") {
777 elem
778 } else if let Some(elem) = response.get("handle") {
779 elem
780 } else {
781 &response
783 };
784
785 if element_value.is_null() {
786 return Ok(None);
787 }
788
789 let guid = element_value["guid"].as_str().ok_or_else(|| {
791 crate::error::Error::ProtocolError("Element GUID missing".to_string())
792 })?;
793
794 let connection = self.base.connection();
796 let handle: crate::protocol::ElementHandle = connection
797 .get_typed::<crate::protocol::ElementHandle>(guid)
798 .await?;
799
800 Ok(Some(Arc::new(handle)))
801 }
802
803 pub async fn query_selector_all(
807 &self,
808 selector: &str,
809 ) -> Result<Vec<Arc<crate::protocol::ElementHandle>>> {
810 #[derive(Deserialize)]
811 struct QueryAllResponse {
812 elements: Vec<serde_json::Value>,
813 }
814
815 let response: QueryAllResponse = self
816 .channel()
817 .send(
818 "querySelectorAll",
819 serde_json::json!({
820 "selector": selector
821 }),
822 )
823 .await?;
824
825 let connection = self.base.connection();
827 let mut handles = Vec::new();
828
829 for element_value in response.elements {
830 let guid = element_value["guid"].as_str().ok_or_else(|| {
831 crate::error::Error::ProtocolError("Element GUID missing".to_string())
832 })?;
833
834 let handle: crate::protocol::ElementHandle = connection
835 .get_typed::<crate::protocol::ElementHandle>(guid)
836 .await?;
837
838 handles.push(Arc::new(handle));
839 }
840
841 Ok(handles)
842 }
843
844 pub(crate) async fn locator_count(&self, selector: &str) -> Result<usize> {
849 #[derive(Deserialize)]
851 struct QueryAllResponse {
852 elements: Vec<serde_json::Value>,
853 }
854
855 let response: QueryAllResponse = self
856 .channel()
857 .send(
858 "querySelectorAll",
859 serde_json::json!({
860 "selector": selector
861 }),
862 )
863 .await?;
864
865 Ok(response.elements.len())
866 }
867
868 pub(crate) async fn locator_text_content(&self, selector: &str) -> Result<Option<String>> {
870 #[derive(Deserialize)]
871 struct TextContentResponse {
872 value: Option<String>,
873 }
874
875 let response: TextContentResponse = self
876 .channel()
877 .send(
878 "textContent",
879 serde_json::json!({
880 "selector": selector,
881 "strict": true,
882 "timeout": crate::DEFAULT_TIMEOUT_MS
883 }),
884 )
885 .await?;
886
887 Ok(response.value)
888 }
889
890 pub(crate) async fn locator_inner_text(&self, selector: &str) -> Result<String> {
892 #[derive(Deserialize)]
893 struct InnerTextResponse {
894 value: String,
895 }
896
897 let response: InnerTextResponse = self
898 .channel()
899 .send(
900 "innerText",
901 serde_json::json!({
902 "selector": selector,
903 "strict": true,
904 "timeout": crate::DEFAULT_TIMEOUT_MS
905 }),
906 )
907 .await?;
908
909 Ok(response.value)
910 }
911
912 pub(crate) async fn locator_inner_html(&self, selector: &str) -> Result<String> {
914 #[derive(Deserialize)]
915 struct InnerHTMLResponse {
916 value: String,
917 }
918
919 let response: InnerHTMLResponse = self
920 .channel()
921 .send(
922 "innerHTML",
923 serde_json::json!({
924 "selector": selector,
925 "strict": true,
926 "timeout": crate::DEFAULT_TIMEOUT_MS
927 }),
928 )
929 .await?;
930
931 Ok(response.value)
932 }
933
934 pub(crate) async fn locator_get_attribute(
936 &self,
937 selector: &str,
938 name: &str,
939 ) -> Result<Option<String>> {
940 #[derive(Deserialize)]
941 struct GetAttributeResponse {
942 value: Option<String>,
943 }
944
945 let response: GetAttributeResponse = self
946 .channel()
947 .send(
948 "getAttribute",
949 serde_json::json!({
950 "selector": selector,
951 "name": name,
952 "strict": true,
953 "timeout": crate::DEFAULT_TIMEOUT_MS
954 }),
955 )
956 .await?;
957
958 Ok(response.value)
959 }
960
961 pub(crate) async fn locator_is_visible(&self, selector: &str) -> Result<bool> {
963 #[derive(Deserialize)]
964 struct IsVisibleResponse {
965 value: bool,
966 }
967
968 let response: IsVisibleResponse = self
969 .channel()
970 .send(
971 "isVisible",
972 serde_json::json!({
973 "selector": selector,
974 "strict": true,
975 "timeout": crate::DEFAULT_TIMEOUT_MS
976 }),
977 )
978 .await?;
979
980 Ok(response.value)
981 }
982
983 pub(crate) async fn locator_is_enabled(&self, selector: &str) -> Result<bool> {
985 #[derive(Deserialize)]
986 struct IsEnabledResponse {
987 value: bool,
988 }
989
990 let response: IsEnabledResponse = self
991 .channel()
992 .send(
993 "isEnabled",
994 serde_json::json!({
995 "selector": selector,
996 "strict": true,
997 "timeout": crate::DEFAULT_TIMEOUT_MS
998 }),
999 )
1000 .await?;
1001
1002 Ok(response.value)
1003 }
1004
1005 pub(crate) async fn locator_is_checked(&self, selector: &str) -> Result<bool> {
1007 #[derive(Deserialize)]
1008 struct IsCheckedResponse {
1009 value: bool,
1010 }
1011
1012 let response: IsCheckedResponse = self
1013 .channel()
1014 .send(
1015 "isChecked",
1016 serde_json::json!({
1017 "selector": selector,
1018 "strict": true,
1019 "timeout": crate::DEFAULT_TIMEOUT_MS
1020 }),
1021 )
1022 .await?;
1023
1024 Ok(response.value)
1025 }
1026
1027 pub(crate) async fn locator_is_editable(&self, selector: &str) -> Result<bool> {
1029 #[derive(Deserialize)]
1030 struct IsEditableResponse {
1031 value: bool,
1032 }
1033
1034 let response: IsEditableResponse = self
1035 .channel()
1036 .send(
1037 "isEditable",
1038 serde_json::json!({
1039 "selector": selector,
1040 "strict": true,
1041 "timeout": crate::DEFAULT_TIMEOUT_MS
1042 }),
1043 )
1044 .await?;
1045
1046 Ok(response.value)
1047 }
1048
1049 pub(crate) async fn locator_is_hidden(&self, selector: &str) -> Result<bool> {
1051 #[derive(Deserialize)]
1052 struct IsHiddenResponse {
1053 value: bool,
1054 }
1055
1056 let response: IsHiddenResponse = self
1057 .channel()
1058 .send(
1059 "isHidden",
1060 serde_json::json!({
1061 "selector": selector,
1062 "strict": true,
1063 "timeout": crate::DEFAULT_TIMEOUT_MS
1064 }),
1065 )
1066 .await?;
1067
1068 Ok(response.value)
1069 }
1070
1071 pub(crate) async fn locator_is_disabled(&self, selector: &str) -> Result<bool> {
1073 #[derive(Deserialize)]
1074 struct IsDisabledResponse {
1075 value: bool,
1076 }
1077
1078 let response: IsDisabledResponse = self
1079 .channel()
1080 .send(
1081 "isDisabled",
1082 serde_json::json!({
1083 "selector": selector,
1084 "strict": true,
1085 "timeout": crate::DEFAULT_TIMEOUT_MS
1086 }),
1087 )
1088 .await?;
1089
1090 Ok(response.value)
1091 }
1092
1093 pub(crate) async fn locator_is_focused(&self, selector: &str) -> Result<bool> {
1099 #[derive(Deserialize)]
1100 struct EvaluateResult {
1101 value: serde_json::Value,
1102 }
1103
1104 let script = r#"selector => {
1107 const elements = document.querySelectorAll(selector);
1108 if (elements.length === 0) return false;
1109 const element = elements[0];
1110 return document.activeElement === element;
1111 }"#;
1112
1113 let params = serde_json::json!({
1114 "expression": script,
1115 "arg": {
1116 "value": {"s": selector},
1117 "handles": []
1118 }
1119 });
1120
1121 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1122
1123 if let serde_json::Value::Object(map) = &result.value
1125 && let Some(b) = map.get("b").and_then(|v| v.as_bool())
1126 {
1127 return Ok(b);
1128 }
1129
1130 Ok(result.value.to_string().to_lowercase().contains("true"))
1132 }
1133
1134 pub(crate) async fn locator_click(
1138 &self,
1139 selector: &str,
1140 options: Option<crate::protocol::ClickOptions>,
1141 ) -> Result<()> {
1142 let mut params = serde_json::json!({
1143 "selector": selector,
1144 "strict": true
1145 });
1146
1147 if let Some(opts) = options {
1148 let opts_json = opts.to_json();
1149 if let Some(obj) = params.as_object_mut()
1150 && let Some(opts_obj) = opts_json.as_object()
1151 {
1152 obj.extend(opts_obj.clone());
1153 }
1154 } else {
1155 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1156 }
1157
1158 self.channel()
1159 .send_no_result("click", params)
1160 .await
1161 .map_err(|e| match e {
1162 Error::Timeout(msg) => {
1163 Error::Timeout(format!("{} (selector: '{}')", msg, selector))
1164 }
1165 other => other,
1166 })
1167 }
1168
1169 pub(crate) async fn locator_dblclick(
1171 &self,
1172 selector: &str,
1173 options: Option<crate::protocol::ClickOptions>,
1174 ) -> Result<()> {
1175 let mut params = serde_json::json!({
1176 "selector": selector,
1177 "strict": true
1178 });
1179
1180 if let Some(opts) = options {
1181 let opts_json = opts.to_json();
1182 if let Some(obj) = params.as_object_mut()
1183 && let Some(opts_obj) = opts_json.as_object()
1184 {
1185 obj.extend(opts_obj.clone());
1186 }
1187 } else {
1188 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1189 }
1190
1191 self.channel().send_no_result("dblclick", params).await
1192 }
1193
1194 pub(crate) async fn locator_fill(
1196 &self,
1197 selector: &str,
1198 text: &str,
1199 options: Option<crate::protocol::FillOptions>,
1200 ) -> Result<()> {
1201 let mut params = serde_json::json!({
1202 "selector": selector,
1203 "value": text,
1204 "strict": true
1205 });
1206
1207 if let Some(opts) = options {
1208 let opts_json = opts.to_json();
1209 if let Some(obj) = params.as_object_mut()
1210 && let Some(opts_obj) = opts_json.as_object()
1211 {
1212 obj.extend(opts_obj.clone());
1213 }
1214 } else {
1215 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1216 }
1217
1218 self.channel().send_no_result("fill", params).await
1219 }
1220
1221 pub(crate) async fn locator_clear(
1223 &self,
1224 selector: &str,
1225 options: Option<crate::protocol::FillOptions>,
1226 ) -> Result<()> {
1227 let mut params = serde_json::json!({
1228 "selector": selector,
1229 "value": "",
1230 "strict": true
1231 });
1232
1233 if let Some(opts) = options {
1234 let opts_json = opts.to_json();
1235 if let Some(obj) = params.as_object_mut()
1236 && let Some(opts_obj) = opts_json.as_object()
1237 {
1238 obj.extend(opts_obj.clone());
1239 }
1240 } else {
1241 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1242 }
1243
1244 self.channel().send_no_result("fill", params).await
1245 }
1246
1247 pub(crate) async fn locator_press(
1249 &self,
1250 selector: &str,
1251 key: &str,
1252 options: Option<crate::protocol::PressOptions>,
1253 ) -> Result<()> {
1254 let mut params = serde_json::json!({
1255 "selector": selector,
1256 "key": key,
1257 "strict": true
1258 });
1259
1260 if let Some(opts) = options {
1261 let opts_json = opts.to_json();
1262 if let Some(obj) = params.as_object_mut()
1263 && let Some(opts_obj) = opts_json.as_object()
1264 {
1265 obj.extend(opts_obj.clone());
1266 }
1267 } else {
1268 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1269 }
1270
1271 self.channel().send_no_result("press", params).await
1272 }
1273
1274 pub(crate) async fn locator_focus(&self, selector: &str) -> Result<()> {
1276 self.channel()
1277 .send_no_result(
1278 "focus",
1279 serde_json::json!({
1280 "selector": selector,
1281 "strict": true,
1282 "timeout": crate::DEFAULT_TIMEOUT_MS
1283 }),
1284 )
1285 .await
1286 }
1287
1288 pub(crate) async fn locator_blur(&self, selector: &str) -> Result<()> {
1290 self.channel()
1291 .send_no_result(
1292 "blur",
1293 serde_json::json!({
1294 "selector": selector,
1295 "strict": true,
1296 "timeout": crate::DEFAULT_TIMEOUT_MS
1297 }),
1298 )
1299 .await
1300 }
1301
1302 pub(crate) async fn locator_press_sequentially(
1306 &self,
1307 selector: &str,
1308 text: &str,
1309 options: Option<crate::protocol::PressSequentiallyOptions>,
1310 ) -> Result<()> {
1311 let mut params = serde_json::json!({
1312 "selector": selector,
1313 "text": text,
1314 "strict": true
1315 });
1316
1317 if let Some(opts) = options {
1318 let opts_json = opts.to_json();
1319 if let Some(obj) = params.as_object_mut()
1320 && let Some(opts_obj) = opts_json.as_object()
1321 {
1322 obj.extend(opts_obj.clone());
1323 }
1324 } else {
1325 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1326 }
1327
1328 self.channel().send_no_result("type", params).await
1329 }
1330
1331 pub(crate) async fn locator_all_inner_texts(&self, selector: &str) -> Result<Vec<String>> {
1333 #[derive(serde::Deserialize)]
1334 struct EvaluateResult {
1335 value: serde_json::Value,
1336 }
1337
1338 let params = serde_json::json!({
1341 "selector": selector,
1342 "expression": "ee => ee.map(e => e.innerText)",
1343 "isFunction": true,
1344 "arg": {
1345 "value": {"v": "null"},
1346 "handles": []
1347 }
1348 });
1349
1350 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1351
1352 Self::parse_string_array(result.value)
1353 }
1354
1355 pub(crate) async fn locator_all_text_contents(&self, selector: &str) -> Result<Vec<String>> {
1357 #[derive(serde::Deserialize)]
1358 struct EvaluateResult {
1359 value: serde_json::Value,
1360 }
1361
1362 let params = serde_json::json!({
1365 "selector": selector,
1366 "expression": "ee => ee.map(e => e.textContent || '')",
1367 "isFunction": true,
1368 "arg": {
1369 "value": {"v": "null"},
1370 "handles": []
1371 }
1372 });
1373
1374 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1375
1376 Self::parse_string_array(result.value)
1377 }
1378
1379 pub(crate) async fn locator_tap(
1386 &self,
1387 selector: &str,
1388 options: Option<crate::protocol::TapOptions>,
1389 ) -> Result<()> {
1390 let mut params = serde_json::json!({
1391 "selector": selector,
1392 "strict": true
1393 });
1394
1395 if let Some(opts) = options {
1396 let opts_json = opts.to_json();
1397 if let Some(obj) = params.as_object_mut()
1398 && let Some(opts_obj) = opts_json.as_object()
1399 {
1400 obj.extend(opts_obj.clone());
1401 }
1402 } else {
1403 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1404 }
1405
1406 self.channel().send_no_result("tap", params).await
1407 }
1408
1409 pub(crate) async fn locator_drag_to(
1415 &self,
1416 source_selector: &str,
1417 target_selector: &str,
1418 options: Option<crate::protocol::DragToOptions>,
1419 ) -> Result<()> {
1420 let mut params = serde_json::json!({
1421 "source": source_selector,
1422 "target": target_selector,
1423 "strict": true
1424 });
1425
1426 if let Some(opts) = options {
1427 let opts_json = opts.to_json();
1428 if let Some(obj) = params.as_object_mut()
1429 && let Some(opts_obj) = opts_json.as_object()
1430 {
1431 obj.extend(opts_obj.clone());
1432 }
1433 } else {
1434 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1435 }
1436
1437 self.channel().send_no_result("dragAndDrop", params).await
1438 }
1439
1440 pub(crate) async fn locator_wait_for(
1447 &self,
1448 selector: &str,
1449 options: Option<crate::protocol::WaitForOptions>,
1450 ) -> Result<()> {
1451 let mut params = serde_json::json!({
1452 "selector": selector,
1453 "strict": true
1454 });
1455
1456 if let Some(opts) = options {
1457 let opts_json = opts.to_json();
1458 if let Some(obj) = params.as_object_mut()
1459 && let Some(opts_obj) = opts_json.as_object()
1460 {
1461 obj.extend(opts_obj.clone());
1462 }
1463 } else {
1464 params["state"] = serde_json::json!("visible");
1466 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1467 }
1468
1469 let _: serde_json::Value = self.channel().send("waitForSelector", params).await?;
1471 Ok(())
1472 }
1473
1474 pub(crate) async fn locator_evaluate<T: serde::Serialize>(
1481 &self,
1482 selector: &str,
1483 expression: &str,
1484 arg: Option<T>,
1485 ) -> Result<serde_json::Value> {
1486 let serialized_arg = match arg {
1487 Some(a) => serialize_argument(&a),
1488 None => serialize_null(),
1489 };
1490
1491 let params = serde_json::json!({
1492 "selector": selector,
1493 "expression": expression,
1494 "isFunction": true,
1495 "arg": serialized_arg,
1496 "strict": true
1497 });
1498
1499 #[derive(Deserialize)]
1500 struct EvaluateResult {
1501 value: serde_json::Value,
1502 }
1503
1504 let result: EvaluateResult = self.channel().send("evalOnSelector", params).await?;
1505 Ok(parse_result(&result.value))
1506 }
1507
1508 pub(crate) async fn locator_evaluate_all<T: serde::Serialize>(
1515 &self,
1516 selector: &str,
1517 expression: &str,
1518 arg: Option<T>,
1519 ) -> Result<serde_json::Value> {
1520 let serialized_arg = match arg {
1521 Some(a) => serialize_argument(&a),
1522 None => serialize_null(),
1523 };
1524
1525 let params = serde_json::json!({
1526 "selector": selector,
1527 "expression": expression,
1528 "isFunction": true,
1529 "arg": serialized_arg
1530 });
1531
1532 #[derive(Deserialize)]
1533 struct EvaluateResult {
1534 value: serde_json::Value,
1535 }
1536
1537 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1538 Ok(parse_result(&result.value))
1539 }
1540
1541 fn parse_string_array(value: serde_json::Value) -> Result<Vec<String>> {
1546 let array = if let Some(arr) = value.get("a").and_then(|v| v.as_array()) {
1548 arr.clone()
1549 } else if let Some(arr) = value.as_array() {
1550 arr.clone()
1551 } else {
1552 return Ok(Vec::new());
1553 };
1554
1555 let mut result = Vec::with_capacity(array.len());
1556 for item in &array {
1557 let s = if let Some(s) = item.get("s").and_then(|v| v.as_str()) {
1559 s.to_string()
1560 } else if let Some(s) = item.as_str() {
1561 s.to_string()
1562 } else if item.is_null() {
1563 String::new()
1564 } else {
1565 item.to_string()
1566 };
1567 result.push(s);
1568 }
1569 Ok(result)
1570 }
1571
1572 pub(crate) async fn locator_check(
1573 &self,
1574 selector: &str,
1575 options: Option<crate::protocol::CheckOptions>,
1576 ) -> Result<()> {
1577 let mut params = serde_json::json!({
1578 "selector": selector,
1579 "strict": true
1580 });
1581
1582 if let Some(opts) = options {
1583 let opts_json = opts.to_json();
1584 if let Some(obj) = params.as_object_mut()
1585 && let Some(opts_obj) = opts_json.as_object()
1586 {
1587 obj.extend(opts_obj.clone());
1588 }
1589 } else {
1590 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1591 }
1592
1593 self.channel().send_no_result("check", params).await
1594 }
1595
1596 pub(crate) async fn locator_uncheck(
1597 &self,
1598 selector: &str,
1599 options: Option<crate::protocol::CheckOptions>,
1600 ) -> Result<()> {
1601 let mut params = serde_json::json!({
1602 "selector": selector,
1603 "strict": true
1604 });
1605
1606 if let Some(opts) = options {
1607 let opts_json = opts.to_json();
1608 if let Some(obj) = params.as_object_mut()
1609 && let Some(opts_obj) = opts_json.as_object()
1610 {
1611 obj.extend(opts_obj.clone());
1612 }
1613 } else {
1614 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1615 }
1616
1617 self.channel().send_no_result("uncheck", params).await
1618 }
1619
1620 pub(crate) async fn locator_hover(
1621 &self,
1622 selector: &str,
1623 options: Option<crate::protocol::HoverOptions>,
1624 ) -> Result<()> {
1625 let mut params = serde_json::json!({
1626 "selector": selector,
1627 "strict": true
1628 });
1629
1630 if let Some(opts) = options {
1631 let opts_json = opts.to_json();
1632 if let Some(obj) = params.as_object_mut()
1633 && let Some(opts_obj) = opts_json.as_object()
1634 {
1635 obj.extend(opts_obj.clone());
1636 }
1637 } else {
1638 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1639 }
1640
1641 self.channel().send_no_result("hover", params).await
1642 }
1643
1644 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
1645 #[derive(Deserialize)]
1646 struct InputValueResponse {
1647 value: String,
1648 }
1649
1650 let response: InputValueResponse = self
1651 .channel()
1652 .send(
1653 "inputValue",
1654 serde_json::json!({
1655 "selector": selector,
1656 "strict": true,
1657 "timeout": crate::DEFAULT_TIMEOUT_MS }),
1659 )
1660 .await?;
1661
1662 Ok(response.value)
1663 }
1664
1665 pub(crate) async fn locator_select_option(
1666 &self,
1667 selector: &str,
1668 value: crate::protocol::SelectOption,
1669 options: Option<crate::protocol::SelectOptions>,
1670 ) -> Result<Vec<String>> {
1671 #[derive(Deserialize)]
1672 struct SelectOptionResponse {
1673 values: Vec<String>,
1674 }
1675
1676 let mut params = serde_json::json!({
1677 "selector": selector,
1678 "strict": true,
1679 "options": [value.to_json()]
1680 });
1681
1682 if let Some(opts) = options {
1683 let opts_json = opts.to_json();
1684 if let Some(obj) = params.as_object_mut()
1685 && let Some(opts_obj) = opts_json.as_object()
1686 {
1687 obj.extend(opts_obj.clone());
1688 }
1689 } else {
1690 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1692 }
1693
1694 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1695
1696 Ok(response.values)
1697 }
1698
1699 pub(crate) async fn locator_select_option_multiple(
1700 &self,
1701 selector: &str,
1702 values: Vec<crate::protocol::SelectOption>,
1703 options: Option<crate::protocol::SelectOptions>,
1704 ) -> Result<Vec<String>> {
1705 #[derive(Deserialize)]
1706 struct SelectOptionResponse {
1707 values: Vec<String>,
1708 }
1709
1710 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
1711
1712 let mut params = serde_json::json!({
1713 "selector": selector,
1714 "strict": true,
1715 "options": values_array
1716 });
1717
1718 if let Some(opts) = options {
1719 let opts_json = opts.to_json();
1720 if let Some(obj) = params.as_object_mut()
1721 && let Some(opts_obj) = opts_json.as_object()
1722 {
1723 obj.extend(opts_obj.clone());
1724 }
1725 } else {
1726 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1728 }
1729
1730 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1731
1732 Ok(response.values)
1733 }
1734
1735 pub(crate) async fn locator_set_input_files(
1736 &self,
1737 selector: &str,
1738 file: &std::path::PathBuf,
1739 ) -> Result<()> {
1740 use base64::{Engine as _, engine::general_purpose};
1741 use std::io::Read;
1742
1743 let mut file_handle = std::fs::File::open(file)?;
1745 let mut buffer = Vec::new();
1746 file_handle.read_to_end(&mut buffer)?;
1747
1748 let base64_content = general_purpose::STANDARD.encode(&buffer);
1750
1751 let file_name = file
1753 .file_name()
1754 .and_then(|n| n.to_str())
1755 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
1756
1757 self.channel()
1758 .send_no_result(
1759 "setInputFiles",
1760 serde_json::json!({
1761 "selector": selector,
1762 "strict": true,
1763 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
1765 "name": file_name,
1766 "buffer": base64_content
1767 }]
1768 }),
1769 )
1770 .await
1771 }
1772
1773 pub(crate) async fn locator_set_input_files_multiple(
1774 &self,
1775 selector: &str,
1776 files: &[&std::path::PathBuf],
1777 ) -> Result<()> {
1778 use base64::{Engine as _, engine::general_purpose};
1779 use std::io::Read;
1780
1781 if files.is_empty() {
1783 return self
1784 .channel()
1785 .send_no_result(
1786 "setInputFiles",
1787 serde_json::json!({
1788 "selector": selector,
1789 "strict": true,
1790 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
1792 }),
1793 )
1794 .await;
1795 }
1796
1797 let mut file_objects = Vec::new();
1799 for file_path in files {
1800 let mut file_handle = std::fs::File::open(file_path)?;
1801 let mut buffer = Vec::new();
1802 file_handle.read_to_end(&mut buffer)?;
1803
1804 let base64_content = general_purpose::STANDARD.encode(&buffer);
1805 let file_name = file_path
1806 .file_name()
1807 .and_then(|n| n.to_str())
1808 .ok_or_else(|| {
1809 crate::error::Error::InvalidArgument("Invalid file path".to_string())
1810 })?;
1811
1812 file_objects.push(serde_json::json!({
1813 "name": file_name,
1814 "buffer": base64_content
1815 }));
1816 }
1817
1818 self.channel()
1819 .send_no_result(
1820 "setInputFiles",
1821 serde_json::json!({
1822 "selector": selector,
1823 "strict": true,
1824 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": file_objects
1826 }),
1827 )
1828 .await
1829 }
1830
1831 pub(crate) async fn locator_set_input_files_payload(
1832 &self,
1833 selector: &str,
1834 file: crate::protocol::FilePayload,
1835 ) -> Result<()> {
1836 use base64::{Engine as _, engine::general_purpose};
1837
1838 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1840
1841 self.channel()
1842 .send_no_result(
1843 "setInputFiles",
1844 serde_json::json!({
1845 "selector": selector,
1846 "strict": true,
1847 "timeout": crate::DEFAULT_TIMEOUT_MS,
1848 "payloads": [{
1849 "name": file.name,
1850 "mimeType": file.mime_type,
1851 "buffer": base64_content
1852 }]
1853 }),
1854 )
1855 .await
1856 }
1857
1858 pub(crate) async fn locator_set_input_files_payload_multiple(
1859 &self,
1860 selector: &str,
1861 files: &[crate::protocol::FilePayload],
1862 ) -> Result<()> {
1863 use base64::{Engine as _, engine::general_purpose};
1864
1865 if files.is_empty() {
1867 return self
1868 .channel()
1869 .send_no_result(
1870 "setInputFiles",
1871 serde_json::json!({
1872 "selector": selector,
1873 "strict": true,
1874 "timeout": crate::DEFAULT_TIMEOUT_MS,
1875 "payloads": []
1876 }),
1877 )
1878 .await;
1879 }
1880
1881 let file_objects: Vec<_> = files
1883 .iter()
1884 .map(|file| {
1885 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1886 serde_json::json!({
1887 "name": file.name,
1888 "mimeType": file.mime_type,
1889 "buffer": base64_content
1890 })
1891 })
1892 .collect();
1893
1894 self.channel()
1895 .send_no_result(
1896 "setInputFiles",
1897 serde_json::json!({
1898 "selector": selector,
1899 "strict": true,
1900 "timeout": crate::DEFAULT_TIMEOUT_MS,
1901 "payloads": file_objects
1902 }),
1903 )
1904 .await
1905 }
1906
1907 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
1911 let params = serde_json::json!({
1912 "expression": expression,
1913 "arg": {
1914 "value": {"v": "null"},
1915 "handles": []
1916 }
1917 });
1918
1919 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
1920 Ok(())
1921 }
1922
1923 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
1935 let params = serde_json::json!({
1936 "expression": expression,
1937 "arg": {
1938 "value": {"v": "null"},
1939 "handles": []
1940 }
1941 });
1942
1943 #[derive(Deserialize)]
1944 struct EvaluateResult {
1945 value: serde_json::Value,
1946 }
1947
1948 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1949
1950 match &result.value {
1957 Value::Object(map) => {
1958 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
1959 Ok(s.to_string())
1961 } else if let Some(n) = map.get("n") {
1962 Ok(n.to_string())
1964 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
1965 Ok(b.to_string())
1967 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
1968 Ok(v.to_string())
1970 } else {
1971 Ok(result.value.to_string())
1973 }
1974 }
1975 _ => {
1976 Ok(result.value.to_string())
1978 }
1979 }
1980 }
1981
1982 pub async fn evaluate<T: serde::Serialize>(
2022 &self,
2023 expression: &str,
2024 arg: Option<&T>,
2025 ) -> Result<Value> {
2026 let serialized_arg = match arg {
2028 Some(a) => serialize_argument(a),
2029 None => serialize_null(),
2030 };
2031
2032 let params = serde_json::json!({
2034 "expression": expression,
2035 "arg": serialized_arg
2036 });
2037
2038 #[derive(Deserialize)]
2040 struct EvaluateResult {
2041 value: serde_json::Value,
2042 }
2043
2044 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2045
2046 Ok(parse_result(&result.value))
2048 }
2049
2050 pub async fn add_style_tag(
2090 &self,
2091 options: crate::protocol::page::AddStyleTagOptions,
2092 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2093 options.validate()?;
2095
2096 let mut params = serde_json::json!({});
2098
2099 if let Some(content) = &options.content {
2100 params["content"] = serde_json::json!(content);
2101 }
2102
2103 if let Some(url) = &options.url {
2104 params["url"] = serde_json::json!(url);
2105 }
2106
2107 if let Some(path) = &options.path {
2108 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2110 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
2111 })?;
2112 params["content"] = serde_json::json!(css_content);
2113 }
2114
2115 #[derive(Deserialize)]
2116 struct AddStyleTagResponse {
2117 element: serde_json::Value,
2118 }
2119
2120 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
2121
2122 let guid = response.element["guid"].as_str().ok_or_else(|| {
2123 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
2124 })?;
2125
2126 let connection = self.base.connection();
2127 let handle: crate::protocol::ElementHandle = connection
2128 .get_typed::<crate::protocol::ElementHandle>(guid)
2129 .await?;
2130
2131 Ok(Arc::new(handle))
2132 }
2133
2134 pub(crate) async fn locator_dispatch_event(
2142 &self,
2143 selector: &str,
2144 type_: &str,
2145 event_init: Option<serde_json::Value>,
2146 ) -> Result<()> {
2147 let event_init_serialized = match event_init {
2150 Some(v) => serialize_argument(&v),
2151 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
2152 };
2153
2154 let params = serde_json::json!({
2155 "selector": selector,
2156 "type": type_,
2157 "eventInit": event_init_serialized,
2158 "strict": true,
2159 "timeout": crate::DEFAULT_TIMEOUT_MS
2160 });
2161
2162 self.channel().send_no_result("dispatchEvent", params).await
2163 }
2164
2165 pub(crate) async fn locator_bounding_box(
2175 &self,
2176 selector: &str,
2177 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
2178 let element = self.query_selector(selector).await?;
2179 match element {
2180 Some(handle) => handle.bounding_box().await,
2181 None => Ok(None),
2182 }
2183 }
2184
2185 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
2192 let element = self.query_selector(selector).await?;
2193 match element {
2194 Some(handle) => handle.scroll_into_view_if_needed().await,
2195 None => Err(crate::error::Error::ElementNotFound(format!(
2196 "Element not found: {}",
2197 selector
2198 ))),
2199 }
2200 }
2201
2202 pub async fn add_script_tag(
2212 &self,
2213 options: crate::protocol::page::AddScriptTagOptions,
2214 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2215 options.validate()?;
2217
2218 let mut params = serde_json::json!({});
2220
2221 if let Some(content) = &options.content {
2222 params["content"] = serde_json::json!(content);
2223 }
2224
2225 if let Some(url) = &options.url {
2226 params["url"] = serde_json::json!(url);
2227 }
2228
2229 if let Some(path) = &options.path {
2230 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2232 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
2233 })?;
2234 params["content"] = serde_json::json!(js_content);
2235 }
2236
2237 if let Some(type_) = &options.type_ {
2238 params["type"] = serde_json::json!(type_);
2239 }
2240
2241 #[derive(Deserialize)]
2242 struct AddScriptTagResponse {
2243 element: serde_json::Value,
2244 }
2245
2246 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
2247
2248 let guid = response.element["guid"].as_str().ok_or_else(|| {
2249 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
2250 })?;
2251
2252 let connection = self.base.connection();
2253 let handle: crate::protocol::ElementHandle = connection
2254 .get_typed::<crate::protocol::ElementHandle>(guid)
2255 .await?;
2256
2257 Ok(Arc::new(handle))
2258 }
2259}
2260
2261impl ChannelOwner for Frame {
2262 fn guid(&self) -> &str {
2263 self.base.guid()
2264 }
2265
2266 fn type_name(&self) -> &str {
2267 self.base.type_name()
2268 }
2269
2270 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
2271 self.base.parent()
2272 }
2273
2274 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
2275 self.base.connection()
2276 }
2277
2278 fn initializer(&self) -> &Value {
2279 self.base.initializer()
2280 }
2281
2282 fn channel(&self) -> &Channel {
2283 self.base.channel()
2284 }
2285
2286 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
2287 self.base.dispose(reason)
2288 }
2289
2290 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
2291 self.base.adopt(child)
2292 }
2293
2294 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
2295 self.base.add_child(guid, child)
2296 }
2297
2298 fn remove_child(&self, guid: &str) {
2299 self.base.remove_child(guid)
2300 }
2301
2302 fn on_event(&self, method: &str, params: Value) {
2303 match method {
2304 "navigated" => {
2305 if let Some(url_value) = params.get("url")
2307 && let Some(url_str) = url_value.as_str()
2308 {
2309 if let Ok(mut url) = self.url.write() {
2311 *url = url_str.to_string();
2312 }
2313 }
2314 let self_clone = self.clone();
2316 tokio::spawn(async move {
2317 if let Some(page) = self_clone.page() {
2318 page.trigger_framenavigated_event(self_clone).await;
2319 }
2320 });
2321 }
2322 "loadstate" => {
2323 if let Some(add) = params.get("add").and_then(|v| v.as_str())
2326 && add == "load"
2327 {
2328 let self_clone = self.clone();
2329 tokio::spawn(async move {
2330 if let Some(page) = self_clone.page() {
2331 page.trigger_load_event().await;
2332 }
2333 });
2334 }
2335 }
2336 "detached" => {
2337 if let Ok(mut flag) = self.is_detached.write() {
2339 *flag = true;
2340 }
2341 }
2342 _ => {
2343 }
2345 }
2346 }
2347
2348 fn was_collected(&self) -> bool {
2349 self.base.was_collected()
2350 }
2351
2352 fn as_any(&self) -> &dyn Any {
2353 self
2354 }
2355}
2356
2357impl std::fmt::Debug for Frame {
2358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2359 f.debug_struct("Frame").field("guid", &self.guid()).finish()
2360 }
2361}
2362
2363fn glob_match(pattern: &str, text: &str) -> bool {
2368 let regex_str = pattern
2369 .replace('.', "\\.")
2370 .replace("**", "\x00") .replace('*', "[^/]*")
2372 .replace('\x00', ".*"); let regex_str = format!("^{}$", regex_str);
2374 regex::Regex::new(®ex_str)
2375 .map(|re| re.is_match(text))
2376 .unwrap_or(false)
2377}