chromiumoxide/handler/
target.rs

1use std::collections::VecDeque;
2use std::pin::Pin;
3use std::sync::Arc;
4use std::time::Instant;
5
6use chromiumoxide_cdp::cdp::browser_protocol::target::DetachFromTargetParams;
7use futures::channel::oneshot::Sender;
8use futures::stream::Stream;
9use futures::task::{Context, Poll};
10
11use chromiumoxide_cdp::cdp::browser_protocol::page::{FrameId, GetFrameTreeParams};
12use chromiumoxide_cdp::cdp::browser_protocol::{
13    browser::BrowserContextId,
14    log as cdplog, performance,
15    target::{AttachToTargetParams, SessionId, SetAutoAttachParams, TargetId, TargetInfo},
16};
17use chromiumoxide_cdp::cdp::events::CdpEvent;
18use chromiumoxide_cdp::cdp::CdpEventMessage;
19use chromiumoxide_types::{Command, Method, Request, Response};
20
21use super::blockers::intercept_manager::NetworkInterceptManager;
22use crate::auth::Credentials;
23use crate::cdp::browser_protocol::target::CloseTargetParams;
24use crate::cmd::CommandChain;
25use crate::cmd::CommandMessage;
26use crate::error::{CdpError, Result};
27use crate::handler::browser::BrowserContext;
28use crate::handler::domworld::DOMWorldKind;
29use crate::handler::emulation::EmulationManager;
30use crate::handler::frame::{
31    FrameEvent, FrameManager, NavigationError, NavigationId, NavigationOk,
32};
33use crate::handler::frame::{FrameRequestedNavigation, UTILITY_WORLD_NAME};
34use crate::handler::network::{NetworkEvent, NetworkManager};
35use crate::handler::page::PageHandle;
36use crate::handler::viewport::Viewport;
37use crate::handler::{PageInner, REQUEST_TIMEOUT};
38use crate::listeners::{EventListenerRequest, EventListeners};
39use crate::{page::Page, ArcHttpRequest};
40use chromiumoxide_cdp::cdp::js_protocol::runtime::{
41    ExecutionContextId, RunIfWaitingForDebuggerParams,
42};
43use std::time::Duration;
44
45macro_rules! advance_state {
46    ($s:ident, $cx:ident, $now:ident, $cmds: ident, $next_state:expr ) => {{
47        if let Poll::Ready(poll) = $cmds.poll($now) {
48            return match poll {
49                None => {
50                    $s.init_state = $next_state;
51                    $s.poll($cx, $now)
52                }
53                Some(Ok((method, params))) => Some(TargetEvent::Request(Request {
54                    method,
55                    session_id: $s.session_id.clone().map(Into::into),
56                    params,
57                })),
58                Some(Err(_)) => Some($s.on_initialization_failed()),
59            };
60        } else {
61            return None;
62        }
63    }};
64}
65
66#[derive(Debug)]
67pub struct Target {
68    /// Info about this target as returned from the chromium instance
69    info: TargetInfo,
70    /// The type of this target
71    r#type: TargetType,
72    /// Configs for this target
73    config: TargetConfig,
74    /// The context this target is running in
75    browser_context: BrowserContext,
76    /// The frame manager that maintains the state of all frames and handles
77    /// navigations of frames
78    frame_manager: FrameManager,
79    /// Handles all the https
80    network_manager: NetworkManager,
81    emulation_manager: EmulationManager,
82    /// The identifier of the session this target is attached to
83    session_id: Option<SessionId>,
84    /// The handle of the browser page of this target
85    page: Option<PageHandle>,
86    /// Drives this target towards initialization
87    pub(crate) init_state: TargetInit,
88    /// Currently queued events to report to the `Handler`
89    queued_events: VecDeque<TargetEvent>,
90    /// All registered event subscriptions
91    event_listeners: EventListeners,
92    /// Senders that need to be notified once the main frame has loaded
93    wait_for_frame_navigation: Vec<Sender<ArcHttpRequest>>,
94    /// The sender who requested the page.
95    initiator: Option<Sender<Result<Page>>>,
96}
97
98impl Target {
99    /// Create a new target instance with `TargetInfo` after a
100    /// `CreateTargetParams` request.
101    pub fn new(info: TargetInfo, config: TargetConfig, browser_context: BrowserContext) -> Self {
102        let ty = TargetType::new(&info.r#type);
103        let request_timeout = config.request_timeout;
104        let mut network_manager = NetworkManager::new(config.ignore_https_errors, request_timeout);
105
106        network_manager.set_cache_enabled(config.cache_enabled);
107        network_manager.set_request_interception(config.request_intercept);
108
109        if let Some(ref headers) = config.extra_headers {
110            network_manager.set_extra_headers(headers.clone());
111        }
112
113        network_manager.ignore_visuals = config.ignore_visuals;
114        network_manager.block_javascript = config.ignore_javascript;
115        network_manager.block_analytics = config.ignore_analytics;
116        network_manager.block_stylesheets = config.ignore_stylesheets;
117        network_manager.only_html = config.only_html;
118        network_manager.intercept_manager = config.intercept_manager;
119
120        Self {
121            info,
122            r#type: ty,
123            config,
124            frame_manager: FrameManager::new(request_timeout),
125            network_manager,
126            emulation_manager: EmulationManager::new(request_timeout),
127            session_id: None,
128            page: None,
129            init_state: TargetInit::AttachToTarget,
130            wait_for_frame_navigation: Default::default(),
131            queued_events: Default::default(),
132            event_listeners: Default::default(),
133            initiator: None,
134            browser_context,
135        }
136    }
137
138    pub fn set_session_id(&mut self, id: SessionId) {
139        self.session_id = Some(id)
140    }
141
142    pub fn session_id(&self) -> Option<&SessionId> {
143        self.session_id.as_ref()
144    }
145
146    pub fn browser_context(&self) -> &BrowserContext {
147        &self.browser_context
148    }
149
150    pub fn session_id_mut(&mut self) -> &mut Option<SessionId> {
151        &mut self.session_id
152    }
153
154    /// The identifier for this target
155    pub fn target_id(&self) -> &TargetId {
156        &self.info.target_id
157    }
158
159    /// The type of this target
160    pub fn r#type(&self) -> &TargetType {
161        &self.r#type
162    }
163
164    /// Whether this target is already initialized
165    pub fn is_initialized(&self) -> bool {
166        matches!(self.init_state, TargetInit::Initialized)
167    }
168
169    /// Navigate a frame
170    pub fn goto(&mut self, req: FrameRequestedNavigation) {
171        self.frame_manager.goto(req)
172    }
173
174    fn create_page(&mut self) {
175        if self.page.is_none() {
176            if let Some(session) = self.session_id.clone() {
177                let handle =
178                    PageHandle::new(self.target_id().clone(), session, self.opener_id().cloned());
179                self.page = Some(handle);
180            }
181        }
182    }
183
184    /// Tries to create the `PageInner` if this target is already initialized
185    pub(crate) fn get_or_create_page(&mut self) -> Option<&Arc<PageInner>> {
186        self.create_page();
187        self.page.as_ref().map(|p| p.inner())
188    }
189
190    pub fn is_page(&self) -> bool {
191        self.r#type().is_page()
192    }
193
194    pub fn browser_context_id(&self) -> Option<&BrowserContextId> {
195        self.info.browser_context_id.as_ref()
196    }
197
198    pub fn info(&self) -> &TargetInfo {
199        &self.info
200    }
201
202    /// Get the target that opened this target. Top-level targets return `None`.
203    pub fn opener_id(&self) -> Option<&TargetId> {
204        self.info.opener_id.as_ref()
205    }
206
207    pub fn frame_manager(&self) -> &FrameManager {
208        &self.frame_manager
209    }
210
211    pub fn frame_manager_mut(&mut self) -> &mut FrameManager {
212        &mut self.frame_manager
213    }
214
215    pub fn event_listeners_mut(&mut self) -> &mut EventListeners {
216        &mut self.event_listeners
217    }
218
219    /// Received a response to a command issued by this target
220    pub fn on_response(&mut self, resp: Response, method: &str) {
221        if let Some(cmds) = self.init_state.commands_mut() {
222            cmds.received_response(method);
223        }
224        #[allow(clippy::single_match)] // allow for now
225        match method {
226            GetFrameTreeParams::IDENTIFIER => {
227                if let Some(resp) = resp
228                    .result
229                    .and_then(|val| GetFrameTreeParams::response_from_value(val).ok())
230                {
231                    self.frame_manager.on_frame_tree(resp.frame_tree);
232                }
233            }
234            // requests originated from the network manager all return an empty response, hence they
235            // can be ignored here
236            _ => {}
237        }
238    }
239
240    pub fn on_event(&mut self, event: CdpEventMessage) {
241        let CdpEventMessage { params, method, .. } = event;
242
243        match &params {
244            // `FrameManager` events
245            CdpEvent::PageFrameAttached(ev) => self
246                .frame_manager
247                .on_frame_attached(ev.frame_id.clone(), Some(ev.parent_frame_id.clone())),
248            CdpEvent::PageFrameDetached(ev) => self.frame_manager.on_frame_detached(ev),
249            CdpEvent::PageFrameNavigated(ev) => self.frame_manager.on_frame_navigated(&ev.frame),
250            CdpEvent::PageNavigatedWithinDocument(ev) => {
251                self.frame_manager.on_frame_navigated_within_document(ev)
252            }
253            CdpEvent::RuntimeExecutionContextCreated(ev) => {
254                self.frame_manager.on_frame_execution_context_created(ev)
255            }
256            CdpEvent::RuntimeExecutionContextDestroyed(ev) => {
257                self.frame_manager.on_frame_execution_context_destroyed(ev)
258            }
259            CdpEvent::RuntimeExecutionContextsCleared(_) => {
260                self.frame_manager.on_execution_contexts_cleared()
261            }
262            CdpEvent::RuntimeBindingCalled(ev) => {
263                // TODO check if binding registered and payload is json
264                self.frame_manager.on_runtime_binding_called(ev)
265            }
266            CdpEvent::PageLifecycleEvent(ev) => self.frame_manager.on_page_lifecycle_event(ev),
267            CdpEvent::PageFrameStartedLoading(ev) => {
268                self.frame_manager.on_frame_started_loading(ev);
269            }
270
271            // `Target` events
272            CdpEvent::TargetAttachedToTarget(ev) => {
273                if ev.waiting_for_debugger {
274                    let runtime_cmd = RunIfWaitingForDebuggerParams::default();
275
276                    self.queued_events.push_back(TargetEvent::Request(Request {
277                        method: runtime_cmd.identifier(),
278                        session_id: Some(ev.session_id.clone().into()),
279                        params: serde_json::to_value(runtime_cmd).unwrap_or_default(),
280                    }));
281                }
282
283                if "service_worker" == &ev.target_info.r#type {
284                    let detach_command = DetachFromTargetParams::builder()
285                        .session_id(ev.session_id.clone())
286                        .build();
287
288                    self.queued_events.push_back(TargetEvent::Request(Request {
289                        method: detach_command.identifier(),
290                        session_id: self.session_id.clone().map(Into::into),
291                        params: serde_json::to_value(detach_command).unwrap_or_default(),
292                    }));
293                }
294            }
295
296            // `NetworkManager` events
297            CdpEvent::FetchRequestPaused(ev) => self.network_manager.on_fetch_request_paused(ev),
298            CdpEvent::FetchAuthRequired(ev) => self.network_manager.on_fetch_auth_required(ev),
299            CdpEvent::NetworkRequestWillBeSent(ev) => {
300                self.network_manager.on_request_will_be_sent(ev)
301            }
302            CdpEvent::NetworkRequestServedFromCache(ev) => {
303                self.network_manager.on_request_served_from_cache(ev)
304            }
305            CdpEvent::NetworkResponseReceived(ev) => self.network_manager.on_response_received(ev),
306            CdpEvent::NetworkLoadingFinished(ev) => {
307                self.network_manager.on_network_loading_finished(ev)
308            }
309            CdpEvent::NetworkLoadingFailed(ev) => {
310                self.network_manager.on_network_loading_failed(ev)
311            }
312            _ => (),
313        }
314        chromiumoxide_cdp::consume_event!(match params {
315           |ev| self.event_listeners.start_send(ev),
316           |json| { let _ = self.event_listeners.try_send_custom(&method, json);}
317        });
318    }
319
320    /// Called when a init command timed out
321    fn on_initialization_failed(&mut self) -> TargetEvent {
322        if let Some(initiator) = self.initiator.take() {
323            let _ = initiator.send(Err(CdpError::Timeout));
324        }
325        self.init_state = TargetInit::Closing;
326        let close_target = CloseTargetParams::new(self.info.target_id.clone());
327        TargetEvent::Request(Request {
328            method: close_target.identifier(),
329            session_id: self.session_id.clone().map(Into::into),
330            params: serde_json::to_value(close_target).unwrap_or_default(),
331        })
332    }
333
334    /// Advance that target's state
335    pub(crate) fn poll(&mut self, cx: &mut Context<'_>, now: Instant) -> Option<TargetEvent> {
336        if !self.is_page() {
337            // can only poll pages
338            return None;
339        }
340
341        match &mut self.init_state {
342            TargetInit::AttachToTarget => {
343                self.init_state = TargetInit::InitializingFrame(FrameManager::init_commands(
344                    self.config.request_timeout,
345                ));
346
347                if let Ok(params) = AttachToTargetParams::builder()
348                    .target_id(self.target_id().clone())
349                    .flatten(true)
350                    .build()
351                {
352                    return Some(TargetEvent::Request(Request::new(
353                        params.identifier(),
354                        serde_json::to_value(params).unwrap_or_default(),
355                    )));
356                } else {
357                    return None;
358                }
359            }
360            TargetInit::InitializingFrame(cmds) => {
361                self.session_id.as_ref()?;
362                if let Poll::Ready(poll) = cmds.poll(now) {
363                    return match poll {
364                        None => {
365                            if let Some(isolated_world_cmds) =
366                                self.frame_manager.ensure_isolated_world(UTILITY_WORLD_NAME)
367                            {
368                                *cmds = isolated_world_cmds;
369                            } else {
370                                self.init_state = TargetInit::InitializingNetwork(
371                                    self.network_manager.init_commands(),
372                                );
373                            }
374                            self.poll(cx, now)
375                        }
376                        Some(Ok((method, params))) => Some(TargetEvent::Request(Request {
377                            method,
378                            session_id: self.session_id.clone().map(Into::into),
379                            params,
380                        })),
381                        Some(Err(_)) => Some(self.on_initialization_failed()),
382                    };
383                } else {
384                    return None;
385                }
386            }
387            TargetInit::InitializingNetwork(cmds) => {
388                advance_state!(
389                    self,
390                    cx,
391                    now,
392                    cmds,
393                    TargetInit::InitializingPage(Self::page_init_commands(
394                        self.config.request_timeout
395                    ))
396                );
397            }
398            TargetInit::InitializingPage(cmds) => {
399                advance_state!(
400                    self,
401                    cx,
402                    now,
403                    cmds,
404                    match self.config.viewport.as_ref() {
405                        Some(viewport) => TargetInit::InitializingEmulation(
406                            self.emulation_manager.init_commands(viewport)
407                        ),
408                        None => TargetInit::Initialized,
409                    }
410                );
411            }
412            TargetInit::InitializingEmulation(cmds) => {
413                advance_state!(self, cx, now, cmds, TargetInit::Initialized);
414            }
415            TargetInit::Initialized => {
416                if let Some(initiator) = self.initiator.take() {
417                    // make sure that the main frame of the page has finished loading
418                    if self
419                        .frame_manager
420                        .main_frame()
421                        .map(|frame| frame.is_loaded())
422                        .unwrap_or_default()
423                    {
424                        if let Some(page) = self.get_or_create_page() {
425                            let _ = initiator.send(Ok(page.clone().into()));
426                        } else {
427                            self.initiator = Some(initiator);
428                        }
429                    } else {
430                        self.initiator = Some(initiator);
431                    }
432                }
433            }
434            TargetInit::Closing => return None,
435        };
436
437        loop {
438            if self.init_state == TargetInit::Closing {
439                break None;
440            }
441
442            if let Some(frame) = self.frame_manager.main_frame() {
443                if frame.is_loaded() {
444                    while let Some(tx) = self.wait_for_frame_navigation.pop() {
445                        let _ = tx.send(frame.http_request().cloned());
446                    }
447                }
448            }
449
450            // Drain queued messages first.
451            if let Some(ev) = self.queued_events.pop_front() {
452                return Some(ev);
453            }
454
455            if let Some(handle) = self.page.as_mut() {
456                while let Poll::Ready(Some(msg)) = Pin::new(&mut handle.rx).poll_next(cx) {
457                    if self.init_state == TargetInit::Closing {
458                        break;
459                    }
460
461                    match msg {
462                        TargetMessage::Command(cmd) => {
463                            self.queued_events.push_back(TargetEvent::Command(cmd));
464                        }
465                        TargetMessage::MainFrame(tx) => {
466                            let _ =
467                                tx.send(self.frame_manager.main_frame().map(|f| f.id().clone()));
468                        }
469                        TargetMessage::AllFrames(tx) => {
470                            let _ = tx.send(
471                                self.frame_manager
472                                    .frames()
473                                    .map(|f| f.id().clone())
474                                    .collect(),
475                            );
476                        }
477                        TargetMessage::Url(req) => {
478                            let GetUrl { frame_id, tx } = req;
479                            let frame = if let Some(frame_id) = frame_id {
480                                self.frame_manager.frame(&frame_id)
481                            } else {
482                                self.frame_manager.main_frame()
483                            };
484                            let _ = tx.send(frame.and_then(|f| f.url().map(str::to_string)));
485                        }
486                        TargetMessage::Name(req) => {
487                            let GetName { frame_id, tx } = req;
488                            let frame = if let Some(frame_id) = frame_id {
489                                self.frame_manager.frame(&frame_id)
490                            } else {
491                                self.frame_manager.main_frame()
492                            };
493                            let _ = tx.send(frame.and_then(|f| f.name().map(str::to_string)));
494                        }
495                        TargetMessage::Parent(req) => {
496                            let GetParent { frame_id, tx } = req;
497                            let frame = self.frame_manager.frame(&frame_id);
498                            let _ = tx.send(frame.and_then(|f| f.parent_id().cloned()));
499                        }
500                        TargetMessage::WaitForNavigation(tx) => {
501                            if let Some(frame) = self.frame_manager.main_frame() {
502                                // TODO submit a navigation watcher: waitForFrameNavigation
503
504                                // TODO return the watchers navigationResponse
505                                if frame.is_loaded() {
506                                    let _ = tx.send(frame.http_request().cloned());
507                                } else {
508                                    self.wait_for_frame_navigation.push(tx);
509                                }
510                            } else {
511                                self.wait_for_frame_navigation.push(tx);
512                            }
513                        }
514                        TargetMessage::AddEventListener(req) => {
515                            // register a new listener
516                            self.event_listeners.add_listener(req);
517                        }
518                        TargetMessage::GetExecutionContext(ctx) => {
519                            let GetExecutionContext {
520                                dom_world,
521                                frame_id,
522                                tx,
523                            } = ctx;
524                            let frame = if let Some(frame_id) = frame_id {
525                                self.frame_manager.frame(&frame_id)
526                            } else {
527                                self.frame_manager.main_frame()
528                            };
529
530                            if let Some(frame) = frame {
531                                match dom_world {
532                                    DOMWorldKind::Main => {
533                                        let _ = tx.send(frame.main_world().execution_context());
534                                    }
535                                    DOMWorldKind::Secondary => {
536                                        let _ =
537                                            tx.send(frame.secondary_world().execution_context());
538                                    }
539                                }
540                            } else {
541                                let _ = tx.send(None);
542                            }
543                        }
544                        TargetMessage::Authenticate(credentials) => {
545                            self.network_manager.authenticate(credentials);
546                        }
547                    }
548                }
549            }
550
551            while let Some(event) = self.network_manager.poll() {
552                if self.init_state == TargetInit::Closing {
553                    break;
554                }
555                match event {
556                    NetworkEvent::SendCdpRequest((method, params)) => {
557                        // send a message to the browser
558                        self.queued_events.push_back(TargetEvent::Request(Request {
559                            method,
560                            session_id: self.session_id.clone().map(Into::into),
561                            params,
562                        }))
563                    }
564                    NetworkEvent::Request(_) => {}
565                    NetworkEvent::Response(_) => {}
566                    NetworkEvent::RequestFailed(request) => {
567                        self.frame_manager.on_http_request_finished(request);
568                    }
569                    NetworkEvent::RequestFinished(request) => {
570                        self.frame_manager.on_http_request_finished(request);
571                    }
572                }
573            }
574
575            while let Some(event) = self.frame_manager.poll(now) {
576                if self.init_state == TargetInit::Closing {
577                    break;
578                }
579                match event {
580                    FrameEvent::NavigationResult(res) => {
581                        self.queued_events
582                            .push_back(TargetEvent::NavigationResult(res));
583                    }
584                    FrameEvent::NavigationRequest(id, req) => {
585                        self.queued_events
586                            .push_back(TargetEvent::NavigationRequest(id, req));
587                    }
588                }
589            }
590
591            if self.queued_events.is_empty() {
592                return None;
593            }
594        }
595    }
596
597    /// Set the sender half of the channel who requested the creation of this
598    /// target
599    pub fn set_initiator(&mut self, tx: Sender<Result<Page>>) {
600        self.initiator = Some(tx);
601    }
602
603    pub(crate) fn page_init_commands(timeout: Duration) -> CommandChain {
604        let attach = SetAutoAttachParams::builder()
605            .flatten(true)
606            .auto_attach(true)
607            .wait_for_debugger_on_start(true)
608            .build()
609            .unwrap();
610        let enable_performance = performance::EnableParams::default();
611        let enable_log = cdplog::EnableParams::default();
612
613        CommandChain::new(
614            vec![
615                (
616                    attach.identifier(),
617                    serde_json::to_value(attach).unwrap_or_default(),
618                ),
619                (
620                    enable_performance.identifier(),
621                    serde_json::to_value(enable_performance).unwrap_or_default(),
622                ),
623                (
624                    enable_log.identifier(),
625                    serde_json::to_value(enable_log).unwrap_or_default(),
626                ),
627            ],
628            timeout,
629        )
630    }
631}
632
633#[derive(Debug, Clone)]
634pub struct TargetConfig {
635    pub ignore_https_errors: bool,
636    ///  Request timeout to use
637    pub request_timeout: Duration,
638    pub viewport: Option<Viewport>,
639    pub request_intercept: bool,
640    pub cache_enabled: bool,
641    pub ignore_visuals: bool,
642    pub ignore_javascript: bool,
643    pub ignore_analytics: bool,
644    pub ignore_stylesheets: bool,
645    pub only_html: bool,
646    pub extra_headers: Option<std::collections::HashMap<String, String>>,
647    pub intercept_manager: NetworkInterceptManager,
648}
649
650impl Default for TargetConfig {
651    fn default() -> Self {
652        Self {
653            ignore_https_errors: true,
654            request_timeout: Duration::from_secs(REQUEST_TIMEOUT),
655            viewport: Default::default(),
656            request_intercept: false,
657            cache_enabled: true,
658            ignore_javascript: false,
659            ignore_visuals: false,
660            ignore_stylesheets: false,
661            ignore_analytics: true,
662            only_html: false,
663            extra_headers: Default::default(),
664            intercept_manager: NetworkInterceptManager::UNKNOWN,
665        }
666    }
667}
668
669#[derive(Debug, Clone, Eq, PartialEq)]
670pub enum TargetType {
671    Page,
672    BackgroundPage,
673    ServiceWorker,
674    SharedWorker,
675    Other,
676    Browser,
677    Webview,
678    Unknown(String),
679}
680
681impl TargetType {
682    pub fn new(ty: &str) -> Self {
683        match ty {
684            "page" => TargetType::Page,
685            "background_page" => TargetType::BackgroundPage,
686            "service_worker" => TargetType::ServiceWorker,
687            "shared_worker" => TargetType::SharedWorker,
688            "other" => TargetType::Other,
689            "browser" => TargetType::Browser,
690            "webview" => TargetType::Webview,
691            s => TargetType::Unknown(s.to_string()),
692        }
693    }
694
695    pub fn is_page(&self) -> bool {
696        matches!(self, TargetType::Page)
697    }
698
699    pub fn is_background_page(&self) -> bool {
700        matches!(self, TargetType::BackgroundPage)
701    }
702
703    pub fn is_service_worker(&self) -> bool {
704        matches!(self, TargetType::ServiceWorker)
705    }
706
707    pub fn is_shared_worker(&self) -> bool {
708        matches!(self, TargetType::SharedWorker)
709    }
710
711    pub fn is_other(&self) -> bool {
712        matches!(self, TargetType::Other)
713    }
714
715    pub fn is_browser(&self) -> bool {
716        matches!(self, TargetType::Browser)
717    }
718
719    pub fn is_webview(&self) -> bool {
720        matches!(self, TargetType::Webview)
721    }
722}
723
724#[derive(Debug)]
725pub(crate) enum TargetEvent {
726    /// An internal request
727    Request(Request),
728    /// An internal navigation request
729    NavigationRequest(NavigationId, Request),
730    /// Indicates that a previous requested navigation has finished
731    NavigationResult(Result<NavigationOk, NavigationError>),
732    /// A new command arrived via a channel
733    Command(CommandMessage),
734}
735
736// TODO this can be moved into the classes?
737#[derive(Debug, PartialEq)]
738pub enum TargetInit {
739    InitializingFrame(CommandChain),
740    InitializingNetwork(CommandChain),
741    InitializingPage(CommandChain),
742    InitializingEmulation(CommandChain),
743    AttachToTarget,
744    Initialized,
745    Closing,
746}
747
748impl TargetInit {
749    fn commands_mut(&mut self) -> Option<&mut CommandChain> {
750        match self {
751            TargetInit::InitializingFrame(cmd) => Some(cmd),
752            TargetInit::InitializingNetwork(cmd) => Some(cmd),
753            TargetInit::InitializingPage(cmd) => Some(cmd),
754            TargetInit::InitializingEmulation(cmd) => Some(cmd),
755            TargetInit::AttachToTarget => None,
756            TargetInit::Initialized => None,
757            TargetInit::Closing => None,
758        }
759    }
760}
761
762#[derive(Debug)]
763pub struct GetExecutionContext {
764    /// For which world the execution context was requested
765    pub dom_world: DOMWorldKind,
766    /// The if of the frame to get the `ExecutionContext` for
767    pub frame_id: Option<FrameId>,
768    /// Sender half of the channel to send the response back
769    pub tx: Sender<Option<ExecutionContextId>>,
770}
771
772impl GetExecutionContext {
773    pub fn new(tx: Sender<Option<ExecutionContextId>>) -> Self {
774        Self {
775            dom_world: DOMWorldKind::Main,
776            frame_id: None,
777            tx,
778        }
779    }
780}
781
782#[derive(Debug)]
783pub struct GetUrl {
784    /// The id of the frame to get the url for (None = main frame)
785    pub frame_id: Option<FrameId>,
786    /// Sender half of the channel to send the response back
787    pub tx: Sender<Option<String>>,
788}
789
790impl GetUrl {
791    pub fn new(tx: Sender<Option<String>>) -> Self {
792        Self { frame_id: None, tx }
793    }
794}
795
796#[derive(Debug)]
797pub struct GetName {
798    /// The id of the frame to get the name for (None = main frame)
799    pub frame_id: Option<FrameId>,
800    /// Sender half of the channel to send the response back
801    pub tx: Sender<Option<String>>,
802}
803
804#[derive(Debug)]
805pub struct GetParent {
806    /// The id of the frame to get the parent for (None = main frame)
807    pub frame_id: FrameId,
808    /// Sender half of the channel to send the response back
809    pub tx: Sender<Option<FrameId>>,
810}
811
812#[derive(Debug)]
813pub enum TargetMessage {
814    /// Execute a command within the session of this target
815    Command(CommandMessage),
816    /// Return the main frame of this target's page
817    MainFrame(Sender<Option<FrameId>>),
818    /// Return all the frames of this target's page
819    AllFrames(Sender<Vec<FrameId>>),
820    /// Return the url if available
821    Url(GetUrl),
822    /// Return the name if available
823    Name(GetName),
824    /// Return the parent id of a frame
825    Parent(GetParent),
826    /// A Message that resolves when the frame finished loading a new url
827    WaitForNavigation(Sender<ArcHttpRequest>),
828    /// A request to submit a new listener that gets notified with every
829    /// received event
830    AddEventListener(EventListenerRequest),
831    /// Get the `ExecutionContext` if available
832    GetExecutionContext(GetExecutionContext),
833    Authenticate(Credentials),
834}