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