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