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::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;
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/// // Cleanup
42/// context1.close().await?;
43/// context2.close().await?;
44/// browser.close().await?;
45/// Ok(())
46/// }
47/// ```
48///
49/// See: <https://playwright.dev/docs/api/class-browsercontext>
50#[derive(Clone)]
51pub struct BrowserContext {
52 base: ChannelOwnerImpl,
53}
54
55impl BrowserContext {
56 /// Creates a new BrowserContext from protocol initialization
57 ///
58 /// This is called by the object factory when the server sends a `__create__` message
59 /// for a BrowserContext object.
60 ///
61 /// # Arguments
62 ///
63 /// * `parent` - The parent Browser object
64 /// * `type_name` - The protocol type name ("BrowserContext")
65 /// * `guid` - The unique identifier for this context
66 /// * `initializer` - The initialization data from the server
67 ///
68 /// # Errors
69 ///
70 /// Returns error if initializer is malformed
71 pub fn new(
72 parent: Arc<dyn ChannelOwner>,
73 type_name: String,
74 guid: Arc<str>,
75 initializer: Value,
76 ) -> Result<Self> {
77 let base = ChannelOwnerImpl::new(
78 ParentOrConnection::Parent(parent),
79 type_name,
80 guid,
81 initializer,
82 );
83
84 let context = Self { base };
85
86 // Enable dialog event subscription
87 // Dialog events need to be explicitly subscribed to via updateSubscription command
88 let channel = context.channel().clone();
89 tokio::spawn(async move {
90 let _ = channel
91 .send_no_result(
92 "updateSubscription",
93 serde_json::json!({
94 "event": "dialog",
95 "enabled": true
96 }),
97 )
98 .await;
99 });
100
101 Ok(context)
102 }
103
104 /// Returns the channel for sending protocol messages
105 ///
106 /// Used internally for sending RPC calls to the context.
107 fn channel(&self) -> &Channel {
108 self.base.channel()
109 }
110
111 /// Adds a script which would be evaluated in one of the following scenarios:
112 ///
113 /// - Whenever a page is created in the browser context or is navigated.
114 /// - Whenever a child frame is attached or navigated in any page in the browser context.
115 ///
116 /// The script is evaluated after the document was created but before any of its scripts
117 /// were run. This is useful to amend the JavaScript environment, e.g. to seed Math.random.
118 ///
119 /// # Arguments
120 ///
121 /// * `script` - Script to be evaluated in all pages in the browser context.
122 ///
123 /// # Errors
124 ///
125 /// Returns error if:
126 /// - Context has been closed
127 /// - Communication with browser process fails
128 ///
129 /// See: <https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script>
130 pub async fn add_init_script(&self, script: &str) -> Result<()> {
131 self.channel()
132 .send_no_result("addInitScript", serde_json::json!({ "source": script }))
133 .await
134 }
135
136 /// Creates a new page in this browser context.
137 ///
138 /// Pages are isolated tabs/windows within a context. Each page starts
139 /// at "about:blank" and can be navigated independently.
140 ///
141 /// # Errors
142 ///
143 /// Returns error if:
144 /// - Context has been closed
145 /// - Communication with browser process fails
146 ///
147 /// See: <https://playwright.dev/docs/api/class-browsercontext#browser-context-new-page>
148 pub async fn new_page(&self) -> Result<Page> {
149 // Response contains the GUID of the created Page
150 #[derive(Deserialize)]
151 struct NewPageResponse {
152 page: GuidRef,
153 }
154
155 #[derive(Deserialize)]
156 struct GuidRef {
157 #[serde(deserialize_with = "crate::server::connection::deserialize_arc_str")]
158 guid: Arc<str>,
159 }
160
161 // Send newPage RPC to server
162 let response: NewPageResponse = self
163 .channel()
164 .send("newPage", serde_json::json!({}))
165 .await?;
166
167 // Retrieve the Page object from the connection registry
168 let page_arc = self.connection().get_object(&response.page.guid).await?;
169
170 // Downcast to Page
171 let page = page_arc.as_any().downcast_ref::<Page>().ok_or_else(|| {
172 crate::error::Error::ProtocolError(format!(
173 "Expected Page object, got {}",
174 page_arc.type_name()
175 ))
176 })?;
177
178 Ok(page.clone())
179 }
180
181 /// Closes the browser context and all its pages.
182 ///
183 /// This is a graceful operation that sends a close command to the context
184 /// and waits for it to shut down properly.
185 ///
186 /// # Errors
187 ///
188 /// Returns error if:
189 /// - Context has already been closed
190 /// - Communication with browser process fails
191 ///
192 /// See: <https://playwright.dev/docs/api/class-browsercontext#browser-context-close>
193 pub async fn close(&self) -> Result<()> {
194 // Send close RPC to server
195 self.channel()
196 .send_no_result("close", serde_json::json!({}))
197 .await
198 }
199
200 /// Pauses the browser context.
201 ///
202 /// This pauses the execution of all pages in the context.
203 pub async fn pause(&self) -> Result<()> {
204 self.channel()
205 .send_no_result("pause", serde_json::Value::Null)
206 .await
207 }
208}
209
210impl ChannelOwner for BrowserContext {
211 fn guid(&self) -> &str {
212 self.base.guid()
213 }
214
215 fn type_name(&self) -> &str {
216 self.base.type_name()
217 }
218
219 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
220 self.base.parent()
221 }
222
223 fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
224 self.base.connection()
225 }
226
227 fn initializer(&self) -> &Value {
228 self.base.initializer()
229 }
230
231 fn channel(&self) -> &Channel {
232 self.base.channel()
233 }
234
235 fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
236 self.base.dispose(reason)
237 }
238
239 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
240 self.base.adopt(child)
241 }
242
243 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
244 self.base.add_child(guid, child)
245 }
246
247 fn remove_child(&self, guid: &str) {
248 self.base.remove_child(guid)
249 }
250
251 fn on_event(&self, method: &str, params: Value) {
252 match method {
253 "dialog" => {
254 // Dialog events come to BrowserContext, need to forward to the associated Page
255 // Event format: {dialog: {guid: "..."}}
256 // The Dialog protocol object has the Page as its parent
257 if let Some(dialog_guid) = params
258 .get("dialog")
259 .and_then(|v| v.get("guid"))
260 .and_then(|v| v.as_str())
261 {
262 let connection = self.connection();
263 let dialog_guid_owned = dialog_guid.to_string();
264
265 tokio::spawn(async move {
266 // Get the Dialog object
267 let dialog_arc = match connection.get_object(&dialog_guid_owned).await {
268 Ok(obj) => obj,
269 Err(_) => return,
270 };
271
272 // Downcast to Dialog
273 let dialog = match dialog_arc
274 .as_any()
275 .downcast_ref::<crate::protocol::Dialog>()
276 {
277 Some(d) => d.clone(),
278 None => return,
279 };
280
281 // Get the Page from the Dialog's parent
282 let page_arc = match dialog_arc.parent() {
283 Some(parent) => parent,
284 None => return,
285 };
286
287 // Downcast to Page
288 let page = match page_arc.as_any().downcast_ref::<Page>() {
289 Some(p) => p.clone(),
290 None => return,
291 };
292
293 // Forward to Page's dialog handlers
294 page.trigger_dialog_event(dialog).await;
295 });
296 }
297 }
298 _ => {
299 // Other events will be handled in future phases
300 }
301 }
302 }
303
304 fn was_collected(&self) -> bool {
305 self.base.was_collected()
306 }
307
308 fn as_any(&self) -> &dyn Any {
309 self
310 }
311}
312
313impl std::fmt::Debug for BrowserContext {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 f.debug_struct("BrowserContext")
316 .field("guid", &self.guid())
317 .finish()
318 }
319}
320
321/// Viewport dimensions for browser context.
322///
323/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context>
324#[derive(Debug, Clone, Serialize, Deserialize)]
325pub struct Viewport {
326 /// Page width in pixels
327 pub width: u32,
328 /// Page height in pixels
329 pub height: u32,
330}
331
332/// Geolocation coordinates.
333///
334/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context>
335#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct Geolocation {
337 /// Latitude between -90 and 90
338 pub latitude: f64,
339 /// Longitude between -180 and 180
340 pub longitude: f64,
341 /// Optional accuracy in meters (default: 0)
342 #[serde(skip_serializing_if = "Option::is_none")]
343 pub accuracy: Option<f64>,
344}
345
346/// Cookie information for storage state.
347///
348/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
349#[derive(Debug, Clone, Serialize, Deserialize)]
350#[serde(rename_all = "camelCase")]
351pub struct Cookie {
352 /// Cookie name
353 pub name: String,
354 /// Cookie value
355 pub value: String,
356 /// Cookie domain (use dot prefix for subdomain matching, e.g., ".example.com")
357 pub domain: String,
358 /// Cookie path
359 pub path: String,
360 /// Unix timestamp in seconds; -1 for session cookies
361 pub expires: f64,
362 /// HTTP-only flag
363 pub http_only: bool,
364 /// Secure flag
365 pub secure: bool,
366 /// SameSite attribute ("Strict", "Lax", "None")
367 #[serde(skip_serializing_if = "Option::is_none")]
368 pub same_site: Option<String>,
369}
370
371/// Local storage item for storage state.
372///
373/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
374#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct LocalStorageItem {
376 /// Storage key
377 pub name: String,
378 /// Storage value
379 pub value: String,
380}
381
382/// Origin with local storage items for storage state.
383///
384/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
385#[derive(Debug, Clone, Serialize, Deserialize)]
386#[serde(rename_all = "camelCase")]
387pub struct Origin {
388 /// Origin URL (e.g., "https://example.com")
389 pub origin: String,
390 /// Local storage items for this origin
391 pub local_storage: Vec<LocalStorageItem>,
392}
393
394/// Storage state containing cookies and local storage.
395///
396/// Used to populate a browser context with saved authentication state,
397/// enabling session persistence across context instances.
398///
399/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
400#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct StorageState {
402 /// List of cookies
403 pub cookies: Vec<Cookie>,
404 /// List of origins with local storage
405 pub origins: Vec<Origin>,
406}
407
408/// Options for creating a new browser context.
409///
410/// Allows customizing viewport, user agent, locale, timezone, geolocation,
411/// permissions, and other browser context settings.
412///
413/// See: <https://playwright.dev/docs/api/class-browser#browser-new-context>
414#[derive(Debug, Clone, Default, Serialize)]
415#[serde(rename_all = "camelCase")]
416pub struct BrowserContextOptions {
417 /// Sets consistent viewport for all pages in the context.
418 /// Set to null via `no_viewport(true)` to disable viewport emulation.
419 #[serde(skip_serializing_if = "Option::is_none")]
420 pub viewport: Option<Viewport>,
421
422 /// Disables viewport emulation when set to true.
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub no_viewport: Option<bool>,
425
426 /// Custom user agent string
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub user_agent: Option<String>,
429
430 /// Locale for the context (e.g., "en-GB", "de-DE", "fr-FR")
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub locale: Option<String>,
433
434 /// Timezone identifier (e.g., "America/New_York", "Europe/Berlin")
435 #[serde(skip_serializing_if = "Option::is_none")]
436 pub timezone_id: Option<String>,
437
438 /// Geolocation coordinates
439 #[serde(skip_serializing_if = "Option::is_none")]
440 pub geolocation: Option<Geolocation>,
441
442 /// List of permissions to grant (e.g., "geolocation", "notifications")
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub permissions: Option<Vec<String>>,
445
446 /// Emulates 'prefers-colors-scheme' media feature ("light", "dark", "no-preference")
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub color_scheme: Option<String>,
449
450 /// Whether the viewport supports touch events
451 #[serde(skip_serializing_if = "Option::is_none")]
452 pub has_touch: Option<bool>,
453
454 /// Whether the meta viewport tag is respected
455 #[serde(skip_serializing_if = "Option::is_none")]
456 pub is_mobile: Option<bool>,
457
458 /// Whether JavaScript is enabled in the context
459 #[serde(skip_serializing_if = "Option::is_none")]
460 pub javascript_enabled: Option<bool>,
461
462 /// Emulates network being offline
463 #[serde(skip_serializing_if = "Option::is_none")]
464 pub offline: Option<bool>,
465
466 /// Whether to automatically download attachments
467 #[serde(skip_serializing_if = "Option::is_none")]
468 pub accept_downloads: Option<bool>,
469
470 /// Whether to bypass Content-Security-Policy
471 #[serde(skip_serializing_if = "Option::is_none")]
472 pub bypass_csp: Option<bool>,
473
474 /// Whether to ignore HTTPS errors
475 #[serde(skip_serializing_if = "Option::is_none")]
476 pub ignore_https_errors: Option<bool>,
477
478 /// Device scale factor (default: 1)
479 #[serde(skip_serializing_if = "Option::is_none")]
480 pub device_scale_factor: Option<f64>,
481
482 /// Extra HTTP headers to send with every request
483 #[serde(skip_serializing_if = "Option::is_none")]
484 pub extra_http_headers: Option<HashMap<String, String>>,
485
486 /// Base URL for relative navigation
487 #[serde(skip_serializing_if = "Option::is_none")]
488 pub base_url: Option<String>,
489
490 /// Storage state to populate the context (cookies, localStorage, sessionStorage).
491 /// Can be an inline StorageState object or a file path string.
492 /// Use builder methods `storage_state()` for inline or `storage_state_path()` for file path.
493 #[serde(skip_serializing_if = "Option::is_none")]
494 pub storage_state: Option<StorageState>,
495
496 /// Storage state file path (alternative to inline storage_state).
497 /// This is handled by the builder and converted to storage_state during serialization.
498 #[serde(skip_serializing_if = "Option::is_none")]
499 pub storage_state_path: Option<String>,
500}
501
502impl BrowserContextOptions {
503 /// Creates a new builder for BrowserContextOptions
504 pub fn builder() -> BrowserContextOptionsBuilder {
505 BrowserContextOptionsBuilder::default()
506 }
507}
508
509/// Builder for BrowserContextOptions
510#[derive(Debug, Clone, Default)]
511pub struct BrowserContextOptionsBuilder {
512 viewport: Option<Viewport>,
513 no_viewport: Option<bool>,
514 user_agent: Option<String>,
515 locale: Option<String>,
516 timezone_id: Option<String>,
517 geolocation: Option<Geolocation>,
518 permissions: Option<Vec<String>>,
519 color_scheme: Option<String>,
520 has_touch: Option<bool>,
521 is_mobile: Option<bool>,
522 javascript_enabled: Option<bool>,
523 offline: Option<bool>,
524 accept_downloads: Option<bool>,
525 bypass_csp: Option<bool>,
526 ignore_https_errors: Option<bool>,
527 device_scale_factor: Option<f64>,
528 extra_http_headers: Option<HashMap<String, String>>,
529 base_url: Option<String>,
530 storage_state: Option<StorageState>,
531 storage_state_path: Option<String>,
532}
533
534impl BrowserContextOptionsBuilder {
535 /// Sets the viewport dimensions
536 pub fn viewport(mut self, viewport: Viewport) -> Self {
537 self.viewport = Some(viewport);
538 self.no_viewport = None; // Clear no_viewport if setting viewport
539 self
540 }
541
542 /// Disables viewport emulation
543 pub fn no_viewport(mut self, no_viewport: bool) -> Self {
544 self.no_viewport = Some(no_viewport);
545 if no_viewport {
546 self.viewport = None; // Clear viewport if setting no_viewport
547 }
548 self
549 }
550
551 /// Sets the user agent string
552 pub fn user_agent(mut self, user_agent: String) -> Self {
553 self.user_agent = Some(user_agent);
554 self
555 }
556
557 /// Sets the locale
558 pub fn locale(mut self, locale: String) -> Self {
559 self.locale = Some(locale);
560 self
561 }
562
563 /// Sets the timezone identifier
564 pub fn timezone_id(mut self, timezone_id: String) -> Self {
565 self.timezone_id = Some(timezone_id);
566 self
567 }
568
569 /// Sets the geolocation
570 pub fn geolocation(mut self, geolocation: Geolocation) -> Self {
571 self.geolocation = Some(geolocation);
572 self
573 }
574
575 /// Sets the permissions to grant
576 pub fn permissions(mut self, permissions: Vec<String>) -> Self {
577 self.permissions = Some(permissions);
578 self
579 }
580
581 /// Sets the color scheme preference
582 pub fn color_scheme(mut self, color_scheme: String) -> Self {
583 self.color_scheme = Some(color_scheme);
584 self
585 }
586
587 /// Sets whether the viewport supports touch events
588 pub fn has_touch(mut self, has_touch: bool) -> Self {
589 self.has_touch = Some(has_touch);
590 self
591 }
592
593 /// Sets whether this is a mobile viewport
594 pub fn is_mobile(mut self, is_mobile: bool) -> Self {
595 self.is_mobile = Some(is_mobile);
596 self
597 }
598
599 /// Sets whether JavaScript is enabled
600 pub fn javascript_enabled(mut self, javascript_enabled: bool) -> Self {
601 self.javascript_enabled = Some(javascript_enabled);
602 self
603 }
604
605 /// Sets whether to emulate offline network
606 pub fn offline(mut self, offline: bool) -> Self {
607 self.offline = Some(offline);
608 self
609 }
610
611 /// Sets whether to automatically download attachments
612 pub fn accept_downloads(mut self, accept_downloads: bool) -> Self {
613 self.accept_downloads = Some(accept_downloads);
614 self
615 }
616
617 /// Sets whether to bypass Content-Security-Policy
618 pub fn bypass_csp(mut self, bypass_csp: bool) -> Self {
619 self.bypass_csp = Some(bypass_csp);
620 self
621 }
622
623 /// Sets whether to ignore HTTPS errors
624 pub fn ignore_https_errors(mut self, ignore_https_errors: bool) -> Self {
625 self.ignore_https_errors = Some(ignore_https_errors);
626 self
627 }
628
629 /// Sets the device scale factor
630 pub fn device_scale_factor(mut self, device_scale_factor: f64) -> Self {
631 self.device_scale_factor = Some(device_scale_factor);
632 self
633 }
634
635 /// Sets extra HTTP headers
636 pub fn extra_http_headers(mut self, extra_http_headers: HashMap<String, String>) -> Self {
637 self.extra_http_headers = Some(extra_http_headers);
638 self
639 }
640
641 /// Sets the base URL for relative navigation
642 pub fn base_url(mut self, base_url: String) -> Self {
643 self.base_url = Some(base_url);
644 self
645 }
646
647 /// Sets the storage state inline (cookies, localStorage).
648 ///
649 /// Populates the browser context with the provided storage state, including
650 /// cookies and local storage. This is useful for initializing a context with
651 /// a saved authentication state.
652 ///
653 /// Mutually exclusive with `storage_state_path()`.
654 ///
655 /// # Example
656 ///
657 /// ```rust
658 /// use playwright_rs::protocol::{BrowserContextOptions, Cookie, StorageState, Origin, LocalStorageItem};
659 ///
660 /// let storage_state = StorageState {
661 /// cookies: vec![Cookie {
662 /// name: "session_id".to_string(),
663 /// value: "abc123".to_string(),
664 /// domain: ".example.com".to_string(),
665 /// path: "/".to_string(),
666 /// expires: -1.0,
667 /// http_only: true,
668 /// secure: true,
669 /// same_site: Some("Lax".to_string()),
670 /// }],
671 /// origins: vec![Origin {
672 /// origin: "https://example.com".to_string(),
673 /// local_storage: vec![LocalStorageItem {
674 /// name: "user_prefs".to_string(),
675 /// value: "{\"theme\":\"dark\"}".to_string(),
676 /// }],
677 /// }],
678 /// };
679 ///
680 /// let options = BrowserContextOptions::builder()
681 /// .storage_state(storage_state)
682 /// .build();
683 /// ```
684 ///
685 /// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
686 pub fn storage_state(mut self, storage_state: StorageState) -> Self {
687 self.storage_state = Some(storage_state);
688 self.storage_state_path = None; // Clear path if setting inline
689 self
690 }
691
692 /// Sets the storage state from a file path.
693 ///
694 /// The file should contain a JSON representation of StorageState with cookies
695 /// and origins. This is useful for loading authentication state saved from a
696 /// previous session.
697 ///
698 /// Mutually exclusive with `storage_state()`.
699 ///
700 /// # Example
701 ///
702 /// ```rust
703 /// use playwright_rs::protocol::BrowserContextOptions;
704 ///
705 /// let options = BrowserContextOptions::builder()
706 /// .storage_state_path("auth.json".to_string())
707 /// .build();
708 /// ```
709 ///
710 /// The file should have this format:
711 /// ```json
712 /// {
713 /// "cookies": [{
714 /// "name": "session_id",
715 /// "value": "abc123",
716 /// "domain": ".example.com",
717 /// "path": "/",
718 /// "expires": -1,
719 /// "httpOnly": true,
720 /// "secure": true,
721 /// "sameSite": "Lax"
722 /// }],
723 /// "origins": [{
724 /// "origin": "https://example.com",
725 /// "localStorage": [{
726 /// "name": "user_prefs",
727 /// "value": "{\"theme\":\"dark\"}"
728 /// }]
729 /// }]
730 /// }
731 /// ```
732 ///
733 /// See: <https://playwright.dev/docs/api/class-browser#browser-new-context-option-storage-state>
734 pub fn storage_state_path(mut self, path: String) -> Self {
735 self.storage_state_path = Some(path);
736 self.storage_state = None; // Clear inline if setting path
737 self
738 }
739
740 /// Builds the BrowserContextOptions
741 pub fn build(self) -> BrowserContextOptions {
742 BrowserContextOptions {
743 viewport: self.viewport,
744 no_viewport: self.no_viewport,
745 user_agent: self.user_agent,
746 locale: self.locale,
747 timezone_id: self.timezone_id,
748 geolocation: self.geolocation,
749 permissions: self.permissions,
750 color_scheme: self.color_scheme,
751 has_touch: self.has_touch,
752 is_mobile: self.is_mobile,
753 javascript_enabled: self.javascript_enabled,
754 offline: self.offline,
755 accept_downloads: self.accept_downloads,
756 bypass_csp: self.bypass_csp,
757 ignore_https_errors: self.ignore_https_errors,
758 device_scale_factor: self.device_scale_factor,
759 extra_http_headers: self.extra_http_headers,
760 base_url: self.base_url,
761 storage_state: self.storage_state,
762 storage_state_path: self.storage_state_path,
763 }
764 }
765}