chromiumoxide/handler/
target_message_future.rs

1use futures::channel::{
2    mpsc,
3    oneshot::{self, channel as oneshot_channel},
4};
5use pin_project_lite::pin_project;
6use std::future::Future;
7use std::pin::Pin;
8use std::task::{Context, Poll};
9
10use crate::handler::target::TargetMessage;
11use crate::{error::Result, ArcHttpRequest};
12
13/// Convenience alias for sending messages to a Target task/actor.
14///
15/// This channel is typically owned by the Target event loop and accepts
16/// `TargetMessage` commands to be processed serially.
17type TargetSender = mpsc::Sender<TargetMessage>;
18
19pin_project! {
20    pub struct TargetMessageFuture<T> {
21        #[pin]
22        rx_request: oneshot::Receiver<T>,
23        #[pin]
24        target_sender: mpsc::Sender<TargetMessage>,
25        message: Option<TargetMessage>,
26    }
27}
28
29impl<T> TargetMessageFuture<T> {
30    pub fn new(
31        target_sender: TargetSender,
32        message: TargetMessage,
33        rx_request: oneshot::Receiver<T>,
34    ) -> Self {
35        Self {
36            target_sender,
37            rx_request,
38            message: Some(message),
39        }
40    }
41
42    /// Helper to build a `TargetMessageFuture<ArcHttpRequest>` for any
43    /// "wait" style target message (navigation, network idle, etc.).
44    ///
45    /// The `make_msg` closure receives the `oneshot::Sender<ArcHttpRequest>` and
46    /// must wrap it into the appropriate `TargetMessage` variant
47    /// (e.g. `TargetMessage::WaitForNavigation(tx)`).
48    pub(crate) fn wait(
49        target_sender: TargetSender,
50        make_msg: impl FnOnce(oneshot::Sender<ArcHttpRequest>) -> TargetMessage,
51    ) -> TargetMessageFuture<ArcHttpRequest> {
52        let (tx, rx_request) = oneshot_channel();
53        let message = make_msg(tx);
54        TargetMessageFuture::new(target_sender, message, rx_request)
55    }
56
57    /// Wait for the main-frame navigation to finish.
58    ///
59    /// This triggers a `TargetMessage::WaitForNavigation` and resolves with
60    /// the final `ArcHttpRequest` associated with that navigation (if any).
61    pub fn wait_for_navigation(target_sender: TargetSender) -> TargetMessageFuture<ArcHttpRequest> {
62        Self::wait(target_sender, TargetMessage::WaitForNavigation)
63    }
64
65    /// Wait until the main frame reaches `networkIdle`.
66    ///
67    /// This triggers a `TargetMessage::WaitForNetworkIdle` and resolves with
68    /// the `ArcHttpRequest` associated with the navigation that led to the
69    /// idle state (if any).
70    pub fn wait_for_network_idle(
71        target_sender: TargetSender,
72    ) -> TargetMessageFuture<ArcHttpRequest> {
73        Self::wait(target_sender, TargetMessage::WaitForNetworkIdle)
74    }
75
76    /// Wait until the main frame reaches `networkAlmostIdle`.
77    ///
78    /// This triggers a `TargetMessage::WaitForNetworkAlmostIdle` and resolves
79    /// with the `ArcHttpRequest` associated with that navigation (if any).
80    pub fn wait_for_network_almost_idle(
81        target_sender: TargetSender,
82    ) -> TargetMessageFuture<ArcHttpRequest> {
83        Self::wait(target_sender, TargetMessage::WaitForNetworkAlmostIdle)
84    }
85}
86
87impl<T> Future for TargetMessageFuture<T> {
88    type Output = Result<T>;
89
90    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
91        let mut this = self.project();
92
93        if this.message.is_some() {
94            match this.target_sender.poll_ready(cx) {
95                Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
96                Poll::Ready(Ok(_)) => {
97                    let message = this.message.take().expect("existence checked above");
98                    this.target_sender.start_send(message)?;
99                    cx.waker().wake_by_ref();
100                    Poll::Pending
101                }
102                Poll::Pending => Poll::Pending,
103            }
104        } else {
105            this.rx_request.as_mut().poll(cx).map_err(Into::into)
106        }
107    }
108}