playwright_rs/protocol/browser_context.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};
9use crate::server::channel::Channel;
10use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use std::any::Any;
14use std::collections::HashMap;
15use std::sync::{Arc, Mutex};
16
17/// BrowserContext represents an isolated browser session.
18///
19/// Contexts are isolated environments within a browser instance. Each context
20/// has its own cookies, cache, and local storage, enabling independent sessions
21/// without interference.
22///
23/// # Example
24///
25/// ```ignore
26/// use playwright_rs::protocol::Playwright;
27///
28/// #[tokio::main]
29/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
30/// let playwright = Playwright::launch().await?;
31/// let browser = playwright.chromium().launch().await?;
32///
33/// // Create isolated contexts
34/// let context1 = browser.new_context().await?;
35/// let context2 = browser.new_context().await?;
36///
37/// // Create pages in each context
38/// let page1 = context1.new_page().await?;
39/// let page2 = context2.new_page().await?;
40///
41/// // Access all pages in a context
42/// let pages = context1.pages();
43/// assert_eq!(pages.len(), 1);
44///
45/// // Access the browser from a context
46/// let ctx_browser = context1.browser().unwrap();
47/// assert_eq!(ctx_browser.name(), browser.name());
48///
49/// // App mode: access initial page created automatically
50/// let chromium = playwright.chromium();
51/// let app_context = chromium
52/// .launch_persistent_context_with_options(
53/// "/tmp/app-data",
54/// playwright_rs::protocol::BrowserContextOptions::builder()
55/// .args(vec!["--app=https://example.com".to_string()])
56/// .headless(true)
57/// .build()
58/// )
59/// .await?;
60///
61/// // Get the initial page (don't create a new one!)
62/// let app_pages = app_context.pages();
63/// if !app_pages.is_empty() {
64/// let initial_page = &app_pages[0];
65/// // Use the initial page...
66/// }
67///
68/// // Cleanup
69/// context1.close().await?;
70/// context2.close().await?;
71/// app_context.close().await?;
72/// browser.close().await?;
73/// Ok(())
74/// }
75/// ```
76///
77/// See: <https://playwright.dev/docs/api/class-browsercontext>
78#[derive(Clone)]
79pub struct BrowserContext {
80 base: ChannelOwnerImpl,
81 /// Browser instance that owns this context (None for persistent contexts)
82 browser: Option<Browser>,
83 /// All open pages in this context
84 pages: Arc<Mutex<Vec<Page>>>,
85}
86
87impl BrowserContext {
88 /// Creates a new BrowserContext from protocol initialization
89 ///
90 /// This is called by the object factory when the server sends a `__create__` message
91 /// for a BrowserContext object.
92 ///
93 /// # Arguments
94 ///
95 /// * `parent` - The parent Browser object
96 /// * `type_name` - The protocol type name ("BrowserContext")
97 /// * `guid` - The unique identifier for this context
98 /// * `initializer` - The initialization data from the server
99 ///
100 /// # Errors
101 ///
102 /// Returns error if initializer is malformed
103 pub fn new(
104 parent: Arc<dyn ChannelOwner>,
105 type_name: String,
106 guid: Arc<str>,
107 initializer: Value,
108 ) -> Result<Self> {
109 let base = ChannelOwnerImpl::new(
110 ParentOrConnection::Parent(parent.clone()),
111 type_name,
112 guid,
113 initializer,
114 );
115
116 // Store browser reference if parent is a Browser
117 // Returns None only for special contexts (Android, Electron) where parent is not a Browser
118 // For both regular contexts and persistent contexts, parent is a Browser instance
119 let browser = parent.as_any().downcast_ref::<Browser>().cloned();
120
121 let context = Self {
122 base,
123 browser,
124 pages: Arc::new(Mutex::new(Vec::new())),
125 };
126
127 // Enable dialog event subscription
128 // Dialog events need to be explicitly subscribed to via updateSubscription command
129 let channel = context.channel().clone();
130 tokio::spawn(async move {
131 let _ = channel
132 .send_no_result(
133 "updateSubscription",
134 serde_json::json!({
135 "event": "dialog",
136 "enabled": true
137 }),
138 )
139 .await;
140 });
141
142 Ok(context)
143 }
144
145 /// Returns the channel for sending protocol messages
146 ///
147 /// Used internally for sending RPC calls to the context.
148 fn channel(&self) -> &Channel {
149 self.base.channel()
150 }
151
152 /// Adds a script which would be evaluated in one of the following scenarios:
153 ///
154 /// - Whenever a page is created in the browser context or is navigated.
155 /// - Whenever a child frame is attached or navigated in any page in the browser context.
156 ///
157 /// The script is evaluated after the document was created but before any of its scripts
158 /// were run. This is useful to amend the JavaScript environment, e.g. to seed Math.random.
159 ///
160 /// # Arguments
161 ///
162 /// * `script` - Script to be evaluated in all pages in the browser context.
163 ///
164 /// # Errors
165 ///
166 /// Returns error if:
167 /// - Context has been closed
168 /// - Communication with browser process fails
169 ///
170 /// See: <https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script>
171 pub async fn add_init_script(&self, script: &str) -> Result<()> {
172 self.channel()
173 .send_no_result("addInitScript", serde_json::json!({ "source": script }))
174 .await
175 }
176
177 /// Creates a new page in this browser context.
178 ///
179 /// Pages are isolated tabs/windows within a context. Each page starts
180 /// at "about:blank" and can be navigated independently.
181 ///
182 /// # Errors
183 ///
184 /// Returns error if:
185 /// - Context has been closed
186 /// - Communication with browser process fails
187 ///
188 /// See: <https://playwright.dev/docs/api/class-browsercontext#browser-context-new-page>
189 pub async fn new_page(&self) -> Result<Page> {
190 // Response contains the GUID of the created Page
191 #[derive(Deserialize)]
192 struct NewPageResponse {
193 page: GuidRef,
194 }
195
196 #[derive(Deserialize)]
197 struct GuidRef {
198 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
199 guid: Arc<str>,
200 }
201
202 // Send newPage RPC to server
203 let response: NewPageResponse = self
204 .channel()
205 .send("newPage", serde_json::json!({}))
206 .await?;
207
208 // Retrieve the Page object from the connection registry
209 let page_arc = self.connection().get_object(&response.page.guid).await?;
210
211 // Downcast to Page
212 let page = page_arc.as_any().downcast_ref::<Page>().ok_or_else(|| {
213 crate::error::Error::ProtocolError(format!(
214 "Expected Page object, got {}",
215 page_arc.type_name()
216 ))
217 })?;
218
219 // Note: Don't track the page here - it will be tracked via the "page" event
220 // that Playwright server sends automatically when a page is created.
221 // Tracking it here would create duplicates.
222
223 Ok(page.clone())
224 }
225
226 /// Returns all open pages in the context.
227 ///
228 /// This method provides a snapshot of all currently active pages that belong
229 /// to this browser context instance. Pages created via `new_page()` and popup
230 /// pages opened through user interactions are included.
231 ///
232 /// In persistent contexts launched with `--app=url`, this will include the
233 /// initial page created automatically by Playwright.
234 ///
235 /// # Errors
236 ///
237 /// This method does not return errors. It provides a snapshot of pages at
238 /// the time of invocation.
239 ///
240 /// See: <https://playwright.dev/docs/api/class-browsercontext#browser-context-pages>
241 pub fn pages(&self) -> Vec<Page> {
242 self.pages.lock().unwrap().clone()
243 }
244
245 /// Returns the browser instance that owns this context.
246 ///
247 /// Returns `None` only for contexts created outside of normal browser
248 /// (e.g., Android or Electron contexts). For both regular contexts and
249 /// persistent contexts, this returns the owning Browser instance.
250 ///
251 /// # Errors
252 ///
253 /// This method does not return errors.
254 ///
255 /// See: <https://playwright.dev/docs/api/class-browsercontext#browser-context-browser>
256 pub fn browser(&self) -> Option<Browser> {
257 self.browser.clone()
258 }
259
260 /// Closes the browser context and all its pages.
261 ///
262 /// This is a graceful operation that sends a close command to the context
263 /// and waits for it to shut down properly.
264 ///
265 /// # Errors
266 ///
267 /// Returns error if:
268 /// - Context has already been closed
269 /// - Communication with browser process fails
270 ///
271 /// See: <https://playwright.dev/docs/api/class-browsercontext#browser-context-close>
272 pub async fn close(&self) -> Result<()> {
273 // Send close RPC to server
274 self.channel()
275 .send_no_result("close", serde_json::json!({}))
276 .await
277 }
278
279 /// Pauses the browser context.
280 ///
281 /// This pauses the execution of all pages in the context.
282 pub async fn pause(&self) -> Result<()> {
283 self.channel()
284 .send_no_result("pause", serde_json::Value::Null)
285 .await
286 }
287}
288
289impl ChannelOwner for BrowserContext {
290 fn guid(&self) -> &str {
291 self.base.guid()
292 }
293
294 fn type_name(&self) -> &str {
295 self.base.type_name()
296 }
297
298 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
299 self.base.parent()
300 }
301
302 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
303 self.base.connection()
304 }
305
306 fn initializer(&self) -> &Value {
307 self.base.initializer()
308 }
309
310 fn channel(&self) -> &Channel {
311 self.base.channel()
312 }
313
314 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
315 self.base.dispose(reason)
316 }
317
318 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
319 self.base.adopt(child)
320 }
321
322 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
323 self.base.add_child(guid, child)
324 }
325
326 fn remove_child(&self, guid: &str) {
327 self.base.remove_child(guid)
328 }
329
330 fn on_event(&self, method: &str, params: Value) {
331 match method {
332 "page" => {
333 // Page events are triggered when pages are created, including:
334 // - Initial page in persistent context with --app mode
335 // - Popup pages opened through user interactions
336 // Event format: {page: {guid: "..."}}
337 if let Some(page_guid) = params
338 .get("page")
339 .and_then(|v| v.get("guid"))
340 .and_then(|v| v.as_str())
341 {
342 let connection = self.connection();
343 let page_guid_owned = page_guid.to_string();
344 let pages = self.pages.clone();
345
346 tokio::spawn(async move {
347 // Get the Page object
348 let page_arc = match connection.get_object(&page_guid_owned).await {
349 Ok(obj) => obj,
350 Err(_) => return,
351 };
352
353 // Downcast to Page
354 let page = match page_arc.as_any().downcast_ref::<Page>() {
355 Some(p) => p.clone(),
356 None => return,
357 };
358
359 // Track the page
360 pages.lock().unwrap().push(page);
361 });
362 }
363 }
364 "dialog" => {
365 // Dialog events come to BrowserContext, need to forward to the associated Page
366 // Event format: {dialog: {guid: "..."}}
367 // The Dialog protocol object has the Page as its parent
368 if let Some(dialog_guid) = params
369 .get("dialog")
370 .and_then(|v| v.get("guid"))
371 .and_then(|v| v.as_str())
372 {
373 let connection = self.connection();
374 let dialog_guid_owned = dialog_guid.to_string();
375
376 tokio::spawn(async move {
377 // Get the Dialog object
378 let dialog_arc = match connection.get_object(&dialog_guid_owned).await {
379 Ok(obj) => obj,
380 Err(_) => return,
381 };
382
383 // Downcast to Dialog
384 let dialog = match dialog_arc
385 .as_any()
386 .downcast_ref::<crate::protocol::Dialog>()
387 {
388 Some(d) => d.clone(),
389 None => return,
390 };
391
392 // Get the Page from the Dialog's parent
393 let page_arc = match dialog_arc.parent() {
394 Some(parent) => parent,
395 None => return,
396 };
397
398 // Downcast to Page
399 let page = match page_arc.as_any().downcast_ref::<Page>() {
400 Some(p) => p.clone(),
401 None => return,
402 };
403
404 // Forward to Page's dialog handlers
405 page.trigger_dialog_event(dialog).await;
406 });
407 }
408 }
409 _ => {
410 // Other events will be handled in future phases
411 }
412 }
413 }
414
415 fn was_collected(&self) -> bool {
416 self.base.was_collected()
417 }
418
419 fn as_any(&self) -> &dyn Any {
420 self
421 }
422}
423
424impl std::fmt::Debug for BrowserContext {
425 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426 f.debug_struct("BrowserContext")
427 .field("guid", &self.guid())
428 .finish()
429 }
430}
431
432/// Viewport dimensions for browser context.
433///
434/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context>
435#[derive(Debug, Clone, Serialize, Deserialize)]
436pub struct Viewport {
437 /// Page width in pixels
438 pub width: u32,
439 /// Page height in pixels
440 pub height: u32,
441}
442
443/// Geolocation coordinates.
444///
445/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context>
446#[derive(Debug, Clone, Serialize, Deserialize)]
447pub struct Geolocation {
448 /// Latitude between -90 and 90
449 pub latitude: f64,
450 /// Longitude between -180 and 180
451 pub longitude: f64,
452 /// Optional accuracy in meters (default: 0)
453 #[serde(skip_serializing_if = "Option::is_none")]
454 pub accuracy: Option<f64>,
455}
456
457/// Cookie information for storage state.
458///
459/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
460#[derive(Debug, Clone, Serialize, Deserialize)]
461#[serde(rename_all = "camelCase")]
462pub struct Cookie {
463 /// Cookie name
464 pub name: String,
465 /// Cookie value
466 pub value: String,
467 /// Cookie domain (use dot prefix for subdomain matching, e.g., ".example.com")
468 pub domain: String,
469 /// Cookie path
470 pub path: String,
471 /// Unix timestamp in seconds; -1 for session cookies
472 pub expires: f64,
473 /// HTTP-only flag
474 pub http_only: bool,
475 /// Secure flag
476 pub secure: bool,
477 /// SameSite attribute ("Strict", "Lax", "None")
478 #[serde(skip_serializing_if = "Option::is_none")]
479 pub same_site: Option<String>,
480}
481
482/// Local storage item for storage state.
483///
484/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
485#[derive(Debug, Clone, Serialize, Deserialize)]
486pub struct LocalStorageItem {
487 /// Storage key
488 pub name: String,
489 /// Storage value
490 pub value: String,
491}
492
493/// Origin with local storage items for storage state.
494///
495/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
496#[derive(Debug, Clone, Serialize, Deserialize)]
497#[serde(rename_all = "camelCase")]
498pub struct Origin {
499 /// Origin URL (e.g., "https://example.com")
500 pub origin: String,
501 /// Local storage items for this origin
502 pub local_storage: Vec<LocalStorageItem>,
503}
504
505/// Storage state containing cookies and local storage.
506///
507/// Used to populate a browser context with saved authentication state,
508/// enabling session persistence across context instances.
509///
510/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
511#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct StorageState {
513 /// List of cookies
514 pub cookies: Vec<Cookie>,
515 /// List of origins with local storage
516 pub origins: Vec<Origin>,
517}
518
519/// Options for creating a new browser context.
520///
521/// Allows customizing viewport, user agent, locale, timezone, geolocation,
522/// permissions, and other browser context settings.
523///
524/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context>
525#[derive(Debug, Clone, Default, Serialize)]
526#[serde(rename_all = "camelCase")]
527pub struct BrowserContextOptions {
528 /// Sets consistent viewport for all pages in the context.
529 /// Set to null via `no_viewport(true)` to disable viewport emulation.
530 #[serde(skip_serializing_if = "Option::is_none")]
531 pub viewport: Option<Viewport>,
532
533 /// Disables viewport emulation when set to true.
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub no_viewport: Option<bool>,
536
537 /// Custom user agent string
538 #[serde(skip_serializing_if = "Option::is_none")]
539 pub user_agent: Option<String>,
540
541 /// Locale for the context (e.g., "en-GB", "de-DE", "fr-FR")
542 #[serde(skip_serializing_if = "Option::is_none")]
543 pub locale: Option<String>,
544
545 /// Timezone identifier (e.g., "America/New_York", "Europe/Berlin")
546 #[serde(skip_serializing_if = "Option::is_none")]
547 pub timezone_id: Option<String>,
548
549 /// Geolocation coordinates
550 #[serde(skip_serializing_if = "Option::is_none")]
551 pub geolocation: Option<Geolocation>,
552
553 /// List of permissions to grant (e.g., "geolocation", "notifications")
554 #[serde(skip_serializing_if = "Option::is_none")]
555 pub permissions: Option<Vec<String>>,
556
557 /// Emulates 'prefers-colors-scheme' media feature ("light", "dark", "no-preference")
558 #[serde(skip_serializing_if = "Option::is_none")]
559 pub color_scheme: Option<String>,
560
561 /// Whether the viewport supports touch events
562 #[serde(skip_serializing_if = "Option::is_none")]
563 pub has_touch: Option<bool>,
564
565 /// Whether the meta viewport tag is respected
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub is_mobile: Option<bool>,
568
569 /// Whether JavaScript is enabled in the context
570 #[serde(skip_serializing_if = "Option::is_none")]
571 pub javascript_enabled: Option<bool>,
572
573 /// Emulates network being offline
574 #[serde(skip_serializing_if = "Option::is_none")]
575 pub offline: Option<bool>,
576
577 /// Whether to automatically download attachments
578 #[serde(skip_serializing_if = "Option::is_none")]
579 pub accept_downloads: Option<bool>,
580
581 /// Whether to bypass Content-Security-Policy
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub bypass_csp: Option<bool>,
584
585 /// Whether to ignore HTTPS errors
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub ignore_https_errors: Option<bool>,
588
589 /// Device scale factor (default: 1)
590 #[serde(skip_serializing_if = "Option::is_none")]
591 pub device_scale_factor: Option<f64>,
592
593 /// Extra HTTP headers to send with every request
594 #[serde(skip_serializing_if = "Option::is_none")]
595 pub extra_http_headers: Option<HashMap<String, String>>,
596
597 /// Base URL for relative navigation
598 #[serde(skip_serializing_if = "Option::is_none")]
599 pub base_url: Option<String>,
600
601 /// Storage state to populate the context (cookies, localStorage, sessionStorage).
602 /// Can be an inline StorageState object or a file path string.
603 /// Use builder methods `storage_state()` for inline or `storage_state_path()` for file path.
604 #[serde(skip_serializing_if = "Option::is_none")]
605 pub storage_state: Option<StorageState>,
606
607 /// Storage state file path (alternative to inline storage_state).
608 /// This is handled by the builder and converted to storage_state during serialization.
609 #[serde(skip_serializing_if = "Option::is_none")]
610 pub storage_state_path: Option<String>,
611
612 // Launch options (for launch_persistent_context)
613 /// Additional arguments to pass to browser instance
614 #[serde(skip_serializing_if = "Option::is_none")]
615 pub args: Option<Vec<String>>,
616
617 /// Browser distribution channel (e.g., "chrome", "msedge")
618 #[serde(skip_serializing_if = "Option::is_none")]
619 pub channel: Option<String>,
620
621 /// Enable Chromium sandboxing (default: false on Linux)
622 #[serde(skip_serializing_if = "Option::is_none")]
623 pub chromium_sandbox: Option<bool>,
624
625 /// Auto-open DevTools (deprecated, default: false)
626 #[serde(skip_serializing_if = "Option::is_none")]
627 pub devtools: Option<bool>,
628
629 /// Directory to save downloads
630 #[serde(skip_serializing_if = "Option::is_none")]
631 pub downloads_path: Option<String>,
632
633 /// Path to custom browser executable
634 #[serde(skip_serializing_if = "Option::is_none")]
635 pub executable_path: Option<String>,
636
637 /// Firefox user preferences (Firefox only)
638 #[serde(skip_serializing_if = "Option::is_none")]
639 pub firefox_user_prefs: Option<HashMap<String, serde_json::Value>>,
640
641 /// Run in headless mode (default: true unless devtools=true)
642 #[serde(skip_serializing_if = "Option::is_none")]
643 pub headless: Option<bool>,
644
645 /// Slow down operations by N milliseconds
646 #[serde(skip_serializing_if = "Option::is_none")]
647 pub slow_mo: Option<f64>,
648
649 /// Timeout for browser launch in milliseconds
650 #[serde(skip_serializing_if = "Option::is_none")]
651 pub timeout: Option<f64>,
652
653 /// Directory to save traces
654 #[serde(skip_serializing_if = "Option::is_none")]
655 pub traces_dir: Option<String>,
656}
657
658impl BrowserContextOptions {
659 /// Creates a new builder for BrowserContextOptions
660 pub fn builder() -> BrowserContextOptionsBuilder {
661 BrowserContextOptionsBuilder::default()
662 }
663}
664
665/// Builder for BrowserContextOptions
666#[derive(Debug, Clone, Default)]
667pub struct BrowserContextOptionsBuilder {
668 viewport: Option<Viewport>,
669 no_viewport: Option<bool>,
670 user_agent: Option<String>,
671 locale: Option<String>,
672 timezone_id: Option<String>,
673 geolocation: Option<Geolocation>,
674 permissions: Option<Vec<String>>,
675 color_scheme: Option<String>,
676 has_touch: Option<bool>,
677 is_mobile: Option<bool>,
678 javascript_enabled: Option<bool>,
679 offline: Option<bool>,
680 accept_downloads: Option<bool>,
681 bypass_csp: Option<bool>,
682 ignore_https_errors: Option<bool>,
683 device_scale_factor: Option<f64>,
684 extra_http_headers: Option<HashMap<String, String>>,
685 base_url: Option<String>,
686 storage_state: Option<StorageState>,
687 storage_state_path: Option<String>,
688 // Launch options
689 args: Option<Vec<String>>,
690 channel: Option<String>,
691 chromium_sandbox: Option<bool>,
692 devtools: Option<bool>,
693 downloads_path: Option<String>,
694 executable_path: Option<String>,
695 firefox_user_prefs: Option<HashMap<String, serde_json::Value>>,
696 headless: Option<bool>,
697 slow_mo: Option<f64>,
698 timeout: Option<f64>,
699 traces_dir: Option<String>,
700}
701
702impl BrowserContextOptionsBuilder {
703 /// Sets the viewport dimensions
704 pub fn viewport(mut self, viewport: Viewport) -> Self {
705 self.viewport = Some(viewport);
706 self.no_viewport = None; // Clear no_viewport if setting viewport
707 self
708 }
709
710 /// Disables viewport emulation
711 pub fn no_viewport(mut self, no_viewport: bool) -> Self {
712 self.no_viewport = Some(no_viewport);
713 if no_viewport {
714 self.viewport = None; // Clear viewport if setting no_viewport
715 }
716 self
717 }
718
719 /// Sets the user agent string
720 pub fn user_agent(mut self, user_agent: String) -> Self {
721 self.user_agent = Some(user_agent);
722 self
723 }
724
725 /// Sets the locale
726 pub fn locale(mut self, locale: String) -> Self {
727 self.locale = Some(locale);
728 self
729 }
730
731 /// Sets the timezone identifier
732 pub fn timezone_id(mut self, timezone_id: String) -> Self {
733 self.timezone_id = Some(timezone_id);
734 self
735 }
736
737 /// Sets the geolocation
738 pub fn geolocation(mut self, geolocation: Geolocation) -> Self {
739 self.geolocation = Some(geolocation);
740 self
741 }
742
743 /// Sets the permissions to grant
744 pub fn permissions(mut self, permissions: Vec<String>) -> Self {
745 self.permissions = Some(permissions);
746 self
747 }
748
749 /// Sets the color scheme preference
750 pub fn color_scheme(mut self, color_scheme: String) -> Self {
751 self.color_scheme = Some(color_scheme);
752 self
753 }
754
755 /// Sets whether the viewport supports touch events
756 pub fn has_touch(mut self, has_touch: bool) -> Self {
757 self.has_touch = Some(has_touch);
758 self
759 }
760
761 /// Sets whether this is a mobile viewport
762 pub fn is_mobile(mut self, is_mobile: bool) -> Self {
763 self.is_mobile = Some(is_mobile);
764 self
765 }
766
767 /// Sets whether JavaScript is enabled
768 pub fn javascript_enabled(mut self, javascript_enabled: bool) -> Self {
769 self.javascript_enabled = Some(javascript_enabled);
770 self
771 }
772
773 /// Sets whether to emulate offline network
774 pub fn offline(mut self, offline: bool) -> Self {
775 self.offline = Some(offline);
776 self
777 }
778
779 /// Sets whether to automatically download attachments
780 pub fn accept_downloads(mut self, accept_downloads: bool) -> Self {
781 self.accept_downloads = Some(accept_downloads);
782 self
783 }
784
785 /// Sets whether to bypass Content-Security-Policy
786 pub fn bypass_csp(mut self, bypass_csp: bool) -> Self {
787 self.bypass_csp = Some(bypass_csp);
788 self
789 }
790
791 /// Sets whether to ignore HTTPS errors
792 pub fn ignore_https_errors(mut self, ignore_https_errors: bool) -> Self {
793 self.ignore_https_errors = Some(ignore_https_errors);
794 self
795 }
796
797 /// Sets the device scale factor
798 pub fn device_scale_factor(mut self, device_scale_factor: f64) -> Self {
799 self.device_scale_factor = Some(device_scale_factor);
800 self
801 }
802
803 /// Sets extra HTTP headers
804 pub fn extra_http_headers(mut self, extra_http_headers: HashMap<String, String>) -> Self {
805 self.extra_http_headers = Some(extra_http_headers);
806 self
807 }
808
809 /// Sets the base URL for relative navigation
810 pub fn base_url(mut self, base_url: String) -> Self {
811 self.base_url = Some(base_url);
812 self
813 }
814
815 /// Sets the storage state inline (cookies, localStorage).
816 ///
817 /// Populates the browser context with the provided storage state, including
818 /// cookies and local storage. This is useful for initializing a context with
819 /// a saved authentication state.
820 ///
821 /// Mutually exclusive with `storage_state_path()`.
822 ///
823 /// # Example
824 ///
825 /// ```rust
826 /// use playwright_rs::protocol::{BrowserContextOptions, Cookie, StorageState, Origin, LocalStorageItem};
827 ///
828 /// let storage_state = StorageState {
829 /// cookies: vec![Cookie {
830 /// name: "session_id".to_string(),
831 /// value: "abc123".to_string(),
832 /// domain: ".example.com".to_string(),
833 /// path: "/".to_string(),
834 /// expires: -1.0,
835 /// http_only: true,
836 /// secure: true,
837 /// same_site: Some("Lax".to_string()),
838 /// }],
839 /// origins: vec![Origin {
840 /// origin: "https://example.com".to_string(),
841 /// local_storage: vec![LocalStorageItem {
842 /// name: "user_prefs".to_string(),
843 /// value: "{\"theme\":\"dark\"}".to_string(),
844 /// }],
845 /// }],
846 /// };
847 ///
848 /// let options = BrowserContextOptions::builder()
849 /// .storage_state(storage_state)
850 /// .build();
851 /// ```
852 ///
853 /// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
854 pub fn storage_state(mut self, storage_state: StorageState) -> Self {
855 self.storage_state = Some(storage_state);
856 self.storage_state_path = None; // Clear path if setting inline
857 self
858 }
859
860 /// Sets the storage state from a file path.
861 ///
862 /// The file should contain a JSON representation of StorageState with cookies
863 /// and origins. This is useful for loading authentication state saved from a
864 /// previous session.
865 ///
866 /// Mutually exclusive with `storage_state()`.
867 ///
868 /// # Example
869 ///
870 /// ```rust
871 /// use playwright_rs::protocol::BrowserContextOptions;
872 ///
873 /// let options = BrowserContextOptions::builder()
874 /// .storage_state_path("auth.json".to_string())
875 /// .build();
876 /// ```
877 ///
878 /// The file should have this format:
879 /// ```json
880 /// {
881 /// "cookies": [{
882 /// "name": "session_id",
883 /// "value": "abc123",
884 /// "domain": ".example.com",
885 /// "path": "/",
886 /// "expires": -1,
887 /// "httpOnly": true,
888 /// "secure": true,
889 /// "sameSite": "Lax"
890 /// }],
891 /// "origins": [{
892 /// "origin": "https://example.com",
893 /// "localStorage": [{
894 /// "name": "user_prefs",
895 /// "value": "{\"theme\":\"dark\"}"
896 /// }]
897 /// }]
898 /// }
899 /// ```
900 ///
901 /// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
902 pub fn storage_state_path(mut self, path: String) -> Self {
903 self.storage_state_path = Some(path);
904 self.storage_state = None; // Clear inline if setting path
905 self
906 }
907
908 /// Sets additional arguments to pass to browser instance (for launch_persistent_context)
909 pub fn args(mut self, args: Vec<String>) -> Self {
910 self.args = Some(args);
911 self
912 }
913
914 /// Sets browser distribution channel (for launch_persistent_context)
915 pub fn channel(mut self, channel: String) -> Self {
916 self.channel = Some(channel);
917 self
918 }
919
920 /// Enables or disables Chromium sandboxing (for launch_persistent_context)
921 pub fn chromium_sandbox(mut self, enabled: bool) -> Self {
922 self.chromium_sandbox = Some(enabled);
923 self
924 }
925
926 /// Auto-open DevTools (for launch_persistent_context)
927 pub fn devtools(mut self, enabled: bool) -> Self {
928 self.devtools = Some(enabled);
929 self
930 }
931
932 /// Sets directory to save downloads (for launch_persistent_context)
933 pub fn downloads_path(mut self, path: String) -> Self {
934 self.downloads_path = Some(path);
935 self
936 }
937
938 /// Sets path to custom browser executable (for launch_persistent_context)
939 pub fn executable_path(mut self, path: String) -> Self {
940 self.executable_path = Some(path);
941 self
942 }
943
944 /// Sets Firefox user preferences (for launch_persistent_context, Firefox only)
945 pub fn firefox_user_prefs(mut self, prefs: HashMap<String, serde_json::Value>) -> Self {
946 self.firefox_user_prefs = Some(prefs);
947 self
948 }
949
950 /// Run in headless mode (for launch_persistent_context)
951 pub fn headless(mut self, enabled: bool) -> Self {
952 self.headless = Some(enabled);
953 self
954 }
955
956 /// Slow down operations by N milliseconds (for launch_persistent_context)
957 pub fn slow_mo(mut self, ms: f64) -> Self {
958 self.slow_mo = Some(ms);
959 self
960 }
961
962 /// Set timeout for browser launch in milliseconds (for launch_persistent_context)
963 pub fn timeout(mut self, ms: f64) -> Self {
964 self.timeout = Some(ms);
965 self
966 }
967
968 /// Set directory to save traces (for launch_persistent_context)
969 pub fn traces_dir(mut self, path: String) -> Self {
970 self.traces_dir = Some(path);
971 self
972 }
973
974 /// Builds the BrowserContextOptions
975 pub fn build(self) -> BrowserContextOptions {
976 BrowserContextOptions {
977 viewport: self.viewport,
978 no_viewport: self.no_viewport,
979 user_agent: self.user_agent,
980 locale: self.locale,
981 timezone_id: self.timezone_id,
982 geolocation: self.geolocation,
983 permissions: self.permissions,
984 color_scheme: self.color_scheme,
985 has_touch: self.has_touch,
986 is_mobile: self.is_mobile,
987 javascript_enabled: self.javascript_enabled,
988 offline: self.offline,
989 accept_downloads: self.accept_downloads,
990 bypass_csp: self.bypass_csp,
991 ignore_https_errors: self.ignore_https_errors,
992 device_scale_factor: self.device_scale_factor,
993 extra_http_headers: self.extra_http_headers,
994 base_url: self.base_url,
995 storage_state: self.storage_state,
996 storage_state_path: self.storage_state_path,
997 // Launch options
998 args: self.args,
999 channel: self.channel,
1000 chromium_sandbox: self.chromium_sandbox,
1001 devtools: self.devtools,
1002 downloads_path: self.downloads_path,
1003 executable_path: self.executable_path,
1004 firefox_user_prefs: self.firefox_user_prefs,
1005 headless: self.headless,
1006 slow_mo: self.slow_mo,
1007 timeout: self.timeout,
1008 traces_dir: self.traces_dir,
1009 }
1010 }
1011}