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 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
239 pub async fn evaluate_handle(
240 &self,
241 expression: &str,
242 ) -> Result<Arc<crate::protocol::ElementHandle>> {
243 let params = serde_json::json!({
244 "expression": expression,
245 "isFunction": false,
246 "arg": {"value": {"v": "undefined"}, "handles": []}
247 });
248
249 #[derive(Deserialize)]
251 struct HandleRef {
252 guid: String,
253 }
254 #[derive(Deserialize)]
255 struct EvaluateHandleResponse {
256 handle: HandleRef,
257 }
258
259 let response: EvaluateHandleResponse = self
260 .channel()
261 .send("evaluateExpressionHandle", params)
262 .await?;
263
264 let guid = &response.handle.guid;
265
266 let connection = self.base.connection();
268 let mut attempts = 0;
269 let max_attempts = 20;
270 let handle = loop {
271 match connection
272 .get_typed::<crate::protocol::ElementHandle>(guid)
273 .await
274 {
275 Ok(h) => break h,
276 Err(_) if attempts < max_attempts => {
277 attempts += 1;
278 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
279 }
280 Err(e) => return Err(e),
281 }
282 };
283
284 Ok(Arc::new(handle))
285 }
286
287 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
310 pub async fn evaluate_handle_js(
311 &self,
312 expression: &str,
313 ) -> Result<std::sync::Arc<crate::protocol::JSHandle>> {
314 let trimmed = expression.trim();
318 let is_function = trimmed.starts_with("(")
319 || trimmed.starts_with("function")
320 || trimmed.starts_with("async ");
321
322 let params = serde_json::json!({
323 "expression": expression,
324 "isFunction": is_function,
325 "arg": {"value": {"v": "undefined"}, "handles": []}
326 });
327
328 #[derive(Deserialize)]
330 struct HandleRef {
331 guid: String,
332 }
333 #[derive(Deserialize)]
334 struct EvaluateHandleResponse {
335 handle: HandleRef,
336 }
337
338 let response: EvaluateHandleResponse = self
339 .channel()
340 .send("evaluateExpressionHandle", params)
341 .await?;
342
343 let guid = &response.handle.guid;
344
345 let connection = self.base.connection();
347 let mut attempts = 0;
348 let max_attempts = 20;
349 let handle = loop {
350 match connection
351 .get_typed::<crate::protocol::JSHandle>(guid)
352 .await
353 {
354 Ok(h) => break h,
355 Err(_) if attempts < max_attempts => {
356 attempts += 1;
357 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
358 }
359 Err(e) => return Err(e),
360 }
361 };
362
363 Ok(std::sync::Arc::new(handle))
364 }
365
366 pub fn locator(&self, selector: &str) -> crate::protocol::Locator {
381 let page = self
382 .page()
383 .expect("Frame::locator() called before set_page(); call page.main_frame() first");
384 crate::protocol::Locator::new(Arc::new(self.clone()), selector.to_string(), page)
385 }
386
387 pub fn get_by_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
391 self.locator(&crate::protocol::locator::get_by_text_selector(text, exact))
392 }
393
394 pub fn get_by_label(&self, text: &str, exact: bool) -> crate::protocol::Locator {
398 self.locator(&crate::protocol::locator::get_by_label_selector(
399 text, exact,
400 ))
401 }
402
403 pub fn get_by_placeholder(&self, text: &str, exact: bool) -> crate::protocol::Locator {
407 self.locator(&crate::protocol::locator::get_by_placeholder_selector(
408 text, exact,
409 ))
410 }
411
412 pub fn get_by_alt_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
416 self.locator(&crate::protocol::locator::get_by_alt_text_selector(
417 text, exact,
418 ))
419 }
420
421 pub fn get_by_title(&self, text: &str, exact: bool) -> crate::protocol::Locator {
425 self.locator(&crate::protocol::locator::get_by_title_selector(
426 text, exact,
427 ))
428 }
429
430 pub fn get_by_test_id(&self, test_id: &str) -> crate::protocol::Locator {
437 use crate::server::channel_owner::ChannelOwner;
438 let attr = self.connection().selectors().test_id_attribute();
439 self.locator(&crate::protocol::locator::get_by_test_id_selector_with_attr(test_id, &attr))
440 }
441
442 pub fn get_by_role(
446 &self,
447 role: crate::protocol::locator::AriaRole,
448 options: Option<crate::protocol::locator::GetByRoleOptions>,
449 ) -> crate::protocol::Locator {
450 self.locator(&crate::protocol::locator::get_by_role_selector(
451 role, options,
452 ))
453 }
454
455 fn channel(&self) -> &Channel {
457 self.base.channel()
458 }
459
460 pub fn url(&self) -> String {
466 self.url.read().unwrap().clone()
467 }
468
469 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid(), url = %url, status = tracing::field::Empty))]
483 pub async fn goto(&self, url: &str, options: Option<GotoOptions>) -> Result<Option<Response>> {
484 let mut params = serde_json::json!({
486 "url": url,
487 });
488
489 if let Some(opts) = options {
491 if let Some(timeout) = opts.timeout {
492 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
493 } else {
494 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
496 }
497 if let Some(wait_until) = opts.wait_until {
498 params["waitUntil"] = serde_json::json!(wait_until.as_str());
499 }
500 } else {
501 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
503 }
504
505 #[derive(Deserialize)]
508 struct GotoResponse {
509 response: Option<ResponseReference>,
510 }
511
512 #[derive(Deserialize)]
513 struct ResponseReference {
514 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
515 guid: Arc<str>,
516 }
517
518 let goto_result: GotoResponse = self.channel().send("goto", params).await?;
519
520 if let Some(response_ref) = goto_result.response {
522 let response_arc = {
528 let mut attempts = 0;
529 let max_attempts = 20; loop {
531 match self.connection().get_object(&response_ref.guid).await {
532 Ok(obj) => break obj,
533 Err(_) if attempts < max_attempts => {
534 attempts += 1;
535 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
536 }
537 Err(e) => return Err(e),
538 }
539 }
540 };
541
542 let initializer = response_arc.initializer();
545
546 let status = initializer["status"].as_u64().ok_or_else(|| {
548 crate::error::Error::ProtocolError("Response missing status".to_string())
549 })? as u16;
550
551 let headers = initializer["headers"]
553 .as_array()
554 .ok_or_else(|| {
555 crate::error::Error::ProtocolError("Response missing headers".to_string())
556 })?
557 .iter()
558 .filter_map(|h| {
559 let name = h["name"].as_str()?;
560 let value = h["value"].as_str()?;
561 Some((name.to_string(), value.to_string()))
562 })
563 .collect();
564
565 tracing::Span::current().record("status", status);
566 Ok(Some(Response::new(
567 initializer["url"]
568 .as_str()
569 .ok_or_else(|| {
570 crate::error::Error::ProtocolError("Response missing url".to_string())
571 })?
572 .to_string(),
573 status,
574 initializer["statusText"].as_str().unwrap_or("").to_string(),
575 headers,
576 Some(response_arc),
577 )))
578 } else {
579 Ok(None)
582 }
583 }
584
585 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
589 pub async fn title(&self) -> Result<String> {
590 #[derive(Deserialize)]
591 struct TitleResponse {
592 value: String,
593 }
594
595 let response: TitleResponse = self.channel().send("title", serde_json::json!({})).await?;
596 Ok(response.value)
597 }
598
599 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
603 pub async fn content(&self) -> Result<String> {
604 #[derive(Deserialize)]
605 struct ContentResponse {
606 value: String,
607 }
608
609 let response: ContentResponse = self
610 .channel()
611 .send("content", serde_json::json!({}))
612 .await?;
613 Ok(response.value)
614 }
615
616 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
620 pub async fn set_content(&self, html: &str, options: Option<GotoOptions>) -> Result<()> {
621 let mut params = serde_json::json!({
622 "html": html,
623 });
624
625 if let Some(opts) = options {
626 if let Some(timeout) = opts.timeout {
627 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
628 } else {
629 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
630 }
631 if let Some(wait_until) = opts.wait_until {
632 params["waitUntil"] = serde_json::json!(wait_until.as_str());
633 }
634 } else {
635 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
636 }
637
638 self.channel().send_no_result("setContent", params).await
639 }
640
641 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
649 pub async fn wait_for_load_state(&self, state: Option<WaitUntil>) -> Result<()> {
650 let target_state = state.unwrap_or(WaitUntil::Load);
651
652 let js_check = match target_state {
653 WaitUntil::Load => "document.readyState === 'complete'",
655 WaitUntil::DomContentLoaded => "document.readyState !== 'loading'",
657 WaitUntil::NetworkIdle => "document.readyState === 'complete'",
660 WaitUntil::Commit => "document.readyState !== 'loading'",
662 };
663
664 let timeout_ms = crate::DEFAULT_TIMEOUT_MS as u64;
665 let poll_interval = std::time::Duration::from_millis(50);
666 let start = std::time::Instant::now();
667
668 loop {
669 #[derive(Deserialize)]
670 struct EvalResponse {
671 value: serde_json::Value,
672 }
673
674 let result: EvalResponse = self
675 .channel()
676 .send(
677 "evaluateExpression",
678 serde_json::json!({
679 "expression": js_check,
680 "isFunction": false,
681 "arg": crate::protocol::serialize_null(),
682 }),
683 )
684 .await?;
685
686 let is_ready = result
688 .value
689 .as_object()
690 .and_then(|m| m.get("b"))
691 .and_then(|v| v.as_bool())
692 .unwrap_or(false);
693
694 if is_ready {
695 return Ok(());
696 }
697
698 if start.elapsed().as_millis() as u64 >= timeout_ms {
699 return Err(crate::error::Error::Timeout(format!(
700 "wait_for_load_state({}) timed out after {}ms",
701 target_state.as_str(),
702 timeout_ms
703 )));
704 }
705
706 tokio::time::sleep(poll_interval).await;
707 }
708 }
709
710 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid(), url = %url))]
717 pub async fn wait_for_url(&self, url: &str, options: Option<GotoOptions>) -> Result<()> {
718 let timeout_ms = options
719 .as_ref()
720 .and_then(|o| o.timeout)
721 .map(|d| d.as_millis() as u64)
722 .unwrap_or(crate::DEFAULT_TIMEOUT_MS as u64);
723
724 let is_glob = url.contains('*');
728
729 let poll_interval = std::time::Duration::from_millis(50);
730 let start = std::time::Instant::now();
731
732 loop {
733 let current_url = self.url();
734
735 let matches = if is_glob {
736 glob_match(url, ¤t_url)
737 } else {
738 current_url == url
739 };
740
741 if matches {
742 if let Some(ref opts) = options
744 && let Some(wait_until) = opts.wait_until
745 {
746 self.wait_for_load_state(Some(wait_until)).await?;
747 }
748 return Ok(());
749 }
750
751 if start.elapsed().as_millis() as u64 >= timeout_ms {
752 return Err(crate::error::Error::Timeout(format!(
753 "wait_for_url({}) timed out after {}ms, current URL: {}",
754 url, timeout_ms, current_url
755 )));
756 }
757
758 tokio::time::sleep(poll_interval).await;
759 }
760 }
761
762 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
766 pub async fn query_selector(
767 &self,
768 selector: &str,
769 ) -> Result<Option<Arc<crate::protocol::ElementHandle>>> {
770 let response: serde_json::Value = self
771 .channel()
772 .send(
773 "querySelector",
774 serde_json::json!({
775 "selector": selector
776 }),
777 )
778 .await?;
779
780 if response.as_object().map(|o| o.is_empty()).unwrap_or(true) {
782 return Ok(None);
783 }
784
785 let element_value = if let Some(elem) = response.get("element") {
787 elem
788 } else if let Some(elem) = response.get("handle") {
789 elem
790 } else {
791 &response
793 };
794
795 if element_value.is_null() {
796 return Ok(None);
797 }
798
799 let guid = element_value["guid"].as_str().ok_or_else(|| {
801 crate::error::Error::ProtocolError("Element GUID missing".to_string())
802 })?;
803
804 let connection = self.base.connection();
806 let handle: crate::protocol::ElementHandle = connection
807 .get_typed::<crate::protocol::ElementHandle>(guid)
808 .await?;
809
810 Ok(Some(Arc::new(handle)))
811 }
812
813 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
817 pub async fn query_selector_all(
818 &self,
819 selector: &str,
820 ) -> Result<Vec<Arc<crate::protocol::ElementHandle>>> {
821 #[derive(Deserialize)]
822 struct QueryAllResponse {
823 elements: Vec<serde_json::Value>,
824 }
825
826 let response: QueryAllResponse = self
827 .channel()
828 .send(
829 "querySelectorAll",
830 serde_json::json!({
831 "selector": selector
832 }),
833 )
834 .await?;
835
836 let connection = self.base.connection();
838 let mut handles = Vec::new();
839
840 for element_value in response.elements {
841 let guid = element_value["guid"].as_str().ok_or_else(|| {
842 crate::error::Error::ProtocolError("Element GUID missing".to_string())
843 })?;
844
845 let handle: crate::protocol::ElementHandle = connection
846 .get_typed::<crate::protocol::ElementHandle>(guid)
847 .await?;
848
849 handles.push(Arc::new(handle));
850 }
851
852 Ok(handles)
853 }
854
855 pub(crate) async fn locator_count(&self, selector: &str) -> Result<usize> {
860 #[derive(Deserialize)]
862 struct QueryAllResponse {
863 elements: Vec<serde_json::Value>,
864 }
865
866 let response: QueryAllResponse = self
867 .channel()
868 .send(
869 "querySelectorAll",
870 serde_json::json!({
871 "selector": selector
872 }),
873 )
874 .await?;
875
876 Ok(response.elements.len())
877 }
878
879 pub(crate) async fn locator_text_content(&self, selector: &str) -> Result<Option<String>> {
881 #[derive(Deserialize)]
882 struct TextContentResponse {
883 value: Option<String>,
884 }
885
886 let response: TextContentResponse = self
887 .channel()
888 .send(
889 "textContent",
890 serde_json::json!({
891 "selector": selector,
892 "strict": true,
893 "timeout": crate::DEFAULT_TIMEOUT_MS
894 }),
895 )
896 .await?;
897
898 Ok(response.value)
899 }
900
901 pub(crate) async fn locator_inner_text(&self, selector: &str) -> Result<String> {
903 #[derive(Deserialize)]
904 struct InnerTextResponse {
905 value: String,
906 }
907
908 let response: InnerTextResponse = self
909 .channel()
910 .send(
911 "innerText",
912 serde_json::json!({
913 "selector": selector,
914 "strict": true,
915 "timeout": crate::DEFAULT_TIMEOUT_MS
916 }),
917 )
918 .await?;
919
920 Ok(response.value)
921 }
922
923 pub(crate) async fn locator_inner_html(&self, selector: &str) -> Result<String> {
925 #[derive(Deserialize)]
926 struct InnerHTMLResponse {
927 value: String,
928 }
929
930 let response: InnerHTMLResponse = self
931 .channel()
932 .send(
933 "innerHTML",
934 serde_json::json!({
935 "selector": selector,
936 "strict": true,
937 "timeout": crate::DEFAULT_TIMEOUT_MS
938 }),
939 )
940 .await?;
941
942 Ok(response.value)
943 }
944
945 pub(crate) async fn locator_get_attribute(
947 &self,
948 selector: &str,
949 name: &str,
950 ) -> Result<Option<String>> {
951 #[derive(Deserialize)]
952 struct GetAttributeResponse {
953 value: Option<String>,
954 }
955
956 let response: GetAttributeResponse = self
957 .channel()
958 .send(
959 "getAttribute",
960 serde_json::json!({
961 "selector": selector,
962 "name": name,
963 "strict": true,
964 "timeout": crate::DEFAULT_TIMEOUT_MS
965 }),
966 )
967 .await?;
968
969 Ok(response.value)
970 }
971
972 pub(crate) async fn locator_is_visible(&self, selector: &str) -> Result<bool> {
974 #[derive(Deserialize)]
975 struct IsVisibleResponse {
976 value: bool,
977 }
978
979 let response: IsVisibleResponse = self
980 .channel()
981 .send(
982 "isVisible",
983 serde_json::json!({
984 "selector": selector,
985 "strict": true,
986 "timeout": crate::DEFAULT_TIMEOUT_MS
987 }),
988 )
989 .await?;
990
991 Ok(response.value)
992 }
993
994 pub(crate) async fn locator_is_enabled(&self, selector: &str) -> Result<bool> {
996 #[derive(Deserialize)]
997 struct IsEnabledResponse {
998 value: bool,
999 }
1000
1001 let response: IsEnabledResponse = self
1002 .channel()
1003 .send(
1004 "isEnabled",
1005 serde_json::json!({
1006 "selector": selector,
1007 "strict": true,
1008 "timeout": crate::DEFAULT_TIMEOUT_MS
1009 }),
1010 )
1011 .await?;
1012
1013 Ok(response.value)
1014 }
1015
1016 pub(crate) async fn locator_is_checked(&self, selector: &str) -> Result<bool> {
1018 #[derive(Deserialize)]
1019 struct IsCheckedResponse {
1020 value: bool,
1021 }
1022
1023 let response: IsCheckedResponse = self
1024 .channel()
1025 .send(
1026 "isChecked",
1027 serde_json::json!({
1028 "selector": selector,
1029 "strict": true,
1030 "timeout": crate::DEFAULT_TIMEOUT_MS
1031 }),
1032 )
1033 .await?;
1034
1035 Ok(response.value)
1036 }
1037
1038 pub(crate) async fn locator_is_editable(&self, selector: &str) -> Result<bool> {
1040 #[derive(Deserialize)]
1041 struct IsEditableResponse {
1042 value: bool,
1043 }
1044
1045 let response: IsEditableResponse = self
1046 .channel()
1047 .send(
1048 "isEditable",
1049 serde_json::json!({
1050 "selector": selector,
1051 "strict": true,
1052 "timeout": crate::DEFAULT_TIMEOUT_MS
1053 }),
1054 )
1055 .await?;
1056
1057 Ok(response.value)
1058 }
1059
1060 pub(crate) async fn locator_is_hidden(&self, selector: &str) -> Result<bool> {
1062 #[derive(Deserialize)]
1063 struct IsHiddenResponse {
1064 value: bool,
1065 }
1066
1067 let response: IsHiddenResponse = self
1068 .channel()
1069 .send(
1070 "isHidden",
1071 serde_json::json!({
1072 "selector": selector,
1073 "strict": true,
1074 "timeout": crate::DEFAULT_TIMEOUT_MS
1075 }),
1076 )
1077 .await?;
1078
1079 Ok(response.value)
1080 }
1081
1082 pub(crate) async fn locator_is_disabled(&self, selector: &str) -> Result<bool> {
1084 #[derive(Deserialize)]
1085 struct IsDisabledResponse {
1086 value: bool,
1087 }
1088
1089 let response: IsDisabledResponse = self
1090 .channel()
1091 .send(
1092 "isDisabled",
1093 serde_json::json!({
1094 "selector": selector,
1095 "strict": true,
1096 "timeout": crate::DEFAULT_TIMEOUT_MS
1097 }),
1098 )
1099 .await?;
1100
1101 Ok(response.value)
1102 }
1103
1104 pub(crate) async fn locator_is_focused(&self, selector: &str) -> Result<bool> {
1110 #[derive(Deserialize)]
1111 struct EvaluateResult {
1112 value: serde_json::Value,
1113 }
1114
1115 let script = r#"selector => {
1118 const elements = document.querySelectorAll(selector);
1119 if (elements.length === 0) return false;
1120 const element = elements[0];
1121 return document.activeElement === element;
1122 }"#;
1123
1124 let params = serde_json::json!({
1125 "expression": script,
1126 "arg": {
1127 "value": {"s": selector},
1128 "handles": []
1129 }
1130 });
1131
1132 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1133
1134 if let serde_json::Value::Object(map) = &result.value
1136 && let Some(b) = map.get("b").and_then(|v| v.as_bool())
1137 {
1138 return Ok(b);
1139 }
1140
1141 Ok(result.value.to_string().to_lowercase().contains("true"))
1143 }
1144
1145 pub(crate) async fn locator_click(
1149 &self,
1150 selector: &str,
1151 options: Option<crate::protocol::ClickOptions>,
1152 ) -> Result<()> {
1153 let mut params = serde_json::json!({
1154 "selector": selector,
1155 "strict": true
1156 });
1157
1158 if let Some(opts) = options {
1159 let opts_json = opts.to_json();
1160 if let Some(obj) = params.as_object_mut()
1161 && let Some(opts_obj) = opts_json.as_object()
1162 {
1163 obj.extend(opts_obj.clone());
1164 }
1165 } else {
1166 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1167 }
1168
1169 self.channel()
1170 .send_no_result("click", params)
1171 .await
1172 .map_err(|e| match e {
1173 Error::Timeout(msg) => {
1174 Error::Timeout(format!("{} (selector: '{}')", msg, selector))
1175 }
1176 other => other,
1177 })
1178 }
1179
1180 pub(crate) async fn locator_dblclick(
1182 &self,
1183 selector: &str,
1184 options: Option<crate::protocol::ClickOptions>,
1185 ) -> Result<()> {
1186 let mut params = serde_json::json!({
1187 "selector": selector,
1188 "strict": true
1189 });
1190
1191 if let Some(opts) = options {
1192 let opts_json = opts.to_json();
1193 if let Some(obj) = params.as_object_mut()
1194 && let Some(opts_obj) = opts_json.as_object()
1195 {
1196 obj.extend(opts_obj.clone());
1197 }
1198 } else {
1199 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1200 }
1201
1202 self.channel().send_no_result("dblclick", params).await
1203 }
1204
1205 pub(crate) async fn locator_fill(
1207 &self,
1208 selector: &str,
1209 text: &str,
1210 options: Option<crate::protocol::FillOptions>,
1211 ) -> Result<()> {
1212 let mut params = serde_json::json!({
1213 "selector": selector,
1214 "value": text,
1215 "strict": true
1216 });
1217
1218 if let Some(opts) = options {
1219 let opts_json = opts.to_json();
1220 if let Some(obj) = params.as_object_mut()
1221 && let Some(opts_obj) = opts_json.as_object()
1222 {
1223 obj.extend(opts_obj.clone());
1224 }
1225 } else {
1226 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1227 }
1228
1229 self.channel().send_no_result("fill", params).await
1230 }
1231
1232 pub(crate) async fn locator_clear(
1234 &self,
1235 selector: &str,
1236 options: Option<crate::protocol::FillOptions>,
1237 ) -> Result<()> {
1238 let mut params = serde_json::json!({
1239 "selector": selector,
1240 "value": "",
1241 "strict": true
1242 });
1243
1244 if let Some(opts) = options {
1245 let opts_json = opts.to_json();
1246 if let Some(obj) = params.as_object_mut()
1247 && let Some(opts_obj) = opts_json.as_object()
1248 {
1249 obj.extend(opts_obj.clone());
1250 }
1251 } else {
1252 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1253 }
1254
1255 self.channel().send_no_result("fill", params).await
1256 }
1257
1258 pub(crate) async fn locator_press(
1260 &self,
1261 selector: &str,
1262 key: &str,
1263 options: Option<crate::protocol::PressOptions>,
1264 ) -> Result<()> {
1265 let mut params = serde_json::json!({
1266 "selector": selector,
1267 "key": key,
1268 "strict": true
1269 });
1270
1271 if let Some(opts) = options {
1272 let opts_json = opts.to_json();
1273 if let Some(obj) = params.as_object_mut()
1274 && let Some(opts_obj) = opts_json.as_object()
1275 {
1276 obj.extend(opts_obj.clone());
1277 }
1278 } else {
1279 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1280 }
1281
1282 self.channel().send_no_result("press", params).await
1283 }
1284
1285 pub(crate) async fn locator_focus(&self, selector: &str) -> Result<()> {
1287 self.channel()
1288 .send_no_result(
1289 "focus",
1290 serde_json::json!({
1291 "selector": selector,
1292 "strict": true,
1293 "timeout": crate::DEFAULT_TIMEOUT_MS
1294 }),
1295 )
1296 .await
1297 }
1298
1299 pub(crate) async fn locator_blur(&self, selector: &str) -> Result<()> {
1301 self.channel()
1302 .send_no_result(
1303 "blur",
1304 serde_json::json!({
1305 "selector": selector,
1306 "strict": true,
1307 "timeout": crate::DEFAULT_TIMEOUT_MS
1308 }),
1309 )
1310 .await
1311 }
1312
1313 pub(crate) async fn locator_press_sequentially(
1317 &self,
1318 selector: &str,
1319 text: &str,
1320 options: Option<crate::protocol::PressSequentiallyOptions>,
1321 ) -> Result<()> {
1322 let mut params = serde_json::json!({
1323 "selector": selector,
1324 "text": text,
1325 "strict": true
1326 });
1327
1328 if let Some(opts) = options {
1329 let opts_json = opts.to_json();
1330 if let Some(obj) = params.as_object_mut()
1331 && let Some(opts_obj) = opts_json.as_object()
1332 {
1333 obj.extend(opts_obj.clone());
1334 }
1335 } else {
1336 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1337 }
1338
1339 self.channel().send_no_result("type", params).await
1340 }
1341
1342 pub(crate) async fn locator_all_inner_texts(&self, selector: &str) -> Result<Vec<String>> {
1344 #[derive(serde::Deserialize)]
1345 struct EvaluateResult {
1346 value: serde_json::Value,
1347 }
1348
1349 let params = serde_json::json!({
1352 "selector": selector,
1353 "expression": "ee => ee.map(e => e.innerText)",
1354 "isFunction": true,
1355 "arg": {
1356 "value": {"v": "null"},
1357 "handles": []
1358 }
1359 });
1360
1361 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1362
1363 Self::parse_string_array(result.value)
1364 }
1365
1366 pub(crate) async fn locator_all_text_contents(&self, selector: &str) -> Result<Vec<String>> {
1368 #[derive(serde::Deserialize)]
1369 struct EvaluateResult {
1370 value: serde_json::Value,
1371 }
1372
1373 let params = serde_json::json!({
1376 "selector": selector,
1377 "expression": "ee => ee.map(e => e.textContent || '')",
1378 "isFunction": true,
1379 "arg": {
1380 "value": {"v": "null"},
1381 "handles": []
1382 }
1383 });
1384
1385 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1386
1387 Self::parse_string_array(result.value)
1388 }
1389
1390 pub(crate) async fn locator_tap(
1397 &self,
1398 selector: &str,
1399 options: Option<crate::protocol::TapOptions>,
1400 ) -> Result<()> {
1401 let mut params = serde_json::json!({
1402 "selector": selector,
1403 "strict": true
1404 });
1405
1406 if let Some(opts) = options {
1407 let opts_json = opts.to_json();
1408 if let Some(obj) = params.as_object_mut()
1409 && let Some(opts_obj) = opts_json.as_object()
1410 {
1411 obj.extend(opts_obj.clone());
1412 }
1413 } else {
1414 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1415 }
1416
1417 self.channel().send_no_result("tap", params).await
1418 }
1419
1420 pub(crate) async fn locator_drag_to(
1426 &self,
1427 source_selector: &str,
1428 target_selector: &str,
1429 options: Option<crate::protocol::DragToOptions>,
1430 ) -> Result<()> {
1431 let mut params = serde_json::json!({
1432 "source": source_selector,
1433 "target": target_selector,
1434 "strict": true
1435 });
1436
1437 if let Some(opts) = options {
1438 let opts_json = opts.to_json();
1439 if let Some(obj) = params.as_object_mut()
1440 && let Some(opts_obj) = opts_json.as_object()
1441 {
1442 obj.extend(opts_obj.clone());
1443 }
1444 } else {
1445 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1446 }
1447
1448 self.channel().send_no_result("dragAndDrop", params).await
1449 }
1450
1451 pub(crate) async fn locator_wait_for(
1458 &self,
1459 selector: &str,
1460 options: Option<crate::protocol::WaitForOptions>,
1461 ) -> Result<()> {
1462 let mut params = serde_json::json!({
1463 "selector": selector,
1464 "strict": true
1465 });
1466
1467 if let Some(opts) = options {
1468 let opts_json = opts.to_json();
1469 if let Some(obj) = params.as_object_mut()
1470 && let Some(opts_obj) = opts_json.as_object()
1471 {
1472 obj.extend(opts_obj.clone());
1473 }
1474 } else {
1475 params["state"] = serde_json::json!("visible");
1477 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1478 }
1479
1480 let _: serde_json::Value = self.channel().send("waitForSelector", params).await?;
1482 Ok(())
1483 }
1484
1485 pub(crate) async fn locator_evaluate<T: serde::Serialize>(
1492 &self,
1493 selector: &str,
1494 expression: &str,
1495 arg: Option<T>,
1496 ) -> Result<serde_json::Value> {
1497 let serialized_arg = match arg {
1498 Some(a) => serialize_argument(&a),
1499 None => serialize_null(),
1500 };
1501
1502 let params = serde_json::json!({
1503 "selector": selector,
1504 "expression": expression,
1505 "isFunction": true,
1506 "arg": serialized_arg,
1507 "strict": true
1508 });
1509
1510 #[derive(Deserialize)]
1511 struct EvaluateResult {
1512 value: serde_json::Value,
1513 }
1514
1515 let result: EvaluateResult = self.channel().send("evalOnSelector", params).await?;
1516 Ok(parse_result(&result.value))
1517 }
1518
1519 pub(crate) async fn locator_evaluate_all<T: serde::Serialize>(
1526 &self,
1527 selector: &str,
1528 expression: &str,
1529 arg: Option<T>,
1530 ) -> Result<serde_json::Value> {
1531 let serialized_arg = match arg {
1532 Some(a) => serialize_argument(&a),
1533 None => serialize_null(),
1534 };
1535
1536 let params = serde_json::json!({
1537 "selector": selector,
1538 "expression": expression,
1539 "isFunction": true,
1540 "arg": serialized_arg
1541 });
1542
1543 #[derive(Deserialize)]
1544 struct EvaluateResult {
1545 value: serde_json::Value,
1546 }
1547
1548 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1549 Ok(parse_result(&result.value))
1550 }
1551
1552 fn parse_string_array(value: serde_json::Value) -> Result<Vec<String>> {
1557 let array = if let Some(arr) = value.get("a").and_then(|v| v.as_array()) {
1559 arr.clone()
1560 } else if let Some(arr) = value.as_array() {
1561 arr.clone()
1562 } else {
1563 return Ok(Vec::new());
1564 };
1565
1566 let mut result = Vec::with_capacity(array.len());
1567 for item in &array {
1568 let s = if let Some(s) = item.get("s").and_then(|v| v.as_str()) {
1570 s.to_string()
1571 } else if let Some(s) = item.as_str() {
1572 s.to_string()
1573 } else if item.is_null() {
1574 String::new()
1575 } else {
1576 item.to_string()
1577 };
1578 result.push(s);
1579 }
1580 Ok(result)
1581 }
1582
1583 pub(crate) async fn locator_check(
1584 &self,
1585 selector: &str,
1586 options: Option<crate::protocol::CheckOptions>,
1587 ) -> Result<()> {
1588 let mut params = serde_json::json!({
1589 "selector": selector,
1590 "strict": true
1591 });
1592
1593 if let Some(opts) = options {
1594 let opts_json = opts.to_json();
1595 if let Some(obj) = params.as_object_mut()
1596 && let Some(opts_obj) = opts_json.as_object()
1597 {
1598 obj.extend(opts_obj.clone());
1599 }
1600 } else {
1601 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1602 }
1603
1604 self.channel().send_no_result("check", params).await
1605 }
1606
1607 pub(crate) async fn locator_uncheck(
1608 &self,
1609 selector: &str,
1610 options: Option<crate::protocol::CheckOptions>,
1611 ) -> Result<()> {
1612 let mut params = serde_json::json!({
1613 "selector": selector,
1614 "strict": true
1615 });
1616
1617 if let Some(opts) = options {
1618 let opts_json = opts.to_json();
1619 if let Some(obj) = params.as_object_mut()
1620 && let Some(opts_obj) = opts_json.as_object()
1621 {
1622 obj.extend(opts_obj.clone());
1623 }
1624 } else {
1625 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1626 }
1627
1628 self.channel().send_no_result("uncheck", params).await
1629 }
1630
1631 pub(crate) async fn locator_hover(
1632 &self,
1633 selector: &str,
1634 options: Option<crate::protocol::HoverOptions>,
1635 ) -> Result<()> {
1636 let mut params = serde_json::json!({
1637 "selector": selector,
1638 "strict": true
1639 });
1640
1641 if let Some(opts) = options {
1642 let opts_json = opts.to_json();
1643 if let Some(obj) = params.as_object_mut()
1644 && let Some(opts_obj) = opts_json.as_object()
1645 {
1646 obj.extend(opts_obj.clone());
1647 }
1648 } else {
1649 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1650 }
1651
1652 self.channel().send_no_result("hover", params).await
1653 }
1654
1655 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
1656 #[derive(Deserialize)]
1657 struct InputValueResponse {
1658 value: String,
1659 }
1660
1661 let response: InputValueResponse = self
1662 .channel()
1663 .send(
1664 "inputValue",
1665 serde_json::json!({
1666 "selector": selector,
1667 "strict": true,
1668 "timeout": crate::DEFAULT_TIMEOUT_MS }),
1670 )
1671 .await?;
1672
1673 Ok(response.value)
1674 }
1675
1676 pub(crate) async fn locator_select_option(
1677 &self,
1678 selector: &str,
1679 value: crate::protocol::SelectOption,
1680 options: Option<crate::protocol::SelectOptions>,
1681 ) -> Result<Vec<String>> {
1682 #[derive(Deserialize)]
1683 struct SelectOptionResponse {
1684 values: Vec<String>,
1685 }
1686
1687 let mut params = serde_json::json!({
1688 "selector": selector,
1689 "strict": true,
1690 "options": [value.to_json()]
1691 });
1692
1693 if let Some(opts) = options {
1694 let opts_json = opts.to_json();
1695 if let Some(obj) = params.as_object_mut()
1696 && let Some(opts_obj) = opts_json.as_object()
1697 {
1698 obj.extend(opts_obj.clone());
1699 }
1700 } else {
1701 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1703 }
1704
1705 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1706
1707 Ok(response.values)
1708 }
1709
1710 pub(crate) async fn locator_select_option_multiple(
1711 &self,
1712 selector: &str,
1713 values: Vec<crate::protocol::SelectOption>,
1714 options: Option<crate::protocol::SelectOptions>,
1715 ) -> Result<Vec<String>> {
1716 #[derive(Deserialize)]
1717 struct SelectOptionResponse {
1718 values: Vec<String>,
1719 }
1720
1721 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
1722
1723 let mut params = serde_json::json!({
1724 "selector": selector,
1725 "strict": true,
1726 "options": values_array
1727 });
1728
1729 if let Some(opts) = options {
1730 let opts_json = opts.to_json();
1731 if let Some(obj) = params.as_object_mut()
1732 && let Some(opts_obj) = opts_json.as_object()
1733 {
1734 obj.extend(opts_obj.clone());
1735 }
1736 } else {
1737 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1739 }
1740
1741 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1742
1743 Ok(response.values)
1744 }
1745
1746 pub(crate) async fn locator_set_input_files(
1747 &self,
1748 selector: &str,
1749 file: &std::path::PathBuf,
1750 ) -> Result<()> {
1751 use base64::{Engine as _, engine::general_purpose};
1752 use std::io::Read;
1753
1754 let mut file_handle = std::fs::File::open(file)?;
1756 let mut buffer = Vec::new();
1757 file_handle.read_to_end(&mut buffer)?;
1758
1759 let base64_content = general_purpose::STANDARD.encode(&buffer);
1761
1762 let file_name = file
1764 .file_name()
1765 .and_then(|n| n.to_str())
1766 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
1767
1768 self.channel()
1769 .send_no_result(
1770 "setInputFiles",
1771 serde_json::json!({
1772 "selector": selector,
1773 "strict": true,
1774 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
1776 "name": file_name,
1777 "buffer": base64_content
1778 }]
1779 }),
1780 )
1781 .await
1782 }
1783
1784 pub(crate) async fn locator_set_input_files_multiple(
1785 &self,
1786 selector: &str,
1787 files: &[&std::path::PathBuf],
1788 ) -> Result<()> {
1789 use base64::{Engine as _, engine::general_purpose};
1790 use std::io::Read;
1791
1792 if files.is_empty() {
1794 return self
1795 .channel()
1796 .send_no_result(
1797 "setInputFiles",
1798 serde_json::json!({
1799 "selector": selector,
1800 "strict": true,
1801 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
1803 }),
1804 )
1805 .await;
1806 }
1807
1808 let mut file_objects = Vec::new();
1810 for file_path in files {
1811 let mut file_handle = std::fs::File::open(file_path)?;
1812 let mut buffer = Vec::new();
1813 file_handle.read_to_end(&mut buffer)?;
1814
1815 let base64_content = general_purpose::STANDARD.encode(&buffer);
1816 let file_name = file_path
1817 .file_name()
1818 .and_then(|n| n.to_str())
1819 .ok_or_else(|| {
1820 crate::error::Error::InvalidArgument("Invalid file path".to_string())
1821 })?;
1822
1823 file_objects.push(serde_json::json!({
1824 "name": file_name,
1825 "buffer": base64_content
1826 }));
1827 }
1828
1829 self.channel()
1830 .send_no_result(
1831 "setInputFiles",
1832 serde_json::json!({
1833 "selector": selector,
1834 "strict": true,
1835 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": file_objects
1837 }),
1838 )
1839 .await
1840 }
1841
1842 pub(crate) async fn locator_set_input_files_payload(
1843 &self,
1844 selector: &str,
1845 file: crate::protocol::FilePayload,
1846 ) -> Result<()> {
1847 use base64::{Engine as _, engine::general_purpose};
1848
1849 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1851
1852 self.channel()
1853 .send_no_result(
1854 "setInputFiles",
1855 serde_json::json!({
1856 "selector": selector,
1857 "strict": true,
1858 "timeout": crate::DEFAULT_TIMEOUT_MS,
1859 "payloads": [{
1860 "name": file.name,
1861 "mimeType": file.mime_type,
1862 "buffer": base64_content
1863 }]
1864 }),
1865 )
1866 .await
1867 }
1868
1869 pub(crate) async fn locator_set_input_files_payload_multiple(
1870 &self,
1871 selector: &str,
1872 files: &[crate::protocol::FilePayload],
1873 ) -> Result<()> {
1874 use base64::{Engine as _, engine::general_purpose};
1875
1876 if files.is_empty() {
1878 return self
1879 .channel()
1880 .send_no_result(
1881 "setInputFiles",
1882 serde_json::json!({
1883 "selector": selector,
1884 "strict": true,
1885 "timeout": crate::DEFAULT_TIMEOUT_MS,
1886 "payloads": []
1887 }),
1888 )
1889 .await;
1890 }
1891
1892 let file_objects: Vec<_> = files
1894 .iter()
1895 .map(|file| {
1896 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1897 serde_json::json!({
1898 "name": file.name,
1899 "mimeType": file.mime_type,
1900 "buffer": base64_content
1901 })
1902 })
1903 .collect();
1904
1905 self.channel()
1906 .send_no_result(
1907 "setInputFiles",
1908 serde_json::json!({
1909 "selector": selector,
1910 "strict": true,
1911 "timeout": crate::DEFAULT_TIMEOUT_MS,
1912 "payloads": file_objects
1913 }),
1914 )
1915 .await
1916 }
1917
1918 pub(crate) async fn locator_aria_snapshot(
1925 &self,
1926 selector: &str,
1927 options: Option<&crate::protocol::AriaSnapshotOptions>,
1928 ) -> Result<String> {
1929 let timeout = options
1930 .and_then(|o| o.timeout)
1931 .unwrap_or(crate::DEFAULT_TIMEOUT_MS);
1932 self.aria_snapshot_raw(selector, timeout, options).await
1933 }
1934
1935 pub(crate) async fn aria_snapshot_raw(
1936 &self,
1937 selector: &str,
1938 timeout: f64,
1939 options: Option<&crate::protocol::AriaSnapshotOptions>,
1940 ) -> Result<String> {
1941 #[derive(Deserialize)]
1942 struct AriaSnapshotResponse {
1943 snapshot: String,
1944 }
1945
1946 let mut params = serde_json::json!({
1947 "selector": selector,
1948 "timeout": timeout,
1949 });
1950 if let Some(opts) = options {
1951 if let Some(mode) = opts.mode {
1952 params["mode"] = serde_json::Value::String(mode.as_str().to_string());
1953 }
1954 if let Some(ref track) = opts.track {
1955 params["track"] = serde_json::Value::String(track.clone());
1956 }
1957 if let Some(depth) = opts.depth {
1958 params["depth"] = serde_json::Value::from(depth);
1959 }
1960 }
1961
1962 let response: AriaSnapshotResponse = self.channel().send("ariaSnapshot", params).await?;
1963 Ok(response.snapshot)
1964 }
1965
1966 pub(crate) async fn frame_resolve_selector(&self, selector: &str) -> Result<String> {
1972 #[derive(Deserialize)]
1973 struct ResolveSelectorResponse {
1974 #[serde(rename = "resolvedSelector")]
1975 resolved_selector: String,
1976 }
1977
1978 let response: ResolveSelectorResponse = self
1979 .channel()
1980 .send(
1981 "resolveSelector",
1982 serde_json::json!({
1983 "selector": selector,
1984 }),
1985 )
1986 .await?;
1987
1988 Ok(response.resolved_selector)
1989 }
1990
1991 pub(crate) async fn locator_highlight(&self, selector: &str) -> Result<()> {
1998 self.channel()
1999 .send_no_result(
2000 "highlight",
2001 serde_json::json!({
2002 "selector": selector
2003 }),
2004 )
2005 .await
2006 }
2007
2008 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
2012 let params = serde_json::json!({
2013 "expression": expression,
2014 "arg": {
2015 "value": {"v": "null"},
2016 "handles": []
2017 }
2018 });
2019
2020 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
2021 Ok(())
2022 }
2023
2024 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
2036 let params = serde_json::json!({
2037 "expression": expression,
2038 "arg": {
2039 "value": {"v": "null"},
2040 "handles": []
2041 }
2042 });
2043
2044 #[derive(Deserialize)]
2045 struct EvaluateResult {
2046 value: serde_json::Value,
2047 }
2048
2049 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2050
2051 match &result.value {
2058 Value::Object(map) => {
2059 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
2060 Ok(s.to_string())
2062 } else if let Some(n) = map.get("n") {
2063 Ok(n.to_string())
2065 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
2066 Ok(b.to_string())
2068 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
2069 Ok(v.to_string())
2071 } else {
2072 Ok(result.value.to_string())
2074 }
2075 }
2076 _ => {
2077 Ok(result.value.to_string())
2079 }
2080 }
2081 }
2082
2083 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid()))]
2123 pub async fn evaluate<T: serde::Serialize>(
2124 &self,
2125 expression: &str,
2126 arg: Option<&T>,
2127 ) -> Result<Value> {
2128 let serialized_arg = match arg {
2130 Some(a) => serialize_argument(a),
2131 None => serialize_null(),
2132 };
2133
2134 let params = serde_json::json!({
2136 "expression": expression,
2137 "arg": serialized_arg
2138 });
2139
2140 #[derive(Deserialize)]
2142 struct EvaluateResult {
2143 value: serde_json::Value,
2144 }
2145
2146 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2147
2148 Ok(parse_result(&result.value))
2150 }
2151
2152 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2192 pub async fn add_style_tag(
2193 &self,
2194 options: crate::protocol::page::AddStyleTagOptions,
2195 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2196 options.validate()?;
2198
2199 let mut params = serde_json::json!({});
2201
2202 if let Some(content) = &options.content {
2203 params["content"] = serde_json::json!(content);
2204 }
2205
2206 if let Some(url) = &options.url {
2207 params["url"] = serde_json::json!(url);
2208 }
2209
2210 if let Some(path) = &options.path {
2211 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2213 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
2214 })?;
2215 params["content"] = serde_json::json!(css_content);
2216 }
2217
2218 #[derive(Deserialize)]
2219 struct AddStyleTagResponse {
2220 element: serde_json::Value,
2221 }
2222
2223 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
2224
2225 let guid = response.element["guid"].as_str().ok_or_else(|| {
2226 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
2227 })?;
2228
2229 let connection = self.base.connection();
2230 let handle: crate::protocol::ElementHandle = connection
2231 .get_typed::<crate::protocol::ElementHandle>(guid)
2232 .await?;
2233
2234 Ok(Arc::new(handle))
2235 }
2236
2237 pub(crate) async fn locator_dispatch_event(
2245 &self,
2246 selector: &str,
2247 type_: &str,
2248 event_init: Option<serde_json::Value>,
2249 ) -> Result<()> {
2250 let event_init_serialized = match event_init {
2253 Some(v) => serialize_argument(&v),
2254 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
2255 };
2256
2257 let params = serde_json::json!({
2258 "selector": selector,
2259 "type": type_,
2260 "eventInit": event_init_serialized,
2261 "strict": true,
2262 "timeout": crate::DEFAULT_TIMEOUT_MS
2263 });
2264
2265 self.channel().send_no_result("dispatchEvent", params).await
2266 }
2267
2268 pub(crate) async fn locator_bounding_box(
2278 &self,
2279 selector: &str,
2280 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
2281 let element = self.query_selector(selector).await?;
2282 match element {
2283 Some(handle) => handle.bounding_box().await,
2284 None => Ok(None),
2285 }
2286 }
2287
2288 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
2295 let element = self.query_selector(selector).await?;
2296 match element {
2297 Some(handle) => handle.scroll_into_view_if_needed().await,
2298 None => Err(crate::error::Error::ElementNotFound(format!(
2299 "Element not found: {}",
2300 selector
2301 ))),
2302 }
2303 }
2304
2305 pub(crate) async fn frame_expect(
2311 &self,
2312 selector: &str,
2313 expression: &str,
2314 expected_value: serde_json::Value,
2315 is_not: bool,
2316 timeout_ms: f64,
2317 ) -> Result<()> {
2318 #[derive(serde::Deserialize)]
2319 #[serde(rename_all = "camelCase")]
2320 struct ExpectResult {
2321 matches: bool,
2322 #[serde(default)]
2323 timed_out: Option<bool>,
2324 #[serde(default)]
2325 error_message: Option<String>,
2326 }
2327
2328 let params = serde_json::json!({
2329 "selector": selector,
2330 "expression": expression,
2331 "expectedValue": expected_value,
2332 "isNot": is_not,
2333 "timeout": timeout_ms
2334 });
2335
2336 let result: ExpectResult = self.channel().send("expect", params).await?;
2337
2338 if result.matches != is_not {
2339 Ok(())
2340 } else {
2341 let msg = result
2342 .error_message
2343 .unwrap_or_else(|| format!("Assertion '{}' failed", expression));
2344 if result.timed_out == Some(true) {
2345 Err(crate::error::Error::AssertionTimeout(msg))
2346 } else {
2347 Err(crate::error::Error::AssertionFailed(msg))
2348 }
2349 }
2350 }
2351
2352 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2362 pub async fn add_script_tag(
2363 &self,
2364 options: crate::protocol::page::AddScriptTagOptions,
2365 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2366 options.validate()?;
2368
2369 let mut params = serde_json::json!({});
2371
2372 if let Some(content) = &options.content {
2373 params["content"] = serde_json::json!(content);
2374 }
2375
2376 if let Some(url) = &options.url {
2377 params["url"] = serde_json::json!(url);
2378 }
2379
2380 if let Some(path) = &options.path {
2381 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2383 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
2384 })?;
2385 params["content"] = serde_json::json!(js_content);
2386 }
2387
2388 if let Some(type_) = &options.type_ {
2389 params["type"] = serde_json::json!(type_);
2390 }
2391
2392 #[derive(Deserialize)]
2393 struct AddScriptTagResponse {
2394 element: serde_json::Value,
2395 }
2396
2397 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
2398
2399 let guid = response.element["guid"].as_str().ok_or_else(|| {
2400 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
2401 })?;
2402
2403 let connection = self.base.connection();
2404 let handle: crate::protocol::ElementHandle = connection
2405 .get_typed::<crate::protocol::ElementHandle>(guid)
2406 .await?;
2407
2408 Ok(Arc::new(handle))
2409 }
2410}
2411
2412impl ChannelOwner for Frame {
2413 fn guid(&self) -> &str {
2414 self.base.guid()
2415 }
2416
2417 fn type_name(&self) -> &str {
2418 self.base.type_name()
2419 }
2420
2421 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
2422 self.base.parent()
2423 }
2424
2425 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
2426 self.base.connection()
2427 }
2428
2429 fn initializer(&self) -> &Value {
2430 self.base.initializer()
2431 }
2432
2433 fn channel(&self) -> &Channel {
2434 self.base.channel()
2435 }
2436
2437 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
2438 self.base.dispose(reason)
2439 }
2440
2441 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
2442 self.base.adopt(child)
2443 }
2444
2445 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
2446 self.base.add_child(guid, child)
2447 }
2448
2449 fn remove_child(&self, guid: &str) {
2450 self.base.remove_child(guid)
2451 }
2452
2453 fn on_event(&self, method: &str, params: Value) {
2454 match method {
2455 "navigated" => {
2456 if let Some(url_value) = params.get("url")
2458 && let Some(url_str) = url_value.as_str()
2459 {
2460 if let Ok(mut url) = self.url.write() {
2462 *url = url_str.to_string();
2463 }
2464 }
2465 let self_clone = self.clone();
2467 tokio::spawn(async move {
2468 if let Some(page) = self_clone.page() {
2469 page.trigger_framenavigated_event(self_clone).await;
2470 }
2471 });
2472 }
2473 "loadstate" => {
2474 if let Some(add) = params.get("add").and_then(|v| v.as_str())
2477 && add == "load"
2478 {
2479 let self_clone = self.clone();
2480 tokio::spawn(async move {
2481 if let Some(page) = self_clone.page() {
2482 page.trigger_load_event().await;
2483 }
2484 });
2485 }
2486 }
2487 "detached" => {
2488 if let Ok(mut flag) = self.is_detached.write() {
2490 *flag = true;
2491 }
2492 }
2493 _ => {
2494 }
2496 }
2497 }
2498
2499 fn was_collected(&self) -> bool {
2500 self.base.was_collected()
2501 }
2502
2503 fn as_any(&self) -> &dyn Any {
2504 self
2505 }
2506}
2507
2508impl std::fmt::Debug for Frame {
2509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2510 f.debug_struct("Frame").field("guid", &self.guid()).finish()
2511 }
2512}
2513
2514fn glob_match(pattern: &str, text: &str) -> bool {
2519 let regex_str = pattern
2520 .replace('.', "\\.")
2521 .replace("**", "\x00") .replace('*', "[^/]*")
2523 .replace('\x00', ".*"); let regex_str = format!("^{}$", regex_str);
2525 regex::Regex::new(®ex_str)
2526 .map(|re| re.is_match(text))
2527 .unwrap_or(false)
2528}