Skip to main content

playwright_rs/protocol/browser_context/
mod.rs

1// BrowserContext protocol object
2//
3// Represents an isolated browser context (session) within a browser instance.
4// Multiple contexts can exist in a single browser, each with its own cookies,
5// cache, and local storage.
6
7use crate::error::Result;
8use crate::protocol::{Browser, Page, Request, ResponseObject, Route};
9use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl};
10use std::collections::HashMap;
11use std::future::Future;
12use std::pin::Pin;
13use std::sync::atomic::AtomicBool;
14use std::sync::{Arc, Mutex};
15
16use crate::protocol::event_registry::EventRegistry;
17use tokio::sync::oneshot;
18
19/// Type alias for boxed route handler future
20type RouteHandlerFuture = Pin<Box<dyn Future<Output = Result<()>> + Send>>;
21
22/// Type alias for boxed binding callback future
23type BindingCallbackFuture = Pin<Box<dyn Future<Output = serde_json::Value> + Send>>;
24
25/// Type alias for boxed service worker handler future
26type ServiceWorkerHandlerFuture = Pin<Box<dyn Future<Output = Result<()>> + Send>>;
27
28/// Context-level service worker event handler
29type ServiceWorkerHandler =
30    Arc<dyn Fn(crate::protocol::Worker) -> ServiceWorkerHandlerFuture + Send + Sync>;
31
32/// Type alias for boxed context-level handler future
33type CtxHandlerFuture = Pin<Box<dyn Future<Output = Result<()>> + Send>>;
34
35/// Binding callback: receives deserialized JS args, returns a JSON value
36type BindingCallback = Arc<dyn Fn(Vec<serde_json::Value>) -> BindingCallbackFuture + Send + Sync>;
37
38/// Type alias for boxed WebSocketRoute handler future
39type WsRouteHandlerFuture = Pin<Box<dyn Future<Output = Result<()>> + Send>>;
40
41/// Storage for a single route handler
42#[derive(Clone)]
43struct RouteHandlerEntry {
44    pattern: String,
45    handler: Arc<dyn Fn(Route) -> RouteHandlerFuture + Send + Sync>,
46    /// Invocations of `handler` still running, for `unroute_all`.
47    in_flight: Arc<crate::protocol::in_flight::InFlight>,
48}
49
50/// Storage for a single WebSocket route handler entry
51#[derive(Clone)]
52struct ContextWsRouteHandlerEntry {
53    pattern: String,
54    handler: Arc<dyn Fn(crate::protocol::WebSocketRoute) -> WsRouteHandlerFuture + Send + Sync>,
55}
56
57/// BrowserContext represents an isolated browser session.
58///
59/// Contexts are isolated environments within a browser instance. Each context
60/// has its own cookies, cache, and local storage, enabling independent sessions
61/// without interference.
62///
63/// # Example
64///
65/// ```no_run
66/// use playwright_rs::protocol::Playwright;
67///
68/// #[tokio::main]
69/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
70///     let playwright = Playwright::launch().await?;
71///     let browser = playwright.chromium().launch().await?;
72///
73///     // Create isolated contexts
74///     let context1 = browser.new_context().await?;
75///     let context2 = browser.new_context().await?;
76///
77///     // Create pages in each context
78///     let page1 = context1.new_page().await?;
79///     let page2 = context2.new_page().await?;
80///
81///     // Access all pages in a context
82///     let pages = context1.pages();
83///     assert_eq!(pages.len(), 1);
84///
85///     // Access the browser from a context
86///     let ctx_browser = context1.browser().unwrap();
87///     assert_eq!(ctx_browser.name(), browser.name());
88///
89///     // App mode: access initial page created automatically
90///     let chromium = playwright.chromium();
91///     let app_context = chromium
92///         .launch_persistent_context_with_options(
93///             "/tmp/app-data",
94///             playwright_rs::protocol::BrowserContextOptions::builder()
95///                 .args(vec!["--app=https://example.com".to_string()])
96///                 .headless(true)
97///                 .build()
98///         )
99///         .await?;
100///
101///     // Get the initial page (don't create a new one!)
102///     let app_pages = app_context.pages();
103///     if !app_pages.is_empty() {
104///         let initial_page = &app_pages[0];
105///         // Use the initial page...
106///     }
107///
108///     // Cleanup
109///     context1.close().await?;
110///     context2.close().await?;
111///     app_context.close().await?;
112///     browser.close().await?;
113///     Ok(())
114/// }
115/// ```
116///
117/// See: <https://playwright.dev/docs/api/class-browsercontext>
118#[derive(Clone)]
119pub struct BrowserContext {
120    base: ChannelOwnerImpl,
121    /// Browser instance that owns this context (None for persistent contexts)
122    browser: Option<Browser>,
123    /// All open pages in this context
124    pages: Arc<Mutex<Vec<Page>>>,
125    /// Route handlers for context-level network interception
126    route_handlers: Arc<Mutex<Vec<RouteHandlerEntry>>>,
127    /// APIRequestContext GUID from initializer (resolved lazily)
128    request_context_guid: Option<String>,
129    /// Tracing GUID from initializer (resolved lazily)
130    tracing_guid: Option<String>,
131    /// Debugger GUID from initializer (resolved lazily)
132    debugger_guid: Option<String>,
133    /// Default action timeout for all pages in this context (milliseconds), stored as f64 bits.
134    default_timeout_ms: Arc<std::sync::atomic::AtomicU64>,
135    /// Default navigation timeout for all pages in this context (milliseconds), stored as f64 bits.
136    default_navigation_timeout_ms: Arc<std::sync::atomic::AtomicU64>,
137    /// `page` event: handlers and one-shot `expect_page` waiters.
138    page_events: Arc<EventRegistry<Page>>,
139    /// `close` event: one-time transition; `dispatch_all` wakes every waiter.
140    close_events: Arc<EventRegistry<()>>,
141    /// `request` event: handlers and one-shot waiters.
142    request: Arc<EventRegistry<Request>>,
143    /// `requestFinished` event handlers (no `expect_*`; waiter queue stays empty).
144    request_finished: Arc<EventRegistry<Request>>,
145    /// `requestFailed` event handlers (no `expect_*`; waiter queue stays empty).
146    request_failed: Arc<EventRegistry<Request>>,
147    /// `response` event: handlers and one-shot waiters.
148    response: Arc<EventRegistry<ResponseObject>>,
149    /// `dialog` event handlers (no `expect_*`; waiter queue stays empty).
150    dialog: Arc<EventRegistry<crate::protocol::Dialog>>,
151    /// `dialogClosed` handlers, subscribed on the first one rather than
152    /// eagerly like `dialog`: nothing accumulates these passively.
153    dialog_closed: Arc<EventRegistry<crate::protocol::Dialog>>,
154    /// Registered binding callbacks keyed by name (for expose_function / expose_binding)
155    binding_callbacks: Arc<Mutex<HashMap<String, BindingCallback>>>,
156    /// `console` event: handlers and one-shot `expect_console_message` waiters.
157    console: Arc<EventRegistry<crate::protocol::ConsoleMessage>>,
158    /// `pageError`-derived weberror event: handlers and one-shot waiters.
159    weberror: Arc<EventRegistry<crate::protocol::WebError>>,
160    /// Context-level service worker event handlers (fired when a service worker is registered)
161    serviceworker_handlers: Arc<Mutex<Vec<ServiceWorkerHandler>>>,
162    /// Handlers for the events forwarded from each page.
163    forwarders: events::Forwarders,
164    /// One-shot senders waiting for the next "serviceworker" event (expect_event("serviceworker"))
165    serviceworker_waiters: Arc<Mutex<Vec<oneshot::Sender<crate::protocol::Worker>>>>,
166    /// Active service workers tracked via "serviceWorker" events
167    service_workers_list: Arc<Mutex<Vec<crate::protocol::Worker>>>,
168    /// WebSocketRoute handlers for route_web_socket()
169    ws_route_handlers: Arc<Mutex<Vec<ContextWsRouteHandlerEntry>>>,
170    /// Whether this context has been closed.
171    /// Set to true when close() is called or a "close" event is received.
172    is_closed: Arc<AtomicBool>,
173    /// The HAR the `record_har` option started, until `close()` writes it.
174    /// An async lock, held across the export, so a concurrent `close()`
175    /// waits for the file instead of closing the context under it.
176    option_har: Arc<tokio::sync::Mutex<Option<crate::protocol::tracing::HarRecording>>>,
177}
178
179// Each concern is its own `impl BrowserContext` block in a child module.
180// Rustdoc lists inherent impls in declaration order, so these follow the
181// order a reader meets the API. One comment per line on purpose: rustfmt
182// sorts adjacent `mod` lines alphabetically.
183// Construction, pages and companion objects, timeouts, close.
184mod lifecycle;
185// Cookies, storage state, headers, permissions, geolocation, credentials.
186mod state;
187// Routes and network interception.
188mod network;
189// Event subscriptions, page-event forwarders and waiters.
190mod events;
191// Init scripts, exposed functions and bindings.
192mod bindings;
193// `BrowserContextOptions` and the types it carries.
194mod options;
195// Server-to-registry event dispatch, last because it is internal.
196mod dispatch;
197
198pub use options::{
199    AcceptDownloads, BrowserContextOptions, BrowserContextOptionsBuilder, RecordHar, RecordVideo,
200    Viewport,
201};
202pub use state::{
203    ClearCookiesOptions, Cookie, Geolocation, GrantPermissionsOptions, HttpCredentials,
204    HttpCredentialsSend, LocalStorageItem, Origin, StorageState, StorageStateOptions,
205};
206
207impl std::fmt::Debug for BrowserContext {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        f.debug_struct("BrowserContext")
210            .field("guid", &self.guid())
211            .finish()
212    }
213}