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("{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 #[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#[derive(Debug, Clone)]
120pub struct DeadlineExceeded {
121 pub deadline: Instant,
123 pub now: Instant,
125}
126
127impl DeadlineExceeded {
128 pub fn new(now: Instant, deadline: Instant) -> Self {
132 Self { deadline, now }
134 }
135}
136
137#[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}