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: impl Into<String>) -> 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.into(), 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(text, exact))
399 }
400
401 pub fn get_by_placeholder(&self, text: &str, exact: bool) -> crate::protocol::Locator {
405 self.locator(crate::protocol::locator::get_by_placeholder_selector(
406 text, exact,
407 ))
408 }
409
410 pub fn get_by_alt_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
414 self.locator(crate::protocol::locator::get_by_alt_text_selector(
415 text, exact,
416 ))
417 }
418
419 pub fn get_by_title(&self, text: &str, exact: bool) -> crate::protocol::Locator {
423 self.locator(crate::protocol::locator::get_by_title_selector(text, exact))
424 }
425
426 pub fn get_by_test_id(&self, test_id: &str) -> crate::protocol::Locator {
433 use crate::server::channel_owner::ChannelOwner;
434 let attr = self.connection().selectors().test_id_attribute();
435 self.locator(crate::protocol::locator::get_by_test_id_selector_with_attr(
436 test_id, &attr,
437 ))
438 }
439
440 pub fn get_by_role(
444 &self,
445 role: crate::protocol::locator::AriaRole,
446 options: Option<crate::protocol::locator::GetByRoleOptions>,
447 ) -> crate::protocol::Locator {
448 self.locator(crate::protocol::locator::get_by_role_selector(
449 role, options,
450 ))
451 }
452
453 fn channel(&self) -> &Channel {
455 self.base.channel()
456 }
457
458 pub fn url(&self) -> String {
464 self.url.read().unwrap().clone()
465 }
466
467 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid(), url = %url, status = tracing::field::Empty))]
481 pub async fn goto(
482 &self,
483 url: &str,
484 options: impl Into<Option<GotoOptions>>,
485 ) -> Result<Option<Response>> {
486 let options = options.into();
487 let mut params = serde_json::json!({
489 "url": url,
490 });
491
492 if let Some(opts) = options {
494 if let Some(timeout) = opts.timeout {
495 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
496 } else {
497 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
499 }
500 if let Some(wait_until) = opts.wait_until {
501 params["waitUntil"] = serde_json::json!(wait_until.as_str());
502 }
503 } else {
504 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
506 }
507
508 #[derive(Deserialize)]
511 struct GotoResponse {
512 response: Option<ResponseReference>,
513 }
514
515 #[derive(Deserialize)]
516 struct ResponseReference {
517 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
518 guid: Arc<str>,
519 }
520
521 let goto_result: GotoResponse = self.channel().send("goto", params).await?;
522
523 if let Some(response_ref) = goto_result.response {
525 let response_arc = {
531 let mut attempts = 0;
532 let max_attempts = 20; loop {
534 match self.connection().get_object(&response_ref.guid).await {
535 Ok(obj) => break obj,
536 Err(_) if attempts < max_attempts => {
537 attempts += 1;
538 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
539 }
540 Err(e) => return Err(e),
541 }
542 }
543 };
544
545 let initializer = response_arc.initializer();
548
549 let status = initializer["status"].as_u64().ok_or_else(|| {
551 crate::error::Error::ProtocolError("Response missing status".to_string())
552 })? as u16;
553
554 let headers = initializer["headers"]
556 .as_array()
557 .ok_or_else(|| {
558 crate::error::Error::ProtocolError("Response missing headers".to_string())
559 })?
560 .iter()
561 .filter_map(|h| {
562 let name = h["name"].as_str()?;
563 let value = h["value"].as_str()?;
564 Some((name.to_string(), value.to_string()))
565 })
566 .collect();
567
568 tracing::Span::current().record("status", status);
569 Ok(Some(Response::new(
570 initializer["url"]
571 .as_str()
572 .ok_or_else(|| {
573 crate::error::Error::ProtocolError("Response missing url".to_string())
574 })?
575 .to_string(),
576 status,
577 initializer["statusText"].as_str().unwrap_or("").to_string(),
578 headers,
579 Some(response_arc),
580 )))
581 } else {
582 Ok(None)
585 }
586 }
587
588 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
592 pub async fn title(&self) -> Result<String> {
593 #[derive(Deserialize)]
594 struct TitleResponse {
595 value: String,
596 }
597
598 let response: TitleResponse = self.channel().send("title", serde_json::json!({})).await?;
599 Ok(response.value)
600 }
601
602 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
606 pub async fn content(&self) -> Result<String> {
607 #[derive(Deserialize)]
608 struct ContentResponse {
609 value: String,
610 }
611
612 let response: ContentResponse = self
613 .channel()
614 .send("content", serde_json::json!({}))
615 .await?;
616 Ok(response.value)
617 }
618
619 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
623 pub async fn set_content(
624 &self,
625 html: &str,
626 options: impl Into<Option<GotoOptions>>,
627 ) -> Result<()> {
628 let options = options.into();
629 let mut params = serde_json::json!({
630 "html": html,
631 });
632
633 if let Some(opts) = options {
634 if let Some(timeout) = opts.timeout {
635 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
636 } else {
637 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
638 }
639 if let Some(wait_until) = opts.wait_until {
640 params["waitUntil"] = serde_json::json!(wait_until.as_str());
641 }
642 } else {
643 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
644 }
645
646 self.channel().send_no_result("setContent", params).await
647 }
648
649 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
657 pub async fn wait_for_load_state(&self, state: Option<WaitUntil>) -> Result<()> {
658 let target_state = state.unwrap_or(WaitUntil::Load);
659
660 let js_check = match target_state {
661 WaitUntil::Load => "document.readyState === 'complete'",
663 WaitUntil::DomContentLoaded => "document.readyState !== 'loading'",
665 WaitUntil::NetworkIdle => "document.readyState === 'complete'",
668 WaitUntil::Commit => "document.readyState !== 'loading'",
670 };
671
672 let timeout_ms = crate::DEFAULT_TIMEOUT_MS as u64;
673 let poll_interval = std::time::Duration::from_millis(50);
674 let start = std::time::Instant::now();
675
676 loop {
677 #[derive(Deserialize)]
678 struct EvalResponse {
679 value: serde_json::Value,
680 }
681
682 let result: EvalResponse = self
683 .channel()
684 .send(
685 "evaluateExpression",
686 serde_json::json!({
687 "expression": js_check,
688 "isFunction": false,
689 "arg": crate::protocol::serialize_null(),
690 }),
691 )
692 .await?;
693
694 let is_ready = result
696 .value
697 .as_object()
698 .and_then(|m| m.get("b"))
699 .and_then(|v| v.as_bool())
700 .unwrap_or(false);
701
702 if is_ready {
703 return Ok(());
704 }
705
706 if start.elapsed().as_millis() as u64 >= timeout_ms {
707 return Err(crate::error::Error::Timeout(format!(
708 "wait_for_load_state({}) timed out after {}ms",
709 target_state.as_str(),
710 timeout_ms
711 )));
712 }
713
714 tokio::time::sleep(poll_interval).await;
715 }
716 }
717
718 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid(), url = %url))]
725 pub async fn wait_for_url(
726 &self,
727 url: &str,
728 options: impl Into<Option<GotoOptions>>,
729 ) -> Result<()> {
730 let options = options.into();
731 let timeout_ms = options
732 .as_ref()
733 .and_then(|o| o.timeout)
734 .map(|d| d.as_millis() as u64)
735 .unwrap_or(crate::DEFAULT_TIMEOUT_MS as u64);
736
737 let is_glob = url.contains('*');
741
742 let poll_interval = std::time::Duration::from_millis(50);
743 let start = std::time::Instant::now();
744
745 loop {
746 let current_url = self.url();
747
748 let matches = if is_glob {
749 crate::protocol::glob::glob_match(url, ¤t_url)
750 } else {
751 current_url == url
752 };
753
754 if matches {
755 if let Some(ref opts) = options
757 && let Some(wait_until) = opts.wait_until
758 {
759 self.wait_for_load_state(Some(wait_until)).await?;
760 }
761 return Ok(());
762 }
763
764 if start.elapsed().as_millis() as u64 >= timeout_ms {
765 return Err(crate::error::Error::Timeout(format!(
766 "wait_for_url({}) timed out after {}ms, current URL: {}",
767 url, timeout_ms, current_url
768 )));
769 }
770
771 tokio::time::sleep(poll_interval).await;
772 }
773 }
774
775 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
779 pub async fn query_selector(
780 &self,
781 selector: &str,
782 ) -> Result<Option<Arc<crate::protocol::ElementHandle>>> {
783 let response: serde_json::Value = self
784 .channel()
785 .send(
786 "querySelector",
787 serde_json::json!({
788 "selector": selector
789 }),
790 )
791 .await?;
792
793 if response.as_object().map(|o| o.is_empty()).unwrap_or(true) {
795 return Ok(None);
796 }
797
798 let element_value = if let Some(elem) = response.get("element") {
800 elem
801 } else if let Some(elem) = response.get("handle") {
802 elem
803 } else {
804 &response
806 };
807
808 if element_value.is_null() {
809 return Ok(None);
810 }
811
812 let guid = element_value["guid"].as_str().ok_or_else(|| {
814 crate::error::Error::ProtocolError("Element GUID missing".to_string())
815 })?;
816
817 let connection = self.base.connection();
819 let handle: crate::protocol::ElementHandle = connection
820 .get_typed::<crate::protocol::ElementHandle>(guid)
821 .await?;
822
823 Ok(Some(Arc::new(handle)))
824 }
825
826 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
830 pub async fn query_selector_all(
831 &self,
832 selector: &str,
833 ) -> Result<Vec<Arc<crate::protocol::ElementHandle>>> {
834 #[derive(Deserialize)]
835 struct QueryAllResponse {
836 elements: Vec<serde_json::Value>,
837 }
838
839 let response: QueryAllResponse = self
840 .channel()
841 .send(
842 "querySelectorAll",
843 serde_json::json!({
844 "selector": selector
845 }),
846 )
847 .await?;
848
849 let connection = self.base.connection();
851 let mut handles = Vec::new();
852
853 for element_value in response.elements {
854 let guid = element_value["guid"].as_str().ok_or_else(|| {
855 crate::error::Error::ProtocolError("Element GUID missing".to_string())
856 })?;
857
858 let handle: crate::protocol::ElementHandle = connection
859 .get_typed::<crate::protocol::ElementHandle>(guid)
860 .await?;
861
862 handles.push(Arc::new(handle));
863 }
864
865 Ok(handles)
866 }
867
868 pub(crate) async fn locator_count(&self, selector: &str) -> Result<usize> {
873 #[derive(Deserialize)]
875 struct QueryAllResponse {
876 elements: Vec<serde_json::Value>,
877 }
878
879 let response: QueryAllResponse = self
880 .channel()
881 .send(
882 "querySelectorAll",
883 serde_json::json!({
884 "selector": selector
885 }),
886 )
887 .await?;
888
889 Ok(response.elements.len())
890 }
891
892 pub(crate) async fn locator_text_content(&self, selector: &str) -> Result<Option<String>> {
894 #[derive(Deserialize)]
895 struct TextContentResponse {
896 value: Option<String>,
897 }
898
899 let response: TextContentResponse = self
900 .channel()
901 .send(
902 "textContent",
903 serde_json::json!({
904 "selector": selector,
905 "strict": true,
906 "timeout": crate::DEFAULT_TIMEOUT_MS
907 }),
908 )
909 .await?;
910
911 Ok(response.value)
912 }
913
914 pub(crate) async fn locator_inner_text(&self, selector: &str) -> Result<String> {
916 #[derive(Deserialize)]
917 struct InnerTextResponse {
918 value: String,
919 }
920
921 let response: InnerTextResponse = self
922 .channel()
923 .send(
924 "innerText",
925 serde_json::json!({
926 "selector": selector,
927 "strict": true,
928 "timeout": crate::DEFAULT_TIMEOUT_MS
929 }),
930 )
931 .await?;
932
933 Ok(response.value)
934 }
935
936 pub(crate) async fn locator_inner_html(&self, selector: &str) -> Result<String> {
938 #[derive(Deserialize)]
939 struct InnerHTMLResponse {
940 value: String,
941 }
942
943 let response: InnerHTMLResponse = self
944 .channel()
945 .send(
946 "innerHTML",
947 serde_json::json!({
948 "selector": selector,
949 "strict": true,
950 "timeout": crate::DEFAULT_TIMEOUT_MS
951 }),
952 )
953 .await?;
954
955 Ok(response.value)
956 }
957
958 pub(crate) async fn locator_get_attribute(
960 &self,
961 selector: &str,
962 name: &str,
963 ) -> Result<Option<String>> {
964 #[derive(Deserialize)]
965 struct GetAttributeResponse {
966 value: Option<String>,
967 }
968
969 let response: GetAttributeResponse = self
970 .channel()
971 .send(
972 "getAttribute",
973 serde_json::json!({
974 "selector": selector,
975 "name": name,
976 "strict": true,
977 "timeout": crate::DEFAULT_TIMEOUT_MS
978 }),
979 )
980 .await?;
981
982 Ok(response.value)
983 }
984
985 pub(crate) async fn locator_is_visible(&self, selector: &str) -> Result<bool> {
987 #[derive(Deserialize)]
988 struct IsVisibleResponse {
989 value: bool,
990 }
991
992 let response: IsVisibleResponse = self
993 .channel()
994 .send(
995 "isVisible",
996 serde_json::json!({
997 "selector": selector,
998 "strict": true,
999 "timeout": crate::DEFAULT_TIMEOUT_MS
1000 }),
1001 )
1002 .await?;
1003
1004 Ok(response.value)
1005 }
1006
1007 pub(crate) async fn locator_is_enabled(&self, selector: &str) -> Result<bool> {
1009 #[derive(Deserialize)]
1010 struct IsEnabledResponse {
1011 value: bool,
1012 }
1013
1014 let response: IsEnabledResponse = self
1015 .channel()
1016 .send(
1017 "isEnabled",
1018 serde_json::json!({
1019 "selector": selector,
1020 "strict": true,
1021 "timeout": crate::DEFAULT_TIMEOUT_MS
1022 }),
1023 )
1024 .await?;
1025
1026 Ok(response.value)
1027 }
1028
1029 pub(crate) async fn locator_is_checked(&self, selector: &str) -> Result<bool> {
1031 #[derive(Deserialize)]
1032 struct IsCheckedResponse {
1033 value: bool,
1034 }
1035
1036 let response: IsCheckedResponse = self
1037 .channel()
1038 .send(
1039 "isChecked",
1040 serde_json::json!({
1041 "selector": selector,
1042 "strict": true,
1043 "timeout": crate::DEFAULT_TIMEOUT_MS
1044 }),
1045 )
1046 .await?;
1047
1048 Ok(response.value)
1049 }
1050
1051 pub(crate) async fn locator_is_editable(&self, selector: &str) -> Result<bool> {
1053 #[derive(Deserialize)]
1054 struct IsEditableResponse {
1055 value: bool,
1056 }
1057
1058 let response: IsEditableResponse = self
1059 .channel()
1060 .send(
1061 "isEditable",
1062 serde_json::json!({
1063 "selector": selector,
1064 "strict": true,
1065 "timeout": crate::DEFAULT_TIMEOUT_MS
1066 }),
1067 )
1068 .await?;
1069
1070 Ok(response.value)
1071 }
1072
1073 pub(crate) async fn locator_is_hidden(&self, selector: &str) -> Result<bool> {
1075 #[derive(Deserialize)]
1076 struct IsHiddenResponse {
1077 value: bool,
1078 }
1079
1080 let response: IsHiddenResponse = self
1081 .channel()
1082 .send(
1083 "isHidden",
1084 serde_json::json!({
1085 "selector": selector,
1086 "strict": true,
1087 "timeout": crate::DEFAULT_TIMEOUT_MS
1088 }),
1089 )
1090 .await?;
1091
1092 Ok(response.value)
1093 }
1094
1095 pub(crate) async fn locator_is_disabled(&self, selector: &str) -> Result<bool> {
1097 #[derive(Deserialize)]
1098 struct IsDisabledResponse {
1099 value: bool,
1100 }
1101
1102 let response: IsDisabledResponse = self
1103 .channel()
1104 .send(
1105 "isDisabled",
1106 serde_json::json!({
1107 "selector": selector,
1108 "strict": true,
1109 "timeout": crate::DEFAULT_TIMEOUT_MS
1110 }),
1111 )
1112 .await?;
1113
1114 Ok(response.value)
1115 }
1116
1117 pub(crate) async fn locator_is_focused(&self, selector: &str) -> Result<bool> {
1123 #[derive(Deserialize)]
1124 struct EvaluateResult {
1125 value: serde_json::Value,
1126 }
1127
1128 let script = r#"selector => {
1131 const elements = document.querySelectorAll(selector);
1132 if (elements.length === 0) return false;
1133 const element = elements[0];
1134 return document.activeElement === element;
1135 }"#;
1136
1137 let params = serde_json::json!({
1138 "expression": script,
1139 "arg": {
1140 "value": {"s": selector},
1141 "handles": []
1142 }
1143 });
1144
1145 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1146
1147 if let serde_json::Value::Object(map) = &result.value
1149 && let Some(b) = map.get("b").and_then(|v| v.as_bool())
1150 {
1151 return Ok(b);
1152 }
1153
1154 Ok(result.value.to_string().to_lowercase().contains("true"))
1156 }
1157
1158 pub(crate) async fn locator_click(
1162 &self,
1163 selector: &str,
1164 options: Option<crate::protocol::ClickOptions>,
1165 ) -> Result<()> {
1166 let mut params = serde_json::json!({
1167 "selector": selector,
1168 "strict": true
1169 });
1170
1171 if let Some(opts) = options {
1172 let opts_json = opts.to_json();
1173 if let Some(obj) = params.as_object_mut()
1174 && let Some(opts_obj) = opts_json.as_object()
1175 {
1176 obj.extend(opts_obj.clone());
1177 }
1178 } else {
1179 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1180 }
1181
1182 self.channel()
1183 .send_no_result("click", params)
1184 .await
1185 .map_err(|e| match e {
1186 Error::Timeout(msg) => {
1187 Error::Timeout(format!("{} (selector: '{}')", msg, selector))
1188 }
1189 other => other,
1190 })
1191 }
1192
1193 pub(crate) async fn locator_dblclick(
1195 &self,
1196 selector: &str,
1197 options: Option<crate::protocol::ClickOptions>,
1198 ) -> Result<()> {
1199 let mut params = serde_json::json!({
1200 "selector": selector,
1201 "strict": true
1202 });
1203
1204 if let Some(opts) = options {
1205 let opts_json = opts.to_json();
1206 if let Some(obj) = params.as_object_mut()
1207 && let Some(opts_obj) = opts_json.as_object()
1208 {
1209 obj.extend(opts_obj.clone());
1210 }
1211 } else {
1212 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1213 }
1214
1215 self.channel().send_no_result("dblclick", params).await
1216 }
1217
1218 pub(crate) async fn locator_fill(
1220 &self,
1221 selector: &str,
1222 text: &str,
1223 options: Option<crate::protocol::FillOptions>,
1224 ) -> Result<()> {
1225 let mut params = serde_json::json!({
1226 "selector": selector,
1227 "value": text,
1228 "strict": true
1229 });
1230
1231 if let Some(opts) = options {
1232 let opts_json = opts.to_json();
1233 if let Some(obj) = params.as_object_mut()
1234 && let Some(opts_obj) = opts_json.as_object()
1235 {
1236 obj.extend(opts_obj.clone());
1237 }
1238 } else {
1239 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1240 }
1241
1242 self.channel().send_no_result("fill", params).await
1243 }
1244
1245 pub(crate) async fn locator_clear(
1247 &self,
1248 selector: &str,
1249 options: Option<crate::protocol::FillOptions>,
1250 ) -> Result<()> {
1251 let mut params = serde_json::json!({
1252 "selector": selector,
1253 "value": "",
1254 "strict": true
1255 });
1256
1257 if let Some(opts) = options {
1258 let opts_json = opts.to_json();
1259 if let Some(obj) = params.as_object_mut()
1260 && let Some(opts_obj) = opts_json.as_object()
1261 {
1262 obj.extend(opts_obj.clone());
1263 }
1264 } else {
1265 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1266 }
1267
1268 self.channel().send_no_result("fill", params).await
1269 }
1270
1271 pub(crate) async fn locator_press(
1273 &self,
1274 selector: &str,
1275 key: &str,
1276 options: Option<crate::protocol::PressOptions>,
1277 ) -> Result<()> {
1278 let mut params = serde_json::json!({
1279 "selector": selector,
1280 "key": key,
1281 "strict": true
1282 });
1283
1284 if let Some(opts) = options {
1285 let opts_json = opts.to_json();
1286 if let Some(obj) = params.as_object_mut()
1287 && let Some(opts_obj) = opts_json.as_object()
1288 {
1289 obj.extend(opts_obj.clone());
1290 }
1291 } else {
1292 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1293 }
1294
1295 self.channel().send_no_result("press", params).await
1296 }
1297
1298 pub(crate) async fn locator_focus(&self, selector: &str) -> Result<()> {
1300 self.channel()
1301 .send_no_result(
1302 "focus",
1303 serde_json::json!({
1304 "selector": selector,
1305 "strict": true,
1306 "timeout": crate::DEFAULT_TIMEOUT_MS
1307 }),
1308 )
1309 .await
1310 }
1311
1312 pub(crate) async fn locator_blur(&self, selector: &str) -> Result<()> {
1314 self.channel()
1315 .send_no_result(
1316 "blur",
1317 serde_json::json!({
1318 "selector": selector,
1319 "strict": true,
1320 "timeout": crate::DEFAULT_TIMEOUT_MS
1321 }),
1322 )
1323 .await
1324 }
1325
1326 pub(crate) async fn locator_press_sequentially(
1330 &self,
1331 selector: &str,
1332 text: &str,
1333 options: Option<crate::protocol::PressSequentiallyOptions>,
1334 ) -> Result<()> {
1335 let mut params = serde_json::json!({
1336 "selector": selector,
1337 "text": text,
1338 "strict": true
1339 });
1340
1341 if let Some(opts) = options {
1342 let opts_json = opts.to_json();
1343 if let Some(obj) = params.as_object_mut()
1344 && let Some(opts_obj) = opts_json.as_object()
1345 {
1346 obj.extend(opts_obj.clone());
1347 }
1348 } else {
1349 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1350 }
1351
1352 self.channel().send_no_result("type", params).await
1353 }
1354
1355 pub(crate) async fn locator_all_inner_texts(&self, selector: &str) -> Result<Vec<String>> {
1357 #[derive(serde::Deserialize)]
1358 struct EvaluateResult {
1359 value: serde_json::Value,
1360 }
1361
1362 let params = serde_json::json!({
1365 "selector": selector,
1366 "expression": "ee => ee.map(e => e.innerText)",
1367 "isFunction": true,
1368 "arg": {
1369 "value": {"v": "null"},
1370 "handles": []
1371 }
1372 });
1373
1374 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1375
1376 Self::parse_string_array(result.value)
1377 }
1378
1379 pub(crate) async fn locator_all_text_contents(&self, selector: &str) -> Result<Vec<String>> {
1381 #[derive(serde::Deserialize)]
1382 struct EvaluateResult {
1383 value: serde_json::Value,
1384 }
1385
1386 let params = serde_json::json!({
1389 "selector": selector,
1390 "expression": "ee => ee.map(e => e.textContent || '')",
1391 "isFunction": true,
1392 "arg": {
1393 "value": {"v": "null"},
1394 "handles": []
1395 }
1396 });
1397
1398 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1399
1400 Self::parse_string_array(result.value)
1401 }
1402
1403 pub(crate) async fn locator_tap(
1410 &self,
1411 selector: &str,
1412 options: Option<crate::protocol::TapOptions>,
1413 ) -> Result<()> {
1414 let mut params = serde_json::json!({
1415 "selector": selector,
1416 "strict": true
1417 });
1418
1419 if let Some(opts) = options {
1420 let opts_json = opts.to_json();
1421 if let Some(obj) = params.as_object_mut()
1422 && let Some(opts_obj) = opts_json.as_object()
1423 {
1424 obj.extend(opts_obj.clone());
1425 }
1426 } else {
1427 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1428 }
1429
1430 self.channel().send_no_result("tap", params).await
1431 }
1432
1433 pub(crate) async fn locator_drag_to(
1439 &self,
1440 source_selector: &str,
1441 target_selector: &str,
1442 options: Option<crate::protocol::DragToOptions>,
1443 ) -> Result<()> {
1444 let mut params = serde_json::json!({
1445 "source": source_selector,
1446 "target": target_selector,
1447 "strict": true
1448 });
1449
1450 if let Some(opts) = options {
1451 let opts_json = opts.to_json();
1452 if let Some(obj) = params.as_object_mut()
1453 && let Some(opts_obj) = opts_json.as_object()
1454 {
1455 obj.extend(opts_obj.clone());
1456 }
1457 } else {
1458 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1459 }
1460
1461 self.channel().send_no_result("dragAndDrop", params).await
1462 }
1463
1464 pub(crate) async fn locator_drop(
1468 &self,
1469 selector: &str,
1470 options: crate::protocol::DropOptions,
1471 ) -> Result<()> {
1472 let mut params = serde_json::json!({
1473 "selector": selector,
1474 "strict": true,
1475 });
1476
1477 let opts_json = options.to_json();
1478 if let Some(obj) = params.as_object_mut()
1479 && let Some(opts_obj) = opts_json.as_object()
1480 {
1481 obj.extend(opts_obj.clone());
1482 }
1483
1484 self.channel().send_no_result("drop", params).await
1485 }
1486
1487 pub(crate) async fn locator_wait_for(
1494 &self,
1495 selector: &str,
1496 options: Option<crate::protocol::WaitForOptions>,
1497 ) -> Result<()> {
1498 let mut params = serde_json::json!({
1499 "selector": selector,
1500 "strict": true
1501 });
1502
1503 if let Some(opts) = options {
1504 let opts_json = opts.to_json();
1505 if let Some(obj) = params.as_object_mut()
1506 && let Some(opts_obj) = opts_json.as_object()
1507 {
1508 obj.extend(opts_obj.clone());
1509 }
1510 } else {
1511 params["state"] = serde_json::json!("visible");
1513 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1514 }
1515
1516 let _: serde_json::Value = self.channel().send("waitForSelector", params).await?;
1518 Ok(())
1519 }
1520
1521 pub(crate) async fn locator_evaluate<T: serde::Serialize>(
1528 &self,
1529 selector: &str,
1530 expression: &str,
1531 arg: Option<T>,
1532 ) -> Result<serde_json::Value> {
1533 let serialized_arg = match arg {
1534 Some(a) => serialize_argument(&a),
1535 None => serialize_null(),
1536 };
1537
1538 let params = serde_json::json!({
1539 "selector": selector,
1540 "expression": expression,
1541 "isFunction": true,
1542 "arg": serialized_arg,
1543 "strict": true
1544 });
1545
1546 #[derive(Deserialize)]
1547 struct EvaluateResult {
1548 value: serde_json::Value,
1549 }
1550
1551 let result: EvaluateResult = self.channel().send("evalOnSelector", params).await?;
1552 Ok(parse_result(&result.value))
1553 }
1554
1555 pub(crate) async fn locator_evaluate_all<T: serde::Serialize>(
1562 &self,
1563 selector: &str,
1564 expression: &str,
1565 arg: Option<T>,
1566 ) -> Result<serde_json::Value> {
1567 let serialized_arg = match arg {
1568 Some(a) => serialize_argument(&a),
1569 None => serialize_null(),
1570 };
1571
1572 let params = serde_json::json!({
1573 "selector": selector,
1574 "expression": expression,
1575 "isFunction": true,
1576 "arg": serialized_arg
1577 });
1578
1579 #[derive(Deserialize)]
1580 struct EvaluateResult {
1581 value: serde_json::Value,
1582 }
1583
1584 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1585 Ok(parse_result(&result.value))
1586 }
1587
1588 fn parse_string_array(value: serde_json::Value) -> Result<Vec<String>> {
1593 let array = if let Some(arr) = value.get("a").and_then(|v| v.as_array()) {
1595 arr.clone()
1596 } else if let Some(arr) = value.as_array() {
1597 arr.clone()
1598 } else {
1599 return Ok(Vec::new());
1600 };
1601
1602 let mut result = Vec::with_capacity(array.len());
1603 for item in &array {
1604 let s = if let Some(s) = item.get("s").and_then(|v| v.as_str()) {
1606 s.to_string()
1607 } else if let Some(s) = item.as_str() {
1608 s.to_string()
1609 } else if item.is_null() {
1610 String::new()
1611 } else {
1612 item.to_string()
1613 };
1614 result.push(s);
1615 }
1616 Ok(result)
1617 }
1618
1619 pub(crate) async fn locator_check(
1620 &self,
1621 selector: &str,
1622 options: Option<crate::protocol::CheckOptions>,
1623 ) -> Result<()> {
1624 let mut params = serde_json::json!({
1625 "selector": selector,
1626 "strict": true
1627 });
1628
1629 if let Some(opts) = options {
1630 let opts_json = opts.to_json();
1631 if let Some(obj) = params.as_object_mut()
1632 && let Some(opts_obj) = opts_json.as_object()
1633 {
1634 obj.extend(opts_obj.clone());
1635 }
1636 } else {
1637 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1638 }
1639
1640 self.channel().send_no_result("check", params).await
1641 }
1642
1643 pub(crate) async fn locator_uncheck(
1644 &self,
1645 selector: &str,
1646 options: Option<crate::protocol::CheckOptions>,
1647 ) -> Result<()> {
1648 let mut params = serde_json::json!({
1649 "selector": selector,
1650 "strict": true
1651 });
1652
1653 if let Some(opts) = options {
1654 let opts_json = opts.to_json();
1655 if let Some(obj) = params.as_object_mut()
1656 && let Some(opts_obj) = opts_json.as_object()
1657 {
1658 obj.extend(opts_obj.clone());
1659 }
1660 } else {
1661 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1662 }
1663
1664 self.channel().send_no_result("uncheck", params).await
1665 }
1666
1667 pub(crate) async fn locator_hover(
1668 &self,
1669 selector: &str,
1670 options: Option<crate::protocol::HoverOptions>,
1671 ) -> Result<()> {
1672 let mut params = serde_json::json!({
1673 "selector": selector,
1674 "strict": true
1675 });
1676
1677 if let Some(opts) = options {
1678 let opts_json = opts.to_json();
1679 if let Some(obj) = params.as_object_mut()
1680 && let Some(opts_obj) = opts_json.as_object()
1681 {
1682 obj.extend(opts_obj.clone());
1683 }
1684 } else {
1685 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1686 }
1687
1688 self.channel().send_no_result("hover", params).await
1689 }
1690
1691 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
1692 #[derive(Deserialize)]
1693 struct InputValueResponse {
1694 value: String,
1695 }
1696
1697 let response: InputValueResponse = self
1698 .channel()
1699 .send(
1700 "inputValue",
1701 serde_json::json!({
1702 "selector": selector,
1703 "strict": true,
1704 "timeout": crate::DEFAULT_TIMEOUT_MS }),
1706 )
1707 .await?;
1708
1709 Ok(response.value)
1710 }
1711
1712 pub(crate) async fn locator_select_option(
1713 &self,
1714 selector: &str,
1715 value: crate::protocol::SelectOption,
1716 options: Option<crate::protocol::SelectOptions>,
1717 ) -> Result<Vec<String>> {
1718 #[derive(Deserialize)]
1719 struct SelectOptionResponse {
1720 values: Vec<String>,
1721 }
1722
1723 let mut params = serde_json::json!({
1724 "selector": selector,
1725 "strict": true,
1726 "options": [value.to_json()]
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_select_option_multiple(
1747 &self,
1748 selector: &str,
1749 values: Vec<crate::protocol::SelectOption>,
1750 options: Option<crate::protocol::SelectOptions>,
1751 ) -> Result<Vec<String>> {
1752 #[derive(Deserialize)]
1753 struct SelectOptionResponse {
1754 values: Vec<String>,
1755 }
1756
1757 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
1758
1759 let mut params = serde_json::json!({
1760 "selector": selector,
1761 "strict": true,
1762 "options": values_array
1763 });
1764
1765 if let Some(opts) = options {
1766 let opts_json = opts.to_json();
1767 if let Some(obj) = params.as_object_mut()
1768 && let Some(opts_obj) = opts_json.as_object()
1769 {
1770 obj.extend(opts_obj.clone());
1771 }
1772 } else {
1773 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1775 }
1776
1777 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1778
1779 Ok(response.values)
1780 }
1781
1782 pub(crate) async fn locator_set_input_files(
1783 &self,
1784 selector: &str,
1785 file: &std::path::PathBuf,
1786 ) -> Result<()> {
1787 use base64::{Engine as _, engine::general_purpose};
1788 use std::io::Read;
1789
1790 let mut file_handle = std::fs::File::open(file)?;
1792 let mut buffer = Vec::new();
1793 file_handle.read_to_end(&mut buffer)?;
1794
1795 let base64_content = general_purpose::STANDARD.encode(&buffer);
1797
1798 let file_name = file
1800 .file_name()
1801 .and_then(|n| n.to_str())
1802 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
1803
1804 self.channel()
1805 .send_no_result(
1806 "setInputFiles",
1807 serde_json::json!({
1808 "selector": selector,
1809 "strict": true,
1810 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
1812 "name": file_name,
1813 "buffer": base64_content
1814 }]
1815 }),
1816 )
1817 .await
1818 }
1819
1820 pub(crate) async fn locator_set_input_files_multiple(
1821 &self,
1822 selector: &str,
1823 files: &[&std::path::PathBuf],
1824 ) -> Result<()> {
1825 use base64::{Engine as _, engine::general_purpose};
1826 use std::io::Read;
1827
1828 if files.is_empty() {
1830 return self
1831 .channel()
1832 .send_no_result(
1833 "setInputFiles",
1834 serde_json::json!({
1835 "selector": selector,
1836 "strict": true,
1837 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
1839 }),
1840 )
1841 .await;
1842 }
1843
1844 let mut file_objects = Vec::new();
1846 for file_path in files {
1847 let mut file_handle = std::fs::File::open(file_path)?;
1848 let mut buffer = Vec::new();
1849 file_handle.read_to_end(&mut buffer)?;
1850
1851 let base64_content = general_purpose::STANDARD.encode(&buffer);
1852 let file_name = file_path
1853 .file_name()
1854 .and_then(|n| n.to_str())
1855 .ok_or_else(|| {
1856 crate::error::Error::InvalidArgument("Invalid file path".to_string())
1857 })?;
1858
1859 file_objects.push(serde_json::json!({
1860 "name": file_name,
1861 "buffer": base64_content
1862 }));
1863 }
1864
1865 self.channel()
1866 .send_no_result(
1867 "setInputFiles",
1868 serde_json::json!({
1869 "selector": selector,
1870 "strict": true,
1871 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": file_objects
1873 }),
1874 )
1875 .await
1876 }
1877
1878 pub(crate) async fn locator_set_input_files_payload(
1879 &self,
1880 selector: &str,
1881 file: crate::protocol::FilePayload,
1882 ) -> Result<()> {
1883 use base64::{Engine as _, engine::general_purpose};
1884
1885 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1887
1888 self.channel()
1889 .send_no_result(
1890 "setInputFiles",
1891 serde_json::json!({
1892 "selector": selector,
1893 "strict": true,
1894 "timeout": crate::DEFAULT_TIMEOUT_MS,
1895 "payloads": [{
1896 "name": file.name,
1897 "mimeType": file.mime_type,
1898 "buffer": base64_content
1899 }]
1900 }),
1901 )
1902 .await
1903 }
1904
1905 pub(crate) async fn locator_set_input_files_payload_multiple(
1906 &self,
1907 selector: &str,
1908 files: &[crate::protocol::FilePayload],
1909 ) -> Result<()> {
1910 use base64::{Engine as _, engine::general_purpose};
1911
1912 if files.is_empty() {
1914 return self
1915 .channel()
1916 .send_no_result(
1917 "setInputFiles",
1918 serde_json::json!({
1919 "selector": selector,
1920 "strict": true,
1921 "timeout": crate::DEFAULT_TIMEOUT_MS,
1922 "payloads": []
1923 }),
1924 )
1925 .await;
1926 }
1927
1928 let file_objects: Vec<_> = files
1930 .iter()
1931 .map(|file| {
1932 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1933 serde_json::json!({
1934 "name": file.name,
1935 "mimeType": file.mime_type,
1936 "buffer": base64_content
1937 })
1938 })
1939 .collect();
1940
1941 self.channel()
1942 .send_no_result(
1943 "setInputFiles",
1944 serde_json::json!({
1945 "selector": selector,
1946 "strict": true,
1947 "timeout": crate::DEFAULT_TIMEOUT_MS,
1948 "payloads": file_objects
1949 }),
1950 )
1951 .await
1952 }
1953
1954 pub(crate) async fn locator_aria_snapshot(
1961 &self,
1962 selector: &str,
1963 options: Option<&crate::protocol::AriaSnapshotOptions>,
1964 ) -> Result<String> {
1965 let timeout = options
1966 .and_then(|o| o.timeout)
1967 .unwrap_or(crate::DEFAULT_TIMEOUT_MS);
1968 self.aria_snapshot_raw(selector, timeout, options).await
1969 }
1970
1971 pub(crate) async fn aria_snapshot_raw(
1972 &self,
1973 selector: &str,
1974 timeout: f64,
1975 options: Option<&crate::protocol::AriaSnapshotOptions>,
1976 ) -> Result<String> {
1977 #[derive(Deserialize)]
1978 struct AriaSnapshotResponse {
1979 snapshot: String,
1980 }
1981
1982 let mut params = serde_json::json!({
1983 "selector": selector,
1984 "timeout": timeout,
1985 });
1986 if let Some(opts) = options {
1987 if let Some(mode) = opts.mode {
1988 params["mode"] = serde_json::Value::String(mode.as_str().to_string());
1989 }
1990 if let Some(ref track) = opts.track {
1991 params["track"] = serde_json::Value::String(track.clone());
1992 }
1993 if let Some(depth) = opts.depth {
1994 params["depth"] = serde_json::Value::from(depth);
1995 }
1996 if let Some(boxes) = opts.boxes {
1997 params["boxes"] = serde_json::Value::Bool(boxes);
1998 }
1999 }
2000
2001 let response: AriaSnapshotResponse = self.channel().send("ariaSnapshot", params).await?;
2002 Ok(response.snapshot)
2003 }
2004
2005 pub(crate) async fn frame_resolve_selector(&self, selector: &str) -> Result<String> {
2011 #[derive(Deserialize)]
2012 struct ResolveSelectorResponse {
2013 #[serde(rename = "resolvedSelector")]
2014 resolved_selector: String,
2015 }
2016
2017 let response: ResolveSelectorResponse = self
2018 .channel()
2019 .send(
2020 "resolveSelector",
2021 serde_json::json!({
2022 "selector": selector,
2023 }),
2024 )
2025 .await?;
2026
2027 Ok(response.resolved_selector)
2028 }
2029
2030 pub(crate) async fn locator_highlight(
2037 &self,
2038 selector: &str,
2039 style: Option<&str>,
2040 ) -> Result<()> {
2041 let mut params = serde_json::json!({ "selector": selector });
2042 if let Some(style) = style {
2043 params["style"] = serde_json::Value::String(style.to_string());
2044 }
2045 self.channel().send_no_result("highlight", params).await
2046 }
2047
2048 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
2052 let params = serde_json::json!({
2053 "expression": expression,
2054 "arg": {
2055 "value": {"v": "null"},
2056 "handles": []
2057 }
2058 });
2059
2060 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
2061 Ok(())
2062 }
2063
2064 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
2076 let params = serde_json::json!({
2077 "expression": expression,
2078 "arg": {
2079 "value": {"v": "null"},
2080 "handles": []
2081 }
2082 });
2083
2084 #[derive(Deserialize)]
2085 struct EvaluateResult {
2086 value: serde_json::Value,
2087 }
2088
2089 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2090
2091 match &result.value {
2098 Value::Object(map) => {
2099 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
2100 Ok(s.to_string())
2102 } else if let Some(n) = map.get("n") {
2103 Ok(n.to_string())
2105 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
2106 Ok(b.to_string())
2108 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
2109 Ok(v.to_string())
2111 } else {
2112 Ok(result.value.to_string())
2114 }
2115 }
2116 _ => {
2117 Ok(result.value.to_string())
2119 }
2120 }
2121 }
2122
2123 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid()))]
2163 pub async fn evaluate<T: serde::Serialize>(
2164 &self,
2165 expression: &str,
2166 arg: Option<&T>,
2167 ) -> Result<Value> {
2168 let serialized_arg = match arg {
2170 Some(a) => serialize_argument(a),
2171 None => serialize_null(),
2172 };
2173
2174 let params = serde_json::json!({
2176 "expression": expression,
2177 "arg": serialized_arg
2178 });
2179
2180 #[derive(Deserialize)]
2182 struct EvaluateResult {
2183 value: serde_json::Value,
2184 }
2185
2186 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2187
2188 Ok(parse_result(&result.value))
2190 }
2191
2192 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2232 pub async fn add_style_tag(
2233 &self,
2234 options: crate::protocol::page::AddStyleTagOptions,
2235 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2236 options.validate()?;
2238
2239 let mut params = serde_json::json!({});
2241
2242 if let Some(content) = &options.content {
2243 params["content"] = serde_json::json!(content);
2244 }
2245
2246 if let Some(url) = &options.url {
2247 params["url"] = serde_json::json!(url);
2248 }
2249
2250 if let Some(path) = &options.path {
2251 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2253 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
2254 })?;
2255 params["content"] = serde_json::json!(css_content);
2256 }
2257
2258 #[derive(Deserialize)]
2259 struct AddStyleTagResponse {
2260 element: serde_json::Value,
2261 }
2262
2263 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
2264
2265 let guid = response.element["guid"].as_str().ok_or_else(|| {
2266 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
2267 })?;
2268
2269 let connection = self.base.connection();
2270 let handle: crate::protocol::ElementHandle = connection
2271 .get_typed::<crate::protocol::ElementHandle>(guid)
2272 .await?;
2273
2274 Ok(Arc::new(handle))
2275 }
2276
2277 pub(crate) async fn locator_dispatch_event(
2285 &self,
2286 selector: &str,
2287 type_: &str,
2288 event_init: Option<serde_json::Value>,
2289 ) -> Result<()> {
2290 let event_init_serialized = match event_init {
2293 Some(v) => serialize_argument(&v),
2294 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
2295 };
2296
2297 let params = serde_json::json!({
2298 "selector": selector,
2299 "type": type_,
2300 "eventInit": event_init_serialized,
2301 "strict": true,
2302 "timeout": crate::DEFAULT_TIMEOUT_MS
2303 });
2304
2305 self.channel().send_no_result("dispatchEvent", params).await
2306 }
2307
2308 pub(crate) async fn locator_bounding_box(
2318 &self,
2319 selector: &str,
2320 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
2321 let element = self.query_selector(selector).await?;
2322 match element {
2323 Some(handle) => handle.bounding_box().await,
2324 None => Ok(None),
2325 }
2326 }
2327
2328 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
2335 let element = self.query_selector(selector).await?;
2336 match element {
2337 Some(handle) => handle.scroll_into_view_if_needed().await,
2338 None => Err(crate::error::Error::ElementNotFound(format!(
2339 "Element not found: {}",
2340 selector
2341 ))),
2342 }
2343 }
2344
2345 pub(crate) async fn frame_expect(
2351 &self,
2352 selector: &str,
2353 expression: &str,
2354 expected_value: serde_json::Value,
2355 is_not: bool,
2356 timeout_ms: f64,
2357 ) -> Result<()> {
2358 let params = serde_json::json!({
2359 "selector": selector,
2360 "expression": expression,
2361 "expectedValue": expected_value,
2362 "isNot": is_not,
2363 "timeout": timeout_ms
2364 });
2365
2366 let result: serde_json::Value = self.channel().send("expect", params).await?;
2374
2375 if crate::server::error_parsing::legacy_expect_verdict(&result, is_not) == Some(false) {
2383 return Err(crate::error::Error::AssertionFailed(format!(
2384 "Assertion failed for selector '{selector}' ({expression}). \
2385 Reported by a pre-1.61 Playwright server, which does not send \
2386 assertion details; connect to a version-matched server for a \
2387 fuller diagnostic."
2388 )));
2389 }
2390 Ok(())
2391 }
2392
2393 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2403 pub async fn add_script_tag(
2404 &self,
2405 options: crate::protocol::page::AddScriptTagOptions,
2406 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2407 options.validate()?;
2409
2410 let mut params = serde_json::json!({});
2412
2413 if let Some(content) = &options.content {
2414 params["content"] = serde_json::json!(content);
2415 }
2416
2417 if let Some(url) = &options.url {
2418 params["url"] = serde_json::json!(url);
2419 }
2420
2421 if let Some(path) = &options.path {
2422 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2424 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
2425 })?;
2426 params["content"] = serde_json::json!(js_content);
2427 }
2428
2429 if let Some(type_) = &options.type_ {
2430 params["type"] = serde_json::json!(type_);
2431 }
2432
2433 #[derive(Deserialize)]
2434 struct AddScriptTagResponse {
2435 element: serde_json::Value,
2436 }
2437
2438 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
2439
2440 let guid = response.element["guid"].as_str().ok_or_else(|| {
2441 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
2442 })?;
2443
2444 let connection = self.base.connection();
2445 let handle: crate::protocol::ElementHandle = connection
2446 .get_typed::<crate::protocol::ElementHandle>(guid)
2447 .await?;
2448
2449 Ok(Arc::new(handle))
2450 }
2451}
2452
2453impl ChannelOwner for Frame {
2454 fn guid(&self) -> &str {
2455 self.base.guid()
2456 }
2457
2458 fn type_name(&self) -> &str {
2459 self.base.type_name()
2460 }
2461
2462 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
2463 self.base.parent()
2464 }
2465
2466 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
2467 self.base.connection()
2468 }
2469
2470 fn initializer(&self) -> &Value {
2471 self.base.initializer()
2472 }
2473
2474 fn channel(&self) -> &Channel {
2475 self.base.channel()
2476 }
2477
2478 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
2479 if let Ok(mut guard) = self.page.lock() {
2483 *guard = None;
2484 }
2485 self.base.dispose(reason)
2486 }
2487
2488 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
2489 self.base.adopt(child)
2490 }
2491
2492 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
2493 self.base.add_child(guid, child)
2494 }
2495
2496 fn remove_child(&self, guid: &str) {
2497 self.base.remove_child(guid)
2498 }
2499
2500 fn on_event(&self, method: &str, params: Value) {
2501 match method {
2502 "navigated" => {
2503 if let Some(url_value) = params.get("url")
2505 && let Some(url_str) = url_value.as_str()
2506 {
2507 if let Ok(mut url) = self.url.write() {
2509 *url = url_str.to_string();
2510 }
2511 }
2512 let self_clone = self.clone();
2514 tokio::spawn(async move {
2515 if let Some(page) = self_clone.page() {
2516 page.trigger_framenavigated_event(self_clone).await;
2517 }
2518 });
2519 }
2520 "loadstate" => {
2521 if let Some(add) = params.get("add").and_then(|v| v.as_str())
2524 && add == "load"
2525 {
2526 let self_clone = self.clone();
2527 tokio::spawn(async move {
2528 if let Some(page) = self_clone.page() {
2529 page.trigger_load_event().await;
2530 }
2531 });
2532 }
2533 }
2534 "detached" => {
2535 if let Ok(mut flag) = self.is_detached.write() {
2537 *flag = true;
2538 }
2539 }
2540 _ => {
2541 }
2543 }
2544 }
2545
2546 fn was_collected(&self) -> bool {
2547 self.base.was_collected()
2548 }
2549
2550 fn as_any(&self) -> &dyn Any {
2551 self
2552 }
2553}
2554
2555impl std::fmt::Debug for Frame {
2556 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2557 f.debug_struct("Frame").field("guid", &self.guid()).finish()
2558 }
2559}