viewpoint_core/browser/
context_builder.rs

1//! Browser context builder for creating contexts with custom configuration.
2
3use std::time::Duration;
4
5use crate::BrowserContext;
6use crate::context::{
7    ColorScheme, ContextOptionsBuilder, ForcedColors, Permission, ProxyConfig, ReducedMotion,
8    StorageState,
9};
10use crate::devices::DeviceDescriptor;
11use crate::error::BrowserError;
12use crate::page::VideoOptions;
13
14use super::Browser;
15
16/// Builder for creating a new browser context with options.
17#[derive(Debug)]
18pub struct NewContextBuilder<'a> {
19    browser: &'a Browser,
20    builder: ContextOptionsBuilder,
21}
22
23impl<'a> NewContextBuilder<'a> {
24    pub(super) fn new(browser: &'a Browser) -> Self {
25        Self {
26            browser,
27            builder: ContextOptionsBuilder::new(),
28        }
29    }
30
31    /// Set storage state from a file path.
32    #[must_use]
33    pub fn storage_state_path(mut self, path: impl Into<std::path::PathBuf>) -> Self {
34        self.builder = self.builder.storage_state_path(path);
35        self
36    }
37
38    /// Set storage state from an object.
39    #[must_use]
40    pub fn storage_state(mut self, state: StorageState) -> Self {
41        self.builder = self.builder.storage_state(state);
42        self
43    }
44
45    /// Set geolocation.
46    #[must_use]
47    pub fn geolocation(mut self, latitude: f64, longitude: f64) -> Self {
48        self.builder = self.builder.geolocation(latitude, longitude);
49        self
50    }
51
52    /// Set geolocation with accuracy.
53    #[must_use]
54    pub fn geolocation_with_accuracy(
55        mut self,
56        latitude: f64,
57        longitude: f64,
58        accuracy: f64,
59    ) -> Self {
60        self.builder = self
61            .builder
62            .geolocation_with_accuracy(latitude, longitude, accuracy);
63        self
64    }
65
66    /// Grant permissions.
67    #[must_use]
68    pub fn permissions(mut self, permissions: Vec<Permission>) -> Self {
69        self.builder = self.builder.permissions(permissions);
70        self
71    }
72
73    /// Set HTTP credentials.
74    #[must_use]
75    pub fn http_credentials(
76        mut self,
77        username: impl Into<String>,
78        password: impl Into<String>,
79    ) -> Self {
80        self.builder = self.builder.http_credentials(username, password);
81        self
82    }
83
84    /// Set extra HTTP headers.
85    #[must_use]
86    pub fn extra_http_headers(
87        mut self,
88        headers: std::collections::HashMap<String, String>,
89    ) -> Self {
90        self.builder = self.builder.extra_http_headers(headers);
91        self
92    }
93
94    /// Add an extra HTTP header.
95    #[must_use]
96    pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
97        self.builder = self.builder.header(name, value);
98        self
99    }
100
101    /// Set offline mode.
102    #[must_use]
103    pub fn offline(mut self, offline: bool) -> Self {
104        self.builder = self.builder.offline(offline);
105        self
106    }
107
108    /// Set default timeout.
109    #[must_use]
110    pub fn default_timeout(mut self, timeout: Duration) -> Self {
111        self.builder = self.builder.default_timeout(timeout);
112        self
113    }
114
115    /// Set default navigation timeout.
116    #[must_use]
117    pub fn default_navigation_timeout(mut self, timeout: Duration) -> Self {
118        self.builder = self.builder.default_navigation_timeout(timeout);
119        self
120    }
121
122    /// Enable touch emulation.
123    #[must_use]
124    pub fn has_touch(mut self, has_touch: bool) -> Self {
125        self.builder = self.builder.has_touch(has_touch);
126        self
127    }
128
129    /// Set locale.
130    #[must_use]
131    pub fn locale(mut self, locale: impl Into<String>) -> Self {
132        self.builder = self.builder.locale(locale);
133        self
134    }
135
136    /// Set timezone.
137    #[must_use]
138    pub fn timezone_id(mut self, timezone_id: impl Into<String>) -> Self {
139        self.builder = self.builder.timezone_id(timezone_id);
140        self
141    }
142
143    /// Set user agent.
144    #[must_use]
145    pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
146        self.builder = self.builder.user_agent(user_agent);
147        self
148    }
149
150    /// Set viewport size.
151    #[must_use]
152    pub fn viewport(mut self, width: i32, height: i32) -> Self {
153        self.builder = self.builder.viewport(width, height);
154        self
155    }
156
157    /// Set color scheme.
158    #[must_use]
159    pub fn color_scheme(mut self, color_scheme: ColorScheme) -> Self {
160        self.builder = self.builder.color_scheme(color_scheme);
161        self
162    }
163
164    /// Set reduced motion preference.
165    #[must_use]
166    pub fn reduced_motion(mut self, reduced_motion: ReducedMotion) -> Self {
167        self.builder = self.builder.reduced_motion(reduced_motion);
168        self
169    }
170
171    /// Set forced colors preference.
172    #[must_use]
173    pub fn forced_colors(mut self, forced_colors: ForcedColors) -> Self {
174        self.builder = self.builder.forced_colors(forced_colors);
175        self
176    }
177
178    /// Set device scale factor (device pixel ratio).
179    #[must_use]
180    pub fn device_scale_factor(mut self, scale_factor: f64) -> Self {
181        self.builder = self.builder.device_scale_factor(scale_factor);
182        self
183    }
184
185    /// Set mobile mode.
186    #[must_use]
187    pub fn is_mobile(mut self, is_mobile: bool) -> Self {
188        self.builder = self.builder.is_mobile(is_mobile);
189        self
190    }
191
192    /// Apply a device descriptor to configure the context.
193    ///
194    /// This sets viewport, user agent, device scale factor, touch, and mobile mode
195    /// based on the device descriptor.
196    ///
197    /// # Example
198    ///
199    /// ```no_run
200    /// use viewpoint_core::{Browser, devices};
201    ///
202    /// # async fn example() -> Result<(), viewpoint_core::CoreError> {
203    /// let browser = Browser::launch().headless(true).launch().await?;
204    ///
205    /// let context = browser.new_context_builder()
206    ///     .device(devices::IPHONE_13)
207    ///     .build()
208    ///     .await?;
209    /// # Ok(())
210    /// # }
211    /// ```
212    #[must_use]
213    pub fn device(mut self, device: DeviceDescriptor) -> Self {
214        self.builder = self.builder.device(device);
215        self
216    }
217
218    /// Enable video recording for pages in this context.
219    ///
220    /// Videos are recorded for each page and saved to the specified directory.
221    ///
222    /// # Example
223    ///
224    /// ```no_run
225    /// use viewpoint_core::{Browser, page::VideoOptions};
226    ///
227    /// # async fn example() -> Result<(), viewpoint_core::CoreError> {
228    /// let browser = Browser::launch().headless(true).launch().await?;
229    /// let context = browser.new_context_builder()
230    ///     .record_video(VideoOptions::new("./videos"))
231    ///     .build()
232    ///     .await?;
233    /// # Ok(())
234    /// # }
235    /// ```
236    #[must_use]
237    pub fn record_video(mut self, options: VideoOptions) -> Self {
238        self.builder = self.builder.record_video(options);
239        self
240    }
241
242    /// Set proxy configuration.
243    ///
244    /// Configure a proxy server for all network requests in this context.
245    /// Supports HTTP, HTTPS, and SOCKS5 proxies with optional authentication.
246    ///
247    /// # Example
248    ///
249    /// ```no_run
250    /// use viewpoint_core::{Browser, context::ProxyConfig};
251    ///
252    /// # async fn example() -> Result<(), viewpoint_core::CoreError> {
253    /// let browser = Browser::launch().headless(true).launch().await?;
254    ///
255    /// // Simple proxy without authentication
256    /// let context = browser.new_context_builder()
257    ///     .proxy(ProxyConfig::new("http://proxy.example.com:8080"))
258    ///     .build()
259    ///     .await?;
260    ///
261    /// // SOCKS5 proxy with authentication
262    /// let context = browser.new_context_builder()
263    ///     .proxy(ProxyConfig::new("socks5://proxy.example.com:1080")
264    ///         .credentials("user", "password"))
265    ///     .build()
266    ///     .await?;
267    /// # Ok(())
268    /// # }
269    /// ```
270    #[must_use]
271    pub fn proxy(mut self, proxy: ProxyConfig) -> Self {
272        self.builder = self.builder.proxy(proxy);
273        self
274    }
275
276    /// Build and create the browser context.
277    ///
278    /// # Errors
279    ///
280    /// Returns an error if context creation fails.
281    pub async fn build(self) -> Result<BrowserContext, BrowserError> {
282        self.browser
283            .new_context_with_options(self.builder.build())
284            .await
285    }
286}