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 fn locator(&self, selector: &str) -> crate::protocol::Locator {
301 let page = self
302 .page()
303 .expect("Frame::locator() called before set_page(); call page.main_frame() first");
304 crate::protocol::Locator::new(Arc::new(self.clone()), selector.to_string(), page)
305 }
306
307 pub fn get_by_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
311 self.locator(&crate::protocol::locator::get_by_text_selector(text, exact))
312 }
313
314 pub fn get_by_label(&self, text: &str, exact: bool) -> crate::protocol::Locator {
318 self.locator(&crate::protocol::locator::get_by_label_selector(
319 text, exact,
320 ))
321 }
322
323 pub fn get_by_placeholder(&self, text: &str, exact: bool) -> crate::protocol::Locator {
327 self.locator(&crate::protocol::locator::get_by_placeholder_selector(
328 text, exact,
329 ))
330 }
331
332 pub fn get_by_alt_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
336 self.locator(&crate::protocol::locator::get_by_alt_text_selector(
337 text, exact,
338 ))
339 }
340
341 pub fn get_by_title(&self, text: &str, exact: bool) -> crate::protocol::Locator {
345 self.locator(&crate::protocol::locator::get_by_title_selector(
346 text, exact,
347 ))
348 }
349
350 pub fn get_by_test_id(&self, test_id: &str) -> crate::protocol::Locator {
354 self.locator(&crate::protocol::locator::get_by_test_id_selector(test_id))
355 }
356
357 pub fn get_by_role(
361 &self,
362 role: crate::protocol::locator::AriaRole,
363 options: Option<crate::protocol::locator::GetByRoleOptions>,
364 ) -> crate::protocol::Locator {
365 self.locator(&crate::protocol::locator::get_by_role_selector(
366 role, options,
367 ))
368 }
369
370 fn channel(&self) -> &Channel {
372 self.base.channel()
373 }
374
375 pub fn url(&self) -> String {
381 self.url.read().unwrap().clone()
382 }
383
384 pub async fn goto(&self, url: &str, options: Option<GotoOptions>) -> Result<Option<Response>> {
398 let mut params = serde_json::json!({
400 "url": url,
401 });
402
403 if let Some(opts) = options {
405 if let Some(timeout) = opts.timeout {
406 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
407 } else {
408 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
410 }
411 if let Some(wait_until) = opts.wait_until {
412 params["waitUntil"] = serde_json::json!(wait_until.as_str());
413 }
414 } else {
415 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
417 }
418
419 #[derive(Deserialize)]
422 struct GotoResponse {
423 response: Option<ResponseReference>,
424 }
425
426 #[derive(Deserialize)]
427 struct ResponseReference {
428 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
429 guid: Arc<str>,
430 }
431
432 let goto_result: GotoResponse = self.channel().send("goto", params).await?;
433
434 if let Some(response_ref) = goto_result.response {
436 let response_arc = {
442 let mut attempts = 0;
443 let max_attempts = 20; loop {
445 match self.connection().get_object(&response_ref.guid).await {
446 Ok(obj) => break obj,
447 Err(_) if attempts < max_attempts => {
448 attempts += 1;
449 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
450 }
451 Err(e) => return Err(e),
452 }
453 }
454 };
455
456 let initializer = response_arc.initializer();
459
460 let status = initializer["status"].as_u64().ok_or_else(|| {
462 crate::error::Error::ProtocolError("Response missing status".to_string())
463 })? as u16;
464
465 let headers = initializer["headers"]
467 .as_array()
468 .ok_or_else(|| {
469 crate::error::Error::ProtocolError("Response missing headers".to_string())
470 })?
471 .iter()
472 .filter_map(|h| {
473 let name = h["name"].as_str()?;
474 let value = h["value"].as_str()?;
475 Some((name.to_string(), value.to_string()))
476 })
477 .collect();
478
479 Ok(Some(Response::new(
480 initializer["url"]
481 .as_str()
482 .ok_or_else(|| {
483 crate::error::Error::ProtocolError("Response missing url".to_string())
484 })?
485 .to_string(),
486 status,
487 initializer["statusText"].as_str().unwrap_or("").to_string(),
488 headers,
489 Some(response_arc),
490 )))
491 } else {
492 Ok(None)
495 }
496 }
497
498 pub async fn title(&self) -> Result<String> {
502 #[derive(Deserialize)]
503 struct TitleResponse {
504 value: String,
505 }
506
507 let response: TitleResponse = self.channel().send("title", serde_json::json!({})).await?;
508 Ok(response.value)
509 }
510
511 pub async fn content(&self) -> Result<String> {
515 #[derive(Deserialize)]
516 struct ContentResponse {
517 value: String,
518 }
519
520 let response: ContentResponse = self
521 .channel()
522 .send("content", serde_json::json!({}))
523 .await?;
524 Ok(response.value)
525 }
526
527 pub async fn set_content(&self, html: &str, options: Option<GotoOptions>) -> Result<()> {
531 let mut params = serde_json::json!({
532 "html": html,
533 });
534
535 if let Some(opts) = options {
536 if let Some(timeout) = opts.timeout {
537 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
538 } else {
539 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
540 }
541 if let Some(wait_until) = opts.wait_until {
542 params["waitUntil"] = serde_json::json!(wait_until.as_str());
543 }
544 } else {
545 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
546 }
547
548 self.channel().send_no_result("setContent", params).await
549 }
550
551 pub async fn wait_for_load_state(&self, state: Option<WaitUntil>) -> Result<()> {
559 let target_state = state.unwrap_or(WaitUntil::Load);
560
561 let js_check = match target_state {
562 WaitUntil::Load => "document.readyState === 'complete'",
564 WaitUntil::DomContentLoaded => "document.readyState !== 'loading'",
566 WaitUntil::NetworkIdle => "document.readyState === 'complete'",
569 WaitUntil::Commit => "document.readyState !== 'loading'",
571 };
572
573 let timeout_ms = crate::DEFAULT_TIMEOUT_MS as u64;
574 let poll_interval = std::time::Duration::from_millis(50);
575 let start = std::time::Instant::now();
576
577 loop {
578 #[derive(Deserialize)]
579 struct EvalResponse {
580 value: serde_json::Value,
581 }
582
583 let result: EvalResponse = self
584 .channel()
585 .send(
586 "evaluateExpression",
587 serde_json::json!({
588 "expression": js_check,
589 "isFunction": false,
590 "arg": crate::protocol::serialize_null(),
591 }),
592 )
593 .await?;
594
595 let is_ready = result
597 .value
598 .as_object()
599 .and_then(|m| m.get("b"))
600 .and_then(|v| v.as_bool())
601 .unwrap_or(false);
602
603 if is_ready {
604 return Ok(());
605 }
606
607 if start.elapsed().as_millis() as u64 >= timeout_ms {
608 return Err(crate::error::Error::Timeout(format!(
609 "wait_for_load_state({}) timed out after {}ms",
610 target_state.as_str(),
611 timeout_ms
612 )));
613 }
614
615 tokio::time::sleep(poll_interval).await;
616 }
617 }
618
619 pub async fn wait_for_url(&self, url: &str, options: Option<GotoOptions>) -> Result<()> {
626 let timeout_ms = options
627 .as_ref()
628 .and_then(|o| o.timeout)
629 .map(|d| d.as_millis() as u64)
630 .unwrap_or(crate::DEFAULT_TIMEOUT_MS as u64);
631
632 let is_glob = url.contains('*');
636
637 let poll_interval = std::time::Duration::from_millis(50);
638 let start = std::time::Instant::now();
639
640 loop {
641 let current_url = self.url();
642
643 let matches = if is_glob {
644 glob_match(url, ¤t_url)
645 } else {
646 current_url == url
647 };
648
649 if matches {
650 if let Some(ref opts) = options
652 && let Some(wait_until) = opts.wait_until
653 {
654 self.wait_for_load_state(Some(wait_until)).await?;
655 }
656 return Ok(());
657 }
658
659 if start.elapsed().as_millis() as u64 >= timeout_ms {
660 return Err(crate::error::Error::Timeout(format!(
661 "wait_for_url({}) timed out after {}ms, current URL: {}",
662 url, timeout_ms, current_url
663 )));
664 }
665
666 tokio::time::sleep(poll_interval).await;
667 }
668 }
669
670 pub async fn query_selector(
674 &self,
675 selector: &str,
676 ) -> Result<Option<Arc<crate::protocol::ElementHandle>>> {
677 let response: serde_json::Value = self
678 .channel()
679 .send(
680 "querySelector",
681 serde_json::json!({
682 "selector": selector
683 }),
684 )
685 .await?;
686
687 if response.as_object().map(|o| o.is_empty()).unwrap_or(true) {
689 return Ok(None);
690 }
691
692 let element_value = if let Some(elem) = response.get("element") {
694 elem
695 } else if let Some(elem) = response.get("handle") {
696 elem
697 } else {
698 &response
700 };
701
702 if element_value.is_null() {
703 return Ok(None);
704 }
705
706 let guid = element_value["guid"].as_str().ok_or_else(|| {
708 crate::error::Error::ProtocolError("Element GUID missing".to_string())
709 })?;
710
711 let connection = self.base.connection();
713 let handle: crate::protocol::ElementHandle = connection
714 .get_typed::<crate::protocol::ElementHandle>(guid)
715 .await?;
716
717 Ok(Some(Arc::new(handle)))
718 }
719
720 pub async fn query_selector_all(
724 &self,
725 selector: &str,
726 ) -> Result<Vec<Arc<crate::protocol::ElementHandle>>> {
727 #[derive(Deserialize)]
728 struct QueryAllResponse {
729 elements: Vec<serde_json::Value>,
730 }
731
732 let response: QueryAllResponse = self
733 .channel()
734 .send(
735 "querySelectorAll",
736 serde_json::json!({
737 "selector": selector
738 }),
739 )
740 .await?;
741
742 let connection = self.base.connection();
744 let mut handles = Vec::new();
745
746 for element_value in response.elements {
747 let guid = element_value["guid"].as_str().ok_or_else(|| {
748 crate::error::Error::ProtocolError("Element GUID missing".to_string())
749 })?;
750
751 let handle: crate::protocol::ElementHandle = connection
752 .get_typed::<crate::protocol::ElementHandle>(guid)
753 .await?;
754
755 handles.push(Arc::new(handle));
756 }
757
758 Ok(handles)
759 }
760
761 pub(crate) async fn locator_count(&self, selector: &str) -> Result<usize> {
766 #[derive(Deserialize)]
768 struct QueryAllResponse {
769 elements: Vec<serde_json::Value>,
770 }
771
772 let response: QueryAllResponse = self
773 .channel()
774 .send(
775 "querySelectorAll",
776 serde_json::json!({
777 "selector": selector
778 }),
779 )
780 .await?;
781
782 Ok(response.elements.len())
783 }
784
785 pub(crate) async fn locator_text_content(&self, selector: &str) -> Result<Option<String>> {
787 #[derive(Deserialize)]
788 struct TextContentResponse {
789 value: Option<String>,
790 }
791
792 let response: TextContentResponse = self
793 .channel()
794 .send(
795 "textContent",
796 serde_json::json!({
797 "selector": selector,
798 "strict": true,
799 "timeout": crate::DEFAULT_TIMEOUT_MS
800 }),
801 )
802 .await?;
803
804 Ok(response.value)
805 }
806
807 pub(crate) async fn locator_inner_text(&self, selector: &str) -> Result<String> {
809 #[derive(Deserialize)]
810 struct InnerTextResponse {
811 value: String,
812 }
813
814 let response: InnerTextResponse = self
815 .channel()
816 .send(
817 "innerText",
818 serde_json::json!({
819 "selector": selector,
820 "strict": true,
821 "timeout": crate::DEFAULT_TIMEOUT_MS
822 }),
823 )
824 .await?;
825
826 Ok(response.value)
827 }
828
829 pub(crate) async fn locator_inner_html(&self, selector: &str) -> Result<String> {
831 #[derive(Deserialize)]
832 struct InnerHTMLResponse {
833 value: String,
834 }
835
836 let response: InnerHTMLResponse = self
837 .channel()
838 .send(
839 "innerHTML",
840 serde_json::json!({
841 "selector": selector,
842 "strict": true,
843 "timeout": crate::DEFAULT_TIMEOUT_MS
844 }),
845 )
846 .await?;
847
848 Ok(response.value)
849 }
850
851 pub(crate) async fn locator_get_attribute(
853 &self,
854 selector: &str,
855 name: &str,
856 ) -> Result<Option<String>> {
857 #[derive(Deserialize)]
858 struct GetAttributeResponse {
859 value: Option<String>,
860 }
861
862 let response: GetAttributeResponse = self
863 .channel()
864 .send(
865 "getAttribute",
866 serde_json::json!({
867 "selector": selector,
868 "name": name,
869 "strict": true,
870 "timeout": crate::DEFAULT_TIMEOUT_MS
871 }),
872 )
873 .await?;
874
875 Ok(response.value)
876 }
877
878 pub(crate) async fn locator_is_visible(&self, selector: &str) -> Result<bool> {
880 #[derive(Deserialize)]
881 struct IsVisibleResponse {
882 value: bool,
883 }
884
885 let response: IsVisibleResponse = self
886 .channel()
887 .send(
888 "isVisible",
889 serde_json::json!({
890 "selector": selector,
891 "strict": true,
892 "timeout": crate::DEFAULT_TIMEOUT_MS
893 }),
894 )
895 .await?;
896
897 Ok(response.value)
898 }
899
900 pub(crate) async fn locator_is_enabled(&self, selector: &str) -> Result<bool> {
902 #[derive(Deserialize)]
903 struct IsEnabledResponse {
904 value: bool,
905 }
906
907 let response: IsEnabledResponse = self
908 .channel()
909 .send(
910 "isEnabled",
911 serde_json::json!({
912 "selector": selector,
913 "strict": true,
914 "timeout": crate::DEFAULT_TIMEOUT_MS
915 }),
916 )
917 .await?;
918
919 Ok(response.value)
920 }
921
922 pub(crate) async fn locator_is_checked(&self, selector: &str) -> Result<bool> {
924 #[derive(Deserialize)]
925 struct IsCheckedResponse {
926 value: bool,
927 }
928
929 let response: IsCheckedResponse = self
930 .channel()
931 .send(
932 "isChecked",
933 serde_json::json!({
934 "selector": selector,
935 "strict": true,
936 "timeout": crate::DEFAULT_TIMEOUT_MS
937 }),
938 )
939 .await?;
940
941 Ok(response.value)
942 }
943
944 pub(crate) async fn locator_is_editable(&self, selector: &str) -> Result<bool> {
946 #[derive(Deserialize)]
947 struct IsEditableResponse {
948 value: bool,
949 }
950
951 let response: IsEditableResponse = self
952 .channel()
953 .send(
954 "isEditable",
955 serde_json::json!({
956 "selector": selector,
957 "strict": true,
958 "timeout": crate::DEFAULT_TIMEOUT_MS
959 }),
960 )
961 .await?;
962
963 Ok(response.value)
964 }
965
966 pub(crate) async fn locator_is_hidden(&self, selector: &str) -> Result<bool> {
968 #[derive(Deserialize)]
969 struct IsHiddenResponse {
970 value: bool,
971 }
972
973 let response: IsHiddenResponse = self
974 .channel()
975 .send(
976 "isHidden",
977 serde_json::json!({
978 "selector": selector,
979 "strict": true,
980 "timeout": crate::DEFAULT_TIMEOUT_MS
981 }),
982 )
983 .await?;
984
985 Ok(response.value)
986 }
987
988 pub(crate) async fn locator_is_disabled(&self, selector: &str) -> Result<bool> {
990 #[derive(Deserialize)]
991 struct IsDisabledResponse {
992 value: bool,
993 }
994
995 let response: IsDisabledResponse = self
996 .channel()
997 .send(
998 "isDisabled",
999 serde_json::json!({
1000 "selector": selector,
1001 "strict": true,
1002 "timeout": crate::DEFAULT_TIMEOUT_MS
1003 }),
1004 )
1005 .await?;
1006
1007 Ok(response.value)
1008 }
1009
1010 pub(crate) async fn locator_is_focused(&self, selector: &str) -> Result<bool> {
1016 #[derive(Deserialize)]
1017 struct EvaluateResult {
1018 value: serde_json::Value,
1019 }
1020
1021 let script = r#"selector => {
1024 const elements = document.querySelectorAll(selector);
1025 if (elements.length === 0) return false;
1026 const element = elements[0];
1027 return document.activeElement === element;
1028 }"#;
1029
1030 let params = serde_json::json!({
1031 "expression": script,
1032 "arg": {
1033 "value": {"s": selector},
1034 "handles": []
1035 }
1036 });
1037
1038 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1039
1040 if let serde_json::Value::Object(map) = &result.value
1042 && let Some(b) = map.get("b").and_then(|v| v.as_bool())
1043 {
1044 return Ok(b);
1045 }
1046
1047 Ok(result.value.to_string().to_lowercase().contains("true"))
1049 }
1050
1051 pub(crate) async fn locator_click(
1055 &self,
1056 selector: &str,
1057 options: Option<crate::protocol::ClickOptions>,
1058 ) -> Result<()> {
1059 let mut params = serde_json::json!({
1060 "selector": selector,
1061 "strict": true
1062 });
1063
1064 if let Some(opts) = options {
1065 let opts_json = opts.to_json();
1066 if let Some(obj) = params.as_object_mut()
1067 && let Some(opts_obj) = opts_json.as_object()
1068 {
1069 obj.extend(opts_obj.clone());
1070 }
1071 } else {
1072 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1073 }
1074
1075 self.channel()
1076 .send_no_result("click", params)
1077 .await
1078 .map_err(|e| match e {
1079 Error::Timeout(msg) => {
1080 Error::Timeout(format!("{} (selector: '{}')", msg, selector))
1081 }
1082 other => other,
1083 })
1084 }
1085
1086 pub(crate) async fn locator_dblclick(
1088 &self,
1089 selector: &str,
1090 options: Option<crate::protocol::ClickOptions>,
1091 ) -> Result<()> {
1092 let mut params = serde_json::json!({
1093 "selector": selector,
1094 "strict": true
1095 });
1096
1097 if let Some(opts) = options {
1098 let opts_json = opts.to_json();
1099 if let Some(obj) = params.as_object_mut()
1100 && let Some(opts_obj) = opts_json.as_object()
1101 {
1102 obj.extend(opts_obj.clone());
1103 }
1104 } else {
1105 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1106 }
1107
1108 self.channel().send_no_result("dblclick", params).await
1109 }
1110
1111 pub(crate) async fn locator_fill(
1113 &self,
1114 selector: &str,
1115 text: &str,
1116 options: Option<crate::protocol::FillOptions>,
1117 ) -> Result<()> {
1118 let mut params = serde_json::json!({
1119 "selector": selector,
1120 "value": text,
1121 "strict": true
1122 });
1123
1124 if let Some(opts) = options {
1125 let opts_json = opts.to_json();
1126 if let Some(obj) = params.as_object_mut()
1127 && let Some(opts_obj) = opts_json.as_object()
1128 {
1129 obj.extend(opts_obj.clone());
1130 }
1131 } else {
1132 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1133 }
1134
1135 self.channel().send_no_result("fill", params).await
1136 }
1137
1138 pub(crate) async fn locator_clear(
1140 &self,
1141 selector: &str,
1142 options: Option<crate::protocol::FillOptions>,
1143 ) -> Result<()> {
1144 let mut params = serde_json::json!({
1145 "selector": selector,
1146 "value": "",
1147 "strict": true
1148 });
1149
1150 if let Some(opts) = options {
1151 let opts_json = opts.to_json();
1152 if let Some(obj) = params.as_object_mut()
1153 && let Some(opts_obj) = opts_json.as_object()
1154 {
1155 obj.extend(opts_obj.clone());
1156 }
1157 } else {
1158 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1159 }
1160
1161 self.channel().send_no_result("fill", params).await
1162 }
1163
1164 pub(crate) async fn locator_press(
1166 &self,
1167 selector: &str,
1168 key: &str,
1169 options: Option<crate::protocol::PressOptions>,
1170 ) -> Result<()> {
1171 let mut params = serde_json::json!({
1172 "selector": selector,
1173 "key": key,
1174 "strict": true
1175 });
1176
1177 if let Some(opts) = options {
1178 let opts_json = opts.to_json();
1179 if let Some(obj) = params.as_object_mut()
1180 && let Some(opts_obj) = opts_json.as_object()
1181 {
1182 obj.extend(opts_obj.clone());
1183 }
1184 } else {
1185 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1186 }
1187
1188 self.channel().send_no_result("press", params).await
1189 }
1190
1191 pub(crate) async fn locator_focus(&self, selector: &str) -> Result<()> {
1193 self.channel()
1194 .send_no_result(
1195 "focus",
1196 serde_json::json!({
1197 "selector": selector,
1198 "strict": true,
1199 "timeout": crate::DEFAULT_TIMEOUT_MS
1200 }),
1201 )
1202 .await
1203 }
1204
1205 pub(crate) async fn locator_blur(&self, selector: &str) -> Result<()> {
1207 self.channel()
1208 .send_no_result(
1209 "blur",
1210 serde_json::json!({
1211 "selector": selector,
1212 "strict": true,
1213 "timeout": crate::DEFAULT_TIMEOUT_MS
1214 }),
1215 )
1216 .await
1217 }
1218
1219 pub(crate) async fn locator_press_sequentially(
1223 &self,
1224 selector: &str,
1225 text: &str,
1226 options: Option<crate::protocol::PressSequentiallyOptions>,
1227 ) -> Result<()> {
1228 let mut params = serde_json::json!({
1229 "selector": selector,
1230 "text": text,
1231 "strict": true
1232 });
1233
1234 if let Some(opts) = options {
1235 let opts_json = opts.to_json();
1236 if let Some(obj) = params.as_object_mut()
1237 && let Some(opts_obj) = opts_json.as_object()
1238 {
1239 obj.extend(opts_obj.clone());
1240 }
1241 } else {
1242 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1243 }
1244
1245 self.channel().send_no_result("type", params).await
1246 }
1247
1248 pub(crate) async fn locator_all_inner_texts(&self, selector: &str) -> Result<Vec<String>> {
1250 #[derive(serde::Deserialize)]
1251 struct EvaluateResult {
1252 value: serde_json::Value,
1253 }
1254
1255 let params = serde_json::json!({
1258 "selector": selector,
1259 "expression": "ee => ee.map(e => e.innerText)",
1260 "isFunction": true,
1261 "arg": {
1262 "value": {"v": "null"},
1263 "handles": []
1264 }
1265 });
1266
1267 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1268
1269 Self::parse_string_array(result.value)
1270 }
1271
1272 pub(crate) async fn locator_all_text_contents(&self, selector: &str) -> Result<Vec<String>> {
1274 #[derive(serde::Deserialize)]
1275 struct EvaluateResult {
1276 value: serde_json::Value,
1277 }
1278
1279 let params = serde_json::json!({
1282 "selector": selector,
1283 "expression": "ee => ee.map(e => e.textContent || '')",
1284 "isFunction": true,
1285 "arg": {
1286 "value": {"v": "null"},
1287 "handles": []
1288 }
1289 });
1290
1291 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1292
1293 Self::parse_string_array(result.value)
1294 }
1295
1296 pub(crate) async fn locator_tap(
1303 &self,
1304 selector: &str,
1305 options: Option<crate::protocol::TapOptions>,
1306 ) -> Result<()> {
1307 let mut params = serde_json::json!({
1308 "selector": selector,
1309 "strict": true
1310 });
1311
1312 if let Some(opts) = options {
1313 let opts_json = opts.to_json();
1314 if let Some(obj) = params.as_object_mut()
1315 && let Some(opts_obj) = opts_json.as_object()
1316 {
1317 obj.extend(opts_obj.clone());
1318 }
1319 } else {
1320 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1321 }
1322
1323 self.channel().send_no_result("tap", params).await
1324 }
1325
1326 pub(crate) async fn locator_drag_to(
1332 &self,
1333 source_selector: &str,
1334 target_selector: &str,
1335 options: Option<crate::protocol::DragToOptions>,
1336 ) -> Result<()> {
1337 let mut params = serde_json::json!({
1338 "source": source_selector,
1339 "target": target_selector,
1340 "strict": true
1341 });
1342
1343 if let Some(opts) = options {
1344 let opts_json = opts.to_json();
1345 if let Some(obj) = params.as_object_mut()
1346 && let Some(opts_obj) = opts_json.as_object()
1347 {
1348 obj.extend(opts_obj.clone());
1349 }
1350 } else {
1351 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1352 }
1353
1354 self.channel().send_no_result("dragAndDrop", params).await
1355 }
1356
1357 pub(crate) async fn locator_wait_for(
1364 &self,
1365 selector: &str,
1366 options: Option<crate::protocol::WaitForOptions>,
1367 ) -> Result<()> {
1368 let mut params = serde_json::json!({
1369 "selector": selector,
1370 "strict": true
1371 });
1372
1373 if let Some(opts) = options {
1374 let opts_json = opts.to_json();
1375 if let Some(obj) = params.as_object_mut()
1376 && let Some(opts_obj) = opts_json.as_object()
1377 {
1378 obj.extend(opts_obj.clone());
1379 }
1380 } else {
1381 params["state"] = serde_json::json!("visible");
1383 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1384 }
1385
1386 let _: serde_json::Value = self.channel().send("waitForSelector", params).await?;
1388 Ok(())
1389 }
1390
1391 pub(crate) async fn locator_evaluate<T: serde::Serialize>(
1398 &self,
1399 selector: &str,
1400 expression: &str,
1401 arg: Option<T>,
1402 ) -> Result<serde_json::Value> {
1403 let serialized_arg = match arg {
1404 Some(a) => serialize_argument(&a),
1405 None => serialize_null(),
1406 };
1407
1408 let params = serde_json::json!({
1409 "selector": selector,
1410 "expression": expression,
1411 "isFunction": true,
1412 "arg": serialized_arg,
1413 "strict": true
1414 });
1415
1416 #[derive(Deserialize)]
1417 struct EvaluateResult {
1418 value: serde_json::Value,
1419 }
1420
1421 let result: EvaluateResult = self.channel().send("evalOnSelector", params).await?;
1422 Ok(parse_result(&result.value))
1423 }
1424
1425 pub(crate) async fn locator_evaluate_all<T: serde::Serialize>(
1432 &self,
1433 selector: &str,
1434 expression: &str,
1435 arg: Option<T>,
1436 ) -> Result<serde_json::Value> {
1437 let serialized_arg = match arg {
1438 Some(a) => serialize_argument(&a),
1439 None => serialize_null(),
1440 };
1441
1442 let params = serde_json::json!({
1443 "selector": selector,
1444 "expression": expression,
1445 "isFunction": true,
1446 "arg": serialized_arg
1447 });
1448
1449 #[derive(Deserialize)]
1450 struct EvaluateResult {
1451 value: serde_json::Value,
1452 }
1453
1454 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1455 Ok(parse_result(&result.value))
1456 }
1457
1458 fn parse_string_array(value: serde_json::Value) -> Result<Vec<String>> {
1463 let array = if let Some(arr) = value.get("a").and_then(|v| v.as_array()) {
1465 arr.clone()
1466 } else if let Some(arr) = value.as_array() {
1467 arr.clone()
1468 } else {
1469 return Ok(Vec::new());
1470 };
1471
1472 let mut result = Vec::with_capacity(array.len());
1473 for item in &array {
1474 let s = if let Some(s) = item.get("s").and_then(|v| v.as_str()) {
1476 s.to_string()
1477 } else if let Some(s) = item.as_str() {
1478 s.to_string()
1479 } else if item.is_null() {
1480 String::new()
1481 } else {
1482 item.to_string()
1483 };
1484 result.push(s);
1485 }
1486 Ok(result)
1487 }
1488
1489 pub(crate) async fn locator_check(
1490 &self,
1491 selector: &str,
1492 options: Option<crate::protocol::CheckOptions>,
1493 ) -> Result<()> {
1494 let mut params = serde_json::json!({
1495 "selector": selector,
1496 "strict": true
1497 });
1498
1499 if let Some(opts) = options {
1500 let opts_json = opts.to_json();
1501 if let Some(obj) = params.as_object_mut()
1502 && let Some(opts_obj) = opts_json.as_object()
1503 {
1504 obj.extend(opts_obj.clone());
1505 }
1506 } else {
1507 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1508 }
1509
1510 self.channel().send_no_result("check", params).await
1511 }
1512
1513 pub(crate) async fn locator_uncheck(
1514 &self,
1515 selector: &str,
1516 options: Option<crate::protocol::CheckOptions>,
1517 ) -> Result<()> {
1518 let mut params = serde_json::json!({
1519 "selector": selector,
1520 "strict": true
1521 });
1522
1523 if let Some(opts) = options {
1524 let opts_json = opts.to_json();
1525 if let Some(obj) = params.as_object_mut()
1526 && let Some(opts_obj) = opts_json.as_object()
1527 {
1528 obj.extend(opts_obj.clone());
1529 }
1530 } else {
1531 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1532 }
1533
1534 self.channel().send_no_result("uncheck", params).await
1535 }
1536
1537 pub(crate) async fn locator_hover(
1538 &self,
1539 selector: &str,
1540 options: Option<crate::protocol::HoverOptions>,
1541 ) -> Result<()> {
1542 let mut params = serde_json::json!({
1543 "selector": selector,
1544 "strict": true
1545 });
1546
1547 if let Some(opts) = options {
1548 let opts_json = opts.to_json();
1549 if let Some(obj) = params.as_object_mut()
1550 && let Some(opts_obj) = opts_json.as_object()
1551 {
1552 obj.extend(opts_obj.clone());
1553 }
1554 } else {
1555 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1556 }
1557
1558 self.channel().send_no_result("hover", params).await
1559 }
1560
1561 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
1562 #[derive(Deserialize)]
1563 struct InputValueResponse {
1564 value: String,
1565 }
1566
1567 let response: InputValueResponse = self
1568 .channel()
1569 .send(
1570 "inputValue",
1571 serde_json::json!({
1572 "selector": selector,
1573 "strict": true,
1574 "timeout": crate::DEFAULT_TIMEOUT_MS }),
1576 )
1577 .await?;
1578
1579 Ok(response.value)
1580 }
1581
1582 pub(crate) async fn locator_select_option(
1583 &self,
1584 selector: &str,
1585 value: crate::protocol::SelectOption,
1586 options: Option<crate::protocol::SelectOptions>,
1587 ) -> Result<Vec<String>> {
1588 #[derive(Deserialize)]
1589 struct SelectOptionResponse {
1590 values: Vec<String>,
1591 }
1592
1593 let mut params = serde_json::json!({
1594 "selector": selector,
1595 "strict": true,
1596 "options": [value.to_json()]
1597 });
1598
1599 if let Some(opts) = options {
1600 let opts_json = opts.to_json();
1601 if let Some(obj) = params.as_object_mut()
1602 && let Some(opts_obj) = opts_json.as_object()
1603 {
1604 obj.extend(opts_obj.clone());
1605 }
1606 } else {
1607 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1609 }
1610
1611 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1612
1613 Ok(response.values)
1614 }
1615
1616 pub(crate) async fn locator_select_option_multiple(
1617 &self,
1618 selector: &str,
1619 values: Vec<crate::protocol::SelectOption>,
1620 options: Option<crate::protocol::SelectOptions>,
1621 ) -> Result<Vec<String>> {
1622 #[derive(Deserialize)]
1623 struct SelectOptionResponse {
1624 values: Vec<String>,
1625 }
1626
1627 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
1628
1629 let mut params = serde_json::json!({
1630 "selector": selector,
1631 "strict": true,
1632 "options": values_array
1633 });
1634
1635 if let Some(opts) = options {
1636 let opts_json = opts.to_json();
1637 if let Some(obj) = params.as_object_mut()
1638 && let Some(opts_obj) = opts_json.as_object()
1639 {
1640 obj.extend(opts_obj.clone());
1641 }
1642 } else {
1643 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1645 }
1646
1647 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1648
1649 Ok(response.values)
1650 }
1651
1652 pub(crate) async fn locator_set_input_files(
1653 &self,
1654 selector: &str,
1655 file: &std::path::PathBuf,
1656 ) -> Result<()> {
1657 use base64::{Engine as _, engine::general_purpose};
1658 use std::io::Read;
1659
1660 let mut file_handle = std::fs::File::open(file)?;
1662 let mut buffer = Vec::new();
1663 file_handle.read_to_end(&mut buffer)?;
1664
1665 let base64_content = general_purpose::STANDARD.encode(&buffer);
1667
1668 let file_name = file
1670 .file_name()
1671 .and_then(|n| n.to_str())
1672 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
1673
1674 self.channel()
1675 .send_no_result(
1676 "setInputFiles",
1677 serde_json::json!({
1678 "selector": selector,
1679 "strict": true,
1680 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
1682 "name": file_name,
1683 "buffer": base64_content
1684 }]
1685 }),
1686 )
1687 .await
1688 }
1689
1690 pub(crate) async fn locator_set_input_files_multiple(
1691 &self,
1692 selector: &str,
1693 files: &[&std::path::PathBuf],
1694 ) -> Result<()> {
1695 use base64::{Engine as _, engine::general_purpose};
1696 use std::io::Read;
1697
1698 if files.is_empty() {
1700 return self
1701 .channel()
1702 .send_no_result(
1703 "setInputFiles",
1704 serde_json::json!({
1705 "selector": selector,
1706 "strict": true,
1707 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
1709 }),
1710 )
1711 .await;
1712 }
1713
1714 let mut file_objects = Vec::new();
1716 for file_path in files {
1717 let mut file_handle = std::fs::File::open(file_path)?;
1718 let mut buffer = Vec::new();
1719 file_handle.read_to_end(&mut buffer)?;
1720
1721 let base64_content = general_purpose::STANDARD.encode(&buffer);
1722 let file_name = file_path
1723 .file_name()
1724 .and_then(|n| n.to_str())
1725 .ok_or_else(|| {
1726 crate::error::Error::InvalidArgument("Invalid file path".to_string())
1727 })?;
1728
1729 file_objects.push(serde_json::json!({
1730 "name": file_name,
1731 "buffer": base64_content
1732 }));
1733 }
1734
1735 self.channel()
1736 .send_no_result(
1737 "setInputFiles",
1738 serde_json::json!({
1739 "selector": selector,
1740 "strict": true,
1741 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": file_objects
1743 }),
1744 )
1745 .await
1746 }
1747
1748 pub(crate) async fn locator_set_input_files_payload(
1749 &self,
1750 selector: &str,
1751 file: crate::protocol::FilePayload,
1752 ) -> Result<()> {
1753 use base64::{Engine as _, engine::general_purpose};
1754
1755 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1757
1758 self.channel()
1759 .send_no_result(
1760 "setInputFiles",
1761 serde_json::json!({
1762 "selector": selector,
1763 "strict": true,
1764 "timeout": crate::DEFAULT_TIMEOUT_MS,
1765 "payloads": [{
1766 "name": file.name,
1767 "mimeType": file.mime_type,
1768 "buffer": base64_content
1769 }]
1770 }),
1771 )
1772 .await
1773 }
1774
1775 pub(crate) async fn locator_set_input_files_payload_multiple(
1776 &self,
1777 selector: &str,
1778 files: &[crate::protocol::FilePayload],
1779 ) -> Result<()> {
1780 use base64::{Engine as _, engine::general_purpose};
1781
1782 if files.is_empty() {
1784 return self
1785 .channel()
1786 .send_no_result(
1787 "setInputFiles",
1788 serde_json::json!({
1789 "selector": selector,
1790 "strict": true,
1791 "timeout": crate::DEFAULT_TIMEOUT_MS,
1792 "payloads": []
1793 }),
1794 )
1795 .await;
1796 }
1797
1798 let file_objects: Vec<_> = files
1800 .iter()
1801 .map(|file| {
1802 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1803 serde_json::json!({
1804 "name": file.name,
1805 "mimeType": file.mime_type,
1806 "buffer": base64_content
1807 })
1808 })
1809 .collect();
1810
1811 self.channel()
1812 .send_no_result(
1813 "setInputFiles",
1814 serde_json::json!({
1815 "selector": selector,
1816 "strict": true,
1817 "timeout": crate::DEFAULT_TIMEOUT_MS,
1818 "payloads": file_objects
1819 }),
1820 )
1821 .await
1822 }
1823
1824 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
1828 let params = serde_json::json!({
1829 "expression": expression,
1830 "arg": {
1831 "value": {"v": "null"},
1832 "handles": []
1833 }
1834 });
1835
1836 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
1837 Ok(())
1838 }
1839
1840 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
1852 let params = serde_json::json!({
1853 "expression": expression,
1854 "arg": {
1855 "value": {"v": "null"},
1856 "handles": []
1857 }
1858 });
1859
1860 #[derive(Deserialize)]
1861 struct EvaluateResult {
1862 value: serde_json::Value,
1863 }
1864
1865 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1866
1867 match &result.value {
1874 Value::Object(map) => {
1875 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
1876 Ok(s.to_string())
1878 } else if let Some(n) = map.get("n") {
1879 Ok(n.to_string())
1881 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
1882 Ok(b.to_string())
1884 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
1885 Ok(v.to_string())
1887 } else {
1888 Ok(result.value.to_string())
1890 }
1891 }
1892 _ => {
1893 Ok(result.value.to_string())
1895 }
1896 }
1897 }
1898
1899 pub async fn evaluate<T: serde::Serialize>(
1939 &self,
1940 expression: &str,
1941 arg: Option<&T>,
1942 ) -> Result<Value> {
1943 let serialized_arg = match arg {
1945 Some(a) => serialize_argument(a),
1946 None => serialize_null(),
1947 };
1948
1949 let params = serde_json::json!({
1951 "expression": expression,
1952 "arg": serialized_arg
1953 });
1954
1955 #[derive(Deserialize)]
1957 struct EvaluateResult {
1958 value: serde_json::Value,
1959 }
1960
1961 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1962
1963 Ok(parse_result(&result.value))
1965 }
1966
1967 pub async fn add_style_tag(
2007 &self,
2008 options: crate::protocol::page::AddStyleTagOptions,
2009 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2010 options.validate()?;
2012
2013 let mut params = serde_json::json!({});
2015
2016 if let Some(content) = &options.content {
2017 params["content"] = serde_json::json!(content);
2018 }
2019
2020 if let Some(url) = &options.url {
2021 params["url"] = serde_json::json!(url);
2022 }
2023
2024 if let Some(path) = &options.path {
2025 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2027 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
2028 })?;
2029 params["content"] = serde_json::json!(css_content);
2030 }
2031
2032 #[derive(Deserialize)]
2033 struct AddStyleTagResponse {
2034 element: serde_json::Value,
2035 }
2036
2037 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
2038
2039 let guid = response.element["guid"].as_str().ok_or_else(|| {
2040 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
2041 })?;
2042
2043 let connection = self.base.connection();
2044 let handle: crate::protocol::ElementHandle = connection
2045 .get_typed::<crate::protocol::ElementHandle>(guid)
2046 .await?;
2047
2048 Ok(Arc::new(handle))
2049 }
2050
2051 pub(crate) async fn locator_dispatch_event(
2059 &self,
2060 selector: &str,
2061 type_: &str,
2062 event_init: Option<serde_json::Value>,
2063 ) -> Result<()> {
2064 let event_init_serialized = match event_init {
2067 Some(v) => serialize_argument(&v),
2068 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
2069 };
2070
2071 let params = serde_json::json!({
2072 "selector": selector,
2073 "type": type_,
2074 "eventInit": event_init_serialized,
2075 "strict": true,
2076 "timeout": crate::DEFAULT_TIMEOUT_MS
2077 });
2078
2079 self.channel().send_no_result("dispatchEvent", params).await
2080 }
2081
2082 pub(crate) async fn locator_bounding_box(
2092 &self,
2093 selector: &str,
2094 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
2095 let element = self.query_selector(selector).await?;
2096 match element {
2097 Some(handle) => handle.bounding_box().await,
2098 None => Ok(None),
2099 }
2100 }
2101
2102 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
2109 let element = self.query_selector(selector).await?;
2110 match element {
2111 Some(handle) => handle.scroll_into_view_if_needed().await,
2112 None => Err(crate::error::Error::ElementNotFound(format!(
2113 "Element not found: {}",
2114 selector
2115 ))),
2116 }
2117 }
2118
2119 pub async fn add_script_tag(
2129 &self,
2130 options: crate::protocol::page::AddScriptTagOptions,
2131 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2132 options.validate()?;
2134
2135 let mut params = serde_json::json!({});
2137
2138 if let Some(content) = &options.content {
2139 params["content"] = serde_json::json!(content);
2140 }
2141
2142 if let Some(url) = &options.url {
2143 params["url"] = serde_json::json!(url);
2144 }
2145
2146 if let Some(path) = &options.path {
2147 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2149 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
2150 })?;
2151 params["content"] = serde_json::json!(js_content);
2152 }
2153
2154 if let Some(type_) = &options.type_ {
2155 params["type"] = serde_json::json!(type_);
2156 }
2157
2158 #[derive(Deserialize)]
2159 struct AddScriptTagResponse {
2160 element: serde_json::Value,
2161 }
2162
2163 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
2164
2165 let guid = response.element["guid"].as_str().ok_or_else(|| {
2166 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
2167 })?;
2168
2169 let connection = self.base.connection();
2170 let handle: crate::protocol::ElementHandle = connection
2171 .get_typed::<crate::protocol::ElementHandle>(guid)
2172 .await?;
2173
2174 Ok(Arc::new(handle))
2175 }
2176}
2177
2178impl ChannelOwner for Frame {
2179 fn guid(&self) -> &str {
2180 self.base.guid()
2181 }
2182
2183 fn type_name(&self) -> &str {
2184 self.base.type_name()
2185 }
2186
2187 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
2188 self.base.parent()
2189 }
2190
2191 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
2192 self.base.connection()
2193 }
2194
2195 fn initializer(&self) -> &Value {
2196 self.base.initializer()
2197 }
2198
2199 fn channel(&self) -> &Channel {
2200 self.base.channel()
2201 }
2202
2203 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
2204 self.base.dispose(reason)
2205 }
2206
2207 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
2208 self.base.adopt(child)
2209 }
2210
2211 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
2212 self.base.add_child(guid, child)
2213 }
2214
2215 fn remove_child(&self, guid: &str) {
2216 self.base.remove_child(guid)
2217 }
2218
2219 fn on_event(&self, method: &str, params: Value) {
2220 match method {
2221 "navigated" => {
2222 if let Some(url_value) = params.get("url")
2224 && let Some(url_str) = url_value.as_str()
2225 {
2226 if let Ok(mut url) = self.url.write() {
2228 *url = url_str.to_string();
2229 }
2230 }
2231 }
2232 "detached" => {
2233 if let Ok(mut flag) = self.is_detached.write() {
2235 *flag = true;
2236 }
2237 }
2238 _ => {
2239 }
2242 }
2243 }
2244
2245 fn was_collected(&self) -> bool {
2246 self.base.was_collected()
2247 }
2248
2249 fn as_any(&self) -> &dyn Any {
2250 self
2251 }
2252}
2253
2254impl std::fmt::Debug for Frame {
2255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2256 f.debug_struct("Frame").field("guid", &self.guid()).finish()
2257 }
2258}
2259
2260fn glob_match(pattern: &str, text: &str) -> bool {
2265 let regex_str = pattern
2266 .replace('.', "\\.")
2267 .replace("**", "\x00") .replace('*', "[^/]*")
2269 .replace('\x00', ".*"); let regex_str = format!("^{}$", regex_str);
2271 regex::Regex::new(®ex_str)
2272 .map(|re| re.is_match(text))
2273 .unwrap_or(false)
2274}