codetether_agent/browser/
error_cdp.rs1use 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}