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 thiserror::Error;
8use tokio_tungstenite::tungstenite;
9use tokio_tungstenite::tungstenite::Message;
10use chromiumoxide_cdp::cdp::browser_protocol::page::FrameId;
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("FrameId {0:?} not found.")]
47    FrameNotFound(FrameId),
48    /// Error message related to a cdp response that is not a
49    /// `chromiumoxide_types::Error`
50    #[error("{0}")]
51    ChromeMessage(String),
52    #[error("{0}")]
53    DecodeError(#[from] DecodeError),
54    #[error("{0}")]
55    ScrollingFailed(String),
56    #[error("Requested value not found.")]
57    NotFound,
58    /// Detailed information about exception (or error) that was thrown during
59    /// script compilation or execution
60    #[error("{0:?}")]
61    JavascriptException(Box<ExceptionDetails>),
62    #[error("{0}")]
63    Url(#[from] url::ParseError),
64}
65
66impl CdpError {
67    pub fn msg(msg: impl Into<String>) -> Self {
68        CdpError::ChromeMessage(msg.into())
69    }
70}
71
72#[derive(Debug, Error)]
73pub enum ChannelError {
74    #[error("channel closed")]
75    Send,
76    #[error("{0}")]
77    Canceled(#[from] tokio::sync::oneshot::error::RecvError),
78}
79
80impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ChannelError {
81    fn from(_: tokio::sync::mpsc::error::SendError<T>) -> Self {
82        ChannelError::Send
83    }
84}
85
86impl<T> From<tokio::sync::mpsc::error::SendError<T>> for CdpError {
87    fn from(err: tokio::sync::mpsc::error::SendError<T>) -> Self {
88        ChannelError::from(err).into()
89    }
90}
91
92impl<T> From<tokio::sync::mpsc::error::TrySendError<T>> for CdpError {
93    fn from(_: tokio::sync::mpsc::error::TrySendError<T>) -> Self {
94        ChannelError::Send.into()
95    }
96}
97
98impl From<tokio::sync::oneshot::error::RecvError> for CdpError {
99    fn from(err: tokio::sync::oneshot::error::RecvError) -> Self {
100        ChannelError::from(err).into()
101    }
102}
103
104impl From<NavigationError> for CdpError {
105    fn from(err: NavigationError) -> Self {
106        match err {
107            NavigationError::Timeout { .. } => CdpError::Timeout,
108            NavigationError::FrameNotFound { frame, .. } => CdpError::FrameNotFound(frame),
109        }
110    }
111}
112
113/// An Error where `now > deadline`
114#[derive(Debug, Clone)]
115pub struct DeadlineExceeded {
116    /// The deadline that was set.
117    pub deadline: Instant,
118    /// The current time
119    pub now: Instant,
120}
121
122impl DeadlineExceeded {
123    /// Creates a new instance
124    ///
125    /// panics if `now > deadline`
126    pub fn new(now: Instant, deadline: Instant) -> Self {
127        // assert!(now > deadline);
128        Self { deadline, now }
129    }
130}
131
132/// `stderr` output of the browser child process
133///
134/// This implements a custom `Debug` formatter similar to [`std::process::Output`]. If the output
135/// is valid UTF-8, format as a string; otherwise format the byte sequence.
136#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
137pub struct BrowserStderr(Vec<u8>);
138
139impl BrowserStderr {
140    pub fn new(stderr: Vec<u8>) -> Self {
141        Self(stderr)
142    }
143
144    pub fn as_slice(&self) -> &[u8] {
145        &self.0
146    }
147
148    pub fn into_vec(self) -> Vec<u8> {
149        self.0
150    }
151}
152
153impl fmt::Debug for BrowserStderr {
154    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
155        let stderr_utf8 = std::str::from_utf8(&self.0);
156        let stderr_debug: &dyn fmt::Debug = match stderr_utf8 {
157            Ok(ref str) => str,
158            Err(_) => &self.0,
159        };
160
161        fmt.debug_tuple("BrowserStderr")
162            .field(stderr_debug)
163            .finish()
164    }
165}