1use crate::error::{Error, Result};
7use crate::protocol::page::{GotoOptions, Response};
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;
15
16#[derive(Clone)]
25pub struct Frame {
26 base: ChannelOwnerImpl,
27}
28
29impl Frame {
30 pub fn new(
35 parent: Arc<dyn ChannelOwner>,
36 type_name: String,
37 guid: Arc<str>,
38 initializer: Value,
39 ) -> Result<Self> {
40 let base = ChannelOwnerImpl::new(
41 ParentOrConnection::Parent(parent),
42 type_name,
43 guid,
44 initializer,
45 );
46
47 Ok(Self { base })
48 }
49
50 fn channel(&self) -> &Channel {
52 self.base.channel()
53 }
54
55 pub async fn goto(&self, url: &str, options: Option<GotoOptions>) -> Result<Option<Response>> {
69 let mut params = serde_json::json!({
71 "url": url,
72 });
73
74 if let Some(opts) = options {
76 if let Some(timeout) = opts.timeout {
77 params["timeout"] = serde_json::json!(timeout.as_millis() as u64);
78 } else {
79 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
81 }
82 if let Some(wait_until) = opts.wait_until {
83 params["waitUntil"] = serde_json::json!(wait_until.as_str());
84 }
85 } else {
86 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
88 }
89
90 #[derive(Deserialize)]
93 struct GotoResponse {
94 response: Option<ResponseReference>,
95 }
96
97 #[derive(Deserialize)]
98 struct ResponseReference {
99 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
100 guid: Arc<str>,
101 }
102
103 let goto_result: GotoResponse = self.channel().send("goto", params).await?;
104
105 if let Some(response_ref) = goto_result.response {
107 let response_arc = {
113 let mut attempts = 0;
114 let max_attempts = 20; loop {
116 match self.connection().get_object(&response_ref.guid).await {
117 Ok(obj) => break obj,
118 Err(_) if attempts < max_attempts => {
119 attempts += 1;
120 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
121 }
122 Err(e) => return Err(e),
123 }
124 }
125 };
126
127 let initializer = response_arc.initializer();
130
131 let status = initializer["status"].as_u64().ok_or_else(|| {
133 crate::error::Error::ProtocolError("Response missing status".to_string())
134 })? as u16;
135
136 let headers = initializer["headers"]
138 .as_array()
139 .ok_or_else(|| {
140 crate::error::Error::ProtocolError("Response missing headers".to_string())
141 })?
142 .iter()
143 .filter_map(|h| {
144 let name = h["name"].as_str()?;
145 let value = h["value"].as_str()?;
146 Some((name.to_string(), value.to_string()))
147 })
148 .collect();
149
150 Ok(Some(Response {
151 url: initializer["url"]
152 .as_str()
153 .ok_or_else(|| {
154 crate::error::Error::ProtocolError("Response missing url".to_string())
155 })?
156 .to_string(),
157 status,
158 status_text: initializer["statusText"].as_str().unwrap_or("").to_string(),
159 ok: (200..300).contains(&status), headers,
161 }))
162 } else {
163 Ok(None)
166 }
167 }
168
169 pub async fn title(&self) -> Result<String> {
173 #[derive(Deserialize)]
174 struct TitleResponse {
175 value: String,
176 }
177
178 let response: TitleResponse = self.channel().send("title", serde_json::json!({})).await?;
179 Ok(response.value)
180 }
181
182 pub async fn query_selector(
186 &self,
187 selector: &str,
188 ) -> Result<Option<Arc<crate::protocol::ElementHandle>>> {
189 let response: serde_json::Value = self
190 .channel()
191 .send(
192 "querySelector",
193 serde_json::json!({
194 "selector": selector
195 }),
196 )
197 .await?;
198
199 if response.as_object().map(|o| o.is_empty()).unwrap_or(true) {
201 return Ok(None);
202 }
203
204 let element_value = if let Some(elem) = response.get("element") {
206 elem
207 } else if let Some(elem) = response.get("handle") {
208 elem
209 } else {
210 &response
212 };
213
214 if element_value.is_null() {
215 return Ok(None);
216 }
217
218 let guid = element_value["guid"].as_str().ok_or_else(|| {
220 crate::error::Error::ProtocolError("Element GUID missing".to_string())
221 })?;
222
223 let connection = self.base.connection();
225 let element = connection.get_object(guid).await?;
226
227 let handle = element
229 .as_any()
230 .downcast_ref::<crate::protocol::ElementHandle>()
231 .map(|e| Arc::new(e.clone()))
232 .ok_or_else(|| {
233 crate::error::Error::ProtocolError(format!(
234 "Object {} is not an ElementHandle",
235 guid
236 ))
237 })?;
238
239 Ok(Some(handle))
240 }
241
242 pub async fn query_selector_all(
246 &self,
247 selector: &str,
248 ) -> Result<Vec<Arc<crate::protocol::ElementHandle>>> {
249 #[derive(Deserialize)]
250 struct QueryAllResponse {
251 elements: Vec<serde_json::Value>,
252 }
253
254 let response: QueryAllResponse = self
255 .channel()
256 .send(
257 "querySelectorAll",
258 serde_json::json!({
259 "selector": selector
260 }),
261 )
262 .await?;
263
264 let connection = self.base.connection();
266 let mut handles = Vec::new();
267
268 for element_value in response.elements {
269 let guid = element_value["guid"].as_str().ok_or_else(|| {
270 crate::error::Error::ProtocolError("Element GUID missing".to_string())
271 })?;
272
273 let element = connection.get_object(guid).await?;
274
275 let handle = element
276 .as_any()
277 .downcast_ref::<crate::protocol::ElementHandle>()
278 .map(|e| Arc::new(e.clone()))
279 .ok_or_else(|| {
280 crate::error::Error::ProtocolError(format!(
281 "Object {} is not an ElementHandle",
282 guid
283 ))
284 })?;
285
286 handles.push(handle);
287 }
288
289 Ok(handles)
290 }
291
292 pub(crate) async fn locator_count(&self, selector: &str) -> Result<usize> {
297 #[derive(Deserialize)]
299 struct QueryAllResponse {
300 elements: Vec<serde_json::Value>,
301 }
302
303 let response: QueryAllResponse = self
304 .channel()
305 .send(
306 "querySelectorAll",
307 serde_json::json!({
308 "selector": selector
309 }),
310 )
311 .await?;
312
313 Ok(response.elements.len())
314 }
315
316 pub(crate) async fn locator_text_content(&self, selector: &str) -> Result<Option<String>> {
318 #[derive(Deserialize)]
319 struct TextContentResponse {
320 value: Option<String>,
321 }
322
323 let response: TextContentResponse = self
324 .channel()
325 .send(
326 "textContent",
327 serde_json::json!({
328 "selector": selector,
329 "strict": true,
330 "timeout": crate::DEFAULT_TIMEOUT_MS
331 }),
332 )
333 .await?;
334
335 Ok(response.value)
336 }
337
338 pub(crate) async fn locator_inner_text(&self, selector: &str) -> Result<String> {
340 #[derive(Deserialize)]
341 struct InnerTextResponse {
342 value: String,
343 }
344
345 let response: InnerTextResponse = self
346 .channel()
347 .send(
348 "innerText",
349 serde_json::json!({
350 "selector": selector,
351 "strict": true,
352 "timeout": crate::DEFAULT_TIMEOUT_MS
353 }),
354 )
355 .await?;
356
357 Ok(response.value)
358 }
359
360 pub(crate) async fn locator_inner_html(&self, selector: &str) -> Result<String> {
362 #[derive(Deserialize)]
363 struct InnerHTMLResponse {
364 value: String,
365 }
366
367 let response: InnerHTMLResponse = self
368 .channel()
369 .send(
370 "innerHTML",
371 serde_json::json!({
372 "selector": selector,
373 "strict": true,
374 "timeout": crate::DEFAULT_TIMEOUT_MS
375 }),
376 )
377 .await?;
378
379 Ok(response.value)
380 }
381
382 pub(crate) async fn locator_get_attribute(
384 &self,
385 selector: &str,
386 name: &str,
387 ) -> Result<Option<String>> {
388 #[derive(Deserialize)]
389 struct GetAttributeResponse {
390 value: Option<String>,
391 }
392
393 let response: GetAttributeResponse = self
394 .channel()
395 .send(
396 "getAttribute",
397 serde_json::json!({
398 "selector": selector,
399 "name": name,
400 "strict": true,
401 "timeout": crate::DEFAULT_TIMEOUT_MS
402 }),
403 )
404 .await?;
405
406 Ok(response.value)
407 }
408
409 pub(crate) async fn locator_is_visible(&self, selector: &str) -> Result<bool> {
411 #[derive(Deserialize)]
412 struct IsVisibleResponse {
413 value: bool,
414 }
415
416 let response: IsVisibleResponse = self
417 .channel()
418 .send(
419 "isVisible",
420 serde_json::json!({
421 "selector": selector,
422 "strict": true,
423 "timeout": crate::DEFAULT_TIMEOUT_MS
424 }),
425 )
426 .await?;
427
428 Ok(response.value)
429 }
430
431 pub(crate) async fn locator_is_enabled(&self, selector: &str) -> Result<bool> {
433 #[derive(Deserialize)]
434 struct IsEnabledResponse {
435 value: bool,
436 }
437
438 let response: IsEnabledResponse = self
439 .channel()
440 .send(
441 "isEnabled",
442 serde_json::json!({
443 "selector": selector,
444 "strict": true,
445 "timeout": crate::DEFAULT_TIMEOUT_MS
446 }),
447 )
448 .await?;
449
450 Ok(response.value)
451 }
452
453 pub(crate) async fn locator_is_checked(&self, selector: &str) -> Result<bool> {
455 #[derive(Deserialize)]
456 struct IsCheckedResponse {
457 value: bool,
458 }
459
460 let response: IsCheckedResponse = self
461 .channel()
462 .send(
463 "isChecked",
464 serde_json::json!({
465 "selector": selector,
466 "strict": true,
467 "timeout": crate::DEFAULT_TIMEOUT_MS
468 }),
469 )
470 .await?;
471
472 Ok(response.value)
473 }
474
475 pub(crate) async fn locator_is_editable(&self, selector: &str) -> Result<bool> {
477 #[derive(Deserialize)]
478 struct IsEditableResponse {
479 value: bool,
480 }
481
482 let response: IsEditableResponse = self
483 .channel()
484 .send(
485 "isEditable",
486 serde_json::json!({
487 "selector": selector,
488 "strict": true,
489 "timeout": crate::DEFAULT_TIMEOUT_MS
490 }),
491 )
492 .await?;
493
494 Ok(response.value)
495 }
496
497 pub(crate) async fn locator_is_focused(&self, selector: &str) -> Result<bool> {
503 #[derive(Deserialize)]
504 struct EvaluateResult {
505 value: serde_json::Value,
506 }
507
508 let script = r#"selector => {
511 const elements = document.querySelectorAll(selector);
512 if (elements.length === 0) return false;
513 const element = elements[0];
514 return document.activeElement === element;
515 }"#;
516
517 let params = serde_json::json!({
518 "expression": script,
519 "arg": {
520 "value": {"s": selector},
521 "handles": []
522 }
523 });
524
525 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
526
527 if let serde_json::Value::Object(map) = &result.value {
529 if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
530 return Ok(b);
531 }
532 }
533
534 Ok(result.value.to_string().to_lowercase().contains("true"))
536 }
537
538 pub(crate) async fn locator_click(
542 &self,
543 selector: &str,
544 options: Option<crate::protocol::ClickOptions>,
545 ) -> Result<()> {
546 let mut params = serde_json::json!({
547 "selector": selector,
548 "strict": true
549 });
550
551 if let Some(opts) = options {
552 let opts_json = opts.to_json();
553 if let Some(obj) = params.as_object_mut() {
554 if let Some(opts_obj) = opts_json.as_object() {
555 obj.extend(opts_obj.clone());
556 }
557 }
558 } else {
559 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
560 }
561
562 self.channel()
563 .send_no_result("click", params)
564 .await
565 .map_err(|e| match e {
566 Error::Timeout(msg) => {
567 Error::Timeout(format!("{} (selector: '{}')", msg, selector))
568 }
569 other => other,
570 })
571 }
572
573 pub(crate) async fn locator_dblclick(
575 &self,
576 selector: &str,
577 options: Option<crate::protocol::ClickOptions>,
578 ) -> Result<()> {
579 let mut params = serde_json::json!({
580 "selector": selector,
581 "strict": true
582 });
583
584 if let Some(opts) = options {
585 let opts_json = opts.to_json();
586 if let Some(obj) = params.as_object_mut() {
587 if let Some(opts_obj) = opts_json.as_object() {
588 obj.extend(opts_obj.clone());
589 }
590 }
591 } else {
592 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
593 }
594
595 self.channel().send_no_result("dblclick", params).await
596 }
597
598 pub(crate) async fn locator_fill(
600 &self,
601 selector: &str,
602 text: &str,
603 options: Option<crate::protocol::FillOptions>,
604 ) -> Result<()> {
605 let mut params = serde_json::json!({
606 "selector": selector,
607 "value": text,
608 "strict": true
609 });
610
611 if let Some(opts) = options {
612 let opts_json = opts.to_json();
613 if let Some(obj) = params.as_object_mut() {
614 if let Some(opts_obj) = opts_json.as_object() {
615 obj.extend(opts_obj.clone());
616 }
617 }
618 } else {
619 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
620 }
621
622 self.channel().send_no_result("fill", params).await
623 }
624
625 pub(crate) async fn locator_clear(
627 &self,
628 selector: &str,
629 options: Option<crate::protocol::FillOptions>,
630 ) -> Result<()> {
631 let mut params = serde_json::json!({
632 "selector": selector,
633 "value": "",
634 "strict": true
635 });
636
637 if let Some(opts) = options {
638 let opts_json = opts.to_json();
639 if let Some(obj) = params.as_object_mut() {
640 if let Some(opts_obj) = opts_json.as_object() {
641 obj.extend(opts_obj.clone());
642 }
643 }
644 } else {
645 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
646 }
647
648 self.channel().send_no_result("fill", params).await
649 }
650
651 pub(crate) async fn locator_press(
653 &self,
654 selector: &str,
655 key: &str,
656 options: Option<crate::protocol::PressOptions>,
657 ) -> Result<()> {
658 let mut params = serde_json::json!({
659 "selector": selector,
660 "key": key,
661 "strict": true
662 });
663
664 if let Some(opts) = options {
665 let opts_json = opts.to_json();
666 if let Some(obj) = params.as_object_mut() {
667 if let Some(opts_obj) = opts_json.as_object() {
668 obj.extend(opts_obj.clone());
669 }
670 }
671 } else {
672 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
673 }
674
675 self.channel().send_no_result("press", params).await
676 }
677
678 pub(crate) async fn locator_check(
679 &self,
680 selector: &str,
681 options: Option<crate::protocol::CheckOptions>,
682 ) -> Result<()> {
683 let mut params = serde_json::json!({
684 "selector": selector,
685 "strict": true
686 });
687
688 if let Some(opts) = options {
689 let opts_json = opts.to_json();
690 if let Some(obj) = params.as_object_mut() {
691 if let Some(opts_obj) = opts_json.as_object() {
692 obj.extend(opts_obj.clone());
693 }
694 }
695 } else {
696 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
697 }
698
699 self.channel().send_no_result("check", params).await
700 }
701
702 pub(crate) async fn locator_uncheck(
703 &self,
704 selector: &str,
705 options: Option<crate::protocol::CheckOptions>,
706 ) -> Result<()> {
707 let mut params = serde_json::json!({
708 "selector": selector,
709 "strict": true
710 });
711
712 if let Some(opts) = options {
713 let opts_json = opts.to_json();
714 if let Some(obj) = params.as_object_mut() {
715 if let Some(opts_obj) = opts_json.as_object() {
716 obj.extend(opts_obj.clone());
717 }
718 }
719 } else {
720 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
721 }
722
723 self.channel().send_no_result("uncheck", params).await
724 }
725
726 pub(crate) async fn locator_hover(
727 &self,
728 selector: &str,
729 options: Option<crate::protocol::HoverOptions>,
730 ) -> Result<()> {
731 let mut params = serde_json::json!({
732 "selector": selector,
733 "strict": true
734 });
735
736 if let Some(opts) = options {
737 let opts_json = opts.to_json();
738 if let Some(obj) = params.as_object_mut() {
739 if let Some(opts_obj) = opts_json.as_object() {
740 obj.extend(opts_obj.clone());
741 }
742 }
743 } else {
744 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
745 }
746
747 self.channel().send_no_result("hover", params).await
748 }
749
750 pub(crate) async fn locator_input_value(&self, selector: &str) -> Result<String> {
751 #[derive(Deserialize)]
752 struct InputValueResponse {
753 value: String,
754 }
755
756 let response: InputValueResponse = self
757 .channel()
758 .send(
759 "inputValue",
760 serde_json::json!({
761 "selector": selector,
762 "strict": true,
763 "timeout": crate::DEFAULT_TIMEOUT_MS }),
765 )
766 .await?;
767
768 Ok(response.value)
769 }
770
771 pub(crate) async fn locator_select_option(
772 &self,
773 selector: &str,
774 value: crate::protocol::SelectOption,
775 options: Option<crate::protocol::SelectOptions>,
776 ) -> Result<Vec<String>> {
777 #[derive(Deserialize)]
778 struct SelectOptionResponse {
779 values: Vec<String>,
780 }
781
782 let mut params = serde_json::json!({
783 "selector": selector,
784 "strict": true,
785 "options": [value.to_json()]
786 });
787
788 if let Some(opts) = options {
789 let opts_json = opts.to_json();
790 if let Some(obj) = params.as_object_mut() {
791 if let Some(opts_obj) = opts_json.as_object() {
792 obj.extend(opts_obj.clone());
793 }
794 }
795 } else {
796 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
798 }
799
800 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
801
802 Ok(response.values)
803 }
804
805 pub(crate) async fn locator_select_option_multiple(
806 &self,
807 selector: &str,
808 values: Vec<crate::protocol::SelectOption>,
809 options: Option<crate::protocol::SelectOptions>,
810 ) -> Result<Vec<String>> {
811 #[derive(Deserialize)]
812 struct SelectOptionResponse {
813 values: Vec<String>,
814 }
815
816 let values_array: Vec<_> = values.iter().map(|v| v.to_json()).collect();
817
818 let mut params = serde_json::json!({
819 "selector": selector,
820 "strict": true,
821 "options": values_array
822 });
823
824 if let Some(opts) = options {
825 let opts_json = opts.to_json();
826 if let Some(obj) = params.as_object_mut() {
827 if let Some(opts_obj) = opts_json.as_object() {
828 obj.extend(opts_obj.clone());
829 }
830 }
831 } else {
832 params["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
834 }
835
836 let response: SelectOptionResponse = self.channel().send("selectOption", params).await?;
837
838 Ok(response.values)
839 }
840
841 pub(crate) async fn locator_set_input_files(
842 &self,
843 selector: &str,
844 file: &std::path::PathBuf,
845 ) -> Result<()> {
846 use base64::{engine::general_purpose, Engine as _};
847 use std::io::Read;
848
849 let mut file_handle = std::fs::File::open(file)?;
851 let mut buffer = Vec::new();
852 file_handle.read_to_end(&mut buffer)?;
853
854 let base64_content = general_purpose::STANDARD.encode(&buffer);
856
857 let file_name = file
859 .file_name()
860 .and_then(|n| n.to_str())
861 .ok_or_else(|| crate::error::Error::InvalidArgument("Invalid file path".to_string()))?;
862
863 self.channel()
864 .send_no_result(
865 "setInputFiles",
866 serde_json::json!({
867 "selector": selector,
868 "strict": true,
869 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": [{
871 "name": file_name,
872 "buffer": base64_content
873 }]
874 }),
875 )
876 .await
877 }
878
879 pub(crate) async fn locator_set_input_files_multiple(
880 &self,
881 selector: &str,
882 files: &[&std::path::PathBuf],
883 ) -> Result<()> {
884 use base64::{engine::general_purpose, Engine as _};
885 use std::io::Read;
886
887 if files.is_empty() {
889 return self
890 .channel()
891 .send_no_result(
892 "setInputFiles",
893 serde_json::json!({
894 "selector": selector,
895 "strict": true,
896 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": []
898 }),
899 )
900 .await;
901 }
902
903 let mut file_objects = Vec::new();
905 for file_path in files {
906 let mut file_handle = std::fs::File::open(file_path)?;
907 let mut buffer = Vec::new();
908 file_handle.read_to_end(&mut buffer)?;
909
910 let base64_content = general_purpose::STANDARD.encode(&buffer);
911 let file_name = file_path
912 .file_name()
913 .and_then(|n| n.to_str())
914 .ok_or_else(|| {
915 crate::error::Error::InvalidArgument("Invalid file path".to_string())
916 })?;
917
918 file_objects.push(serde_json::json!({
919 "name": file_name,
920 "buffer": base64_content
921 }));
922 }
923
924 self.channel()
925 .send_no_result(
926 "setInputFiles",
927 serde_json::json!({
928 "selector": selector,
929 "strict": true,
930 "timeout": crate::DEFAULT_TIMEOUT_MS, "payloads": file_objects
932 }),
933 )
934 .await
935 }
936
937 pub(crate) async fn locator_set_input_files_payload(
938 &self,
939 selector: &str,
940 file: crate::protocol::FilePayload,
941 ) -> Result<()> {
942 use base64::{engine::general_purpose, Engine as _};
943
944 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
946
947 self.channel()
948 .send_no_result(
949 "setInputFiles",
950 serde_json::json!({
951 "selector": selector,
952 "strict": true,
953 "timeout": crate::DEFAULT_TIMEOUT_MS,
954 "payloads": [{
955 "name": file.name,
956 "mimeType": file.mime_type,
957 "buffer": base64_content
958 }]
959 }),
960 )
961 .await
962 }
963
964 pub(crate) async fn locator_set_input_files_payload_multiple(
965 &self,
966 selector: &str,
967 files: &[crate::protocol::FilePayload],
968 ) -> Result<()> {
969 use base64::{engine::general_purpose, Engine as _};
970
971 if files.is_empty() {
973 return self
974 .channel()
975 .send_no_result(
976 "setInputFiles",
977 serde_json::json!({
978 "selector": selector,
979 "strict": true,
980 "timeout": crate::DEFAULT_TIMEOUT_MS,
981 "payloads": []
982 }),
983 )
984 .await;
985 }
986
987 let file_objects: Vec<_> = files
989 .iter()
990 .map(|file| {
991 let base64_content = general_purpose::STANDARD.encode(&file.buffer);
992 serde_json::json!({
993 "name": file.name,
994 "mimeType": file.mime_type,
995 "buffer": base64_content
996 })
997 })
998 .collect();
999
1000 self.channel()
1001 .send_no_result(
1002 "setInputFiles",
1003 serde_json::json!({
1004 "selector": selector,
1005 "strict": true,
1006 "timeout": crate::DEFAULT_TIMEOUT_MS,
1007 "payloads": file_objects
1008 }),
1009 )
1010 .await
1011 }
1012
1013 pub(crate) async fn frame_evaluate_expression(&self, expression: &str) -> Result<()> {
1017 let params = serde_json::json!({
1018 "expression": expression,
1019 "arg": {
1020 "value": {"v": "null"},
1021 "handles": []
1022 }
1023 });
1024
1025 let _: serde_json::Value = self.channel().send("evaluateExpression", params).await?;
1026 Ok(())
1027 }
1028
1029 pub(crate) async fn frame_evaluate_expression_value(&self, expression: &str) -> Result<String> {
1041 let params = serde_json::json!({
1042 "expression": expression,
1043 "arg": {
1044 "value": {"v": "null"},
1045 "handles": []
1046 }
1047 });
1048
1049 #[derive(Deserialize)]
1050 struct EvaluateResult {
1051 value: serde_json::Value,
1052 }
1053
1054 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1055
1056 match &result.value {
1063 Value::Object(map) => {
1064 if let Some(s) = map.get("s").and_then(|v| v.as_str()) {
1065 Ok(s.to_string())
1067 } else if let Some(n) = map.get("n") {
1068 Ok(n.to_string())
1070 } else if let Some(b) = map.get("b").and_then(|v| v.as_bool()) {
1071 Ok(b.to_string())
1073 } else if let Some(v) = map.get("v").and_then(|v| v.as_str()) {
1074 Ok(v.to_string())
1076 } else {
1077 Ok(result.value.to_string())
1079 }
1080 }
1081 _ => {
1082 Ok(result.value.to_string())
1084 }
1085 }
1086 }
1087
1088 pub async fn evaluate<T: serde::Serialize>(
1128 &self,
1129 expression: &str,
1130 arg: Option<&T>,
1131 ) -> Result<Value> {
1132 let serialized_arg = match arg {
1134 Some(a) => serialize_argument(a),
1135 None => serialize_null(),
1136 };
1137
1138 let params = serde_json::json!({
1140 "expression": expression,
1141 "arg": serialized_arg
1142 });
1143
1144 #[derive(Deserialize)]
1146 struct EvaluateResult {
1147 value: serde_json::Value,
1148 }
1149
1150 let result: EvaluateResult = self.channel().send("evaluateExpression", params).await?;
1151
1152 Ok(parse_result(&result.value))
1154 }
1155
1156 pub async fn add_style_tag(
1196 &self,
1197 options: crate::protocol::page::AddStyleTagOptions,
1198 ) -> Result<Arc<crate::protocol::ElementHandle>> {
1199 options.validate()?;
1201
1202 let mut params = serde_json::json!({});
1204
1205 if let Some(content) = &options.content {
1206 params["content"] = serde_json::json!(content);
1207 }
1208
1209 if let Some(url) = &options.url {
1210 params["url"] = serde_json::json!(url);
1211 }
1212
1213 if let Some(path) = &options.path {
1214 let css_content = tokio::fs::read_to_string(path).await.map_err(|e| {
1216 Error::InvalidArgument(format!("Failed to read CSS file '{}': {}", path, e))
1217 })?;
1218 params["content"] = serde_json::json!(css_content);
1219 }
1220
1221 #[derive(Deserialize)]
1222 struct AddStyleTagResponse {
1223 element: serde_json::Value,
1224 }
1225
1226 let response: AddStyleTagResponse = self.channel().send("addStyleTag", params).await?;
1227
1228 let guid = response.element["guid"].as_str().ok_or_else(|| {
1229 Error::ProtocolError("Element GUID missing in addStyleTag response".to_string())
1230 })?;
1231
1232 let connection = self.base.connection();
1233 let element = connection.get_object(guid).await?;
1234
1235 let handle = element
1236 .as_any()
1237 .downcast_ref::<crate::protocol::ElementHandle>()
1238 .map(|e| Arc::new(e.clone()))
1239 .ok_or_else(|| {
1240 Error::ProtocolError(format!("Object {} is not an ElementHandle", guid))
1241 })?;
1242
1243 Ok(handle)
1244 }
1245}
1246
1247impl ChannelOwner for Frame {
1248 fn guid(&self) -> &str {
1249 self.base.guid()
1250 }
1251
1252 fn type_name(&self) -> &str {
1253 self.base.type_name()
1254 }
1255
1256 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
1257 self.base.parent()
1258 }
1259
1260 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
1261 self.base.connection()
1262 }
1263
1264 fn initializer(&self) -> &Value {
1265 self.base.initializer()
1266 }
1267
1268 fn channel(&self) -> &Channel {
1269 self.base.channel()
1270 }
1271
1272 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
1273 self.base.dispose(reason)
1274 }
1275
1276 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
1277 self.base.adopt(child)
1278 }
1279
1280 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
1281 self.base.add_child(guid, child)
1282 }
1283
1284 fn remove_child(&self, guid: &str) {
1285 self.base.remove_child(guid)
1286 }
1287
1288 fn on_event(&self, _method: &str, _params: Value) {
1289 }
1292
1293 fn was_collected(&self) -> bool {
1294 self.base.was_collected()
1295 }
1296
1297 fn as_any(&self) -> &dyn Any {
1298 self
1299 }
1300}
1301
1302impl std::fmt::Debug for Frame {
1303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1304 f.debug_struct("Frame").field("guid", &self.guid()).finish()
1305 }
1306}