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 serde::Deserialize;
12use serde_json::Value;
13use std::any::Any;
14use std::sync::{Arc, RwLock};
15
16#[derive(Clone)]
25pub struct Frame {
26 base: ChannelOwnerImpl,
27 url: Arc<RwLock<String>>,
30}
31
32impl Frame {
33 pub fn new(
38 parent: Arc<dyn ChannelOwner>,
39 type_name: String,
40 guid: Arc<str>,
41 initializer: Value,
42 ) -> Result<Self> {
43 let base = ChannelOwnerImpl::new(
44 ParentOrConnection::Parent(parent),
45 type_name,
46 guid,
47 initializer.clone(),
48 );
49
50 let initial_url = initializer
52 .get("url")
53 .and_then(|v| v.as_str())
54 .unwrap_or("about:blank")
55 .to_string();
56
57 let url = Arc::new(RwLock::new(initial_url));
58
59 Ok(Self { base, url })
60 }
61
62 fn channel(&self) -> &Channel {
64 self.base.channel()
65 }
66
67 pub fn url(&self) -> String {
73 self.url.read().unwrap().clone()
74 }
75
76 pub async fn goto(&self, url: &str, options: Option<GotoOptions>) -> Result<Option<Response>> {
90 let mut params = serde_json::json!({
92 "url": url,
93 });
94
95 if let Some(opts) = options {
97 if let Some(timeout) = opts.timeout {
98 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
99 } else {
100 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
102 }
103 if let Some(wait_until) = opts.wait_until {
104 params["waitUntil"] = serde_json::json!(wait_until.as_str());
105 }
106 } else {
107 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
109 }
110
111 #[derive(Deserialize)]
114 struct GotoResponse {
115 response: Option<ResponseReference>,
116 }
117
118 #[derive(Deserialize)]
119 struct ResponseReference {
120 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
121 guid: Arc<str>,
122 }
123
124 let goto_result: GotoResponse = self.channel().send("goto", params).await?;
125
126 if let Some(response_ref) = goto_result.response {
128 let response_arc = {
134 let mut attempts = 0;
135 let max_attempts = 20; loop {
137 match self.connection().get_object(&response_ref.guid).await {
138 Ok(obj) => break obj,
139 Err(_) if attempts < max_attempts => {
140 attempts += 1;
141 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
142 }
143 Err(e) => return Err(e),
144 }
145 }
146 };
147
148 let initializer = response_arc.initializer();
151
152 let status = initializer["status"].as_u64().ok_or_else(|| {
154 crate::error::Error::ProtocolError("Response missing status".to_string())
155 })? as u16;
156
157 let headers = initializer["headers"]
159 .as_array()
160 .ok_or_else(|| {
161 crate::error::Error::ProtocolError("Response missing headers".to_string())
162 })?
163 .iter()
164 .filter_map(|h| {
165 let name = h["name"].as_str()?;
166 let value = h["value"].as_str()?;
167 Some((name.to_string(), value.to_string()))
168 })
169 .collect();
170
171 Ok(Some(Response::new(
172 initializer["url"]
173 .as_str()
174 .ok_or_else(|| {
175 crate::error::Error::ProtocolError("Response missing url".to_string())
176 })?
177 .to_string(),
178 status,
179 initializer["statusText"].as_str().unwrap_or("").to_string(),
180 headers,
181 Some(response_arc),
182 )))
183 } else {
184 Ok(None)
187 }
188 }
189
190 pub async fn title(&self) -> Result<String> {
194 #[derive(Deserialize)]
195 struct TitleResponse {
196 value: String,
197 }
198
199 let response: TitleResponse = self.channel().send("title", serde_json::json!({})).await?;
200 Ok(response.value)
201 }
202
203 pub async fn content(&self) -> Result<String> {
207 #[derive(Deserialize)]
208 struct ContentResponse {
209 value: String,
210 }
211
212 let response: ContentResponse = self
213 .channel()
214 .send("content", serde_json::json!({}))
215 .await?;
216 Ok(response.value)
217 }
218
219 pub async fn set_content(&self, html: &str, options: Option<GotoOptions>) -> Result<()> {
223 let mut params = serde_json::json!({
224 "html": html,
225 });
226
227 if let Some(opts) = options {
228 if let Some(timeout) = opts.timeout {
229 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
230 } else {
231 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
232 }
233 if let Some(wait_until) = opts.wait_until {
234 params["waitUntil"] = serde_json::json!(wait_until.as_str());
235 }
236 } else {
237 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
238 }
239
240 self.channel().send_no_result("setContent", params).await
241 }
242
243 pub async fn wait_for_load_state(&self, state: Option<WaitUntil>) -> Result<()> {
251 let target_state = state.unwrap_or(WaitUntil::Load);
252
253 let js_check = match target_state {
254 WaitUntil::Load => "document.readyState === 'complete'",
256 WaitUntil::DomContentLoaded => "document.readyState !== 'loading'",
258 WaitUntil::NetworkIdle => "document.readyState === 'complete'",
261 WaitUntil::Commit => "document.readyState !== 'loading'",
263 };
264
265 let timeout_ms = crate::DEFAULT_TIMEOUT_MS as u64;
266 let poll_interval = std::time::Duration::from_millis(50);
267 let start = std::time::Instant::now();
268
269 loop {
270 #[derive(Deserialize)]
271 struct EvalResponse {
272 value: serde_json::Value,
273 }
274
275 let result: EvalResponse = self
276 .channel()
277 .send(
278 "evaluateExpression",
279 serde_json::json!({
280 "expression": js_check,
281 "isFunction": false,
282 "arg": crate::protocol::serialize_null(),
283 }),
284 )
285 .await?;
286
287 let is_ready = result
289 .value
290 .as_object()
291 .and_then(|m| m.get("b"))
292 .and_then(|v| v.as_bool())
293 .unwrap_or(false);
294
295 if is_ready {
296 return Ok(());
297 }
298
299 if start.elapsed().as_millis() as u64 >= timeout_ms {
300 return Err(crate::error::Error::Timeout(format!(
301 "wait_for_load_state({}) timed out after {}ms",
302 target_state.as_str(),
303 timeout_ms
304 )));
305 }
306
307 tokio::time::sleep(poll_interval).await;
308 }
309 }
310
311 pub async fn wait_for_url(&self, url: &str, options: Option<GotoOptions>) -> Result<()> {
318 let timeout_ms = options
319 .as_ref()
320 .and_then(|o| o.timeout)
321 .map(|d| d.as_millis() as u64)
322 .unwrap_or(crate::DEFAULT_TIMEOUT_MS as u64);
323
324 let is_glob = url.contains('*');
328
329 let poll_interval = std::time::Duration::from_millis(50);
330 let start = std::time::Instant::now();
331
332 loop {
333 let current_url = self.url();
334
335 let matches = if is_glob {
336 glob_match(url, ¤t_url)
337 } else {
338 current_url == url
339 };
340
341 if matches {
342 if let Some(ref opts) = options {
344 if let Some(wait_until) = opts.wait_until {
345 self.wait_for_load_state(Some(wait_until)).await?;
346 }
347 }
348 return Ok(());
349 }
350
351 if start.elapsed().as_millis() as u64 >= timeout_ms {
352 return Err(crate::error::Error::Timeout(format!(
353 "wait_for_url({}) timed out after {}ms, current URL: {}",
354 url, timeout_ms, current_url
355 )));
356 }
357
358 tokio::time::sleep(poll_interval).await;
359 }
360 }
361
362 pub async fn query_selector(
366 &self,
367 selector: &str,
368 ) -> Result<Option<Arc<crate::protocol::ElementHandle>>> {
369 let response: serde_json::Value = self
370 .channel()
371 .send(
372 "querySelector",
373 serde_json::json!({
374 "selector": selector
375 }),
376 )
377 .await?;
378
379 if response.as_object().map(|o| o.is_empty()).unwrap_or(true) {
381 return Ok(None);
382 }
383
384 let element_value = if let Some(elem) = response.get("element") {
386 elem
387 } else if let Some(elem) = response.get("handle") {
388 elem
389 } else {
390 &response
392 };
393
394 if element_value.is_null() {
395 return Ok(None);
396 }
397
398 let guid = element_value["guid"].as_str().ok_or_else(|| {
400 crate::error::Error::ProtocolError("Element GUID missing".to_string())
401 })?;
402
403 let connection = self.base.connection();
405 let element = connection.get_object(guid).await?;
406
407 let handle = element
409 .as_any()
410 .downcast_ref::<crate::protocol::ElementHandle>()
411 .map(|e| Arc::new(e.clone()))
412 .ok_or_else(|| {
413 crate::error::Error::ProtocolError(format!(
414 "Object {} is not an ElementHandle",
415 guid
416 ))
417 })?;
418
419 Ok(Some(handle))
420 }
421
422 pub async fn query_selector_all(
426 &self,
427 selector: &str,
428 ) -> Result<Vec<Arc<crate::protocol::ElementHandle>>> {
429 #[derive(Deserialize)]
430 struct QueryAllResponse {
431 elements: Vec<serde_json::Value>,
432 }
433
434 let response: QueryAllResponse = self
435 .channel()
436 .send(
437 "querySelectorAll",
438 serde_json::json!({
439 "selector": selector
440 }),
441 )
442 .await?;
443
444 let connection = self.base.connection();
446 let mut handles = Vec::new();
447
448 for element_value in response.elements {
449 let guid = element_value["guid"].as_str().ok_or_else(|| {
450 crate::error::Error::ProtocolError("Element GUID missing".to_string())
451 })?;
452
453 let element = connection.get_object(guid).await?;
454
455 let handle = element
456 .as_any()
457 .downcast_ref::<crate::protocol::ElementHandle>()
458 .map(|e| Arc::new(e.clone()))
459 .ok_or_else(|| {
460 crate::error::Error::ProtocolError(format!(
461 "Object {} is not an ElementHandle",
462 guid
463 ))
464 })?;
465
466 handles.push(handle);
467 }
468
469 Ok(handles)
470 }
471
472 pub(crate) async fn locator_count(&self, selector: &str) -> Result<usize> {
477 #[derive(Deserialize)]
479 struct QueryAllResponse {
480 elements: Vec<serde_json::Value>,
481 }
482
483 let response: QueryAllResponse = self
484 .channel()
485 .send(
486 "querySelectorAll",
487 serde_json::json!({
488 "selector": selector
489 }),
490 )
491 .await?;
492
493 Ok(response.elements.len())
494 }
495
496 pub(crate) async fn locator_text_content(&self, selector: &str) -> Result<Option<String>> {
498 #[derive(Deserialize)]
499 struct TextContentResponse {
500 value: Option<String>,
501 }
502
503 let response: TextContentResponse = self
504 .channel()
505 .send(
506 "textContent",
507 serde_json::json!({
508 "selector": selector,
509 "strict": true,
510 "timeout": crate::DEFAULT_TIMEOUT_MS
511 }),
512 )
513 .await?;
514
515 Ok(response.value)
516 }
517
518 pub(crate) async fn locator_inner_text(&self, selector: &str) -> Result<String> {
520 #[derive(Deserialize)]
521 struct InnerTextResponse {
522 value: String,
523 }
524
525 let response: InnerTextResponse = self
526 .channel()
527 .send(
528 "innerText",
529 serde_json::json!({
530 "selector": selector,
531 "strict": true,
532 "timeout": crate::DEFAULT_TIMEOUT_MS
533 }),
534 )
535 .await?;
536
537 Ok(response.value)
538 }
539
540 pub(crate) async fn locator_inner_html(&self, selector: &str) -> Result<String> {
542 #[derive(Deserialize)]
543 struct InnerHTMLResponse {
544 value: String,
545 }
546
547 let response: InnerHTMLResponse = self
548 .channel()
549 .send(
550 "innerHTML",
551 serde_json::json!({
552 "selector": selector,
553 "strict": true,
554 "timeout": crate::DEFAULT_TIMEOUT_MS
555 }),
556 )
557 .await?;
558
559 Ok(response.value)
560 }
561
562 pub(crate) async fn locator_get_attribute(
564 &self,
565 selector: &str,
566 name: &str,
567 ) -> Result<Option<String>> {
568 #[derive(Deserialize)]
569 struct GetAttributeResponse {
570 value: Option<String>,
571 }
572
573 let response: GetAttributeResponse = self
574 .channel()
575 .send(
576 "getAttribute",
577 serde_json::json!({
578 "selector": selector,
579 "name": name,
580 "strict": true,
581 "timeout": crate::DEFAULT_TIMEOUT_MS
582 }),
583 )
584 .await?;
585
586 Ok(response.value)
587 }
588
589 pub(crate) async fn locator_is_visible(&self, selector: &str) -> Result<bool> {
591 #[derive(Deserialize)]
592 struct IsVisibleResponse {
593 value: bool,
594 }
595
596 let response: IsVisibleResponse = self
597 .channel()
598 .send(
599 "isVisible",
600 serde_json::json!({
601 "selector": selector,
602 "strict": true,
603 "timeout": crate::DEFAULT_TIMEOUT_MS
604 }),
605 )
606 .await?;
607
608 Ok(response.value)
609 }
610
611 pub(crate) async fn locator_is_enabled(&self, selector: &str) -> Result<bool> {
613 #[derive(Deserialize)]
614 struct IsEnabledResponse {
615 value: bool,
616 }
617
618 let response: IsEnabledResponse = self
619 .channel()
620 .send(
621 "isEnabled",
622 serde_json::json!({
623 "selector": selector,
624 "strict": true,
625 "timeout": crate::DEFAULT_TIMEOUT_MS
626 }),
627 )
628 .await?;
629
630 Ok(response.value)
631 }
632
633 pub(crate) async fn locator_is_checked(&self, selector: &str) -> Result<bool> {
635 #[derive(Deserialize)]
636 struct IsCheckedResponse {
637 value: bool,
638 }
639
640 let response: IsCheckedResponse = self
641 .channel()
642 .send(
643 "isChecked",
644 serde_json::json!({
645 "selector": selector,
646 "strict": true,
647 "timeout": crate::DEFAULT_TIMEOUT_MS
648 }),
649 )
650 .await?;
651
652 Ok(response.value)
653 }
654
655 pub(crate) async fn locator_is_editable(&self, selector: &str) -> Result<bool> {
657 #[derive(Deserialize)]
658 struct IsEditableResponse {
659 value: bool,
660 }
661
662 let response: IsEditableResponse = self
663 .channel()
664 .send(
665 "isEditable",
666 serde_json::json!({
667 "selector": selector,
668 "strict": true,
669 "timeout": crate::DEFAULT_TIMEOUT_MS
670 }),
671 )
672 .await?;
673
674 Ok(response.value)
675 }
676
677 pub(crate) async fn locator_is_hidden(&self, selector: &str) -> Result<bool> {
679 #[derive(Deserialize)]
680 struct IsHiddenResponse {
681 value: bool,
682 }
683
684 let response: IsHiddenResponse = self
685 .channel()
686 .send(
687 "isHidden",
688 serde_json::json!({
689 "selector": selector,
690 "strict": true,
691 "timeout": crate::DEFAULT_TIMEOUT_MS
692 }),
693 )
694 .await?;
695
696 Ok(response.value)
697 }
698
699 pub(crate) async fn locator_is_disabled(&self, selector: &str) -> Result<bool> {
701 #[derive(Deserialize)]
702 struct IsDisabledResponse {
703 value: bool,
704 }
705
706 let response: IsDisabledResponse = self
707 .channel()
708 .send(
709 "isDisabled",
710 serde_json::json!({
711 "selector": selector,
712 "strict": true,
713 "timeout": crate::DEFAULT_TIMEOUT_MS
714 }),
715 )
716 .await?;
717
718 Ok(response.value)
719 }
720
721 pub(crate) async fn locator_is_focused(&self, selector: &str) -> Result<bool> {
727 #[derive(Deserialize)]
728 struct EvaluateResult {
729 value: serde_json::Value,
730 }
731
732 let script = r#"selector => {
735 const elements = document.querySelectorAll(selector);
736 if (elements.length === 0) return false;
737 const element = elements[0];
738 return document.activeElement === element;
739 }"#;
740
741 let params = serde_json::json!({
742 "expression": script,
743 "arg": {
744 "value": {"s": selector},
745 "handles": []
746 }
747 });
748
749 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
750
751 if let serde_json::Value::Object(map) = &result.value {
753 if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
754 return Ok(b);
755 }
756 }
757
758 Ok(result.value.to_string().to_lowercase().contains("true"))
760 }
761
762 pub(crate) async fn locator_click(
766 &self,
767 selector: &str,
768 options: Option<crate::protocol::ClickOptions>,
769 ) -> Result<()> {
770 let mut params = serde_json::json!({
771 "selector": selector,
772 "strict": true
773 });
774
775 if let Some(opts) = options {
776 let opts_json = opts.to_json();
777 if let Some(obj) = params.as_object_mut() {
778 if let Some(opts_obj) = opts_json.as_object() {
779 obj.extend(opts_obj.clone());
780 }
781 }
782 } else {
783 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
784 }
785
786 self.channel()
787 .send_no_result("click", params)
788 .await
789 .map_err(|e| match e {
790 Error::Timeout(msg) => {
791 Error::Timeout(format!("{} (selector: '{}')", msg, selector))
792 }
793 other => other,
794 })
795 }
796
797 pub(crate) async fn locator_dblclick(
799 &self,
800 selector: &str,
801 options: Option<crate::protocol::ClickOptions>,
802 ) -> Result<()> {
803 let mut params = serde_json::json!({
804 "selector": selector,
805 "strict": true
806 });
807
808 if let Some(opts) = options {
809 let opts_json = opts.to_json();
810 if let Some(obj) = params.as_object_mut() {
811 if let Some(opts_obj) = opts_json.as_object() {
812 obj.extend(opts_obj.clone());
813 }
814 }
815 } else {
816 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
817 }
818
819 self.channel().send_no_result("dblclick", params).await
820 }
821
822 pub(crate) async fn locator_fill(
824 &self,
825 selector: &str,
826 text: &str,
827 options: Option<crate::protocol::FillOptions>,
828 ) -> Result<()> {
829 let mut params = serde_json::json!({
830 "selector": selector,
831 "value": text,
832 "strict": true
833 });
834
835 if let Some(opts) = options {
836 let opts_json = opts.to_json();
837 if let Some(obj) = params.as_object_mut() {
838 if let Some(opts_obj) = opts_json.as_object() {
839 obj.extend(opts_obj.clone());
840 }
841 }
842 } else {
843 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
844 }
845
846 self.channel().send_no_result("fill", params).await
847 }
848
849 pub(crate) async fn locator_clear(
851 &self,
852 selector: &str,
853 options: Option<crate::protocol::FillOptions>,
854 ) -> Result<()> {
855 let mut params = serde_json::json!({
856 "selector": selector,
857 "value": "",
858 "strict": true
859 });
860
861 if let Some(opts) = options {
862 let opts_json = opts.to_json();
863 if let Some(obj) = params.as_object_mut() {
864 if let Some(opts_obj) = opts_json.as_object() {
865 obj.extend(opts_obj.clone());
866 }
867 }
868 } else {
869 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
870 }
871
872 self.channel().send_no_result("fill", params).await
873 }
874
875 pub(crate) async fn locator_press(
877 &self,
878 selector: &str,
879 key: &str,
880 options: Option<crate::protocol::PressOptions>,
881 ) -> Result<()> {
882 let mut params = serde_json::json!({
883 "selector": selector,
884 "key": key,
885 "strict": true
886 });
887
888 if let Some(opts) = options {
889 let opts_json = opts.to_json();
890 if let Some(obj) = params.as_object_mut() {
891 if let Some(opts_obj) = opts_json.as_object() {
892 obj.extend(opts_obj.clone());
893 }
894 }
895 } else {
896 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
897 }
898
899 self.channel().send_no_result("press", params).await
900 }
901
902 pub(crate) async fn locator_focus(&self, selector: &str) -> Result<()> {
904 self.channel()
905 .send_no_result(
906 "focus",
907 serde_json::json!({
908 "selector": selector,
909 "strict": true,
910 "timeout": crate::DEFAULT_TIMEOUT_MS
911 }),
912 )
913 .await
914 }
915
916 pub(crate) async fn locator_blur(&self, selector: &str) -> Result<()> {
918 self.channel()
919 .send_no_result(
920 "blur",
921 serde_json::json!({
922 "selector": selector,
923 "strict": true,
924 "timeout": crate::DEFAULT_TIMEOUT_MS
925 }),
926 )
927 .await
928 }
929
930 pub(crate) async fn locator_press_sequentially(
934 &self,
935 selector: &str,
936 text: &str,
937 options: Option<crate::protocol::PressSequentiallyOptions>,
938 ) -> Result<()> {
939 let mut params = serde_json::json!({
940 "selector": selector,
941 "text": text,
942 "strict": true
943 });
944
945 if let Some(opts) = options {
946 let opts_json = opts.to_json();
947 if let Some(obj) = params.as_object_mut() {
948 if let Some(opts_obj) = opts_json.as_object() {
949 obj.extend(opts_obj.clone());
950 }
951 }
952 } else {
953 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
954 }
955
956 self.channel().send_no_result("type", params).await
957 }
958
959 pub(crate) async fn locator_all_inner_texts(&self, selector: &str) -> Result<Vec<String>> {
961 #[derive(serde::Deserialize)]
962 struct EvaluateResult {
963 value: serde_json::Value,
964 }
965
966 let params = serde_json::json!({
969 "selector": selector,
970 "expression": "ee => ee.map(e => e.innerText)",
971 "isFunction": true,
972 "arg": {
973 "value": {"v": "null"},
974 "handles": []
975 }
976 });
977
978 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
979
980 Self::parse_string_array(result.value)
981 }
982
983 pub(crate) async fn locator_all_text_contents(&self, selector: &str) -> Result<Vec<String>> {
985 #[derive(serde::Deserialize)]
986 struct EvaluateResult {
987 value: serde_json::Value,
988 }
989
990 let params = serde_json::json!({
993 "selector": selector,
994 "expression": "ee => ee.map(e => e.textContent || '')",
995 "isFunction": true,
996 "arg": {
997 "value": {"v": "null"},
998 "handles": []
999 }
1000 });
1001
1002 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1003
1004 Self::parse_string_array(result.value)
1005 }
1006
1007 pub(crate) async fn locator_tap(
1014 &self,
1015 selector: &str,
1016 options: Option<crate::protocol::TapOptions>,
1017 ) -> Result<()> {
1018 let mut params = serde_json::json!({
1019 "selector": selector,
1020 "strict": true
1021 });
1022
1023 if let Some(opts) = options {
1024 let opts_json = opts.to_json();
1025 if let Some(obj) = params.as_object_mut() {
1026 if let Some(opts_obj) = opts_json.as_object() {
1027 obj.extend(opts_obj.clone());
1028 }
1029 }
1030 } else {
1031 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1032 }
1033
1034 self.channel().send_no_result("tap", params).await
1035 }
1036
1037 pub(crate) async fn locator_drag_to(
1043 &self,
1044 source_selector: &str,
1045 target_selector: &str,
1046 options: Option<crate::protocol::DragToOptions>,
1047 ) -> Result<()> {
1048 let mut params = serde_json::json!({
1049 "source": source_selector,
1050 "target": target_selector,
1051 "strict": true
1052 });
1053
1054 if let Some(opts) = options {
1055 let opts_json = opts.to_json();
1056 if let Some(obj) = params.as_object_mut() {
1057 if let Some(opts_obj) = opts_json.as_object() {
1058 obj.extend(opts_obj.clone());
1059 }
1060 }
1061 } else {
1062 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1063 }
1064
1065 self.channel().send_no_result("dragAndDrop", params).await
1066 }
1067
1068 pub(crate) async fn locator_wait_for(
1075 &self,
1076 selector: &str,
1077 options: Option<crate::protocol::WaitForOptions>,
1078 ) -> Result<()> {
1079 let mut params = serde_json::json!({
1080 "selector": selector,
1081 "strict": true
1082 });
1083
1084 if let Some(opts) = options {
1085 let opts_json = opts.to_json();
1086 if let Some(obj) = params.as_object_mut() {
1087 if let Some(opts_obj) = opts_json.as_object() {
1088 obj.extend(opts_obj.clone());
1089 }
1090 }
1091 } else {
1092 params["state"] = serde_json::json!("visible");
1094 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1095 }
1096
1097 let _: serde_json::Value = self.channel().send("waitForSelector", params).await?;
1099 Ok(())
1100 }
1101
1102 pub(crate) async fn locator_evaluate<T: serde::Serialize>(
1109 &self,
1110 selector: &str,
1111 expression: &str,
1112 arg: Option<T>,
1113 ) -> Result<serde_json::Value> {
1114 let serialized_arg = match arg {
1115 Some(a) => serialize_argument(&a),
1116 None => serialize_null(),
1117 };
1118
1119 let params = serde_json::json!({
1120 "selector": selector,
1121 "expression": expression,
1122 "isFunction": true,
1123 "arg": serialized_arg,
1124 "strict": true
1125 });
1126
1127 #[derive(Deserialize)]
1128 struct EvaluateResult {
1129 value: serde_json::Value,
1130 }
1131
1132 let result: EvaluateResult = self.channel().send("evalOnSelector", params).await?;
1133 Ok(parse_result(&result.value))
1134 }
1135
1136 pub(crate) async fn locator_evaluate_all<T: serde::Serialize>(
1143 &self,
1144 selector: &str,
1145 expression: &str,
1146 arg: Option<T>,
1147 ) -> Result<serde_json::Value> {
1148 let serialized_arg = match arg {
1149 Some(a) => serialize_argument(&a),
1150 None => serialize_null(),
1151 };
1152
1153 let params = serde_json::json!({
1154 "selector": selector,
1155 "expression": expression,
1156 "isFunction": true,
1157 "arg": serialized_arg
1158 });
1159
1160 #[derive(Deserialize)]
1161 struct EvaluateResult {
1162 value: serde_json::Value,
1163 }
1164
1165 let result: EvaluateResult = self.channel().send("evalOnSelectorAll", params).await?;
1166 Ok(parse_result(&result.value))
1167 }
1168
1169 fn parse_string_array(value: serde_json::Value) -> Result<Vec<String>> {
1174 let array = if let Some(arr) = value.get("a").and_then(|v| v.as_array()) {
1176 arr.clone()
1177 } else if let Some(arr) = value.as_array() {
1178 arr.clone()
1179 } else {
1180 return Ok(Vec::new());
1181 };
1182
1183 let mut result = Vec::with_capacity(array.len());
1184 for item in &array {
1185 let s = if let Some(s) = item.get("s").and_then(|v| v.as_str()) {
1187 s.to_string()
1188 } else if let Some(s) = item.as_str() {
1189 s.to_string()
1190 } else if item.is_null() {
1191 String::new()
1192 } else {
1193 item.to_string()
1194 };
1195 result.push(s);
1196 }
1197 Ok(result)
1198 }
1199
1200 pub(crate) async fn locator_check(
1201 &self,
1202 selector: &str,
1203 options: Option<crate::protocol::CheckOptions>,
1204 ) -> Result<()> {
1205 let mut params = serde_json::json!({
1206 "selector": selector,
1207 "strict": true
1208 });
1209
1210 if let Some(opts) = options {
1211 let opts_json = opts.to_json();
1212 if let Some(obj) = params.as_object_mut() {
1213 if let Some(opts_obj) = opts_json.as_object() {
1214 obj.extend(opts_obj.clone());
1215 }
1216 }
1217 } else {
1218 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1219 }
1220
1221 self.channel().send_no_result("check", params).await
1222 }
1223
1224 pub(crate) async fn locator_uncheck(
1225 &self,
1226 selector: &str,
1227 options: Option<crate::protocol::CheckOptions>,
1228 ) -> Result<()> {
1229 let mut params = serde_json::json!({
1230 "selector": selector,
1231 "strict": true
1232 });
1233
1234 if let Some(opts) = options {
1235 let opts_json = opts.to_json();
1236 if let Some(obj) = params.as_object_mut() {
1237 if let Some(opts_obj) = opts_json.as_object() {
1238 obj.extend(opts_obj.clone());
1239 }
1240 }
1241 } else {
1242 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1243 }
1244
1245 self.channel().send_no_result("uncheck", params).await
1246 }
1247
1248 pub(crate) async fn locator_hover(
1249 &self,
1250 selector: &str,
1251 options: Option<crate::protocol::HoverOptions>,
1252 ) -> Result<()> {
1253 let mut params = serde_json::json!({
1254 "selector": selector,
1255 "strict": true
1256 });
1257
1258 if let Some(opts) = options {
1259 let opts_json = opts.to_json();
1260 if let Some(obj) = params.as_object_mut() {
1261 if let Some(opts_obj) = opts_json.as_object() {
1262 obj.extend(opts_obj.clone());
1263 }
1264 }
1265 } else {
1266 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1267 }
1268
1269 self.channel().send_no_result("hover", params).await
1270 }
1271
1272 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
1273 #[derive(Deserialize)]
1274 struct InputValueResponse {
1275 value: String,
1276 }
1277
1278 let response: InputValueResponse = self
1279 .channel()
1280 .send(
1281 "inputValue",
1282 serde_json::json!({
1283 "selector": selector,
1284 "strict": true,
1285 "timeout": crate::DEFAULT_TIMEOUT_MS }),
1287 )
1288 .await?;
1289
1290 Ok(response.value)
1291 }
1292
1293 pub(crate) async fn locator_select_option(
1294 &self,
1295 selector: &str,
1296 value: crate::protocol::SelectOption,
1297 options: Option<crate::protocol::SelectOptions>,
1298 ) -> Result<Vec<String>> {
1299 #[derive(Deserialize)]
1300 struct SelectOptionResponse {
1301 values: Vec<String>,
1302 }
1303
1304 let mut params = serde_json::json!({
1305 "selector": selector,
1306 "strict": true,
1307 "options": [value.to_json()]
1308 });
1309
1310 if let Some(opts) = options {
1311 let opts_json = opts.to_json();
1312 if let Some(obj) = params.as_object_mut() {
1313 if let Some(opts_obj) = opts_json.as_object() {
1314 obj.extend(opts_obj.clone());
1315 }
1316 }
1317 } else {
1318 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1320 }
1321
1322 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1323
1324 Ok(response.values)
1325 }
1326
1327 pub(crate) async fn locator_select_option_multiple(
1328 &self,
1329 selector: &str,
1330 values: Vec<crate::protocol::SelectOption>,
1331 options: Option<crate::protocol::SelectOptions>,
1332 ) -> Result<Vec<String>> {
1333 #[derive(Deserialize)]
1334 struct SelectOptionResponse {
1335 values: Vec<String>,
1336 }
1337
1338 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
1339
1340 let mut params = serde_json::json!({
1341 "selector": selector,
1342 "strict": true,
1343 "options": values_array
1344 });
1345
1346 if let Some(opts) = options {
1347 let opts_json = opts.to_json();
1348 if let Some(obj) = params.as_object_mut() {
1349 if let Some(opts_obj) = opts_json.as_object() {
1350 obj.extend(opts_obj.clone());
1351 }
1352 }
1353 } else {
1354 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
1356 }
1357
1358 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
1359
1360 Ok(response.values)
1361 }
1362
1363 pub(crate) async fn locator_set_input_files(
1364 &self,
1365 selector: &str,
1366 file: &std::path::PathBuf,
1367 ) -> Result<()> {
1368 use base64::{Engine as _, engine::general_purpose};
1369 use std::io::Read;
1370
1371 let mut file_handle = std::fs::File::open(file)?;
1373 let mut buffer = Vec::new();
1374 file_handle.read_to_end(&mut buffer)?;
1375
1376 let base64_content = general_purpose::STANDARD.encode(&buffer);
1378
1379 let file_name = file
1381 .file_name()
1382 .and_then(|n| n.to_str())
1383 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
1384
1385 self.channel()
1386 .send_no_result(
1387 "setInputFiles",
1388 serde_json::json!({
1389 "selector": selector,
1390 "strict": true,
1391 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
1393 "name": file_name,
1394 "buffer": base64_content
1395 }]
1396 }),
1397 )
1398 .await
1399 }
1400
1401 pub(crate) async fn locator_set_input_files_multiple(
1402 &self,
1403 selector: &str,
1404 files: &[&std::path::PathBuf],
1405 ) -> Result<()> {
1406 use base64::{Engine as _, engine::general_purpose};
1407 use std::io::Read;
1408
1409 if files.is_empty() {
1411 return self
1412 .channel()
1413 .send_no_result(
1414 "setInputFiles",
1415 serde_json::json!({
1416 "selector": selector,
1417 "strict": true,
1418 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
1420 }),
1421 )
1422 .await;
1423 }
1424
1425 let mut file_objects = Vec::new();
1427 for file_path in files {
1428 let mut file_handle = std::fs::File::open(file_path)?;
1429 let mut buffer = Vec::new();
1430 file_handle.read_to_end(&mut buffer)?;
1431
1432 let base64_content = general_purpose::STANDARD.encode(&buffer);
1433 let file_name = file_path
1434 .file_name()
1435 .and_then(|n| n.to_str())
1436 .ok_or_else(|| {
1437 crate::error::Error::InvalidArgument("Invalid file path".to_string())
1438 })?;
1439
1440 file_objects.push(serde_json::json!({
1441 "name": file_name,
1442 "buffer": base64_content
1443 }));
1444 }
1445
1446 self.channel()
1447 .send_no_result(
1448 "setInputFiles",
1449 serde_json::json!({
1450 "selector": selector,
1451 "strict": true,
1452 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": file_objects
1454 }),
1455 )
1456 .await
1457 }
1458
1459 pub(crate) async fn locator_set_input_files_payload(
1460 &self,
1461 selector: &str,
1462 file: crate::protocol::FilePayload,
1463 ) -> Result<()> {
1464 use base64::{Engine as _, engine::general_purpose};
1465
1466 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1468
1469 self.channel()
1470 .send_no_result(
1471 "setInputFiles",
1472 serde_json::json!({
1473 "selector": selector,
1474 "strict": true,
1475 "timeout": crate::DEFAULT_TIMEOUT_MS,
1476 "payloads": [{
1477 "name": file.name,
1478 "mimeType": file.mime_type,
1479 "buffer": base64_content
1480 }]
1481 }),
1482 )
1483 .await
1484 }
1485
1486 pub(crate) async fn locator_set_input_files_payload_multiple(
1487 &self,
1488 selector: &str,
1489 files: &[crate::protocol::FilePayload],
1490 ) -> Result<()> {
1491 use base64::{Engine as _, engine::general_purpose};
1492
1493 if files.is_empty() {
1495 return self
1496 .channel()
1497 .send_no_result(
1498 "setInputFiles",
1499 serde_json::json!({
1500 "selector": selector,
1501 "strict": true,
1502 "timeout": crate::DEFAULT_TIMEOUT_MS,
1503 "payloads": []
1504 }),
1505 )
1506 .await;
1507 }
1508
1509 let file_objects: Vec<_> = files
1511 .iter()
1512 .map(|file| {
1513 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
1514 serde_json::json!({
1515 "name": file.name,
1516 "mimeType": file.mime_type,
1517 "buffer": base64_content
1518 })
1519 })
1520 .collect();
1521
1522 self.channel()
1523 .send_no_result(
1524 "setInputFiles",
1525 serde_json::json!({
1526 "selector": selector,
1527 "strict": true,
1528 "timeout": crate::DEFAULT_TIMEOUT_MS,
1529 "payloads": file_objects
1530 }),
1531 )
1532 .await
1533 }
1534
1535 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
1539 let params = serde_json::json!({
1540 "expression": expression,
1541 "arg": {
1542 "value": {"v": "null"},
1543 "handles": []
1544 }
1545 });
1546
1547 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
1548 Ok(())
1549 }
1550
1551 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
1563 let params = serde_json::json!({
1564 "expression": expression,
1565 "arg": {
1566 "value": {"v": "null"},
1567 "handles": []
1568 }
1569 });
1570
1571 #[derive(Deserialize)]
1572 struct EvaluateResult {
1573 value: serde_json::Value,
1574 }
1575
1576 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1577
1578 match &result.value {
1585 Value::Object(map) => {
1586 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
1587 Ok(s.to_string())
1589 } else if let Some(n) = map.get("n") {
1590 Ok(n.to_string())
1592 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
1593 Ok(b.to_string())
1595 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
1596 Ok(v.to_string())
1598 } else {
1599 Ok(result.value.to_string())
1601 }
1602 }
1603 _ => {
1604 Ok(result.value.to_string())
1606 }
1607 }
1608 }
1609
1610 pub async fn evaluate<T: serde::Serialize>(
1650 &self,
1651 expression: &str,
1652 arg: Option<&T>,
1653 ) -> Result<Value> {
1654 let serialized_arg = match arg {
1656 Some(a) => serialize_argument(a),
1657 None => serialize_null(),
1658 };
1659
1660 let params = serde_json::json!({
1662 "expression": expression,
1663 "arg": serialized_arg
1664 });
1665
1666 #[derive(Deserialize)]
1668 struct EvaluateResult {
1669 value: serde_json::Value,
1670 }
1671
1672 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1673
1674 Ok(parse_result(&result.value))
1676 }
1677
1678 pub async fn add_style_tag(
1718 &self,
1719 options: crate::protocol::page::AddStyleTagOptions,
1720 ) -> Result<Arc<crate::protocol::ElementHandle>> {
1721 options.validate()?;
1723
1724 let mut params = serde_json::json!({});
1726
1727 if let Some(content) = &options.content {
1728 params["content"] = serde_json::json!(content);
1729 }
1730
1731 if let Some(url) = &options.url {
1732 params["url"] = serde_json::json!(url);
1733 }
1734
1735 if let Some(path) = &options.path {
1736 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
1738 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
1739 })?;
1740 params["content"] = serde_json::json!(css_content);
1741 }
1742
1743 #[derive(Deserialize)]
1744 struct AddStyleTagResponse {
1745 element: serde_json::Value,
1746 }
1747
1748 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
1749
1750 let guid = response.element["guid"].as_str().ok_or_else(|| {
1751 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
1752 })?;
1753
1754 let connection = self.base.connection();
1755 let element = connection.get_object(guid).await?;
1756
1757 let handle = element
1758 .as_any()
1759 .downcast_ref::<crate::protocol::ElementHandle>()
1760 .map(|e| Arc::new(e.clone()))
1761 .ok_or_else(|| {
1762 Error::ProtocolError(format!("Object {} is not an ElementHandle", guid))
1763 })?;
1764
1765 Ok(handle)
1766 }
1767
1768 pub(crate) async fn locator_dispatch_event(
1776 &self,
1777 selector: &str,
1778 type_: &str,
1779 event_init: Option<serde_json::Value>,
1780 ) -> Result<()> {
1781 let event_init_serialized = match event_init {
1784 Some(v) => serialize_argument(&v),
1785 None => serde_json::json!({"value": {"v": "undefined"}, "handles": []}),
1786 };
1787
1788 let params = serde_json::json!({
1789 "selector": selector,
1790 "type": type_,
1791 "eventInit": event_init_serialized,
1792 "strict": true,
1793 "timeout": crate::DEFAULT_TIMEOUT_MS
1794 });
1795
1796 self.channel().send_no_result("dispatchEvent", params).await
1797 }
1798
1799 pub(crate) async fn locator_bounding_box(
1809 &self,
1810 selector: &str,
1811 ) -> Result<Option<crate::protocol::locator::BoundingBox>> {
1812 let element = self.query_selector(selector).await?;
1813 match element {
1814 Some(handle) => handle.bounding_box().await,
1815 None => Ok(None),
1816 }
1817 }
1818
1819 pub(crate) async fn locator_scroll_into_view_if_needed(&self, selector: &str) -> Result<()> {
1826 let element = self.query_selector(selector).await?;
1827 match element {
1828 Some(handle) => handle.scroll_into_view_if_needed().await,
1829 None => Err(crate::error::Error::ElementNotFound(format!(
1830 "Element not found: {}",
1831 selector
1832 ))),
1833 }
1834 }
1835
1836 pub async fn add_script_tag(
1846 &self,
1847 options: crate::protocol::page::AddScriptTagOptions,
1848 ) -> Result<Arc<crate::protocol::ElementHandle>> {
1849 options.validate()?;
1851
1852 let mut params = serde_json::json!({});
1854
1855 if let Some(content) = &options.content {
1856 params["content"] = serde_json::json!(content);
1857 }
1858
1859 if let Some(url) = &options.url {
1860 params["url"] = serde_json::json!(url);
1861 }
1862
1863 if let Some(path) = &options.path {
1864 let js_content = tokio::fs::read_to_string(path).await.map_err(|e| {
1866 Error::InvalidArgument(format!("Failed to read JS file '{}': {}", path, e))
1867 })?;
1868 params["content"] = serde_json::json!(js_content);
1869 }
1870
1871 if let Some(type_) = &options.type_ {
1872 params["type"] = serde_json::json!(type_);
1873 }
1874
1875 #[derive(Deserialize)]
1876 struct AddScriptTagResponse {
1877 element: serde_json::Value,
1878 }
1879
1880 let response: AddScriptTagResponse = self.channel().send("addScriptTag", params).await?;
1881
1882 let guid = response.element["guid"].as_str().ok_or_else(|| {
1883 Error::ProtocolError("Element GUID missing in addScriptTag response".to_string())
1884 })?;
1885
1886 let connection = self.base.connection();
1887 let element = connection.get_object(guid).await?;
1888
1889 let handle = element
1890 .as_any()
1891 .downcast_ref::<crate::protocol::ElementHandle>()
1892 .map(|e| Arc::new(e.clone()))
1893 .ok_or_else(|| {
1894 Error::ProtocolError(format!("Object {} is not an ElementHandle", guid))
1895 })?;
1896
1897 Ok(handle)
1898 }
1899}
1900
1901impl ChannelOwner for Frame {
1902 fn guid(&self) -> &str {
1903 self.base.guid()
1904 }
1905
1906 fn type_name(&self) -> &str {
1907 self.base.type_name()
1908 }
1909
1910 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
1911 self.base.parent()
1912 }
1913
1914 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
1915 self.base.connection()
1916 }
1917
1918 fn initializer(&self) -> &Value {
1919 self.base.initializer()
1920 }
1921
1922 fn channel(&self) -> &Channel {
1923 self.base.channel()
1924 }
1925
1926 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
1927 self.base.dispose(reason)
1928 }
1929
1930 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
1931 self.base.adopt(child)
1932 }
1933
1934 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
1935 self.base.add_child(guid, child)
1936 }
1937
1938 fn remove_child(&self, guid: &str) {
1939 self.base.remove_child(guid)
1940 }
1941
1942 fn on_event(&self, method: &str, params: Value) {
1943 match method {
1944 "navigated" => {
1945 if let Some(url_value) = params.get("url") {
1947 if let Some(url_str) = url_value.as_str() {
1948 if let Ok(mut url) = self.url.write() {
1950 *url = url_str.to_string();
1951 }
1952 }
1953 }
1954 }
1955 _ => {
1956 }
1959 }
1960 }
1961
1962 fn was_collected(&self) -> bool {
1963 self.base.was_collected()
1964 }
1965
1966 fn as_any(&self) -> &dyn Any {
1967 self
1968 }
1969}
1970
1971impl std::fmt::Debug for Frame {
1972 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1973 f.debug_struct("Frame").field("guid", &self.guid()).finish()
1974 }
1975}
1976
1977fn glob_match(pattern: &str, text: &str) -> bool {
1982 let regex_str = pattern
1983 .replace('.', "\\.")
1984 .replace("**", "\x00") .replace('*', "[^/]*")
1986 .replace('\x00', ".*"); let regex_str = format!("^{}$", regex_str);
1988 regex::Regex::new(®ex_str)
1989 .map(|re| re.is_match(text))
1990 .unwrap_or(false)
1991}