chromiumoxide/handler/
mod.rs

1use crate::handler::blockers::intercept_manager::NetworkInterceptManager;
2use hashbrown::{HashMap, HashSet};
3use std::pin::Pin;
4use std::time::{Duration, Instant};
5
6use fnv::FnvHashMap;
7use futures::channel::mpsc::Receiver;
8use futures::channel::oneshot::Sender as OneshotSender;
9use futures::stream::{Fuse, Stream, StreamExt};
10use futures::task::{Context, Poll};
11
12use crate::listeners::{EventListenerRequest, EventListeners};
13use chromiumoxide_cdp::cdp::browser_protocol::browser::*;
14use chromiumoxide_cdp::cdp::browser_protocol::target::*;
15use chromiumoxide_cdp::cdp::events::CdpEvent;
16use chromiumoxide_cdp::cdp::events::CdpEventMessage;
17use chromiumoxide_types::{CallId, Message, Method, Response};
18use chromiumoxide_types::{MethodId, Request as CdpRequest};
19pub(crate) use page::PageInner;
20
21use crate::cmd::{to_command_response, CommandMessage};
22use crate::conn::Connection;
23use crate::error::{CdpError, Result};
24use crate::handler::browser::BrowserContext;
25use crate::handler::frame::FrameRequestedNavigation;
26use crate::handler::frame::{NavigationError, NavigationId, NavigationOk};
27use crate::handler::job::PeriodicJob;
28use crate::handler::session::Session;
29use crate::handler::target::TargetEvent;
30use crate::handler::target::{Target, TargetConfig};
31use crate::handler::viewport::Viewport;
32use crate::page::Page;
33
34/// Standard timeout in MS
35pub const REQUEST_TIMEOUT: u64 = 60_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;
47mod page;
48mod session;
49pub mod target;
50pub mod target_message_future;
51pub mod viewport;
52
53/// The handler that monitors the state of the chromium browser and drives all
54/// the requests and events.
55#[must_use = "streams do nothing unless polled"]
56#[derive(Debug)]
57pub struct Handler {
58    /// Commands that are being processed and awaiting a response from the
59    /// chromium instance together with the timestamp when the request
60    /// started.
61    pending_commands: FnvHashMap<CallId, (PendingRequest, MethodId, Instant)>,
62    /// Connection to the browser instance
63    from_browser: Fuse<Receiver<HandlerMessage>>,
64    pub default_browser_context: BrowserContext,
65    pub browser_contexts: HashSet<BrowserContext>,
66    /// Used to loop over all targets in a consistent manner
67    target_ids: Vec<TargetId>,
68    /// The created and attached targets
69    targets: HashMap<TargetId, Target>,
70    /// Currently queued in navigations for targets
71    navigations: FnvHashMap<NavigationId, NavigationRequest>,
72    /// Keeps track of all the current active sessions
73    ///
74    /// There can be multiple sessions per target.
75    sessions: HashMap<SessionId, Session>,
76    /// The websocket connection to the chromium instance
77    conn: Connection<CdpEventMessage>,
78    /// Evicts timed out requests periodically
79    evict_command_timeout: PeriodicJob,
80    /// The internal identifier for a specific navigation
81    next_navigation_id: usize,
82    /// How this handler will configure targets etc,
83    config: HandlerConfig,
84    /// All registered event subscriptions
85    event_listeners: EventListeners,
86    /// Keeps track is the browser is closing
87    closing: bool,
88}
89
90impl Handler {
91    /// Create a new `Handler` that drives the connection and listens for
92    /// messages on the receiver `rx`.
93    pub(crate) fn new(
94        mut conn: Connection<CdpEventMessage>,
95        rx: Receiver<HandlerMessage>,
96        config: HandlerConfig,
97    ) -> Self {
98        let discover = SetDiscoverTargetsParams::new(true);
99        let discover_id = discover.identifier();
100
101        if let Ok(params) = serde_json::to_value(discover) {
102            let _ = conn.submit_command(discover_id, None, params);
103        }
104
105        let browser_contexts = config
106            .context_ids
107            .iter()
108            .map(|id| BrowserContext::from(id.clone()))
109            .collect();
110
111        Self {
112            pending_commands: Default::default(),
113            from_browser: rx.fuse(),
114            default_browser_context: Default::default(),
115            browser_contexts,
116            target_ids: Default::default(),
117            targets: Default::default(),
118            navigations: Default::default(),
119            sessions: Default::default(),
120            conn,
121            evict_command_timeout: PeriodicJob::new(config.request_timeout),
122            next_navigation_id: 0,
123            config,
124            event_listeners: Default::default(),
125            closing: false,
126        }
127    }
128
129    /// Return the target with the matching `target_id`
130    pub fn get_target(&self, target_id: &TargetId) -> Option<&Target> {
131        self.targets.get(target_id)
132    }
133
134    /// Iterator over all currently attached targets
135    pub fn targets(&self) -> impl Iterator<Item = &Target> + '_ {
136        self.targets.values()
137    }
138
139    /// The default Browser context
140    pub fn default_browser_context(&self) -> &BrowserContext {
141        &self.default_browser_context
142    }
143
144    /// The default Browser context
145    pub fn set_default_browser_context(&mut self, context_id: BrowserContextId) -> &BrowserContext {
146        let browser_context = BrowserContext {
147            id: Some(context_id),
148        };
149        self.browser_contexts.insert(browser_context.clone());
150        self.default_browser_context = browser_context;
151        &self.default_browser_context
152    }
153
154    /// Iterator over all currently available browser contexts
155    pub fn browser_contexts(&self) -> impl Iterator<Item = &BrowserContext> + '_ {
156        self.browser_contexts.iter()
157    }
158
159    /// received a response to a navigation request like `Page.navigate`
160    fn on_navigation_response(&mut self, id: NavigationId, resp: Response) {
161        if let Some(nav) = self.navigations.remove(&id) {
162            match nav {
163                NavigationRequest::Navigate(mut nav) => {
164                    if nav.navigated {
165                        let _ = nav.tx.send(Ok(resp));
166                    } else {
167                        nav.set_response(resp);
168                        self.navigations
169                            .insert(id, NavigationRequest::Navigate(nav));
170                    }
171                }
172            }
173        }
174    }
175
176    /// A navigation has finished.
177    fn on_navigation_lifecycle_completed(&mut self, res: Result<NavigationOk, NavigationError>) {
178        match res {
179            Ok(ok) => {
180                let id = *ok.navigation_id();
181                if let Some(nav) = self.navigations.remove(&id) {
182                    match nav {
183                        NavigationRequest::Navigate(mut nav) => {
184                            if let Some(resp) = nav.response.take() {
185                                let _ = nav.tx.send(Ok(resp));
186                            } else {
187                                nav.set_navigated();
188                                self.navigations
189                                    .insert(id, NavigationRequest::Navigate(nav));
190                            }
191                        }
192                    }
193                }
194            }
195            Err(err) => {
196                if let Some(nav) = self.navigations.remove(err.navigation_id()) {
197                    match nav {
198                        NavigationRequest::Navigate(nav) => {
199                            let _ = nav.tx.send(Err(err.into()));
200                        }
201                    }
202                }
203            }
204        }
205    }
206
207    /// Received a response to a request.
208    fn on_response(&mut self, resp: Response) {
209        if let Some((req, method, _)) = self.pending_commands.remove(&resp.id) {
210            match req {
211                PendingRequest::CreateTarget(tx) => {
212                    match to_command_response::<CreateTargetParams>(resp, method) {
213                        Ok(resp) => {
214                            if let Some(target) = self.targets.get_mut(&resp.target_id) {
215                                // move the sender to the target that sends its page once
216                                // initialized
217                                target.set_initiator(tx);
218                            }
219                        }
220                        Err(err) => {
221                            let _ = tx.send(Err(err)).ok();
222                        }
223                    }
224                }
225                PendingRequest::GetTargets(tx) => {
226                    match to_command_response::<GetTargetsParams>(resp, method) {
227                        Ok(resp) => {
228                            let targets: Vec<TargetInfo> = resp.result.target_infos;
229                            let results = targets.clone();
230                            for target_info in targets {
231                                let target_id = target_info.target_id.clone();
232                                let event: EventTargetCreated = EventTargetCreated { target_info };
233                                self.on_target_created(event);
234                                let attach = AttachToTargetParams::new(target_id);
235
236                                let _ = self.conn.submit_command(
237                                    attach.identifier(),
238                                    None,
239                                    serde_json::to_value(attach).unwrap_or_default(),
240                                );
241                            }
242
243                            let _ = tx.send(Ok(results)).ok();
244                        }
245                        Err(err) => {
246                            let _ = tx.send(Err(err)).ok();
247                        }
248                    }
249                }
250                PendingRequest::Navigate(id) => {
251                    self.on_navigation_response(id, resp);
252                    if self.config.only_html && !self.config.created_first_target {
253                        self.config.created_first_target = true;
254                    }
255                }
256                PendingRequest::ExternalCommand(tx) => {
257                    let _ = tx.send(Ok(resp)).ok();
258                }
259                PendingRequest::InternalCommand(target_id) => {
260                    if let Some(target) = self.targets.get_mut(&target_id) {
261                        target.on_response(resp, method.as_ref());
262                    }
263                }
264                PendingRequest::CloseBrowser(tx) => {
265                    self.closing = true;
266                    let _ = tx.send(Ok(CloseReturns {})).ok();
267                }
268            }
269        }
270    }
271
272    /// Submit a command initiated via channel
273    pub(crate) fn submit_external_command(
274        &mut self,
275        msg: CommandMessage,
276        now: Instant,
277    ) -> Result<()> {
278        let call_id = self
279            .conn
280            .submit_command(msg.method.clone(), msg.session_id, msg.params)?;
281        self.pending_commands.insert(
282            call_id,
283            (PendingRequest::ExternalCommand(msg.sender), msg.method, now),
284        );
285        Ok(())
286    }
287
288    pub(crate) fn submit_internal_command(
289        &mut self,
290        target_id: TargetId,
291        req: CdpRequest,
292        now: Instant,
293    ) -> Result<()> {
294        let call_id = self.conn.submit_command(
295            req.method.clone(),
296            req.session_id.map(Into::into),
297            req.params,
298        )?;
299        self.pending_commands.insert(
300            call_id,
301            (PendingRequest::InternalCommand(target_id), req.method, now),
302        );
303        Ok(())
304    }
305
306    fn submit_fetch_targets(&mut self, tx: OneshotSender<Result<Vec<TargetInfo>>>, now: Instant) {
307        let msg = GetTargetsParams { filter: None };
308        let method = msg.identifier();
309
310        if let Ok(params) = serde_json::to_value(msg) {
311            if let Ok(call_id) = self.conn.submit_command(method.clone(), None, params) {
312                self.pending_commands
313                    .insert(call_id, (PendingRequest::GetTargets(tx), method, now));
314            }
315        }
316    }
317
318    /// Send the Request over to the server and store its identifier to handle
319    /// the response once received.
320    fn submit_navigation(&mut self, id: NavigationId, req: CdpRequest, now: Instant) {
321        if let Ok(call_id) = self.conn.submit_command(
322            req.method.clone(),
323            req.session_id.map(Into::into),
324            req.params,
325        ) {
326            self.pending_commands
327                .insert(call_id, (PendingRequest::Navigate(id), req.method, now));
328        }
329    }
330
331    fn submit_close(&mut self, tx: OneshotSender<Result<CloseReturns>>, now: Instant) {
332        let close_msg = CloseParams::default();
333        let method = close_msg.identifier();
334
335        if let Ok(call_id) = self.conn.submit_command(
336            method.clone(),
337            None,
338            serde_json::to_value(close_msg).unwrap(),
339        ) {
340            self.pending_commands
341                .insert(call_id, (PendingRequest::CloseBrowser(tx), method, now));
342        }
343    }
344
345    /// Process a message received by the target's page via channel
346    fn on_target_message(&mut self, target: &mut Target, msg: CommandMessage, now: Instant) {
347        // if let some
348        if msg.is_navigation() {
349            let (req, tx) = msg.split();
350            let id = self.next_navigation_id();
351            target.goto(FrameRequestedNavigation::new(id, req));
352            self.navigations.insert(
353                id,
354                NavigationRequest::Navigate(NavigationInProgress::new(tx)),
355            );
356        } else {
357            let _ = self.submit_external_command(msg, now);
358        }
359    }
360
361    /// An identifier for queued `NavigationRequest`s.
362    fn next_navigation_id(&mut self) -> NavigationId {
363        let id = NavigationId(self.next_navigation_id);
364        self.next_navigation_id = self.next_navigation_id.wrapping_add(1);
365        id
366    }
367
368    /// Create a new page and send it to the receiver when ready
369    ///
370    /// First a `CreateTargetParams` is send to the server, this will trigger
371    /// `EventTargetCreated` which results in a new `Target` being created.
372    /// Once the response to the request is received the initialization process
373    /// of the target kicks in. This triggers a queue of initialization requests
374    /// of the `Target`, once those are all processed and the `url` fo the
375    /// `CreateTargetParams` has finished loading (The `Target`'s `Page` is
376    /// ready and idle), the `Target` sends its newly created `Page` as response
377    /// to the initiator (`tx`) of the `CreateTargetParams` request.
378    fn create_page(&mut self, params: CreateTargetParams, tx: OneshotSender<Result<Page>>) {
379        let about_blank = params.url == "about:blank";
380        let http_check =
381            !about_blank && params.url.starts_with("http") || params.url.starts_with("file://");
382
383        if about_blank || http_check {
384            let method = params.identifier();
385
386            match serde_json::to_value(params) {
387                Ok(params) => match self.conn.submit_command(method.clone(), None, params) {
388                    Ok(call_id) => {
389                        self.pending_commands.insert(
390                            call_id,
391                            (PendingRequest::CreateTarget(tx), method, Instant::now()),
392                        );
393                    }
394                    Err(err) => {
395                        let _ = tx.send(Err(err.into())).ok();
396                    }
397                },
398                Err(err) => {
399                    let _ = tx.send(Err(err.into())).ok();
400                }
401            }
402        } else {
403            let _ = tx.send(Err(CdpError::NotFound)).ok();
404        }
405    }
406
407    /// Process an incoming event read from the websocket
408    fn on_event(&mut self, event: CdpEventMessage) {
409        if let Some(ref session_id) = event.session_id {
410            if let Some(session) = self.sessions.get(session_id.as_str()) {
411                if let Some(target) = self.targets.get_mut(session.target_id()) {
412                    return target.on_event(event);
413                }
414            }
415        }
416        let CdpEventMessage { params, method, .. } = event;
417
418        match params {
419            CdpEvent::TargetTargetCreated(ref ev) => self.on_target_created(ev.clone()),
420            CdpEvent::TargetAttachedToTarget(ref ev) => self.on_attached_to_target(ev.clone()),
421            CdpEvent::TargetTargetDestroyed(ref ev) => self.on_target_destroyed(ev.clone()),
422            CdpEvent::TargetDetachedFromTarget(ref ev) => self.on_detached_from_target(ev.clone()),
423            _ => {}
424        }
425
426        chromiumoxide_cdp::consume_event!(match params {
427            |ev| self.event_listeners.start_send(ev),
428            |json| { let _ = self.event_listeners.try_send_custom(&method, json);}
429        });
430    }
431
432    /// Fired when a new target was created on the chromium instance
433    ///
434    /// Creates a new `Target` instance and keeps track of it
435    fn on_target_created(&mut self, event: EventTargetCreated) {
436        let browser_ctx = match event.target_info.browser_context_id {
437            Some(ref context_id) => {
438                let browser_context = BrowserContext {
439                    id: Some(context_id.clone()),
440                };
441                if self.default_browser_context.id.is_none() {
442                    self.default_browser_context = browser_context.clone();
443                };
444                self.browser_contexts.insert(browser_context.clone());
445
446                browser_context
447            }
448            _ => event
449                .target_info
450                .browser_context_id
451                .clone()
452                .map(BrowserContext::from)
453                .filter(|id| self.browser_contexts.contains(id))
454                .unwrap_or_else(|| self.default_browser_context.clone()),
455        };
456
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                extra_headers: self.config.extra_headers.clone(),
471                only_html: self.config.only_html && self.config.created_first_target,
472                intercept_manager: self.config.intercept_manager,
473            },
474            browser_ctx,
475        );
476
477        self.target_ids.push(target.target_id().clone());
478        self.targets.insert(target.target_id().clone(), target);
479    }
480
481    /// A new session is attached to a target
482    fn on_attached_to_target(&mut self, event: Box<EventAttachedToTarget>) {
483        let session = Session::new(event.session_id.clone(), event.target_info.target_id);
484        if let Some(target) = self.targets.get_mut(session.target_id()) {
485            target.set_session_id(session.session_id().clone())
486        }
487        self.sessions.insert(event.session_id, session);
488    }
489
490    /// The session was detached from target.
491    /// Can be issued multiple times per target if multiple session have been
492    /// attached to it.
493    fn on_detached_from_target(&mut self, event: EventDetachedFromTarget) {
494        // remove the session
495        if let Some(session) = self.sessions.remove(&event.session_id) {
496            if let Some(target) = self.targets.get_mut(session.target_id()) {
497                target.session_id().take();
498            }
499        }
500    }
501
502    /// Fired when the target was destroyed in the browser
503    fn on_target_destroyed(&mut self, event: EventTargetDestroyed) {
504        if let Some(target) = self.targets.remove(&event.target_id) {
505            // TODO shutdown?
506            if let Some(session) = target.session_id() {
507                self.sessions.remove(session);
508            }
509        }
510    }
511
512    /// House keeping of commands
513    ///
514    /// Remove all commands where `now` > `timestamp of command starting point +
515    /// request timeout` and notify the senders that their request timed out.
516    fn evict_timed_out_commands(&mut self, now: Instant) {
517        let timed_out = self
518            .pending_commands
519            .iter()
520            .filter(|(_, (_, _, timestamp))| now > (*timestamp + self.config.request_timeout))
521            .map(|(k, _)| *k)
522            .collect::<Vec<_>>();
523
524        for call in timed_out {
525            if let Some((req, _, _)) = self.pending_commands.remove(&call) {
526                match req {
527                    PendingRequest::CreateTarget(tx) => {
528                        let _ = tx.send(Err(CdpError::Timeout));
529                    }
530                    PendingRequest::GetTargets(tx) => {
531                        let _ = tx.send(Err(CdpError::Timeout));
532                    }
533                    PendingRequest::Navigate(nav) => {
534                        if let Some(nav) = self.navigations.remove(&nav) {
535                            match nav {
536                                NavigationRequest::Navigate(nav) => {
537                                    let _ = nav.tx.send(Err(CdpError::Timeout));
538                                }
539                            }
540                        }
541                    }
542                    PendingRequest::ExternalCommand(tx) => {
543                        let _ = tx.send(Err(CdpError::Timeout));
544                    }
545                    PendingRequest::InternalCommand(_) => {}
546                    PendingRequest::CloseBrowser(tx) => {
547                        let _ = tx.send(Err(CdpError::Timeout));
548                    }
549                }
550            }
551        }
552    }
553
554    pub fn event_listeners_mut(&mut self) -> &mut EventListeners {
555        &mut self.event_listeners
556    }
557}
558
559impl Stream for Handler {
560    type Item = Result<()>;
561
562    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
563        let pin = self.get_mut();
564
565        loop {
566            let now = Instant::now();
567            // temporary pinning of the browser receiver should be safe as we are pinning
568            // through the already pinned self. with the receivers we can also
569            // safely ignore exhaustion as those are fused.
570            while let Poll::Ready(Some(msg)) = Pin::new(&mut pin.from_browser).poll_next(cx) {
571                match msg {
572                    HandlerMessage::Command(cmd) => {
573                        pin.submit_external_command(cmd, now)?;
574                    }
575                    HandlerMessage::FetchTargets(tx) => {
576                        pin.submit_fetch_targets(tx, now);
577                    }
578                    HandlerMessage::CloseBrowser(tx) => {
579                        pin.submit_close(tx, now);
580                    }
581                    HandlerMessage::CreatePage(params, tx) => {
582                        pin.create_page(params, tx);
583                    }
584                    HandlerMessage::GetPages(tx) => {
585                        let pages: Vec<_> = pin
586                            .targets
587                            .values_mut()
588                            .filter(|p: &&mut Target| p.is_page())
589                            .filter_map(|target| target.get_or_create_page())
590                            .map(|page| Page::from(page.clone()))
591                            .collect();
592                        let _ = tx.send(pages);
593                    }
594                    HandlerMessage::InsertContext(ctx) => {
595                        if pin.default_browser_context.id.is_none() {
596                            pin.default_browser_context = ctx.clone();
597                        }
598                        pin.browser_contexts.insert(ctx);
599                    }
600                    HandlerMessage::DisposeContext(ctx) => {
601                        pin.browser_contexts.remove(&ctx);
602                    }
603                    HandlerMessage::GetPage(target_id, tx) => {
604                        let page = pin
605                            .targets
606                            .get_mut(&target_id)
607                            .and_then(|target| target.get_or_create_page())
608                            .map(|page| Page::from(page.clone()));
609                        let _ = tx.send(page);
610                    }
611                    HandlerMessage::AddEventListener(req) => {
612                        pin.event_listeners.add_listener(req);
613                    }
614                }
615            }
616
617            for n in (0..pin.target_ids.len()).rev() {
618                let target_id = pin.target_ids.swap_remove(n);
619
620                if let Some((id, mut target)) = pin.targets.remove_entry(&target_id) {
621                    while let Some(event) = target.poll(cx, now) {
622                        match event {
623                            TargetEvent::Request(req) => {
624                                let _ = pin.submit_internal_command(
625                                    target.target_id().clone(),
626                                    req,
627                                    now,
628                                );
629                            }
630                            TargetEvent::Command(msg) => {
631                                pin.on_target_message(&mut target, msg, now);
632                            }
633                            TargetEvent::NavigationRequest(id, req) => {
634                                pin.submit_navigation(id, req, now);
635                            }
636                            TargetEvent::NavigationResult(res) => {
637                                pin.on_navigation_lifecycle_completed(res)
638                            }
639                        }
640                    }
641
642                    // poll the target's event listeners
643                    target.event_listeners_mut().poll(cx);
644                    // poll the handler's event listeners
645                    pin.event_listeners_mut().poll(cx);
646
647                    pin.targets.insert(id, target);
648                    pin.target_ids.push(target_id);
649                }
650            }
651
652            let mut done = true;
653
654            while let Poll::Ready(Some(ev)) = Pin::new(&mut pin.conn).poll_next(cx) {
655                match ev {
656                    Ok(Message::Response(resp)) => {
657                        pin.on_response(resp);
658                        if pin.closing {
659                            // handler should stop processing
660                            return Poll::Ready(None);
661                        }
662                    }
663                    Ok(Message::Event(ev)) => {
664                        pin.on_event(ev);
665                    }
666                    Err(err) => {
667                        tracing::error!("WS Connection error: {:?}", err);
668                        return Poll::Ready(Some(Err(err)));
669                    }
670                }
671                done = false;
672            }
673
674            if pin.evict_command_timeout.poll_ready(cx) {
675                // evict all commands that timed out
676                pin.evict_timed_out_commands(now);
677            }
678
679            if done {
680                // no events/responses were read from the websocket
681                return Poll::Pending;
682            }
683        }
684    }
685}
686
687/// How to configure the handler
688#[derive(Debug, Clone)]
689pub struct HandlerConfig {
690    /// Whether the `NetworkManager`s should ignore https errors
691    pub ignore_https_errors: bool,
692    /// Window and device settings
693    pub viewport: Option<Viewport>,
694    /// Context ids to set from the get go
695    pub context_ids: Vec<BrowserContextId>,
696    /// default request timeout to use
697    pub request_timeout: Duration,
698    /// Whether to enable request interception
699    pub request_intercept: bool,
700    /// Whether to enable cache
701    pub cache_enabled: bool,
702    /// Whether to enable Service Workers
703    pub service_worker_enabled: bool,
704    /// Whether to ignore visuals.
705    pub ignore_visuals: bool,
706    /// Whether to ignore stylesheets.
707    pub ignore_stylesheets: bool,
708    /// Whether to ignore Javascript only allowing critical framework or lib based rendering.
709    pub ignore_javascript: bool,
710    /// Whether to ignore analytics.
711    pub ignore_analytics: bool,
712    /// Whether to ignore ads.
713    pub ignore_ads: bool,
714    /// Extra headers.
715    pub extra_headers: Option<std::collections::HashMap<String, String>>,
716    /// Only Html.
717    pub only_html: bool,
718    /// Created the first target.
719    pub created_first_target: bool,
720    /// The network intercept manager.
721    pub intercept_manager: NetworkInterceptManager,
722}
723
724impl Default for HandlerConfig {
725    fn default() -> Self {
726        Self {
727            ignore_https_errors: true,
728            viewport: Default::default(),
729            context_ids: Vec::new(),
730            request_timeout: Duration::from_millis(REQUEST_TIMEOUT),
731            request_intercept: false,
732            cache_enabled: true,
733            service_worker_enabled: true,
734            ignore_visuals: false,
735            ignore_stylesheets: false,
736            ignore_ads: false,
737            ignore_javascript: false,
738            ignore_analytics: true,
739            only_html: false,
740            extra_headers: Default::default(),
741            created_first_target: false,
742            intercept_manager: NetworkInterceptManager::UNKNOWN,
743        }
744    }
745}
746
747/// Wraps the sender half of the channel who requested a navigation
748#[derive(Debug)]
749pub struct NavigationInProgress<T> {
750    /// Marker to indicate whether a navigation lifecycle has completed
751    navigated: bool,
752    /// The response of the issued navigation request
753    response: Option<Response>,
754    /// Sender who initiated the navigation request
755    tx: OneshotSender<T>,
756}
757
758impl<T> NavigationInProgress<T> {
759    fn new(tx: OneshotSender<T>) -> Self {
760        Self {
761            navigated: false,
762            response: None,
763            tx,
764        }
765    }
766
767    /// The response to the cdp request has arrived
768    fn set_response(&mut self, resp: Response) {
769        self.response = Some(resp);
770    }
771
772    /// The navigation process has finished, the page finished loading.
773    fn set_navigated(&mut self) {
774        self.navigated = true;
775    }
776}
777
778/// Request type for navigation
779#[derive(Debug)]
780enum NavigationRequest {
781    /// Represents a simple `NavigateParams` ("Page.navigate")
782    Navigate(NavigationInProgress<Result<Response>>),
783    // TODO are there more?
784}
785
786/// Different kind of submitted request submitted from the  `Handler` to the
787/// `Connection` and being waited on for the response.
788#[derive(Debug)]
789enum PendingRequest {
790    /// A Request to create a new `Target` that results in the creation of a
791    /// `Page` that represents a browser page.
792    CreateTarget(OneshotSender<Result<Page>>),
793    /// A Request to fetch old `Target`s created before connection
794    GetTargets(OneshotSender<Result<Vec<TargetInfo>>>),
795    /// A Request to navigate a specific `Target`.
796    ///
797    /// Navigation requests are not automatically completed once the response to
798    /// the raw cdp navigation request (like `NavigateParams`) arrives, but only
799    /// after the `Target` notifies the `Handler` that the `Page` has finished
800    /// loading, which comes after the response.
801    Navigate(NavigationId),
802    /// A common request received via a channel (`Page`).
803    ExternalCommand(OneshotSender<Result<Response>>),
804    /// Requests that are initiated directly from a `Target` (all the
805    /// initialization commands).
806    InternalCommand(TargetId),
807    // A Request to close the browser.
808    CloseBrowser(OneshotSender<Result<CloseReturns>>),
809}
810
811/// Events used internally to communicate with the handler, which are executed
812/// in the background
813// TODO rename to BrowserMessage
814#[derive(Debug)]
815pub(crate) enum HandlerMessage {
816    CreatePage(CreateTargetParams, OneshotSender<Result<Page>>),
817    FetchTargets(OneshotSender<Result<Vec<TargetInfo>>>),
818    InsertContext(BrowserContext),
819    DisposeContext(BrowserContext),
820    GetPages(OneshotSender<Vec<Page>>),
821    Command(CommandMessage),
822    GetPage(TargetId, OneshotSender<Option<Page>>),
823    AddEventListener(EventListenerRequest),
824    CloseBrowser(OneshotSender<Result<CloseReturns>>),
825}