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!({
247 "expression": expression,
248 "arg": {"value": {"v": "undefined"}, "handles": []}
249 });
250
251 #[derive(Deserialize)]
253 struct HandleRef {
254 guid: String,
255 }
256 #[derive(Deserialize)]
257 struct EvaluateHandleResponse {
258 handle: HandleRef,
259 }
260
261 let response: EvaluateHandleResponse = self
262 .channel()
263 .send("evaluateExpressionHandle", params)
264 .await?;
265
266 let guid = &response.handle.guid;
267
268 let handle = self
270 .base
271 .connection()
272 .wait_for_typed::<crate::protocol::ElementHandle>(guid)
273 .await?;
274
275 Ok(Arc::new(handle))
276 }
277
278 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
301 pub async fn evaluate_handle_js(
302 &self,
303 expression: &str,
304 ) -> Result<std::sync::Arc<crate::protocol::JSHandle>> {
305 let params = serde_json::json!({
310 "expression": expression,
311 "arg": {"value": {"v": "undefined"}, "handles": []}
312 });
313
314 #[derive(Deserialize)]
316 struct HandleRef {
317 guid: String,
318 }
319 #[derive(Deserialize)]
320 struct EvaluateHandleResponse {
321 handle: HandleRef,
322 }
323
324 let response: EvaluateHandleResponse = self
325 .channel()
326 .send("evaluateExpressionHandle", params)
327 .await?;
328
329 let guid = &response.handle.guid;
330
331 let handle = crate::protocol::JSHandle::wait_for(&self.base.connection(), guid).await?;
332
333 Ok(std::sync::Arc::new(handle))
334 }
335
336 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
348 pub(crate) async fn wait_for_function_internal(
349 &self,
350 expression: &str,
351 selector: Option<&str>,
352 options: impl Into<Option<crate::protocol::WaitForFunctionOptions>>,
353 ) -> Result<Option<std::sync::Arc<crate::protocol::JSHandle>>> {
354 let options = options.into().unwrap_or_default();
355
356 let timeout = options
360 .timeout
361 .or_else(|| self.page().map(|p| p.default_timeout_ms()))
362 .unwrap_or(crate::DEFAULT_TIMEOUT_MS);
363
364 let mut params = serde_json::json!({
365 "expression": expression,
366 "arg": {"value": {"v": "undefined"}, "handles": []},
367 "timeout": timeout,
368 });
369 if let Some(interval) = options.polling_interval {
370 params["pollingInterval"] = serde_json::json!(interval);
371 }
372 if let Some(selector) = selector {
373 params["selector"] = serde_json::json!(selector);
374 params["strict"] = serde_json::json!(true);
375 }
376
377 #[derive(Deserialize)]
378 struct HandleRef {
379 guid: String,
380 }
381 #[derive(Deserialize)]
382 struct WaitForFunctionResponse {
383 handle: Option<HandleRef>,
384 }
385
386 let response: WaitForFunctionResponse =
387 self.channel().send("waitForFunction", params).await?;
388
389 let Some(handle_ref) = response.handle else {
390 return Ok(None);
391 };
392
393 let handle =
394 crate::protocol::JSHandle::wait_for(&self.base.connection(), &handle_ref.guid).await?;
395 Ok(Some(std::sync::Arc::new(handle)))
396 }
397
398 pub async fn wait_for_function(
410 &self,
411 expression: &str,
412 options: impl Into<Option<crate::protocol::WaitForFunctionOptions>>,
413 ) -> Result<std::sync::Arc<crate::protocol::JSHandle>> {
414 self.wait_for_function_internal(expression, None, options)
415 .await?
416 .ok_or_else(|| {
417 crate::error::Error::ProtocolError(
418 "waitForFunction returned no handle for a selector-less wait".to_string(),
419 )
420 })
421 }
422
423 pub(crate) async fn evaluate_with_fn_arg(
431 &self,
432 expression: &str,
433 binding_name: &str,
434 ) -> Result<Value> {
435 let params = serde_json::json!({
436 "expression": expression,
437 "arg": { "value": { "fn": binding_name }, "handles": [] },
438 });
439
440 #[derive(Deserialize)]
441 struct EvaluateResult {
442 value: serde_json::Value,
443 }
444
445 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
446 Ok(parse_result(&result.value))
447 }
448
449 pub fn locator(&self, selector: impl Into<String>) -> crate::protocol::Locator {
464 let page = self
465 .page()
466 .expect("Frame::locator() called before set_page(); call page.main_frame() first");
467 crate::protocol::Locator::new(Arc::new(self.clone()), selector.into(), page)
468 }
469
470 pub fn get_by_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
474 self.locator(crate::protocol::locator::get_by_text_selector(text, exact))
475 }
476
477 pub fn get_by_label(&self, text: &str, exact: bool) -> crate::protocol::Locator {
481 self.locator(crate::protocol::locator::get_by_label_selector(text, exact))
482 }
483
484 pub fn get_by_placeholder(&self, text: &str, exact: bool) -> crate::protocol::Locator {
488 self.locator(crate::protocol::locator::get_by_placeholder_selector(
489 text, exact,
490 ))
491 }
492
493 pub fn get_by_alt_text(&self, text: &str, exact: bool) -> crate::protocol::Locator {
497 self.locator(crate::protocol::locator::get_by_alt_text_selector(
498 text, exact,
499 ))
500 }
501
502 pub fn get_by_title(&self, text: &str, exact: bool) -> crate::protocol::Locator {
506 self.locator(crate::protocol::locator::get_by_title_selector(text, exact))
507 }
508
509 pub fn get_by_test_id(&self, test_id: &str) -> crate::protocol::Locator {
516 use crate::server::channel_owner::ChannelOwner;
517 let attr = self.connection().selectors().test_id_attribute();
518 self.locator(crate::protocol::locator::get_by_test_id_selector_with_attr(
519 test_id, &attr,
520 ))
521 }
522
523 pub fn get_by_role(
527 &self,
528 role: crate::protocol::locator::AriaRole,
529 options: Option<crate::protocol::locator::GetByRoleOptions>,
530 ) -> crate::protocol::Locator {
531 self.locator(crate::protocol::locator::get_by_role_selector(
532 role, options,
533 ))
534 }
535
536 fn channel(&self) -> &Channel {
538 self.base.channel()
539 }
540
541 pub fn url(&self) -> String {
547 self.url.read().unwrap().clone()
548 }
549
550 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid(), url = %url, status = tracing::field::Empty))]
564 pub async fn goto(
565 &self,
566 url: &str,
567 options: impl Into<Option<GotoOptions>>,
568 ) -> Result<Option<Response>> {
569 let options = options.into();
570 let mut params = serde_json::json!({
572 "url": url,
573 });
574
575 if let Some(opts) = options {
577 if let Some(timeout) = opts.timeout {
578 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
579 } else {
580 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
582 }
583 if let Some(wait_until) = opts.wait_until {
584 params["waitUntil"] = serde_json::json!(wait_until.as_str());
585 }
586 } else {
587 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
589 }
590
591 #[derive(Deserialize)]
594 struct GotoResponse {
595 response: Option<ResponseReference>,
596 }
597
598 #[derive(Deserialize)]
599 struct ResponseReference {
600 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
601 guid: Arc<str>,
602 }
603
604 let goto_result: GotoResponse = self.channel().send("goto", params).await?;
605
606 if let Some(response_ref) = goto_result.response {
608 let response_arc = self
610 .connection()
611 .wait_for_object(&response_ref.guid)
612 .await?;
613
614 let initializer = response_arc.initializer();
617
618 let status = initializer["status"].as_u64().ok_or_else(|| {
620 crate::error::Error::ProtocolError("Response missing status".to_string())
621 })? as u16;
622
623 let headers = initializer["headers"]
625 .as_array()
626 .ok_or_else(|| {
627 crate::error::Error::ProtocolError("Response missing headers".to_string())
628 })?
629 .iter()
630 .filter_map(|h| {
631 let name = h["name"].as_str()?;
632 let value = h["value"].as_str()?;
633 Some((name.to_string(), value.to_string()))
634 })
635 .collect();
636
637 tracing::Span::current().record("status", status);
638 Ok(Some(Response::new(
639 initializer["url"]
640 .as_str()
641 .ok_or_else(|| {
642 crate::error::Error::ProtocolError("Response missing url".to_string())
643 })?
644 .to_string(),
645 status,
646 initializer["statusText"].as_str().unwrap_or("").to_string(),
647 headers,
648 Some(response_arc),
649 )))
650 } else {
651 Ok(None)
654 }
655 }
656
657 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
661 pub async fn title(&self) -> Result<String> {
662 #[derive(Deserialize)]
663 struct TitleResponse {
664 value: String,
665 }
666
667 let response: TitleResponse = self.channel().send("title", serde_json::json!({})).await?;
668 Ok(response.value)
669 }
670
671 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
675 pub async fn content(&self) -> Result<String> {
676 #[derive(Deserialize)]
677 struct ContentResponse {
678 value: String,
679 }
680
681 let response: ContentResponse = self
682 .channel()
683 .send("content", serde_json::json!({}))
684 .await?;
685 Ok(response.value)
686 }
687
688 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
692 pub async fn set_content(
693 &self,
694 html: &str,
695 options: impl Into<Option<GotoOptions>>,
696 ) -> Result<()> {
697 let options = options.into();
698 let mut params = serde_json::json!({
699 "html": html,
700 });
701
702 if let Some(opts) = options {
703 if let Some(timeout) = opts.timeout {
704 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
705 } else {
706 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
707 }
708 if let Some(wait_until) = opts.wait_until {
709 params["waitUntil"] = serde_json::json!(wait_until.as_str());
710 }
711 } else {
712 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
713 }
714
715 self.channel().send_no_result("setContent", params).await
716 }
717
718 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
726 pub async fn wait_for_load_state(&self, state: Option<WaitUntil>) -> Result<()> {
727 let target_state = state.unwrap_or(WaitUntil::Load);
728
729 let js_check = match target_state {
730 WaitUntil::Load => "document.readyState === 'complete'",
732 WaitUntil::DomContentLoaded => "document.readyState !== 'loading'",
734 WaitUntil::NetworkIdle => "document.readyState === 'complete'",
737 WaitUntil::Commit => "document.readyState !== 'loading'",
739 };
740
741 let timeout_ms = crate::DEFAULT_TIMEOUT_MS as u64;
742 let poll_interval = std::time::Duration::from_millis(50);
743 let start = std::time::Instant::now();
744
745 loop {
746 #[derive(Deserialize)]
747 struct EvalResponse {
748 value: serde_json::Value,
749 }
750
751 let result: EvalResponse = self
752 .channel()
753 .send(
754 "evaluateExpression",
755 serde_json::json!({
756 "expression": js_check,
757 "isFunction": false,
758 "arg": crate::protocol::serialize_null(),
759 }),
760 )
761 .await?;
762
763 let is_ready = result
765 .value
766 .as_object()
767 .and_then(|m| m.get("b"))
768 .and_then(|v| v.as_bool())
769 .unwrap_or(false);
770
771 if is_ready {
772 return Ok(());
773 }
774
775 if start.elapsed().as_millis() as u64 >= timeout_ms {
776 return Err(crate::error::Error::Timeout(format!(
777 "wait_for_load_state({}) timed out after {}ms",
778 target_state.as_str(),
779 timeout_ms
780 )));
781 }
782
783 tokio::time::sleep(poll_interval).await;
784 }
785 }
786
787 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid(), url = %url))]
794 pub async fn wait_for_url(
795 &self,
796 url: &str,
797 options: impl Into<Option<GotoOptions>>,
798 ) -> Result<()> {
799 let options = options.into();
800 let timeout_ms = options
801 .as_ref()
802 .and_then(|o| o.timeout)
803 .map(|d| d.as_millis() as u64)
804 .unwrap_or(crate::DEFAULT_TIMEOUT_MS as u64);
805
806 let matcher = if url.contains('*') {
810 Some(crate::protocol::glob::GlobMatcher::new(url))
811 } else {
812 None
813 };
814
815 let poll_interval = std::time::Duration::from_millis(50);
816 let start = std::time::Instant::now();
817
818 loop {
819 let current_url = self.url();
820
821 let matches = match &matcher {
822 Some(matcher) => matcher.as_ref().is_some_and(|m| m.matches(¤t_url)),
824 None => current_url == url,
825 };
826
827 if matches {
828 if let Some(ref opts) = options
830 && let Some(wait_until) = opts.wait_until
831 {
832 self.wait_for_load_state(Some(wait_until)).await?;
833 }
834 return Ok(());
835 }
836
837 if start.elapsed().as_millis() as u64 >= timeout_ms {
838 return Err(crate::error::Error::Timeout(format!(
839 "wait_for_url({}) timed out after {}ms, current URL: {}",
840 url, timeout_ms, current_url
841 )));
842 }
843
844 tokio::time::sleep(poll_interval).await;
845 }
846 }
847
848 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
852 pub async fn query_selector(
853 &self,
854 selector: &str,
855 ) -> Result<Option<Arc<crate::protocol::ElementHandle>>> {
856 let response: serde_json::Value = self
857 .channel()
858 .send(
859 "querySelector",
860 serde_json::json!({
861 "selector": selector
862 }),
863 )
864 .await?;
865
866 if response.as_object().map(|o| o.is_empty()).unwrap_or(true) {
868 return Ok(None);
869 }
870
871 let element_value = if let Some(elem) = response.get("element") {
873 elem
874 } else if let Some(elem) = response.get("handle") {
875 elem
876 } else {
877 &response
879 };
880
881 if element_value.is_null() {
882 return Ok(None);
883 }
884
885 let guid = element_value["guid"].as_str().ok_or_else(|| {
887 crate::error::Error::ProtocolError("Element GUID missing".to_string())
888 })?;
889
890 let connection = self.base.connection();
892 let handle: crate::protocol::ElementHandle = connection
893 .get_typed::<crate::protocol::ElementHandle>(guid)
894 .await?;
895
896 Ok(Some(Arc::new(handle)))
897 }
898
899 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
903 pub async fn query_selector_all(
904 &self,
905 selector: &str,
906 ) -> Result<Vec<Arc<crate::protocol::ElementHandle>>> {
907 #[derive(Deserialize)]
908 struct QueryAllResponse {
909 elements: Vec<serde_json::Value>,
910 }
911
912 let response: QueryAllResponse = self
913 .channel()
914 .send(
915 "querySelectorAll",
916 serde_json::json!({
917 "selector": selector
918 }),
919 )
920 .await?;
921
922 let connection = self.base.connection();
924 let mut handles = Vec::new();
925
926 for element_value in response.elements {
927 let guid = element_value["guid"].as_str().ok_or_else(|| {
928 crate::error::Error::ProtocolError("Element GUID missing".to_string())
929 })?;
930
931 let handle: crate::protocol::ElementHandle = connection
932 .get_typed::<crate::protocol::ElementHandle>(guid)
933 .await?;
934
935 handles.push(Arc::new(handle));
936 }
937
938 Ok(handles)
939 }
940
941 pub(crate) async fn locator_count(&self, selector: &str) -> Result<usize> {
946 #[derive(Deserialize)]
948 struct QueryAllResponse {
949 elements: Vec<serde_json::Value>,
950 }
951
952 let response: QueryAllResponse = self
953 .channel()
954 .send(
955 "querySelectorAll",
956 serde_json::json!({
957 "selector": selector
958 }),
959 )
960 .await?;
961
962 Ok(response.elements.len())
963 }
964
965 pub(crate) async fn locator_text_content(&self, selector: &str) -> Result<Option<String>> {
967 #[derive(Deserialize)]
968 struct TextContentResponse {
969 value: Option<String>,
970 }
971
972 let response: TextContentResponse = self
973 .channel()
974 .send(
975 "textContent",
976 serde_json::json!({
977 "selector": selector,
978 "strict": true,
979 "timeout": crate::DEFAULT_TIMEOUT_MS
980 }),
981 )
982 .await?;
983
984 Ok(response.value)
985 }
986
987 pub(crate) async fn locator_inner_text(&self, selector: &str) -> Result<String> {
989 #[derive(Deserialize)]
990 struct InnerTextResponse {
991 value: String,
992 }
993
994 let response: InnerTextResponse = self
995 .channel()
996 .send(
997 "innerText",
998 serde_json::json!({
999 "selector": selector,
1000 "strict": true,
1001 "timeout": crate::DEFAULT_TIMEOUT_MS
1002 }),
1003 )
1004 .await?;
1005
1006 Ok(response.value)
1007 }
1008
1009 pub(crate) async fn locator_inner_html(&self, selector: &str) -> Result<String> {
1011 #[derive(Deserialize)]
1012 struct InnerHTMLResponse {
1013 value: String,
1014 }
1015
1016 let response: InnerHTMLResponse = self
1017 .channel()
1018 .send(
1019 "innerHTML",
1020 serde_json::json!({
1021 "selector": selector,
1022 "strict": true,
1023 "timeout": crate::DEFAULT_TIMEOUT_MS
1024 }),
1025 )
1026 .await?;
1027
1028 Ok(response.value)
1029 }
1030
1031 pub(crate) async fn locator_get_attribute(
1033 &self,
1034 selector: &str,
1035 name: &str,
1036 ) -> Result<Option<String>> {
1037 #[derive(Deserialize)]
1038 struct GetAttributeResponse {
1039 value: Option<String>,
1040 }
1041
1042 let response: GetAttributeResponse = self
1043 .channel()
1044 .send(
1045 "getAttribute",
1046 serde_json::json!({
1047 "selector": selector,
1048 "name": name,
1049 "strict": true,
1050 "timeout": crate::DEFAULT_TIMEOUT_MS
1051 }),
1052 )
1053 .await?;
1054
1055 Ok(response.value)
1056 }
1057
1058 pub(crate) async fn locator_is_visible(&self, selector: &str) -> Result<bool> {
1060 #[derive(Deserialize)]
1061 struct IsVisibleResponse {
1062 value: bool,
1063 }
1064
1065 let response: IsVisibleResponse = self
1066 .channel()
1067 .send(
1068 "isVisible",
1069 serde_json::json!({
1070 "selector": selector,
1071 "strict": true,
1072 "timeout": crate::DEFAULT_TIMEOUT_MS
1073 }),
1074 )
1075 .await?;
1076
1077 Ok(response.value)
1078 }
1079
1080 pub(crate) async fn locator_is_enabled(&self, selector: &str) -> Result<bool> {
1082 #[derive(Deserialize)]
1083 struct IsEnabledResponse {
1084 value: bool,
1085 }
1086
1087 let response: IsEnabledResponse = self
1088 .channel()
1089 .send(
1090 "isEnabled",
1091 serde_json::json!({
1092 "selector": selector,
1093 "strict": true,
1094 "timeout": crate::DEFAULT_TIMEOUT_MS
1095 }),
1096 )
1097 .await?;
1098
1099 Ok(response.value)
1100 }
1101
1102 pub(crate) async fn locator_is_checked(&self, selector: &str) -> Result<bool> {
1104 #[derive(Deserialize)]
1105 struct IsCheckedResponse {
1106 value: bool,
1107 }
1108
1109 let response: IsCheckedResponse = self
1110 .channel()
1111 .send(
1112 "isChecked",
1113 serde_json::json!({
1114 "selector": selector,
1115 "strict": true,
1116 "timeout": crate::DEFAULT_TIMEOUT_MS
1117 }),
1118 )
1119 .await?;
1120
1121 Ok(response.value)
1122 }
1123
1124 pub(crate) async fn locator_is_editable(&self, selector: &str) -> Result<bool> {
1126 #[derive(Deserialize)]
1127 struct IsEditableResponse {
1128 value: bool,
1129 }
1130
1131 let response: IsEditableResponse = self
1132 .channel()
1133 .send(
1134 "isEditable",
1135 serde_json::json!({
1136 "selector": selector,
1137 "strict": true,
1138 "timeout": crate::DEFAULT_TIMEOUT_MS
1139 }),
1140 )
1141 .await?;
1142
1143 Ok(response.value)
1144 }
1145
1146 pub(crate) async fn locator_is_hidden(&self, selector: &str) -> Result<bool> {
1148 #[derive(Deserialize)]
1149 struct IsHiddenResponse {
1150 value: bool,
1151 }
1152
1153 let response: IsHiddenResponse = self
1154 .channel()
1155 .send(
1156 "isHidden",
1157 serde_json::json!({
1158 "selector": selector,
1159 "strict": true,
1160 "timeout": crate::DEFAULT_TIMEOUT_MS
1161 }),
1162 )
1163 .await?;
1164
1165 Ok(response.value)
1166 }
1167
1168 pub(crate) async fn locator_is_disabled(&self, selector: &str) -> Result<bool> {
1170 #[derive(Deserialize)]
1171 struct IsDisabledResponse {
1172 value: bool,
1173 }
1174
1175 let response: IsDisabledResponse = self
1176 .channel()
1177 .send(
1178 "isDisabled",
1179 serde_json::json!({
1180 "selector": selector,
1181 "strict": true,
1182 "timeout": crate::DEFAULT_TIMEOUT_MS
1183 }),
1184 )
1185 .await?;
1186
1187 Ok(response.value)
1188 }
1189
1190 pub(crate) async fn locator_is_focused(&self, selector: &str) -> Result<bool> {
1196 #[derive(Deserialize)]
1197 struct EvaluateResult {
1198 value: serde_json::Value,
1199 }
1200
1201 let script = r#"selector => {
1204 const elements = document.querySelectorAll(selector);
1205 if (elements.length === 0) return false;
1206 const element = elements[0];
1207 return document.activeElement === element;
1208 }"#;
1209
1210 let params = serde_json::json!({
1211 "expression": script,
1212 "arg": {
1213 "value": {"s": selector},
1214 "handles": []
1215 }
1216 });
1217
1218 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1219
1220 if let serde_json::Value::Object(map) = &result.value
1222 && let Some(b) = map.get("b").and_then(|v| v.as_bool())
1223 {
1224 return Ok(b);
1225 }
1226
1227 Ok(result.value.to_string().to_lowercase().contains("true"))
1229 }
1230
1231 pub(crate) async fn locator_click(
1235 &self,
1236 selector: &str,
1237 options: Option<crate::protocol::ClickOptions>,
1238 ) -> Result<()> {
1239 let mut params = serde_json::json!({
1240 "selector": selector,
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()
1256 .send_no_result("click", params)
1257 .await
1258 .map_err(|e| match e {
1259 Error::Timeout(msg) => {
1260 Error::Timeout(format!("{} (selector: '{}')", msg, selector))
1261 }
1262 other => other,
1263 })
1264 }
1265
1266 pub(crate) async fn locator_dblclick(
1268 &self,
1269 selector: &str,
1270 options: Option<crate::protocol::ClickOptions>,
1271 ) -> Result<()> {
1272 let mut params = serde_json::json!({
1273 "selector": selector,
1274 "strict": true
1275 });
1276
1277 if let Some(opts) = options {
1278 let opts_json = opts.to_json();
1279 if let Some(obj) = params.as_object_mut()
1280 && let Some(opts_obj) = opts_json.as_object()
1281 {
1282 obj.extend(opts_obj.clone());
1283 }
1284 } else {
1285 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1286 }
1287
1288 self.channel().send_no_result("dblclick", params).await
1289 }
1290
1291 pub(crate) async fn locator_fill(
1293 &self,
1294 selector: &str,
1295 text: &str,
1296 options: Option<crate::protocol::FillOptions>,
1297 ) -> Result<()> {
1298 let mut params = serde_json::json!({
1299 "selector": selector,
1300 "value": text,
1301 "strict": true
1302 });
1303
1304 if let Some(opts) = options {
1305 let opts_json = opts.to_json();
1306 if let Some(obj) = params.as_object_mut()
1307 && let Some(opts_obj) = opts_json.as_object()
1308 {
1309 obj.extend(opts_obj.clone());
1310 }
1311 } else {
1312 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1313 }
1314
1315 self.channel().send_no_result("fill", params).await
1316 }
1317
1318 pub(crate) async fn locator_clear(
1320 &self,
1321 selector: &str,
1322 options: Option<crate::protocol::FillOptions>,
1323 ) -> Result<()> {
1324 let mut params = serde_json::json!({
1325 "selector": selector,
1326 "value": "",
1327 "strict": true
1328 });
1329
1330 if let Some(opts) = options {
1331 let opts_json = opts.to_json();
1332 if let Some(obj) = params.as_object_mut()
1333 && let Some(opts_obj) = opts_json.as_object()
1334 {
1335 obj.extend(opts_obj.clone());
1336 }
1337 } else {
1338 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1339 }
1340
1341 self.channel().send_no_result("fill", params).await
1342 }
1343
1344 pub(crate) async fn locator_press(
1346 &self,
1347 selector: &str,
1348 key: &str,
1349 options: Option<crate::protocol::PressOptions>,
1350 ) -> Result<()> {
1351 let mut params = serde_json::json!({
1352 "selector": selector,
1353 "key": key,
1354 "strict": true
1355 });
1356
1357 if let Some(opts) = options {
1358 let opts_json = opts.to_json();
1359 if let Some(obj) = params.as_object_mut()
1360 && let Some(opts_obj) = opts_json.as_object()
1361 {
1362 obj.extend(opts_obj.clone());
1363 }
1364 } else {
1365 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1366 }
1367
1368 self.channel().send_no_result("press", params).await
1369 }
1370
1371 pub(crate) async fn locator_focus(&self, selector: &str) -> Result<()> {
1373 self.channel()
1374 .send_no_result(
1375 "focus",
1376 serde_json::json!({
1377 "selector": selector,
1378 "strict": true,
1379 "timeout": crate::DEFAULT_TIMEOUT_MS
1380 }),
1381 )
1382 .await
1383 }
1384
1385 pub(crate) async fn locator_blur(&self, selector: &str) -> Result<()> {
1387 self.channel()
1388 .send_no_result(
1389 "blur",
1390 serde_json::json!({
1391 "selector": selector,
1392 "strict": true,
1393 "timeout": crate::DEFAULT_TIMEOUT_MS
1394 }),
1395 )
1396 .await
1397 }
1398
1399 pub(crate) async fn locator_press_sequentially(
1403 &self,
1404 selector: &str,
1405 text: &str,
1406 options: Option<crate::protocol::PressSequentiallyOptions>,
1407 ) -> Result<()> {
1408 let mut params = serde_json::json!({
1409 "selector": selector,
1410 "text": text,
1411 "strict": true
1412 });
1413
1414 if let Some(opts) = options {
1415 let opts_json = opts.to_json();
1416 if let Some(obj) = params.as_object_mut()
1417 && let Some(opts_obj) = opts_json.as_object()
1418 {
1419 obj.extend(opts_obj.clone());
1420 }
1421 } else {
1422 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1423 }
1424
1425 self.channel().send_no_result("type", params).await
1426 }
1427
1428 pub(crate) async fn locator_all_inner_texts(&self, selector: &str) -> Result<Vec<String>> {
1430 #[derive(serde::Deserialize)]
1431 struct EvaluateResult {
1432 value: serde_json::Value,
1433 }
1434
1435 let params = serde_json::json!({
1438 "selector": selector,
1439 "expression": "ee => ee.map(e => e.innerText)",
1440 "isFunction": true,
1441 "arg": {
1442 "value": {"v": "null"},
1443 "handles": []
1444 }
1445 });
1446
1447 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1448
1449 Self::parse_string_array(result.value)
1450 }
1451
1452 pub(crate) async fn locator_all_text_contents(&self, selector: &str) -> Result<Vec<String>> {
1454 #[derive(serde::Deserialize)]
1455 struct EvaluateResult {
1456 value: serde_json::Value,
1457 }
1458
1459 let params = serde_json::json!({
1462 "selector": selector,
1463 "expression": "ee => ee.map(e => e.textContent || '')",
1464 "isFunction": true,
1465 "arg": {
1466 "value": {"v": "null"},
1467 "handles": []
1468 }
1469 });
1470
1471 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1472
1473 Self::parse_string_array(result.value)
1474 }
1475
1476 pub(crate) async fn locator_tap(
1483 &self,
1484 selector: &str,
1485 options: Option<crate::protocol::TapOptions>,
1486 ) -> Result<()> {
1487 let mut params = serde_json::json!({
1488 "selector": selector,
1489 "strict": true
1490 });
1491
1492 if let Some(opts) = options {
1493 let opts_json = opts.to_json();
1494 if let Some(obj) = params.as_object_mut()
1495 && let Some(opts_obj) = opts_json.as_object()
1496 {
1497 obj.extend(opts_obj.clone());
1498 }
1499 } else {
1500 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1501 }
1502
1503 self.channel().send_no_result("tap", params).await
1504 }
1505
1506 pub(crate) async fn locator_drag_to(
1512 &self,
1513 source_selector: &str,
1514 target_selector: &str,
1515 options: Option<crate::protocol::DragToOptions>,
1516 ) -> Result<()> {
1517 let mut params = serde_json::json!({
1518 "source": source_selector,
1519 "target": target_selector,
1520 "strict": true
1521 });
1522
1523 if let Some(opts) = options {
1524 let opts_json = opts.to_json();
1525 if let Some(obj) = params.as_object_mut()
1526 && let Some(opts_obj) = opts_json.as_object()
1527 {
1528 obj.extend(opts_obj.clone());
1529 }
1530 } else {
1531 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1532 }
1533
1534 self.channel().send_no_result("dragAndDrop", params).await
1535 }
1536
1537 pub(crate) async fn locator_drop(
1541 &self,
1542 selector: &str,
1543 options: crate::protocol::DropOptions,
1544 ) -> Result<()> {
1545 let mut params = serde_json::json!({
1546 "selector": selector,
1547 "strict": true,
1548 });
1549
1550 let opts_json = options.to_json();
1551 if let Some(obj) = params.as_object_mut()
1552 && let Some(opts_obj) = opts_json.as_object()
1553 {
1554 obj.extend(opts_obj.clone());
1555 }
1556
1557 self.channel().send_no_result("drop", params).await
1558 }
1559
1560 pub(crate) async fn locator_wait_for(
1567 &self,
1568 selector: &str,
1569 options: Option<crate::protocol::WaitForOptions>,
1570 ) -> Result<()> {
1571 let mut params = serde_json::json!({
1572 "selector": selector,
1573 "strict": true
1574 });
1575
1576 if let Some(opts) = options {
1577 let opts_json = opts.to_json();
1578 if let Some(obj) = params.as_object_mut()
1579 && let Some(opts_obj) = opts_json.as_object()
1580 {
1581 obj.extend(opts_obj.clone());
1582 }
1583 } else {
1584 params["state"] = serde_json::json!("visible");
1586 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1587 }
1588
1589 let _: serde_json::Value = self.channel().send("waitForSelector", params).await?;
1591 Ok(())
1592 }
1593
1594 pub(crate) async fn locator_evaluate<T: serde::Serialize>(
1601 &self,
1602 selector: &str,
1603 expression: &str,
1604 arg: Option<T>,
1605 ) -> Result<serde_json::Value> {
1606 let serialized_arg = match arg {
1607 Some(a) => serialize_argument(&a),
1608 None => serialize_null(),
1609 };
1610
1611 let params = serde_json::json!({
1612 "selector": selector,
1613 "expression": expression,
1614 "isFunction": true,
1615 "arg": serialized_arg,
1616 "strict": true
1617 });
1618
1619 #[derive(Deserialize)]
1620 struct EvaluateResult {
1621 value: serde_json::Value,
1622 }
1623
1624 let result: EvaluateResult = self.channel().send("evalOnSelector", params).await?;
1625 Ok(parse_result(&result.value))
1626 }
1627
1628 pub(crate) async fn locator_evaluate_all<T: serde::Serialize>(
1635 &self,
1636 selector: &str,
1637 expression: &str,
1638 arg: Option<T>,
1639 ) -> Result<serde_json::Value> {
1640 let serialized_arg = match arg {
1641 Some(a) => serialize_argument(&a),
1642 None => serialize_null(),
1643 };
1644
1645 let params = serde_json::json!({
1646 "selector": selector,
1647 "expression": expression,
1648 "isFunction": true,
1649 "arg": serialized_arg
1650 });
1651
1652 #[derive(Deserialize)]
1653 struct EvaluateResult {
1654 value: serde_json::Value,
1655 }
1656
1657 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1658 Ok(parse_result(&result.value))
1659 }
1660
1661 fn parse_string_array(value: serde_json::Value) -> Result<Vec<String>> {
1666 let array = if let Some(arr) = value.get("a").and_then(|v| v.as_array()) {
1668 arr.clone()
1669 } else if let Some(arr) = value.as_array() {
1670 arr.clone()
1671 } else {
1672 return Ok(Vec::new());
1673 };
1674
1675 let mut result = Vec::with_capacity(array.len());
1676 for item in &array {
1677 let s = if let Some(s) = item.get("s").and_then(|v| v.as_str()) {
1679 s.to_string()
1680 } else if let Some(s) = item.as_str() {
1681 s.to_string()
1682 } else if item.is_null() {
1683 String::new()
1684 } else {
1685 item.to_string()
1686 };
1687 result.push(s);
1688 }
1689 Ok(result)
1690 }
1691
1692 pub(crate) async fn locator_check(
1693 &self,
1694 selector: &str,
1695 options: Option<crate::protocol::CheckOptions>,
1696 ) -> Result<()> {
1697 let mut params = serde_json::json!({
1698 "selector": selector,
1699 "strict": true
1700 });
1701
1702 if let Some(opts) = options {
1703 let opts_json = opts.to_json();
1704 if let Some(obj) = params.as_object_mut()
1705 && let Some(opts_obj) = opts_json.as_object()
1706 {
1707 obj.extend(opts_obj.clone());
1708 }
1709 } else {
1710 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1711 }
1712
1713 self.channel().send_no_result("check", params).await
1714 }
1715
1716 pub(crate) async fn locator_uncheck(
1717 &self,
1718 selector: &str,
1719 options: Option<crate::protocol::CheckOptions>,
1720 ) -> Result<()> {
1721 let mut params = serde_json::json!({
1722 "selector": selector,
1723 "strict": true
1724 });
1725
1726 if let Some(opts) = options {
1727 let opts_json = opts.to_json();
1728 if let Some(obj) = params.as_object_mut()
1729 && let Some(opts_obj) = opts_json.as_object()
1730 {
1731 obj.extend(opts_obj.clone());
1732 }
1733 } else {
1734 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1735 }
1736
1737 self.channel().send_no_result("uncheck", params).await
1738 }
1739
1740 pub(crate) async fn locator_hover(
1741 &self,
1742 selector: &str,
1743 options: Option<crate::protocol::HoverOptions>,
1744 ) -> Result<()> {
1745 let mut params = serde_json::json!({
1746 "selector": selector,
1747 "strict": true
1748 });
1749
1750 if let Some(opts) = options {
1751 let opts_json = opts.to_json();
1752 if let Some(obj) = params.as_object_mut()
1753 && let Some(opts_obj) = opts_json.as_object()
1754 {
1755 obj.extend(opts_obj.clone());
1756 }
1757 } else {
1758 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1759 }
1760
1761 self.channel().send_no_result("hover", params).await
1762 }
1763
1764 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
1765 #[derive(Deserialize)]
1766 struct InputValueResponse {
1767 value: String,
1768 }
1769
1770 let response: InputValueResponse = self
1771 .channel()
1772 .send(
1773 "inputValue",
1774 serde_json::json!({
1775 "selector": selector,
1776 "strict": true,
1777 "timeout": crate::DEFAULT_TIMEOUT_MS }),
1779 )
1780 .await?;
1781
1782 Ok(response.value)
1783 }
1784
1785 pub(crate) async fn locator_select_option(
1786 &self,
1787 selector: &str,
1788 value: crate::protocol::SelectOption,
1789 options: Option<crate::protocol::SelectOptions>,
1790 ) -> Result<Vec<String>> {
1791 #[derive(Deserialize)]
1792 struct SelectOptionResponse {
1793 values: Vec<String>,
1794 }
1795
1796 let mut params = serde_json::json!({
1797 "selector": selector,
1798 "strict": true,
1799 "options": [value.to_json()]
1800 });
1801
1802 if let Some(opts) = options {
1803 let opts_json = opts.to_json();
1804 if let Some(obj) = params.as_object_mut()
1805 && let Some(opts_obj) = opts_json.as_object()
1806 {
1807 obj.extend(opts_obj.clone());
1808 }
1809 } else {
1810 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1812 }
1813
1814 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1815
1816 Ok(response.values)
1817 }
1818
1819 pub(crate) async fn locator_select_option_multiple(
1820 &self,
1821 selector: &str,
1822 values: Vec<crate::protocol::SelectOption>,
1823 options: Option<crate::protocol::SelectOptions>,
1824 ) -> Result<Vec<String>> {
1825 #[derive(Deserialize)]
1826 struct SelectOptionResponse {
1827 values: Vec<String>,
1828 }
1829
1830 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
1831
1832 let mut params = serde_json::json!({
1833 "selector": selector,
1834 "strict": true,
1835 "options": values_array
1836 });
1837
1838 if let Some(opts) = options {
1839 let opts_json = opts.to_json();
1840 if let Some(obj) = params.as_object_mut()
1841 && let Some(opts_obj) = opts_json.as_object()
1842 {
1843 obj.extend(opts_obj.clone());
1844 }
1845 } else {
1846 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1848 }
1849
1850 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1851
1852 Ok(response.values)
1853 }
1854
1855 pub(crate) async fn locator_set_input_files(
1856 &self,
1857 selector: &str,
1858 file: &std::path::PathBuf,
1859 ) -> Result<()> {
1860 use base64::{Engine as _, engine::general_purpose};
1861 use std::io::Read;
1862
1863 let mut file_handle = std::fs::File::open(file)?;
1865 let mut buffer = Vec::new();
1866 file_handle.read_to_end(&mut buffer)?;
1867
1868 let base64_content = general_purpose::STANDARD.encode(&buffer);
1870
1871 let file_name = file
1873 .file_name()
1874 .and_then(|n| n.to_str())
1875 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
1876
1877 self.channel()
1878 .send_no_result(
1879 "setInputFiles",
1880 serde_json::json!({
1881 "selector": selector,
1882 "strict": true,
1883 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
1885 "name": file_name,
1886 "buffer": base64_content
1887 }]
1888 }),
1889 )
1890 .await
1891 }
1892
1893 pub(crate) async fn locator_set_input_files_multiple(
1894 &self,
1895 selector: &str,
1896 files: &[&std::path::PathBuf],
1897 ) -> Result<()> {
1898 use base64::{Engine as _, engine::general_purpose};
1899 use std::io::Read;
1900
1901 if files.is_empty() {
1903 return self
1904 .channel()
1905 .send_no_result(
1906 "setInputFiles",
1907 serde_json::json!({
1908 "selector": selector,
1909 "strict": true,
1910 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
1912 }),
1913 )
1914 .await;
1915 }
1916
1917 let mut file_objects = Vec::new();
1919 for file_path in files {
1920 let mut file_handle = std::fs::File::open(file_path)?;
1921 let mut buffer = Vec::new();
1922 file_handle.read_to_end(&mut buffer)?;
1923
1924 let base64_content = general_purpose::STANDARD.encode(&buffer);
1925 let file_name = file_path
1926 .file_name()
1927 .and_then(|n| n.to_str())
1928 .ok_or_else(|| {
1929 crate::error::Error::InvalidArgument("Invalid file path".to_string())
1930 })?;
1931
1932 file_objects.push(serde_json::json!({
1933 "name": file_name,
1934 "buffer": base64_content
1935 }));
1936 }
1937
1938 self.channel()
1939 .send_no_result(
1940 "setInputFiles",
1941 serde_json::json!({
1942 "selector": selector,
1943 "strict": true,
1944 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": file_objects
1946 }),
1947 )
1948 .await
1949 }
1950
1951 pub(crate) async fn locator_set_input_files_payload(
1952 &self,
1953 selector: &str,
1954 file: crate::protocol::FilePayload,
1955 ) -> Result<()> {
1956 use base64::{Engine as _, engine::general_purpose};
1957
1958 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1960
1961 self.channel()
1962 .send_no_result(
1963 "setInputFiles",
1964 serde_json::json!({
1965 "selector": selector,
1966 "strict": true,
1967 "timeout": crate::DEFAULT_TIMEOUT_MS,
1968 "payloads": [{
1969 "name": file.name,
1970 "mimeType": file.mime_type,
1971 "buffer": base64_content
1972 }]
1973 }),
1974 )
1975 .await
1976 }
1977
1978 pub(crate) async fn locator_set_input_files_payload_multiple(
1979 &self,
1980 selector: &str,
1981 files: &[crate::protocol::FilePayload],
1982 ) -> Result<()> {
1983 use base64::{Engine as _, engine::general_purpose};
1984
1985 if files.is_empty() {
1987 return self
1988 .channel()
1989 .send_no_result(
1990 "setInputFiles",
1991 serde_json::json!({
1992 "selector": selector,
1993 "strict": true,
1994 "timeout": crate::DEFAULT_TIMEOUT_MS,
1995 "payloads": []
1996 }),
1997 )
1998 .await;
1999 }
2000
2001 let file_objects: Vec<_> = files
2003 .iter()
2004 .map(|file| {
2005 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
2006 serde_json::json!({
2007 "name": file.name,
2008 "mimeType": file.mime_type,
2009 "buffer": base64_content
2010 })
2011 })
2012 .collect();
2013
2014 self.channel()
2015 .send_no_result(
2016 "setInputFiles",
2017 serde_json::json!({
2018 "selector": selector,
2019 "strict": true,
2020 "timeout": crate::DEFAULT_TIMEOUT_MS,
2021 "payloads": file_objects
2022 }),
2023 )
2024 .await
2025 }
2026
2027 pub(crate) async fn locator_aria_snapshot(
2034 &self,
2035 selector: &str,
2036 options: Option<&crate::protocol::AriaSnapshotOptions>,
2037 ) -> Result<String> {
2038 let timeout = options
2039 .and_then(|o| o.timeout)
2040 .unwrap_or(crate::DEFAULT_TIMEOUT_MS);
2041 self.aria_snapshot_raw(selector, timeout, options).await
2042 }
2043
2044 pub(crate) async fn aria_snapshot_raw(
2045 &self,
2046 selector: &str,
2047 timeout: f64,
2048 options: Option<&crate::protocol::AriaSnapshotOptions>,
2049 ) -> Result<String> {
2050 #[derive(Deserialize)]
2051 struct AriaSnapshotResponse {
2052 snapshot: String,
2053 }
2054
2055 let mut params = serde_json::json!({
2056 "selector": selector,
2057 "timeout": timeout,
2058 });
2059 if let Some(opts) = options {
2060 if let Some(mode) = opts.mode {
2061 params["mode"] = serde_json::Value::String(mode.as_str().to_string());
2062 }
2063 if let Some(depth) = opts.depth {
2064 params["depth"] = serde_json::Value::from(depth);
2065 }
2066 if let Some(boxes) = opts.boxes {
2067 params["boxes"] = serde_json::Value::Bool(boxes);
2068 }
2069 }
2070
2071 let response: AriaSnapshotResponse = self.channel().send("ariaSnapshot", params).await?;
2072 Ok(response.snapshot)
2073 }
2074
2075 pub(crate) async fn frame_resolve_selector(&self, selector: &str) -> Result<String> {
2081 #[derive(Deserialize)]
2082 struct ResolveSelectorResponse {
2083 #[serde(rename = "resolvedSelector")]
2084 resolved_selector: String,
2085 }
2086
2087 let response: ResolveSelectorResponse = self
2088 .channel()
2089 .send(
2090 "resolveSelector",
2091 serde_json::json!({
2092 "selector": selector,
2093 }),
2094 )
2095 .await?;
2096
2097 Ok(response.resolved_selector)
2098 }
2099
2100 pub(crate) async fn locator_highlight(
2107 &self,
2108 selector: &str,
2109 style: Option<&str>,
2110 ) -> Result<()> {
2111 let mut params = serde_json::json!({ "selector": selector });
2112 if let Some(style) = style {
2113 params["style"] = serde_json::Value::String(style.to_string());
2114 }
2115 self.channel().send_no_result("highlight", params).await
2116 }
2117
2118 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
2122 let params = serde_json::json!({
2123 "expression": expression,
2124 "arg": {
2125 "value": {"v": "null"},
2126 "handles": []
2127 }
2128 });
2129
2130 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
2131 Ok(())
2132 }
2133
2134 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
2146 let params = serde_json::json!({
2147 "expression": expression,
2148 "arg": {
2149 "value": {"v": "null"},
2150 "handles": []
2151 }
2152 });
2153
2154 #[derive(Deserialize)]
2155 struct EvaluateResult {
2156 value: serde_json::Value,
2157 }
2158
2159 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2160
2161 match &result.value {
2168 Value::Object(map) => {
2169 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
2170 Ok(s.to_string())
2172 } else if let Some(n) = map.get("n") {
2173 Ok(n.to_string())
2175 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
2176 Ok(b.to_string())
2178 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
2179 Ok(v.to_string())
2181 } else {
2182 Ok(result.value.to_string())
2184 }
2185 }
2186 _ => {
2187 Ok(result.value.to_string())
2189 }
2190 }
2191 }
2192
2193 #[tracing::instrument(level = "info", skip_all, fields(guid = %self.guid()))]
2233 pub async fn evaluate<T: serde::Serialize>(
2234 &self,
2235 expression: &str,
2236 arg: Option<&T>,
2237 ) -> Result<Value> {
2238 let serialized_arg = match arg {
2240 Some(a) => serialize_argument(a),
2241 None => serialize_null(),
2242 };
2243
2244 let params = serde_json::json!({
2246 "expression": expression,
2247 "arg": serialized_arg
2248 });
2249
2250 #[derive(Deserialize)]
2252 struct EvaluateResult {
2253 value: serde_json::Value,
2254 }
2255
2256 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
2257
2258 Ok(parse_result(&result.value))
2260 }
2261
2262 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2302 pub async fn add_style_tag(
2303 &self,
2304 options: crate::protocol::page::AddStyleTagOptions,
2305 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2306 options.validate()?;
2308
2309 let mut params = serde_json::json!({});
2311
2312 if let Some(content) = &options.content {
2313 params["content"] = serde_json::json!(content);
2314 }
2315
2316 if let Some(url) = &options.url {
2317 params["url"] = serde_json::json!(url);
2318 }
2319
2320 if let Some(path) = &options.path {
2321 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2323 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
2324 })?;
2325 params["content"] = serde_json::json!(css_content);
2326 }
2327
2328 #[derive(Deserialize)]
2329 struct AddStyleTagResponse {
2330 element: serde_json::Value,
2331 }
2332
2333 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
2334
2335 let guid = response.element["guid"].as_str().ok_or_else(|| {
2336 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
2337 })?;
2338
2339 let connection = self.base.connection();
2340 let handle: crate::protocol::ElementHandle = connection
2341 .get_typed::<crate::protocol::ElementHandle>(guid)
2342 .await?;
2343
2344 Ok(Arc::new(handle))
2345 }
2346
2347 pub(crate) async fn locator_dispatch_event(
2355 &self,
2356 selector: &str,
2357 type_: &str,
2358 event_init: Option<serde_json::Value>,
2359 ) -> Result<()> {
2360 let event_init_serialized = match event_init {
2363 Some(v) => serialize_argument(&v),
2364 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
2365 };
2366
2367 let params = serde_json::json!({
2368 "selector": selector,
2369 "type": type_,
2370 "eventInit": event_init_serialized,
2371 "strict": true,
2372 "timeout": crate::DEFAULT_TIMEOUT_MS
2373 });
2374
2375 self.channel().send_no_result("dispatchEvent", params).await
2376 }
2377
2378 pub(crate) async fn locator_bounding_box(
2388 &self,
2389 selector: &str,
2390 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
2391 let element = self.query_selector(selector).await?;
2392 match element {
2393 Some(handle) => handle.bounding_box().await,
2394 None => Ok(None),
2395 }
2396 }
2397
2398 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
2405 let element = self.query_selector(selector).await?;
2406 match element {
2407 Some(handle) => handle.scroll_into_view_if_needed().await,
2408 None => Err(crate::error::Error::ElementNotFound(format!(
2409 "Element not found: {}",
2410 selector
2411 ))),
2412 }
2413 }
2414
2415 pub(crate) async fn frame_expect(
2421 &self,
2422 selector: &str,
2423 expression: &str,
2424 expected_value: serde_json::Value,
2425 is_not: bool,
2426 timeout_ms: f64,
2427 ) -> Result<()> {
2428 let params = serde_json::json!({
2429 "selector": selector,
2430 "expression": expression,
2431 "expectedValue": expected_value,
2432 "isNot": is_not,
2433 "timeout": timeout_ms
2434 });
2435
2436 let result: serde_json::Value = self.channel().send("expect", params).await?;
2444
2445 if crate::server::error_parsing::legacy_expect_verdict(&result, is_not) == Some(false) {
2453 return Err(crate::error::Error::AssertionFailed(format!(
2454 "Assertion failed for selector '{selector}' ({expression}). \
2455 Reported by a pre-1.61 Playwright server, which does not send \
2456 assertion details; connect to a version-matched server for a \
2457 fuller diagnostic."
2458 )));
2459 }
2460 Ok(())
2461 }
2462
2463 #[tracing::instrument(level = "debug", skip_all, fields(guid = %self.guid()))]
2473 pub async fn add_script_tag(
2474 &self,
2475 options: crate::protocol::page::AddScriptTagOptions,
2476 ) -> Result<Arc<crate::protocol::ElementHandle>> {
2477 options.validate()?;
2479
2480 let mut params = serde_json::json!({});
2482
2483 if let Some(content) = &options.content {
2484 params["content"] = serde_json::json!(content);
2485 }
2486
2487 if let Some(url) = &options.url {
2488 params["url"] = serde_json::json!(url);
2489 }
2490
2491 if let Some(path) = &options.path {
2492 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
2494 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
2495 })?;
2496 params["content"] = serde_json::json!(js_content);
2497 }
2498
2499 if let Some(type_) = &options.type_ {
2500 params["type"] = serde_json::json!(type_);
2501 }
2502
2503 #[derive(Deserialize)]
2504 struct AddScriptTagResponse {
2505 element: serde_json::Value,
2506 }
2507
2508 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
2509
2510 let guid = response.element["guid"].as_str().ok_or_else(|| {
2511 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
2512 })?;
2513
2514 let connection = self.base.connection();
2515 let handle: crate::protocol::ElementHandle = connection
2516 .get_typed::<crate::protocol::ElementHandle>(guid)
2517 .await?;
2518
2519 Ok(Arc::new(handle))
2520 }
2521}
2522
2523impl ChannelOwner for Frame {
2524 fn guid(&self) -> &str {
2525 self.base.guid()
2526 }
2527
2528 fn type_name(&self) -> &str {
2529 self.base.type_name()
2530 }
2531
2532 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
2533 self.base.parent()
2534 }
2535
2536 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
2537 self.base.connection()
2538 }
2539
2540 fn initializer(&self) -> &Value {
2541 self.base.initializer()
2542 }
2543
2544 fn channel(&self) -> &Channel {
2545 self.base.channel()
2546 }
2547
2548 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
2549 if let Ok(mut guard) = self.page.lock() {
2553 *guard = None;
2554 }
2555 self.base.dispose(reason)
2556 }
2557
2558 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
2559 self.base.adopt(child)
2560 }
2561
2562 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
2563 self.base.add_child(guid, child)
2564 }
2565
2566 fn remove_child(&self, guid: &str) {
2567 self.base.remove_child(guid)
2568 }
2569
2570 fn on_event(&self, method: &str, params: Value) {
2571 match method {
2572 "navigated" => {
2573 if let Some(url_value) = params.get("url")
2575 && let Some(url_str) = url_value.as_str()
2576 {
2577 if let Ok(mut url) = self.url.write() {
2579 *url = url_str.to_string();
2580 }
2581 }
2582 let self_clone = self.clone();
2584 tokio::spawn(async move {
2585 if let Some(page) = self_clone.page() {
2586 page.trigger_framenavigated_event(self_clone).await;
2587 }
2588 });
2589 }
2590 "loadstate" => {
2591 if let Some(add) = params.get("add").and_then(|v| v.as_str())
2594 && add == "load"
2595 {
2596 let self_clone = self.clone();
2597 tokio::spawn(async move {
2598 if let Some(page) = self_clone.page() {
2599 page.trigger_load_event().await;
2600 }
2601 });
2602 }
2603 }
2604 "detached" => {
2605 if let Ok(mut flag) = self.is_detached.write() {
2607 *flag = true;
2608 }
2609 }
2610 _ => {
2611 }
2613 }
2614 }
2615
2616 fn was_collected(&self) -> bool {
2617 self.base.was_collected()
2618 }
2619
2620 fn as_any(&self) -> &dyn Any {
2621 self
2622 }
2623}
2624
2625impl std::fmt::Debug for Frame {
2626 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2627 f.debug_struct("Frame").field("guid", &self.guid()).finish()
2628 }
2629}