playwright_core/protocol/
browser_context.rs1use crate::channel::Channel;
8use crate::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
9use crate::error::Result;
10use crate::protocol::Page;
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use std::any::Any;
14use std::collections::HashMap;
15use std::sync::Arc;
16
17#[derive(Clone)]
51pub struct BrowserContext {
52 base: ChannelOwnerImpl,
53}
54
55impl BrowserContext {
56 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 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 fn channel(&self) -> &Channel {
108 self.base.channel()
109 }
110
111 pub async fn new_page(&self) -> Result<Page> {
124 #[derive(Deserialize)]
126 struct NewPageResponse {
127 page: GuidRef,
128 }
129
130 #[derive(Deserialize)]
131 struct GuidRef {
132 #[serde(deserialize_with = "crate::connection::deserialize_arc_str")]
133 guid: Arc<str>,
134 }
135
136 let response: NewPageResponse = self
138 .channel()
139 .send("newPage", serde_json::json!({}))
140 .await?;
141
142 let page_arc = self.connection().get_object(&response.page.guid).await?;
144
145 let page = page_arc.as_any().downcast_ref::<Page>().ok_or_else(|| {
147 crate::error::Error::ProtocolError(format!(
148 "Expected Page object, got {}",
149 page_arc.type_name()
150 ))
151 })?;
152
153 Ok(page.clone())
154 }
155
156 pub async fn close(&self) -> Result<()> {
169 self.channel()
171 .send_no_result("close", serde_json::json!({}))
172 .await
173 }
174}
175
176impl ChannelOwner for BrowserContext {
177 fn guid(&self) -> &str {
178 self.base.guid()
179 }
180
181 fn type_name(&self) -> &str {
182 self.base.type_name()
183 }
184
185 fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
186 self.base.parent()
187 }
188
189 fn connection(&self) -> Arc<dyn crate::connection::ConnectionLike> {
190 self.base.connection()
191 }
192
193 fn initializer(&self) -> &Value {
194 self.base.initializer()
195 }
196
197 fn channel(&self) -> &Channel {
198 self.base.channel()
199 }
200
201 fn dispose(&self, reason: crate::channel_owner::DisposeReason) {
202 self.base.dispose(reason)
203 }
204
205 fn adopt(&self, child: Arc<dyn ChannelOwner>) {
206 self.base.adopt(child)
207 }
208
209 fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
210 self.base.add_child(guid, child)
211 }
212
213 fn remove_child(&self, guid: &str) {
214 self.base.remove_child(guid)
215 }
216
217 fn on_event(&self, method: &str, params: Value) {
218 match method {
219 "dialog" => {
220 if let Some(dialog_guid) = params
224 .get("dialog")
225 .and_then(|v| v.get("guid"))
226 .and_then(|v| v.as_str())
227 {
228 let connection = self.connection();
229 let dialog_guid_owned = dialog_guid.to_string();
230
231 tokio::spawn(async move {
232 let dialog_arc = match connection.get_object(&dialog_guid_owned).await {
234 Ok(obj) => obj,
235 Err(_) => return,
236 };
237
238 let dialog = match dialog_arc
240 .as_any()
241 .downcast_ref::<crate::protocol::Dialog>()
242 {
243 Some(d) => d.clone(),
244 None => return,
245 };
246
247 let page_arc = match dialog_arc.parent() {
249 Some(parent) => parent,
250 None => return,
251 };
252
253 let page = match page_arc.as_any().downcast_ref::<Page>() {
255 Some(p) => p.clone(),
256 None => return,
257 };
258
259 page.trigger_dialog_event(dialog).await;
261 });
262 }
263 }
264 _ => {
265 }
267 }
268 }
269
270 fn was_collected(&self) -> bool {
271 self.base.was_collected()
272 }
273
274 fn as_any(&self) -> &dyn Any {
275 self
276 }
277}
278
279impl std::fmt::Debug for BrowserContext {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 f.debug_struct("BrowserContext")
282 .field("guid", &self.guid())
283 .finish()
284 }
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct Viewport {
292 pub width: u32,
294 pub height: u32,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct Geolocation {
303 pub latitude: f64,
305 pub longitude: f64,
307 #[serde(skip_serializing_if = "Option::is_none")]
309 pub accuracy: Option<f64>,
310}
311
312#[derive(Debug, Clone, Default, Serialize)]
319#[serde(rename_all = "camelCase")]
320pub struct BrowserContextOptions {
321 #[serde(skip_serializing_if = "Option::is_none")]
324 pub viewport: Option<Viewport>,
325
326 #[serde(skip_serializing_if = "Option::is_none")]
328 pub no_viewport: Option<bool>,
329
330 #[serde(skip_serializing_if = "Option::is_none")]
332 pub user_agent: Option<String>,
333
334 #[serde(skip_serializing_if = "Option::is_none")]
336 pub locale: Option<String>,
337
338 #[serde(skip_serializing_if = "Option::is_none")]
340 pub timezone_id: Option<String>,
341
342 #[serde(skip_serializing_if = "Option::is_none")]
344 pub geolocation: Option<Geolocation>,
345
346 #[serde(skip_serializing_if = "Option::is_none")]
348 pub permissions: Option<Vec<String>>,
349
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub color_scheme: Option<String>,
353
354 #[serde(skip_serializing_if = "Option::is_none")]
356 pub has_touch: Option<bool>,
357
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub is_mobile: Option<bool>,
361
362 #[serde(skip_serializing_if = "Option::is_none")]
364 pub javascript_enabled: Option<bool>,
365
366 #[serde(skip_serializing_if = "Option::is_none")]
368 pub offline: Option<bool>,
369
370 #[serde(skip_serializing_if = "Option::is_none")]
372 pub accept_downloads: Option<bool>,
373
374 #[serde(skip_serializing_if = "Option::is_none")]
376 pub bypass_csp: Option<bool>,
377
378 #[serde(skip_serializing_if = "Option::is_none")]
380 pub ignore_https_errors: Option<bool>,
381
382 #[serde(skip_serializing_if = "Option::is_none")]
384 pub device_scale_factor: Option<f64>,
385
386 #[serde(skip_serializing_if = "Option::is_none")]
388 pub extra_http_headers: Option<HashMap<String, String>>,
389
390 #[serde(skip_serializing_if = "Option::is_none")]
392 pub base_url: Option<String>,
393}
394
395impl BrowserContextOptions {
396 pub fn builder() -> BrowserContextOptionsBuilder {
398 BrowserContextOptionsBuilder::default()
399 }
400}
401
402#[derive(Debug, Clone, Default)]
404pub struct BrowserContextOptionsBuilder {
405 viewport: Option<Viewport>,
406 no_viewport: Option<bool>,
407 user_agent: Option<String>,
408 locale: Option<String>,
409 timezone_id: Option<String>,
410 geolocation: Option<Geolocation>,
411 permissions: Option<Vec<String>>,
412 color_scheme: Option<String>,
413 has_touch: Option<bool>,
414 is_mobile: Option<bool>,
415 javascript_enabled: Option<bool>,
416 offline: Option<bool>,
417 accept_downloads: Option<bool>,
418 bypass_csp: Option<bool>,
419 ignore_https_errors: Option<bool>,
420 device_scale_factor: Option<f64>,
421 extra_http_headers: Option<HashMap<String, String>>,
422 base_url: Option<String>,
423}
424
425impl BrowserContextOptionsBuilder {
426 pub fn viewport(mut self, viewport: Viewport) -> Self {
428 self.viewport = Some(viewport);
429 self.no_viewport = None; self
431 }
432
433 pub fn no_viewport(mut self, no_viewport: bool) -> Self {
435 self.no_viewport = Some(no_viewport);
436 if no_viewport {
437 self.viewport = None; }
439 self
440 }
441
442 pub fn user_agent(mut self, user_agent: String) -> Self {
444 self.user_agent = Some(user_agent);
445 self
446 }
447
448 pub fn locale(mut self, locale: String) -> Self {
450 self.locale = Some(locale);
451 self
452 }
453
454 pub fn timezone_id(mut self, timezone_id: String) -> Self {
456 self.timezone_id = Some(timezone_id);
457 self
458 }
459
460 pub fn geolocation(mut self, geolocation: Geolocation) -> Self {
462 self.geolocation = Some(geolocation);
463 self
464 }
465
466 pub fn permissions(mut self, permissions: Vec<String>) -> Self {
468 self.permissions = Some(permissions);
469 self
470 }
471
472 pub fn color_scheme(mut self, color_scheme: String) -> Self {
474 self.color_scheme = Some(color_scheme);
475 self
476 }
477
478 pub fn has_touch(mut self, has_touch: bool) -> Self {
480 self.has_touch = Some(has_touch);
481 self
482 }
483
484 pub fn is_mobile(mut self, is_mobile: bool) -> Self {
486 self.is_mobile = Some(is_mobile);
487 self
488 }
489
490 pub fn javascript_enabled(mut self, javascript_enabled: bool) -> Self {
492 self.javascript_enabled = Some(javascript_enabled);
493 self
494 }
495
496 pub fn offline(mut self, offline: bool) -> Self {
498 self.offline = Some(offline);
499 self
500 }
501
502 pub fn accept_downloads(mut self, accept_downloads: bool) -> Self {
504 self.accept_downloads = Some(accept_downloads);
505 self
506 }
507
508 pub fn bypass_csp(mut self, bypass_csp: bool) -> Self {
510 self.bypass_csp = Some(bypass_csp);
511 self
512 }
513
514 pub fn ignore_https_errors(mut self, ignore_https_errors: bool) -> Self {
516 self.ignore_https_errors = Some(ignore_https_errors);
517 self
518 }
519
520 pub fn device_scale_factor(mut self, device_scale_factor: f64) -> Self {
522 self.device_scale_factor = Some(device_scale_factor);
523 self
524 }
525
526 pub fn extra_http_headers(mut self, extra_http_headers: HashMap<String, String>) -> Self {
528 self.extra_http_headers = Some(extra_http_headers);
529 self
530 }
531
532 pub fn base_url(mut self, base_url: String) -> Self {
534 self.base_url = Some(base_url);
535 self
536 }
537
538 pub fn build(self) -> BrowserContextOptions {
540 BrowserContextOptions {
541 viewport: self.viewport,
542 no_viewport: self.no_viewport,
543 user_agent: self.user_agent,
544 locale: self.locale,
545 timezone_id: self.timezone_id,
546 geolocation: self.geolocation,
547 permissions: self.permissions,
548 color_scheme: self.color_scheme,
549 has_touch: self.has_touch,
550 is_mobile: self.is_mobile,
551 javascript_enabled: self.javascript_enabled,
552 offline: self.offline,
553 accept_downloads: self.accept_downloads,
554 bypass_csp: self.bypass_csp,
555 ignore_https_errors: self.ignore_https_errors,
556 device_scale_factor: self.device_scale_factor,
557 extra_http_headers: self.extra_http_headers,
558 base_url: self.base_url,
559 }
560 }
561}