Skip to main content

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