1#![allow(dead_code)]
6#![allow(missing_docs)]
7
8use tauri_runtime::{
9 dpi::{PhysicalPosition, PhysicalSize, Position, Size},
10 monitor::Monitor,
11 webview::{DetachedWebview, PendingWebview},
12 window::{
13 CursorIcon, DetachedWindow, DetachedWindowWebview, PendingWindow, RawWindow, WindowBuilder,
14 WindowBuilderBase, WindowEvent, WindowId,
15 },
16 DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState,
17 Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent,
18 WebviewDispatch, WindowDispatch, WindowEventId,
19};
20
21#[cfg(target_os = "macos")]
22use tauri_utils::TitleBarStyle;
23use tauri_utils::{config::WindowConfig, Theme};
24use url::Url;
25
26#[cfg(windows)]
27use windows::Win32::Foundation::HWND;
28
29use std::{
30 cell::RefCell,
31 collections::HashMap,
32 fmt,
33 sync::{
34 atomic::{AtomicBool, AtomicU32, Ordering},
35 mpsc::{channel, sync_channel, Receiver, SyncSender},
36 Arc, Mutex,
37 },
38};
39
40type ShortcutMap = HashMap<String, Box<dyn Fn() + Send + 'static>>;
41
42enum Message {
43 Task(Box<dyn FnOnce() + Send>),
44 CloseWindow(WindowId),
45 DestroyWindow(WindowId),
46}
47
48struct Webview;
49
50struct Window {
51 label: String,
52 webviews: Vec<Webview>,
53}
54
55#[derive(Clone)]
56pub struct RuntimeContext {
57 is_running: Arc<AtomicBool>,
58 windows: Arc<RefCell<HashMap<WindowId, Window>>>,
59 shortcuts: Arc<Mutex<ShortcutMap>>,
60 run_tx: SyncSender<Message>,
61 next_window_id: Arc<AtomicU32>,
62 next_webview_id: Arc<AtomicU32>,
63 next_window_event_id: Arc<AtomicU32>,
64 next_webview_event_id: Arc<AtomicU32>,
65}
66
67#[allow(clippy::non_send_fields_in_send_ty)]
69unsafe impl Send for RuntimeContext {}
70
71#[allow(clippy::non_send_fields_in_send_ty)]
73unsafe impl Sync for RuntimeContext {}
74
75impl RuntimeContext {
76 fn send_message(&self, message: Message) -> Result<()> {
77 if self.is_running.load(Ordering::Relaxed) {
78 self
79 .run_tx
80 .send(message)
81 .map_err(|_| Error::FailedToSendMessage)
82 } else {
83 match message {
84 Message::Task(task) => task(),
85 Message::CloseWindow(id) | Message::DestroyWindow(id) => {
86 self.windows.borrow_mut().remove(&id);
87 }
88 }
89 Ok(())
90 }
91 }
92
93 fn next_window_id(&self) -> WindowId {
94 self.next_window_id.fetch_add(1, Ordering::Relaxed).into()
95 }
96
97 fn next_webview_id(&self) -> u32 {
98 self.next_webview_id.fetch_add(1, Ordering::Relaxed)
99 }
100
101 fn next_window_event_id(&self) -> WindowEventId {
102 self.next_window_event_id.fetch_add(1, Ordering::Relaxed)
103 }
104
105 fn next_webview_event_id(&self) -> WindowEventId {
106 self.next_webview_event_id.fetch_add(1, Ordering::Relaxed)
107 }
108}
109
110impl fmt::Debug for RuntimeContext {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 f.debug_struct("RuntimeContext").finish()
113 }
114}
115
116#[derive(Debug, Clone)]
117pub struct MockRuntimeHandle {
118 context: RuntimeContext,
119}
120
121impl<T: UserEvent> RuntimeHandle<T> for MockRuntimeHandle {
122 type Runtime = MockRuntime;
123
124 fn create_proxy(&self) -> EventProxy {
125 EventProxy {}
126 }
127
128 #[cfg(target_os = "macos")]
129 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
130 fn set_activation_policy(
131 &self,
132 activation_policy: tauri_runtime::ActivationPolicy,
133 ) -> Result<()> {
134 Ok(())
135 }
136
137 #[cfg(target_os = "macos")]
138 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
139 fn set_dock_visibility(&self, visible: bool) -> Result<()> {
140 Ok(())
141 }
142
143 fn request_exit(&self, code: i32) -> Result<()> {
144 unimplemented!()
145 }
146
147 fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
149 &self,
150 pending: PendingWindow<T, Self::Runtime>,
151 _after_window_creation: Option<F>,
152 ) -> Result<DetachedWindow<T, Self::Runtime>> {
153 let id = self.context.next_window_id();
154
155 let (webview_id, webviews) = if let Some(w) = &pending.webview {
156 (Some(self.context.next_webview_id()), vec![Webview])
157 } else {
158 (None, Vec::new())
159 };
160
161 self.context.windows.borrow_mut().insert(
162 id,
163 Window {
164 label: pending.label.clone(),
165 webviews,
166 },
167 );
168
169 let webview = webview_id.map(|id| DetachedWindowWebview {
170 webview: DetachedWebview {
171 label: pending.label.clone(),
172 dispatcher: MockWebviewDispatcher {
173 id,
174 context: self.context.clone(),
175 url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
176 last_evaluated_script: Default::default(),
177 },
178 },
179 use_https_scheme: false,
180 });
181
182 Ok(DetachedWindow {
183 id,
184 label: pending.label,
185 dispatcher: MockWindowDispatcher {
186 id,
187 context: self.context.clone(),
188 },
189 webview,
190 })
191 }
192
193 fn create_webview(
194 &self,
195 window_id: WindowId,
196 pending: PendingWebview<T, Self::Runtime>,
197 ) -> Result<DetachedWebview<T, Self::Runtime>> {
198 let id = self.context.next_webview_id();
199 let webview = Webview;
200 if let Some(w) = self.context.windows.borrow_mut().get_mut(&window_id) {
201 w.webviews.push(webview);
202 }
203
204 Ok(DetachedWebview {
205 label: pending.label,
206 dispatcher: MockWebviewDispatcher {
207 id,
208 context: self.context.clone(),
209 last_evaluated_script: Default::default(),
210 url: Arc::new(Mutex::new(pending.url)),
211 },
212 })
213 }
214
215 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
217 self.context.send_message(Message::Task(Box::new(f)))
218 }
219
220 fn display_handle(
221 &self,
222 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
223 #[cfg(target_os = "linux")]
224 return Ok(unsafe {
225 raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::Xlib(
226 raw_window_handle::XlibDisplayHandle::new(None, 0),
227 ))
228 });
229 #[cfg(target_os = "macos")]
230 return Ok(unsafe {
231 raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::AppKit(
232 raw_window_handle::AppKitDisplayHandle::new(),
233 ))
234 });
235 #[cfg(windows)]
236 return Ok(unsafe {
237 raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::Windows(
238 raw_window_handle::WindowsDisplayHandle::new(),
239 ))
240 });
241 #[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
242 unimplemented!();
243 }
244
245 fn primary_monitor(&self) -> Option<Monitor> {
246 unimplemented!()
247 }
248
249 fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
250 unimplemented!()
251 }
252
253 fn available_monitors(&self) -> Vec<Monitor> {
254 unimplemented!()
255 }
256
257 fn set_theme(&self, theme: Option<Theme>) {
258 unimplemented!()
259 }
260
261 #[cfg(target_os = "macos")]
263 fn show(&self) -> Result<()> {
264 Ok(())
265 }
266
267 #[cfg(target_os = "macos")]
269 fn hide(&self) -> Result<()> {
270 Ok(())
271 }
272
273 fn set_device_event_filter(&self, _: DeviceEventFilter) {
274 }
276
277 #[cfg(target_os = "android")]
278 fn find_class<'a>(
279 &self,
280 env: &mut jni::JNIEnv<'a>,
281 activity: &jni::objects::JObject<'_>,
282 name: impl Into<String>,
283 ) -> std::result::Result<jni::objects::JClass<'a>, jni::errors::Error> {
284 todo!()
285 }
286
287 #[cfg(target_os = "android")]
288 fn run_on_android_context<F>(&self, f: F)
289 where
290 F: FnOnce(&mut jni::JNIEnv, &jni::objects::JObject, &jni::objects::JObject) + Send + 'static,
291 {
292 todo!()
293 }
294
295 #[cfg(any(target_os = "macos", target_os = "ios"))]
296 fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(
297 &self,
298 cb: F,
299 ) -> Result<()> {
300 todo!()
301 }
302
303 #[cfg(any(target_os = "macos", target_os = "ios"))]
304 fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(
305 &self,
306 uuid: [u8; 16],
307 cb: F,
308 ) -> Result<()> {
309 todo!()
310 }
311
312 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
313 Ok(PhysicalPosition::new(0.0, 0.0))
314 }
315}
316
317#[derive(Debug, Clone)]
318pub struct MockWebviewDispatcher {
319 id: u32,
320 context: RuntimeContext,
321 url: Arc<Mutex<String>>,
322 last_evaluated_script: Arc<Mutex<Option<String>>>,
323}
324
325impl MockWebviewDispatcher {
326 pub fn last_evaluated_script(&self) -> Option<String> {
327 self.last_evaluated_script.lock().unwrap().clone()
328 }
329}
330
331#[derive(Debug, Clone)]
332pub struct MockWindowDispatcher {
333 id: WindowId,
334 context: RuntimeContext,
335}
336
337#[derive(Debug, Clone)]
338pub struct MockWindowBuilder {}
339
340impl WindowBuilderBase for MockWindowBuilder {}
341
342impl WindowBuilder for MockWindowBuilder {
343 fn new() -> Self {
344 Self {}
345 }
346
347 fn with_config(config: &WindowConfig) -> Self {
348 Self {}
349 }
350
351 fn center(self) -> Self {
352 self
353 }
354
355 fn position(self, x: f64, y: f64) -> Self {
356 self
357 }
358
359 fn inner_size(self, min_width: f64, min_height: f64) -> Self {
360 self
361 }
362
363 fn min_inner_size(self, min_width: f64, min_height: f64) -> Self {
364 self
365 }
366
367 fn max_inner_size(self, max_width: f64, max_height: f64) -> Self {
368 self
369 }
370
371 fn inner_size_constraints(
372 self,
373 constraints: tauri_runtime::window::WindowSizeConstraints,
374 ) -> Self {
375 self
376 }
377
378 fn prevent_overflow(self) -> Self {
379 self
380 }
381
382 fn prevent_overflow_with_margin(self, margin: tauri_runtime::dpi::Size) -> Self {
383 self
384 }
385
386 fn resizable(self, resizable: bool) -> Self {
387 self
388 }
389
390 fn maximizable(self, resizable: bool) -> Self {
391 self
392 }
393
394 fn minimizable(self, resizable: bool) -> Self {
395 self
396 }
397
398 fn closable(self, resizable: bool) -> Self {
399 self
400 }
401
402 fn title<S: Into<String>>(self, title: S) -> Self {
403 self
404 }
405
406 fn fullscreen(self, fullscreen: bool) -> Self {
407 self
408 }
409
410 fn focused(self, focused: bool) -> Self {
411 self
412 }
413
414 fn focusable(self, focusable: bool) -> Self {
415 self
416 }
417
418 fn maximized(self, maximized: bool) -> Self {
419 self
420 }
421
422 fn visible(self, visible: bool) -> Self {
423 self
424 }
425
426 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
427 #[cfg_attr(
428 docsrs,
429 doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
430 )]
431 fn transparent(self, transparent: bool) -> Self {
432 self
433 }
434
435 fn decorations(self, decorations: bool) -> Self {
436 self
437 }
438
439 fn always_on_bottom(self, always_on_bottom: bool) -> Self {
440 self
441 }
442
443 fn always_on_top(self, always_on_top: bool) -> Self {
444 self
445 }
446
447 fn visible_on_all_workspaces(self, visible_on_all_workspaces: bool) -> Self {
448 self
449 }
450
451 fn content_protected(self, protected: bool) -> Self {
452 self
453 }
454
455 fn icon(self, icon: Icon<'_>) -> Result<Self> {
456 Ok(self)
457 }
458
459 fn skip_taskbar(self, skip: bool) -> Self {
460 self
461 }
462
463 fn window_classname<S: Into<String>>(self, classname: S) -> Self {
464 self
465 }
466
467 fn shadow(self, enable: bool) -> Self {
468 self
469 }
470
471 #[cfg(windows)]
472 fn owner(self, owner: HWND) -> Self {
473 self
474 }
475
476 #[cfg(windows)]
477 fn parent(self, parent: HWND) -> Self {
478 self
479 }
480
481 #[cfg(target_os = "macos")]
482 fn parent(self, parent: *mut std::ffi::c_void) -> Self {
483 self
484 }
485
486 #[cfg(any(
487 target_os = "linux",
488 target_os = "dragonfly",
489 target_os = "freebsd",
490 target_os = "netbsd",
491 target_os = "openbsd"
492 ))]
493 fn transient_for(self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
494 self
495 }
496
497 #[cfg(windows)]
498 fn drag_and_drop(self, enabled: bool) -> Self {
499 self
500 }
501
502 #[cfg(target_os = "macos")]
503 fn title_bar_style(self, style: TitleBarStyle) -> Self {
504 self
505 }
506
507 #[cfg(target_os = "macos")]
508 fn traffic_light_position<P: Into<Position>>(self, position: P) -> Self {
509 self
510 }
511
512 #[cfg(target_os = "macos")]
513 fn hidden_title(self, transparent: bool) -> Self {
514 self
515 }
516
517 #[cfg(target_os = "macos")]
518 fn tabbing_identifier(self, identifier: &str) -> Self {
519 self
520 }
521
522 fn theme(self, theme: Option<Theme>) -> Self {
523 self
524 }
525
526 fn has_icon(&self) -> bool {
527 false
528 }
529
530 fn get_theme(&self) -> Option<Theme> {
531 None
532 }
533
534 fn background_color(self, _color: tauri_utils::config::Color) -> Self {
535 self
536 }
537
538 #[cfg(target_os = "android")]
539 fn activity_name<S: Into<String>>(self, _class_name: S) -> Self {
540 self
541 }
542
543 #[cfg(target_os = "android")]
544 fn created_by_activity_name<S: Into<String>>(self, _class_name: S) -> Self {
545 self
546 }
547
548 #[cfg(target_os = "ios")]
549 fn requested_by_scene_identifier<S: Into<String>>(self, _identifier: S) -> Self {
550 self
551 }
552}
553
554impl<T: UserEvent> WebviewDispatch<T> for MockWebviewDispatcher {
555 type Runtime = MockRuntime;
556
557 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
558 self.context.send_message(Message::Task(Box::new(f)))
559 }
560
561 fn on_webview_event<F: Fn(&tauri_runtime::window::WebviewEvent) + Send + 'static>(
562 &self,
563 f: F,
564 ) -> tauri_runtime::WebviewEventId {
565 self.context.next_window_event_id()
566 }
567
568 fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()> {
569 Ok(())
570 }
571
572 #[cfg(any(debug_assertions, feature = "devtools"))]
573 fn open_devtools(&self) {}
574
575 #[cfg(any(debug_assertions, feature = "devtools"))]
576 fn close_devtools(&self) {}
577
578 #[cfg(any(debug_assertions, feature = "devtools"))]
579 fn is_devtools_open(&self) -> Result<bool> {
580 Ok(false)
581 }
582
583 fn set_zoom(&self, scale_factor: f64) -> Result<()> {
584 Ok(())
585 }
586
587 fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
588 self
589 .last_evaluated_script
590 .lock()
591 .unwrap()
592 .replace(script.into());
593 Ok(())
594 }
595
596 fn eval_script_with_callback<S: Into<String>>(
597 &self,
598 script: S,
599 callback: impl Fn(String) + Send + 'static,
600 ) -> Result<()> {
601 self
602 .last_evaluated_script
603 .lock()
604 .unwrap()
605 .replace(script.into());
606 Ok(())
607 }
608
609 fn url(&self) -> Result<String> {
610 Ok(self.url.lock().unwrap().clone())
611 }
612
613 fn bounds(&self) -> Result<tauri_runtime::dpi::Rect> {
614 Ok(tauri_runtime::dpi::Rect::default())
615 }
616
617 fn position(&self) -> Result<PhysicalPosition<i32>> {
618 Ok(PhysicalPosition { x: 0, y: 0 })
619 }
620
621 fn size(&self) -> Result<PhysicalSize<u32>> {
622 Ok(PhysicalSize {
623 width: 0,
624 height: 0,
625 })
626 }
627
628 fn navigate(&self, url: Url) -> Result<()> {
629 *self.url.lock().unwrap() = url.to_string();
630 Ok(())
631 }
632
633 fn reload(&self) -> Result<()> {
634 Ok(())
635 }
636
637 fn print(&self) -> Result<()> {
638 Ok(())
639 }
640
641 fn close(&self) -> Result<()> {
642 Ok(())
643 }
644
645 fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> Result<()> {
646 Ok(())
647 }
648
649 fn set_size(&self, _size: Size) -> Result<()> {
650 Ok(())
651 }
652
653 fn set_position(&self, _position: Position) -> Result<()> {
654 Ok(())
655 }
656
657 fn set_focus(&self) -> Result<()> {
658 Ok(())
659 }
660
661 fn reparent(&self, window_id: WindowId) -> Result<()> {
662 Ok(())
663 }
664
665 fn cookies(&self) -> Result<Vec<tauri_runtime::Cookie<'static>>> {
666 Ok(Vec::new())
667 }
668
669 fn cookies_for_url(&self, url: Url) -> Result<Vec<tauri_runtime::Cookie<'static>>> {
670 Ok(Vec::new())
671 }
672
673 fn set_cookie(&self, cookie: tauri_runtime::Cookie<'_>) -> Result<()> {
674 Ok(())
675 }
676
677 fn delete_cookie(&self, cookie: tauri_runtime::Cookie<'_>) -> Result<()> {
678 Ok(())
679 }
680
681 fn set_auto_resize(&self, auto_resize: bool) -> Result<()> {
682 Ok(())
683 }
684
685 fn clear_all_browsing_data(&self) -> Result<()> {
686 Ok(())
687 }
688
689 fn hide(&self) -> Result<()> {
690 Ok(())
691 }
692
693 fn show(&self) -> Result<()> {
694 Ok(())
695 }
696
697 fn set_background_color(&self, color: Option<tauri_utils::config::Color>) -> Result<()> {
698 Ok(())
699 }
700}
701
702impl<T: UserEvent> WindowDispatch<T> for MockWindowDispatcher {
703 type Runtime = MockRuntime;
704
705 type WindowBuilder = MockWindowBuilder;
706
707 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
708 self.context.send_message(Message::Task(Box::new(f)))
709 }
710
711 fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
712 self.context.next_window_event_id()
713 }
714
715 fn scale_factor(&self) -> Result<f64> {
716 Ok(1.0)
717 }
718
719 fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
720 Ok(PhysicalPosition { x: 0, y: 0 })
721 }
722
723 fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
724 Ok(PhysicalPosition { x: 0, y: 0 })
725 }
726
727 fn inner_size(&self) -> Result<PhysicalSize<u32>> {
728 Ok(PhysicalSize {
729 width: 0,
730 height: 0,
731 })
732 }
733
734 fn outer_size(&self) -> Result<PhysicalSize<u32>> {
735 Ok(PhysicalSize {
736 width: 0,
737 height: 0,
738 })
739 }
740
741 fn is_fullscreen(&self) -> Result<bool> {
742 Ok(false)
743 }
744
745 fn is_minimized(&self) -> Result<bool> {
746 Ok(false)
747 }
748
749 fn is_maximized(&self) -> Result<bool> {
750 Ok(false)
751 }
752
753 fn is_focused(&self) -> Result<bool> {
754 Ok(false)
755 }
756
757 fn is_decorated(&self) -> Result<bool> {
758 Ok(false)
759 }
760
761 fn is_resizable(&self) -> Result<bool> {
762 Ok(false)
763 }
764
765 fn is_maximizable(&self) -> Result<bool> {
766 Ok(true)
767 }
768
769 fn is_minimizable(&self) -> Result<bool> {
770 Ok(true)
771 }
772
773 fn is_closable(&self) -> Result<bool> {
774 Ok(true)
775 }
776
777 fn is_visible(&self) -> Result<bool> {
778 Ok(true)
779 }
780
781 fn title(&self) -> Result<String> {
782 Ok(String::new())
783 }
784
785 fn current_monitor(&self) -> Result<Option<Monitor>> {
786 Ok(None)
787 }
788
789 fn primary_monitor(&self) -> Result<Option<Monitor>> {
790 Ok(None)
791 }
792
793 fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
794 Ok(None)
795 }
796
797 fn available_monitors(&self) -> Result<Vec<Monitor>> {
798 Ok(Vec::new())
799 }
800
801 fn theme(&self) -> Result<Theme> {
802 Ok(Theme::Light)
803 }
804
805 #[cfg(any(
806 target_os = "linux",
807 target_os = "dragonfly",
808 target_os = "freebsd",
809 target_os = "netbsd",
810 target_os = "openbsd"
811 ))]
812 fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
813 unimplemented!()
814 }
815
816 #[cfg(any(
817 target_os = "linux",
818 target_os = "dragonfly",
819 target_os = "freebsd",
820 target_os = "netbsd",
821 target_os = "openbsd"
822 ))]
823 fn default_vbox(&self) -> Result<gtk::Box> {
824 unimplemented!()
825 }
826
827 #[cfg(target_os = "android")]
828 fn activity_name(&self) -> Result<String> {
829 unimplemented!()
830 }
831
832 #[cfg(target_os = "ios")]
833 fn scene_identifier(&self) -> Result<String> {
834 unimplemented!()
835 }
836
837 fn window_handle(
838 &self,
839 ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
840 #[cfg(target_os = "linux")]
841 return unsafe {
842 Ok(raw_window_handle::WindowHandle::borrow_raw(
843 raw_window_handle::RawWindowHandle::Xlib(raw_window_handle::XlibWindowHandle::new(0)),
844 ))
845 };
846 #[cfg(target_os = "macos")]
847 return unsafe {
848 Ok(raw_window_handle::WindowHandle::borrow_raw(
849 raw_window_handle::RawWindowHandle::AppKit(raw_window_handle::AppKitWindowHandle::new(
850 std::ptr::NonNull::from(&()).cast(),
851 )),
852 ))
853 };
854 #[cfg(windows)]
855 return unsafe {
856 Ok(raw_window_handle::WindowHandle::borrow_raw(
857 raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::new(
858 std::num::NonZeroIsize::MIN,
859 )),
860 ))
861 };
862 #[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
863 unimplemented!();
864 }
865
866 fn center(&self) -> Result<()> {
867 Ok(())
868 }
869
870 fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
871 Ok(())
872 }
873
874 fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
875 &mut self,
876 pending: PendingWindow<T, Self::Runtime>,
877 _after_window_creation: Option<F>,
878 ) -> Result<DetachedWindow<T, Self::Runtime>> {
879 let id = self.context.next_window_id();
880
881 let (webview_id, webviews) = if let Some(w) = &pending.webview {
882 (Some(self.context.next_webview_id()), vec![Webview])
883 } else {
884 (None, Vec::new())
885 };
886
887 self.context.windows.borrow_mut().insert(
888 id,
889 Window {
890 label: pending.label.clone(),
891 webviews,
892 },
893 );
894
895 let webview = webview_id.map(|id| DetachedWindowWebview {
896 webview: DetachedWebview {
897 label: pending.label.clone(),
898 dispatcher: MockWebviewDispatcher {
899 id,
900 context: self.context.clone(),
901 url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
902 last_evaluated_script: Default::default(),
903 },
904 },
905 use_https_scheme: false,
906 });
907
908 Ok(DetachedWindow {
909 id,
910 label: pending.label,
911 dispatcher: MockWindowDispatcher {
912 id,
913 context: self.context.clone(),
914 },
915 webview,
916 })
917 }
918
919 fn create_webview(
920 &mut self,
921 pending: PendingWebview<T, Self::Runtime>,
922 ) -> Result<DetachedWebview<T, Self::Runtime>> {
923 let id = self.context.next_webview_id();
924 let webview = Webview;
925 if let Some(w) = self.context.windows.borrow_mut().get_mut(&self.id) {
926 w.webviews.push(webview);
927 }
928
929 Ok(DetachedWebview {
930 label: pending.label,
931 dispatcher: MockWebviewDispatcher {
932 id,
933 context: self.context.clone(),
934 last_evaluated_script: Default::default(),
935 url: Arc::new(Mutex::new(pending.url)),
936 },
937 })
938 }
939
940 fn set_resizable(&self, resizable: bool) -> Result<()> {
941 Ok(())
942 }
943
944 fn set_maximizable(&self, maximizable: bool) -> Result<()> {
945 Ok(())
946 }
947
948 fn set_minimizable(&self, minimizable: bool) -> Result<()> {
949 Ok(())
950 }
951
952 fn set_closable(&self, closable: bool) -> Result<()> {
953 Ok(())
954 }
955
956 fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
957 Ok(())
958 }
959
960 fn maximize(&self) -> Result<()> {
961 Ok(())
962 }
963
964 fn unmaximize(&self) -> Result<()> {
965 Ok(())
966 }
967
968 fn minimize(&self) -> Result<()> {
969 Ok(())
970 }
971
972 fn unminimize(&self) -> Result<()> {
973 Ok(())
974 }
975
976 fn show(&self) -> Result<()> {
977 Ok(())
978 }
979
980 fn hide(&self) -> Result<()> {
981 Ok(())
982 }
983
984 fn close(&self) -> Result<()> {
985 self.context.send_message(Message::CloseWindow(self.id))?;
986 Ok(())
987 }
988
989 fn destroy(&self) -> Result<()> {
990 self.context.send_message(Message::DestroyWindow(self.id))?;
991 Ok(())
992 }
993
994 fn set_decorations(&self, decorations: bool) -> Result<()> {
995 Ok(())
996 }
997
998 fn set_shadow(&self, shadow: bool) -> Result<()> {
999 Ok(())
1000 }
1001
1002 fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> {
1003 Ok(())
1004 }
1005
1006 fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
1007 Ok(())
1008 }
1009
1010 fn set_visible_on_all_workspaces(&self, visible_on_all_workspaces: bool) -> Result<()> {
1011 Ok(())
1012 }
1013
1014 fn set_content_protected(&self, protected: bool) -> Result<()> {
1015 Ok(())
1016 }
1017
1018 fn set_size(&self, size: Size) -> Result<()> {
1019 Ok(())
1020 }
1021
1022 fn set_min_size(&self, size: Option<Size>) -> Result<()> {
1023 Ok(())
1024 }
1025
1026 fn set_max_size(&self, size: Option<Size>) -> Result<()> {
1027 Ok(())
1028 }
1029
1030 fn set_position(&self, position: Position) -> Result<()> {
1031 Ok(())
1032 }
1033
1034 fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
1035 Ok(())
1036 }
1037
1038 #[cfg(target_os = "macos")]
1039 fn set_simple_fullscreen(&self, enable: bool) -> Result<()> {
1040 Ok(())
1041 }
1042
1043 fn set_focus(&self) -> Result<()> {
1044 Ok(())
1045 }
1046
1047 fn set_focusable(&self, focusable: bool) -> Result<()> {
1048 Ok(())
1049 }
1050
1051 fn set_icon(&self, icon: Icon<'_>) -> Result<()> {
1052 Ok(())
1053 }
1054
1055 fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
1056 Ok(())
1057 }
1058
1059 fn set_cursor_grab(&self, grab: bool) -> Result<()> {
1060 Ok(())
1061 }
1062
1063 fn set_cursor_visible(&self, visible: bool) -> Result<()> {
1064 Ok(())
1065 }
1066
1067 fn set_cursor_icon(&self, icon: CursorIcon) -> Result<()> {
1068 Ok(())
1069 }
1070
1071 fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> Result<()> {
1072 Ok(())
1073 }
1074
1075 fn set_ignore_cursor_events(&self, ignore: bool) -> Result<()> {
1076 Ok(())
1077 }
1078
1079 fn start_dragging(&self) -> Result<()> {
1080 Ok(())
1081 }
1082
1083 fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> {
1084 Ok(())
1085 }
1086
1087 fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> {
1088 Ok(())
1089 }
1090
1091 fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
1092 Ok(())
1093 }
1094
1095 fn set_badge_label(&self, label: Option<String>) -> Result<()> {
1096 Ok(())
1097 }
1098
1099 fn set_overlay_icon(&self, icon: Option<Icon<'_>>) -> Result<()> {
1100 Ok(())
1101 }
1102
1103 fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
1104 Ok(())
1105 }
1106
1107 fn set_traffic_light_position(&self, position: Position) -> Result<()> {
1108 Ok(())
1109 }
1110
1111 fn set_size_constraints(
1112 &self,
1113 constraints: tauri_runtime::window::WindowSizeConstraints,
1114 ) -> Result<()> {
1115 Ok(())
1116 }
1117
1118 fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
1119 Ok(())
1120 }
1121
1122 fn set_enabled(&self, enabled: bool) -> Result<()> {
1123 Ok(())
1124 }
1125
1126 fn is_enabled(&self) -> Result<bool> {
1127 Ok(true)
1128 }
1129
1130 fn is_always_on_top(&self) -> Result<bool> {
1131 Ok(false)
1132 }
1133
1134 fn set_background_color(&self, color: Option<tauri_utils::config::Color>) -> Result<()> {
1135 Ok(())
1136 }
1137}
1138
1139#[derive(Debug, Clone)]
1140pub struct EventProxy {}
1141
1142impl<T: UserEvent> EventLoopProxy<T> for EventProxy {
1143 fn send_event(&self, event: T) -> Result<()> {
1144 Ok(())
1145 }
1146}
1147
1148#[derive(Debug)]
1149pub struct MockRuntime {
1150 is_running: Arc<AtomicBool>,
1151 pub context: RuntimeContext,
1152 run_rx: Receiver<Message>,
1153}
1154
1155impl MockRuntime {
1156 fn init() -> Self {
1157 let is_running = Arc::new(AtomicBool::new(false));
1158 let (tx, rx) = sync_channel(256);
1159 let context = RuntimeContext {
1160 is_running: is_running.clone(),
1161 windows: Default::default(),
1162 shortcuts: Default::default(),
1163 run_tx: tx,
1164 next_window_id: Default::default(),
1165 next_webview_id: Default::default(),
1166 next_window_event_id: Default::default(),
1167 next_webview_event_id: Default::default(),
1168 };
1169 Self {
1170 is_running,
1171 context,
1172 run_rx: rx,
1173 }
1174 }
1175}
1176
1177impl<T: UserEvent> Runtime<T> for MockRuntime {
1178 type WindowDispatcher = MockWindowDispatcher;
1179 type WebviewDispatcher = MockWebviewDispatcher;
1180 type Handle = MockRuntimeHandle;
1181 type EventLoopProxy = EventProxy;
1182
1183 fn new(_args: RuntimeInitArgs) -> Result<Self> {
1184 Ok(Self::init())
1185 }
1186
1187 #[cfg(any(
1188 windows,
1189 target_os = "linux",
1190 target_os = "dragonfly",
1191 target_os = "freebsd",
1192 target_os = "netbsd",
1193 target_os = "openbsd"
1194 ))]
1195 fn new_any_thread(_args: RuntimeInitArgs) -> Result<Self> {
1196 Ok(Self::init())
1197 }
1198
1199 fn create_proxy(&self) -> EventProxy {
1200 EventProxy {}
1201 }
1202
1203 fn handle(&self) -> Self::Handle {
1204 MockRuntimeHandle {
1205 context: self.context.clone(),
1206 }
1207 }
1208
1209 fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
1210 &self,
1211 pending: PendingWindow<T, Self>,
1212 _after_window_creation: Option<F>,
1213 ) -> Result<DetachedWindow<T, Self>> {
1214 let id = self.context.next_window_id();
1215
1216 let (webview_id, webviews) = if let Some(w) = &pending.webview {
1217 (Some(self.context.next_webview_id()), vec![Webview])
1218 } else {
1219 (None, Vec::new())
1220 };
1221
1222 self.context.windows.borrow_mut().insert(
1223 id,
1224 Window {
1225 label: pending.label.clone(),
1226 webviews,
1227 },
1228 );
1229
1230 let webview = webview_id.map(|id| DetachedWindowWebview {
1231 webview: DetachedWebview {
1232 label: pending.label.clone(),
1233 dispatcher: MockWebviewDispatcher {
1234 id,
1235 context: self.context.clone(),
1236 url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
1237 last_evaluated_script: Default::default(),
1238 },
1239 },
1240 use_https_scheme: false,
1241 });
1242
1243 Ok(DetachedWindow {
1244 id,
1245 label: pending.label,
1246 dispatcher: MockWindowDispatcher {
1247 id,
1248 context: self.context.clone(),
1249 },
1250 webview,
1251 })
1252 }
1253
1254 fn create_webview(
1255 &self,
1256 window_id: WindowId,
1257 pending: PendingWebview<T, Self>,
1258 ) -> Result<DetachedWebview<T, Self>> {
1259 let id = self.context.next_webview_id();
1260 let webview = Webview;
1261 if let Some(w) = self.context.windows.borrow_mut().get_mut(&window_id) {
1262 w.webviews.push(webview);
1263 }
1264
1265 Ok(DetachedWebview {
1266 label: pending.label,
1267 dispatcher: MockWebviewDispatcher {
1268 id,
1269 context: self.context.clone(),
1270 last_evaluated_script: Default::default(),
1271 url: Arc::new(Mutex::new(pending.url)),
1272 },
1273 })
1274 }
1275
1276 fn primary_monitor(&self) -> Option<Monitor> {
1277 unimplemented!()
1278 }
1279
1280 fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
1281 unimplemented!()
1282 }
1283
1284 fn available_monitors(&self) -> Vec<Monitor> {
1285 unimplemented!()
1286 }
1287
1288 fn set_theme(&self, theme: Option<Theme>) {
1289 unimplemented!()
1290 }
1291
1292 #[cfg(target_os = "macos")]
1293 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1294 fn set_activation_policy(&mut self, activation_policy: tauri_runtime::ActivationPolicy) {}
1295
1296 #[cfg(target_os = "macos")]
1297 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1298 fn set_dock_visibility(&mut self, visible: bool) {}
1299
1300 #[cfg(target_os = "macos")]
1301 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1302 fn show(&self) {}
1303
1304 #[cfg(target_os = "macos")]
1305 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
1306 fn hide(&self) {}
1307
1308 fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {}
1309
1310 #[cfg(any(
1311 target_os = "macos",
1312 windows,
1313 target_os = "linux",
1314 target_os = "dragonfly",
1315 target_os = "freebsd",
1316 target_os = "netbsd",
1317 target_os = "openbsd"
1318 ))]
1319 fn run_iteration<F: FnMut(RunEvent<T>)>(&mut self, callback: F) {}
1320
1321 fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32 {
1322 self.run(callback);
1323
1324 0
1325 }
1326
1327 fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) {
1328 self.is_running.store(true, Ordering::Relaxed);
1329 callback(RunEvent::Ready);
1330
1331 loop {
1332 if let Ok(m) = self.run_rx.try_recv() {
1333 match m {
1334 Message::Task(p) => p(),
1335 Message::CloseWindow(id) => {
1336 let label = self
1337 .context
1338 .windows
1339 .borrow()
1340 .get(&id)
1341 .map(|w| w.label.clone());
1342 if let Some(label) = label {
1343 let (tx, rx) = channel();
1344 callback(RunEvent::WindowEvent {
1345 label,
1346 event: WindowEvent::CloseRequested { signal_tx: tx },
1347 });
1348
1349 let should_prevent = matches!(rx.try_recv(), Ok(true));
1350 if !should_prevent {
1351 self.context.windows.borrow_mut().remove(&id);
1352
1353 let is_empty = self.context.windows.borrow().is_empty();
1354 if is_empty {
1355 let (tx, rx) = channel();
1356 callback(RunEvent::ExitRequested { code: None, tx });
1357
1358 let recv = rx.try_recv();
1359 let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
1360
1361 if !should_prevent {
1362 break;
1363 }
1364 }
1365 }
1366 }
1367 }
1368 Message::DestroyWindow(id) => {
1369 let removed = self.context.windows.borrow_mut().remove(&id).is_some();
1370 if removed {
1371 let is_empty = self.context.windows.borrow().is_empty();
1372 if is_empty {
1373 let (tx, rx) = channel();
1374 callback(RunEvent::ExitRequested { code: None, tx });
1375
1376 let recv = rx.try_recv();
1377 let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
1378
1379 if !should_prevent {
1380 break;
1381 }
1382 }
1383 }
1384 }
1385 }
1386 }
1387
1388 callback(RunEvent::MainEventsCleared);
1389
1390 std::thread::sleep(std::time::Duration::from_secs(1));
1391 }
1392
1393 callback(RunEvent::Exit);
1394 }
1395
1396 fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
1397 Ok(PhysicalPosition::new(0.0, 0.0))
1398 }
1399}