viewpoint_core/context/weberror/mod.rs
1//! Web error handling for BrowserContext.
2//!
3//! This module provides web error event handling functionality.
4
5use std::future::Future;
6use std::pin::Pin;
7
8use tracing::debug;
9use viewpoint_cdp::protocol::runtime::ExceptionThrownEvent;
10
11use super::BrowserContext;
12use crate::page::page_error::{PageError as PageErrorInfo, WebError};
13
14/// Type alias for web error handler function.
15pub type WebErrorHandler =
16 Box<dyn Fn(WebError) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
17
18impl BrowserContext {
19 /// Start listening for web errors from all pages in this context.
20 pub(crate) fn start_weberror_listener(&self) {
21 let mut events = self.connection().subscribe_events();
22 let pages = self.pages.clone();
23 let weberror_handler = self.weberror_handler.clone();
24 let context_id = self.context_id().to_string();
25
26 tokio::spawn(async move {
27 while let Ok(event) = events.recv().await {
28 if event.method == "Runtime.exceptionThrown" {
29 if let Some(params) = &event.params {
30 if let Ok(exception_event) =
31 serde_json::from_value::<ExceptionThrownEvent>(params.clone())
32 {
33 // Get session ID and target ID if available
34 let session_id = event.session_id.clone().unwrap_or_default();
35
36 // Find the matching page
37 let target_id = {
38 let pages_guard = pages.read().await;
39 pages_guard
40 .iter()
41 .find(|p| p.session_id == session_id)
42 .map(|p| p.target_id.clone())
43 .unwrap_or_default()
44 };
45
46 // Only handle errors from pages in this context
47 if target_id.is_empty() && session_id.is_empty() {
48 continue;
49 }
50
51 let page_error = PageErrorInfo::from_event(exception_event);
52 let web_error = WebError::new(page_error, target_id, session_id);
53
54 // Check if there's a handler
55 let handler = weberror_handler.read().await;
56 if let Some(ref h) = *handler {
57 h(web_error).await;
58 } else {
59 debug!(
60 "Web error in context {} (no handler): {}",
61 context_id,
62 web_error.message()
63 );
64 }
65 }
66 }
67 }
68 }
69 });
70 }
71
72 /// Set a handler for web errors (uncaught exceptions from any page).
73 ///
74 /// The handler will be called whenever an uncaught JavaScript exception
75 /// occurs in any page within this context.
76 ///
77 /// # Example
78 ///
79 /// ```no_run
80 /// use viewpoint_core::Browser;
81 ///
82 /// # async fn example() -> Result<(), viewpoint_core::CoreError> {
83 /// # let browser = Browser::launch().headless(true).launch().await?;
84 /// # let context = browser.new_context().await?;
85 /// context.on_weberror(|error| async move {
86 /// eprintln!("Error in {}: {}", error.target_id(), error.message());
87 /// if let Some(stack) = error.stack() {
88 /// eprintln!("Stack:\n{}", stack);
89 /// }
90 /// }).await;
91 /// # Ok(())
92 /// # }
93 pub async fn on_weberror<F, Fut>(&self, handler: F)
94 where
95 F: Fn(WebError) -> Fut + Send + Sync + 'static,
96 Fut: Future<Output = ()> + Send + 'static,
97 {
98 let mut weberror_handler = self.weberror_handler.write().await;
99 *weberror_handler = Some(Box::new(move |error| Box::pin(handler(error))));
100 }
101
102 /// Remove the web error handler.
103 pub async fn off_weberror(&self) {
104 let mut weberror_handler = self.weberror_handler.write().await;
105 *weberror_handler = None;
106 }
107}