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}