chromiumoxide/handler/
mod.rs

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