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 = Box<
16    dyn Fn(WebError) -> Pin<Box<dyn Future<Output = ()> + Send>>
17        + Send
18        + Sync,
19>;
20
21impl BrowserContext {
22    /// Start listening for web errors from all pages in this context.
23    pub(crate) fn start_weberror_listener(&self) {
24        let mut events = self.connection().subscribe_events();
25        let pages = self.pages.clone();
26        let weberror_handler = self.weberror_handler.clone();
27        let context_id = self.context_id().to_string();
28        
29        tokio::spawn(async move {
30            while let Ok(event) = events.recv().await {
31                if event.method == "Runtime.exceptionThrown" {
32                    if let Some(params) = &event.params {
33                        if let Ok(exception_event) = serde_json::from_value::<ExceptionThrownEvent>(params.clone()) {
34                            // Get session ID and target ID if available
35                            let session_id = event.session_id.clone().unwrap_or_default();
36                            
37                            // Find the matching page
38                            let target_id = {
39                                let pages_guard = pages.read().await;
40                                pages_guard.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!("Web error in context {} (no handler): {}", context_id, web_error.message());
60                            }
61                        }
62                    }
63                }
64            }
65        });
66    }
67
68    /// Set a handler for web errors (uncaught exceptions from any page).
69    ///
70    /// The handler will be called whenever an uncaught JavaScript exception
71    /// occurs in any page within this context.
72    ///
73    /// # Example
74    ///
75    /// ```ignore
76    /// context.on_weberror(|error| async move {
77    ///     eprintln!("Error in {}: {}", error.target_id(), error.message());
78    ///     if let Some(stack) = error.stack() {
79    ///         eprintln!("Stack:\n{}", stack);
80    ///     }
81    /// }).await;
82    /// ```
83    pub async fn on_weberror<F, Fut>(&self, handler: F)
84    where
85        F: Fn(WebError) -> Fut + Send + Sync + 'static,
86        Fut: Future<Output = ()> + Send + 'static,
87    {
88        let mut weberror_handler = self.weberror_handler.write().await;
89        *weberror_handler = Some(Box::new(move |error| {
90            Box::pin(handler(error))
91        }));
92    }
93
94    /// Remove the web error handler.
95    pub async fn off_weberror(&self) {
96        let mut weberror_handler = self.weberror_handler.write().await;
97        *weberror_handler = None;
98    }
99}