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 crate::protocol::glob::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_drop(
1455 &self,
1456 selector: &str,
1457 options: crate::protocol::DropOptions,
1458 ) -> Result<()> {
1459 let mut params = serde_json::json!({
1460 "selector": selector,
1461 "strict": true,
1462 });
1463
1464 let opts_json = options.to_json();
1465 if let Some(obj) = params.as_object_mut()
1466 && let Some(opts_obj) = opts_json.as_object()
1467 {
1468 obj.extend(opts_obj.clone());
1469 }
1470
1471 self.channel().send_no_result("drop", params).await
1472 }
1473
1474 pub(crate) async fn locator_wait_for(
1481 &self,
1482 selector: &str,
1483 options: Option<crate::protocol::WaitForOptions>,
1484 ) -> Result<()> {
1485 let mut params = serde_json::json!({
1486 "selector": selector,
1487 "strict": true
1488 });
1489
1490 if let Some(opts) = options {
1491 let opts_json = opts.to_json();
1492 if let Some(obj) = params.as_object_mut()
1493 && let Some(opts_obj) = opts_json.as_object()
1494 {
1495 obj.extend(opts_obj.clone());
1496 }
1497 } else {
1498 params["state"] = serde_json::json!("visible");
1500 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1501 }
1502
1503 let _: serde_json::Value = self.channel().send("waitForSelector", params).await?;
1505 Ok(())
1506 }
1507
1508 pub(crate) async fn locator_evaluate<T: serde::Serialize>(
1515 &self,
1516 selector: &str,
1517 expression: &str,
1518 arg: Option<T>,
1519 ) -> Result<serde_json::Value> {
1520 let serialized_arg = match arg {
1521 Some(a) => serialize_argument(&a),
1522 None => serialize_null(),
1523 };
1524
1525 let params = serde_json::json!({
1526 "selector": selector,
1527 "expression": expression,
1528 "isFunction": true,
1529 "arg": serialized_arg,
1530 "strict": true
1531 });
1532
1533 #[derive(Deserialize)]
1534 struct EvaluateResult {
1535 value: serde_json::Value,
1536 }
1537
1538 let result: EvaluateResult = self.channel().send("evalOnSelector", params).await?;
1539 Ok(parse_result(&result.value))
1540 }
1541
1542 pub(crate) async fn locator_evaluate_all<T: serde::Serialize>(
1549 &self,
1550 selector: &str,
1551 expression: &str,
1552 arg: Option<T>,
1553 ) -> Result<serde_json::Value> {
1554 let serialized_arg = match arg {
1555 Some(a) => serialize_argument(&a),
1556 None => serialize_null(),
1557 };
1558
1559 let params = serde_json::json!({
1560 "selector": selector,
1561 "expression": expression,
1562 "isFunction": true,
1563 "arg": serialized_arg
1564 });
1565
1566 #[derive(Deserialize)]
1567 struct EvaluateResult {
1568 value: serde_json::Value,
1569 }
1570
1571 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1572 Ok(parse_result(&result.value))
1573 }
1574
1575 fn parse_string_array(value: serde_json::Value) -> Result<Vec<String>> {
1580 let array = if let Some(arr) = value.get("a").and_then(|v| v.as_array()) {
1582 arr.clone()
1583 } else if let Some(arr) = value.as_array() {
1584 arr.clone()
1585 } else {
1586 return Ok(Vec::new());
1587 };
1588
1589 let mut result = Vec::with_capacity(array.len());
1590 for item in &array {
1591 let s = if let Some(s) = item.get("s").and_then(|v| v.as_str()) {
1593 s.to_string()
1594 } else if let Some(s) = item.as_str() {
1595 s.to_string()
1596 } else if item.is_null() {
1597 String::new()
1598 } else {
1599 item.to_string()
1600 };
1601 result.push(s);
1602 }
1603 Ok(result)
1604 }
1605
1606 pub(crate) async fn locator_check(
1607 &self,
1608 selector: &str,
1609 options: Option<crate::protocol::CheckOptions>,
1610 ) -> Result<()> {
1611 let mut params = serde_json::json!({
1612 "selector": selector,
1613 "strict": true
1614 });
1615
1616 if let Some(opts) = options {
1617 let opts_json = opts.to_json();
1618 if let Some(obj) = params.as_object_mut()
1619 && let Some(opts_obj) = opts_json.as_object()
1620 {
1621 obj.extend(opts_obj.clone());
1622 }
1623 } else {
1624 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1625 }
1626
1627 self.channel().send_no_result("check", params).await
1628 }
1629
1630 pub(crate) async fn locator_uncheck(
1631 &self,
1632 selector: &str,
1633 options: Option<crate::protocol::CheckOptions>,
1634 ) -> Result<()> {
1635 let mut params = serde_json::json!({
1636 "selector": selector,
1637 "strict": true
1638 });
1639
1640 if let Some(opts) = options {
1641 let opts_json = opts.to_json();
1642 if let Some(obj) = params.as_object_mut()
1643 && let Some(opts_obj) = opts_json.as_object()
1644 {
1645 obj.extend(opts_obj.clone());
1646 }
1647 } else {
1648 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1649 }
1650
1651 self.channel().send_no_result("uncheck", params).await
1652 }
1653
1654 pub(crate) async fn locator_hover(
1655 &self,
1656 selector: &str,
1657 options: Option<crate::protocol::HoverOptions>,
1658 ) -> Result<()> {
1659 let mut params = serde_json::json!({
1660 "selector": selector,
1661 "strict": true
1662 });
1663
1664 if let Some(opts) = options {
1665 let opts_json = opts.to_json();
1666 if let Some(obj) = params.as_object_mut()
1667 && let Some(opts_obj) = opts_json.as_object()
1668 {
1669 obj.extend(opts_obj.clone());
1670 }
1671 } else {
1672 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1673 }
1674
1675 self.channel().send_no_result("hover", params).await
1676 }
1677
1678 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
1679 #[derive(Deserialize)]
1680 struct InputValueResponse {
1681 value: String,
1682 }
1683
1684 let response: InputValueResponse = self
1685 .channel()
1686 .send(
1687 "inputValue",
1688 serde_json::json!({
1689 "selector": selector,
1690 "strict": true,
1691 "timeout": crate::DEFAULT_TIMEOUT_MS }),
1693 )
1694 .await?;
1695
1696 Ok(response.value)
1697 }
1698
1699 pub(crate) async fn locator_select_option(
1700 &self,
1701 selector: &str,
1702 value: crate::protocol::SelectOption,
1703 options: Option<crate::protocol::SelectOptions>,
1704 ) -> Result<Vec<String>> {
1705 #[derive(Deserialize)]
1706 struct SelectOptionResponse {
1707 values: Vec<String>,
1708 }
1709
1710 let mut params = serde_json::json!({
1711 "selector": selector,
1712 "strict": true,
1713 "options": [value.to_json()]
1714 });
1715
1716 if let Some(opts) = options {
1717 let opts_json = opts.to_json();
1718 if let Some(obj) = params.as_object_mut()
1719 && let Some(opts_obj) = opts_json.as_object()
1720 {
1721 obj.extend(opts_obj.clone());
1722 }
1723 } else {
1724 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1726 }
1727
1728 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1729
1730 Ok(response.values)
1731 }
1732
1733 pub(crate) async fn locator_select_option_multiple(
1734 &self,
1735 selector: &str,
1736 values: Vec<crate::protocol::SelectOption>,
1737 options: Option<crate::protocol::SelectOptions>,
1738 ) -> Result<Vec<String>> {
1739 #[derive(Deserialize)]
1740 struct SelectOptionResponse {
1741 values: Vec<String>,
1742 }
1743
1744 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
1745
1746 let mut params = serde_json::json!({
1747 "selector": selector,
1748 "strict": true,
1749 "options": values_array
1750 });
1751
1752 if let Some(opts) = options {
1753 let opts_json = opts.to_json();
1754 if let Some(obj) = params.as_object_mut()
1755 && let Some(opts_obj) = opts_json.as_object()
1756 {
1757 obj.extend(opts_obj.clone());
1758 }
1759 } else {
1760 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1762 }
1763
1764 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1765
1766 Ok(response.values)
1767 }
1768
1769 pub(crate) async fn locator_set_input_files(
1770 &self,
1771 selector: &str,
1772 file: &std::path::PathBuf,
1773 ) -> Result<()> {
1774 use base64::{Engine as _, engine::general_purpose};
1775 use std::io::Read;
1776
1777 let mut file_handle = std::fs::File::open(file)?;
1779 let mut buffer = Vec::new();
1780 file_handle.read_to_end(&mut buffer)?;
1781
1782 let base64_content = general_purpose::STANDARD.encode(&buffer);
1784
1785 let file_name = file
1787 .file_name()
1788 .and_then(|n| n.to_str())
1789 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
1790
1791 self.channel()
1792 .send_no_result(
1793 "setInputFiles",
1794 serde_json::json!({
1795 "selector": selector,
1796 "strict": true,
1797 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
1799 "name": file_name,
1800 "buffer": base64_content
1801 }]
1802 }),
1803 )
1804 .await
1805 }
1806
1807 pub(crate) async fn locator_set_input_files_multiple(
1808 &self,
1809 selector: &str,
1810 files: &[&std::path::PathBuf],
1811 ) -> Result<()> {
1812 use base64::{Engine as _, engine::general_purpose};
1813 use std::io::Read;
1814
1815 if files.is_empty() {
1817 return self
1818 .channel()
1819 .send_no_result(
1820 "setInputFiles",
1821 serde_json::json!({
1822 "selector": selector,
1823 "strict": true,
1824 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
1826 }),
1827 )
1828 .await;
1829 }
1830
1831 let mut file_objects = Vec::new();
1833 for file_path in files {
1834 let mut file_handle = std::fs::File::open(file_path)?;
1835 let mut buffer = Vec::new();
1836 file_handle.read_to_end(&mut buffer)?;
1837
1838 let base64_content = general_purpose::STANDARD.encode(&buffer);
1839 let file_name = file_path
1840 .file_name()
1841 .and_then(|n| n.to_str())
1842 .ok_or_else(|| {
1843 crate::error::Error::InvalidArgument("Invalid file path".to_string())
1844 })?;
1845
1846 file_objects.push(serde_json::json!({
1847 "name": file_name,
1848 "buffer": base64_content
1849 }));
1850 }
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, "payloads": file_objects
1860 }),
1861 )
1862 .await
1863 }
1864
1865 pub(crate) async fn locator_set_input_files_payload(
1866 &self,
1867 selector: &str,
1868 file: crate::protocol::FilePayload,
1869 ) -> Result<()> {
1870 use base64::{Engine as _, engine::general_purpose};
1871
1872 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1874
1875 self.channel()
1876 .send_no_result(
1877 "setInputFiles",
1878 serde_json::json!({
1879 "selector": selector,
1880 "strict": true,
1881 "timeout": crate::DEFAULT_TIMEOUT_MS,
1882 "payloads": [{
1883 "name": file.name,
1884 "mimeType": file.mime_type,
1885 "buffer": base64_content
1886 }]
1887 }),
1888 )
1889 .await
1890 }
1891
1892 pub(crate) async fn locator_set_input_files_payload_multiple(
1893 &self,
1894 selector: &str,
1895 files: &[crate::protocol::FilePayload],
1896 ) -> Result<()> {
1897 use base64::{Engine as _, engine::general_purpose};
1898
1899 if files.is_empty() {
1901 return self
1902 .channel()
1903 .send_no_result(
1904 "setInputFiles",
1905 serde_json::json!({
1906 "selector": selector,
1907 "strict": true,
1908 "timeout": crate::DEFAULT_TIMEOUT_MS,
1909 "payloads": []
1910 }),
1911 )
1912 .await;
1913 }
1914
1915 let file_objects: Vec<_> = files
1917 .iter()
1918 .map(|file| {
1919 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1920 serde_json::json!({
1921 "name": file.name,
1922 "mimeType": file.mime_type,
1923 "buffer": base64_content
1924 })
1925 })
1926 .collect();
1927
1928 self.channel()
1929 .send_no_result(
1930 "setInputFiles",
1931 serde_json::json!({
1932 "selector": selector,
1933 "strict": true,
1934 "timeout": crate::DEFAULT_TIMEOUT_MS,
1935 "payloads": file_objects
1936 }),
1937 )
1938 .await
1939 }
1940
1941 pub(crate) async fn locator_aria_snapshot(
1948 &self,
1949 selector: &str,
1950 options: Option<&crate::protocol::AriaSnapshotOptions>,
1951 ) -> Result<String> {
1952 let timeout = options
1953 .and_then(|o| o.timeout)
1954 .unwrap_or(crate::DEFAULT_TIMEOUT_MS);
1955 self.aria_snapshot_raw(selector, timeout, options).await
1956 }
1957
1958 pub(crate) async fn aria_snapshot_raw(
1959 &self,
1960 selector: &str,
1961 timeout: f64,
1962 options: Option<&crate::protocol::AriaSnapshotOptions>,
1963 ) -> Result<String> {
1964 #[derive(Deserialize)]
1965 struct AriaSnapshotResponse {
1966 snapshot: String,
1967 }
1968
1969 let mut params = serde_json::json!({
1970 "selector": selector,
1971 "timeout": timeout,
1972 });
1973 if let Some(opts) = options {
1974 if let Some(mode) = opts.mode {
1975 params["mode"] = serde_json::Value::String(mode.as_str().to_string());
1976 }
1977 if let Some(ref track) = opts.track {
1978 params["track"] = serde_json::Value::String(track.clone());
1979 }
1980 if let Some(depth) = opts.depth {
1981 params["depth"] = serde_json::Value::from(depth);
1982 }
1983 if let Some(boxes) = opts.boxes {
1984 params["boxes"] = serde_json::Value::Bool(boxes);
1985 }
1986 }
1987
1988 let response: AriaSnapshotResponse = self.channel().send("ariaSnapshot", params).await?;
1989 Ok(response.snapshot)
1990 }
1991
1992 pub(crate) async fn frame_resolve_selector(&self, selector: &str) -> Result<String> {
1998 #[derive(Deserialize)]
1999 struct ResolveSelectorResponse {
2000 #[serde(rename = "resolvedSelector")]
2001 resolved_selector: String,
2002 }
2003
2004 let response: ResolveSelectorResponse = self
2005 .channel()
2006 .send(
2007 "resolveSelector",
2008 serde_json::json!({
2009 "selector": selector,
2010 }),
2011 )
2012 .await?;
2013
2014 Ok(response.resolved_selector)
2015 }
2016
2017 pub(crate) async fn locator_highlight(
2024 &self,
2025 selector: &str,
2026 style: Option<&str>,
2027 ) -> Result<()> {
2028 let mut params = serde_json::json!({ "selector": selector });
2029 if let Some(style) = style {
2030 params["style"] = serde_json::Value::String(style.to_string());
2031 }
2032 self.channel().send_no_result("highlight", params).await
2033 }
2034
2035 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
2039 let params = serde_json::json!({
2040 "expression": expression,
2041 "arg": {
2042 "value": {"v": "null"},
2043 "handles": []
2044 }
2045 });
2046
2047 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
2048 Ok(())
2049 }
2050
2051 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
2063 let params = serde_json::json!({
2064 "expression": expression,
2065 "arg": {
2066 "value": {"v": "null"},
2067 "handles": []
2068 }
2069 });
2070
2071 #[derive(Deserialize)]
2072 struct EvaluateResult {
2073 value: serde_json::Value,
2074 }
2075
2076 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2077
2078 match &result.value {
2085 Value::Object(map) => {
2086 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
2087 Ok(s.to_string())
2089 } else if let Some(n) = map.get("n") {
2090 Ok(n.to_string())
2092 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
2093 Ok(b.to_string())
2095 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
2096 Ok(v.to_string())
2098 } else {
2099 Ok(result.value.to_string())
2101 }
2102 }
2103 _ => {
2104 Ok(result.value.to_string())
2106 }
2107 }
2108 }
2109
2110 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid()))]
2150 pub async fn evaluate<T: serde::Serialize>(
2151 &self,
2152 expression: &str,
2153 arg: Option<&T>,
2154 ) -> Result<Value> {
2155 let serialized_arg = match arg {
2157 Some(a) => serialize_argument(a),
2158 None => serialize_null(),
2159 };
2160
2161 let params = serde_json::json!({
2163 "expression": expression,
2164 "arg": serialized_arg
2165 });
2166
2167 #[derive(Deserialize)]
2169 struct EvaluateResult {
2170 value: serde_json::Value,
2171 }
2172
2173 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2174
2175 Ok(parse_result(&result.value))
2177 }
2178
2179 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2219 pub async fn add_style_tag(
2220 &self,
2221 options: crate::protocol::page::AddStyleTagOptions,
2222 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2223 options.validate()?;
2225
2226 let mut params = serde_json::json!({});
2228
2229 if let Some(content) = &options.content {
2230 params["content"] = serde_json::json!(content);
2231 }
2232
2233 if let Some(url) = &options.url {
2234 params["url"] = serde_json::json!(url);
2235 }
2236
2237 if let Some(path) = &options.path {
2238 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2240 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
2241 })?;
2242 params["content"] = serde_json::json!(css_content);
2243 }
2244
2245 #[derive(Deserialize)]
2246 struct AddStyleTagResponse {
2247 element: serde_json::Value,
2248 }
2249
2250 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
2251
2252 let guid = response.element["guid"].as_str().ok_or_else(|| {
2253 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
2254 })?;
2255
2256 let connection = self.base.connection();
2257 let handle: crate::protocol::ElementHandle = connection
2258 .get_typed::<crate::protocol::ElementHandle>(guid)
2259 .await?;
2260
2261 Ok(Arc::new(handle))
2262 }
2263
2264 pub(crate) async fn locator_dispatch_event(
2272 &self,
2273 selector: &str,
2274 type_: &str,
2275 event_init: Option<serde_json::Value>,
2276 ) -> Result<()> {
2277 let event_init_serialized = match event_init {
2280 Some(v) => serialize_argument(&v),
2281 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
2282 };
2283
2284 let params = serde_json::json!({
2285 "selector": selector,
2286 "type": type_,
2287 "eventInit": event_init_serialized,
2288 "strict": true,
2289 "timeout": crate::DEFAULT_TIMEOUT_MS
2290 });
2291
2292 self.channel().send_no_result("dispatchEvent", params).await
2293 }
2294
2295 pub(crate) async fn locator_bounding_box(
2305 &self,
2306 selector: &str,
2307 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
2308 let element = self.query_selector(selector).await?;
2309 match element {
2310 Some(handle) => handle.bounding_box().await,
2311 None => Ok(None),
2312 }
2313 }
2314
2315 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
2322 let element = self.query_selector(selector).await?;
2323 match element {
2324 Some(handle) => handle.scroll_into_view_if_needed().await,
2325 None => Err(crate::error::Error::ElementNotFound(format!(
2326 "Element not found: {}",
2327 selector
2328 ))),
2329 }
2330 }
2331
2332 pub(crate) async fn frame_expect(
2338 &self,
2339 selector: &str,
2340 expression: &str,
2341 expected_value: serde_json::Value,
2342 is_not: bool,
2343 timeout_ms: f64,
2344 ) -> Result<()> {
2345 #[derive(serde::Deserialize)]
2346 #[serde(rename_all = "camelCase")]
2347 struct ExpectResult {
2348 matches: bool,
2349 #[serde(default)]
2350 timed_out: Option<bool>,
2351 #[serde(default)]
2352 error_message: Option<String>,
2353 }
2354
2355 let params = serde_json::json!({
2356 "selector": selector,
2357 "expression": expression,
2358 "expectedValue": expected_value,
2359 "isNot": is_not,
2360 "timeout": timeout_ms
2361 });
2362
2363 let result: ExpectResult = self.channel().send("expect", params).await?;
2364
2365 if result.matches != is_not {
2366 Ok(())
2367 } else {
2368 let msg = result
2369 .error_message
2370 .unwrap_or_else(|| format!("Assertion '{}' failed", expression));
2371 if result.timed_out == Some(true) {
2372 Err(crate::error::Error::AssertionTimeout(msg))
2373 } else {
2374 Err(crate::error::Error::AssertionFailed(msg))
2375 }
2376 }
2377 }
2378
2379 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2389 pub async fn add_script_tag(
2390 &self,
2391 options: crate::protocol::page::AddScriptTagOptions,
2392 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2393 options.validate()?;
2395
2396 let mut params = serde_json::json!({});
2398
2399 if let Some(content) = &options.content {
2400 params["content"] = serde_json::json!(content);
2401 }
2402
2403 if let Some(url) = &options.url {
2404 params["url"] = serde_json::json!(url);
2405 }
2406
2407 if let Some(path) = &options.path {
2408 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2410 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
2411 })?;
2412 params["content"] = serde_json::json!(js_content);
2413 }
2414
2415 if let Some(type_) = &options.type_ {
2416 params["type"] = serde_json::json!(type_);
2417 }
2418
2419 #[derive(Deserialize)]
2420 struct AddScriptTagResponse {
2421 element: serde_json::Value,
2422 }
2423
2424 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
2425
2426 let guid = response.element["guid"].as_str().ok_or_else(|| {
2427 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
2428 })?;
2429
2430 let connection = self.base.connection();
2431 let handle: crate::protocol::ElementHandle = connection
2432 .get_typed::<crate::protocol::ElementHandle>(guid)
2433 .await?;
2434
2435 Ok(Arc::new(handle))
2436 }
2437}
2438
2439impl ChannelOwner for Frame {
2440 fn guid(&self) -> &str {
2441 self.base.guid()
2442 }
2443
2444 fn type_name(&self) -> &str {
2445 self.base.type_name()
2446 }
2447
2448 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
2449 self.base.parent()
2450 }
2451
2452 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
2453 self.base.connection()
2454 }
2455
2456 fn initializer(&self) -> &Value {
2457 self.base.initializer()
2458 }
2459
2460 fn channel(&self) -> &Channel {
2461 self.base.channel()
2462 }
2463
2464 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
2465 if let Ok(mut guard) = self.page.lock() {
2469 *guard = None;
2470 }
2471 self.base.dispose(reason)
2472 }
2473
2474 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
2475 self.base.adopt(child)
2476 }
2477
2478 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
2479 self.base.add_child(guid, child)
2480 }
2481
2482 fn remove_child(&self, guid: &str) {
2483 self.base.remove_child(guid)
2484 }
2485
2486 fn on_event(&self, method: &str, params: Value) {
2487 match method {
2488 "navigated" => {
2489 if let Some(url_value) = params.get("url")
2491 && let Some(url_str) = url_value.as_str()
2492 {
2493 if let Ok(mut url) = self.url.write() {
2495 *url = url_str.to_string();
2496 }
2497 }
2498 let self_clone = self.clone();
2500 tokio::spawn(async move {
2501 if let Some(page) = self_clone.page() {
2502 page.trigger_framenavigated_event(self_clone).await;
2503 }
2504 });
2505 }
2506 "loadstate" => {
2507 if let Some(add) = params.get("add").and_then(|v| v.as_str())
2510 && add == "load"
2511 {
2512 let self_clone = self.clone();
2513 tokio::spawn(async move {
2514 if let Some(page) = self_clone.page() {
2515 page.trigger_load_event().await;
2516 }
2517 });
2518 }
2519 }
2520 "detached" => {
2521 if let Ok(mut flag) = self.is_detached.write() {
2523 *flag = true;
2524 }
2525 }
2526 _ => {
2527 }
2529 }
2530 }
2531
2532 fn was_collected(&self) -> bool {
2533 self.base.was_collected()
2534 }
2535
2536 fn as_any(&self) -> &dyn Any {
2537 self
2538 }
2539}
2540
2541impl std::fmt::Debug for Frame {
2542 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2543 f.debug_struct("Frame").field("guid", &self.guid()).finish()
2544 }
2545}