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