Skip to main content

codetether_agent/browser/
error_cdp.rs

1use super::BrowserError;
2use chromiumoxide::cdp::js_protocol::runtime::{ExceptionDetails, StackTrace};
3use chromiumoxide::error::CdpError;
4
5impl From<CdpError> for BrowserError {
6    fn from(error: CdpError) -> Self {
7        match error {
8            CdpError::FrameNotFound(_) | CdpError::NotFound => Self::ElementNotFound("page".into()),
9            CdpError::JavascriptException(details) => exception(*details),
10            CdpError::NoResponse | CdpError::Ws(_) | CdpError::ChannelSendError(_) => {
11                Self::BrowserCrashed
12            }
13            CdpError::Timeout => Self::NavigationTimeout,
14            other => Self::OperationFailed(other.to_string()),
15        }
16    }
17}
18
19fn exception(details: ExceptionDetails) -> BrowserError {
20    let message = details
21        .exception
22        .as_ref()
23        .and_then(|value| value.description.as_deref().map(summary))
24        .unwrap_or_else(|| details.text.clone());
25    if details.text.contains("in promise") {
26        return BrowserError::EvalRejection { message };
27    }
28    BrowserError::JsException {
29        message,
30        stack: details.stack_trace.map(stack),
31    }
32}
33
34fn stack(trace: StackTrace) -> String {
35    trace
36        .call_frames
37        .into_iter()
38        .map(|frame| {
39            format!(
40                "at {} ({}:{}:{})",
41                frame.function_name,
42                frame.url,
43                frame.line_number + 1,
44                frame.column_number + 1
45            )
46        })
47        .collect::<Vec<_>>()
48        .join("\n")
49}
50
51fn summary(value: &str) -> String {
52    value.lines().next().unwrap_or(value).to_string()
53}