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 locator_aria_snapshot(&self, selector: &str) -> Result<String> {
1914 self.aria_snapshot_raw(selector, crate::DEFAULT_TIMEOUT_MS)
1915 .await
1916 }
1917
1918 pub(crate) async fn aria_snapshot_raw(&self, selector: &str, timeout: f64) -> Result<String> {
1919 #[derive(Deserialize)]
1920 struct AriaSnapshotResponse {
1921 snapshot: String,
1922 }
1923
1924 let response: AriaSnapshotResponse = self
1925 .channel()
1926 .send(
1927 "ariaSnapshot",
1928 serde_json::json!({
1929 "selector": selector,
1930 "timeout": timeout
1931 }),
1932 )
1933 .await?;
1934
1935 Ok(response.snapshot)
1936 }
1937
1938 pub(crate) async fn locator_highlight(&self, selector: &str) -> Result<()> {
1945 self.channel()
1946 .send_no_result(
1947 "highlight",
1948 serde_json::json!({
1949 "selector": selector
1950 }),
1951 )
1952 .await
1953 }
1954
1955 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
1959 let params = serde_json::json!({
1960 "expression": expression,
1961 "arg": {
1962 "value": {"v": "null"},
1963 "handles": []
1964 }
1965 });
1966
1967 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
1968 Ok(())
1969 }
1970
1971 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
1983 let params = serde_json::json!({
1984 "expression": expression,
1985 "arg": {
1986 "value": {"v": "null"},
1987 "handles": []
1988 }
1989 });
1990
1991 #[derive(Deserialize)]
1992 struct EvaluateResult {
1993 value: serde_json::Value,
1994 }
1995
1996 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1997
1998 match &result.value {
2005 Value::Object(map) => {
2006 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
2007 Ok(s.to_string())
2009 } else if let Some(n) = map.get("n") {
2010 Ok(n.to_string())
2012 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
2013 Ok(b.to_string())
2015 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
2016 Ok(v.to_string())
2018 } else {
2019 Ok(result.value.to_string())
2021 }
2022 }
2023 _ => {
2024 Ok(result.value.to_string())
2026 }
2027 }
2028 }
2029
2030 pub async fn evaluate<T: serde::Serialize>(
2070 &self,
2071 expression: &str,
2072 arg: Option<&T>,
2073 ) -> Result<Value> {
2074 let serialized_arg = match arg {
2076 Some(a) => serialize_argument(a),
2077 None => serialize_null(),
2078 };
2079
2080 let params = serde_json::json!({
2082 "expression": expression,
2083 "arg": serialized_arg
2084 });
2085
2086 #[derive(Deserialize)]
2088 struct EvaluateResult {
2089 value: serde_json::Value,
2090 }
2091
2092 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2093
2094 Ok(parse_result(&result.value))
2096 }
2097
2098 pub async fn add_style_tag(
2138 &self,
2139 options: crate::protocol::page::AddStyleTagOptions,
2140 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2141 options.validate()?;
2143
2144 let mut params = serde_json::json!({});
2146
2147 if let Some(content) = &options.content {
2148 params["content"] = serde_json::json!(content);
2149 }
2150
2151 if let Some(url) = &options.url {
2152 params["url"] = serde_json::json!(url);
2153 }
2154
2155 if let Some(path) = &options.path {
2156 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2158 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
2159 })?;
2160 params["content"] = serde_json::json!(css_content);
2161 }
2162
2163 #[derive(Deserialize)]
2164 struct AddStyleTagResponse {
2165 element: serde_json::Value,
2166 }
2167
2168 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
2169
2170 let guid = response.element["guid"].as_str().ok_or_else(|| {
2171 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
2172 })?;
2173
2174 let connection = self.base.connection();
2175 let handle: crate::protocol::ElementHandle = connection
2176 .get_typed::<crate::protocol::ElementHandle>(guid)
2177 .await?;
2178
2179 Ok(Arc::new(handle))
2180 }
2181
2182 pub(crate) async fn locator_dispatch_event(
2190 &self,
2191 selector: &str,
2192 type_: &str,
2193 event_init: Option<serde_json::Value>,
2194 ) -> Result<()> {
2195 let event_init_serialized = match event_init {
2198 Some(v) => serialize_argument(&v),
2199 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
2200 };
2201
2202 let params = serde_json::json!({
2203 "selector": selector,
2204 "type": type_,
2205 "eventInit": event_init_serialized,
2206 "strict": true,
2207 "timeout": crate::DEFAULT_TIMEOUT_MS
2208 });
2209
2210 self.channel().send_no_result("dispatchEvent", params).await
2211 }
2212
2213 pub(crate) async fn locator_bounding_box(
2223 &self,
2224 selector: &str,
2225 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
2226 let element = self.query_selector(selector).await?;
2227 match element {
2228 Some(handle) => handle.bounding_box().await,
2229 None => Ok(None),
2230 }
2231 }
2232
2233 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
2240 let element = self.query_selector(selector).await?;
2241 match element {
2242 Some(handle) => handle.scroll_into_view_if_needed().await,
2243 None => Err(crate::error::Error::ElementNotFound(format!(
2244 "Element not found: {}",
2245 selector
2246 ))),
2247 }
2248 }
2249
2250 pub(crate) async fn frame_expect(
2256 &self,
2257 selector: &str,
2258 expression: &str,
2259 expected_value: serde_json::Value,
2260 is_not: bool,
2261 timeout_ms: f64,
2262 ) -> Result<()> {
2263 #[derive(serde::Deserialize)]
2264 #[serde(rename_all = "camelCase")]
2265 struct ExpectResult {
2266 matches: bool,
2267 #[serde(default)]
2268 timed_out: Option<bool>,
2269 #[serde(default)]
2270 error_message: Option<String>,
2271 }
2272
2273 let params = serde_json::json!({
2274 "selector": selector,
2275 "expression": expression,
2276 "expectedValue": expected_value,
2277 "isNot": is_not,
2278 "timeout": timeout_ms
2279 });
2280
2281 let result: ExpectResult = self.channel().send("expect", params).await?;
2282
2283 if result.matches != is_not {
2284 Ok(())
2285 } else {
2286 let msg = result
2287 .error_message
2288 .unwrap_or_else(|| format!("Assertion '{}' failed", expression));
2289 if result.timed_out == Some(true) {
2290 Err(crate::error::Error::AssertionTimeout(msg))
2291 } else {
2292 Err(crate::error::Error::AssertionFailed(msg))
2293 }
2294 }
2295 }
2296
2297 pub async fn add_script_tag(
2307 &self,
2308 options: crate::protocol::page::AddScriptTagOptions,
2309 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2310 options.validate()?;
2312
2313 let mut params = serde_json::json!({});
2315
2316 if let Some(content) = &options.content {
2317 params["content"] = serde_json::json!(content);
2318 }
2319
2320 if let Some(url) = &options.url {
2321 params["url"] = serde_json::json!(url);
2322 }
2323
2324 if let Some(path) = &options.path {
2325 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2327 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
2328 })?;
2329 params["content"] = serde_json::json!(js_content);
2330 }
2331
2332 if let Some(type_) = &options.type_ {
2333 params["type"] = serde_json::json!(type_);
2334 }
2335
2336 #[derive(Deserialize)]
2337 struct AddScriptTagResponse {
2338 element: serde_json::Value,
2339 }
2340
2341 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
2342
2343 let guid = response.element["guid"].as_str().ok_or_else(|| {
2344 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
2345 })?;
2346
2347 let connection = self.base.connection();
2348 let handle: crate::protocol::ElementHandle = connection
2349 .get_typed::<crate::protocol::ElementHandle>(guid)
2350 .await?;
2351
2352 Ok(Arc::new(handle))
2353 }
2354}
2355
2356impl ChannelOwner for Frame {
2357 fn guid(&self) -> &str {
2358 self.base.guid()
2359 }
2360
2361 fn type_name(&self) -> &str {
2362 self.base.type_name()
2363 }
2364
2365 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
2366 self.base.parent()
2367 }
2368
2369 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
2370 self.base.connection()
2371 }
2372
2373 fn initializer(&self) -> &Value {
2374 self.base.initializer()
2375 }
2376
2377 fn channel(&self) -> &Channel {
2378 self.base.channel()
2379 }
2380
2381 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
2382 self.base.dispose(reason)
2383 }
2384
2385 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
2386 self.base.adopt(child)
2387 }
2388
2389 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
2390 self.base.add_child(guid, child)
2391 }
2392
2393 fn remove_child(&self, guid: &str) {
2394 self.base.remove_child(guid)
2395 }
2396
2397 fn on_event(&self, method: &str, params: Value) {
2398 match method {
2399 "navigated" => {
2400 if let Some(url_value) = params.get("url")
2402 && let Some(url_str) = url_value.as_str()
2403 {
2404 if let Ok(mut url) = self.url.write() {
2406 *url = url_str.to_string();
2407 }
2408 }
2409 let self_clone = self.clone();
2411 tokio::spawn(async move {
2412 if let Some(page) = self_clone.page() {
2413 page.trigger_framenavigated_event(self_clone).await;
2414 }
2415 });
2416 }
2417 "loadstate" => {
2418 if let Some(add) = params.get("add").and_then(|v| v.as_str())
2421 && add == "load"
2422 {
2423 let self_clone = self.clone();
2424 tokio::spawn(async move {
2425 if let Some(page) = self_clone.page() {
2426 page.trigger_load_event().await;
2427 }
2428 });
2429 }
2430 }
2431 "detached" => {
2432 if let Ok(mut flag) = self.is_detached.write() {
2434 *flag = true;
2435 }
2436 }
2437 _ => {
2438 }
2440 }
2441 }
2442
2443 fn was_collected(&self) -> bool {
2444 self.base.was_collected()
2445 }
2446
2447 fn as_any(&self) -> &dyn Any {
2448 self
2449 }
2450}
2451
2452impl std::fmt::Debug for Frame {
2453 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2454 f.debug_struct("Frame").field("guid", &self.guid()).finish()
2455 }
2456}
2457
2458fn glob_match(pattern: &str, text: &str) -> bool {
2463 let regex_str = pattern
2464 .replace('.', "\\.")
2465 .replace("**", "\x00") .replace('*', "[^/]*")
2467 .replace('\x00', ".*"); let regex_str = format!("^{}$", regex_str);
2469 regex::Regex::new(®ex_str)
2470 .map(|re| re.is_match(text))
2471 .unwrap_or(false)
2472}