tauri_runtime_wry/
lib.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! The [`wry`] Tauri [`Runtime`].
6//!
7//! None of the exposed API of this crate is stable, and it may break semver
8//! compatibility in the future. The major version only signifies the intended Tauri version.
9
10#![doc(
11  html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
12  html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
13)]
14
15use self::monitor::MonitorExt;
16use http::Request;
17#[cfg(target_os = "macos")]
18use objc2::ClassType;
19use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
20
21#[cfg(windows)]
22use tauri_runtime::webview::ScrollBarStyle;
23use tauri_runtime::{
24  dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
25  monitor::Monitor,
26  webview::{DetachedWebview, DownloadEvent, PendingWebview, WebviewIpcHandler},
27  window::{
28    CursorIcon, DetachedWindow, DetachedWindowWebview, DragDropEvent, PendingWindow, RawWindow,
29    WebviewEvent, WindowBuilder, WindowBuilderBase, WindowEvent, WindowId, WindowSizeConstraints,
30  },
31  Cookie, DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon,
32  ProgressBarState, ProgressBarStatus, Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs,
33  UserAttentionType, UserEvent, WebviewDispatch, WebviewEventId, WindowDispatch, WindowEventId,
34};
35
36#[cfg(target_vendor = "apple")]
37use objc2::rc::Retained;
38#[cfg(target_os = "macos")]
39use tao::platform::macos::{EventLoopWindowTargetExtMacOS, WindowBuilderExtMacOS};
40#[cfg(target_os = "linux")]
41use tao::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
42#[cfg(windows)]
43use tao::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
44#[cfg(windows)]
45use webview2_com::{ContainsFullScreenElementChangedEventHandler, FocusChangedEventHandler};
46#[cfg(windows)]
47use windows::Win32::Foundation::HWND;
48#[cfg(target_os = "ios")]
49use wry::WebViewBuilderExtIos;
50#[cfg(target_os = "macos")]
51use wry::WebViewBuilderExtMacos;
52#[cfg(windows)]
53use wry::WebViewBuilderExtWindows;
54#[cfg(target_vendor = "apple")]
55use wry::{WebViewBuilderExtDarwin, WebViewExtDarwin};
56
57use tao::{
58  dpi::{
59    LogicalPosition as TaoLogicalPosition, LogicalSize as TaoLogicalSize,
60    PhysicalPosition as TaoPhysicalPosition, PhysicalSize as TaoPhysicalSize,
61    Position as TaoPosition, Size as TaoSize,
62  },
63  event::{Event, StartCause, WindowEvent as TaoWindowEvent},
64  event_loop::{
65    ControlFlow, DeviceEventFilter as TaoDeviceEventFilter, EventLoop, EventLoopBuilder,
66    EventLoopProxy as TaoEventLoopProxy, EventLoopWindowTarget,
67  },
68  monitor::MonitorHandle,
69  window::{
70    CursorIcon as TaoCursorIcon, Fullscreen, Icon as TaoWindowIcon,
71    ProgressBarState as TaoProgressBarState, ProgressState as TaoProgressState, Theme as TaoTheme,
72    UserAttentionType as TaoUserAttentionType,
73  },
74};
75#[cfg(desktop)]
76use tauri_utils::config::PreventOverflowConfig;
77#[cfg(target_os = "macos")]
78use tauri_utils::TitleBarStyle;
79use tauri_utils::{
80  config::{Color, WindowConfig},
81  Theme,
82};
83use url::Url;
84#[cfg(windows)]
85use wry::ScrollBarStyle as WryScrollBarStyle;
86use wry::{
87  DragDropEvent as WryDragDropEvent, ProxyConfig, ProxyEndpoint, WebContext as WryWebContext,
88  WebView, WebViewBuilder,
89};
90
91pub use tao;
92pub use tao::window::{Window, WindowBuilder as TaoWindowBuilder, WindowId as TaoWindowId};
93pub use wry;
94pub use wry::webview_version;
95
96#[cfg(windows)]
97use wry::WebViewExtWindows;
98#[cfg(target_os = "android")]
99use wry::{
100  prelude::{dispatch, find_class},
101  WebViewBuilderExtAndroid, WebViewExtAndroid,
102};
103#[cfg(not(any(
104  target_os = "windows",
105  target_os = "macos",
106  target_os = "ios",
107  target_os = "android"
108)))]
109use wry::{WebViewBuilderExtUnix, WebViewExtUnix};
110
111#[cfg(target_os = "ios")]
112pub use tao::platform::ios::WindowExtIOS;
113#[cfg(target_os = "macos")]
114pub use tao::platform::macos::{
115  ActivationPolicy as TaoActivationPolicy, EventLoopExtMacOS, WindowExtMacOS,
116};
117#[cfg(target_os = "macos")]
118use tauri_runtime::ActivationPolicy;
119
120use std::{
121  cell::RefCell,
122  collections::{
123    hash_map::Entry::{Occupied, Vacant},
124    BTreeMap, HashMap, HashSet,
125  },
126  fmt,
127  ops::Deref,
128  path::PathBuf,
129  rc::Rc,
130  sync::{
131    atomic::{AtomicBool, AtomicU32, Ordering},
132    mpsc::{channel, Sender},
133    Arc, Mutex, Weak,
134  },
135  thread::{current as current_thread, ThreadId},
136};
137
138pub type WebviewId = u32;
139type IpcHandler = dyn Fn(Request<String>) + 'static;
140
141#[cfg(not(debug_assertions))]
142mod dialog;
143mod monitor;
144#[cfg(any(
145  windows,
146  target_os = "linux",
147  target_os = "dragonfly",
148  target_os = "freebsd",
149  target_os = "netbsd",
150  target_os = "openbsd"
151))]
152mod undecorated_resizing;
153mod util;
154mod webview;
155mod window;
156
157pub use webview::Webview;
158use window::WindowExt as _;
159
160#[derive(Debug)]
161pub struct WebContext {
162  pub inner: WryWebContext,
163  pub referenced_by_webviews: HashSet<String>,
164  // on Linux the custom protocols are associated with the context
165  // and you cannot register a URI scheme more than once
166  pub registered_custom_protocols: HashSet<String>,
167}
168
169pub type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
170// window
171pub type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
172pub type WindowEventListeners = Arc<Mutex<HashMap<WindowEventId, WindowEventHandler>>>;
173pub type WebviewEventHandler = Box<dyn Fn(&WebviewEvent) + Send>;
174pub type WebviewEventListeners = Arc<Mutex<HashMap<WebviewEventId, WebviewEventHandler>>>;
175
176#[derive(Debug, Clone, Default)]
177pub struct WindowIdStore(Arc<Mutex<HashMap<TaoWindowId, WindowId>>>);
178
179impl WindowIdStore {
180  pub fn insert(&self, w: TaoWindowId, id: WindowId) {
181    self.0.lock().unwrap().insert(w, id);
182  }
183
184  pub fn get(&self, w: &TaoWindowId) -> Option<WindowId> {
185    self.0.lock().unwrap().get(w).copied()
186  }
187}
188
189#[macro_export]
190macro_rules! getter {
191  ($self: ident, $rx: expr, $message: expr) => {{
192    $crate::send_user_message(&$self.context, $message)?;
193    $rx
194      .recv()
195      .map_err(|_| $crate::Error::FailedToReceiveMessage)
196  }};
197}
198
199macro_rules! window_getter {
200  ($self: ident, $message: expr) => {{
201    let (tx, rx) = channel();
202    getter!($self, rx, Message::Window($self.window_id, $message(tx)))
203  }};
204}
205
206macro_rules! event_loop_window_getter {
207  ($self: ident, $message: expr) => {{
208    let (tx, rx) = channel();
209    getter!($self, rx, Message::EventLoopWindowTarget($message(tx)))
210  }};
211}
212
213macro_rules! webview_getter {
214  ($self: ident, $message: expr) => {{
215    let (tx, rx) = channel();
216    getter!(
217      $self,
218      rx,
219      Message::Webview(
220        *$self.window_id.lock().unwrap(),
221        $self.webview_id,
222        $message(tx)
223      )
224    )
225  }};
226}
227
228pub(crate) fn send_user_message<T: UserEvent>(
229  context: &Context<T>,
230  message: Message<T>,
231) -> Result<()> {
232  if current_thread().id() == context.main_thread_id {
233    handle_user_message(
234      &context.main_thread.window_target,
235      message,
236      UserMessageContext {
237        window_id_map: context.window_id_map.clone(),
238        windows: context.main_thread.windows.clone(),
239      },
240    );
241    Ok(())
242  } else {
243    context
244      .proxy
245      .send_event(message)
246      .map_err(|_| Error::FailedToSendMessage)
247  }
248}
249
250#[derive(Clone)]
251pub struct Context<T: UserEvent> {
252  pub window_id_map: WindowIdStore,
253  main_thread_id: ThreadId,
254  pub proxy: TaoEventLoopProxy<Message<T>>,
255  main_thread: DispatcherMainThreadContext<T>,
256  plugins: Arc<Mutex<Vec<Box<dyn Plugin<T> + Send>>>>,
257  next_window_id: Arc<AtomicU32>,
258  next_webview_id: Arc<AtomicU32>,
259  next_window_event_id: Arc<AtomicU32>,
260  next_webview_event_id: Arc<AtomicU32>,
261  webview_runtime_installed: bool,
262}
263
264impl<T: UserEvent> Context<T> {
265  pub fn run_threaded<R, F>(&self, f: F) -> R
266  where
267    F: FnOnce(Option<&DispatcherMainThreadContext<T>>) -> R,
268  {
269    f(if current_thread().id() == self.main_thread_id {
270      Some(&self.main_thread)
271    } else {
272      None
273    })
274  }
275
276  fn next_window_id(&self) -> WindowId {
277    self.next_window_id.fetch_add(1, Ordering::Relaxed).into()
278  }
279
280  fn next_webview_id(&self) -> WebviewId {
281    self.next_webview_id.fetch_add(1, Ordering::Relaxed)
282  }
283
284  fn next_window_event_id(&self) -> u32 {
285    self.next_window_event_id.fetch_add(1, Ordering::Relaxed)
286  }
287
288  fn next_webview_event_id(&self) -> u32 {
289    self.next_webview_event_id.fetch_add(1, Ordering::Relaxed)
290  }
291}
292
293impl<T: UserEvent> Context<T> {
294  fn create_window<F: Fn(RawWindow) + Send + 'static>(
295    &self,
296    pending: PendingWindow<T, Wry<T>>,
297    after_window_creation: Option<F>,
298  ) -> Result<DetachedWindow<T, Wry<T>>> {
299    let label = pending.label.clone();
300    let context = self.clone();
301    let window_id = self.next_window_id();
302    let (webview_id, use_https_scheme) = pending
303      .webview
304      .as_ref()
305      .map(|w| {
306        (
307          Some(context.next_webview_id()),
308          w.webview_attributes.use_https_scheme,
309        )
310      })
311      .unwrap_or((None, false));
312
313    send_user_message(
314      self,
315      Message::CreateWindow(
316        window_id,
317        Box::new(move |event_loop| {
318          create_window(
319            window_id,
320            webview_id.unwrap_or_default(),
321            event_loop,
322            &context,
323            pending,
324            after_window_creation,
325          )
326        }),
327      ),
328    )?;
329
330    let dispatcher = WryWindowDispatcher {
331      window_id,
332      context: self.clone(),
333    };
334
335    let detached_webview = webview_id.map(|id| {
336      let webview = DetachedWebview {
337        label: label.clone(),
338        dispatcher: WryWebviewDispatcher {
339          window_id: Arc::new(Mutex::new(window_id)),
340          webview_id: id,
341          context: self.clone(),
342        },
343      };
344      DetachedWindowWebview {
345        webview,
346        use_https_scheme,
347      }
348    });
349
350    Ok(DetachedWindow {
351      id: window_id,
352      label,
353      dispatcher,
354      webview: detached_webview,
355    })
356  }
357
358  fn create_webview(
359    &self,
360    window_id: WindowId,
361    pending: PendingWebview<T, Wry<T>>,
362  ) -> Result<DetachedWebview<T, Wry<T>>> {
363    let label = pending.label.clone();
364    let context = self.clone();
365
366    let webview_id = self.next_webview_id();
367
368    let window_id_wrapper = Arc::new(Mutex::new(window_id));
369    let window_id_wrapper_ = window_id_wrapper.clone();
370
371    send_user_message(
372      self,
373      Message::CreateWebview(
374        window_id,
375        Box::new(move |window, options| {
376          create_webview(
377            WebviewKind::WindowChild,
378            window,
379            window_id_wrapper_,
380            webview_id,
381            &context,
382            pending,
383            options.focused_webview,
384          )
385        }),
386      ),
387    )?;
388
389    let dispatcher = WryWebviewDispatcher {
390      window_id: window_id_wrapper,
391      webview_id,
392      context: self.clone(),
393    };
394
395    Ok(DetachedWebview { label, dispatcher })
396  }
397}
398
399#[cfg(feature = "tracing")]
400#[derive(Debug, Clone, Default)]
401pub struct ActiveTraceSpanStore(Rc<RefCell<Vec<ActiveTracingSpan>>>);
402
403#[cfg(feature = "tracing")]
404impl ActiveTraceSpanStore {
405  pub fn remove_window_draw(&self) {
406    self
407      .0
408      .borrow_mut()
409      .retain(|t| !matches!(t, ActiveTracingSpan::WindowDraw { id: _, span: _ }));
410  }
411}
412
413#[cfg(feature = "tracing")]
414#[derive(Debug)]
415pub enum ActiveTracingSpan {
416  WindowDraw {
417    id: TaoWindowId,
418    span: tracing::span::EnteredSpan,
419  },
420}
421
422#[derive(Debug)]
423pub struct WindowsStore(pub RefCell<BTreeMap<WindowId, WindowWrapper>>);
424
425// SAFETY: we ensure this type is only used on the main thread.
426#[allow(clippy::non_send_fields_in_send_ty)]
427unsafe impl Send for WindowsStore {}
428
429// SAFETY: we ensure this type is only used on the main thread.
430#[allow(clippy::non_send_fields_in_send_ty)]
431unsafe impl Sync for WindowsStore {}
432
433#[derive(Debug, Clone)]
434pub struct DispatcherMainThreadContext<T: UserEvent> {
435  pub window_target: EventLoopWindowTarget<Message<T>>,
436  pub web_context: WebContextStore,
437  // changing this to an Rc will cause frequent app crashes.
438  pub windows: Arc<WindowsStore>,
439  #[cfg(feature = "tracing")]
440  pub active_tracing_spans: ActiveTraceSpanStore,
441}
442
443// SAFETY: we ensure this type is only used on the main thread.
444#[allow(clippy::non_send_fields_in_send_ty)]
445unsafe impl<T: UserEvent> Send for DispatcherMainThreadContext<T> {}
446
447// SAFETY: we ensure this type is only used on the main thread.
448#[allow(clippy::non_send_fields_in_send_ty)]
449unsafe impl<T: UserEvent> Sync for DispatcherMainThreadContext<T> {}
450
451impl<T: UserEvent> fmt::Debug for Context<T> {
452  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453    f.debug_struct("Context")
454      .field("main_thread_id", &self.main_thread_id)
455      .field("proxy", &self.proxy)
456      .field("main_thread", &self.main_thread)
457      .finish()
458  }
459}
460
461pub struct DeviceEventFilterWrapper(pub TaoDeviceEventFilter);
462
463impl From<DeviceEventFilter> for DeviceEventFilterWrapper {
464  fn from(item: DeviceEventFilter) -> Self {
465    match item {
466      DeviceEventFilter::Always => Self(TaoDeviceEventFilter::Always),
467      DeviceEventFilter::Never => Self(TaoDeviceEventFilter::Never),
468      DeviceEventFilter::Unfocused => Self(TaoDeviceEventFilter::Unfocused),
469    }
470  }
471}
472
473pub struct RectWrapper(pub wry::Rect);
474impl From<tauri_runtime::dpi::Rect> for RectWrapper {
475  fn from(value: tauri_runtime::dpi::Rect) -> Self {
476    RectWrapper(wry::Rect {
477      position: value.position,
478      size: value.size,
479    })
480  }
481}
482
483/// Wrapper around a [`tao::window::Icon`] that can be created from an [`Icon`].
484pub struct TaoIcon(pub TaoWindowIcon);
485
486impl TryFrom<Icon<'_>> for TaoIcon {
487  type Error = Error;
488  fn try_from(icon: Icon<'_>) -> std::result::Result<Self, Self::Error> {
489    TaoWindowIcon::from_rgba(icon.rgba.to_vec(), icon.width, icon.height)
490      .map(Self)
491      .map_err(|e| Error::InvalidIcon(Box::new(e)))
492  }
493}
494
495pub struct WindowEventWrapper(pub Option<WindowEvent>);
496
497impl WindowEventWrapper {
498  fn map_from_tao(
499    event: &TaoWindowEvent<'_>,
500    #[allow(unused_variables)] window: &WindowWrapper,
501  ) -> Self {
502    let event = match event {
503      TaoWindowEvent::Resized(size) => WindowEvent::Resized(PhysicalSizeWrapper(*size).into()),
504      TaoWindowEvent::Moved(position) => {
505        WindowEvent::Moved(PhysicalPositionWrapper(*position).into())
506      }
507      TaoWindowEvent::Destroyed => WindowEvent::Destroyed,
508      TaoWindowEvent::ScaleFactorChanged {
509        scale_factor,
510        new_inner_size,
511      } => WindowEvent::ScaleFactorChanged {
512        scale_factor: *scale_factor,
513        new_inner_size: PhysicalSizeWrapper(**new_inner_size).into(),
514      },
515      TaoWindowEvent::Focused(focused) => {
516        #[cfg(not(windows))]
517        return Self(Some(WindowEvent::Focused(*focused)));
518        // on multiwebview mode, if there's no focused webview, it means we're receiving a direct window focus change
519        // (without receiving a webview focus, such as when clicking the taskbar app icon or using Alt + Tab)
520        // in this case we must send the focus change event here
521        #[cfg(windows)]
522        if window.has_children.load(Ordering::Relaxed) {
523          const FOCUSED_WEBVIEW_MARKER: &str = "__tauriWindow?";
524          let mut focused_webview = window.focused_webview.lock().unwrap();
525          // when we focus a webview and the window was previously focused, we get a blur event here
526          // so on blur we should only send events if the current focus is owned by the window
527          if !*focused
528            && focused_webview
529              .as_deref()
530              .is_some_and(|w| w != FOCUSED_WEBVIEW_MARKER)
531          {
532            return Self(None);
533          }
534
535          // reset focused_webview on blur, or set to a dummy value on focus
536          // (to prevent double focus event when we click a webview after focusing a window)
537          *focused_webview = (*focused).then(|| FOCUSED_WEBVIEW_MARKER.to_string());
538
539          return Self(Some(WindowEvent::Focused(*focused)));
540        } else {
541          // when not on multiwebview mode, we handle focus change events on the webview (add_GotFocus and add_LostFocus)
542          return Self(None);
543        }
544      }
545      TaoWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
546      _ => return Self(None),
547    };
548    Self(Some(event))
549  }
550
551  fn parse(window: &WindowWrapper, event: &TaoWindowEvent<'_>) -> Self {
552    match event {
553      // resized event from tao doesn't include a reliable size on macOS
554      // because wry replaces the NSView
555      TaoWindowEvent::Resized(_) => {
556        if let Some(w) = &window.inner {
557          let size = inner_size(
558            w,
559            &window.webviews,
560            window.has_children.load(Ordering::Relaxed),
561          );
562          Self(Some(WindowEvent::Resized(PhysicalSizeWrapper(size).into())))
563        } else {
564          Self(None)
565        }
566      }
567      e => Self::map_from_tao(e, window),
568    }
569  }
570}
571
572pub fn map_theme(theme: &TaoTheme) -> Theme {
573  match theme {
574    TaoTheme::Light => Theme::Light,
575    TaoTheme::Dark => Theme::Dark,
576    _ => Theme::Light,
577  }
578}
579
580#[cfg(target_os = "macos")]
581fn tao_activation_policy(activation_policy: ActivationPolicy) -> TaoActivationPolicy {
582  match activation_policy {
583    ActivationPolicy::Regular => TaoActivationPolicy::Regular,
584    ActivationPolicy::Accessory => TaoActivationPolicy::Accessory,
585    ActivationPolicy::Prohibited => TaoActivationPolicy::Prohibited,
586    _ => unimplemented!(),
587  }
588}
589
590pub struct MonitorHandleWrapper(pub MonitorHandle);
591
592impl From<MonitorHandleWrapper> for Monitor {
593  fn from(monitor: MonitorHandleWrapper) -> Monitor {
594    Self {
595      name: monitor.0.name(),
596      position: PhysicalPositionWrapper(monitor.0.position()).into(),
597      size: PhysicalSizeWrapper(monitor.0.size()).into(),
598      work_area: monitor.0.work_area(),
599      scale_factor: monitor.0.scale_factor(),
600    }
601  }
602}
603
604pub struct PhysicalPositionWrapper<T>(pub TaoPhysicalPosition<T>);
605
606impl<T> From<PhysicalPositionWrapper<T>> for PhysicalPosition<T> {
607  fn from(position: PhysicalPositionWrapper<T>) -> Self {
608    Self {
609      x: position.0.x,
610      y: position.0.y,
611    }
612  }
613}
614
615impl<T> From<PhysicalPosition<T>> for PhysicalPositionWrapper<T> {
616  fn from(position: PhysicalPosition<T>) -> Self {
617    Self(TaoPhysicalPosition {
618      x: position.x,
619      y: position.y,
620    })
621  }
622}
623
624struct LogicalPositionWrapper<T>(TaoLogicalPosition<T>);
625
626impl<T> From<LogicalPosition<T>> for LogicalPositionWrapper<T> {
627  fn from(position: LogicalPosition<T>) -> Self {
628    Self(TaoLogicalPosition {
629      x: position.x,
630      y: position.y,
631    })
632  }
633}
634
635pub struct PhysicalSizeWrapper<T>(pub TaoPhysicalSize<T>);
636
637impl<T> From<PhysicalSizeWrapper<T>> for PhysicalSize<T> {
638  fn from(size: PhysicalSizeWrapper<T>) -> Self {
639    Self {
640      width: size.0.width,
641      height: size.0.height,
642    }
643  }
644}
645
646impl<T> From<PhysicalSize<T>> for PhysicalSizeWrapper<T> {
647  fn from(size: PhysicalSize<T>) -> Self {
648    Self(TaoPhysicalSize {
649      width: size.width,
650      height: size.height,
651    })
652  }
653}
654
655struct LogicalSizeWrapper<T>(TaoLogicalSize<T>);
656
657impl<T> From<LogicalSize<T>> for LogicalSizeWrapper<T> {
658  fn from(size: LogicalSize<T>) -> Self {
659    Self(TaoLogicalSize {
660      width: size.width,
661      height: size.height,
662    })
663  }
664}
665
666pub struct SizeWrapper(pub TaoSize);
667
668impl From<Size> for SizeWrapper {
669  fn from(size: Size) -> Self {
670    match size {
671      Size::Logical(s) => Self(TaoSize::Logical(LogicalSizeWrapper::from(s).0)),
672      Size::Physical(s) => Self(TaoSize::Physical(PhysicalSizeWrapper::from(s).0)),
673    }
674  }
675}
676
677pub struct PositionWrapper(pub TaoPosition);
678
679impl From<Position> for PositionWrapper {
680  fn from(position: Position) -> Self {
681    match position {
682      Position::Logical(s) => Self(TaoPosition::Logical(LogicalPositionWrapper::from(s).0)),
683      Position::Physical(s) => Self(TaoPosition::Physical(PhysicalPositionWrapper::from(s).0)),
684    }
685  }
686}
687
688#[derive(Debug, Clone)]
689pub struct UserAttentionTypeWrapper(pub TaoUserAttentionType);
690
691impl From<UserAttentionType> for UserAttentionTypeWrapper {
692  fn from(request_type: UserAttentionType) -> Self {
693    let o = match request_type {
694      UserAttentionType::Critical => TaoUserAttentionType::Critical,
695      UserAttentionType::Informational => TaoUserAttentionType::Informational,
696    };
697    Self(o)
698  }
699}
700
701#[derive(Debug)]
702pub struct CursorIconWrapper(pub TaoCursorIcon);
703
704impl From<CursorIcon> for CursorIconWrapper {
705  fn from(icon: CursorIcon) -> Self {
706    use CursorIcon::*;
707    let i = match icon {
708      Default => TaoCursorIcon::Default,
709      Crosshair => TaoCursorIcon::Crosshair,
710      Hand => TaoCursorIcon::Hand,
711      Arrow => TaoCursorIcon::Arrow,
712      Move => TaoCursorIcon::Move,
713      Text => TaoCursorIcon::Text,
714      Wait => TaoCursorIcon::Wait,
715      Help => TaoCursorIcon::Help,
716      Progress => TaoCursorIcon::Progress,
717      NotAllowed => TaoCursorIcon::NotAllowed,
718      ContextMenu => TaoCursorIcon::ContextMenu,
719      Cell => TaoCursorIcon::Cell,
720      VerticalText => TaoCursorIcon::VerticalText,
721      Alias => TaoCursorIcon::Alias,
722      Copy => TaoCursorIcon::Copy,
723      NoDrop => TaoCursorIcon::NoDrop,
724      Grab => TaoCursorIcon::Grab,
725      Grabbing => TaoCursorIcon::Grabbing,
726      AllScroll => TaoCursorIcon::AllScroll,
727      ZoomIn => TaoCursorIcon::ZoomIn,
728      ZoomOut => TaoCursorIcon::ZoomOut,
729      EResize => TaoCursorIcon::EResize,
730      NResize => TaoCursorIcon::NResize,
731      NeResize => TaoCursorIcon::NeResize,
732      NwResize => TaoCursorIcon::NwResize,
733      SResize => TaoCursorIcon::SResize,
734      SeResize => TaoCursorIcon::SeResize,
735      SwResize => TaoCursorIcon::SwResize,
736      WResize => TaoCursorIcon::WResize,
737      EwResize => TaoCursorIcon::EwResize,
738      NsResize => TaoCursorIcon::NsResize,
739      NeswResize => TaoCursorIcon::NeswResize,
740      NwseResize => TaoCursorIcon::NwseResize,
741      ColResize => TaoCursorIcon::ColResize,
742      RowResize => TaoCursorIcon::RowResize,
743      _ => TaoCursorIcon::Default,
744    };
745    Self(i)
746  }
747}
748
749pub struct ProgressStateWrapper(pub TaoProgressState);
750
751impl From<ProgressBarStatus> for ProgressStateWrapper {
752  fn from(status: ProgressBarStatus) -> Self {
753    let state = match status {
754      ProgressBarStatus::None => TaoProgressState::None,
755      ProgressBarStatus::Normal => TaoProgressState::Normal,
756      ProgressBarStatus::Indeterminate => TaoProgressState::Indeterminate,
757      ProgressBarStatus::Paused => TaoProgressState::Paused,
758      ProgressBarStatus::Error => TaoProgressState::Error,
759    };
760    Self(state)
761  }
762}
763
764pub struct ProgressBarStateWrapper(pub TaoProgressBarState);
765
766impl From<ProgressBarState> for ProgressBarStateWrapper {
767  fn from(progress_state: ProgressBarState) -> Self {
768    Self(TaoProgressBarState {
769      progress: progress_state.progress,
770      state: progress_state
771        .status
772        .map(|state| ProgressStateWrapper::from(state).0),
773      desktop_filename: progress_state.desktop_filename,
774    })
775  }
776}
777
778#[derive(Clone, Default)]
779pub struct WindowBuilderWrapper {
780  inner: TaoWindowBuilder,
781  center: bool,
782  prevent_overflow: Option<Size>,
783  #[cfg(target_os = "macos")]
784  tabbing_identifier: Option<String>,
785}
786
787impl std::fmt::Debug for WindowBuilderWrapper {
788  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
789    let mut s = f.debug_struct("WindowBuilderWrapper");
790    s.field("inner", &self.inner)
791      .field("center", &self.center)
792      .field("prevent_overflow", &self.prevent_overflow);
793    #[cfg(target_os = "macos")]
794    {
795      s.field("tabbing_identifier", &self.tabbing_identifier);
796    }
797    s.finish()
798  }
799}
800
801// SAFETY: this type is `Send` since `menu_items` are read only here
802#[allow(clippy::non_send_fields_in_send_ty)]
803unsafe impl Send for WindowBuilderWrapper {}
804
805impl WindowBuilderBase for WindowBuilderWrapper {}
806impl WindowBuilder for WindowBuilderWrapper {
807  fn new() -> Self {
808    #[allow(unused_mut)]
809    let mut builder = Self::default().focused(true);
810
811    #[cfg(target_os = "macos")]
812    {
813      // TODO: find a proper way to prevent webview being pushed out of the window.
814      // Workround for issue: https://github.com/tauri-apps/tauri/issues/10225
815      // The window requires `NSFullSizeContentViewWindowMask` flag to prevent devtools
816      // pushing the content view out of the window.
817      // By setting the default style to `TitleBarStyle::Visible` should fix the issue for most of the users.
818      builder = builder.title_bar_style(TitleBarStyle::Visible);
819    }
820
821    builder = builder.title("Tauri App");
822
823    #[cfg(windows)]
824    {
825      builder = builder.window_classname("Tauri Window");
826    }
827
828    builder
829  }
830
831  fn with_config(config: &WindowConfig) -> Self {
832    let mut window = WindowBuilderWrapper::new();
833
834    #[cfg(target_os = "macos")]
835    {
836      window = window
837        .hidden_title(config.hidden_title)
838        .title_bar_style(config.title_bar_style);
839      if let Some(identifier) = &config.tabbing_identifier {
840        window = window.tabbing_identifier(identifier);
841      }
842      if let Some(position) = &config.traffic_light_position {
843        window = window.traffic_light_position(tauri_runtime::dpi::LogicalPosition::new(
844          position.x, position.y,
845        ));
846      }
847    }
848
849    #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
850    {
851      window = window.transparent(config.transparent);
852    }
853    #[cfg(all(
854      target_os = "macos",
855      not(feature = "macos-private-api"),
856      debug_assertions
857    ))]
858    if config.transparent {
859      eprintln!(
860        "The window is set to be transparent but the `macos-private-api` is not enabled.
861        This can be enabled via the `tauri.macOSPrivateApi` configuration property <https://v2.tauri.app/reference/config/#macosprivateapi>
862      ");
863    }
864
865    #[cfg(target_os = "linux")]
866    {
867      // Mouse event is disabled on Linux since sudden event bursts could block event loop.
868      window.inner = window.inner.with_cursor_moved_event(false);
869    }
870
871    #[cfg(desktop)]
872    {
873      window = window
874        .title(config.title.to_string())
875        .inner_size(config.width, config.height)
876        .focused(config.focus)
877        .focusable(config.focusable)
878        .visible(config.visible)
879        .resizable(config.resizable)
880        .fullscreen(config.fullscreen)
881        .decorations(config.decorations)
882        .maximized(config.maximized)
883        .always_on_bottom(config.always_on_bottom)
884        .always_on_top(config.always_on_top)
885        .visible_on_all_workspaces(config.visible_on_all_workspaces)
886        .content_protected(config.content_protected)
887        .skip_taskbar(config.skip_taskbar)
888        .theme(config.theme)
889        .closable(config.closable)
890        .maximizable(config.maximizable)
891        .minimizable(config.minimizable)
892        .shadow(config.shadow);
893
894      let mut constraints = WindowSizeConstraints::default();
895
896      if let Some(min_width) = config.min_width {
897        constraints.min_width = Some(tao::dpi::LogicalUnit::new(min_width).into());
898      }
899      if let Some(min_height) = config.min_height {
900        constraints.min_height = Some(tao::dpi::LogicalUnit::new(min_height).into());
901      }
902      if let Some(max_width) = config.max_width {
903        constraints.max_width = Some(tao::dpi::LogicalUnit::new(max_width).into());
904      }
905      if let Some(max_height) = config.max_height {
906        constraints.max_height = Some(tao::dpi::LogicalUnit::new(max_height).into());
907      }
908      if let Some(color) = config.background_color {
909        window = window.background_color(color);
910      }
911      window = window.inner_size_constraints(constraints);
912
913      if let (Some(x), Some(y)) = (config.x, config.y) {
914        window = window.position(x, y);
915      }
916
917      if config.center {
918        window = window.center();
919      }
920
921      if let Some(window_classname) = &config.window_classname {
922        window = window.window_classname(window_classname);
923      }
924
925      if let Some(prevent_overflow) = &config.prevent_overflow {
926        window = match prevent_overflow {
927          PreventOverflowConfig::Enable(true) => window.prevent_overflow(),
928          PreventOverflowConfig::Margin(margin) => window
929            .prevent_overflow_with_margin(TaoPhysicalSize::new(margin.width, margin.height).into()),
930          _ => window,
931        };
932      }
933    }
934
935    window
936  }
937
938  fn center(mut self) -> Self {
939    self.center = true;
940    self
941  }
942
943  fn position(mut self, x: f64, y: f64) -> Self {
944    self.inner = self.inner.with_position(TaoLogicalPosition::new(x, y));
945    self
946  }
947
948  fn inner_size(mut self, width: f64, height: f64) -> Self {
949    self.inner = self
950      .inner
951      .with_inner_size(TaoLogicalSize::new(width, height));
952    self
953  }
954
955  fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
956    self.inner = self
957      .inner
958      .with_min_inner_size(TaoLogicalSize::new(min_width, min_height));
959    self
960  }
961
962  fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
963    self.inner = self
964      .inner
965      .with_max_inner_size(TaoLogicalSize::new(max_width, max_height));
966    self
967  }
968
969  fn inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self {
970    self.inner.window.inner_size_constraints = tao::window::WindowSizeConstraints {
971      min_width: constraints.min_width,
972      min_height: constraints.min_height,
973      max_width: constraints.max_width,
974      max_height: constraints.max_height,
975    };
976    self
977  }
978
979  /// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size) on creation
980  ///
981  /// ## Platform-specific
982  ///
983  /// - **iOS / Android:** Unsupported.
984  fn prevent_overflow(mut self) -> Self {
985    self
986      .prevent_overflow
987      .replace(PhysicalSize::new(0, 0).into());
988    self
989  }
990
991  /// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size)
992  /// on creation with a margin
993  ///
994  /// ## Platform-specific
995  ///
996  /// - **iOS / Android:** Unsupported.
997  fn prevent_overflow_with_margin(mut self, margin: Size) -> Self {
998    self.prevent_overflow.replace(margin);
999    self
1000  }
1001
1002  fn resizable(mut self, resizable: bool) -> Self {
1003    self.inner = self.inner.with_resizable(resizable);
1004    self
1005  }
1006
1007  fn maximizable(mut self, maximizable: bool) -> Self {
1008    self.inner = self.inner.with_maximizable(maximizable);
1009    self
1010  }
1011
1012  fn minimizable(mut self, minimizable: bool) -> Self {
1013    self.inner = self.inner.with_minimizable(minimizable);
1014    self
1015  }
1016
1017  fn closable(mut self, closable: bool) -> Self {
1018    self.inner = self.inner.with_closable(closable);
1019    self
1020  }
1021
1022  fn title<S: Into<String>>(mut self, title: S) -> Self {
1023    self.inner = self.inner.with_title(title.into());
1024    self
1025  }
1026
1027  fn fullscreen(mut self, fullscreen: bool) -> Self {
1028    self.inner = if fullscreen {
1029      self
1030        .inner
1031        .with_fullscreen(Some(Fullscreen::Borderless(None)))
1032    } else {
1033      self.inner.with_fullscreen(None)
1034    };
1035    self
1036  }
1037
1038  fn focused(mut self, focused: bool) -> Self {
1039    self.inner = self.inner.with_focused(focused);
1040    self
1041  }
1042
1043  fn focusable(mut self, focusable: bool) -> Self {
1044    self.inner = self.inner.with_focusable(focusable);
1045    self
1046  }
1047
1048  fn maximized(mut self, maximized: bool) -> Self {
1049    self.inner = self.inner.with_maximized(maximized);
1050    self
1051  }
1052
1053  fn visible(mut self, visible: bool) -> Self {
1054    self.inner = self.inner.with_visible(visible);
1055    self
1056  }
1057
1058  #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
1059  fn transparent(mut self, transparent: bool) -> Self {
1060    self.inner = self.inner.with_transparent(transparent);
1061    self
1062  }
1063
1064  fn decorations(mut self, decorations: bool) -> Self {
1065    self.inner = self.inner.with_decorations(decorations);
1066    self
1067  }
1068
1069  fn always_on_bottom(mut self, always_on_bottom: bool) -> Self {
1070    self.inner = self.inner.with_always_on_bottom(always_on_bottom);
1071    self
1072  }
1073
1074  fn always_on_top(mut self, always_on_top: bool) -> Self {
1075    self.inner = self.inner.with_always_on_top(always_on_top);
1076    self
1077  }
1078
1079  fn visible_on_all_workspaces(mut self, visible_on_all_workspaces: bool) -> Self {
1080    self.inner = self
1081      .inner
1082      .with_visible_on_all_workspaces(visible_on_all_workspaces);
1083    self
1084  }
1085
1086  fn content_protected(mut self, protected: bool) -> Self {
1087    self.inner = self.inner.with_content_protection(protected);
1088    self
1089  }
1090
1091  fn shadow(#[allow(unused_mut)] mut self, _enable: bool) -> Self {
1092    #[cfg(windows)]
1093    {
1094      self.inner = self.inner.with_undecorated_shadow(_enable);
1095    }
1096    #[cfg(target_os = "macos")]
1097    {
1098      self.inner = self.inner.with_has_shadow(_enable);
1099    }
1100    self
1101  }
1102
1103  #[cfg(windows)]
1104  fn owner(mut self, owner: HWND) -> Self {
1105    self.inner = self.inner.with_owner_window(owner.0 as _);
1106    self
1107  }
1108
1109  #[cfg(windows)]
1110  fn parent(mut self, parent: HWND) -> Self {
1111    self.inner = self.inner.with_parent_window(parent.0 as _);
1112    self
1113  }
1114
1115  #[cfg(target_os = "macos")]
1116  fn parent(mut self, parent: *mut std::ffi::c_void) -> Self {
1117    self.inner = self.inner.with_parent_window(parent);
1118    self
1119  }
1120
1121  #[cfg(any(
1122    target_os = "linux",
1123    target_os = "dragonfly",
1124    target_os = "freebsd",
1125    target_os = "netbsd",
1126    target_os = "openbsd"
1127  ))]
1128  fn transient_for(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
1129    self.inner = self.inner.with_transient_for(parent);
1130    self
1131  }
1132
1133  #[cfg(windows)]
1134  fn drag_and_drop(mut self, enabled: bool) -> Self {
1135    self.inner = self.inner.with_drag_and_drop(enabled);
1136    self
1137  }
1138
1139  #[cfg(target_os = "macos")]
1140  fn title_bar_style(mut self, style: TitleBarStyle) -> Self {
1141    match style {
1142      TitleBarStyle::Visible => {
1143        self.inner = self.inner.with_titlebar_transparent(false);
1144        // Fixes rendering issue when resizing window with devtools open (https://github.com/tauri-apps/tauri/issues/3914)
1145        self.inner = self.inner.with_fullsize_content_view(true);
1146      }
1147      TitleBarStyle::Transparent => {
1148        self.inner = self.inner.with_titlebar_transparent(true);
1149        self.inner = self.inner.with_fullsize_content_view(false);
1150      }
1151      TitleBarStyle::Overlay => {
1152        self.inner = self.inner.with_titlebar_transparent(true);
1153        self.inner = self.inner.with_fullsize_content_view(true);
1154      }
1155      unknown => {
1156        #[cfg(feature = "tracing")]
1157        tracing::warn!("unknown title bar style applied: {unknown}");
1158
1159        #[cfg(not(feature = "tracing"))]
1160        eprintln!("unknown title bar style applied: {unknown}");
1161      }
1162    }
1163    self
1164  }
1165
1166  #[cfg(target_os = "macos")]
1167  fn traffic_light_position<P: Into<Position>>(mut self, position: P) -> Self {
1168    self.inner = self.inner.with_traffic_light_inset(position.into());
1169    self
1170  }
1171
1172  #[cfg(target_os = "macos")]
1173  fn hidden_title(mut self, hidden: bool) -> Self {
1174    self.inner = self.inner.with_title_hidden(hidden);
1175    self
1176  }
1177
1178  #[cfg(target_os = "macos")]
1179  fn tabbing_identifier(mut self, identifier: &str) -> Self {
1180    self.inner = self.inner.with_tabbing_identifier(identifier);
1181    self.tabbing_identifier.replace(identifier.into());
1182    self
1183  }
1184
1185  fn icon(mut self, icon: Icon) -> Result<Self> {
1186    self.inner = self
1187      .inner
1188      .with_window_icon(Some(TaoIcon::try_from(icon)?.0));
1189    Ok(self)
1190  }
1191
1192  fn background_color(mut self, color: Color) -> Self {
1193    self.inner = self.inner.with_background_color(color.into());
1194    self
1195  }
1196
1197  #[cfg(any(windows, target_os = "linux"))]
1198  fn skip_taskbar(mut self, skip: bool) -> Self {
1199    self.inner = self.inner.with_skip_taskbar(skip);
1200    self
1201  }
1202
1203  #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
1204  fn skip_taskbar(self, _skip: bool) -> Self {
1205    self
1206  }
1207
1208  fn theme(mut self, theme: Option<Theme>) -> Self {
1209    self.inner = self.inner.with_theme(if let Some(t) = theme {
1210      match t {
1211        Theme::Dark => Some(TaoTheme::Dark),
1212        _ => Some(TaoTheme::Light),
1213      }
1214    } else {
1215      None
1216    });
1217
1218    self
1219  }
1220
1221  fn has_icon(&self) -> bool {
1222    self.inner.window.window_icon.is_some()
1223  }
1224
1225  fn get_theme(&self) -> Option<Theme> {
1226    self.inner.window.preferred_theme.map(|theme| match theme {
1227      TaoTheme::Dark => Theme::Dark,
1228      _ => Theme::Light,
1229    })
1230  }
1231
1232  #[cfg(windows)]
1233  fn window_classname<S: Into<String>>(mut self, window_classname: S) -> Self {
1234    self.inner = self.inner.with_window_classname(window_classname);
1235    self
1236  }
1237  #[cfg(not(windows))]
1238  fn window_classname<S: Into<String>>(self, _window_classname: S) -> Self {
1239    self
1240  }
1241}
1242
1243#[cfg(any(
1244  target_os = "linux",
1245  target_os = "dragonfly",
1246  target_os = "freebsd",
1247  target_os = "netbsd",
1248  target_os = "openbsd"
1249))]
1250pub struct GtkWindow(pub gtk::ApplicationWindow);
1251#[cfg(any(
1252  target_os = "linux",
1253  target_os = "dragonfly",
1254  target_os = "freebsd",
1255  target_os = "netbsd",
1256  target_os = "openbsd"
1257))]
1258#[allow(clippy::non_send_fields_in_send_ty)]
1259unsafe impl Send for GtkWindow {}
1260
1261#[cfg(any(
1262  target_os = "linux",
1263  target_os = "dragonfly",
1264  target_os = "freebsd",
1265  target_os = "netbsd",
1266  target_os = "openbsd"
1267))]
1268pub struct GtkBox(pub gtk::Box);
1269#[cfg(any(
1270  target_os = "linux",
1271  target_os = "dragonfly",
1272  target_os = "freebsd",
1273  target_os = "netbsd",
1274  target_os = "openbsd"
1275))]
1276#[allow(clippy::non_send_fields_in_send_ty)]
1277unsafe impl Send for GtkBox {}
1278
1279pub struct SendRawWindowHandle(pub raw_window_handle::RawWindowHandle);
1280unsafe impl Send for SendRawWindowHandle {}
1281
1282pub enum ApplicationMessage {
1283  #[cfg(target_os = "macos")]
1284  Show,
1285  #[cfg(target_os = "macos")]
1286  Hide,
1287  #[cfg(any(target_os = "macos", target_os = "ios"))]
1288  FetchDataStoreIdentifiers(Box<dyn FnOnce(Vec<[u8; 16]>) + Send + 'static>),
1289  #[cfg(any(target_os = "macos", target_os = "ios"))]
1290  RemoveDataStore([u8; 16], Box<dyn FnOnce(Result<()>) + Send + 'static>),
1291}
1292
1293pub enum WindowMessage {
1294  AddEventListener(WindowEventId, Box<dyn Fn(&WindowEvent) + Send>),
1295  // Getters
1296  ScaleFactor(Sender<f64>),
1297  InnerPosition(Sender<Result<PhysicalPosition<i32>>>),
1298  OuterPosition(Sender<Result<PhysicalPosition<i32>>>),
1299  InnerSize(Sender<PhysicalSize<u32>>),
1300  OuterSize(Sender<PhysicalSize<u32>>),
1301  IsFullscreen(Sender<bool>),
1302  IsMinimized(Sender<bool>),
1303  IsMaximized(Sender<bool>),
1304  IsFocused(Sender<bool>),
1305  IsDecorated(Sender<bool>),
1306  IsResizable(Sender<bool>),
1307  IsMaximizable(Sender<bool>),
1308  IsMinimizable(Sender<bool>),
1309  IsClosable(Sender<bool>),
1310  IsVisible(Sender<bool>),
1311  Title(Sender<String>),
1312  CurrentMonitor(Sender<Option<MonitorHandle>>),
1313  PrimaryMonitor(Sender<Option<MonitorHandle>>),
1314  MonitorFromPoint(Sender<Option<MonitorHandle>>, (f64, f64)),
1315  AvailableMonitors(Sender<Vec<MonitorHandle>>),
1316  #[cfg(any(
1317    target_os = "linux",
1318    target_os = "dragonfly",
1319    target_os = "freebsd",
1320    target_os = "netbsd",
1321    target_os = "openbsd"
1322  ))]
1323  GtkWindow(Sender<GtkWindow>),
1324  #[cfg(any(
1325    target_os = "linux",
1326    target_os = "dragonfly",
1327    target_os = "freebsd",
1328    target_os = "netbsd",
1329    target_os = "openbsd"
1330  ))]
1331  GtkBox(Sender<GtkBox>),
1332  RawWindowHandle(Sender<std::result::Result<SendRawWindowHandle, raw_window_handle::HandleError>>),
1333  Theme(Sender<Theme>),
1334  IsEnabled(Sender<bool>),
1335  IsAlwaysOnTop(Sender<bool>),
1336  // Setters
1337  Center,
1338  RequestUserAttention(Option<UserAttentionTypeWrapper>),
1339  SetEnabled(bool),
1340  SetResizable(bool),
1341  SetMaximizable(bool),
1342  SetMinimizable(bool),
1343  SetClosable(bool),
1344  SetTitle(String),
1345  Maximize,
1346  Unmaximize,
1347  Minimize,
1348  Unminimize,
1349  Show,
1350  Hide,
1351  Close,
1352  Destroy,
1353  SetDecorations(bool),
1354  SetShadow(bool),
1355  SetAlwaysOnBottom(bool),
1356  SetAlwaysOnTop(bool),
1357  SetVisibleOnAllWorkspaces(bool),
1358  SetContentProtected(bool),
1359  SetSize(Size),
1360  SetMinSize(Option<Size>),
1361  SetMaxSize(Option<Size>),
1362  SetSizeConstraints(WindowSizeConstraints),
1363  SetPosition(Position),
1364  SetFullscreen(bool),
1365  #[cfg(target_os = "macos")]
1366  SetSimpleFullscreen(bool),
1367  SetFocus,
1368  SetFocusable(bool),
1369  SetIcon(TaoWindowIcon),
1370  SetSkipTaskbar(bool),
1371  SetCursorGrab(bool),
1372  SetCursorVisible(bool),
1373  SetCursorIcon(CursorIcon),
1374  SetCursorPosition(Position),
1375  SetIgnoreCursorEvents(bool),
1376  SetBadgeCount(Option<i64>, Option<String>),
1377  SetBadgeLabel(Option<String>),
1378  SetOverlayIcon(Option<TaoIcon>),
1379  SetProgressBar(ProgressBarState),
1380  SetTitleBarStyle(tauri_utils::TitleBarStyle),
1381  SetTrafficLightPosition(Position),
1382  SetTheme(Option<Theme>),
1383  SetBackgroundColor(Option<Color>),
1384  DragWindow,
1385  ResizeDragWindow(tauri_runtime::ResizeDirection),
1386  RequestRedraw,
1387}
1388
1389#[derive(Debug, Clone)]
1390pub enum SynthesizedWindowEvent {
1391  Focused(bool),
1392  DragDrop(DragDropEvent),
1393}
1394
1395impl From<SynthesizedWindowEvent> for WindowEventWrapper {
1396  fn from(event: SynthesizedWindowEvent) -> Self {
1397    let event = match event {
1398      SynthesizedWindowEvent::Focused(focused) => WindowEvent::Focused(focused),
1399      SynthesizedWindowEvent::DragDrop(event) => WindowEvent::DragDrop(event),
1400    };
1401    Self(Some(event))
1402  }
1403}
1404
1405pub enum WebviewMessage {
1406  AddEventListener(WebviewEventId, Box<dyn Fn(&WebviewEvent) + Send>),
1407  #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
1408  EvaluateScript(String),
1409  #[cfg(all(feature = "tracing", not(target_os = "android")))]
1410  EvaluateScript(String, Sender<()>, tracing::Span),
1411  CookiesForUrl(Url, Sender<Result<Vec<tauri_runtime::Cookie<'static>>>>),
1412  Cookies(Sender<Result<Vec<tauri_runtime::Cookie<'static>>>>),
1413  SetCookie(tauri_runtime::Cookie<'static>),
1414  DeleteCookie(tauri_runtime::Cookie<'static>),
1415  WebviewEvent(WebviewEvent),
1416  SynthesizedWindowEvent(SynthesizedWindowEvent),
1417  Navigate(Url),
1418  Reload,
1419  Print,
1420  Close,
1421  Show,
1422  Hide,
1423  SetPosition(Position),
1424  SetSize(Size),
1425  SetBounds(tauri_runtime::dpi::Rect),
1426  SetFocus,
1427  Reparent(WindowId, Sender<Result<()>>),
1428  SetAutoResize(bool),
1429  SetZoom(f64),
1430  SetBackgroundColor(Option<Color>),
1431  ClearAllBrowsingData,
1432  // Getters
1433  Url(Sender<Result<String>>),
1434  Bounds(Sender<Result<tauri_runtime::dpi::Rect>>),
1435  Position(Sender<Result<PhysicalPosition<i32>>>),
1436  Size(Sender<Result<PhysicalSize<u32>>>),
1437  WithWebview(Box<dyn FnOnce(Webview) + Send>),
1438  // Devtools
1439  #[cfg(any(debug_assertions, feature = "devtools"))]
1440  OpenDevTools,
1441  #[cfg(any(debug_assertions, feature = "devtools"))]
1442  CloseDevTools,
1443  #[cfg(any(debug_assertions, feature = "devtools"))]
1444  IsDevToolsOpen(Sender<bool>),
1445}
1446
1447pub enum EventLoopWindowTargetMessage {
1448  CursorPosition(Sender<Result<PhysicalPosition<f64>>>),
1449  SetTheme(Option<Theme>),
1450  SetDeviceEventFilter(DeviceEventFilter),
1451}
1452
1453pub type CreateWindowClosure<T> =
1454  Box<dyn FnOnce(&EventLoopWindowTarget<Message<T>>) -> Result<WindowWrapper> + Send>;
1455
1456pub type CreateWebviewClosure =
1457  Box<dyn FnOnce(&Window, CreateWebviewOptions) -> Result<WebviewWrapper> + Send>;
1458
1459pub struct CreateWebviewOptions {
1460  pub focused_webview: Arc<Mutex<Option<String>>>,
1461}
1462
1463pub enum Message<T: 'static> {
1464  Task(Box<dyn FnOnce() + Send>),
1465  #[cfg(target_os = "macos")]
1466  SetActivationPolicy(ActivationPolicy),
1467  #[cfg(target_os = "macos")]
1468  SetDockVisibility(bool),
1469  RequestExit(i32),
1470  Application(ApplicationMessage),
1471  Window(WindowId, WindowMessage),
1472  Webview(WindowId, WebviewId, WebviewMessage),
1473  EventLoopWindowTarget(EventLoopWindowTargetMessage),
1474  CreateWebview(WindowId, CreateWebviewClosure),
1475  CreateWindow(WindowId, CreateWindowClosure<T>),
1476  CreateRawWindow(
1477    WindowId,
1478    Box<dyn FnOnce() -> (String, TaoWindowBuilder) + Send>,
1479    Sender<Result<Weak<Window>>>,
1480  ),
1481  UserEvent(T),
1482}
1483
1484impl<T: UserEvent> Clone for Message<T> {
1485  fn clone(&self) -> Self {
1486    match self {
1487      Self::UserEvent(t) => Self::UserEvent(t.clone()),
1488      _ => unimplemented!(),
1489    }
1490  }
1491}
1492
1493/// The Tauri [`WebviewDispatch`] for [`Wry`].
1494#[derive(Debug, Clone)]
1495pub struct WryWebviewDispatcher<T: UserEvent> {
1496  window_id: Arc<Mutex<WindowId>>,
1497  webview_id: WebviewId,
1498  context: Context<T>,
1499}
1500
1501impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
1502  type Runtime = Wry<T>;
1503
1504  fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
1505    send_user_message(&self.context, Message::Task(Box::new(f)))
1506  }
1507
1508  fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
1509    let id = self.context.next_webview_event_id();
1510    let _ = self.context.proxy.send_event(Message::Webview(
1511      *self.window_id.lock().unwrap(),
1512      self.webview_id,
1513      WebviewMessage::AddEventListener(id, Box::new(f)),
1514    ));
1515    id
1516  }
1517
1518  fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()> {
1519    send_user_message(
1520      &self.context,
1521      Message::Webview(
1522        *self.window_id.lock().unwrap(),
1523        self.webview_id,
1524        WebviewMessage::WithWebview(Box::new(move |webview| f(Box::new(webview)))),
1525      ),
1526    )
1527  }
1528
1529  #[cfg(any(debug_assertions, feature = "devtools"))]
1530  fn open_devtools(&self) {
1531    let _ = send_user_message(
1532      &self.context,
1533      Message::Webview(
1534        *self.window_id.lock().unwrap(),
1535        self.webview_id,
1536        WebviewMessage::OpenDevTools,
1537      ),
1538    );
1539  }
1540
1541  #[cfg(any(debug_assertions, feature = "devtools"))]
1542  fn close_devtools(&self) {
1543    let _ = send_user_message(
1544      &self.context,
1545      Message::Webview(
1546        *self.window_id.lock().unwrap(),
1547        self.webview_id,
1548        WebviewMessage::CloseDevTools,
1549      ),
1550    );
1551  }
1552
1553  /// Gets the devtools window's current open state.
1554  #[cfg(any(debug_assertions, feature = "devtools"))]
1555  fn is_devtools_open(&self) -> Result<bool> {
1556    webview_getter!(self, WebviewMessage::IsDevToolsOpen)
1557  }
1558
1559  // Getters
1560
1561  fn url(&self) -> Result<String> {
1562    webview_getter!(self, WebviewMessage::Url)?
1563  }
1564
1565  fn bounds(&self) -> Result<tauri_runtime::dpi::Rect> {
1566    webview_getter!(self, WebviewMessage::Bounds)?
1567  }
1568
1569  fn position(&self) -> Result<PhysicalPosition<i32>> {
1570    webview_getter!(self, WebviewMessage::Position)?
1571  }
1572
1573  fn size(&self) -> Result<PhysicalSize<u32>> {
1574    webview_getter!(self, WebviewMessage::Size)?
1575  }
1576
1577  // Setters
1578
1579  fn navigate(&self, url: Url) -> Result<()> {
1580    send_user_message(
1581      &self.context,
1582      Message::Webview(
1583        *self.window_id.lock().unwrap(),
1584        self.webview_id,
1585        WebviewMessage::Navigate(url),
1586      ),
1587    )
1588  }
1589
1590  fn reload(&self) -> Result<()> {
1591    send_user_message(
1592      &self.context,
1593      Message::Webview(
1594        *self.window_id.lock().unwrap(),
1595        self.webview_id,
1596        WebviewMessage::Reload,
1597      ),
1598    )
1599  }
1600
1601  fn print(&self) -> Result<()> {
1602    send_user_message(
1603      &self.context,
1604      Message::Webview(
1605        *self.window_id.lock().unwrap(),
1606        self.webview_id,
1607        WebviewMessage::Print,
1608      ),
1609    )
1610  }
1611
1612  fn close(&self) -> Result<()> {
1613    send_user_message(
1614      &self.context,
1615      Message::Webview(
1616        *self.window_id.lock().unwrap(),
1617        self.webview_id,
1618        WebviewMessage::Close,
1619      ),
1620    )
1621  }
1622
1623  fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> Result<()> {
1624    send_user_message(
1625      &self.context,
1626      Message::Webview(
1627        *self.window_id.lock().unwrap(),
1628        self.webview_id,
1629        WebviewMessage::SetBounds(bounds),
1630      ),
1631    )
1632  }
1633
1634  fn set_size(&self, size: Size) -> Result<()> {
1635    send_user_message(
1636      &self.context,
1637      Message::Webview(
1638        *self.window_id.lock().unwrap(),
1639        self.webview_id,
1640        WebviewMessage::SetSize(size),
1641      ),
1642    )
1643  }
1644
1645  fn set_position(&self, position: Position) -> Result<()> {
1646    send_user_message(
1647      &self.context,
1648      Message::Webview(
1649        *self.window_id.lock().unwrap(),
1650        self.webview_id,
1651        WebviewMessage::SetPosition(position),
1652      ),
1653    )
1654  }
1655
1656  fn set_focus(&self) -> Result<()> {
1657    send_user_message(
1658      &self.context,
1659      Message::Webview(
1660        *self.window_id.lock().unwrap(),
1661        self.webview_id,
1662        WebviewMessage::SetFocus,
1663      ),
1664    )
1665  }
1666
1667  fn reparent(&self, window_id: WindowId) -> Result<()> {
1668    let mut current_window_id = self.window_id.lock().unwrap();
1669    let (tx, rx) = channel();
1670    send_user_message(
1671      &self.context,
1672      Message::Webview(
1673        *current_window_id,
1674        self.webview_id,
1675        WebviewMessage::Reparent(window_id, tx),
1676      ),
1677    )?;
1678
1679    rx.recv().unwrap()?;
1680
1681    *current_window_id = window_id;
1682    Ok(())
1683  }
1684
1685  fn cookies_for_url(&self, url: Url) -> Result<Vec<Cookie<'static>>> {
1686    let current_window_id = self.window_id.lock().unwrap();
1687    let (tx, rx) = channel();
1688    send_user_message(
1689      &self.context,
1690      Message::Webview(
1691        *current_window_id,
1692        self.webview_id,
1693        WebviewMessage::CookiesForUrl(url, tx),
1694      ),
1695    )?;
1696
1697    rx.recv().unwrap()
1698  }
1699
1700  fn cookies(&self) -> Result<Vec<Cookie<'static>>> {
1701    webview_getter!(self, WebviewMessage::Cookies)?
1702  }
1703
1704  fn set_cookie(&self, cookie: Cookie<'_>) -> Result<()> {
1705    send_user_message(
1706      &self.context,
1707      Message::Webview(
1708        *self.window_id.lock().unwrap(),
1709        self.webview_id,
1710        WebviewMessage::SetCookie(cookie.into_owned()),
1711      ),
1712    )?;
1713    Ok(())
1714  }
1715
1716  fn delete_cookie(&self, cookie: Cookie<'_>) -> Result<()> {
1717    send_user_message(
1718      &self.context,
1719      Message::Webview(
1720        *self.window_id.lock().unwrap(),
1721        self.webview_id,
1722        WebviewMessage::DeleteCookie(cookie.into_owned()),
1723      ),
1724    )?;
1725    Ok(())
1726  }
1727
1728  fn set_auto_resize(&self, auto_resize: bool) -> Result<()> {
1729    send_user_message(
1730      &self.context,
1731      Message::Webview(
1732        *self.window_id.lock().unwrap(),
1733        self.webview_id,
1734        WebviewMessage::SetAutoResize(auto_resize),
1735      ),
1736    )
1737  }
1738
1739  #[cfg(all(feature = "tracing", not(target_os = "android")))]
1740  fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
1741    // use a channel so the EvaluateScript task uses the current span as parent
1742    let (tx, rx) = channel();
1743    getter!(
1744      self,
1745      rx,
1746      Message::Webview(
1747        *self.window_id.lock().unwrap(),
1748        self.webview_id,
1749        WebviewMessage::EvaluateScript(script.into(), tx, tracing::Span::current()),
1750      )
1751    )
1752  }
1753
1754  #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
1755  fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
1756    send_user_message(
1757      &self.context,
1758      Message::Webview(
1759        *self.window_id.lock().unwrap(),
1760        self.webview_id,
1761        WebviewMessage::EvaluateScript(script.into()),
1762      ),
1763    )
1764  }
1765
1766  fn set_zoom(&self, scale_factor: f64) -> Result<()> {
1767    send_user_message(
1768      &self.context,
1769      Message::Webview(
1770        *self.window_id.lock().unwrap(),
1771        self.webview_id,
1772        WebviewMessage::SetZoom(scale_factor),
1773      ),
1774    )
1775  }
1776
1777  fn clear_all_browsing_data(&self) -> Result<()> {
1778    send_user_message(
1779      &self.context,
1780      Message::Webview(
1781        *self.window_id.lock().unwrap(),
1782        self.webview_id,
1783        WebviewMessage::ClearAllBrowsingData,
1784      ),
1785    )
1786  }
1787
1788  fn hide(&self) -> Result<()> {
1789    send_user_message(
1790      &self.context,
1791      Message::Webview(
1792        *self.window_id.lock().unwrap(),
1793        self.webview_id,
1794        WebviewMessage::Hide,
1795      ),
1796    )
1797  }
1798
1799  fn show(&self) -> Result<()> {
1800    send_user_message(
1801      &self.context,
1802      Message::Webview(
1803        *self.window_id.lock().unwrap(),
1804        self.webview_id,
1805        WebviewMessage::Show,
1806      ),
1807    )
1808  }
1809
1810  fn set_background_color(&self, color: Option<Color>) -> Result<()> {
1811    send_user_message(
1812      &self.context,
1813      Message::Webview(
1814        *self.window_id.lock().unwrap(),
1815        self.webview_id,
1816        WebviewMessage::SetBackgroundColor(color),
1817      ),
1818    )
1819  }
1820}
1821
1822/// The Tauri [`WindowDispatch`] for [`Wry`].
1823#[derive(Debug, Clone)]
1824pub struct WryWindowDispatcher<T: UserEvent> {
1825  window_id: WindowId,
1826  context: Context<T>,
1827}
1828
1829// SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`.
1830#[allow(clippy::non_send_fields_in_send_ty)]
1831unsafe impl<T: UserEvent> Sync for WryWindowDispatcher<T> {}
1832
1833fn get_raw_window_handle<T: UserEvent>(
1834  dispatcher: &WryWindowDispatcher<T>,
1835) -> Result<std::result::Result<SendRawWindowHandle, raw_window_handle::HandleError>> {
1836  window_getter!(dispatcher, WindowMessage::RawWindowHandle)
1837}
1838
1839impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
1840  type Runtime = Wry<T>;
1841  type WindowBuilder = WindowBuilderWrapper;
1842
1843  fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
1844    send_user_message(&self.context, Message::Task(Box::new(f)))
1845  }
1846
1847  fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
1848    let id = self.context.next_window_event_id();
1849    let _ = self.context.proxy.send_event(Message::Window(
1850      self.window_id,
1851      WindowMessage::AddEventListener(id, Box::new(f)),
1852    ));
1853    id
1854  }
1855
1856  // Getters
1857
1858  fn scale_factor(&self) -> Result<f64> {
1859    window_getter!(self, WindowMessage::ScaleFactor)
1860  }
1861
1862  fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
1863    window_getter!(self, WindowMessage::InnerPosition)?
1864  }
1865
1866  fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
1867    window_getter!(self, WindowMessage::OuterPosition)?
1868  }
1869
1870  fn inner_size(&self) -> Result<PhysicalSize<u32>> {
1871    window_getter!(self, WindowMessage::InnerSize)
1872  }
1873
1874  fn outer_size(&self) -> Result<PhysicalSize<u32>> {
1875    window_getter!(self, WindowMessage::OuterSize)
1876  }
1877
1878  fn is_fullscreen(&self) -> Result<bool> {
1879    window_getter!(self, WindowMessage::IsFullscreen)
1880  }
1881
1882  fn is_minimized(&self) -> Result<bool> {
1883    window_getter!(self, WindowMessage::IsMinimized)
1884  }
1885
1886  fn is_maximized(&self) -> Result<bool> {
1887    window_getter!(self, WindowMessage::IsMaximized)
1888  }
1889
1890  fn is_focused(&self) -> Result<bool> {
1891    window_getter!(self, WindowMessage::IsFocused)
1892  }
1893
1894  /// Gets the window's current decoration state.
1895  fn is_decorated(&self) -> Result<bool> {
1896    window_getter!(self, WindowMessage::IsDecorated)
1897  }
1898
1899  /// Gets the window's current resizable state.
1900  fn is_resizable(&self) -> Result<bool> {
1901    window_getter!(self, WindowMessage::IsResizable)
1902  }
1903
1904  /// Gets the current native window's maximize button state
1905  fn is_maximizable(&self) -> Result<bool> {
1906    window_getter!(self, WindowMessage::IsMaximizable)
1907  }
1908
1909  /// Gets the current native window's minimize button state
1910  fn is_minimizable(&self) -> Result<bool> {
1911    window_getter!(self, WindowMessage::IsMinimizable)
1912  }
1913
1914  /// Gets the current native window's close button state
1915  fn is_closable(&self) -> Result<bool> {
1916    window_getter!(self, WindowMessage::IsClosable)
1917  }
1918
1919  fn is_visible(&self) -> Result<bool> {
1920    window_getter!(self, WindowMessage::IsVisible)
1921  }
1922
1923  fn title(&self) -> Result<String> {
1924    window_getter!(self, WindowMessage::Title)
1925  }
1926
1927  fn current_monitor(&self) -> Result<Option<Monitor>> {
1928    Ok(window_getter!(self, WindowMessage::CurrentMonitor)?.map(|m| MonitorHandleWrapper(m).into()))
1929  }
1930
1931  fn primary_monitor(&self) -> Result<Option<Monitor>> {
1932    Ok(window_getter!(self, WindowMessage::PrimaryMonitor)?.map(|m| MonitorHandleWrapper(m).into()))
1933  }
1934
1935  fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
1936    let (tx, rx) = channel();
1937
1938    let _ = send_user_message(
1939      &self.context,
1940      Message::Window(self.window_id, WindowMessage::MonitorFromPoint(tx, (x, y))),
1941    );
1942
1943    Ok(
1944      rx.recv()
1945        .map_err(|_| crate::Error::FailedToReceiveMessage)?
1946        .map(|m| MonitorHandleWrapper(m).into()),
1947    )
1948  }
1949
1950  fn available_monitors(&self) -> Result<Vec<Monitor>> {
1951    Ok(
1952      window_getter!(self, WindowMessage::AvailableMonitors)?
1953        .into_iter()
1954        .map(|m| MonitorHandleWrapper(m).into())
1955        .collect(),
1956    )
1957  }
1958
1959  fn theme(&self) -> Result<Theme> {
1960    window_getter!(self, WindowMessage::Theme)
1961  }
1962
1963  fn is_enabled(&self) -> Result<bool> {
1964    window_getter!(self, WindowMessage::IsEnabled)
1965  }
1966
1967  fn is_always_on_top(&self) -> Result<bool> {
1968    window_getter!(self, WindowMessage::IsAlwaysOnTop)
1969  }
1970
1971  #[cfg(any(
1972    target_os = "linux",
1973    target_os = "dragonfly",
1974    target_os = "freebsd",
1975    target_os = "netbsd",
1976    target_os = "openbsd"
1977  ))]
1978  fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
1979    window_getter!(self, WindowMessage::GtkWindow).map(|w| w.0)
1980  }
1981
1982  #[cfg(any(
1983    target_os = "linux",
1984    target_os = "dragonfly",
1985    target_os = "freebsd",
1986    target_os = "netbsd",
1987    target_os = "openbsd"
1988  ))]
1989  fn default_vbox(&self) -> Result<gtk::Box> {
1990    window_getter!(self, WindowMessage::GtkBox).map(|w| w.0)
1991  }
1992
1993  fn window_handle(
1994    &self,
1995  ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
1996    get_raw_window_handle(self)
1997      .map_err(|_| raw_window_handle::HandleError::Unavailable)
1998      .and_then(|r| r.map(|h| unsafe { raw_window_handle::WindowHandle::borrow_raw(h.0) }))
1999  }
2000
2001  // Setters
2002
2003  fn center(&self) -> Result<()> {
2004    send_user_message(
2005      &self.context,
2006      Message::Window(self.window_id, WindowMessage::Center),
2007    )
2008  }
2009
2010  fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
2011    send_user_message(
2012      &self.context,
2013      Message::Window(
2014        self.window_id,
2015        WindowMessage::RequestUserAttention(request_type.map(Into::into)),
2016      ),
2017    )
2018  }
2019
2020  // Creates a window by dispatching a message to the event loop.
2021  // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
2022  fn create_window<F: Fn(RawWindow) + Send + 'static>(
2023    &mut self,
2024    pending: PendingWindow<T, Self::Runtime>,
2025    after_window_creation: Option<F>,
2026  ) -> Result<DetachedWindow<T, Self::Runtime>> {
2027    self.context.create_window(pending, after_window_creation)
2028  }
2029
2030  // Creates a webview by dispatching a message to the event loop.
2031  // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
2032  fn create_webview(
2033    &mut self,
2034    pending: PendingWebview<T, Self::Runtime>,
2035  ) -> Result<DetachedWebview<T, Self::Runtime>> {
2036    self.context.create_webview(self.window_id, pending)
2037  }
2038
2039  fn set_resizable(&self, resizable: bool) -> Result<()> {
2040    send_user_message(
2041      &self.context,
2042      Message::Window(self.window_id, WindowMessage::SetResizable(resizable)),
2043    )
2044  }
2045
2046  fn set_enabled(&self, enabled: bool) -> Result<()> {
2047    send_user_message(
2048      &self.context,
2049      Message::Window(self.window_id, WindowMessage::SetEnabled(enabled)),
2050    )
2051  }
2052
2053  fn set_maximizable(&self, maximizable: bool) -> Result<()> {
2054    send_user_message(
2055      &self.context,
2056      Message::Window(self.window_id, WindowMessage::SetMaximizable(maximizable)),
2057    )
2058  }
2059
2060  fn set_minimizable(&self, minimizable: bool) -> Result<()> {
2061    send_user_message(
2062      &self.context,
2063      Message::Window(self.window_id, WindowMessage::SetMinimizable(minimizable)),
2064    )
2065  }
2066
2067  fn set_closable(&self, closable: bool) -> Result<()> {
2068    send_user_message(
2069      &self.context,
2070      Message::Window(self.window_id, WindowMessage::SetClosable(closable)),
2071    )
2072  }
2073
2074  fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
2075    send_user_message(
2076      &self.context,
2077      Message::Window(self.window_id, WindowMessage::SetTitle(title.into())),
2078    )
2079  }
2080
2081  fn maximize(&self) -> Result<()> {
2082    send_user_message(
2083      &self.context,
2084      Message::Window(self.window_id, WindowMessage::Maximize),
2085    )
2086  }
2087
2088  fn unmaximize(&self) -> Result<()> {
2089    send_user_message(
2090      &self.context,
2091      Message::Window(self.window_id, WindowMessage::Unmaximize),
2092    )
2093  }
2094
2095  fn minimize(&self) -> Result<()> {
2096    send_user_message(
2097      &self.context,
2098      Message::Window(self.window_id, WindowMessage::Minimize),
2099    )
2100  }
2101
2102  fn unminimize(&self) -> Result<()> {
2103    send_user_message(
2104      &self.context,
2105      Message::Window(self.window_id, WindowMessage::Unminimize),
2106    )
2107  }
2108
2109  fn show(&self) -> Result<()> {
2110    send_user_message(
2111      &self.context,
2112      Message::Window(self.window_id, WindowMessage::Show),
2113    )
2114  }
2115
2116  fn hide(&self) -> Result<()> {
2117    send_user_message(
2118      &self.context,
2119      Message::Window(self.window_id, WindowMessage::Hide),
2120    )
2121  }
2122
2123  fn close(&self) -> Result<()> {
2124    // NOTE: close cannot use the `send_user_message` function because it accesses the event loop callback
2125    self
2126      .context
2127      .proxy
2128      .send_event(Message::Window(self.window_id, WindowMessage::Close))
2129      .map_err(|_| Error::FailedToSendMessage)
2130  }
2131
2132  fn destroy(&self) -> Result<()> {
2133    // NOTE: destroy cannot use the `send_user_message` function because it accesses the event loop callback
2134    self
2135      .context
2136      .proxy
2137      .send_event(Message::Window(self.window_id, WindowMessage::Destroy))
2138      .map_err(|_| Error::FailedToSendMessage)
2139  }
2140
2141  fn set_decorations(&self, decorations: bool) -> Result<()> {
2142    send_user_message(
2143      &self.context,
2144      Message::Window(self.window_id, WindowMessage::SetDecorations(decorations)),
2145    )
2146  }
2147
2148  fn set_shadow(&self, enable: bool) -> Result<()> {
2149    send_user_message(
2150      &self.context,
2151      Message::Window(self.window_id, WindowMessage::SetShadow(enable)),
2152    )
2153  }
2154
2155  fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> {
2156    send_user_message(
2157      &self.context,
2158      Message::Window(
2159        self.window_id,
2160        WindowMessage::SetAlwaysOnBottom(always_on_bottom),
2161      ),
2162    )
2163  }
2164
2165  fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
2166    send_user_message(
2167      &self.context,
2168      Message::Window(self.window_id, WindowMessage::SetAlwaysOnTop(always_on_top)),
2169    )
2170  }
2171
2172  fn set_visible_on_all_workspaces(&self, visible_on_all_workspaces: bool) -> Result<()> {
2173    send_user_message(
2174      &self.context,
2175      Message::Window(
2176        self.window_id,
2177        WindowMessage::SetVisibleOnAllWorkspaces(visible_on_all_workspaces),
2178      ),
2179    )
2180  }
2181
2182  fn set_content_protected(&self, protected: bool) -> Result<()> {
2183    send_user_message(
2184      &self.context,
2185      Message::Window(
2186        self.window_id,
2187        WindowMessage::SetContentProtected(protected),
2188      ),
2189    )
2190  }
2191
2192  fn set_size(&self, size: Size) -> Result<()> {
2193    send_user_message(
2194      &self.context,
2195      Message::Window(self.window_id, WindowMessage::SetSize(size)),
2196    )
2197  }
2198
2199  fn set_min_size(&self, size: Option<Size>) -> Result<()> {
2200    send_user_message(
2201      &self.context,
2202      Message::Window(self.window_id, WindowMessage::SetMinSize(size)),
2203    )
2204  }
2205
2206  fn set_max_size(&self, size: Option<Size>) -> Result<()> {
2207    send_user_message(
2208      &self.context,
2209      Message::Window(self.window_id, WindowMessage::SetMaxSize(size)),
2210    )
2211  }
2212
2213  fn set_size_constraints(&self, constraints: WindowSizeConstraints) -> Result<()> {
2214    send_user_message(
2215      &self.context,
2216      Message::Window(
2217        self.window_id,
2218        WindowMessage::SetSizeConstraints(constraints),
2219      ),
2220    )
2221  }
2222
2223  fn set_position(&self, position: Position) -> Result<()> {
2224    send_user_message(
2225      &self.context,
2226      Message::Window(self.window_id, WindowMessage::SetPosition(position)),
2227    )
2228  }
2229
2230  fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
2231    send_user_message(
2232      &self.context,
2233      Message::Window(self.window_id, WindowMessage::SetFullscreen(fullscreen)),
2234    )
2235  }
2236
2237  #[cfg(target_os = "macos")]
2238  fn set_simple_fullscreen(&self, enable: bool) -> Result<()> {
2239    send_user_message(
2240      &self.context,
2241      Message::Window(self.window_id, WindowMessage::SetSimpleFullscreen(enable)),
2242    )
2243  }
2244
2245  fn set_focus(&self) -> Result<()> {
2246    send_user_message(
2247      &self.context,
2248      Message::Window(self.window_id, WindowMessage::SetFocus),
2249    )
2250  }
2251
2252  fn set_focusable(&self, focusable: bool) -> Result<()> {
2253    send_user_message(
2254      &self.context,
2255      Message::Window(self.window_id, WindowMessage::SetFocusable(focusable)),
2256    )
2257  }
2258
2259  fn set_icon(&self, icon: Icon) -> Result<()> {
2260    send_user_message(
2261      &self.context,
2262      Message::Window(
2263        self.window_id,
2264        WindowMessage::SetIcon(TaoIcon::try_from(icon)?.0),
2265      ),
2266    )
2267  }
2268
2269  fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
2270    send_user_message(
2271      &self.context,
2272      Message::Window(self.window_id, WindowMessage::SetSkipTaskbar(skip)),
2273    )
2274  }
2275
2276  fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
2277    send_user_message(
2278      &self.context,
2279      Message::Window(self.window_id, WindowMessage::SetCursorGrab(grab)),
2280    )
2281  }
2282
2283  fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
2284    send_user_message(
2285      &self.context,
2286      Message::Window(self.window_id, WindowMessage::SetCursorVisible(visible)),
2287    )
2288  }
2289
2290  fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
2291    send_user_message(
2292      &self.context,
2293      Message::Window(self.window_id, WindowMessage::SetCursorIcon(icon)),
2294    )
2295  }
2296
2297  fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
2298    send_user_message(
2299      &self.context,
2300      Message::Window(
2301        self.window_id,
2302        WindowMessage::SetCursorPosition(position.into()),
2303      ),
2304    )
2305  }
2306
2307  fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
2308    send_user_message(
2309      &self.context,
2310      Message::Window(self.window_id, WindowMessage::SetIgnoreCursorEvents(ignore)),
2311    )
2312  }
2313
2314  fn start_dragging(&self) -> Result<()> {
2315    send_user_message(
2316      &self.context,
2317      Message::Window(self.window_id, WindowMessage::DragWindow),
2318    )
2319  }
2320
2321  fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> {
2322    send_user_message(
2323      &self.context,
2324      Message::Window(self.window_id, WindowMessage::ResizeDragWindow(direction)),
2325    )
2326  }
2327
2328  fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
2329    send_user_message(
2330      &self.context,
2331      Message::Window(
2332        self.window_id,
2333        WindowMessage::SetBadgeCount(count, desktop_filename),
2334      ),
2335    )
2336  }
2337
2338  fn set_badge_label(&self, label: Option<String>) -> Result<()> {
2339    send_user_message(
2340      &self.context,
2341      Message::Window(self.window_id, WindowMessage::SetBadgeLabel(label)),
2342    )
2343  }
2344
2345  fn set_overlay_icon(&self, icon: Option<Icon>) -> Result<()> {
2346    let icon: Result<Option<TaoIcon>> = icon.map_or(Ok(None), |x| Ok(Some(TaoIcon::try_from(x)?)));
2347
2348    send_user_message(
2349      &self.context,
2350      Message::Window(self.window_id, WindowMessage::SetOverlayIcon(icon?)),
2351    )
2352  }
2353
2354  fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> {
2355    send_user_message(
2356      &self.context,
2357      Message::Window(
2358        self.window_id,
2359        WindowMessage::SetProgressBar(progress_state),
2360      ),
2361    )
2362  }
2363
2364  fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
2365    send_user_message(
2366      &self.context,
2367      Message::Window(self.window_id, WindowMessage::SetTitleBarStyle(style)),
2368    )
2369  }
2370
2371  fn set_traffic_light_position(&self, position: Position) -> Result<()> {
2372    send_user_message(
2373      &self.context,
2374      Message::Window(
2375        self.window_id,
2376        WindowMessage::SetTrafficLightPosition(position),
2377      ),
2378    )
2379  }
2380
2381  fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
2382    send_user_message(
2383      &self.context,
2384      Message::Window(self.window_id, WindowMessage::SetTheme(theme)),
2385    )
2386  }
2387
2388  fn set_background_color(&self, color: Option<Color>) -> Result<()> {
2389    send_user_message(
2390      &self.context,
2391      Message::Window(self.window_id, WindowMessage::SetBackgroundColor(color)),
2392    )
2393  }
2394}
2395
2396#[derive(Clone)]
2397pub struct WebviewWrapper {
2398  label: String,
2399  id: WebviewId,
2400  inner: Rc<WebView>,
2401  context_store: WebContextStore,
2402  webview_event_listeners: WebviewEventListeners,
2403  // the key of the WebContext if it's not shared
2404  context_key: Option<PathBuf>,
2405  bounds: Arc<Mutex<Option<WebviewBounds>>>,
2406}
2407
2408impl Deref for WebviewWrapper {
2409  type Target = WebView;
2410
2411  #[inline(always)]
2412  fn deref(&self) -> &Self::Target {
2413    &self.inner
2414  }
2415}
2416
2417impl Drop for WebviewWrapper {
2418  fn drop(&mut self) {
2419    if Rc::get_mut(&mut self.inner).is_some() {
2420      let mut context_store = self.context_store.lock().unwrap();
2421
2422      if let Some(web_context) = context_store.get_mut(&self.context_key) {
2423        web_context.referenced_by_webviews.remove(&self.label);
2424
2425        if web_context.referenced_by_webviews.is_empty() {
2426          context_store.remove(&self.context_key);
2427        }
2428      }
2429    }
2430  }
2431}
2432
2433pub struct WindowWrapper {
2434  label: String,
2435  inner: Option<Arc<Window>>,
2436  // whether this window has child webviews
2437  // or it's just a container for a single webview
2438  has_children: AtomicBool,
2439  webviews: Vec<WebviewWrapper>,
2440  window_event_listeners: WindowEventListeners,
2441  #[cfg(windows)]
2442  background_color: Option<tao::window::RGBA>,
2443  #[cfg(windows)]
2444  is_window_transparent: bool,
2445  #[cfg(windows)]
2446  surface: Option<softbuffer::Surface<Arc<Window>, Arc<Window>>>,
2447  focused_webview: Arc<Mutex<Option<String>>>,
2448}
2449
2450impl WindowWrapper {
2451  pub fn label(&self) -> &str {
2452    &self.label
2453  }
2454}
2455
2456impl fmt::Debug for WindowWrapper {
2457  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2458    f.debug_struct("WindowWrapper")
2459      .field("label", &self.label)
2460      .field("inner", &self.inner)
2461      .finish()
2462  }
2463}
2464
2465#[derive(Debug, Clone)]
2466pub struct EventProxy<T: UserEvent>(TaoEventLoopProxy<Message<T>>);
2467
2468#[cfg(target_os = "ios")]
2469#[allow(clippy::non_send_fields_in_send_ty)]
2470unsafe impl<T: UserEvent> Sync for EventProxy<T> {}
2471
2472impl<T: UserEvent> EventLoopProxy<T> for EventProxy<T> {
2473  fn send_event(&self, event: T) -> Result<()> {
2474    self
2475      .0
2476      .send_event(Message::UserEvent(event))
2477      .map_err(|_| Error::EventLoopClosed)
2478  }
2479}
2480
2481pub trait PluginBuilder<T: UserEvent> {
2482  type Plugin: Plugin<T>;
2483  fn build(self, context: Context<T>) -> Self::Plugin;
2484}
2485
2486pub trait Plugin<T: UserEvent> {
2487  fn on_event(
2488    &mut self,
2489    event: &Event<Message<T>>,
2490    event_loop: &EventLoopWindowTarget<Message<T>>,
2491    proxy: &TaoEventLoopProxy<Message<T>>,
2492    control_flow: &mut ControlFlow,
2493    context: EventLoopIterationContext<'_, T>,
2494    web_context: &WebContextStore,
2495  ) -> bool;
2496}
2497
2498/// A Tauri [`Runtime`] wrapper around wry.
2499pub struct Wry<T: UserEvent> {
2500  context: Context<T>,
2501  event_loop: EventLoop<Message<T>>,
2502}
2503
2504impl<T: UserEvent> fmt::Debug for Wry<T> {
2505  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2506    f.debug_struct("Wry")
2507      .field("main_thread_id", &self.context.main_thread_id)
2508      .field("event_loop", &self.event_loop)
2509      .field("windows", &self.context.main_thread.windows)
2510      .field("web_context", &self.context.main_thread.web_context)
2511      .finish()
2512  }
2513}
2514
2515/// A handle to the Wry runtime.
2516#[derive(Debug, Clone)]
2517pub struct WryHandle<T: UserEvent> {
2518  context: Context<T>,
2519}
2520
2521// SAFETY: this is safe since the `Context` usage is guarded on `send_user_message`.
2522#[allow(clippy::non_send_fields_in_send_ty)]
2523unsafe impl<T: UserEvent> Sync for WryHandle<T> {}
2524
2525impl<T: UserEvent> WryHandle<T> {
2526  /// Creates a new tao window using a callback, and returns its window id.
2527  pub fn create_tao_window<F: FnOnce() -> (String, TaoWindowBuilder) + Send + 'static>(
2528    &self,
2529    f: F,
2530  ) -> Result<Weak<Window>> {
2531    let id = self.context.next_window_id();
2532    let (tx, rx) = channel();
2533    send_user_message(&self.context, Message::CreateRawWindow(id, Box::new(f), tx))?;
2534    rx.recv().unwrap()
2535  }
2536
2537  /// Gets the [`WebviewId'] associated with the given [`WindowId`].
2538  pub fn window_id(&self, window_id: TaoWindowId) -> WindowId {
2539    *self
2540      .context
2541      .window_id_map
2542      .0
2543      .lock()
2544      .unwrap()
2545      .get(&window_id)
2546      .unwrap()
2547  }
2548
2549  /// Send a message to the event loop.
2550  pub fn send_event(&self, message: Message<T>) -> Result<()> {
2551    self
2552      .context
2553      .proxy
2554      .send_event(message)
2555      .map_err(|_| Error::FailedToSendMessage)?;
2556    Ok(())
2557  }
2558
2559  pub fn plugin<P: PluginBuilder<T> + 'static>(&mut self, plugin: P)
2560  where
2561    <P as PluginBuilder<T>>::Plugin: Send,
2562  {
2563    self
2564      .context
2565      .plugins
2566      .lock()
2567      .unwrap()
2568      .push(Box::new(plugin.build(self.context.clone())));
2569  }
2570}
2571
2572impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
2573  type Runtime = Wry<T>;
2574
2575  fn create_proxy(&self) -> EventProxy<T> {
2576    EventProxy(self.context.proxy.clone())
2577  }
2578
2579  #[cfg(target_os = "macos")]
2580  fn set_activation_policy(&self, activation_policy: ActivationPolicy) -> Result<()> {
2581    send_user_message(
2582      &self.context,
2583      Message::SetActivationPolicy(activation_policy),
2584    )
2585  }
2586
2587  #[cfg(target_os = "macos")]
2588  fn set_dock_visibility(&self, visible: bool) -> Result<()> {
2589    send_user_message(&self.context, Message::SetDockVisibility(visible))
2590  }
2591
2592  fn request_exit(&self, code: i32) -> Result<()> {
2593    // NOTE: request_exit cannot use the `send_user_message` function because it accesses the event loop callback
2594    self
2595      .context
2596      .proxy
2597      .send_event(Message::RequestExit(code))
2598      .map_err(|_| Error::FailedToSendMessage)
2599  }
2600
2601  // Creates a window by dispatching a message to the event loop.
2602  // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
2603  fn create_window<F: Fn(RawWindow) + Send + 'static>(
2604    &self,
2605    pending: PendingWindow<T, Self::Runtime>,
2606    after_window_creation: Option<F>,
2607  ) -> Result<DetachedWindow<T, Self::Runtime>> {
2608    self.context.create_window(pending, after_window_creation)
2609  }
2610
2611  // Creates a webview by dispatching a message to the event loop.
2612  // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
2613  fn create_webview(
2614    &self,
2615    window_id: WindowId,
2616    pending: PendingWebview<T, Self::Runtime>,
2617  ) -> Result<DetachedWebview<T, Self::Runtime>> {
2618    self.context.create_webview(window_id, pending)
2619  }
2620
2621  fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
2622    send_user_message(&self.context, Message::Task(Box::new(f)))
2623  }
2624
2625  fn display_handle(
2626    &self,
2627  ) -> std::result::Result<DisplayHandle<'_>, raw_window_handle::HandleError> {
2628    self.context.main_thread.window_target.display_handle()
2629  }
2630
2631  fn primary_monitor(&self) -> Option<Monitor> {
2632    self
2633      .context
2634      .main_thread
2635      .window_target
2636      .primary_monitor()
2637      .map(|m| MonitorHandleWrapper(m).into())
2638  }
2639
2640  fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
2641    self
2642      .context
2643      .main_thread
2644      .window_target
2645      .monitor_from_point(x, y)
2646      .map(|m| MonitorHandleWrapper(m).into())
2647  }
2648
2649  fn available_monitors(&self) -> Vec<Monitor> {
2650    self
2651      .context
2652      .main_thread
2653      .window_target
2654      .available_monitors()
2655      .map(|m| MonitorHandleWrapper(m).into())
2656      .collect()
2657  }
2658
2659  fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
2660    event_loop_window_getter!(self, EventLoopWindowTargetMessage::CursorPosition)?
2661      .map(PhysicalPositionWrapper)
2662      .map(Into::into)
2663      .map_err(|_| Error::FailedToGetCursorPosition)
2664  }
2665
2666  fn set_theme(&self, theme: Option<Theme>) {
2667    let _ = send_user_message(
2668      &self.context,
2669      Message::EventLoopWindowTarget(EventLoopWindowTargetMessage::SetTheme(theme)),
2670    );
2671  }
2672
2673  #[cfg(target_os = "macos")]
2674  fn show(&self) -> tauri_runtime::Result<()> {
2675    send_user_message(
2676      &self.context,
2677      Message::Application(ApplicationMessage::Show),
2678    )
2679  }
2680
2681  #[cfg(target_os = "macos")]
2682  fn hide(&self) -> tauri_runtime::Result<()> {
2683    send_user_message(
2684      &self.context,
2685      Message::Application(ApplicationMessage::Hide),
2686    )
2687  }
2688
2689  fn set_device_event_filter(&self, filter: DeviceEventFilter) {
2690    let _ = send_user_message(
2691      &self.context,
2692      Message::EventLoopWindowTarget(EventLoopWindowTargetMessage::SetDeviceEventFilter(filter)),
2693    );
2694  }
2695
2696  #[cfg(target_os = "android")]
2697  fn find_class<'a>(
2698    &self,
2699    env: &mut jni::JNIEnv<'a>,
2700    activity: &jni::objects::JObject<'_>,
2701    name: impl Into<String>,
2702  ) -> std::result::Result<jni::objects::JClass<'a>, jni::errors::Error> {
2703    find_class(env, activity, name.into())
2704  }
2705
2706  #[cfg(target_os = "android")]
2707  fn run_on_android_context<F>(&self, f: F)
2708  where
2709    F: FnOnce(&mut jni::JNIEnv, &jni::objects::JObject, &jni::objects::JObject) + Send + 'static,
2710  {
2711    dispatch(f)
2712  }
2713
2714  #[cfg(any(target_os = "macos", target_os = "ios"))]
2715  fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(
2716    &self,
2717    cb: F,
2718  ) -> Result<()> {
2719    send_user_message(
2720      &self.context,
2721      Message::Application(ApplicationMessage::FetchDataStoreIdentifiers(Box::new(cb))),
2722    )
2723  }
2724
2725  #[cfg(any(target_os = "macos", target_os = "ios"))]
2726  fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(
2727    &self,
2728    uuid: [u8; 16],
2729    cb: F,
2730  ) -> Result<()> {
2731    send_user_message(
2732      &self.context,
2733      Message::Application(ApplicationMessage::RemoveDataStore(uuid, Box::new(cb))),
2734    )
2735  }
2736}
2737
2738impl<T: UserEvent> Wry<T> {
2739  fn init_with_builder(
2740    mut event_loop_builder: EventLoopBuilder<Message<T>>,
2741    #[allow(unused_variables)] args: RuntimeInitArgs,
2742  ) -> Result<Self> {
2743    #[cfg(windows)]
2744    if let Some(hook) = args.msg_hook {
2745      use tao::platform::windows::EventLoopBuilderExtWindows;
2746      event_loop_builder.with_msg_hook(hook);
2747    }
2748
2749    #[cfg(any(
2750      target_os = "linux",
2751      target_os = "dragonfly",
2752      target_os = "freebsd",
2753      target_os = "netbsd",
2754      target_os = "openbsd"
2755    ))]
2756    if let Some(app_id) = args.app_id {
2757      use tao::platform::unix::EventLoopBuilderExtUnix;
2758      event_loop_builder.with_app_id(app_id);
2759    }
2760    Self::init(event_loop_builder.build())
2761  }
2762
2763  fn init(event_loop: EventLoop<Message<T>>) -> Result<Self> {
2764    let main_thread_id = current_thread().id();
2765    let web_context = WebContextStore::default();
2766
2767    let windows = Arc::new(WindowsStore(RefCell::new(BTreeMap::default())));
2768    let window_id_map = WindowIdStore::default();
2769
2770    let context = Context {
2771      window_id_map,
2772      main_thread_id,
2773      proxy: event_loop.create_proxy(),
2774      main_thread: DispatcherMainThreadContext {
2775        window_target: event_loop.deref().clone(),
2776        web_context,
2777        windows,
2778        #[cfg(feature = "tracing")]
2779        active_tracing_spans: Default::default(),
2780      },
2781      plugins: Default::default(),
2782      next_window_id: Default::default(),
2783      next_webview_id: Default::default(),
2784      next_window_event_id: Default::default(),
2785      next_webview_event_id: Default::default(),
2786      webview_runtime_installed: wry::webview_version().is_ok(),
2787    };
2788
2789    Ok(Self {
2790      context,
2791      event_loop,
2792    })
2793  }
2794}
2795
2796impl<T: UserEvent> Runtime<T> for Wry<T> {
2797  type WindowDispatcher = WryWindowDispatcher<T>;
2798  type WebviewDispatcher = WryWebviewDispatcher<T>;
2799  type Handle = WryHandle<T>;
2800
2801  type EventLoopProxy = EventProxy<T>;
2802
2803  fn new(args: RuntimeInitArgs) -> Result<Self> {
2804    Self::init_with_builder(EventLoopBuilder::<Message<T>>::with_user_event(), args)
2805  }
2806  #[cfg(any(
2807    target_os = "linux",
2808    target_os = "dragonfly",
2809    target_os = "freebsd",
2810    target_os = "netbsd",
2811    target_os = "openbsd"
2812  ))]
2813  fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
2814    use tao::platform::unix::EventLoopBuilderExtUnix;
2815    let mut event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
2816    event_loop_builder.with_any_thread(true);
2817    Self::init_with_builder(event_loop_builder, args)
2818  }
2819
2820  #[cfg(windows)]
2821  fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> {
2822    use tao::platform::windows::EventLoopBuilderExtWindows;
2823    let mut event_loop_builder = EventLoopBuilder::<Message<T>>::with_user_event();
2824    event_loop_builder.with_any_thread(true);
2825    Self::init_with_builder(event_loop_builder, args)
2826  }
2827
2828  fn create_proxy(&self) -> EventProxy<T> {
2829    EventProxy(self.event_loop.create_proxy())
2830  }
2831
2832  fn handle(&self) -> Self::Handle {
2833    WryHandle {
2834      context: self.context.clone(),
2835    }
2836  }
2837
2838  fn create_window<F: Fn(RawWindow) + Send + 'static>(
2839    &self,
2840    pending: PendingWindow<T, Self>,
2841    after_window_creation: Option<F>,
2842  ) -> Result<DetachedWindow<T, Self>> {
2843    let label = pending.label.clone();
2844    let window_id = self.context.next_window_id();
2845    let (webview_id, use_https_scheme) = pending
2846      .webview
2847      .as_ref()
2848      .map(|w| {
2849        (
2850          Some(self.context.next_webview_id()),
2851          w.webview_attributes.use_https_scheme,
2852        )
2853      })
2854      .unwrap_or((None, false));
2855
2856    let window = create_window(
2857      window_id,
2858      webview_id.unwrap_or_default(),
2859      &self.event_loop,
2860      &self.context,
2861      pending,
2862      after_window_creation,
2863    )?;
2864
2865    let dispatcher = WryWindowDispatcher {
2866      window_id,
2867      context: self.context.clone(),
2868    };
2869
2870    self
2871      .context
2872      .main_thread
2873      .windows
2874      .0
2875      .borrow_mut()
2876      .insert(window_id, window);
2877
2878    let detached_webview = webview_id.map(|id| {
2879      let webview = DetachedWebview {
2880        label: label.clone(),
2881        dispatcher: WryWebviewDispatcher {
2882          window_id: Arc::new(Mutex::new(window_id)),
2883          webview_id: id,
2884          context: self.context.clone(),
2885        },
2886      };
2887      DetachedWindowWebview {
2888        webview,
2889        use_https_scheme,
2890      }
2891    });
2892
2893    Ok(DetachedWindow {
2894      id: window_id,
2895      label,
2896      dispatcher,
2897      webview: detached_webview,
2898    })
2899  }
2900
2901  fn create_webview(
2902    &self,
2903    window_id: WindowId,
2904    pending: PendingWebview<T, Self>,
2905  ) -> Result<DetachedWebview<T, Self>> {
2906    let label = pending.label.clone();
2907
2908    let window = self
2909      .context
2910      .main_thread
2911      .windows
2912      .0
2913      .borrow()
2914      .get(&window_id)
2915      .map(|w| (w.inner.clone(), w.focused_webview.clone()));
2916    if let Some((Some(window), focused_webview)) = window {
2917      let window_id_wrapper = Arc::new(Mutex::new(window_id));
2918
2919      let webview_id = self.context.next_webview_id();
2920
2921      let webview = create_webview(
2922        WebviewKind::WindowChild,
2923        &window,
2924        window_id_wrapper.clone(),
2925        webview_id,
2926        &self.context,
2927        pending,
2928        focused_webview,
2929      )?;
2930
2931      #[allow(unknown_lints, clippy::manual_inspect)]
2932      self
2933        .context
2934        .main_thread
2935        .windows
2936        .0
2937        .borrow_mut()
2938        .get_mut(&window_id)
2939        .map(|w| {
2940          w.webviews.push(webview);
2941          w.has_children.store(true, Ordering::Relaxed);
2942          w
2943        });
2944
2945      let dispatcher = WryWebviewDispatcher {
2946        window_id: window_id_wrapper,
2947        webview_id,
2948        context: self.context.clone(),
2949      };
2950
2951      Ok(DetachedWebview { label, dispatcher })
2952    } else {
2953      Err(Error::WindowNotFound)
2954    }
2955  }
2956
2957  fn primary_monitor(&self) -> Option<Monitor> {
2958    self
2959      .context
2960      .main_thread
2961      .window_target
2962      .primary_monitor()
2963      .map(|m| MonitorHandleWrapper(m).into())
2964  }
2965
2966  fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
2967    self
2968      .context
2969      .main_thread
2970      .window_target
2971      .monitor_from_point(x, y)
2972      .map(|m| MonitorHandleWrapper(m).into())
2973  }
2974
2975  fn available_monitors(&self) -> Vec<Monitor> {
2976    self
2977      .context
2978      .main_thread
2979      .window_target
2980      .available_monitors()
2981      .map(|m| MonitorHandleWrapper(m).into())
2982      .collect()
2983  }
2984
2985  fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
2986    self
2987      .context
2988      .main_thread
2989      .window_target
2990      .cursor_position()
2991      .map(PhysicalPositionWrapper)
2992      .map(Into::into)
2993      .map_err(|_| Error::FailedToGetCursorPosition)
2994  }
2995
2996  fn set_theme(&self, theme: Option<Theme>) {
2997    self.event_loop.set_theme(to_tao_theme(theme));
2998  }
2999
3000  #[cfg(target_os = "macos")]
3001  fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
3002    self
3003      .event_loop
3004      .set_activation_policy(tao_activation_policy(activation_policy));
3005  }
3006
3007  #[cfg(target_os = "macos")]
3008  fn set_dock_visibility(&mut self, visible: bool) {
3009    self.event_loop.set_dock_visibility(visible);
3010  }
3011
3012  #[cfg(target_os = "macos")]
3013  fn show(&self) {
3014    self.event_loop.show_application();
3015  }
3016
3017  #[cfg(target_os = "macos")]
3018  fn hide(&self) {
3019    self.event_loop.hide_application();
3020  }
3021
3022  fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {
3023    self
3024      .event_loop
3025      .set_device_event_filter(DeviceEventFilterWrapper::from(filter).0);
3026  }
3027
3028  #[cfg(desktop)]
3029  fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, mut callback: F) {
3030    use tao::platform::run_return::EventLoopExtRunReturn;
3031    let windows = self.context.main_thread.windows.clone();
3032    let window_id_map = self.context.window_id_map.clone();
3033    let web_context = &self.context.main_thread.web_context;
3034    let plugins = self.context.plugins.clone();
3035
3036    #[cfg(feature = "tracing")]
3037    let active_tracing_spans = self.context.main_thread.active_tracing_spans.clone();
3038
3039    let proxy = self.event_loop.create_proxy();
3040
3041    self
3042      .event_loop
3043      .run_return(|event, event_loop, control_flow| {
3044        *control_flow = ControlFlow::Wait;
3045        if let Event::MainEventsCleared = &event {
3046          *control_flow = ControlFlow::Exit;
3047        }
3048
3049        for p in plugins.lock().unwrap().iter_mut() {
3050          let prevent_default = p.on_event(
3051            &event,
3052            event_loop,
3053            &proxy,
3054            control_flow,
3055            EventLoopIterationContext {
3056              callback: &mut callback,
3057              window_id_map: window_id_map.clone(),
3058              windows: windows.clone(),
3059              #[cfg(feature = "tracing")]
3060              active_tracing_spans: active_tracing_spans.clone(),
3061            },
3062            web_context,
3063          );
3064          if prevent_default {
3065            return;
3066          }
3067        }
3068
3069        handle_event_loop(
3070          event,
3071          event_loop,
3072          control_flow,
3073          EventLoopIterationContext {
3074            callback: &mut callback,
3075            windows: windows.clone(),
3076            window_id_map: window_id_map.clone(),
3077            #[cfg(feature = "tracing")]
3078            active_tracing_spans: active_tracing_spans.clone(),
3079          },
3080        );
3081      });
3082  }
3083
3084  fn run<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) {
3085    let event_handler = make_event_handler(&self, callback);
3086
3087    self.event_loop.run(event_handler)
3088  }
3089
3090  #[cfg(not(target_os = "ios"))]
3091  fn run_return<F: FnMut(RunEvent<T>) + 'static>(mut self, callback: F) -> i32 {
3092    use tao::platform::run_return::EventLoopExtRunReturn;
3093
3094    let event_handler = make_event_handler(&self, callback);
3095
3096    self.event_loop.run_return(event_handler)
3097  }
3098
3099  #[cfg(target_os = "ios")]
3100  fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32 {
3101    self.run(callback);
3102    0
3103  }
3104}
3105
3106fn make_event_handler<T, F>(
3107  runtime: &Wry<T>,
3108  mut callback: F,
3109) -> impl FnMut(Event<'_, Message<T>>, &EventLoopWindowTarget<Message<T>>, &mut ControlFlow)
3110where
3111  T: UserEvent,
3112  F: FnMut(RunEvent<T>) + 'static,
3113{
3114  let windows = runtime.context.main_thread.windows.clone();
3115  let window_id_map = runtime.context.window_id_map.clone();
3116  let web_context = runtime.context.main_thread.web_context.clone();
3117  let plugins = runtime.context.plugins.clone();
3118
3119  #[cfg(feature = "tracing")]
3120  let active_tracing_spans = runtime.context.main_thread.active_tracing_spans.clone();
3121  let proxy = runtime.event_loop.create_proxy();
3122
3123  move |event, event_loop, control_flow| {
3124    for p in plugins.lock().unwrap().iter_mut() {
3125      let prevent_default = p.on_event(
3126        &event,
3127        event_loop,
3128        &proxy,
3129        control_flow,
3130        EventLoopIterationContext {
3131          callback: &mut callback,
3132          window_id_map: window_id_map.clone(),
3133          windows: windows.clone(),
3134          #[cfg(feature = "tracing")]
3135          active_tracing_spans: active_tracing_spans.clone(),
3136        },
3137        &web_context,
3138      );
3139      if prevent_default {
3140        return;
3141      }
3142    }
3143    handle_event_loop(
3144      event,
3145      event_loop,
3146      control_flow,
3147      EventLoopIterationContext {
3148        callback: &mut callback,
3149        window_id_map: window_id_map.clone(),
3150        windows: windows.clone(),
3151        #[cfg(feature = "tracing")]
3152        active_tracing_spans: active_tracing_spans.clone(),
3153      },
3154    );
3155  }
3156}
3157
3158pub struct EventLoopIterationContext<'a, T: UserEvent> {
3159  pub callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
3160  pub window_id_map: WindowIdStore,
3161  pub windows: Arc<WindowsStore>,
3162  #[cfg(feature = "tracing")]
3163  pub active_tracing_spans: ActiveTraceSpanStore,
3164}
3165
3166struct UserMessageContext {
3167  windows: Arc<WindowsStore>,
3168  window_id_map: WindowIdStore,
3169}
3170
3171fn handle_user_message<T: UserEvent>(
3172  event_loop: &EventLoopWindowTarget<Message<T>>,
3173  message: Message<T>,
3174  context: UserMessageContext,
3175) {
3176  let UserMessageContext {
3177    window_id_map,
3178    windows,
3179  } = context;
3180  match message {
3181    Message::Task(task) => task(),
3182    #[cfg(target_os = "macos")]
3183    Message::SetActivationPolicy(activation_policy) => {
3184      event_loop.set_activation_policy_at_runtime(tao_activation_policy(activation_policy))
3185    }
3186    #[cfg(target_os = "macos")]
3187    Message::SetDockVisibility(visible) => event_loop.set_dock_visibility(visible),
3188    Message::RequestExit(_code) => panic!("cannot handle RequestExit on the main thread"),
3189    Message::Application(application_message) => match application_message {
3190      #[cfg(target_os = "macos")]
3191      ApplicationMessage::Show => {
3192        event_loop.show_application();
3193      }
3194      #[cfg(target_os = "macos")]
3195      ApplicationMessage::Hide => {
3196        event_loop.hide_application();
3197      }
3198      #[cfg(any(target_os = "macos", target_os = "ios"))]
3199      ApplicationMessage::FetchDataStoreIdentifiers(cb) => {
3200        if let Err(e) = WebView::fetch_data_store_identifiers(cb) {
3201          // this shouldn't ever happen because we're running on the main thread
3202          // but let's be safe and warn here
3203          log::error!("failed to fetch data store identifiers: {e}");
3204        }
3205      }
3206      #[cfg(any(target_os = "macos", target_os = "ios"))]
3207      ApplicationMessage::RemoveDataStore(uuid, cb) => {
3208        WebView::remove_data_store(&uuid, move |res| {
3209          cb(res.map_err(|_| Error::FailedToRemoveDataStore))
3210        })
3211      }
3212    },
3213    Message::Window(id, window_message) => {
3214      let w = windows.0.borrow().get(&id).map(|w| {
3215        (
3216          w.inner.clone(),
3217          w.webviews.clone(),
3218          w.has_children.load(Ordering::Relaxed),
3219          w.window_event_listeners.clone(),
3220        )
3221      });
3222      if let Some((Some(window), webviews, has_children, window_event_listeners)) = w {
3223        match window_message {
3224          WindowMessage::AddEventListener(id, listener) => {
3225            window_event_listeners.lock().unwrap().insert(id, listener);
3226          }
3227
3228          // Getters
3229          WindowMessage::ScaleFactor(tx) => tx.send(window.scale_factor()).unwrap(),
3230          WindowMessage::InnerPosition(tx) => tx
3231            .send(
3232              window
3233                .inner_position()
3234                .map(|p| PhysicalPositionWrapper(p).into())
3235                .map_err(|_| Error::FailedToSendMessage),
3236            )
3237            .unwrap(),
3238          WindowMessage::OuterPosition(tx) => tx
3239            .send(
3240              window
3241                .outer_position()
3242                .map(|p| PhysicalPositionWrapper(p).into())
3243                .map_err(|_| Error::FailedToSendMessage),
3244            )
3245            .unwrap(),
3246          WindowMessage::InnerSize(tx) => tx
3247            .send(PhysicalSizeWrapper(inner_size(&window, &webviews, has_children)).into())
3248            .unwrap(),
3249          WindowMessage::OuterSize(tx) => tx
3250            .send(PhysicalSizeWrapper(window.outer_size()).into())
3251            .unwrap(),
3252          WindowMessage::IsFullscreen(tx) => tx.send(window.fullscreen().is_some()).unwrap(),
3253          WindowMessage::IsMinimized(tx) => tx.send(window.is_minimized()).unwrap(),
3254          WindowMessage::IsMaximized(tx) => tx.send(window.is_maximized()).unwrap(),
3255          WindowMessage::IsFocused(tx) => tx.send(window.is_focused()).unwrap(),
3256          WindowMessage::IsDecorated(tx) => tx.send(window.is_decorated()).unwrap(),
3257          WindowMessage::IsResizable(tx) => tx.send(window.is_resizable()).unwrap(),
3258          WindowMessage::IsMaximizable(tx) => tx.send(window.is_maximizable()).unwrap(),
3259          WindowMessage::IsMinimizable(tx) => tx.send(window.is_minimizable()).unwrap(),
3260          WindowMessage::IsClosable(tx) => tx.send(window.is_closable()).unwrap(),
3261          WindowMessage::IsVisible(tx) => tx.send(window.is_visible()).unwrap(),
3262          WindowMessage::Title(tx) => tx.send(window.title()).unwrap(),
3263          WindowMessage::CurrentMonitor(tx) => tx.send(window.current_monitor()).unwrap(),
3264          WindowMessage::PrimaryMonitor(tx) => tx.send(window.primary_monitor()).unwrap(),
3265          WindowMessage::MonitorFromPoint(tx, (x, y)) => {
3266            tx.send(window.monitor_from_point(x, y)).unwrap()
3267          }
3268          WindowMessage::AvailableMonitors(tx) => {
3269            tx.send(window.available_monitors().collect()).unwrap()
3270          }
3271          #[cfg(any(
3272            target_os = "linux",
3273            target_os = "dragonfly",
3274            target_os = "freebsd",
3275            target_os = "netbsd",
3276            target_os = "openbsd"
3277          ))]
3278          WindowMessage::GtkWindow(tx) => tx.send(GtkWindow(window.gtk_window().clone())).unwrap(),
3279          #[cfg(any(
3280            target_os = "linux",
3281            target_os = "dragonfly",
3282            target_os = "freebsd",
3283            target_os = "netbsd",
3284            target_os = "openbsd"
3285          ))]
3286          WindowMessage::GtkBox(tx) => tx
3287            .send(GtkBox(window.default_vbox().unwrap().clone()))
3288            .unwrap(),
3289          WindowMessage::RawWindowHandle(tx) => tx
3290            .send(
3291              window
3292                .window_handle()
3293                .map(|h| SendRawWindowHandle(h.as_raw())),
3294            )
3295            .unwrap(),
3296          WindowMessage::Theme(tx) => {
3297            tx.send(map_theme(&window.theme())).unwrap();
3298          }
3299          WindowMessage::IsEnabled(tx) => tx.send(window.is_enabled()).unwrap(),
3300          WindowMessage::IsAlwaysOnTop(tx) => tx.send(window.is_always_on_top()).unwrap(),
3301          // Setters
3302          WindowMessage::Center => window.center(),
3303          WindowMessage::RequestUserAttention(request_type) => {
3304            window.request_user_attention(request_type.map(|r| r.0));
3305          }
3306          WindowMessage::SetResizable(resizable) => {
3307            window.set_resizable(resizable);
3308            #[cfg(windows)]
3309            if !resizable {
3310              undecorated_resizing::detach_resize_handler(window.hwnd());
3311            } else if !window.is_decorated() {
3312              undecorated_resizing::attach_resize_handler(
3313                window.hwnd(),
3314                window.has_undecorated_shadow(),
3315              );
3316            }
3317          }
3318          WindowMessage::SetMaximizable(maximizable) => window.set_maximizable(maximizable),
3319          WindowMessage::SetMinimizable(minimizable) => window.set_minimizable(minimizable),
3320          WindowMessage::SetClosable(closable) => window.set_closable(closable),
3321          WindowMessage::SetTitle(title) => window.set_title(&title),
3322          WindowMessage::Maximize => window.set_maximized(true),
3323          WindowMessage::Unmaximize => window.set_maximized(false),
3324          WindowMessage::Minimize => window.set_minimized(true),
3325          WindowMessage::Unminimize => window.set_minimized(false),
3326          WindowMessage::SetEnabled(enabled) => window.set_enabled(enabled),
3327          WindowMessage::Show => window.set_visible(true),
3328          WindowMessage::Hide => window.set_visible(false),
3329          WindowMessage::Close => {
3330            panic!("cannot handle `WindowMessage::Close` on the main thread")
3331          }
3332          WindowMessage::Destroy => {
3333            panic!("cannot handle `WindowMessage::Destroy` on the main thread")
3334          }
3335          WindowMessage::SetDecorations(decorations) => {
3336            window.set_decorations(decorations);
3337            #[cfg(windows)]
3338            if decorations {
3339              undecorated_resizing::detach_resize_handler(window.hwnd());
3340            } else if window.is_resizable() {
3341              undecorated_resizing::attach_resize_handler(
3342                window.hwnd(),
3343                window.has_undecorated_shadow(),
3344              );
3345            }
3346          }
3347          WindowMessage::SetShadow(_enable) => {
3348            #[cfg(windows)]
3349            {
3350              window.set_undecorated_shadow(_enable);
3351              undecorated_resizing::update_drag_hwnd_rgn_for_undecorated(window.hwnd(), _enable);
3352            }
3353            #[cfg(target_os = "macos")]
3354            window.set_has_shadow(_enable);
3355          }
3356          WindowMessage::SetAlwaysOnBottom(always_on_bottom) => {
3357            window.set_always_on_bottom(always_on_bottom)
3358          }
3359          WindowMessage::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top),
3360          WindowMessage::SetVisibleOnAllWorkspaces(visible_on_all_workspaces) => {
3361            window.set_visible_on_all_workspaces(visible_on_all_workspaces)
3362          }
3363          WindowMessage::SetContentProtected(protected) => window.set_content_protection(protected),
3364          WindowMessage::SetSize(size) => {
3365            window.set_inner_size(SizeWrapper::from(size).0);
3366          }
3367          WindowMessage::SetMinSize(size) => {
3368            window.set_min_inner_size(size.map(|s| SizeWrapper::from(s).0));
3369          }
3370          WindowMessage::SetMaxSize(size) => {
3371            window.set_max_inner_size(size.map(|s| SizeWrapper::from(s).0));
3372          }
3373          WindowMessage::SetSizeConstraints(constraints) => {
3374            window.set_inner_size_constraints(tao::window::WindowSizeConstraints {
3375              min_width: constraints.min_width,
3376              min_height: constraints.min_height,
3377              max_width: constraints.max_width,
3378              max_height: constraints.max_height,
3379            });
3380          }
3381          WindowMessage::SetPosition(position) => {
3382            window.set_outer_position(PositionWrapper::from(position).0)
3383          }
3384          WindowMessage::SetFullscreen(fullscreen) => {
3385            if fullscreen {
3386              window.set_fullscreen(Some(Fullscreen::Borderless(None)))
3387            } else {
3388              window.set_fullscreen(None)
3389            }
3390          }
3391
3392          #[cfg(target_os = "macos")]
3393          WindowMessage::SetSimpleFullscreen(enable) => {
3394            window.set_simple_fullscreen(enable);
3395          }
3396
3397          WindowMessage::SetFocus => {
3398            window.set_focus();
3399          }
3400          WindowMessage::SetFocusable(focusable) => {
3401            window.set_focusable(focusable);
3402          }
3403          WindowMessage::SetIcon(icon) => {
3404            window.set_window_icon(Some(icon));
3405          }
3406          #[allow(unused_variables)]
3407          WindowMessage::SetSkipTaskbar(skip) => {
3408            #[cfg(any(windows, target_os = "linux"))]
3409            let _ = window.set_skip_taskbar(skip);
3410          }
3411          WindowMessage::SetCursorGrab(grab) => {
3412            let _ = window.set_cursor_grab(grab);
3413          }
3414          WindowMessage::SetCursorVisible(visible) => {
3415            window.set_cursor_visible(visible);
3416          }
3417          WindowMessage::SetCursorIcon(icon) => {
3418            window.set_cursor_icon(CursorIconWrapper::from(icon).0);
3419          }
3420          WindowMessage::SetCursorPosition(position) => {
3421            let _ = window.set_cursor_position(PositionWrapper::from(position).0);
3422          }
3423          WindowMessage::SetIgnoreCursorEvents(ignore) => {
3424            let _ = window.set_ignore_cursor_events(ignore);
3425          }
3426          WindowMessage::DragWindow => {
3427            let _ = window.drag_window();
3428          }
3429          WindowMessage::ResizeDragWindow(direction) => {
3430            let _ = window.drag_resize_window(match direction {
3431              tauri_runtime::ResizeDirection::East => tao::window::ResizeDirection::East,
3432              tauri_runtime::ResizeDirection::North => tao::window::ResizeDirection::North,
3433              tauri_runtime::ResizeDirection::NorthEast => tao::window::ResizeDirection::NorthEast,
3434              tauri_runtime::ResizeDirection::NorthWest => tao::window::ResizeDirection::NorthWest,
3435              tauri_runtime::ResizeDirection::South => tao::window::ResizeDirection::South,
3436              tauri_runtime::ResizeDirection::SouthEast => tao::window::ResizeDirection::SouthEast,
3437              tauri_runtime::ResizeDirection::SouthWest => tao::window::ResizeDirection::SouthWest,
3438              tauri_runtime::ResizeDirection::West => tao::window::ResizeDirection::West,
3439            });
3440          }
3441          WindowMessage::RequestRedraw => {
3442            window.request_redraw();
3443          }
3444          WindowMessage::SetBadgeCount(_count, _desktop_filename) => {
3445            #[cfg(target_os = "ios")]
3446            window.set_badge_count(
3447              _count.map_or(0, |x| x.clamp(i32::MIN as i64, i32::MAX as i64) as i32),
3448            );
3449
3450            #[cfg(target_os = "macos")]
3451            window.set_badge_label(_count.map(|x| x.to_string()));
3452
3453            #[cfg(any(
3454              target_os = "linux",
3455              target_os = "dragonfly",
3456              target_os = "freebsd",
3457              target_os = "netbsd",
3458              target_os = "openbsd"
3459            ))]
3460            window.set_badge_count(_count, _desktop_filename);
3461          }
3462          WindowMessage::SetBadgeLabel(_label) => {
3463            #[cfg(target_os = "macos")]
3464            window.set_badge_label(_label);
3465          }
3466          WindowMessage::SetOverlayIcon(_icon) => {
3467            #[cfg(windows)]
3468            window.set_overlay_icon(_icon.map(|x| x.0).as_ref());
3469          }
3470          WindowMessage::SetProgressBar(progress_state) => {
3471            window.set_progress_bar(ProgressBarStateWrapper::from(progress_state).0);
3472          }
3473          WindowMessage::SetTitleBarStyle(_style) => {
3474            #[cfg(target_os = "macos")]
3475            match _style {
3476              TitleBarStyle::Visible => {
3477                window.set_titlebar_transparent(false);
3478                window.set_fullsize_content_view(true);
3479              }
3480              TitleBarStyle::Transparent => {
3481                window.set_titlebar_transparent(true);
3482                window.set_fullsize_content_view(false);
3483              }
3484              TitleBarStyle::Overlay => {
3485                window.set_titlebar_transparent(true);
3486                window.set_fullsize_content_view(true);
3487              }
3488              unknown => {
3489                #[cfg(feature = "tracing")]
3490                tracing::warn!("unknown title bar style applied: {unknown}");
3491
3492                #[cfg(not(feature = "tracing"))]
3493                eprintln!("unknown title bar style applied: {unknown}");
3494              }
3495            };
3496          }
3497          WindowMessage::SetTrafficLightPosition(_position) => {
3498            #[cfg(target_os = "macos")]
3499            window.set_traffic_light_inset(_position);
3500          }
3501          WindowMessage::SetTheme(theme) => {
3502            window.set_theme(to_tao_theme(theme));
3503          }
3504          WindowMessage::SetBackgroundColor(color) => {
3505            window.set_background_color(color.map(Into::into))
3506          }
3507        }
3508      }
3509    }
3510    Message::Webview(window_id, webview_id, webview_message) => {
3511      #[cfg(any(
3512        target_os = "macos",
3513        windows,
3514        target_os = "linux",
3515        target_os = "dragonfly",
3516        target_os = "freebsd",
3517        target_os = "netbsd",
3518        target_os = "openbsd"
3519      ))]
3520      if let WebviewMessage::Reparent(new_parent_window_id, tx) = webview_message {
3521        let webview_handle = windows.0.borrow_mut().get_mut(&window_id).and_then(|w| {
3522          w.webviews
3523            .iter()
3524            .position(|w| w.id == webview_id)
3525            .map(|webview_index| w.webviews.remove(webview_index))
3526        });
3527
3528        if let Some(webview) = webview_handle {
3529          if let Some((Some(new_parent_window), new_parent_window_webviews)) = windows
3530            .0
3531            .borrow_mut()
3532            .get_mut(&new_parent_window_id)
3533            .map(|w| (w.inner.clone(), &mut w.webviews))
3534          {
3535            #[cfg(target_os = "macos")]
3536            let reparent_result = {
3537              use wry::WebViewExtMacOS;
3538              webview.inner.reparent(new_parent_window.ns_window() as _)
3539            };
3540            #[cfg(windows)]
3541            let reparent_result = { webview.inner.reparent(new_parent_window.hwnd()) };
3542
3543            #[cfg(any(
3544              target_os = "linux",
3545              target_os = "dragonfly",
3546              target_os = "freebsd",
3547              target_os = "netbsd",
3548              target_os = "openbsd"
3549            ))]
3550            let reparent_result = {
3551              if let Some(container) = new_parent_window.default_vbox() {
3552                webview.inner.reparent(container)
3553              } else {
3554                Err(wry::Error::MessageSender)
3555              }
3556            };
3557
3558            match reparent_result {
3559              Ok(_) => {
3560                new_parent_window_webviews.push(webview);
3561                tx.send(Ok(())).unwrap();
3562              }
3563              Err(e) => {
3564                log::error!("failed to reparent webview: {e}");
3565                tx.send(Err(Error::FailedToSendMessage)).unwrap();
3566              }
3567            }
3568          }
3569        } else {
3570          tx.send(Err(Error::FailedToSendMessage)).unwrap();
3571        }
3572
3573        return;
3574      }
3575
3576      let webview_handle = windows.0.borrow().get(&window_id).map(|w| {
3577        (
3578          w.inner.clone(),
3579          w.webviews.iter().find(|w| w.id == webview_id).cloned(),
3580        )
3581      });
3582      if let Some((Some(window), Some(webview))) = webview_handle {
3583        match webview_message {
3584          WebviewMessage::WebviewEvent(_) => { /* already handled */ }
3585          WebviewMessage::SynthesizedWindowEvent(_) => { /* already handled */ }
3586          WebviewMessage::Reparent(_window_id, _tx) => { /* already handled */ }
3587          WebviewMessage::AddEventListener(id, listener) => {
3588            webview
3589              .webview_event_listeners
3590              .lock()
3591              .unwrap()
3592              .insert(id, listener);
3593          }
3594
3595          #[cfg(all(feature = "tracing", not(target_os = "android")))]
3596          WebviewMessage::EvaluateScript(script, tx, span) => {
3597            let _span = span.entered();
3598            if let Err(e) = webview.evaluate_script(&script) {
3599              log::error!("{e}");
3600            }
3601            tx.send(()).unwrap();
3602          }
3603          #[cfg(not(all(feature = "tracing", not(target_os = "android"))))]
3604          WebviewMessage::EvaluateScript(script) => {
3605            if let Err(e) = webview.evaluate_script(&script) {
3606              log::error!("{e}");
3607            }
3608          }
3609          WebviewMessage::Navigate(url) => {
3610            if let Err(e) = webview.load_url(url.as_str()) {
3611              log::error!("failed to navigate to url {}: {}", url, e);
3612            }
3613          }
3614          WebviewMessage::Reload => {
3615            if let Err(e) = webview.reload() {
3616              log::error!("failed to reload: {e}");
3617            }
3618          }
3619          WebviewMessage::Show => {
3620            if let Err(e) = webview.set_visible(true) {
3621              log::error!("failed to change webview visibility: {e}");
3622            }
3623          }
3624          WebviewMessage::Hide => {
3625            if let Err(e) = webview.set_visible(false) {
3626              log::error!("failed to change webview visibility: {e}");
3627            }
3628          }
3629          WebviewMessage::Print => {
3630            let _ = webview.print();
3631          }
3632          WebviewMessage::Close => {
3633            #[allow(unknown_lints, clippy::manual_inspect)]
3634            windows.0.borrow_mut().get_mut(&window_id).map(|window| {
3635              if let Some(i) = window.webviews.iter().position(|w| w.id == webview.id) {
3636                window.webviews.remove(i);
3637              }
3638              window
3639            });
3640          }
3641          WebviewMessage::SetBounds(bounds) => {
3642            let bounds: RectWrapper = bounds.into();
3643            let bounds = bounds.0;
3644
3645            if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3646              let scale_factor = window.scale_factor();
3647              let size = bounds.size.to_logical::<f32>(scale_factor);
3648              let position = bounds.position.to_logical::<f32>(scale_factor);
3649              let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3650              b.width_rate = size.width / window_size.width;
3651              b.height_rate = size.height / window_size.height;
3652              b.x_rate = position.x / window_size.width;
3653              b.y_rate = position.y / window_size.height;
3654            }
3655
3656            if let Err(e) = webview.set_bounds(bounds) {
3657              log::error!("failed to set webview size: {e}");
3658            }
3659          }
3660          WebviewMessage::SetSize(size) => match webview.bounds() {
3661            Ok(mut bounds) => {
3662              bounds.size = size;
3663
3664              let scale_factor = window.scale_factor();
3665              let size = size.to_logical::<f32>(scale_factor);
3666
3667              if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3668                let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3669                b.width_rate = size.width / window_size.width;
3670                b.height_rate = size.height / window_size.height;
3671              }
3672
3673              if let Err(e) = webview.set_bounds(bounds) {
3674                log::error!("failed to set webview size: {e}");
3675              }
3676            }
3677            Err(e) => {
3678              log::error!("failed to get webview bounds: {e}");
3679            }
3680          },
3681          WebviewMessage::SetPosition(position) => match webview.bounds() {
3682            Ok(mut bounds) => {
3683              bounds.position = position;
3684
3685              let scale_factor = window.scale_factor();
3686              let position = position.to_logical::<f32>(scale_factor);
3687
3688              if let Some(b) = &mut *webview.bounds.lock().unwrap() {
3689                let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3690                b.x_rate = position.x / window_size.width;
3691                b.y_rate = position.y / window_size.height;
3692              }
3693
3694              if let Err(e) = webview.set_bounds(bounds) {
3695                log::error!("failed to set webview position: {e}");
3696              }
3697            }
3698            Err(e) => {
3699              log::error!("failed to get webview bounds: {e}");
3700            }
3701          },
3702          WebviewMessage::SetZoom(scale_factor) => {
3703            if let Err(e) = webview.zoom(scale_factor) {
3704              log::error!("failed to set webview zoom: {e}");
3705            }
3706          }
3707          WebviewMessage::SetBackgroundColor(color) => {
3708            if let Err(e) =
3709              webview.set_background_color(color.map(Into::into).unwrap_or((255, 255, 255, 255)))
3710            {
3711              log::error!("failed to set webview background color: {e}");
3712            }
3713          }
3714          WebviewMessage::ClearAllBrowsingData => {
3715            if let Err(e) = webview.clear_all_browsing_data() {
3716              log::error!("failed to clear webview browsing data: {e}");
3717            }
3718          }
3719          // Getters
3720          WebviewMessage::Url(tx) => {
3721            tx.send(
3722              webview
3723                .url()
3724                .map(|u| u.parse().expect("invalid webview URL"))
3725                .map_err(|_| Error::FailedToSendMessage),
3726            )
3727            .unwrap();
3728          }
3729
3730          WebviewMessage::Cookies(tx) => {
3731            tx.send(webview.cookies().map_err(|_| Error::FailedToSendMessage))
3732              .unwrap();
3733          }
3734
3735          WebviewMessage::SetCookie(cookie) => {
3736            if let Err(e) = webview.set_cookie(&cookie) {
3737              log::error!("failed to set webview cookie: {e}");
3738            }
3739          }
3740
3741          WebviewMessage::DeleteCookie(cookie) => {
3742            if let Err(e) = webview.delete_cookie(&cookie) {
3743              log::error!("failed to delete webview cookie: {e}");
3744            }
3745          }
3746
3747          WebviewMessage::CookiesForUrl(url, tx) => {
3748            let webview_cookies = webview
3749              .cookies_for_url(url.as_str())
3750              .map_err(|_| Error::FailedToSendMessage);
3751            tx.send(webview_cookies).unwrap();
3752          }
3753
3754          WebviewMessage::Bounds(tx) => {
3755            tx.send(
3756              webview
3757                .bounds()
3758                .map(|bounds| tauri_runtime::dpi::Rect {
3759                  size: bounds.size,
3760                  position: bounds.position,
3761                })
3762                .map_err(|_| Error::FailedToSendMessage),
3763            )
3764            .unwrap();
3765          }
3766          WebviewMessage::Position(tx) => {
3767            tx.send(
3768              webview
3769                .bounds()
3770                .map(|bounds| bounds.position.to_physical(window.scale_factor()))
3771                .map_err(|_| Error::FailedToSendMessage),
3772            )
3773            .unwrap();
3774          }
3775          WebviewMessage::Size(tx) => {
3776            tx.send(
3777              webview
3778                .bounds()
3779                .map(|bounds| bounds.size.to_physical(window.scale_factor()))
3780                .map_err(|_| Error::FailedToSendMessage),
3781            )
3782            .unwrap();
3783          }
3784          WebviewMessage::SetFocus => {
3785            if let Err(e) = webview.focus() {
3786              log::error!("failed to focus webview: {e}");
3787            }
3788          }
3789          WebviewMessage::SetAutoResize(auto_resize) => match webview.bounds() {
3790            Ok(bounds) => {
3791              let scale_factor = window.scale_factor();
3792              let window_size = window.inner_size().to_logical::<f32>(scale_factor);
3793              *webview.bounds.lock().unwrap() = if auto_resize {
3794                let size = bounds.size.to_logical::<f32>(scale_factor);
3795                let position = bounds.position.to_logical::<f32>(scale_factor);
3796                Some(WebviewBounds {
3797                  x_rate: position.x / window_size.width,
3798                  y_rate: position.y / window_size.height,
3799                  width_rate: size.width / window_size.width,
3800                  height_rate: size.height / window_size.height,
3801                })
3802              } else {
3803                None
3804              };
3805            }
3806            Err(e) => {
3807              log::error!("failed to get webview bounds: {e}");
3808            }
3809          },
3810          WebviewMessage::WithWebview(f) => {
3811            #[cfg(any(
3812              target_os = "linux",
3813              target_os = "dragonfly",
3814              target_os = "freebsd",
3815              target_os = "netbsd",
3816              target_os = "openbsd"
3817            ))]
3818            {
3819              f(webview.webview());
3820            }
3821            #[cfg(target_os = "macos")]
3822            {
3823              use wry::WebViewExtMacOS;
3824              f(Webview {
3825                webview: Retained::into_raw(webview.webview()) as *mut objc2::runtime::AnyObject
3826                  as *mut std::ffi::c_void,
3827                manager: Retained::into_raw(webview.manager()) as *mut objc2::runtime::AnyObject
3828                  as *mut std::ffi::c_void,
3829                ns_window: Retained::into_raw(webview.ns_window()) as *mut objc2::runtime::AnyObject
3830                  as *mut std::ffi::c_void,
3831              });
3832            }
3833            #[cfg(target_os = "ios")]
3834            {
3835              use wry::WebViewExtIOS;
3836
3837              f(Webview {
3838                webview: Retained::into_raw(webview.inner.webview())
3839                  as *mut objc2::runtime::AnyObject
3840                  as *mut std::ffi::c_void,
3841                manager: Retained::into_raw(webview.inner.manager())
3842                  as *mut objc2::runtime::AnyObject
3843                  as *mut std::ffi::c_void,
3844                view_controller: window.ui_view_controller(),
3845              });
3846            }
3847            #[cfg(windows)]
3848            {
3849              f(Webview {
3850                controller: webview.controller(),
3851                environment: webview.environment(),
3852              });
3853            }
3854            #[cfg(target_os = "android")]
3855            {
3856              f(webview.handle())
3857            }
3858          }
3859          #[cfg(any(debug_assertions, feature = "devtools"))]
3860          WebviewMessage::OpenDevTools => {
3861            webview.open_devtools();
3862          }
3863          #[cfg(any(debug_assertions, feature = "devtools"))]
3864          WebviewMessage::CloseDevTools => {
3865            webview.close_devtools();
3866          }
3867          #[cfg(any(debug_assertions, feature = "devtools"))]
3868          WebviewMessage::IsDevToolsOpen(tx) => {
3869            tx.send(webview.is_devtools_open()).unwrap();
3870          }
3871        }
3872      }
3873    }
3874    Message::CreateWebview(window_id, handler) => {
3875      let window = windows
3876        .0
3877        .borrow()
3878        .get(&window_id)
3879        .map(|w| (w.inner.clone(), w.focused_webview.clone()));
3880      if let Some((Some(window), focused_webview)) = window {
3881        match handler(&window, CreateWebviewOptions { focused_webview }) {
3882          Ok(webview) => {
3883            #[allow(unknown_lints, clippy::manual_inspect)]
3884            windows.0.borrow_mut().get_mut(&window_id).map(|w| {
3885              w.webviews.push(webview);
3886              w.has_children.store(true, Ordering::Relaxed);
3887              w
3888            });
3889          }
3890          Err(e) => {
3891            log::error!("{e}");
3892          }
3893        }
3894      }
3895    }
3896    Message::CreateWindow(window_id, handler) => match handler(event_loop) {
3897      // wait for borrow_mut to be available - on Windows we might poll for the window to be inserted
3898      Ok(webview) => loop {
3899        if let Ok(mut windows) = windows.0.try_borrow_mut() {
3900          windows.insert(window_id, webview);
3901          break;
3902        }
3903      },
3904      Err(e) => {
3905        log::error!("{e}");
3906      }
3907    },
3908    Message::CreateRawWindow(window_id, handler, sender) => {
3909      let (label, builder) = handler();
3910
3911      #[cfg(windows)]
3912      let background_color = builder.window.background_color;
3913      #[cfg(windows)]
3914      let is_window_transparent = builder.window.transparent;
3915
3916      if let Ok(window) = builder.build(event_loop) {
3917        window_id_map.insert(window.id(), window_id);
3918
3919        let window = Arc::new(window);
3920
3921        #[cfg(windows)]
3922        let surface = if is_window_transparent {
3923          if let Ok(context) = softbuffer::Context::new(window.clone()) {
3924            if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
3925              window.draw_surface(&mut surface, background_color);
3926              Some(surface)
3927            } else {
3928              None
3929            }
3930          } else {
3931            None
3932          }
3933        } else {
3934          None
3935        };
3936
3937        windows.0.borrow_mut().insert(
3938          window_id,
3939          WindowWrapper {
3940            label,
3941            has_children: AtomicBool::new(false),
3942            inner: Some(window.clone()),
3943            window_event_listeners: Default::default(),
3944            webviews: Vec::new(),
3945            #[cfg(windows)]
3946            background_color,
3947            #[cfg(windows)]
3948            is_window_transparent,
3949            #[cfg(windows)]
3950            surface,
3951            focused_webview: Default::default(),
3952          },
3953        );
3954        sender.send(Ok(Arc::downgrade(&window))).unwrap();
3955      } else {
3956        sender.send(Err(Error::CreateWindow)).unwrap();
3957      }
3958    }
3959
3960    Message::UserEvent(_) => (),
3961    Message::EventLoopWindowTarget(message) => match message {
3962      EventLoopWindowTargetMessage::CursorPosition(sender) => {
3963        let pos = event_loop
3964          .cursor_position()
3965          .map_err(|_| Error::FailedToSendMessage);
3966        sender.send(pos).unwrap();
3967      }
3968      EventLoopWindowTargetMessage::SetTheme(theme) => {
3969        event_loop.set_theme(to_tao_theme(theme));
3970      }
3971      EventLoopWindowTargetMessage::SetDeviceEventFilter(filter) => {
3972        event_loop.set_device_event_filter(DeviceEventFilterWrapper::from(filter).0);
3973      }
3974    },
3975  }
3976}
3977
3978fn handle_event_loop<T: UserEvent>(
3979  event: Event<'_, Message<T>>,
3980  event_loop: &EventLoopWindowTarget<Message<T>>,
3981  control_flow: &mut ControlFlow,
3982  context: EventLoopIterationContext<'_, T>,
3983) {
3984  let EventLoopIterationContext {
3985    callback,
3986    window_id_map,
3987    windows,
3988    #[cfg(feature = "tracing")]
3989    active_tracing_spans,
3990  } = context;
3991  if *control_flow != ControlFlow::Exit {
3992    *control_flow = ControlFlow::Wait;
3993  }
3994
3995  match event {
3996    Event::NewEvents(StartCause::Init) => {
3997      callback(RunEvent::Ready);
3998    }
3999
4000    Event::NewEvents(StartCause::Poll) => {
4001      callback(RunEvent::Resumed);
4002    }
4003
4004    Event::MainEventsCleared => {
4005      callback(RunEvent::MainEventsCleared);
4006    }
4007
4008    Event::LoopDestroyed => {
4009      callback(RunEvent::Exit);
4010    }
4011
4012    #[cfg(windows)]
4013    Event::RedrawRequested(id) => {
4014      if let Some(window_id) = window_id_map.get(&id) {
4015        let mut windows_ref = windows.0.borrow_mut();
4016        if let Some(window) = windows_ref.get_mut(&window_id) {
4017          if window.is_window_transparent {
4018            let background_color = window.background_color;
4019            if let Some(surface) = &mut window.surface {
4020              if let Some(window) = &window.inner {
4021                window.draw_surface(surface, background_color);
4022              }
4023            }
4024          }
4025        }
4026      }
4027    }
4028
4029    #[cfg(feature = "tracing")]
4030    Event::RedrawEventsCleared => {
4031      active_tracing_spans.remove_window_draw();
4032    }
4033
4034    Event::UserEvent(Message::Webview(
4035      window_id,
4036      webview_id,
4037      WebviewMessage::WebviewEvent(event),
4038    )) => {
4039      let windows_ref = windows.0.borrow();
4040      if let Some(window) = windows_ref.get(&window_id) {
4041        if let Some(webview) = window.webviews.iter().find(|w| w.id == webview_id) {
4042          let label = webview.label.clone();
4043          let webview_event_listeners = webview.webview_event_listeners.clone();
4044
4045          drop(windows_ref);
4046
4047          callback(RunEvent::WebviewEvent {
4048            label,
4049            event: event.clone(),
4050          });
4051          let listeners = webview_event_listeners.lock().unwrap();
4052          let handlers = listeners.values();
4053          for handler in handlers {
4054            handler(&event);
4055          }
4056        }
4057      }
4058    }
4059
4060    Event::UserEvent(Message::Webview(
4061      window_id,
4062      _webview_id,
4063      WebviewMessage::SynthesizedWindowEvent(event),
4064    )) => {
4065      if let Some(event) = WindowEventWrapper::from(event).0 {
4066        let windows_ref = windows.0.borrow();
4067        let window = windows_ref.get(&window_id);
4068        if let Some(window) = window {
4069          let label = window.label.clone();
4070          let window_event_listeners = window.window_event_listeners.clone();
4071
4072          drop(windows_ref);
4073
4074          callback(RunEvent::WindowEvent {
4075            label,
4076            event: event.clone(),
4077          });
4078
4079          let listeners = window_event_listeners.lock().unwrap();
4080          let handlers = listeners.values();
4081          for handler in handlers {
4082            handler(&event);
4083          }
4084        }
4085      }
4086    }
4087
4088    Event::WindowEvent {
4089      event, window_id, ..
4090    } => {
4091      if let Some(window_id) = window_id_map.get(&window_id) {
4092        {
4093          let windows_ref = windows.0.borrow();
4094          if let Some(window) = windows_ref.get(&window_id) {
4095            if let Some(event) = WindowEventWrapper::parse(window, &event).0 {
4096              let label = window.label.clone();
4097              let window_event_listeners = window.window_event_listeners.clone();
4098
4099              drop(windows_ref);
4100
4101              callback(RunEvent::WindowEvent {
4102                label,
4103                event: event.clone(),
4104              });
4105              let listeners = window_event_listeners.lock().unwrap();
4106              let handlers = listeners.values();
4107              for handler in handlers {
4108                handler(&event);
4109              }
4110            }
4111          }
4112        }
4113
4114        match event {
4115          #[cfg(windows)]
4116          TaoWindowEvent::ThemeChanged(theme) => {
4117            if let Some(window) = windows.0.borrow().get(&window_id) {
4118              for webview in &window.webviews {
4119                let theme = match theme {
4120                  TaoTheme::Dark => wry::Theme::Dark,
4121                  TaoTheme::Light => wry::Theme::Light,
4122                  _ => wry::Theme::Light,
4123                };
4124                if let Err(e) = webview.set_theme(theme) {
4125                  log::error!("failed to set theme: {e}");
4126                }
4127              }
4128            }
4129          }
4130          TaoWindowEvent::CloseRequested => {
4131            on_close_requested(callback, window_id, windows);
4132          }
4133          TaoWindowEvent::Destroyed => {
4134            let removed = windows.0.borrow_mut().remove(&window_id).is_some();
4135            if removed {
4136              let is_empty = windows.0.borrow().is_empty();
4137              if is_empty {
4138                let (tx, rx) = channel();
4139                callback(RunEvent::ExitRequested { code: None, tx });
4140
4141                let recv = rx.try_recv();
4142                let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
4143
4144                if !should_prevent {
4145                  *control_flow = ControlFlow::Exit;
4146                }
4147              }
4148            }
4149          }
4150          TaoWindowEvent::Resized(size) => {
4151            if let Some((Some(window), webviews)) = windows
4152              .0
4153              .borrow()
4154              .get(&window_id)
4155              .map(|w| (w.inner.clone(), w.webviews.clone()))
4156            {
4157              let size = size.to_logical::<f32>(window.scale_factor());
4158              for webview in webviews {
4159                if let Some(b) = &*webview.bounds.lock().unwrap() {
4160                  if let Err(e) = webview.set_bounds(wry::Rect {
4161                    position: LogicalPosition::new(size.width * b.x_rate, size.height * b.y_rate)
4162                      .into(),
4163                    size: LogicalSize::new(size.width * b.width_rate, size.height * b.height_rate)
4164                      .into(),
4165                  }) {
4166                    log::error!("failed to autoresize webview: {e}");
4167                  }
4168                }
4169              }
4170            }
4171          }
4172          _ => {}
4173        }
4174      }
4175    }
4176    Event::UserEvent(message) => match message {
4177      Message::RequestExit(code) => {
4178        let (tx, rx) = channel();
4179        callback(RunEvent::ExitRequested {
4180          code: Some(code),
4181          tx,
4182        });
4183
4184        let recv = rx.try_recv();
4185        let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent));
4186
4187        if !should_prevent {
4188          *control_flow = ControlFlow::Exit;
4189        }
4190      }
4191      Message::Window(id, WindowMessage::Close) => {
4192        on_close_requested(callback, id, windows);
4193      }
4194      Message::Window(id, WindowMessage::Destroy) => {
4195        on_window_close(id, windows);
4196      }
4197      Message::UserEvent(t) => callback(RunEvent::UserEvent(t)),
4198      message => {
4199        handle_user_message(
4200          event_loop,
4201          message,
4202          UserMessageContext {
4203            window_id_map,
4204            windows,
4205          },
4206        );
4207      }
4208    },
4209    #[cfg(any(target_os = "macos", target_os = "ios"))]
4210    Event::Opened { urls } => {
4211      callback(RunEvent::Opened { urls });
4212    }
4213    #[cfg(target_os = "macos")]
4214    Event::Reopen {
4215      has_visible_windows,
4216      ..
4217    } => callback(RunEvent::Reopen {
4218      has_visible_windows,
4219    }),
4220    _ => (),
4221  }
4222}
4223
4224fn on_close_requested<'a, T: UserEvent>(
4225  callback: &'a mut (dyn FnMut(RunEvent<T>) + 'static),
4226  window_id: WindowId,
4227  windows: Arc<WindowsStore>,
4228) {
4229  let (tx, rx) = channel();
4230  let windows_ref = windows.0.borrow();
4231  if let Some(w) = windows_ref.get(&window_id) {
4232    let label = w.label.clone();
4233    let window_event_listeners = w.window_event_listeners.clone();
4234
4235    drop(windows_ref);
4236
4237    let listeners = window_event_listeners.lock().unwrap();
4238    let handlers = listeners.values();
4239    for handler in handlers {
4240      handler(&WindowEvent::CloseRequested {
4241        signal_tx: tx.clone(),
4242      });
4243    }
4244    callback(RunEvent::WindowEvent {
4245      label,
4246      event: WindowEvent::CloseRequested { signal_tx: tx },
4247    });
4248    if let Ok(true) = rx.try_recv() {
4249    } else {
4250      on_window_close(window_id, windows);
4251    }
4252  }
4253}
4254
4255fn on_window_close(window_id: WindowId, windows: Arc<WindowsStore>) {
4256  if let Some(window_wrapper) = windows.0.borrow_mut().get_mut(&window_id) {
4257    window_wrapper.inner = None;
4258    #[cfg(windows)]
4259    window_wrapper.surface.take();
4260  }
4261}
4262
4263fn parse_proxy_url(url: &Url) -> Result<ProxyConfig> {
4264  let host = url.host().map(|h| h.to_string()).unwrap_or_default();
4265  let port = url.port().map(|p| p.to_string()).unwrap_or_default();
4266
4267  if url.scheme() == "http" {
4268    let config = ProxyConfig::Http(ProxyEndpoint { host, port });
4269
4270    Ok(config)
4271  } else if url.scheme() == "socks5" {
4272    let config = ProxyConfig::Socks5(ProxyEndpoint { host, port });
4273
4274    Ok(config)
4275  } else {
4276    Err(Error::InvalidProxyUrl)
4277  }
4278}
4279
4280fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
4281  window_id: WindowId,
4282  webview_id: u32,
4283  event_loop: &EventLoopWindowTarget<Message<T>>,
4284  context: &Context<T>,
4285  pending: PendingWindow<T, Wry<T>>,
4286  after_window_creation: Option<F>,
4287) -> Result<WindowWrapper> {
4288  #[allow(unused_mut)]
4289  let PendingWindow {
4290    mut window_builder,
4291    label,
4292    webview,
4293  } = pending;
4294
4295  #[cfg(feature = "tracing")]
4296  let _webview_create_span = tracing::debug_span!("wry::webview::create").entered();
4297  #[cfg(feature = "tracing")]
4298  let window_draw_span = tracing::debug_span!("wry::window::draw").entered();
4299  #[cfg(feature = "tracing")]
4300  let window_create_span =
4301    tracing::debug_span!(parent: &window_draw_span, "wry::window::create").entered();
4302
4303  let window_event_listeners = WindowEventListeners::default();
4304
4305  #[cfg(windows)]
4306  let background_color = window_builder.inner.window.background_color;
4307  #[cfg(windows)]
4308  let is_window_transparent = window_builder.inner.window.transparent;
4309
4310  #[cfg(target_os = "macos")]
4311  {
4312    if window_builder.tabbing_identifier.is_none()
4313      || window_builder.inner.window.transparent
4314      || !window_builder.inner.window.decorations
4315    {
4316      window_builder.inner = window_builder.inner.with_automatic_window_tabbing(false);
4317    }
4318  }
4319
4320  #[cfg(desktop)]
4321  if window_builder.prevent_overflow.is_some() || window_builder.center {
4322    let monitor = if let Some(window_position) = &window_builder.inner.window.position {
4323      event_loop.available_monitors().find(|m| {
4324        let monitor_pos = m.position();
4325        let monitor_size = m.size();
4326
4327        // type annotations required for 32bit targets.
4328        let window_position = window_position.to_physical::<i32>(m.scale_factor());
4329
4330        monitor_pos.x <= window_position.x
4331          && window_position.x < monitor_pos.x + monitor_size.width as i32
4332          && monitor_pos.y <= window_position.y
4333          && window_position.y < monitor_pos.y + monitor_size.height as i32
4334      })
4335    } else {
4336      event_loop.primary_monitor()
4337    };
4338    if let Some(monitor) = monitor {
4339      let scale_factor = monitor.scale_factor();
4340      let desired_size = window_builder
4341        .inner
4342        .window
4343        .inner_size
4344        .unwrap_or_else(|| TaoPhysicalSize::new(800, 600).into());
4345      let mut inner_size = window_builder
4346        .inner
4347        .window
4348        .inner_size_constraints
4349        .clamp(desired_size, scale_factor)
4350        .to_physical::<u32>(scale_factor);
4351      let mut window_size = inner_size;
4352      #[allow(unused_mut)]
4353      // Left and right window shadow counts as part of the window on Windows
4354      // We need to include it when calculating positions, but not size
4355      let mut shadow_width = 0;
4356      #[cfg(windows)]
4357      if window_builder.inner.window.decorations {
4358        use windows::Win32::UI::WindowsAndMessaging::{AdjustWindowRect, WS_OVERLAPPEDWINDOW};
4359        let mut rect = windows::Win32::Foundation::RECT::default();
4360        let result = unsafe { AdjustWindowRect(&mut rect, WS_OVERLAPPEDWINDOW, false) };
4361        if result.is_ok() {
4362          shadow_width = (rect.right - rect.left) as u32;
4363          // rect.bottom is made out of shadow, and we don't care about it
4364          window_size.height += -rect.top as u32;
4365        }
4366      }
4367
4368      if let Some(margin) = window_builder.prevent_overflow {
4369        let work_area = monitor.work_area();
4370        let margin = margin.to_physical::<u32>(scale_factor);
4371        let constraint = PhysicalSize::new(
4372          work_area.size.width - margin.width,
4373          work_area.size.height - margin.height,
4374        );
4375        if window_size.width > constraint.width || window_size.height > constraint.height {
4376          if window_size.width > constraint.width {
4377            inner_size.width = inner_size
4378              .width
4379              .saturating_sub(window_size.width - constraint.width);
4380            window_size.width = constraint.width;
4381          }
4382          if window_size.height > constraint.height {
4383            inner_size.height = inner_size
4384              .height
4385              .saturating_sub(window_size.height - constraint.height);
4386            window_size.height = constraint.height;
4387          }
4388          window_builder.inner.window.inner_size = Some(inner_size.into());
4389        }
4390      }
4391
4392      if window_builder.center {
4393        window_size.width += shadow_width;
4394        let position = window::calculate_window_center_position(window_size, monitor);
4395        let logical_position = position.to_logical::<f64>(scale_factor);
4396        window_builder = window_builder.position(logical_position.x, logical_position.y);
4397      }
4398    }
4399  };
4400
4401  let window = window_builder
4402    .inner
4403    .build(event_loop)
4404    .map_err(|_| Error::CreateWindow)?;
4405
4406  #[cfg(feature = "tracing")]
4407  {
4408    drop(window_create_span);
4409
4410    context
4411      .main_thread
4412      .active_tracing_spans
4413      .0
4414      .borrow_mut()
4415      .push(ActiveTracingSpan::WindowDraw {
4416        id: window.id(),
4417        span: window_draw_span,
4418      });
4419  }
4420
4421  context.window_id_map.insert(window.id(), window_id);
4422
4423  if let Some(handler) = after_window_creation {
4424    let raw = RawWindow {
4425      #[cfg(windows)]
4426      hwnd: window.hwnd(),
4427      #[cfg(any(
4428        target_os = "linux",
4429        target_os = "dragonfly",
4430        target_os = "freebsd",
4431        target_os = "netbsd",
4432        target_os = "openbsd"
4433      ))]
4434      gtk_window: window.gtk_window(),
4435      #[cfg(any(
4436        target_os = "linux",
4437        target_os = "dragonfly",
4438        target_os = "freebsd",
4439        target_os = "netbsd",
4440        target_os = "openbsd"
4441      ))]
4442      default_vbox: window.default_vbox(),
4443      _marker: &std::marker::PhantomData,
4444    };
4445    handler(raw);
4446  }
4447
4448  let mut webviews = Vec::new();
4449
4450  let focused_webview = Arc::new(Mutex::new(None));
4451
4452  if let Some(webview) = webview {
4453    webviews.push(create_webview(
4454      #[cfg(feature = "unstable")]
4455      WebviewKind::WindowChild,
4456      #[cfg(not(feature = "unstable"))]
4457      WebviewKind::WindowContent,
4458      &window,
4459      Arc::new(Mutex::new(window_id)),
4460      webview_id,
4461      context,
4462      webview,
4463      focused_webview.clone(),
4464    )?);
4465  }
4466
4467  let window = Arc::new(window);
4468
4469  #[cfg(windows)]
4470  let surface = if is_window_transparent {
4471    if let Ok(context) = softbuffer::Context::new(window.clone()) {
4472      if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
4473        window.draw_surface(&mut surface, background_color);
4474        Some(surface)
4475      } else {
4476        None
4477      }
4478    } else {
4479      None
4480    }
4481  } else {
4482    None
4483  };
4484
4485  Ok(WindowWrapper {
4486    label,
4487    has_children: AtomicBool::new(false),
4488    inner: Some(window),
4489    webviews,
4490    window_event_listeners,
4491    #[cfg(windows)]
4492    background_color,
4493    #[cfg(windows)]
4494    is_window_transparent,
4495    #[cfg(windows)]
4496    surface,
4497    focused_webview,
4498  })
4499}
4500
4501/// the kind of the webview
4502#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
4503enum WebviewKind {
4504  // webview is the entire window content
4505  WindowContent,
4506  // webview is a child of the window, which can contain other webviews too
4507  WindowChild,
4508}
4509
4510#[derive(Debug, Clone)]
4511struct WebviewBounds {
4512  x_rate: f32,
4513  y_rate: f32,
4514  width_rate: f32,
4515  height_rate: f32,
4516}
4517
4518fn create_webview<T: UserEvent>(
4519  kind: WebviewKind,
4520  window: &Window,
4521  window_id: Arc<Mutex<WindowId>>,
4522  id: WebviewId,
4523  context: &Context<T>,
4524  pending: PendingWebview<T, Wry<T>>,
4525  #[allow(unused_variables)] focused_webview: Arc<Mutex<Option<String>>>,
4526) -> Result<WebviewWrapper> {
4527  if !context.webview_runtime_installed {
4528    #[cfg(all(not(debug_assertions), windows))]
4529    dialog::error(
4530      r#"Could not find the WebView2 Runtime.
4531
4532Make sure it is installed or download it from <A href="https://developer.microsoft.com/en-us/microsoft-edge/webview2">https://developer.microsoft.com/en-us/microsoft-edge/webview2</A>
4533
4534You may have it installed on another user account, but it is not available for this one.
4535"#,
4536    );
4537
4538    if cfg!(target_os = "macos") {
4539      log::warn!("WebKit webview runtime not found, attempting to create webview anyway.");
4540    } else {
4541      return Err(Error::WebviewRuntimeNotInstalled);
4542    }
4543  }
4544
4545  #[allow(unused_mut)]
4546  let PendingWebview {
4547    webview_attributes,
4548    uri_scheme_protocols,
4549    label,
4550    ipc_handler,
4551    url,
4552    ..
4553  } = pending;
4554
4555  let mut web_context = context
4556    .main_thread
4557    .web_context
4558    .lock()
4559    .expect("poisoned WebContext store");
4560  let is_first_context = web_context.is_empty();
4561  // the context must be stored on the HashMap because it must outlive the WebView on macOS
4562  let automation_enabled = std::env::var("TAURI_WEBVIEW_AUTOMATION").as_deref() == Ok("true");
4563  let web_context_key = webview_attributes.data_directory;
4564  let entry = web_context.entry(web_context_key.clone());
4565  let web_context = match entry {
4566    Occupied(occupied) => {
4567      let occupied = occupied.into_mut();
4568      occupied.referenced_by_webviews.insert(label.clone());
4569      occupied
4570    }
4571    Vacant(vacant) => {
4572      let mut web_context = WryWebContext::new(web_context_key.clone());
4573      web_context.set_allows_automation(if automation_enabled {
4574        is_first_context
4575      } else {
4576        false
4577      });
4578      vacant.insert(WebContext {
4579        inner: web_context,
4580        referenced_by_webviews: [label.clone()].into(),
4581        registered_custom_protocols: HashSet::new(),
4582      })
4583    }
4584  };
4585
4586  let mut webview_builder = WebViewBuilder::new_with_web_context(&mut web_context.inner)
4587    .with_id(&label)
4588    .with_focused(webview_attributes.focus)
4589    .with_transparent(webview_attributes.transparent)
4590    .with_accept_first_mouse(webview_attributes.accept_first_mouse)
4591    .with_incognito(webview_attributes.incognito)
4592    .with_clipboard(webview_attributes.clipboard)
4593    .with_hotkeys_zoom(webview_attributes.zoom_hotkeys_enabled);
4594
4595  if url != "about:blank" {
4596    webview_builder = webview_builder.with_url(&url);
4597  }
4598
4599  #[cfg(target_os = "macos")]
4600  if let Some(webview_configuration) = webview_attributes.webview_configuration {
4601    webview_builder = webview_builder.with_webview_configuration(webview_configuration);
4602  }
4603
4604  #[cfg(any(target_os = "windows", target_os = "android"))]
4605  {
4606    webview_builder = webview_builder.with_https_scheme(webview_attributes.use_https_scheme);
4607  }
4608
4609  if let Some(background_throttling) = webview_attributes.background_throttling {
4610    webview_builder = webview_builder.with_background_throttling(match background_throttling {
4611      tauri_utils::config::BackgroundThrottlingPolicy::Disabled => {
4612        wry::BackgroundThrottlingPolicy::Disabled
4613      }
4614      tauri_utils::config::BackgroundThrottlingPolicy::Suspend => {
4615        wry::BackgroundThrottlingPolicy::Suspend
4616      }
4617      tauri_utils::config::BackgroundThrottlingPolicy::Throttle => {
4618        wry::BackgroundThrottlingPolicy::Throttle
4619      }
4620    });
4621  }
4622
4623  if webview_attributes.javascript_disabled {
4624    webview_builder = webview_builder.with_javascript_disabled();
4625  }
4626
4627  if let Some(color) = webview_attributes.background_color {
4628    webview_builder = webview_builder.with_background_color(color.into());
4629  }
4630
4631  if webview_attributes.drag_drop_handler_enabled {
4632    let proxy = context.proxy.clone();
4633    let window_id_ = window_id.clone();
4634    webview_builder = webview_builder.with_drag_drop_handler(move |event| {
4635      let event = match event {
4636        WryDragDropEvent::Enter {
4637          paths,
4638          position: (x, y),
4639        } => DragDropEvent::Enter {
4640          paths,
4641          position: PhysicalPosition::new(x as _, y as _),
4642        },
4643        WryDragDropEvent::Over { position: (x, y) } => DragDropEvent::Over {
4644          position: PhysicalPosition::new(x as _, y as _),
4645        },
4646        WryDragDropEvent::Drop {
4647          paths,
4648          position: (x, y),
4649        } => DragDropEvent::Drop {
4650          paths,
4651          position: PhysicalPosition::new(x as _, y as _),
4652        },
4653        WryDragDropEvent::Leave => DragDropEvent::Leave,
4654        _ => unimplemented!(),
4655      };
4656
4657      let message = if kind == WebviewKind::WindowContent {
4658        WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::DragDrop(event))
4659      } else {
4660        WebviewMessage::WebviewEvent(WebviewEvent::DragDrop(event))
4661      };
4662
4663      let _ = proxy.send_event(Message::Webview(*window_id_.lock().unwrap(), id, message));
4664      true
4665    });
4666  }
4667
4668  if let Some(navigation_handler) = pending.navigation_handler {
4669    webview_builder = webview_builder.with_navigation_handler(move |url| {
4670      url
4671        .parse()
4672        .map(|url| navigation_handler(&url))
4673        .unwrap_or(true)
4674    });
4675  }
4676
4677  if let Some(new_window_handler) = pending.new_window_handler {
4678    #[cfg(desktop)]
4679    let context = context.clone();
4680    webview_builder = webview_builder.with_new_window_req_handler(move |url, features| {
4681      url
4682        .parse()
4683        .map(|url| {
4684          let response = new_window_handler(
4685            url,
4686            tauri_runtime::webview::NewWindowFeatures::new(
4687              features.size,
4688              features.position,
4689              tauri_runtime::webview::NewWindowOpener {
4690                #[cfg(desktop)]
4691                webview: features.opener.webview,
4692                #[cfg(windows)]
4693                environment: features.opener.environment,
4694                #[cfg(target_os = "macos")]
4695                target_configuration: features.opener.target_configuration,
4696              },
4697            ),
4698          );
4699          match response {
4700            tauri_runtime::webview::NewWindowResponse::Allow => wry::NewWindowResponse::Allow,
4701            #[cfg(desktop)]
4702            tauri_runtime::webview::NewWindowResponse::Create { window_id } => {
4703              let windows = &context.main_thread.windows.0;
4704              let webview = loop {
4705                if let Some(webview) = windows.try_borrow().ok().and_then(|windows| {
4706                  windows
4707                    .get(&window_id)
4708                    .map(|window| window.webviews.first().unwrap().clone())
4709                }) {
4710                  break webview;
4711                } else {
4712                  // on Windows the window is created async so we should wait for it to be available
4713                  std::thread::sleep(std::time::Duration::from_millis(50));
4714                  continue;
4715                };
4716              };
4717
4718              #[cfg(desktop)]
4719              wry::NewWindowResponse::Create {
4720                #[cfg(target_os = "macos")]
4721                webview: wry::WebViewExtMacOS::webview(&*webview).as_super().into(),
4722                #[cfg(any(
4723                  target_os = "linux",
4724                  target_os = "dragonfly",
4725                  target_os = "freebsd",
4726                  target_os = "netbsd",
4727                  target_os = "openbsd",
4728                ))]
4729                webview: webview.webview(),
4730                #[cfg(windows)]
4731                webview: webview.webview(),
4732              }
4733            }
4734            tauri_runtime::webview::NewWindowResponse::Deny => wry::NewWindowResponse::Deny,
4735          }
4736        })
4737        .unwrap_or(wry::NewWindowResponse::Deny)
4738    });
4739  }
4740
4741  if let Some(document_title_changed_handler) = pending.document_title_changed_handler {
4742    webview_builder =
4743      webview_builder.with_document_title_changed_handler(document_title_changed_handler)
4744  }
4745
4746  let webview_bounds = if let Some(bounds) = webview_attributes.bounds {
4747    let bounds: RectWrapper = bounds.into();
4748    let bounds = bounds.0;
4749
4750    let scale_factor = window.scale_factor();
4751    let position = bounds.position.to_logical::<f32>(scale_factor);
4752    let size = bounds.size.to_logical::<f32>(scale_factor);
4753
4754    webview_builder = webview_builder.with_bounds(bounds);
4755
4756    let window_size = window.inner_size().to_logical::<f32>(scale_factor);
4757
4758    if webview_attributes.auto_resize {
4759      Some(WebviewBounds {
4760        x_rate: position.x / window_size.width,
4761        y_rate: position.y / window_size.height,
4762        width_rate: size.width / window_size.width,
4763        height_rate: size.height / window_size.height,
4764      })
4765    } else {
4766      None
4767    }
4768  } else {
4769    #[cfg(feature = "unstable")]
4770    {
4771      webview_builder = webview_builder.with_bounds(wry::Rect {
4772        position: LogicalPosition::new(0, 0).into(),
4773        size: window.inner_size().into(),
4774      });
4775      Some(WebviewBounds {
4776        x_rate: 0.,
4777        y_rate: 0.,
4778        width_rate: 1.,
4779        height_rate: 1.,
4780      })
4781    }
4782    #[cfg(not(feature = "unstable"))]
4783    None
4784  };
4785
4786  if let Some(download_handler) = pending.download_handler {
4787    let download_handler_ = download_handler.clone();
4788    webview_builder = webview_builder.with_download_started_handler(move |url, path| {
4789      if let Ok(url) = url.parse() {
4790        download_handler_(DownloadEvent::Requested {
4791          url,
4792          destination: path,
4793        })
4794      } else {
4795        false
4796      }
4797    });
4798    webview_builder = webview_builder.with_download_completed_handler(move |url, path, success| {
4799      if let Ok(url) = url.parse() {
4800        download_handler(DownloadEvent::Finished { url, path, success });
4801      }
4802    });
4803  }
4804
4805  if let Some(page_load_handler) = pending.on_page_load_handler {
4806    webview_builder = webview_builder.with_on_page_load_handler(move |event, url| {
4807      let _ = url.parse().map(|url| {
4808        page_load_handler(
4809          url,
4810          match event {
4811            wry::PageLoadEvent::Started => tauri_runtime::webview::PageLoadEvent::Started,
4812            wry::PageLoadEvent::Finished => tauri_runtime::webview::PageLoadEvent::Finished,
4813          },
4814        )
4815      });
4816    });
4817  }
4818
4819  if let Some(user_agent) = webview_attributes.user_agent {
4820    webview_builder = webview_builder.with_user_agent(&user_agent);
4821  }
4822
4823  if let Some(proxy_url) = webview_attributes.proxy_url {
4824    let config = parse_proxy_url(&proxy_url)?;
4825
4826    webview_builder = webview_builder.with_proxy_config(config);
4827  }
4828
4829  #[cfg(windows)]
4830  {
4831    if let Some(additional_browser_args) = webview_attributes.additional_browser_args {
4832      webview_builder = webview_builder.with_additional_browser_args(&additional_browser_args);
4833    }
4834
4835    if let Some(environment) = webview_attributes.environment {
4836      webview_builder = webview_builder.with_environment(environment);
4837    }
4838
4839    webview_builder = webview_builder.with_theme(match window.theme() {
4840      TaoTheme::Dark => wry::Theme::Dark,
4841      TaoTheme::Light => wry::Theme::Light,
4842      _ => wry::Theme::Light,
4843    });
4844
4845    webview_builder =
4846      webview_builder.with_scroll_bar_style(match webview_attributes.scroll_bar_style {
4847        ScrollBarStyle::Default => WryScrollBarStyle::Default,
4848        ScrollBarStyle::FluentOverlay => WryScrollBarStyle::FluentOverlay,
4849        _ => unreachable!(),
4850      });
4851  }
4852
4853  #[cfg(windows)]
4854  {
4855    webview_builder = webview_builder
4856      .with_browser_extensions_enabled(webview_attributes.browser_extensions_enabled);
4857  }
4858
4859  #[cfg(any(
4860    windows,
4861    target_os = "linux",
4862    target_os = "dragonfly",
4863    target_os = "freebsd",
4864    target_os = "netbsd",
4865    target_os = "openbsd"
4866  ))]
4867  {
4868    if let Some(path) = &webview_attributes.extensions_path {
4869      webview_builder = webview_builder.with_extensions_path(path);
4870    }
4871  }
4872
4873  #[cfg(any(
4874    target_os = "linux",
4875    target_os = "dragonfly",
4876    target_os = "freebsd",
4877    target_os = "netbsd",
4878    target_os = "openbsd"
4879  ))]
4880  {
4881    if let Some(related_view) = webview_attributes.related_view {
4882      webview_builder = webview_builder.with_related_view(related_view);
4883    }
4884  }
4885
4886  #[cfg(any(target_os = "macos", target_os = "ios"))]
4887  {
4888    if let Some(data_store_identifier) = &webview_attributes.data_store_identifier {
4889      webview_builder = webview_builder.with_data_store_identifier(*data_store_identifier);
4890    }
4891
4892    webview_builder =
4893      webview_builder.with_allow_link_preview(webview_attributes.allow_link_preview);
4894  }
4895
4896  #[cfg(target_os = "ios")]
4897  {
4898    if let Some(input_accessory_view_builder) = webview_attributes.input_accessory_view_builder {
4899      webview_builder = webview_builder
4900        .with_input_accessory_view_builder(move |webview| input_accessory_view_builder.0(webview));
4901    }
4902  }
4903
4904  #[cfg(target_os = "macos")]
4905  {
4906    if let Some(position) = &webview_attributes.traffic_light_position {
4907      webview_builder = webview_builder.with_traffic_light_inset(*position);
4908    }
4909  }
4910
4911  webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
4912    kind,
4913    window_id.clone(),
4914    id,
4915    context.clone(),
4916    label.clone(),
4917    ipc_handler,
4918  ));
4919
4920  for script in webview_attributes.initialization_scripts {
4921    webview_builder = webview_builder
4922      .with_initialization_script_for_main_only(script.script, script.for_main_frame_only);
4923  }
4924
4925  for (scheme, protocol) in uri_scheme_protocols {
4926    // on Linux the custom protocols are associated with the web context
4927    // and you cannot register a scheme more than once
4928    #[cfg(any(
4929      target_os = "linux",
4930      target_os = "dragonfly",
4931      target_os = "freebsd",
4932      target_os = "netbsd",
4933      target_os = "openbsd"
4934    ))]
4935    {
4936      if web_context.registered_custom_protocols.contains(&scheme) {
4937        continue;
4938      }
4939
4940      web_context
4941        .registered_custom_protocols
4942        .insert(scheme.clone());
4943    }
4944
4945    webview_builder = webview_builder.with_asynchronous_custom_protocol(
4946      scheme,
4947      move |webview_id, request, responder| {
4948        protocol(
4949          webview_id,
4950          request,
4951          Box::new(move |response| responder.respond(response)),
4952        )
4953      },
4954    );
4955  }
4956
4957  #[cfg(any(debug_assertions, feature = "devtools"))]
4958  {
4959    webview_builder = webview_builder.with_devtools(webview_attributes.devtools.unwrap_or(true));
4960  }
4961
4962  #[cfg(target_os = "android")]
4963  {
4964    if let Some(on_webview_created) = pending.on_webview_created {
4965      webview_builder = webview_builder.on_webview_created(move |ctx| {
4966        on_webview_created(tauri_runtime::webview::CreationContext {
4967          env: ctx.env,
4968          activity: ctx.activity,
4969          webview: ctx.webview,
4970        })
4971      });
4972    }
4973  }
4974
4975  let webview = match kind {
4976    #[cfg(not(any(
4977      target_os = "windows",
4978      target_os = "macos",
4979      target_os = "ios",
4980      target_os = "android"
4981    )))]
4982    WebviewKind::WindowChild => {
4983      // only way to account for menu bar height, and also works for multiwebviews :)
4984      let vbox = window.default_vbox().unwrap();
4985      webview_builder.build_gtk(vbox)
4986    }
4987    #[cfg(any(
4988      target_os = "windows",
4989      target_os = "macos",
4990      target_os = "ios",
4991      target_os = "android"
4992    ))]
4993    WebviewKind::WindowChild => webview_builder.build_as_child(&window),
4994    WebviewKind::WindowContent => {
4995      #[cfg(any(
4996        target_os = "windows",
4997        target_os = "macos",
4998        target_os = "ios",
4999        target_os = "android"
5000      ))]
5001      let builder = webview_builder.build(&window);
5002      #[cfg(not(any(
5003        target_os = "windows",
5004        target_os = "macos",
5005        target_os = "ios",
5006        target_os = "android"
5007      )))]
5008      let builder = {
5009        let vbox = window.default_vbox().unwrap();
5010        webview_builder.build_gtk(vbox)
5011      };
5012      builder
5013    }
5014  }
5015  .map_err(|e| Error::CreateWebview(Box::new(e)))?;
5016
5017  if kind == WebviewKind::WindowContent {
5018    #[cfg(any(
5019      target_os = "linux",
5020      target_os = "dragonfly",
5021      target_os = "freebsd",
5022      target_os = "netbsd",
5023      target_os = "openbsd"
5024    ))]
5025    undecorated_resizing::attach_resize_handler(&webview);
5026    #[cfg(windows)]
5027    if window.is_resizable() && !window.is_decorated() {
5028      undecorated_resizing::attach_resize_handler(window.hwnd(), window.has_undecorated_shadow());
5029    }
5030  }
5031
5032  #[cfg(windows)]
5033  {
5034    let controller = webview.controller();
5035    let proxy_clone = context.proxy.clone();
5036    let window_id_ = window_id.clone();
5037    let mut token = 0;
5038    unsafe {
5039      let label_ = label.clone();
5040      let focused_webview_ = focused_webview.clone();
5041      controller.add_GotFocus(
5042        &FocusChangedEventHandler::create(Box::new(move |_, _| {
5043          let mut focused_webview = focused_webview_.lock().unwrap();
5044          // when using multiwebview mode, we should check if the focus change is actually a "webview focus change"
5045          // instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus)
5046          let already_focused = focused_webview.is_some();
5047          focused_webview.replace(label_.clone());
5048
5049          if !already_focused {
5050            let _ = proxy_clone.send_event(Message::Webview(
5051              *window_id_.lock().unwrap(),
5052              id,
5053              WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)),
5054            ));
5055          }
5056          Ok(())
5057        })),
5058        &mut token,
5059      )
5060    }
5061    .unwrap();
5062    unsafe {
5063      let label_ = label.clone();
5064      let window_id_ = window_id.clone();
5065      let proxy_clone = context.proxy.clone();
5066      controller.add_LostFocus(
5067        &FocusChangedEventHandler::create(Box::new(move |_, _| {
5068          let mut focused_webview = focused_webview.lock().unwrap();
5069          // when using multiwebview mode, we should handle webview focus changes
5070          // so we check is the currently focused webview matches this webview's
5071          // (in this case, it means we lost the window focus)
5072          //
5073          // on multiwebview mode if we change focus to a different webview
5074          // we get the gotFocus event of the other webview before the lostFocus
5075          // so this check makes sense
5076          let lost_window_focus = focused_webview.as_ref().map_or(true, |w| w == &label_);
5077
5078          if lost_window_focus {
5079            // only reset when we lost window focus - otherwise some other webview is focused
5080            *focused_webview = None;
5081            let _ = proxy_clone.send_event(Message::Webview(
5082              *window_id_.lock().unwrap(),
5083              id,
5084              WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)),
5085            ));
5086          }
5087          Ok(())
5088        })),
5089        &mut token,
5090      )
5091    }
5092    .unwrap();
5093
5094    if let Ok(webview) = unsafe { controller.CoreWebView2() } {
5095      let proxy_clone = context.proxy.clone();
5096      unsafe {
5097        let _ = webview.add_ContainsFullScreenElementChanged(
5098          &ContainsFullScreenElementChangedEventHandler::create(Box::new(move |sender, _| {
5099            let mut contains_fullscreen_element = windows::core::BOOL::default();
5100            sender
5101              .ok_or_else(windows::core::Error::empty)?
5102              .ContainsFullScreenElement(&mut contains_fullscreen_element)?;
5103            let _ = proxy_clone.send_event(Message::Window(
5104              *window_id.lock().unwrap(),
5105              WindowMessage::SetFullscreen(contains_fullscreen_element.as_bool()),
5106            ));
5107            Ok(())
5108          })),
5109          &mut token,
5110        );
5111      }
5112    }
5113  }
5114
5115  Ok(WebviewWrapper {
5116    label,
5117    id,
5118    inner: Rc::new(webview),
5119    context_store: context.main_thread.web_context.clone(),
5120    webview_event_listeners: Default::default(),
5121    context_key: if automation_enabled {
5122      None
5123    } else {
5124      web_context_key
5125    },
5126    bounds: Arc::new(Mutex::new(webview_bounds)),
5127  })
5128}
5129
5130/// Create a wry ipc handler from a tauri ipc handler.
5131fn create_ipc_handler<T: UserEvent>(
5132  _kind: WebviewKind,
5133  window_id: Arc<Mutex<WindowId>>,
5134  webview_id: WebviewId,
5135  context: Context<T>,
5136  label: String,
5137  ipc_handler: Option<WebviewIpcHandler<T, Wry<T>>>,
5138) -> Box<IpcHandler> {
5139  Box::new(move |request| {
5140    if let Some(handler) = &ipc_handler {
5141      handler(
5142        DetachedWebview {
5143          label: label.clone(),
5144          dispatcher: WryWebviewDispatcher {
5145            window_id: window_id.clone(),
5146            webview_id,
5147            context: context.clone(),
5148          },
5149        },
5150        request,
5151      );
5152    }
5153  })
5154}
5155
5156#[cfg(target_os = "macos")]
5157fn inner_size(
5158  window: &Window,
5159  webviews: &[WebviewWrapper],
5160  has_children: bool,
5161) -> TaoPhysicalSize<u32> {
5162  if !has_children && !webviews.is_empty() {
5163    use wry::WebViewExtMacOS;
5164    let webview = webviews.first().unwrap();
5165    let view = unsafe { Retained::cast_unchecked::<objc2_app_kit::NSView>(webview.webview()) };
5166    let view_frame = view.frame();
5167    let logical: TaoLogicalSize<f64> = (view_frame.size.width, view_frame.size.height).into();
5168    return logical.to_physical(window.scale_factor());
5169  }
5170
5171  window.inner_size()
5172}
5173
5174#[cfg(not(target_os = "macos"))]
5175#[allow(unused_variables)]
5176fn inner_size(
5177  window: &Window,
5178  webviews: &[WebviewWrapper],
5179  has_children: bool,
5180) -> TaoPhysicalSize<u32> {
5181  window.inner_size()
5182}
5183
5184fn to_tao_theme(theme: Option<Theme>) -> Option<TaoTheme> {
5185  match theme {
5186    Some(Theme::Light) => Some(TaoTheme::Light),
5187    Some(Theme::Dark) => Some(TaoTheme::Dark),
5188    _ => None,
5189  }
5190}