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