viewpoint_core/context/page_events/
mod.rs

1//! Page event handling for BrowserContext.
2//!
3//! This module provides page and close event handling functionality.
4
5use std::future::Future;
6
7use super::BrowserContext;
8use super::events::{HandlerId, WaitForPageBuilder};
9use crate::error::ContextError;
10use crate::page::Page;
11
12impl BrowserContext {
13    /// Register a handler for new page events.
14    ///
15    /// The handler will be called whenever a new page is created in this context.
16    /// Returns a handler ID that can be used to remove the handler with `off_page`.
17    ///
18    /// # Example
19    ///
20    /// ```no_run
21    /// use viewpoint_core::{Browser, Page};
22    ///
23    /// # async fn example() -> Result<(), viewpoint_core::CoreError> {
24    /// let browser = Browser::launch().headless(true).launch().await?;
25    /// let context = browser.new_context().await?;
26    ///
27    /// let handler_id = context.on_page(|page: Page| async move {
28    ///     println!("New page created: {}", page.url().await.unwrap_or_default());
29    /// }).await;
30    ///
31    /// // Later, remove the handler
32    /// context.off_page(handler_id).await;
33    /// # Ok(())
34    /// # }
35    /// ```
36    pub async fn on_page<F, Fut>(&self, handler: F) -> HandlerId
37    where
38        F: Fn(Page) -> Fut + Send + Sync + 'static,
39        Fut: Future<Output = ()> + Send + 'static,
40    {
41        self.event_manager.on_page(handler).await
42    }
43
44    /// Remove a page event handler by its ID.
45    ///
46    /// Returns `true` if a handler was removed, `false` if the ID was not found.
47    pub async fn off_page(&self, handler_id: HandlerId) -> bool {
48        self.event_manager.off_page(handler_id).await
49    }
50
51    /// Register a handler for page activated events.
52    ///
53    /// The handler will be called when a page becomes the active/foreground tab,
54    /// including when the user clicks on a tab in the browser UI or switches tabs
55    /// programmatically via `page.bring_to_front()`.
56    /// Returns a handler ID that can be used to remove the handler with `off_page_activated`.
57    ///
58    /// # Example
59    ///
60    /// ```no_run
61    /// use viewpoint_core::{Browser, Page};
62    ///
63    /// # async fn example() -> Result<(), viewpoint_core::CoreError> {
64    /// let browser = Browser::launch().headless(true).launch().await?;
65    /// let context = browser.new_context().await?;
66    ///
67    /// let handler_id = context.on_page_activated(|page: Page| async move {
68    ///     println!("Page activated: {}", page.url().await.unwrap_or_default());
69    /// }).await;
70    ///
71    /// // Later, remove the handler
72    /// context.off_page_activated(handler_id).await;
73    /// # Ok(())
74    /// # }
75    /// ```
76    pub async fn on_page_activated<F, Fut>(&self, handler: F) -> HandlerId
77    where
78        F: Fn(Page) -> Fut + Send + Sync + 'static,
79        Fut: Future<Output = ()> + Send + 'static,
80    {
81        self.event_manager.on_page_activated(handler).await
82    }
83
84    /// Remove a page activated event handler by its ID.
85    ///
86    /// Returns `true` if a handler was removed, `false` if the ID was not found.
87    pub async fn off_page_activated(&self, handler_id: HandlerId) -> bool {
88        self.event_manager.off_page_activated(handler_id).await
89    }
90
91    /// Register a handler for context close events.
92    ///
93    /// The handler will be called when the context is about to close,
94    /// before cleanup begins.
95    ///
96    /// # Example
97    ///
98    /// ```no_run
99    /// use viewpoint_core::Browser;
100    ///
101    /// # async fn example() -> Result<(), viewpoint_core::CoreError> {
102    /// let browser = Browser::launch().headless(true).launch().await?;
103    /// let context = browser.new_context().await?;
104    ///
105    /// let handler_id = context.on_close(|| async {
106    ///     println!("Context is closing!");
107    /// }).await;
108    ///
109    /// // Later, remove the handler
110    /// context.off_close(handler_id).await;
111    /// # Ok(())
112    /// # }
113    /// ```
114    pub async fn on_close<F, Fut>(&self, handler: F) -> HandlerId
115    where
116        F: Fn() -> Fut + Send + Sync + 'static,
117        Fut: Future<Output = ()> + Send + 'static,
118    {
119        self.event_manager.on_close(handler).await
120    }
121
122    /// Remove a close event handler by its ID.
123    ///
124    /// Returns `true` if a handler was removed, `false` if the ID was not found.
125    pub async fn off_close(&self, handler_id: HandlerId) -> bool {
126        self.event_manager.off_close(handler_id).await
127    }
128
129    /// Wait for a new page to be created during an action.
130    ///
131    /// This is useful for handling popups or links that open in new tabs.
132    /// The action is executed and the method waits for a new page to be
133    /// created as a result.
134    ///
135    /// # Example
136    ///
137    /// ```no_run
138    /// use viewpoint_core::{Browser, error::ContextError};
139    ///
140    /// # async fn example() -> Result<(), viewpoint_core::CoreError> {
141    /// let browser = Browser::launch().headless(true).launch().await?;
142    /// let context = browser.new_context().await?;
143    /// let page = context.new_page().await?;
144    ///
145    /// let popup = context.wait_for_page(|| async {
146    ///     page.locator("a[target=_blank]").click().await
147    ///         .map_err(|e| ContextError::Internal(e.to_string()))?;
148    ///     Ok(())
149    /// }).wait().await?;
150    ///
151    /// // Now work with the popup page
152    /// popup.goto("https://example.com").goto().await?;
153    /// # Ok(())
154    /// # }
155    /// ```
156    ///
157    /// # Errors
158    ///
159    /// Returns an error if:
160    /// - The action fails
161    /// - No page is created within the timeout (30 seconds)
162    pub fn wait_for_page<F, Fut>(&self, action: F) -> WaitForPageBuilder<'_, F, Fut>
163    where
164        F: FnOnce() -> Fut,
165        Fut: Future<Output = Result<(), ContextError>>,
166    {
167        WaitForPageBuilder::new(&self.event_manager, action)
168    }
169}