1use crate::listeners::{EventListenerRequest, EventListeners};
2use chromiumoxide_cdp::cdp::browser_protocol::browser::*;
3use chromiumoxide_cdp::cdp::browser_protocol::target::*;
4use chromiumoxide_cdp::cdp::events::CdpEvent;
5use chromiumoxide_cdp::cdp::events::CdpEventMessage;
6use chromiumoxide_types::{CallId, Message, Method, Response};
7use chromiumoxide_types::{MethodId, Request as CdpRequest};
8use fnv::FnvHashMap;
9use futures::channel::mpsc::Receiver;
10use futures::channel::oneshot::Sender as OneshotSender;
11use futures::stream::{Fuse, Stream, StreamExt};
12use futures::task::{Context, Poll};
13use hashbrown::{HashMap, HashSet};
14use spider_network_blocker::intercept_manager::NetworkInterceptManager;
15use std::pin::Pin;
16use std::time::{Duration, Instant};
17use tokio_tungstenite::tungstenite::error::ProtocolError;
18use tokio_tungstenite::tungstenite::Error;
19
20use crate::cmd::{to_command_response, CommandMessage};
21use crate::conn::Connection;
22use crate::error::{CdpError, Result};
23use crate::handler::browser::BrowserContext;
24use crate::handler::frame::FrameRequestedNavigation;
25use crate::handler::frame::{NavigationError, NavigationId, NavigationOk};
26use crate::handler::job::PeriodicJob;
27use crate::handler::session::Session;
28use crate::handler::target::TargetEvent;
29use crate::handler::target::{Target, TargetConfig};
30use crate::handler::viewport::Viewport;
31use crate::page::Page;
32pub(crate) use page::PageInner;
33
34pub const REQUEST_TIMEOUT: u64 = 30_000;
36
37pub mod blockers;
38pub mod browser;
39pub mod commandfuture;
40pub mod domworld;
41pub mod emulation;
42pub mod frame;
43pub mod http;
44pub mod httpfuture;
45mod job;
46pub mod network;
47pub mod network_utils;
48mod page;
49mod session;
50pub mod target;
51pub mod target_message_future;
52pub mod viewport;
53
54#[must_use = "streams do nothing unless polled"]
57#[derive(Debug)]
58pub struct Handler {
59 pub default_browser_context: BrowserContext,
60 pub browser_contexts: HashSet<BrowserContext>,
61 pending_commands: FnvHashMap<CallId, (PendingRequest, MethodId, Instant)>,
65 from_browser: Fuse<Receiver<HandlerMessage>>,
67 target_ids: Vec<TargetId>,
69 targets: HashMap<TargetId, Target>,
71 navigations: FnvHashMap<NavigationId, NavigationRequest>,
73 sessions: HashMap<SessionId, Session>,
77 conn: Connection<CdpEventMessage>,
79 evict_command_timeout: PeriodicJob,
81 next_navigation_id: usize,
83 config: HandlerConfig,
85 event_listeners: EventListeners,
87 closing: bool,
89 remaining_bytes: Option<u64>,
91 budget_exhausted: bool,
93 attached_targets: HashSet<TargetId>,
95}
96
97lazy_static::lazy_static! {
98 static ref DISCOVER_ID: (std::borrow::Cow<'static, str>, serde_json::Value) = {
100 let discover = SetDiscoverTargetsParams::new(true);
101 (discover.identifier(), serde_json::to_value(discover).expect("valid discover target params"))
102 };
103 static ref TARGET_PARAMS_ID: (std::borrow::Cow<'static, str>, serde_json::Value) = {
105 let msg = GetTargetsParams { filter: None };
106 (msg.identifier(), serde_json::to_value(msg).expect("valid paramtarget"))
107 };
108 static ref CLOSE_PARAMS_ID: (std::borrow::Cow<'static, str>, serde_json::Value) = {
110 let close_msg = CloseParams::default();
111 (close_msg.identifier(), serde_json::to_value(close_msg).expect("valid close params"))
112 };
113}
114
115impl Handler {
116 pub(crate) fn new(
119 mut conn: Connection<CdpEventMessage>,
120 rx: Receiver<HandlerMessage>,
121 config: HandlerConfig,
122 ) -> Self {
123 let discover = DISCOVER_ID.clone();
124 let _ = conn.submit_command(discover.0, None, discover.1);
125
126 let browser_contexts = config
127 .context_ids
128 .iter()
129 .map(|id| BrowserContext::from(id.clone()))
130 .collect();
131
132 Self {
133 pending_commands: Default::default(),
134 from_browser: rx.fuse(),
135 default_browser_context: Default::default(),
136 browser_contexts,
137 target_ids: Default::default(),
138 targets: Default::default(),
139 navigations: Default::default(),
140 sessions: Default::default(),
141 conn,
142 evict_command_timeout: PeriodicJob::new(config.request_timeout),
143 next_navigation_id: 0,
144 config,
145 event_listeners: Default::default(),
146 closing: false,
147 remaining_bytes: None,
148 budget_exhausted: false,
149 attached_targets: Default::default(),
150 }
151 }
152
153 pub fn get_target(&self, target_id: &TargetId) -> Option<&Target> {
155 self.targets.get(target_id)
156 }
157
158 pub fn targets(&self) -> impl Iterator<Item = &Target> + '_ {
160 self.targets.values()
161 }
162
163 pub fn default_browser_context(&self) -> &BrowserContext {
165 &self.default_browser_context
166 }
167
168 pub fn browser_contexts(&self) -> impl Iterator<Item = &BrowserContext> + '_ {
170 self.browser_contexts.iter()
171 }
172
173 fn on_navigation_response(&mut self, id: NavigationId, resp: Response) {
175 if let Some(nav) = self.navigations.remove(&id) {
176 match nav {
177 NavigationRequest::Navigate(mut nav) => {
178 if nav.navigated {
179 let _ = nav.tx.send(Ok(resp));
180 } else {
181 nav.set_response(resp);
182 self.navigations
183 .insert(id, NavigationRequest::Navigate(nav));
184 }
185 }
186 }
187 }
188 }
189
190 fn on_navigation_lifecycle_completed(&mut self, res: Result<NavigationOk, NavigationError>) {
192 match res {
193 Ok(ok) => {
194 let id = *ok.navigation_id();
195 if let Some(nav) = self.navigations.remove(&id) {
196 match nav {
197 NavigationRequest::Navigate(mut nav) => {
198 if let Some(resp) = nav.response.take() {
199 let _ = nav.tx.send(Ok(resp));
200 } else {
201 nav.set_navigated();
202 self.navigations
203 .insert(id, NavigationRequest::Navigate(nav));
204 }
205 }
206 }
207 }
208 }
209 Err(err) => {
210 if let Some(nav) = self.navigations.remove(err.navigation_id()) {
211 match nav {
212 NavigationRequest::Navigate(nav) => {
213 let _ = nav.tx.send(Err(err.into()));
214 }
215 }
216 }
217 }
218 }
219 }
220
221 fn on_response(&mut self, resp: Response) {
223 if let Some((req, method, _)) = self.pending_commands.remove(&resp.id) {
224 match req {
225 PendingRequest::CreateTarget(tx) => {
226 match to_command_response::<CreateTargetParams>(resp, method) {
227 Ok(resp) => {
228 if let Some(target) = self.targets.get_mut(&resp.target_id) {
229 target.set_initiator(tx);
230 } else {
231 let _ = tx.send(Err(CdpError::NotFound)).ok();
232 }
233 }
234 Err(err) => {
235 let _ = tx.send(Err(err)).ok();
236 }
237 }
238 }
239 PendingRequest::GetTargets(tx) => {
240 match to_command_response::<GetTargetsParams>(resp, method) {
241 Ok(resp) => {
242 let targets = resp.result.target_infos;
243 let results = targets.clone();
244
245 for target_info in targets {
246 let event: EventTargetCreated = EventTargetCreated { target_info };
247 self.on_target_created(event);
248 }
249
250 let _ = tx.send(Ok(results)).ok();
251 }
252 Err(err) => {
253 let _ = tx.send(Err(err)).ok();
254 }
255 }
256 }
257 PendingRequest::Navigate(id) => {
258 self.on_navigation_response(id, resp);
259 if self.config.only_html && !self.config.created_first_target {
260 self.config.created_first_target = true;
261 }
262 }
263 PendingRequest::ExternalCommand(tx) => {
264 let _ = tx.send(Ok(resp)).ok();
265 }
266 PendingRequest::InternalCommand(target_id) => {
267 if let Some(target) = self.targets.get_mut(&target_id) {
268 target.on_response(resp, method.as_ref());
269 }
270 }
271 PendingRequest::CloseBrowser(tx) => {
272 self.closing = true;
273 let _ = tx.send(Ok(CloseReturns {})).ok();
274 }
275 }
276 }
277 }
278
279 pub(crate) fn submit_external_command(
281 &mut self,
282 msg: CommandMessage,
283 now: Instant,
284 ) -> Result<()> {
285 let call_id = self
286 .conn
287 .submit_command(msg.method.clone(), msg.session_id, msg.params)?;
288 self.pending_commands.insert(
289 call_id,
290 (PendingRequest::ExternalCommand(msg.sender), msg.method, now),
291 );
292 Ok(())
293 }
294
295 pub(crate) fn submit_internal_command(
296 &mut self,
297 target_id: TargetId,
298 req: CdpRequest,
299 now: Instant,
300 ) -> Result<()> {
301 let call_id = self.conn.submit_command(
302 req.method.clone(),
303 req.session_id.map(Into::into),
304 req.params,
305 )?;
306 self.pending_commands.insert(
307 call_id,
308 (PendingRequest::InternalCommand(target_id), req.method, now),
309 );
310 Ok(())
311 }
312
313 fn submit_fetch_targets(&mut self, tx: OneshotSender<Result<Vec<TargetInfo>>>, now: Instant) {
314 let msg = TARGET_PARAMS_ID.clone();
315
316 if let Ok(call_id) = self.conn.submit_command(msg.0.clone(), None, msg.1) {
317 self.pending_commands
318 .insert(call_id, (PendingRequest::GetTargets(tx), msg.0, now));
319 }
320 }
321
322 fn submit_navigation(&mut self, id: NavigationId, req: CdpRequest, now: Instant) {
325 if let Ok(call_id) = self.conn.submit_command(
326 req.method.clone(),
327 req.session_id.map(Into::into),
328 req.params,
329 ) {
330 self.pending_commands
331 .insert(call_id, (PendingRequest::Navigate(id), req.method, now));
332 }
333 }
334
335 fn submit_close(&mut self, tx: OneshotSender<Result<CloseReturns>>, now: Instant) {
336 let close_msg = CLOSE_PARAMS_ID.clone();
337
338 if let Ok(call_id) = self
339 .conn
340 .submit_command(close_msg.0.clone(), None, close_msg.1)
341 {
342 self.pending_commands.insert(
343 call_id,
344 (PendingRequest::CloseBrowser(tx), close_msg.0, now),
345 );
346 }
347 }
348
349 fn on_target_message(&mut self, target: &mut Target, msg: CommandMessage, now: Instant) {
351 if msg.is_navigation() {
352 let (req, tx) = msg.split();
353 let id = self.next_navigation_id();
354
355 target.goto(FrameRequestedNavigation::new(id, req));
356
357 self.navigations.insert(
358 id,
359 NavigationRequest::Navigate(NavigationInProgress::new(tx)),
360 );
361 } else {
362 let _ = self.submit_external_command(msg, now);
363 }
364 }
365
366 fn next_navigation_id(&mut self) -> NavigationId {
368 let id = NavigationId(self.next_navigation_id);
369 self.next_navigation_id = self.next_navigation_id.wrapping_add(1);
370 id
371 }
372
373 fn create_page(&mut self, params: CreateTargetParams, tx: OneshotSender<Result<Page>>) {
384 let about_blank = params.url == "about:blank";
385 let http_check =
386 !about_blank && params.url.starts_with("http") || params.url.starts_with("file://");
387
388 if about_blank || http_check {
389 let method = params.identifier();
390
391 match serde_json::to_value(params) {
392 Ok(params) => match self.conn.submit_command(method.clone(), None, params) {
393 Ok(call_id) => {
394 self.pending_commands.insert(
395 call_id,
396 (PendingRequest::CreateTarget(tx), method, Instant::now()),
397 );
398 }
399 Err(err) => {
400 let _ = tx.send(Err(err.into())).ok();
401 }
402 },
403 Err(err) => {
404 let _ = tx.send(Err(err.into())).ok();
405 }
406 }
407 } else {
408 let _ = tx.send(Err(CdpError::NotFound)).ok();
409 }
410 }
411
412 fn on_event(&mut self, event: CdpEventMessage) {
414 if let Some(session_id) = &event.session_id {
415 if let Some(session) = self.sessions.get(session_id.as_str()) {
416 if let Some(target) = self.targets.get_mut(session.target_id()) {
417 return target.on_event(event);
418 }
419 }
420 }
421 let CdpEventMessage { params, method, .. } = event;
422
423 match params {
424 CdpEvent::TargetTargetCreated(ref ev) => self.on_target_created(ev.clone()),
425 CdpEvent::TargetAttachedToTarget(ref ev) => self.on_attached_to_target(ev.clone()),
426 CdpEvent::TargetTargetDestroyed(ref ev) => self.on_target_destroyed(ev.clone()),
427 CdpEvent::TargetDetachedFromTarget(ref ev) => self.on_detached_from_target(ev.clone()),
428 _ => {}
429 }
430
431 chromiumoxide_cdp::consume_event!(match params {
432 |ev| self.event_listeners.start_send(ev),
433 |json| { let _ = self.event_listeners.try_send_custom(&method, json);}
434 });
435 }
436
437 fn on_target_created(&mut self, event: EventTargetCreated) {
441 if !self.browser_contexts.is_empty() {
442 if let Some(ref context_id) = event.target_info.browser_context_id {
443 let bc = BrowserContext {
444 id: Some(context_id.clone()),
445 };
446 if !self.browser_contexts.contains(&bc) {
447 return;
448 }
449 }
450 }
451 let browser_ctx = event
452 .target_info
453 .browser_context_id
454 .clone()
455 .map(BrowserContext::from)
456 .unwrap_or_else(|| self.default_browser_context.clone());
457 let target = Target::new(
458 event.target_info,
459 TargetConfig {
460 ignore_https_errors: self.config.ignore_https_errors,
461 request_timeout: self.config.request_timeout,
462 viewport: self.config.viewport.clone(),
463 request_intercept: self.config.request_intercept,
464 cache_enabled: self.config.cache_enabled,
465 service_worker_enabled: self.config.service_worker_enabled,
466 ignore_visuals: self.config.ignore_visuals,
467 ignore_stylesheets: self.config.ignore_stylesheets,
468 ignore_javascript: self.config.ignore_javascript,
469 ignore_analytics: self.config.ignore_analytics,
470 ignore_prefetch: self.config.ignore_prefetch,
471 extra_headers: self.config.extra_headers.clone(),
472 only_html: self.config.only_html && self.config.created_first_target,
473 intercept_manager: self.config.intercept_manager,
474 max_bytes_allowed: self.config.max_bytes_allowed,
475 whitelist_patterns: self.config.whitelist_patterns.clone(),
476 blacklist_patterns: self.config.blacklist_patterns.clone(),
477 },
478 browser_ctx,
479 );
480
481 self.target_ids.push(target.target_id().clone());
482 self.targets.insert(target.target_id().clone(), target);
483 }
484
485 fn on_attached_to_target(&mut self, event: Box<EventAttachedToTarget>) {
487 let session = Session::new(event.session_id.clone(), event.target_info.target_id);
488 if let Some(target) = self.targets.get_mut(session.target_id()) {
489 target.set_session_id(session.session_id().clone())
490 }
491 self.sessions.insert(event.session_id, session);
492 }
493
494 fn on_detached_from_target(&mut self, event: EventDetachedFromTarget) {
498 if let Some(session) = self.sessions.remove(&event.session_id) {
500 if let Some(target) = self.targets.get_mut(session.target_id()) {
501 target.session_id().take();
502 }
503 }
504 }
505
506 fn on_target_destroyed(&mut self, event: EventTargetDestroyed) {
508 self.attached_targets.remove(&event.target_id);
509
510 if let Some(target) = self.targets.remove(&event.target_id) {
511 if let Some(session) = target.session_id() {
513 self.sessions.remove(session);
514 }
515 }
516 }
517
518 fn evict_timed_out_commands(&mut self, now: Instant) {
523 let timed_out = self
524 .pending_commands
525 .iter()
526 .filter(|(_, (_, _, timestamp))| now > (*timestamp + self.config.request_timeout))
527 .map(|(k, _)| *k)
528 .collect::<Vec<_>>();
529
530 for call in timed_out {
531 if let Some((req, _, _)) = self.pending_commands.remove(&call) {
532 match req {
533 PendingRequest::CreateTarget(tx) => {
534 let _ = tx.send(Err(CdpError::Timeout));
535 }
536 PendingRequest::GetTargets(tx) => {
537 let _ = tx.send(Err(CdpError::Timeout));
538 }
539 PendingRequest::Navigate(nav) => {
540 if let Some(nav) = self.navigations.remove(&nav) {
541 match nav {
542 NavigationRequest::Navigate(nav) => {
543 let _ = nav.tx.send(Err(CdpError::Timeout));
544 }
545 }
546 }
547 }
548 PendingRequest::ExternalCommand(tx) => {
549 let _ = tx.send(Err(CdpError::Timeout));
550 }
551 PendingRequest::InternalCommand(_) => {}
552 PendingRequest::CloseBrowser(tx) => {
553 let _ = tx.send(Err(CdpError::Timeout));
554 }
555 }
556 }
557 }
558 }
559
560 pub fn event_listeners_mut(&mut self) -> &mut EventListeners {
561 &mut self.event_listeners
562 }
563}
564
565impl Stream for Handler {
566 type Item = Result<()>;
567
568 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
569 let pin = self.get_mut();
570
571 let mut dispose = false;
572
573 loop {
574 let now = Instant::now();
575 while let Poll::Ready(Some(msg)) = Pin::new(&mut pin.from_browser).poll_next(cx) {
579 match msg {
580 HandlerMessage::Command(cmd) => {
581 pin.submit_external_command(cmd, now)?;
582 }
583 HandlerMessage::FetchTargets(tx) => {
584 pin.submit_fetch_targets(tx, now);
585 }
586 HandlerMessage::CloseBrowser(tx) => {
587 pin.submit_close(tx, now);
588 }
589 HandlerMessage::CreatePage(params, tx) => {
590 if let Some(ref id) = params.browser_context_id {
591 pin.browser_contexts
592 .insert(BrowserContext::from(id.clone()));
593 }
594 pin.create_page(params, tx);
595 }
596 HandlerMessage::GetPages(tx) => {
597 let pages: Vec<_> = pin
598 .targets
599 .values_mut()
600 .filter(|p: &&mut Target| p.is_page())
601 .filter_map(|target| target.get_or_create_page())
602 .map(|page| Page::from(page.clone()))
603 .collect();
604 let _ = tx.send(pages);
605 }
606 HandlerMessage::InsertContext(ctx) => {
607 if pin.default_browser_context.id().is_none() {
608 pin.default_browser_context = ctx.clone();
609 }
610 pin.browser_contexts.insert(ctx);
611 }
612 HandlerMessage::DisposeContext(ctx) => {
613 pin.browser_contexts.remove(&ctx);
614 pin.attached_targets.retain(|tid| {
615 pin.targets
616 .get(tid)
617 .and_then(|t| t.browser_context_id()) .map(|id| Some(id) != ctx.id())
619 .unwrap_or(true)
620 });
621 pin.closing = true;
622 dispose = true;
623 }
624 HandlerMessage::GetPage(target_id, tx) => {
625 let page = pin
626 .targets
627 .get_mut(&target_id)
628 .and_then(|target| target.get_or_create_page())
629 .map(|page| Page::from(page.clone()));
630 let _ = tx.send(page);
631 }
632 HandlerMessage::AddEventListener(req) => {
633 pin.event_listeners.add_listener(req);
634 }
635 }
636 }
637
638 for n in (0..pin.target_ids.len()).rev() {
639 let target_id = pin.target_ids.swap_remove(n);
640
641 if let Some((id, mut target)) = pin.targets.remove_entry(&target_id) {
642 while let Some(event) = target.poll(cx, now) {
643 match event {
644 TargetEvent::Request(req) => {
645 let _ = pin.submit_internal_command(
646 target.target_id().clone(),
647 req,
648 now,
649 );
650 }
651 TargetEvent::Command(msg) => {
652 pin.on_target_message(&mut target, msg, now);
653 }
654 TargetEvent::NavigationRequest(id, req) => {
655 pin.submit_navigation(id, req, now);
656 }
657 TargetEvent::NavigationResult(res) => {
658 pin.on_navigation_lifecycle_completed(res)
659 }
660 TargetEvent::BytesConsumed(n) => {
661 if let Some(rem) = pin.remaining_bytes.as_mut() {
662 *rem = rem.saturating_sub(n);
663 if *rem == 0 {
664 pin.budget_exhausted = true;
665 }
666 }
667 }
668 }
669 }
670
671 target.event_listeners_mut().poll(cx);
673 pin.event_listeners_mut().poll(cx);
675
676 pin.targets.insert(id, target);
677 pin.target_ids.push(target_id);
678 }
679 }
680
681 let mut done = true;
682
683 while let Poll::Ready(Some(ev)) = Pin::new(&mut pin.conn).poll_next(cx) {
684 match ev {
685 Ok(boxed_msg) => match *boxed_msg {
686 Message::Response(resp) => {
687 pin.on_response(resp);
688 if pin.closing {
689 return Poll::Ready(None);
690 }
691 }
692 Message::Event(ev) => {
693 pin.on_event(ev);
694 }
695 },
696 Err(err) => {
697 tracing::error!("WS Connection error: {:?}", err);
698 match err {
699 CdpError::Ws(ref ws_error) => match ws_error {
700 Error::AlreadyClosed => {
701 pin.closing = true;
702 dispose = true;
703 break;
704 }
705 Error::Protocol(detail)
706 if detail == &ProtocolError::ResetWithoutClosingHandshake =>
707 {
708 pin.closing = true;
709 dispose = true;
710 break;
711 }
712 _ => {}
713 },
714 _ => {}
715 };
716 return Poll::Ready(Some(Err(err)));
717 }
718 }
719 done = false;
720 }
721
722 if pin.evict_command_timeout.poll_ready(cx) {
723 pin.evict_timed_out_commands(now);
725 }
726
727 if pin.budget_exhausted {
728 for t in pin.targets.values_mut() {
729 t.network_manager.set_block_all(true);
730 }
731 }
732
733 if dispose {
734 return Poll::Ready(None);
735 }
736
737 if done {
738 return Poll::Pending;
740 }
741 }
742 }
743}
744
745#[derive(Debug, Clone)]
747pub struct HandlerConfig {
748 pub ignore_https_errors: bool,
750 pub viewport: Option<Viewport>,
752 pub context_ids: Vec<BrowserContextId>,
754 pub request_timeout: Duration,
756 pub request_intercept: bool,
758 pub cache_enabled: bool,
760 pub service_worker_enabled: bool,
762 pub ignore_visuals: bool,
764 pub ignore_stylesheets: bool,
766 pub ignore_javascript: bool,
768 pub ignore_analytics: bool,
770 pub ignore_prefetch: bool,
772 pub ignore_ads: bool,
774 pub extra_headers: Option<std::collections::HashMap<String, String>>,
776 pub only_html: bool,
778 pub created_first_target: bool,
780 pub intercept_manager: NetworkInterceptManager,
782 pub max_bytes_allowed: Option<u64>,
784 pub whitelist_patterns: Option<Vec<String>>,
786 pub blacklist_patterns: Option<Vec<String>>,
788}
789
790impl Default for HandlerConfig {
791 fn default() -> Self {
792 Self {
793 ignore_https_errors: true,
794 viewport: Default::default(),
795 context_ids: Vec::new(),
796 request_timeout: Duration::from_millis(REQUEST_TIMEOUT),
797 request_intercept: false,
798 cache_enabled: true,
799 service_worker_enabled: true,
800 ignore_visuals: false,
801 ignore_stylesheets: false,
802 ignore_ads: false,
803 ignore_javascript: false,
804 ignore_analytics: true,
805 ignore_prefetch: true,
806 only_html: false,
807 extra_headers: Default::default(),
808 created_first_target: false,
809 intercept_manager: NetworkInterceptManager::Unknown,
810 max_bytes_allowed: None,
811 whitelist_patterns: None,
812 blacklist_patterns: None,
813 }
814 }
815}
816
817#[derive(Debug)]
819pub struct NavigationInProgress<T> {
820 navigated: bool,
822 response: Option<Response>,
824 tx: OneshotSender<T>,
826}
827
828impl<T> NavigationInProgress<T> {
829 fn new(tx: OneshotSender<T>) -> Self {
830 Self {
831 navigated: false,
832 response: None,
833 tx,
834 }
835 }
836
837 fn set_response(&mut self, resp: Response) {
839 self.response = Some(resp);
840 }
841
842 fn set_navigated(&mut self) {
844 self.navigated = true;
845 }
846}
847
848#[derive(Debug)]
850enum NavigationRequest {
851 Navigate(NavigationInProgress<Result<Response>>),
853 }
855
856#[derive(Debug)]
859enum PendingRequest {
860 CreateTarget(OneshotSender<Result<Page>>),
863 GetTargets(OneshotSender<Result<Vec<TargetInfo>>>),
865 Navigate(NavigationId),
872 ExternalCommand(OneshotSender<Result<Response>>),
874 InternalCommand(TargetId),
877 CloseBrowser(OneshotSender<Result<CloseReturns>>),
879}
880
881#[derive(Debug)]
885pub(crate) enum HandlerMessage {
886 CreatePage(CreateTargetParams, OneshotSender<Result<Page>>),
887 FetchTargets(OneshotSender<Result<Vec<TargetInfo>>>),
888 InsertContext(BrowserContext),
889 DisposeContext(BrowserContext),
890 GetPages(OneshotSender<Vec<Page>>),
891 Command(CommandMessage),
892 GetPage(TargetId, OneshotSender<Option<Page>>),
893 AddEventListener(EventListenerRequest),
894 CloseBrowser(OneshotSender<Result<CloseReturns>>),
895}