Skip to main content

tauri_runtime_wry/
lib.rs

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