1use crate::browser::Browser;
5use crate::api_request::APIRequestContext;
6use crate::cdp::session::CdpSession;
7use crate::download::Download;
8use crate::error::{Error, Result};
9use crate::options::NewContextOptions;
10use crate::page::Page;
11use crate::request::Request;
12use crate::response::Response;
13use crate::route::RouteEntry;
14use crate::route::{Route, RouteFulfillOptions, RouteHandler};
15use crate::types::{ConsoleMessage, Headers, Position};
16use crate::clock::{Clock, ClockInstallOptions};
17use crate::har::{routes_from_har, HarRoute, RouteFromHarOptions};
18use parking_lot::Mutex;
19use serde_json::{json, Value};
20use std::future::Future;
21use std::pin::Pin;
22use std::sync::atomic::{AtomicU64, Ordering};
23use std::sync::Arc;
24
25type PageHandler = Arc<
26 dyn Fn(Page) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync,
27>;
28
29type RequestHandler =
31 Arc<dyn Fn(Request) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
32
33type ResponseHandler =
35 Arc<dyn Fn(Response) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
36
37type ConsoleHandler =
39 Arc<dyn Fn(ConsoleMessage) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
40
41type DownloadHandler =
43 Arc<dyn Fn(Download) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
44
45type CloseHandler =
47 Arc<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
48
49type ExposedBindingHandler =
51 Arc<dyn Fn(Vec<Value>) -> Pin<Box<dyn Future<Output = Value> + Send>> + Send + Sync>;
52
53#[derive(Clone)]
56struct ExposedFunction {
57 name: String,
58 handler: ExposedBindingHandler,
59}
60
61#[derive(Clone)]
64pub struct BrowserContext {
65 inner: Arc<ContextInner>,
66}
67
68struct ContextInner {
69 browser: Browser,
70 browser_context_id: Option<String>,
71 init_scripts: Mutex<Vec<String>>,
72 default_timeout_ms: AtomicU64,
73 default_navigation_timeout_ms: AtomicU64,
76 extra_http_headers: Mutex<Option<Headers>>,
77 user_agent: Mutex<Option<String>>,
78 viewport: Mutex<Option<crate::types::Viewport>>,
79 geolocation: Mutex<Option<Position>>,
81 offline: Mutex<bool>,
83 pages: Mutex<Vec<Page>>,
84 on_page_handlers: Mutex<Vec<PageHandler>>,
85 context_routes: Mutex<Vec<RouteEntry>>,
88 exposed_functions: Mutex<Vec<ExposedFunction>>,
90 utility_page: Mutex<Option<Page>>,
91 tracing: crate::Tracing,
94 pending_clock_install: Mutex<Option<ClockInstallOptions>>,
99 closed: Mutex<bool>,
100
101 on_close_handlers: Mutex<Vec<CloseHandler>>,
103 on_console_handlers: Mutex<Vec<ConsoleHandler>>,
104 on_request_handlers: Mutex<Vec<RequestHandler>>,
105 on_response_handlers: Mutex<Vec<ResponseHandler>>,
106 on_requestfailed_handlers: Mutex<Vec<RequestHandler>>,
107 on_download_handlers: Mutex<Vec<DownloadHandler>>,
108}
109
110fn page_session_owned(page: &Page) -> CdpSession {
117 let session = page.session();
118 CdpSession::target(
119 Arc::clone(session.connection()),
120 session.session_id().unwrap_or(""),
121 )
122}
123
124impl BrowserContext {
125 pub(crate) async fn create(browser: Browser, opts: Option<NewContextOptions>) -> Result<Self> {
127 let v = browser
128 .browser_session()
129 .send(
130 "Target.createBrowserContext",
131 json!({"disposeOnDetach": true}),
132 )
133 .await?;
134 let bcid = v
135 .get("browserContextId")
136 .and_then(|x| x.as_str())
137 .ok_or_else(|| Error::ProtocolError("createBrowserContext missing id".into()))?
138 .to_string();
139
140 let ctx = Self::build(browser.clone(), Some(bcid), opts);
141 browser.track_context(ctx.clone());
142 Ok(ctx)
143 }
144
145 pub(crate) fn default_for(browser: Browser) -> Self {
147 Self::build(browser, None, None)
148 }
149
150 fn build(browser: Browser, bcid: Option<String>, opts: Option<NewContextOptions>) -> Self {
151 let opts = opts.unwrap_or_default();
152 let tracing = crate::Tracing::new(browser.new_browser_cdp_session());
153 Self {
154 inner: Arc::new(ContextInner {
155 browser,
156 browser_context_id: bcid,
157 init_scripts: Mutex::new(Vec::new()),
158 default_timeout_ms: AtomicU64::new(30_000),
159 default_navigation_timeout_ms: AtomicU64::new(30_000),
160 extra_http_headers: Mutex::new(opts.extra_http_headers),
161 user_agent: Mutex::new(opts.user_agent),
162 viewport: Mutex::new(opts.viewport),
163 geolocation: Mutex::new(None),
164 offline: Mutex::new(false),
165 pages: Mutex::new(Vec::new()),
166 on_page_handlers: Mutex::new(Vec::new()),
167 context_routes: Mutex::new(Vec::new()),
168 exposed_functions: Mutex::new(Vec::new()),
169 utility_page: Mutex::new(None),
170 tracing,
171 pending_clock_install: Mutex::new(None),
172 closed: Mutex::new(false),
173 on_close_handlers: Mutex::new(Vec::new()),
174 on_console_handlers: Mutex::new(Vec::new()),
175 on_request_handlers: Mutex::new(Vec::new()),
176 on_response_handlers: Mutex::new(Vec::new()),
177 on_requestfailed_handlers: Mutex::new(Vec::new()),
178 on_download_handlers: Mutex::new(Vec::new()),
179 }),
180 }
181 }
182
183 pub fn browser_context_id(&self) -> Option<&str> {
185 self.inner.browser_context_id.as_deref()
186 }
187
188 pub async fn new_page(&self) -> Result<Page> {
190 if *self.inner.closed.lock() {
191 return Err(Error::TargetClosed {
192 target_type: "context".into(),
193 context: "context already closed".into(),
194 });
195 }
196
197 let mut params = json!({"url": "about:blank"});
198 if let Some(id) = &self.inner.browser_context_id {
199 params["browserContextId"] = json!(id);
200 }
201 let resp = self
202 .inner
203 .browser
204 .browser_session()
205 .send("Target.createTarget", params)
206 .await?;
207 let target_id = resp
208 .get("targetId")
209 .and_then(|x| x.as_str())
210 .ok_or_else(|| Error::ProtocolError("createTarget missing targetId".into()))?
211 .to_string();
212
213 let attach = self
214 .inner
215 .browser
216 .browser_session()
217 .send(
218 "Target.attachToTarget",
219 json!({"targetId": target_id, "flatten": true}),
220 )
221 .await?;
222 let session_id = attach
223 .get("sessionId")
224 .and_then(|x| x.as_str())
225 .ok_or_else(|| Error::ProtocolError("attachToTarget missing sessionId".into()))?
226 .to_string();
227
228 let init_scripts = self.inner.init_scripts.lock().clone();
229 let headers = self.inner.extra_http_headers.lock().clone();
230 let ua = self.inner.user_agent.lock().clone();
231 let viewport = self.inner.viewport.lock().as_ref().copied();
232 let timeout_ms = self.inner.default_timeout_ms.load(Ordering::Relaxed);
233
234 let page = Page::attach(
235 self.inner.browser.clone(),
236 session_id,
237 target_id,
238 &init_scripts,
239 timeout_ms,
240 headers.as_ref(),
241 ua.as_deref(),
242 viewport,
243 )
244 .await?;
245
246 self.inner.pages.lock().push(page.clone());
247
248 let handlers = self.inner.on_page_handlers.lock().clone();
250 for h in handlers {
251 let p = page.clone();
252 tokio::spawn(async move {
253 (h)(p).await;
254 });
255 }
256
257 let routes = self.inner.context_routes.lock().clone();
260 for entry in routes {
261 let handler = Arc::clone(&entry.handler);
262 page.route(&entry.pattern, move |r| {
263 let h = Arc::clone(&handler);
264 async move {
265 (h)(r).await;
266 }
267 })
268 .await?;
269 }
270
271 page.set_default_navigation_timeout(
273 self.inner
274 .default_navigation_timeout_ms
275 .load(Ordering::Relaxed),
276 );
277 if let Some(clock_opts) = self.inner.pending_clock_install.lock().clone() {
281 let _ = Clock::new(page_session_owned(&page))
284 .install(Some(clock_opts))
285 .await;
286 }
287 if let Some(geo) = self.inner.geolocation.lock().as_ref().copied() {
288 page.set_geolocation(Some((geo.x, geo.y))).await?;
289 }
290 if *self.inner.offline.lock() {
291 page.set_offline(true).await?;
292 }
293
294 let exposed = self.inner.exposed_functions.lock().clone();
296 for f in exposed {
297 let handler = Arc::clone(&f.handler);
298 page.expose_function(&f.name, move |args| {
299 let h = Arc::clone(&handler);
300 async move { (h)(args).await }
301 })
302 .await?;
303 }
304
305 self.register_page_event_fanout(&page);
308
309 Ok(page)
310 }
311
312 fn register_page_event_fanout(&self, page: &Page) {
317 {
319 let handlers = self.inner.on_close_handlers.lock().clone();
320 if !handlers.is_empty() {
321 page.on_close(move || {
322 let hs = handlers.clone();
323 async move {
324 for h in hs {
325 (h)().await;
326 }
327 }
328 });
329 }
330 }
331 {
333 let handlers = self.inner.on_console_handlers.lock().clone();
334 if !handlers.is_empty() {
335 page.on_console(move |msg| {
336 let hs = handlers.clone();
337 async move {
338 for h in hs {
339 (h)(msg.clone()).await;
340 }
341 }
342 });
343 }
344 }
345 {
347 let handlers = self.inner.on_request_handlers.lock().clone();
348 if !handlers.is_empty() {
349 page.on_request(move |req| {
350 let hs = handlers.clone();
351 async move {
352 for h in hs {
353 (h)(req.clone()).await;
354 }
355 }
356 });
357 }
358 }
359 {
361 let handlers = self.inner.on_response_handlers.lock().clone();
362 if !handlers.is_empty() {
363 page.on_response(move |resp| {
364 let hs = handlers.clone();
365 async move {
366 for h in hs {
367 (h)(resp.clone()).await;
368 }
369 }
370 });
371 }
372 }
373 {
375 let handlers = self.inner.on_requestfailed_handlers.lock().clone();
376 if !handlers.is_empty() {
377 page.on_requestfailed(move |req| {
378 let hs = handlers.clone();
379 async move {
380 for h in hs {
381 (h)(req.clone()).await;
382 }
383 }
384 });
385 }
386 }
387 {
389 let handlers = self.inner.on_download_handlers.lock().clone();
390 if !handlers.is_empty() {
391 page.on_download(move |dl| {
392 let hs = handlers.clone();
393 async move {
394 for h in hs {
395 (h)(dl.clone()).await;
396 }
397 }
398 });
399 }
400 }
401 }
402
403 pub async fn add_init_script(&self, script: &str) -> Result<()> {
405 self.inner.init_scripts.lock().push(script.to_string());
406 let pages = self.inner.pages.lock().clone();
408 for p in pages {
409 p.add_init_script(script).await?;
410 }
411 Ok(())
412 }
413
414 pub fn set_default_timeout(&self, timeout_ms: u64) {
416 self.inner.default_timeout_ms.store(timeout_ms, Ordering::Relaxed);
417 for p in self.inner.pages.lock().iter() {
418 p.set_default_timeout(timeout_ms);
419 }
420 }
421
422 pub async fn set_extra_http_headers(&self, headers: Headers) -> Result<()> {
424 *self.inner.extra_http_headers.lock() = Some(headers.clone());
425 let pages = self.inner.pages.lock().clone();
426 for p in pages {
427 p.set_extra_http_headers(headers.clone()).await?;
428 }
429 Ok(())
430 }
431
432 pub fn request(&self) -> APIRequestContext {
437 let headers = self
438 .inner
439 .extra_http_headers
440 .lock()
441 .clone()
442 .unwrap_or_default();
443 APIRequestContext::new(headers)
444 }
445
446 pub fn pages(&self) -> Vec<Page> {
448 self.inner.pages.lock().clone()
449 }
450
451 pub fn browser(&self) -> Browser {
453 self.inner.browser.clone()
454 }
455
456 #[allow(dead_code)]
461 async fn ensure_utility_page(&self) -> Result<Page> {
462 if let Some(p) = self.inner.utility_page.lock().clone() {
463 return Ok(p);
464 }
465 let page = self.new_page().await?;
467 *self.inner.utility_page.lock() = Some(page.clone());
468 Ok(page)
469 }
470
471 pub async fn add_cookies(&self, cookies: &[crate::types::Cookie]) -> Result<()> {
473 let mut params = json!({ "cookies": serde_json::to_value(cookies)? });
476 if let Some(id) = &self.inner.browser_context_id {
477 params["browserContextId"] = json!(id);
478 }
479 self.inner
480 .browser
481 .browser_session()
482 .send("Storage.setCookies", params)
483 .await?;
484 Ok(())
485 }
486
487 pub async fn cookies(&self) -> Result<Vec<Value>> {
489 let mut params = json!({});
490 if let Some(id) = &self.inner.browser_context_id {
491 params["browserContextId"] = json!(id);
492 }
493 let v = self
494 .inner
495 .browser
496 .browser_session()
497 .send("Storage.getCookies", params)
498 .await?;
499 let cookies = v
500 .get("cookies")
501 .cloned()
502 .unwrap_or_else(|| Value::Array(vec![]));
503 Ok(cookies
504 .as_array()
505 .cloned()
506 .unwrap_or_default())
507 }
508
509 pub async fn clear_cookies(&self) -> Result<()> {
511 let mut params = json!({});
512 if let Some(id) = &self.inner.browser_context_id {
513 params["browserContextId"] = json!(id);
514 }
515 self.inner
516 .browser
517 .browser_session()
518 .send("Storage.clearCookies", params)
519 .await?;
520 Ok(())
521 }
522
523 pub fn on_page<F, Fut>(&self, handler: F)
527 where
528 F: Fn(Page) -> Fut + Send + Sync + 'static,
529 Fut: Future<Output = ()> + Send + 'static,
530 {
531 let h: PageHandler = Arc::new(move |p| Box::pin(handler(p)));
532 self.inner.on_page_handlers.lock().push(h);
533 }
534
535 pub async fn close(&self) -> Result<()> {
537 if *self.inner.closed.lock() {
538 return Ok(());
539 }
540 *self.inner.closed.lock() = true;
541
542 let pages = std::mem::take(&mut *self.inner.pages.lock());
544 for p in pages {
545 let _ = p.close().await;
546 }
547 if let Some(_util) = self.inner.utility_page.lock().take() {
548 }
550
551 if let Some(bcid) = &self.inner.browser_context_id {
552 let _ = self
553 .inner
554 .browser
555 .browser_session()
556 .send("Target.disposeBrowserContext", json!({"browserContextId": bcid}))
557 .await;
558 }
559 Ok(())
560 }
561
562 pub async fn grant_permissions(&self, permissions: &[&str]) -> Result<()> {
567 self.inner
568 .browser
569 .browser_session()
570 .send(
571 "Browser.grantPermissions",
572 json!({ "permissions": permissions }),
573 )
574 .await
575 .map(|_: Value| ())
576 }
577
578 pub async fn clear_permissions(&self) -> Result<()> {
580 self.inner
581 .browser
582 .browser_session()
583 .send("Browser.resetPermissions", json!({}))
584 .await
585 .map(|_: Value| ())
586 }
587
588 pub async fn storage_state(&self) -> Result<crate::types::StorageState> {
594 let cookies = self.cookies().await?;
595
596 let mut origins: Vec<crate::types::OriginStorage> = Vec::new();
598 for page in self.pages() {
599 let v: Value = page
601 .evaluate(
602 "JSON.stringify({ origin: location.origin, entries: Object.entries(localStorage).map(([n,v]) => ({name:n, value:v})) })",
603 )
604 .await?;
605 let parsed: Value = match &v {
607 Value::String(s) => serde_json::from_str(s).unwrap_or(Value::Null),
608 other => other.clone(),
609 };
610 let origin = parsed
611 .get("origin")
612 .and_then(|o| o.as_str())
613 .unwrap_or("")
614 .to_string();
615 if origin.is_empty() || origin == "null" {
617 continue;
618 }
619 let entries = parsed
620 .get("entries")
621 .and_then(|e| e.as_array())
622 .cloned()
623 .unwrap_or_default();
624 let local_storage: Vec<crate::types::NameValue> = entries
625 .into_iter()
626 .filter_map(|e| {
627 Some(crate::types::NameValue {
628 name: e.get("name")?.as_str()?.to_string(),
629 value: e.get("value")?.as_str()?.to_string(),
630 })
631 })
632 .collect();
633
634 if let Some(existing) = origins.iter_mut().find(|o| o.origin == origin) {
635 for nv in local_storage {
637 if let Some(pos) = existing.localStorage.iter().position(|x| x.name == nv.name) {
638 existing.localStorage[pos].value = nv.value;
639 } else {
640 existing.localStorage.push(nv);
641 }
642 }
643 } else {
644 origins.push(crate::types::OriginStorage {
645 origin,
646 localStorage: local_storage,
647 });
648 }
649 }
650
651 Ok(crate::types::StorageState { cookies, origins })
652 }
653
654 pub async fn set_storage_state(
659 &self,
660 state: &crate::types::StorageState,
661 ) -> Result<()> {
662 if !state.cookies.is_empty() {
664 let mut params = json!({ "cookies": state.cookies });
668 if let Some(id) = &self.inner.browser_context_id {
669 params["browserContextId"] = json!(id);
670 }
671 self.inner
672 .browser
673 .browser_session()
674 .send("Storage.setCookies", params)
675 .await?;
676 }
677
678 for origin_state in &state.origins {
680 if origin_state.localStorage.is_empty() {
681 continue;
682 }
683 let mut target: Option<Page> = None;
685 for p in self.pages() {
686 let p_origin: String = match p.evaluate::<String>("location.origin").await {
687 Ok(o) => o,
688 Err(_) => continue,
689 };
690 if p_origin == origin_state.origin {
691 target = Some(p);
692 break;
693 }
694 }
695 let page = match target {
696 Some(p) => p,
697 None => {
698 let p = self.new_page().await?;
699 let url = format!("{}/", origin_state.origin);
700 p.goto(&url, None).await?;
701 p
702 }
703 };
704
705 let _: Value = page
707 .evaluate_with_arg(
708 "(() => { for (const {name, value} of arg) localStorage.setItem(name, value); return null; })()",
709 &origin_state.localStorage,
710 )
711 .await?;
712 }
713
714 Ok(())
715 }
716
717 pub async fn route<F, Fut>(&self, pattern: &str, handler: F) -> Result<()>
727 where
728 F: Fn(Route) -> Fut + Send + Sync + 'static,
729 Fut: Future<Output = ()> + Send + 'static,
730 {
731 let handler: RouteHandler = Arc::new(move |r| Box::pin(handler(r)));
732 let entry = RouteEntry {
733 pattern: pattern.to_string(),
734 handler: Arc::clone(&handler),
735 };
736 self.inner.context_routes.lock().push(entry);
737
738 let pat = pattern.to_string();
740 for page in self.pages() {
741 let h = Arc::clone(&handler);
742 page.route(&pat, move |r| {
743 let h = Arc::clone(&h);
744 async move {
745 (h)(r).await;
746 }
747 })
748 .await?;
749 }
750 Ok(())
751 }
752
753 pub async fn unroute(&self, pattern: &str) -> Result<()> {
756 let mut routes = self.inner.context_routes.lock();
757 if let Some(pos) = routes.iter().position(|e| e.pattern == pattern) {
758 routes.remove(pos);
759 }
760 drop(routes);
761
762 for page in self.pages() {
763 page.unroute(pattern).await?;
764 }
765 Ok(())
766 }
767
768 pub async fn unroute_all(&self) -> Result<()> {
770 self.inner.context_routes.lock().clear();
771 for page in self.pages() {
772 page.unroute_all().await?;
773 }
774 Ok(())
775 }
776
777 pub async fn route_from_har(
797 &self,
798 har_path: impl AsRef<std::path::Path>,
799 options: Option<RouteFromHarOptions>,
800 ) -> Result<()> {
801 let opts = options;
802 let parsed = routes_from_har(har_path.as_ref(), opts.as_ref())?;
803
804 let update = opts.as_ref().and_then(|o| o.update).unwrap_or(false);
807
808 for HarRoute {
809 method,
810 pattern,
811 status,
812 headers,
813 body,
814 } in parsed
815 {
816 let method = method;
817 let status = status;
818 let headers = headers.clone();
819 let body = body.clone();
820 let update = update;
821 self.route(&pattern, move |route: Route| {
822 let method = method.clone();
823 let status = status;
824 let headers = headers.clone();
825 let body = body.clone();
826 async move {
827 if update {
828 let _ = route.fallback().await;
830 return;
831 }
832 if !route.request().method().eq_ignore_ascii_case(&method) {
836 let _ = route.fallback().await;
837 return;
838 }
839 let opts = RouteFulfillOptions {
840 status: Some(status),
841 headers: Some(headers),
842 content_type: None,
843 body: Some(body),
844 };
845 if route.fulfill(opts).await.is_err() {
848 let _ = route.fallback().await;
849 }
850 }
851 })
852 .await?;
853 }
854 Ok(())
855 }
856
857 pub fn tracing(&self) -> crate::Tracing {
863 self.inner.tracing.clone()
864 }
865
866 pub async fn clock(&self) -> Result<Clock> {
894 let pages = self.pages();
895 let session = if let Some(first) = pages.into_iter().next() {
896 page_session_owned(&first)
897 } else {
898 let page = self.ensure_utility_page().await?;
902 page_session_owned(&page)
903 };
904 Ok(Clock::new(session))
905 }
906
907 pub async fn set_clock_install(
914 &self,
915 options: Option<ClockInstallOptions>,
916 ) -> Result<()> {
917 let opts = options.clone();
918 *self.inner.pending_clock_install.lock() = opts;
919 if let Some(first) = self.pages().into_iter().next() {
922 Clock::new(page_session_owned(&first))
923 .install(options.clone())
924 .await?;
925 }
926 Ok(())
927 }
928
929 pub fn on_close<F, Fut>(&self, handler: F)
938 where
939 F: Fn() -> Fut + Send + Sync + 'static,
940 Fut: Future<Output = ()> + Send + 'static,
941 {
942 let h: CloseHandler = Arc::new(move || Box::pin(handler()));
943 self.inner.on_close_handlers.lock().push(h.clone());
944 for page in self.pages() {
945 let h = h.clone();
946 page.on_close(move || {
947 let hh = h.clone();
948 async move { (hh)().await }
949 });
950 }
951 }
952
953 pub fn on_console<F, Fut>(&self, handler: F)
955 where
956 F: Fn(ConsoleMessage) -> Fut + Send + Sync + 'static,
957 Fut: Future<Output = ()> + Send + 'static,
958 {
959 let h: ConsoleHandler = Arc::new(move |m| Box::pin(handler(m)));
960 self.inner.on_console_handlers.lock().push(h.clone());
961 for page in self.pages() {
962 let h = h.clone();
963 page.on_console(move |m| {
964 let hh = h.clone();
965 async move { (hh)(m).await }
966 });
967 }
968 }
969
970 pub fn on_request<F, Fut>(&self, handler: F)
972 where
973 F: Fn(Request) -> Fut + Send + Sync + 'static,
974 Fut: Future<Output = ()> + Send + 'static,
975 {
976 let h: RequestHandler = Arc::new(move |r| Box::pin(handler(r)));
977 self.inner.on_request_handlers.lock().push(h.clone());
978 for page in self.pages() {
979 let h = h.clone();
980 page.on_request(move |r| {
981 let hh = h.clone();
982 async move { (hh)(r).await }
983 });
984 }
985 }
986
987 pub fn on_response<F, Fut>(&self, handler: F)
989 where
990 F: Fn(Response) -> Fut + Send + Sync + 'static,
991 Fut: Future<Output = ()> + Send + 'static,
992 {
993 let h: ResponseHandler = Arc::new(move |r| Box::pin(handler(r)));
994 self.inner.on_response_handlers.lock().push(h.clone());
995 for page in self.pages() {
996 let h = h.clone();
997 page.on_response(move |r| {
998 let hh = h.clone();
999 async move { (hh)(r).await }
1000 });
1001 }
1002 }
1003
1004 pub fn on_requestfailed<F, Fut>(&self, handler: F)
1006 where
1007 F: Fn(Request) -> Fut + Send + Sync + 'static,
1008 Fut: Future<Output = ()> + Send + 'static,
1009 {
1010 let h: RequestHandler = Arc::new(move |r| Box::pin(handler(r)));
1011 self.inner.on_requestfailed_handlers.lock().push(h.clone());
1012 for page in self.pages() {
1013 let h = h.clone();
1014 page.on_requestfailed(move |r| {
1015 let hh = h.clone();
1016 async move { (hh)(r).await }
1017 });
1018 }
1019 }
1020
1021 pub fn on_download<F, Fut>(&self, handler: F)
1023 where
1024 F: Fn(Download) -> Fut + Send + Sync + 'static,
1025 Fut: Future<Output = ()> + Send + 'static,
1026 {
1027 let h: DownloadHandler = Arc::new(move |d| Box::pin(handler(d)));
1028 self.inner.on_download_handlers.lock().push(h.clone());
1029 for page in self.pages() {
1030 let h = h.clone();
1031 page.on_download(move |d| {
1032 let hh = h.clone();
1033 async move { (hh)(d).await }
1034 });
1035 }
1036 }
1037
1038 pub async fn set_geolocation(&self, geolocation: Option<Position>) -> Result<()> {
1048 *self.inner.geolocation.lock() = geolocation;
1049 let geo = geolocation.map(|p| (p.x, p.y));
1050 for page in self.pages() {
1051 page.set_geolocation(geo).await?;
1052 }
1053 Ok(())
1054 }
1055
1056 pub async fn set_offline(&self, offline: bool) -> Result<()> {
1058 *self.inner.offline.lock() = offline;
1059 for page in self.pages() {
1060 page.set_offline(offline).await?;
1061 }
1062 Ok(())
1063 }
1064
1065 pub fn set_default_navigation_timeout(&self, ms: u64) {
1068 self.inner
1069 .default_navigation_timeout_ms
1070 .store(ms, Ordering::Relaxed);
1071 for page in self.inner.pages.lock().iter() {
1072 page.set_default_navigation_timeout(ms);
1073 }
1074 }
1075
1076 pub async fn expose_function<F, Fut>(&self, name: &str, callback: F) -> Result<()>
1083 where
1084 F: Fn(Vec<Value>) -> Fut + Send + Sync + 'static,
1085 Fut: Future<Output = Value> + Send + 'static,
1086 {
1087 let handler: ExposedBindingHandler = Arc::new(move |args| Box::pin(callback(args)));
1088 self.inner.exposed_functions.lock().push(ExposedFunction {
1089 name: name.to_string(),
1090 handler: Arc::clone(&handler),
1091 });
1092 let name = name.to_string();
1093 for page in self.pages() {
1094 let h = Arc::clone(&handler);
1095 page.expose_function(&name, move |args| {
1096 let hh = Arc::clone(&h);
1097 async move { (hh)(args).await }
1098 })
1099 .await?;
1100 }
1101 Ok(())
1102 }
1103
1104 pub async fn new_cdp_session(&self, page: &Page) -> Result<CdpSession> {
1110 let browser_session = self.inner.browser.browser_session();
1111 let conn = Arc::clone(browser_session.connection());
1112 let resp: Value = browser_session
1113 .send(
1114 "Target.attachToTarget",
1115 json!({ "targetId": page.target_id(), "flatten": true }),
1116 )
1117 .await?;
1118 let session_id = resp
1119 .get("sessionId")
1120 .and_then(|v| v.as_str())
1121 .ok_or_else(|| {
1122 Error::ProtocolError("attachToTarget missing sessionId".into())
1123 })?
1124 .to_string();
1125 Ok(CdpSession::target(conn, session_id))
1126 }
1127}