1use std::sync::atomic::{AtomicUsize, Ordering};
2use std::sync::Arc;
3
4use chromiumoxide_cdp::cdp::browser_protocol::accessibility::{
5 GetFullAxTreeParamsBuilder, GetFullAxTreeReturns, GetPartialAxTreeParamsBuilder,
6 GetPartialAxTreeReturns,
7};
8use tokio::sync::mpsc::{channel, Receiver};
9use tokio::sync::oneshot::channel as oneshot_channel;
10use tokio::sync::Notify;
11
12use chromiumoxide_cdp::cdp::browser_protocol::browser::{GetVersionParams, GetVersionReturns};
13use chromiumoxide_cdp::cdp::browser_protocol::dom::{
14 BackendNodeId, DiscardSearchResultsParams, GetOuterHtmlParams, GetSearchResultsParams, NodeId,
15 PerformSearchParams, QuerySelectorAllParams, QuerySelectorParams, Rgba,
16};
17use chromiumoxide_cdp::cdp::browser_protocol::emulation::{
18 ClearDeviceMetricsOverrideParams, SetDefaultBackgroundColorOverrideParams,
19 SetDeviceMetricsOverrideParams,
20};
21use chromiumoxide_cdp::cdp::browser_protocol::input::{
22 DispatchDragEventParams, DispatchDragEventType, DispatchKeyEventParams, DispatchKeyEventType,
23 DispatchMouseEventParams, DispatchMouseEventType, DragData, MouseButton,
24};
25use chromiumoxide_cdp::cdp::browser_protocol::page::{
26 FrameId, GetLayoutMetricsParams, GetLayoutMetricsReturns, PrintToPdfParams, SetBypassCspParams,
27 Viewport,
28};
29use chromiumoxide_cdp::cdp::browser_protocol::target::{ActivateTargetParams, SessionId, TargetId};
30use chromiumoxide_cdp::cdp::js_protocol::runtime::{
31 CallFunctionOnParams, CallFunctionOnReturns, EvaluateParams, ExecutionContextId, RemoteObjectId,
32};
33use chromiumoxide_types::{Command, CommandResponse};
34
35use crate::cmd::{to_command_response, CommandMessage};
36use crate::error::{CdpError, Result};
37use crate::handler::commandfuture::CommandFuture;
38use crate::handler::domworld::DOMWorldKind;
39use crate::handler::httpfuture::HttpFuture;
40use crate::handler::sender::PageSender;
41use crate::handler::target::{GetExecutionContext, TargetMessage};
42use crate::handler::target_message_future::TargetMessageFuture;
43use crate::js::EvaluationResult;
44use crate::layout::{Delta, Point, ScrollBehavior};
45use crate::mouse::SmartMouse;
46use crate::page::ScreenshotParams;
47use crate::{keys, utils, ArcHttpRequest};
48
49static ACTIVE_PAGES: AtomicUsize = AtomicUsize::new(0);
53
54#[inline]
56pub fn active_page_count() -> usize {
57 ACTIVE_PAGES.load(Ordering::Relaxed)
58}
59
60#[derive(Debug)]
61pub struct PageHandle {
62 pub(crate) rx: Receiver<TargetMessage>,
63 page: Arc<PageInner>,
64}
65
66impl PageHandle {
67 pub fn new(
68 target_id: TargetId,
69 session_id: SessionId,
70 opener_id: Option<TargetId>,
71 request_timeout: std::time::Duration,
72 page_wake: Option<Arc<Notify>>,
73 ) -> Self {
74 let (commands, rx) = channel(2048);
75 let page = PageInner {
76 target_id,
77 session_id,
78 opener_id,
79 sender: PageSender::new(commands, page_wake),
80 smart_mouse: SmartMouse::new(),
81 request_timeout,
82 };
83 ACTIVE_PAGES.fetch_add(1, Ordering::Relaxed);
84 Self {
85 rx,
86 page: Arc::new(page),
87 }
88 }
89
90 pub(crate) fn inner(&self) -> &Arc<PageInner> {
91 &self.page
92 }
93}
94
95#[derive(Debug)]
96pub(crate) struct PageInner {
97 target_id: TargetId,
99 session_id: SessionId,
101 opener_id: Option<TargetId>,
103 sender: PageSender,
105 pub(crate) smart_mouse: SmartMouse,
107 request_timeout: std::time::Duration,
109}
110
111impl Drop for PageInner {
112 fn drop(&mut self) {
113 ACTIVE_PAGES.fetch_sub(1, Ordering::Relaxed);
114 }
115}
116
117impl PageInner {
118 pub(crate) async fn execute<T: Command>(&self, cmd: T) -> Result<CommandResponse<T::Response>> {
120 execute(
121 cmd,
122 self.sender.clone(),
123 Some(self.session_id.clone()),
124 self.request_timeout,
125 )
126 .await
127 }
128
129 pub(crate) async fn send_command<T: Command>(&self, cmd: T) -> Result<&Self> {
131 let _ = send_command(
132 cmd,
133 self.sender.clone(),
134 Some(self.session_id.clone()),
135 self.request_timeout,
136 )
137 .await;
138 Ok(self)
139 }
140
141 pub(crate) fn command_future<T: Command>(&self, cmd: T) -> Result<CommandFuture<T>> {
143 CommandFuture::new(
144 cmd,
145 self.sender.clone(),
146 Some(self.session_id.clone()),
147 self.request_timeout,
148 )
149 }
150
151 pub(crate) fn wait_for_navigation(&self) -> TargetMessageFuture<ArcHttpRequest> {
153 TargetMessageFuture::<ArcHttpRequest>::wait_for_navigation(
154 self.sender.clone(),
155 self.request_timeout,
156 )
157 }
158
159 pub(crate) fn wait_for_network_idle(&self) -> TargetMessageFuture<ArcHttpRequest> {
161 TargetMessageFuture::<ArcHttpRequest>::wait_for_network_idle(
162 self.sender.clone(),
163 self.request_timeout,
164 )
165 }
166
167 pub(crate) fn wait_for_network_almost_idle(&self) -> TargetMessageFuture<ArcHttpRequest> {
169 TargetMessageFuture::<ArcHttpRequest>::wait_for_network_almost_idle(
170 self.sender.clone(),
171 self.request_timeout,
172 )
173 }
174
175 pub(crate) fn http_future<T: Command>(&self, cmd: T) -> Result<HttpFuture<T>> {
178 Ok(HttpFuture::new(
179 self.sender.clone(),
180 self.command_future(cmd)?,
181 self.request_timeout,
182 ))
183 }
184
185 pub fn target_id(&self) -> &TargetId {
187 &self.target_id
188 }
189
190 pub fn session_id(&self) -> &SessionId {
192 &self.session_id
193 }
194
195 pub fn opener_id(&self) -> &Option<TargetId> {
197 &self.opener_id
198 }
199
200 pub(crate) async fn send_msg(&self, msg: TargetMessage) -> Result<()> {
204 match self.sender.try_send(msg) {
205 Ok(()) => Ok(()),
206 Err(tokio::sync::mpsc::error::TrySendError::Full(msg)) => {
207 tokio::time::timeout(self.request_timeout, self.sender.send(msg))
208 .await
209 .map_err(|_| CdpError::Timeout)?
210 .map_err(|_| CdpError::ChannelSendError(crate::error::ChannelError::Send))?;
211 Ok(())
212 }
213 Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
214 Err(CdpError::ChannelSendError(crate::error::ChannelError::Send))
215 }
216 }
217 }
218
219 pub(crate) async fn recv_msg<T>(&self, rx: tokio::sync::oneshot::Receiver<T>) -> Result<T> {
221 tokio::time::timeout(self.request_timeout, rx)
222 .await
223 .map_err(|_| CdpError::Timeout)?
224 .map_err(|e| CdpError::ChannelSendError(crate::error::ChannelError::Canceled(e)))
225 }
226
227 pub async fn find_element(&self, selector: impl Into<String>, node: NodeId) -> Result<NodeId> {
230 Ok(self
231 .execute(QuerySelectorParams::new(node, selector))
232 .await?
233 .node_id)
234 }
235
236 pub async fn outer_html(
238 &self,
239 object_id: RemoteObjectId,
240 node_id: NodeId,
241 backend_node_id: BackendNodeId,
242 ) -> Result<String> {
243 let cmd = GetOuterHtmlParams {
244 backend_node_id: Some(backend_node_id),
245 node_id: Some(node_id),
246 object_id: Some(object_id),
247 ..Default::default()
248 };
249
250 let chromiumoxide_types::CommandResponse { result, .. } = self.execute(cmd).await?;
251
252 Ok(result.outer_html)
253 }
254
255 pub async fn activate(&self) -> Result<&Self> {
257 self.execute(ActivateTargetParams::new(self.target_id().clone()))
258 .await?;
259 Ok(self)
260 }
261
262 pub async fn version(&self) -> Result<GetVersionReturns> {
264 Ok(self.execute(GetVersionParams::default()).await?.result)
265 }
266
267 pub(crate) async fn find_elements(
269 &self,
270 selector: impl Into<String>,
271 node: NodeId,
272 ) -> Result<Vec<NodeId>> {
273 Ok(self
274 .execute(QuerySelectorAllParams::new(node, selector))
275 .await?
276 .result
277 .node_ids)
278 }
279
280 pub async fn find_xpaths(&self, query: impl Into<String>) -> Result<Vec<NodeId>> {
282 let perform_search_returns = self
283 .execute(PerformSearchParams {
284 query: query.into(),
285 include_user_agent_shadow_dom: Some(true),
286 })
287 .await?
288 .result;
289
290 let search_results = self
291 .execute(GetSearchResultsParams::new(
292 perform_search_returns.search_id.clone(),
293 0,
294 perform_search_returns.result_count,
295 ))
296 .await?
297 .result;
298
299 self.execute(DiscardSearchResultsParams::new(
300 perform_search_returns.search_id,
301 ))
302 .await?;
303
304 Ok(search_results.node_ids)
305 }
306
307 pub async fn move_mouse(&self, point: Point) -> Result<&Self> {
310 self.smart_mouse.set_position(point);
311 self.execute(DispatchMouseEventParams::new(
312 DispatchMouseEventType::MouseMoved,
313 point.x,
314 point.y,
315 ))
316 .await?;
317 Ok(self)
318 }
319
320 pub async fn move_mouse_smooth(&self, target: Point) -> Result<&Self> {
323 let path = self.smart_mouse.path_to(target);
324 for step in &path {
325 self.execute(DispatchMouseEventParams::new(
326 DispatchMouseEventType::MouseMoved,
327 step.point.x,
328 step.point.y,
329 ))
330 .await?;
331 tokio::time::sleep(step.delay).await;
332 }
333 Ok(self)
334 }
335
336 pub fn mouse_position(&self) -> Point {
338 self.smart_mouse.position()
339 }
340
341 pub async fn scroll_by(
344 &self,
345 delta_x: f64,
346 delta_y: f64,
347 behavior: ScrollBehavior,
348 ) -> Result<&Self> {
349 let behavior_str = match behavior {
350 ScrollBehavior::Auto => "auto",
351 ScrollBehavior::Instant => "instant",
352 ScrollBehavior::Smooth => "smooth",
353 };
354
355 self.evaluate_expression(format!(
356 "window.scrollBy({{top: {}, left: {}, behavior: '{}'}});",
357 delta_y, delta_x, behavior_str
358 ))
359 .await?;
360
361 Ok(self)
362 }
363
364 pub async fn drag(
369 &self,
370 drag_type: DispatchDragEventType,
371 point: Point,
372 drag_data: DragData,
373 modifiers: Option<i64>,
374 ) -> Result<&Self> {
375 let mut params: DispatchDragEventParams =
376 DispatchDragEventParams::new(drag_type, point.x, point.y, drag_data);
377
378 if let Some(modifiers) = modifiers {
379 params.modifiers = Some(modifiers);
380 }
381
382 self.execute(params).await?;
383 Ok(self)
384 }
385
386 pub async fn scroll(&self, point: Point, delta: Delta) -> Result<&Self> {
389 let mut params: DispatchMouseEventParams =
390 DispatchMouseEventParams::new(DispatchMouseEventType::MouseWheel, point.x, point.y);
391
392 params.delta_x = Some(delta.delta_x);
393 params.delta_y = Some(delta.delta_y);
394
395 self.execute(params).await?;
396 Ok(self)
397 }
398
399 pub async fn click_with_count_base(
401 &self,
402 point: Point,
403 click_count: impl Into<i64>,
404 modifiers: impl Into<i64>,
405 button: impl Into<MouseButton>,
406 ) -> Result<&Self> {
407 let cmd = DispatchMouseEventParams::builder()
408 .x(point.x)
409 .y(point.y)
410 .button(button)
411 .click_count(click_count)
412 .modifiers(modifiers);
413
414 if let Ok(cmd) = cmd
415 .clone()
416 .r#type(DispatchMouseEventType::MousePressed)
417 .build()
418 {
419 self.move_mouse(point).await?.send_command(cmd).await?;
420 }
421
422 if let Ok(cmd) = cmd.r#type(DispatchMouseEventType::MouseReleased).build() {
423 self.execute(cmd).await?;
424 }
425
426 self.smart_mouse.set_position(point);
427 Ok(self)
428 }
429
430 pub async fn click_smooth(&self, point: Point) -> Result<&Self> {
432 self.move_mouse_smooth(point).await?;
433 self.click(point).await
434 }
435
436 pub async fn click_with_count(
438 &self,
439 point: Point,
440 click_count: impl Into<i64>,
441 modifiers: impl Into<i64>,
442 ) -> Result<&Self> {
443 self.click_with_count_base(point, click_count, modifiers, MouseButton::Left)
444 .await
445 }
446
447 pub async fn right_click_with_count(
449 &self,
450 point: Point,
451 click_count: impl Into<i64>,
452 modifiers: impl Into<i64>,
453 ) -> Result<&Self> {
454 self.click_with_count_base(point, click_count, modifiers, MouseButton::Right)
455 .await
456 }
457
458 pub async fn middle_click_with_count(
460 &self,
461 point: Point,
462 click_count: impl Into<i64>,
463 modifiers: impl Into<i64>,
464 ) -> Result<&Self> {
465 self.click_with_count_base(point, click_count, modifiers, MouseButton::Middle)
466 .await
467 }
468
469 pub async fn back_click_with_count(
471 &self,
472 point: Point,
473 click_count: impl Into<i64>,
474 modifiers: impl Into<i64>,
475 ) -> Result<&Self> {
476 self.click_with_count_base(point, click_count, modifiers, MouseButton::Back)
477 .await
478 }
479
480 pub async fn forward_click_with_count(
482 &self,
483 point: Point,
484 click_count: impl Into<i64>,
485 modifiers: impl Into<i64>,
486 ) -> Result<&Self> {
487 self.click_with_count_base(point, click_count, modifiers, MouseButton::Forward)
488 .await
489 }
490
491 pub async fn click_and_drag(
493 &self,
494 from: Point,
495 to: Point,
496 modifiers: impl Into<i64>,
497 ) -> Result<&Self> {
498 let modifiers = modifiers.into();
499 let click_count = 1;
500
501 let cmd = DispatchMouseEventParams::builder()
502 .button(MouseButton::Left)
503 .click_count(click_count)
504 .modifiers(modifiers);
505
506 if let Ok(cmd) = cmd
507 .clone()
508 .x(from.x)
509 .y(from.y)
510 .r#type(DispatchMouseEventType::MousePressed)
511 .build()
512 {
513 self.move_mouse(from).await?.send_command(cmd).await?;
514 }
515
516 if let Ok(cmd) = cmd
517 .clone()
518 .x(to.x)
519 .y(to.y)
520 .r#type(DispatchMouseEventType::MouseMoved)
521 .build()
522 {
523 self.move_mouse(to).await?.send_command(cmd).await?;
524 }
525
526 if let Ok(cmd) = cmd
527 .r#type(DispatchMouseEventType::MouseReleased)
528 .x(to.x)
529 .y(to.y)
530 .build()
531 {
532 self.send_command(cmd).await?;
533 }
534
535 self.smart_mouse.set_position(to);
536 Ok(self)
537 }
538
539 pub async fn click_and_drag_smooth(
542 &self,
543 from: Point,
544 to: Point,
545 modifiers: impl Into<i64>,
546 ) -> Result<&Self> {
547 let modifiers = modifiers.into();
548
549 self.move_mouse_smooth(from).await?;
551
552 if let Ok(cmd) = DispatchMouseEventParams::builder()
554 .x(from.x)
555 .y(from.y)
556 .button(MouseButton::Left)
557 .click_count(1)
558 .modifiers(modifiers)
559 .r#type(DispatchMouseEventType::MousePressed)
560 .build()
561 {
562 self.send_command(cmd).await?;
563 }
564
565 let path = self.smart_mouse.path_to(to);
567 for step in &path {
568 if let Ok(cmd) = DispatchMouseEventParams::builder()
569 .x(step.point.x)
570 .y(step.point.y)
571 .button(MouseButton::Left)
572 .modifiers(modifiers)
573 .r#type(DispatchMouseEventType::MouseMoved)
574 .build()
575 {
576 self.send_command(cmd).await?;
577 }
578 tokio::time::sleep(step.delay).await;
579 }
580
581 if let Ok(cmd) = DispatchMouseEventParams::builder()
583 .x(to.x)
584 .y(to.y)
585 .button(MouseButton::Left)
586 .click_count(1)
587 .modifiers(modifiers)
588 .r#type(DispatchMouseEventType::MouseReleased)
589 .build()
590 {
591 self.send_command(cmd).await?;
592 }
593
594 Ok(self)
595 }
596
597 pub async fn click(&self, point: Point) -> Result<&Self> {
599 self.click_with_count(point, 1, 0).await
600 }
601
602 pub async fn double_click(&self, point: Point) -> Result<&Self> {
604 self.click_with_count(point, 2, 0).await
605 }
606
607 pub async fn right_click(&self, point: Point) -> Result<&Self> {
609 self.right_click_with_count(point, 1, 0).await
610 }
611
612 pub async fn middle_click(&self, point: Point) -> Result<&Self> {
614 self.middle_click_with_count(point, 1, 0).await
615 }
616
617 pub async fn back_click(&self, point: Point) -> Result<&Self> {
619 self.back_click_with_count(point, 1, 0).await
620 }
621
622 pub async fn forward_click(&self, point: Point) -> Result<&Self> {
624 self.forward_click_with_count(point, 1, 0).await
625 }
626
627 pub async fn click_with_modifier(
629 &self,
630 point: Point,
631 modifiers: impl Into<i64>,
632 ) -> Result<&Self> {
633 self.click_with_count(point, 1, modifiers).await
634 }
635
636 pub async fn right_click_with_modifier(
638 &self,
639 point: Point,
640 modifiers: impl Into<i64>,
641 ) -> Result<&Self> {
642 self.right_click_with_count(point, 1, modifiers).await
643 }
644
645 pub async fn middle_click_with_modifier(
647 &self,
648 point: Point,
649 modifiers: impl Into<i64>,
650 ) -> Result<&Self> {
651 self.middle_click_with_count(point, 1, modifiers).await
652 }
653
654 pub async fn double_click_with_modifier(
656 &self,
657 point: Point,
658 modifiers: impl Into<i64>,
659 ) -> Result<&Self> {
660 self.click_with_count(point, 2, modifiers).await
661 }
662
663 pub async fn type_str(&self, input: impl AsRef<str>) -> Result<&Self> {
672 for c in input.as_ref().split("").filter(|s| !s.is_empty()) {
673 self._press_key(c, None).await?;
674 }
675 Ok(self)
676 }
677
678 pub async fn get_full_ax_tree(
680 &self,
681 depth: Option<i64>,
682 frame_id: Option<FrameId>,
683 ) -> Result<GetFullAxTreeReturns> {
684 let mut builder = GetFullAxTreeParamsBuilder::default();
685
686 if let Some(depth) = depth {
687 builder = builder.depth(depth);
688 }
689
690 if let Some(frame_id) = frame_id {
691 builder = builder.frame_id(frame_id);
692 }
693
694 let resp = self.execute(builder.build()).await?;
695
696 Ok(resp.result)
697 }
698
699 pub async fn get_partial_ax_tree(
701 &self,
702 node_id: Option<chromiumoxide_cdp::cdp::browser_protocol::dom::NodeId>,
703 backend_node_id: Option<BackendNodeId>,
704 object_id: Option<RemoteObjectId>,
705 fetch_relatives: Option<bool>,
706 ) -> Result<GetPartialAxTreeReturns> {
707 let mut builder = GetPartialAxTreeParamsBuilder::default();
708
709 if let Some(node_id) = node_id {
710 builder = builder.node_id(node_id);
711 }
712
713 if let Some(backend_node_id) = backend_node_id {
714 builder = builder.backend_node_id(backend_node_id);
715 }
716
717 if let Some(object_id) = object_id {
718 builder = builder.object_id(object_id);
719 }
720
721 if let Some(fetch_relatives) = fetch_relatives {
722 builder = builder.fetch_relatives(fetch_relatives);
723 }
724
725 let resp = self.execute(builder.build()).await?;
726
727 Ok(resp.result)
728 }
729
730 pub async fn type_str_with_modifier(
739 &self,
740 input: impl AsRef<str>,
741 modifiers: Option<i64>,
742 ) -> Result<&Self> {
743 for c in input.as_ref().split("").filter(|s| !s.is_empty()) {
744 self._press_key(c, modifiers).await?;
745 }
746 Ok(self)
747 }
748
749 async fn _press_key(&self, key: impl AsRef<str>, modifiers: Option<i64>) -> Result<&Self> {
752 let key = key.as_ref();
753 let key_definition = keys::get_key_definition(key)
754 .ok_or_else(|| CdpError::msg(format!("Key not found: {key}")))?;
755 let mut cmd = DispatchKeyEventParams::builder();
756
757 let key_down_event_type = if let Some(txt) = key_definition.text {
760 cmd = cmd.text(txt);
761 DispatchKeyEventType::KeyDown
762 } else if key_definition.key.len() == 1 {
763 cmd = cmd.text(key_definition.key);
764 DispatchKeyEventType::KeyDown
765 } else {
766 DispatchKeyEventType::RawKeyDown
767 };
768
769 cmd = cmd
770 .r#type(DispatchKeyEventType::KeyDown)
771 .key(key_definition.key)
772 .code(key_definition.code)
773 .windows_virtual_key_code(key_definition.key_code)
774 .native_virtual_key_code(key_definition.key_code);
775
776 if let Some(modifiers) = modifiers {
777 cmd = cmd.modifiers(modifiers);
778 }
779
780 if let Ok(cmd) = cmd.clone().r#type(key_down_event_type).build() {
781 self.execute(cmd).await?;
782 }
783
784 if let Ok(cmd) = cmd.r#type(DispatchKeyEventType::KeyUp).build() {
785 self.execute(cmd).await?;
786 }
787
788 Ok(self)
789 }
790
791 pub async fn press_key(&self, key: impl AsRef<str>) -> Result<&Self> {
794 self._press_key(key, None).await
795 }
796
797 pub async fn press_key_with_modifier(
800 &self,
801 key: impl AsRef<str>,
802 modifiers: Option<i64>,
803 ) -> Result<&Self> {
804 self._press_key(key, modifiers).await
805 }
806
807 pub async fn call_js_fn(
810 &self,
811 function_declaration: impl Into<String>,
812 await_promise: bool,
813 remote_object_id: RemoteObjectId,
814 ) -> Result<CallFunctionOnReturns> {
815 if let Ok(resp) = CallFunctionOnParams::builder()
816 .object_id(remote_object_id)
817 .function_declaration(function_declaration)
818 .generate_preview(true)
819 .await_promise(await_promise)
820 .build()
821 {
822 let resp = self.execute(resp).await?;
823 Ok(resp.result)
824 } else {
825 Err(CdpError::NotFound)
826 }
827 }
828
829 pub async fn evaluate_expression(
830 &self,
831 evaluate: impl Into<EvaluateParams>,
832 ) -> Result<EvaluationResult> {
833 let mut evaluate = evaluate.into();
834 if evaluate.context_id.is_none() {
835 evaluate.context_id = self.execution_context().await?;
836 }
837 if evaluate.await_promise.is_none() {
838 evaluate.await_promise = Some(true);
839 }
840 if evaluate.return_by_value.is_none() {
841 evaluate.return_by_value = Some(true);
842 }
843
844 let resp = self.execute(evaluate).await?.result;
847
848 if let Some(exception) = resp.exception_details {
849 return Err(CdpError::JavascriptException(Box::new(exception)));
850 }
851
852 Ok(EvaluationResult::new(resp.result))
853 }
854
855 pub async fn evaluate_function(
856 &self,
857 evaluate: impl Into<CallFunctionOnParams>,
858 ) -> Result<EvaluationResult> {
859 let mut evaluate = evaluate.into();
860 if evaluate.execution_context_id.is_none() {
861 evaluate.execution_context_id = self.execution_context().await?;
862 }
863 if evaluate.await_promise.is_none() {
864 evaluate.await_promise = Some(true);
865 }
866 if evaluate.return_by_value.is_none() {
867 evaluate.return_by_value = Some(true);
868 }
869
870 let resp = self.execute(evaluate).await?.result;
873 if let Some(exception) = resp.exception_details {
874 return Err(CdpError::JavascriptException(Box::new(exception)));
875 }
876 Ok(EvaluationResult::new(resp.result))
877 }
878
879 pub async fn execution_context(&self) -> Result<Option<ExecutionContextId>> {
880 self.execution_context_for_world(None, DOMWorldKind::Main)
881 .await
882 }
883
884 pub async fn secondary_execution_context(&self) -> Result<Option<ExecutionContextId>> {
885 self.execution_context_for_world(None, DOMWorldKind::Secondary)
886 .await
887 }
888
889 pub async fn frame_execution_context(
890 &self,
891 frame_id: FrameId,
892 ) -> Result<Option<ExecutionContextId>> {
893 self.execution_context_for_world(Some(frame_id), DOMWorldKind::Main)
894 .await
895 }
896
897 pub async fn frame_secondary_execution_context(
898 &self,
899 frame_id: FrameId,
900 ) -> Result<Option<ExecutionContextId>> {
901 self.execution_context_for_world(Some(frame_id), DOMWorldKind::Secondary)
902 .await
903 }
904
905 pub async fn execution_context_for_world(
906 &self,
907 frame_id: Option<FrameId>,
908 dom_world: DOMWorldKind,
909 ) -> Result<Option<ExecutionContextId>> {
910 let (tx, rx) = oneshot_channel();
911 let msg = TargetMessage::GetExecutionContext(GetExecutionContext {
912 dom_world,
913 frame_id,
914 tx,
915 });
916 match self.sender.try_send(msg) {
917 Ok(()) => {}
918 Err(tokio::sync::mpsc::error::TrySendError::Full(msg)) => {
919 tokio::time::timeout(self.request_timeout, self.sender.send(msg))
920 .await
921 .map_err(|_| CdpError::Timeout)?
922 .map_err(|_| CdpError::ChannelSendError(crate::error::ChannelError::Send))?;
923 }
924 Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
925 return Err(CdpError::ChannelSendError(crate::error::ChannelError::Send));
926 }
927 }
928 Ok(tokio::time::timeout(self.request_timeout, rx)
929 .await
930 .map_err(|_| CdpError::Timeout)??)
931 }
932
933 pub async fn layout_metrics(&self) -> Result<GetLayoutMetricsReturns> {
935 Ok(self
936 .execute(GetLayoutMetricsParams::default())
937 .await?
938 .result)
939 }
940
941 pub async fn set_bypass_csp(&self, enabled: bool) -> Result<&Self> {
943 self.execute(SetBypassCspParams::new(enabled)).await?;
944 Ok(self)
945 }
946
947 pub async fn screenshot(&self, params: impl Into<ScreenshotParams>) -> Result<Vec<u8>> {
949 self.activate().await?;
950 let params = params.into();
951 let full_page = params.full_page();
952 let omit_background = params.omit_background();
953
954 let mut cdp_params = params.cdp_params;
955
956 if full_page {
957 let metrics = self.layout_metrics().await?;
958 let width = metrics.css_content_size.width;
959 let height = metrics.css_content_size.height;
960
961 cdp_params.clip = Some(Viewport {
962 x: 0.,
963 y: 0.,
964 width,
965 height,
966 scale: 1.,
967 });
968
969 self.execute(SetDeviceMetricsOverrideParams::new(
970 width as i64,
971 height as i64,
972 1.,
973 false,
974 ))
975 .await?;
976 }
977
978 if omit_background {
979 self.execute(SetDefaultBackgroundColorOverrideParams {
980 color: Some(Rgba {
981 r: 0,
982 g: 0,
983 b: 0,
984 a: Some(0.),
985 }),
986 })
987 .await?;
988 }
989
990 let res = self.execute(cdp_params).await?.result;
991
992 if omit_background {
993 self.send_command(SetDefaultBackgroundColorOverrideParams { color: None })
994 .await?;
995 }
996
997 if full_page {
998 self.send_command(ClearDeviceMetricsOverrideParams {})
999 .await?;
1000 }
1001
1002 Ok(utils::base64::decode(&res.data)?)
1003 }
1004
1005 pub async fn print_to_pdf(&self, params: impl Into<PrintToPdfParams>) -> Result<Vec<u8>> {
1007 self.activate().await?;
1008 let params = params.into();
1009
1010 let res = self.execute(params).await?.result;
1011
1012 Ok(utils::base64::decode(&res.data)?)
1013 }
1014}
1015
1016pub(crate) async fn execute<T: Command>(
1017 cmd: T,
1018 sender: PageSender,
1019 session: Option<SessionId>,
1020 request_timeout: std::time::Duration,
1021) -> Result<CommandResponse<T::Response>> {
1022 let method = cmd.identifier();
1023 let rx = send_command(cmd, sender, session, request_timeout).await?;
1024 let resp = tokio::time::timeout(request_timeout, rx)
1025 .await
1026 .map_err(|_| CdpError::Timeout)???;
1027 to_command_response::<T>(resp, method)
1028}
1029
1030pub(crate) async fn send_command<T: Command>(
1035 cmd: T,
1036 sender: PageSender,
1037 session: Option<SessionId>,
1038 request_timeout: std::time::Duration,
1039) -> Result<tokio::sync::oneshot::Receiver<Result<chromiumoxide_types::Response, CdpError>>> {
1040 let (tx, rx) = oneshot_channel();
1041 let msg = CommandMessage::with_session(cmd, tx, session)?;
1042 let target_msg = TargetMessage::Command(msg);
1043 match sender.try_send(target_msg) {
1044 Ok(()) => {}
1045 Err(tokio::sync::mpsc::error::TrySendError::Full(msg)) => {
1046 tokio::time::timeout(request_timeout, sender.send(msg))
1047 .await
1048 .map_err(|_| CdpError::Timeout)?
1049 .map_err(|_| CdpError::ChannelSendError(crate::error::ChannelError::Send))?;
1050 }
1051 Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
1052 return Err(CdpError::ChannelSendError(crate::error::ChannelError::Send));
1053 }
1054 }
1055 Ok(rx)
1056}