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