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