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