Skip to main content

chromiumoxide/
error.rs

1use std::fmt;
2use std::io;
3use std::process::ExitStatus;
4use std::time::Instant;
5
6use base64::DecodeError;
7use chromiumoxide_cdp::cdp::browser_protocol::page::FrameId;
8use thiserror::Error;
9use tokio_tungstenite::tungstenite;
10use tokio_tungstenite::tungstenite::Message;
11
12use crate::handler::frame::NavigationError;
13use chromiumoxide_cdp::cdp::js_protocol::runtime::ExceptionDetails;
14
15pub type Result<T, E = CdpError> = std::result::Result<T, E>;
16
17#[derive(Debug, Error)]
18pub enum CdpError {
19    #[error("{0}")]
20    Ws(#[from] tungstenite::Error),
21    #[error("{0}")]
22    Io(#[from] io::Error),
23    #[error("{0}")]
24    Serde(#[from] serde_json::Error),
25    #[error("{0}")]
26    #[cfg(feature = "simd")]
27    SerdeSonic(#[from] sonic_rs::Error),
28    #[error("{0}")]
29    Chrome(#[from] chromiumoxide_types::Error),
30    #[error("Received no response from the chromium instance.")]
31    NoResponse,
32    #[error("Received unexpected ws message: {0:?}")]
33    UnexpectedWsMessage(Message),
34    #[error("{0}")]
35    ChannelSendError(#[from] ChannelError),
36    #[error("Browser process exited with status {0:?} before websocket URL could be resolved, stderr: {1:?}")]
37    LaunchExit(ExitStatus, BrowserStderr),
38    #[error("Timeout while resolving websocket URL from browser process, stderr: {0:?}")]
39    LaunchTimeout(BrowserStderr),
40    #[error(
41        "Input/Output error while resolving websocket URL from browser process, stderr: {1:?}: {0}"
42    )]
43    LaunchIo(#[source] io::Error, BrowserStderr),
44    #[error("Request timed out.")]
45    Timeout,
46    #[error("net::ERR_TOO_MANY_NAVIGATIONS: main-frame navigated {0} times within a single goto")]
47    TooManyNavigations(u32),
48    #[error("FrameId {0:?} not found.")]
49    FrameNotFound(FrameId),
50    /// Error message related to a cdp response that is not a
51    /// `chromiumoxide_types::Error`
52    #[error("{0}")]
53    ChromeMessage(String),
54    #[error("{0}")]
55    DecodeError(#[from] DecodeError),
56    #[error("{0}")]
57    ScrollingFailed(String),
58    #[error("Requested value not found.")]
59    NotFound,
60    /// Detailed information about exception (or error) that was thrown during
61    /// script compilation or execution
62    #[error("{0:?}")]
63    JavascriptException(Box<ExceptionDetails>),
64    #[error("{0}")]
65    Url(#[from] url::ParseError),
66}
67
68impl CdpError {
69    pub fn msg(msg: impl Into<String>) -> Self {
70        CdpError::ChromeMessage(msg.into())
71    }
72}
73
74#[derive(Debug, Error)]
75pub enum ChannelError {
76    #[error("channel closed")]
77    Send,
78    #[error("{0}")]
79    Canceled(#[from] tokio::sync::oneshot::error::RecvError),
80}
81
82impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ChannelError {
83    fn from(_: tokio::sync::mpsc::error::SendError<T>) -> Self {
84        ChannelError::Send
85    }
86}
87
88impl<T> From<tokio::sync::mpsc::error::SendError<T>> for CdpError {
89    fn from(err: tokio::sync::mpsc::error::SendError<T>) -> Self {
90        ChannelError::from(err).into()
91    }
92}
93
94impl<T> From<tokio::sync::mpsc::error::TrySendError<T>> for CdpError {
95    fn from(_: tokio::sync::mpsc::error::TrySendError<T>) -> Self {
96        ChannelError::Send.into()
97    }
98}
99
100impl From<tokio::sync::oneshot::error::RecvError> for CdpError {
101    fn from(err: tokio::sync::oneshot::error::RecvError) -> Self {
102        ChannelError::from(err).into()
103    }
104}
105
106impl From<NavigationError> for CdpError {
107    fn from(err: NavigationError) -> Self {
108        match err {
109            NavigationError::Timeout { .. } => CdpError::Timeout,
110            NavigationError::FrameNotFound { frame, .. } => CdpError::FrameNotFound(frame),
111            NavigationError::TooManyNavigations { count, .. } => {
112                CdpError::TooManyNavigations(count)
113            }
114        }
115    }
116}
117
118/// An Error where `now > deadline`
119#[derive(Debug, Clone)]
120pub struct DeadlineExceeded {
121    /// The deadline that was set.
122    pub deadline: Instant,
123    /// The current time
124    pub now: Instant,
125}
126
127impl DeadlineExceeded {
128    /// Creates a new instance
129    ///
130    /// panics if `now > deadline`
131    pub fn new(now: Instant, deadline: Instant) -> Self {
132        // assert!(now > deadline);
133        Self { deadline, now }
134    }
135}
136
137/// `stderr` output of the browser child process
138///
139/// This implements a custom `Debug` formatter similar to [`std::process::Output`]. If the output
140/// is valid UTF-8, format as a string; otherwise format the byte sequence.
141#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
142pub struct BrowserStderr(Vec<u8>);
143
144impl BrowserStderr {
145    pub fn new(stderr: Vec<u8>) -> Self {
146        Self(stderr)
147    }
148
149    pub fn as_slice(&self) -> &[u8] {
150        &self.0
151    }
152
153    pub fn into_vec(self) -> Vec<u8> {
154        self.0
155    }
156}
157
158impl fmt::Debug for BrowserStderr {
159    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
160        let stderr_utf8 = std::str::from_utf8(&self.0);
161        let stderr_debug: &dyn fmt::Debug = match stderr_utf8 {
162            Ok(ref str) => str,
163            Err(_) => &self.0,
164        };
165
166        fmt.debug_tuple("BrowserStderr")
167            .field(stderr_debug)
168            .finish()
169    }
170}