tauri/
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//! Tauri is a framework for building tiny, blazing fast binaries for all major desktop platforms.
6//! Developers can integrate any front-end framework that compiles to HTML, JS and CSS for building their user interface.
7//! The backend of the application is a rust-sourced binary with an API that the front-end can interact with.
8//!
9//! # Cargo features
10//!
11//! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that can be enabled or disabled:
12//!
13//! - **wry** *(enabled by default)*: Enables the [wry](https://github.com/tauri-apps/wry) runtime. Only disable it if you want a custom runtime.
14//! - **common-controls-v6** *(enabled by default)*: Enables [Common Controls v6](https://learn.microsoft.com/en-us/windows/win32/controls/common-control-versions) support on Windows, mainly for the predefined `about` menu item.
15//! - **unstable**: Enables unstable features. Be careful, it might introduce breaking changes in future minor releases.
16//! - **tracing**: Enables [`tracing`](https://docs.rs/tracing/latest/tracing) for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers.
17//! - **test**: Enables the [`mod@test`] module exposing unit test helpers.
18//! - **objc-exception**: This feature flag is no-op since 2.3.0.
19//! - **linux-libxdo**: Enables linking to libxdo which enables Cut, Copy, Paste and SelectAll menu items to work on Linux.
20//! - **isolation**: Enables the isolation pattern. Enabled by default if the `app > security > pattern > use` config option is set to `isolation` on the `tauri.conf.json` file.
21//! - **custom-protocol**: Feature managed by the Tauri CLI. When enabled, Tauri assumes a production environment instead of a development one.
22//! - **devtools**: Enables the developer tools (Web inspector) and [`window::Window#method.open_devtools`]. Enabled by default on debug builds.
23//!   On macOS it uses private APIs, so you can't enable it if your app will be published to the App Store.
24//! - **native-tls**: Provides TLS support to connect over HTTPS.
25//! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL.
26//! - **rustls-tls**: Provides TLS support to connect over HTTPS using rustls.
27//! - **process-relaunch-dangerous-allow-symlink-macos**: Allows the [`process::current_binary`] function to allow symlinks on macOS (this is dangerous, see the Security section in the documentation website).
28//! - **tray-icon**: Enables application tray icon APIs. Enabled by default if the `trayIcon` config is defined on the `tauri.conf.json` file.
29//! - **macos-private-api**: Enables features only available in **macOS**'s private APIs, currently the `transparent` window functionality and the `fullScreenEnabled` preference setting to `true`. Enabled by default if the `tauri > macosPrivateApi` config flag is set to `true` on the `tauri.conf.json` file.
30//! - **webview-data-url**: Enables usage of data URLs on the webview.
31//! - **compression** *(enabled by default): Enables asset compression. You should only disable this if you want faster compile times in release builds - it produces larger binaries.
32//! - **config-json5**: Adds support to JSON5 format for `tauri.conf.json`.
33//! - **config-toml**: Adds support to TOML format for the configuration `Tauri.toml`.
34//! - **image-ico**: Adds support to parse `.ico` image, see [`Image`].
35//! - **image-png**: Adds support to parse `.png` image, see [`Image`].
36//! - **macos-proxy**: Adds support for [`WebviewBuilder::proxy_url`] on macOS. Requires macOS 14+.
37//! - **specta**: Add support for [`specta::specta`](https://docs.rs/specta/%5E2.0.0-rc.9/specta/attr.specta.html) with Tauri arguments such as [`State`](crate::State), [`Window`](crate::Window) and [`AppHandle`](crate::AppHandle)
38//!
39//! ## Cargo allowlist features
40//!
41//! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that enables commands for Tauri's API package.
42//! These features are automatically enabled by the Tauri CLI based on the `allowlist` configuration under `tauri.conf.json`.
43//!
44//! ### Protocol allowlist
45//!
46//! - **protocol-asset**: Enables the `asset` custom protocol.
47
48#![doc(
49  html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
50  html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
51)]
52#![warn(missing_docs, rust_2018_idioms)]
53#![cfg_attr(docsrs, feature(doc_cfg))]
54
55/// Setups the binding that initializes an iOS plugin.
56#[cfg(target_os = "ios")]
57#[macro_export]
58macro_rules! ios_plugin_binding {
59  ($fn_name: ident) => {
60    tauri::swift_rs::swift!(fn $fn_name() -> *const ::std::ffi::c_void);
61  }
62}
63#[cfg(target_os = "macos")]
64#[doc(hidden)]
65pub use embed_plist;
66pub use error::{Error, Result};
67use ipc::{RuntimeAuthority, RuntimeCapability};
68pub use resources::{Resource, ResourceId, ResourceTable};
69#[cfg(target_os = "ios")]
70#[doc(hidden)]
71pub use swift_rs;
72pub use tauri_macros::include_image;
73#[cfg(mobile)]
74pub use tauri_macros::mobile_entry_point;
75pub use tauri_macros::{command, generate_handler};
76
77use tauri_utils::assets::AssetsIter;
78pub use url::Url;
79
80pub(crate) mod app;
81pub mod async_runtime;
82mod error;
83mod event;
84pub mod ipc;
85mod manager;
86mod pattern;
87pub mod plugin;
88pub(crate) mod protocol;
89mod resources;
90mod vibrancy;
91pub mod webview;
92pub mod window;
93use tauri_runtime as runtime;
94pub mod image;
95#[cfg(target_os = "ios")]
96mod ios;
97#[cfg(desktop)]
98#[cfg_attr(docsrs, doc(cfg(desktop)))]
99pub mod menu;
100/// Path APIs.
101pub mod path;
102pub mod process;
103/// The allowlist scopes.
104pub mod scope;
105mod state;
106
107#[cfg(all(desktop, feature = "tray-icon"))]
108#[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
109pub mod tray;
110pub use tauri_utils as utils;
111
112pub use http;
113
114/// A Tauri [`Runtime`] wrapper around wry.
115#[cfg(feature = "wry")]
116#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
117pub type Wry = tauri_runtime_wry::Wry<EventLoopMessage>;
118/// A Tauri [`RuntimeHandle`] wrapper around wry.
119#[cfg(feature = "wry")]
120#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
121pub type WryHandle = tauri_runtime_wry::WryHandle<EventLoopMessage>;
122
123#[cfg(all(feature = "wry", target_os = "android"))]
124#[cfg_attr(docsrs, doc(cfg(all(feature = "wry", target_os = "android"))))]
125#[doc(hidden)]
126#[macro_export]
127macro_rules! android_binding {
128  ($domain:ident, $app_name:ident, $main:ident, $wry:path) => {
129    use $wry::{
130      android_setup,
131      prelude::{JClass, JNIEnv, JString},
132    };
133
134    ::tauri::wry::android_binding!($domain, $app_name, $wry);
135
136    ::tauri::tao::android_binding!(
137      $domain,
138      $app_name,
139      WryActivity,
140      android_setup,
141      $main,
142      ::tauri::tao
143    );
144
145    // be careful when renaming this, the `Java_app_tauri_plugin_PluginManager_handlePluginResponse` symbol is checked by the CLI
146    ::tauri::tao::platform::android::prelude::android_fn!(
147      app_tauri,
148      plugin,
149      PluginManager,
150      handlePluginResponse,
151      [i32, JString, JString],
152    );
153    ::tauri::tao::platform::android::prelude::android_fn!(
154      app_tauri,
155      plugin,
156      PluginManager,
157      sendChannelData,
158      [i64, JString],
159    );
160
161    // this function is a glue between PluginManager.kt > handlePluginResponse and Rust
162    #[allow(non_snake_case)]
163    pub fn handlePluginResponse(
164      mut env: JNIEnv,
165      _: JClass,
166      id: i32,
167      success: JString,
168      error: JString,
169    ) {
170      ::tauri::handle_android_plugin_response(&mut env, id, success, error);
171    }
172
173    // this function is a glue between PluginManager.kt > sendChannelData and Rust
174    #[allow(non_snake_case)]
175    pub fn sendChannelData(mut env: JNIEnv, _: JClass, id: i64, data: JString) {
176      ::tauri::send_channel_data(&mut env, id, data);
177    }
178  };
179}
180
181#[cfg(all(feature = "wry", target_os = "android"))]
182#[doc(hidden)]
183pub use plugin::mobile::{handle_android_plugin_response, send_channel_data};
184#[cfg(all(feature = "wry", target_os = "android"))]
185#[doc(hidden)]
186pub use tauri_runtime_wry::{tao, wry};
187
188/// A task to run on the main thread.
189pub type SyncTask = Box<dyn FnOnce() + Send>;
190
191use serde::Serialize;
192use std::{
193  borrow::Cow,
194  collections::HashMap,
195  fmt::{self, Debug},
196  sync::MutexGuard,
197};
198use utils::assets::{AssetKey, CspHash, EmbeddedAssets};
199
200#[cfg(feature = "wry")]
201#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
202pub use tauri_runtime_wry::webview_version;
203
204#[cfg(target_os = "macos")]
205#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
206pub use runtime::ActivationPolicy;
207
208#[cfg(target_os = "macos")]
209pub use self::utils::TitleBarStyle;
210
211use self::event::EventName;
212pub use self::event::{Event, EventId, EventTarget};
213use self::manager::EmitPayload;
214pub use {
215  self::app::{
216    App, AppHandle, AssetResolver, Builder, CloseRequestApi, ExitRequestApi, RunEvent,
217    UriSchemeContext, UriSchemeResponder, WebviewEvent, WindowEvent,
218  },
219  self::manager::Asset,
220  self::runtime::{
221    dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
222    webview::WebviewAttributes,
223    window::{CursorIcon, DragDropEvent, WindowSizeConstraints},
224    DeviceEventFilter, Rect, UserAttentionType,
225  },
226  self::state::{State, StateManager},
227  self::utils::{
228    config::{Config, WebviewUrl},
229    Env, PackageInfo, Theme,
230  },
231  self::webview::{Webview, WebviewWindow, WebviewWindowBuilder},
232  self::window::{Monitor, Window},
233  scope::*,
234};
235
236#[cfg(feature = "unstable")]
237#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
238pub use {self::webview::WebviewBuilder, self::window::WindowBuilder};
239
240/// The Tauri version.
241pub const VERSION: &str = env!("CARGO_PKG_VERSION");
242
243#[cfg(target_os = "ios")]
244#[doc(hidden)]
245pub fn log_stdout() {
246  use std::{
247    ffi::CString,
248    fs::File,
249    io::{BufRead, BufReader},
250    os::unix::prelude::*,
251    thread,
252  };
253
254  let mut logpipe: [RawFd; 2] = Default::default();
255  unsafe {
256    libc::pipe(logpipe.as_mut_ptr());
257    libc::dup2(logpipe[1], libc::STDOUT_FILENO);
258    libc::dup2(logpipe[1], libc::STDERR_FILENO);
259  }
260  thread::spawn(move || unsafe {
261    let file = File::from_raw_fd(logpipe[0]);
262    let mut reader = BufReader::new(file);
263    let mut buffer = String::new();
264    loop {
265      buffer.clear();
266      if let Ok(len) = reader.read_line(&mut buffer) {
267        if len == 0 {
268          break;
269        } else if let Ok(msg) = CString::new(buffer.as_bytes())
270          .map_err(|_| ())
271          .and_then(|c| c.into_string().map_err(|_| ()))
272        {
273          log::info!("{}", msg);
274        }
275      }
276    }
277  });
278}
279
280/// The user event type.
281#[derive(Debug, Clone)]
282pub enum EventLoopMessage {
283  /// An event from a menu item, could be on the window menu bar, application menu bar (on macOS) or tray icon menu.
284  #[cfg(desktop)]
285  MenuEvent(menu::MenuEvent),
286  /// An event from a menu item, could be on the window menu bar, application menu bar (on macOS) or tray icon menu.
287  #[cfg(all(desktop, feature = "tray-icon"))]
288  #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
289  TrayIconEvent(tray::TrayIconEvent),
290}
291
292/// The webview runtime interface. A wrapper around [`runtime::Runtime`] with the proper user event type associated.
293pub trait Runtime: runtime::Runtime<EventLoopMessage> {}
294/// The webview runtime handle. A wrapper arond [`runtime::RuntimeHandle`] with the proper user event type associated.
295pub trait RuntimeHandle: runtime::RuntimeHandle<EventLoopMessage> {}
296
297impl<W: runtime::Runtime<EventLoopMessage>> Runtime for W {}
298impl<R: runtime::RuntimeHandle<EventLoopMessage>> RuntimeHandle for R {}
299
300/// Reads the config file at compile time and generates a [`Context`] based on its content.
301///
302/// The default config file path is a `tauri.conf.json` file inside the Cargo manifest directory of
303/// the crate being built.
304///
305/// # Custom Config Path
306///
307/// You may pass a string literal to this macro to specify a custom path for the Tauri config file.
308/// If the path is relative, it will be search for relative to the Cargo manifest of the compiling
309/// crate.
310///
311/// # Note
312///
313/// This macro should not be called if you are using [`tauri-build`] to generate the context from
314/// inside your build script as it will just cause excess computations that will be discarded. Use
315/// either the [`tauri-build`] method or this macro - not both.
316///
317/// [`tauri-build`]: https://docs.rs/tauri-build
318pub use tauri_macros::generate_context;
319
320/// Include a [`Context`] that was generated by [`tauri-build`] inside your build script.
321///
322/// You should either use [`tauri-build`] and this macro to include the compile time generated code,
323/// or [`generate_context!`]. Do not use both at the same time, as they generate the same code and
324/// will cause excess computations that will be discarded.
325///
326/// [`tauri-build`]: https://docs.rs/tauri-build
327#[macro_export]
328macro_rules! tauri_build_context {
329  () => {
330    include!(concat!(env!("OUT_DIR"), "/tauri-build-context.rs"))
331  };
332}
333
334pub use pattern::Pattern;
335
336/// Whether we are running in development mode or not.
337pub const fn is_dev() -> bool {
338  !cfg!(feature = "custom-protocol")
339}
340
341/// Represents a container of file assets that are retrievable during runtime.
342pub trait Assets<R: Runtime>: Send + Sync + 'static {
343  /// Initialize the asset provider.
344  fn setup(&self, app: &App<R>) {
345    let _ = app;
346  }
347
348  /// Get the content of the passed [`AssetKey`].
349  fn get(&self, key: &AssetKey) -> Option<Cow<'_, [u8]>>;
350
351  /// Iterator for the assets.
352  fn iter(&self) -> Box<tauri_utils::assets::AssetsIter<'_>>;
353
354  /// Gets the hashes for the CSP tag of the HTML on the given path.
355  fn csp_hashes(&self, html_path: &AssetKey) -> Box<dyn Iterator<Item = CspHash<'_>> + '_>;
356}
357
358impl<R: Runtime> Assets<R> for EmbeddedAssets {
359  fn get(&self, key: &AssetKey) -> Option<Cow<'_, [u8]>> {
360    EmbeddedAssets::get(self, key)
361  }
362
363  fn iter(&self) -> Box<AssetsIter<'_>> {
364    EmbeddedAssets::iter(self)
365  }
366
367  fn csp_hashes(&self, html_path: &AssetKey) -> Box<dyn Iterator<Item = CspHash<'_>> + '_> {
368    EmbeddedAssets::csp_hashes(self, html_path)
369  }
370}
371
372/// User supplied data required inside of a Tauri application.
373///
374/// # Stability
375/// This is the output of the [`generate_context`] macro, and is not considered part of the stable API.
376/// Unless you know what you are doing and are prepared for this type to have breaking changes, do not create it yourself.
377#[tauri_macros::default_runtime(Wry, wry)]
378pub struct Context<R: Runtime> {
379  pub(crate) config: Config,
380  #[cfg(dev)]
381  pub(crate) config_parent: Option<std::path::PathBuf>,
382  /// Asset provider.
383  pub assets: Box<dyn Assets<R>>,
384  pub(crate) default_window_icon: Option<image::Image<'static>>,
385  pub(crate) app_icon: Option<Vec<u8>>,
386  #[cfg(all(desktop, feature = "tray-icon"))]
387  pub(crate) tray_icon: Option<image::Image<'static>>,
388  pub(crate) package_info: PackageInfo,
389  pub(crate) pattern: Pattern,
390  pub(crate) runtime_authority: RuntimeAuthority,
391  pub(crate) plugin_global_api_scripts: Option<&'static [&'static str]>,
392}
393
394impl<R: Runtime> fmt::Debug for Context<R> {
395  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396    let mut d = f.debug_struct("Context");
397    d.field("config", &self.config)
398      .field("default_window_icon", &self.default_window_icon)
399      .field("app_icon", &self.app_icon)
400      .field("package_info", &self.package_info)
401      .field("pattern", &self.pattern)
402      .field("plugin_global_api_scripts", &self.plugin_global_api_scripts);
403
404    #[cfg(all(desktop, feature = "tray-icon"))]
405    d.field("tray_icon", &self.tray_icon);
406
407    d.finish()
408  }
409}
410
411impl<R: Runtime> Context<R> {
412  /// The config the application was prepared with.
413  #[inline(always)]
414  pub fn config(&self) -> &Config {
415    &self.config
416  }
417
418  /// A mutable reference to the config the application was prepared with.
419  #[inline(always)]
420  pub fn config_mut(&mut self) -> &mut Config {
421    &mut self.config
422  }
423
424  /// The assets to be served directly by Tauri.
425  #[inline(always)]
426  pub fn assets(&self) -> &dyn Assets<R> {
427    self.assets.as_ref()
428  }
429
430  /// Replace the [`Assets`] implementation and returns the previous value so you can use it as a fallback if desired.
431  #[inline(always)]
432  pub fn set_assets(&mut self, assets: Box<dyn Assets<R>>) -> Box<dyn Assets<R>> {
433    std::mem::replace(&mut self.assets, assets)
434  }
435
436  /// The default window icon Tauri should use when creating windows.
437  #[inline(always)]
438  pub fn default_window_icon(&self) -> Option<&image::Image<'_>> {
439    self.default_window_icon.as_ref()
440  }
441
442  /// Set the default window icon Tauri should use when creating windows.
443  #[inline(always)]
444  pub fn set_default_window_icon(&mut self, icon: Option<image::Image<'static>>) {
445    self.default_window_icon = icon;
446  }
447
448  /// The icon to use on the tray icon.
449  #[cfg(all(desktop, feature = "tray-icon"))]
450  #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
451  #[inline(always)]
452  pub fn tray_icon(&self) -> Option<&image::Image<'_>> {
453    self.tray_icon.as_ref()
454  }
455
456  /// Set the icon to use on the tray icon.
457  #[cfg(all(desktop, feature = "tray-icon"))]
458  #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
459  #[inline(always)]
460  pub fn set_tray_icon(&mut self, icon: Option<image::Image<'static>>) {
461    self.tray_icon = icon;
462  }
463
464  /// Package information.
465  #[inline(always)]
466  pub fn package_info(&self) -> &PackageInfo {
467    &self.package_info
468  }
469
470  /// A mutable reference to the package information.
471  #[inline(always)]
472  pub fn package_info_mut(&mut self) -> &mut PackageInfo {
473    &mut self.package_info
474  }
475
476  /// The application pattern.
477  #[inline(always)]
478  pub fn pattern(&self) -> &Pattern {
479    &self.pattern
480  }
481
482  /// A mutable reference to the resolved ACL.
483  ///
484  /// # Stability
485  ///
486  /// This API is unstable.
487  #[doc(hidden)]
488  #[inline(always)]
489  pub fn runtime_authority_mut(&mut self) -> &mut RuntimeAuthority {
490    &mut self.runtime_authority
491  }
492
493  /// Create a new [`Context`] from the minimal required items.
494  #[inline(always)]
495  #[allow(clippy::too_many_arguments)]
496  pub fn new(
497    config: Config,
498    assets: Box<dyn Assets<R>>,
499    default_window_icon: Option<image::Image<'static>>,
500    app_icon: Option<Vec<u8>>,
501    package_info: PackageInfo,
502    pattern: Pattern,
503    runtime_authority: RuntimeAuthority,
504    plugin_global_api_scripts: Option<&'static [&'static str]>,
505  ) -> Self {
506    Self {
507      config,
508      #[cfg(dev)]
509      config_parent: None,
510      assets,
511      default_window_icon,
512      app_icon,
513      #[cfg(all(desktop, feature = "tray-icon"))]
514      tray_icon: None,
515      package_info,
516      pattern,
517      runtime_authority,
518      plugin_global_api_scripts,
519    }
520  }
521
522  #[cfg(dev)]
523  #[doc(hidden)]
524  pub fn with_config_parent(&mut self, config_parent: impl AsRef<std::path::Path>) {
525    self
526      .config_parent
527      .replace(config_parent.as_ref().to_owned());
528  }
529}
530
531// TODO: expand these docs
532/// Manages a running application.
533pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
534  /// The application handle associated with this manager.
535  fn app_handle(&self) -> &AppHandle<R> {
536    self.managed_app_handle()
537  }
538
539  /// The [`Config`] the manager was created with.
540  fn config(&self) -> &Config {
541    self.manager().config()
542  }
543
544  /// The [`PackageInfo`] the manager was created with.
545  fn package_info(&self) -> &PackageInfo {
546    self.manager().package_info()
547  }
548
549  /// Fetch a single window from the manager.
550  #[cfg(feature = "unstable")]
551  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
552  fn get_window(&self, label: &str) -> Option<Window<R>> {
553    self.manager().get_window(label)
554  }
555
556  /// Fetch the focused window. Returns `None` if there is not any focused window.
557  #[cfg(feature = "unstable")]
558  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
559  fn get_focused_window(&self) -> Option<Window<R>> {
560    self.manager().get_focused_window()
561  }
562
563  /// Fetch all managed windows.
564  #[cfg(feature = "unstable")]
565  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
566  fn windows(&self) -> HashMap<String, Window<R>> {
567    self.manager().windows()
568  }
569
570  /// Fetch a single webview from the manager.
571  #[cfg(feature = "unstable")]
572  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
573  fn get_webview(&self, label: &str) -> Option<Webview<R>> {
574    self.manager().get_webview(label)
575  }
576
577  /// Fetch all managed webviews.
578  #[cfg(feature = "unstable")]
579  #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
580  fn webviews(&self) -> HashMap<String, Webview<R>> {
581    self.manager().webviews()
582  }
583
584  /// Fetch a single webview window from the manager.
585  fn get_webview_window(&self, label: &str) -> Option<WebviewWindow<R>> {
586    self.manager().get_webview(label).and_then(|webview| {
587      let window = webview.window();
588      if window.is_webview_window() {
589        Some(WebviewWindow {
590          window: window.clone(),
591          webview,
592        })
593      } else {
594        None
595      }
596    })
597  }
598
599  /// Fetch all managed webview windows.
600  fn webview_windows(&self) -> HashMap<String, WebviewWindow<R>> {
601    self
602      .manager()
603      .webviews()
604      .into_iter()
605      .filter_map(|(label, webview)| {
606        let window = webview.window();
607        if window.is_webview_window() {
608          Some((
609            label,
610            WebviewWindow {
611              window: window.clone(),
612              webview,
613            },
614          ))
615        } else {
616          None
617        }
618      })
619      .collect::<HashMap<_, _>>()
620  }
621
622  /// Add `state` to the state managed by the application.
623  ///
624  /// If the state for the `T` type has previously been set, the state is unchanged and false is returned. Otherwise true is returned.
625  ///
626  /// Managed state can be retrieved by any command handler via the
627  /// [`State`] guard. In particular, if a value of type `T`
628  /// is managed by Tauri, adding `State<T>` to the list of arguments in a
629  /// command handler instructs Tauri to retrieve the managed value.
630  /// Additionally, [`state`](Self#method.state) can be used to retrieve the value manually.
631  ///
632  /// # Mutability
633  ///
634  /// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
635  ///
636  /// ```rust,no_run
637  /// use std::{collections::HashMap, sync::Mutex};
638  /// use tauri::State;
639  /// // here we use Mutex to achieve interior mutability
640  /// struct Storage {
641  ///   store: Mutex<HashMap<u64, String>>,
642  /// }
643  /// struct Connection;
644  /// struct DbConnection {
645  ///   db: Mutex<Option<Connection>>,
646  /// }
647  ///
648  /// #[tauri::command]
649  /// fn connect(connection: State<DbConnection>) {
650  ///   // initialize the connection, mutating the state with interior mutability
651  ///   *connection.db.lock().unwrap() = Some(Connection {});
652  /// }
653  ///
654  /// #[tauri::command]
655  /// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
656  ///   // mutate the storage behind the Mutex
657  ///   storage.store.lock().unwrap().insert(key, value);
658  /// }
659  ///
660  /// tauri::Builder::default()
661  ///   .manage(Storage { store: Default::default() })
662  ///   .manage(DbConnection { db: Default::default() })
663  ///   .invoke_handler(tauri::generate_handler![connect, storage_insert])
664  ///   // on an actual app, remove the string argument
665  ///   .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
666  ///   .expect("error while running tauri application");
667  /// ```
668  ///
669  /// # Examples
670  ///
671  /// ```rust,no_run
672  /// use tauri::{Manager, State};
673  ///
674  /// struct MyInt(isize);
675  /// struct MyString(String);
676  ///
677  /// #[tauri::command]
678  /// fn int_command(state: State<MyInt>) -> String {
679  ///     format!("The stateful int is: {}", state.0)
680  /// }
681  ///
682  /// #[tauri::command]
683  /// fn string_command<'r>(state: State<'r, MyString>) {
684  ///     println!("state: {}", state.inner().0);
685  /// }
686  ///
687  /// tauri::Builder::default()
688  ///   .setup(|app| {
689  ///     app.manage(MyInt(0));
690  ///     app.manage(MyString("tauri".into()));
691  ///     // `MyInt` is already managed, so `manage()` returns false
692  ///     assert!(!app.manage(MyInt(1)));
693  ///     // read the `MyInt` managed state with the turbofish syntax
694  ///     let int = app.state::<MyInt>();
695  ///     assert_eq!(int.0, 0);
696  ///     // read the `MyString` managed state with the `State` guard
697  ///     let val: State<MyString> = app.state();
698  ///     assert_eq!(val.0, "tauri");
699  ///     Ok(())
700  ///   })
701  ///   .invoke_handler(tauri::generate_handler![int_command, string_command])
702  ///   // on an actual app, remove the string argument
703  ///   .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
704  ///   .expect("error while running tauri application");
705  /// ```
706  fn manage<T>(&self, state: T) -> bool
707  where
708    T: Send + Sync + 'static,
709  {
710    self.manager().state().set(state)
711  }
712
713  /// Removes the state managed by the application for T. Returns the state if it was actually removed.
714  ///
715  /// <div class="warning">
716  ///
717  /// This method is *UNSAFE* and calling it will cause previously obtained references through
718  /// [Manager::state] and [State::inner] to become dangling references.
719  ///
720  /// It is currently deprecated and may be removed in the future.
721  ///
722  /// If you really want to unmanage a state, use [std::sync::Mutex] and [Option::take] to wrap the state instead.
723  ///
724  /// See [tauri-apps/tauri#12721] for more information.
725  ///
726  /// [tauri-apps/tauri#12721]: https://github.com/tauri-apps/tauri/issues/12721
727  ///
728  /// </div>
729  #[deprecated(
730    since = "2.3.0",
731    note = "This method is unsafe, since it can cause dangling references."
732  )]
733  fn unmanage<T>(&self) -> Option<T>
734  where
735    T: Send + Sync + 'static,
736  {
737    // The caller decides to break the safety here, then OK, just let it go.
738    unsafe { self.manager().state().unmanage() }
739  }
740
741  /// Retrieves the managed state for the type `T`.
742  ///
743  /// # Panics
744  ///
745  /// Panics if the state for the type `T` has not been previously [managed](Self::manage).
746  /// Use [try_state](Self::try_state) for a non-panicking version.
747  fn state<T>(&self) -> State<'_, T>
748  where
749    T: Send + Sync + 'static,
750  {
751    self
752      .manager()
753      .state
754      .try_get()
755      .expect("state() called before manage() for given type")
756  }
757
758  /// Attempts to retrieve the managed state for the type `T`.
759  ///
760  /// Returns `Some` if the state has previously been [managed](Self::manage). Otherwise returns `None`.
761  fn try_state<T>(&self) -> Option<State<'_, T>>
762  where
763    T: Send + Sync + 'static,
764  {
765    self.manager().state.try_get()
766  }
767
768  /// Get a reference to the resources table of this manager.
769  fn resources_table(&self) -> MutexGuard<'_, ResourceTable>;
770
771  /// Gets the managed [`Env`].
772  fn env(&self) -> Env {
773    self.state::<Env>().inner().clone()
774  }
775
776  /// Gets the scope for the asset protocol.
777  #[cfg(feature = "protocol-asset")]
778  fn asset_protocol_scope(&self) -> scope::fs::Scope {
779    self.state::<Scopes>().inner().asset_protocol.clone()
780  }
781
782  /// The path resolver.
783  fn path(&self) -> &crate::path::PathResolver<R> {
784    self.state::<crate::path::PathResolver<R>>().inner()
785  }
786
787  /// Adds a capability to the app.
788  ///
789  /// Note that by default every capability file in the `src-tauri/capabilities` folder
790  /// are automatically enabled unless specific capabilities are configured in [`tauri.conf.json > app > security > capabilities`],
791  /// so you should use a different director for the runtime-added capabilities or use [tauri_build::Attributes::capabilities_path_pattern].
792  ///
793  /// # Examples
794  /// ```
795  /// use tauri::Manager;
796  ///
797  /// tauri::Builder::default()
798  ///   .setup(|app| {
799  ///     #[cfg(feature = "beta")]
800  ///     app.add_capability(include_str!("../capabilities/beta/cap.json"));
801  ///
802  ///     #[cfg(feature = "stable")]
803  ///     app.add_capability(include_str!("../capabilities/stable/cap.json"));
804  ///     Ok(())
805  ///   });
806  /// ```
807  ///
808  /// The above example assumes the following directory layout:
809  /// ```md
810  /// ├── capabilities
811  /// │   ├── app (default capabilities used by any app flavor)
812  /// |   |   |-- cap.json
813  /// │   ├── beta (capabilities only added to a `beta` flavor)
814  /// |   |   |-- cap.json
815  /// │   ├── stable (capabilities only added to a `stable` flavor)
816  /// |       |-- cap.json
817  /// ```
818  ///
819  /// For this layout to be properly parsed by Tauri, we need to change the build script to
820  ///
821  /// ```skip
822  /// // only pick up capabilities in the capabilities/app folder by default
823  /// let attributes = tauri_build::Attributes::new().capabilities_path_pattern("./capabilities/app/*.json");
824  /// tauri_build::try_build(attributes).unwrap();
825  /// ```
826  ///
827  /// [`tauri.conf.json > app > security > capabilities`]: https://tauri.app/reference/config/#capabilities
828  /// [tauri_build::Attributes::capabilities_path_pattern]: https://docs.rs/tauri-build/2/tauri_build/struct.Attributes.html#method.capabilities_path_pattern
829  fn add_capability(&self, capability: impl RuntimeCapability) -> Result<()> {
830    self
831      .manager()
832      .runtime_authority
833      .lock()
834      .unwrap()
835      .add_capability(capability)
836  }
837}
838
839/// Listen to events.
840pub trait Listener<R: Runtime>: sealed::ManagerBase<R> {
841  /// Listen to an emitted event on this manager.
842  ///
843  /// # Examples
844  /// ```
845  /// use tauri::{Manager, Listener, Emitter};
846  ///
847  /// #[tauri::command]
848  /// fn synchronize(window: tauri::Window) {
849  ///   // emits the synchronized event to all windows
850  ///   window.emit("synchronized", ());
851  /// }
852  ///
853  /// tauri::Builder::default()
854  ///   .setup(|app| {
855  ///     app.listen("synchronized", |event| {
856  ///       println!("app is in sync");
857  ///     });
858  ///     Ok(())
859  ///   })
860  ///   .invoke_handler(tauri::generate_handler![synchronize]);
861  /// ```
862  /// # Panics
863  /// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
864  fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
865  where
866    F: Fn(Event) + Send + 'static;
867
868  /// Listen to an event on this manager only once.
869  ///
870  /// See [`Self::listen`] for more information.
871  /// # Panics
872  /// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
873  fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
874  where
875    F: FnOnce(Event) + Send + 'static;
876
877  /// Remove an event listener.
878  ///
879  /// # Examples
880  /// ```
881  /// use tauri::{Manager, Listener};
882  ///
883  /// tauri::Builder::default()
884  ///   .setup(|app| {
885  ///     let handle = app.handle().clone();
886  ///     let handler = app.listen_any("ready", move |event| {
887  ///       println!("app is ready");
888  ///
889  ///       // we no longer need to listen to the event
890  ///       // we also could have used `app.once_global` instead
891  ///       handle.unlisten(event.id());
892  ///     });
893  ///
894  ///     // stop listening to the event when you do not need it anymore
895  ///     app.unlisten(handler);
896  ///
897  ///
898  ///     Ok(())
899  ///   });
900  /// ```
901  fn unlisten(&self, id: EventId);
902
903  /// Listen to an emitted event to any [target](EventTarget).
904  ///
905  /// # Examples
906  /// ```
907  /// use tauri::{Manager, Emitter, Listener};
908  ///
909  /// #[tauri::command]
910  /// fn synchronize(window: tauri::Window) {
911  ///   // emits the synchronized event to all windows
912  ///   window.emit("synchronized", ());
913  /// }
914  ///
915  /// tauri::Builder::default()
916  ///   .setup(|app| {
917  ///     app.listen_any("synchronized", |event| {
918  ///       println!("app is in sync");
919  ///     });
920  ///     Ok(())
921  ///   })
922  ///   .invoke_handler(tauri::generate_handler![synchronize]);
923  /// ```
924  /// # Panics
925  /// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
926  fn listen_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
927  where
928    F: Fn(Event) + Send + 'static,
929  {
930    let event = EventName::new(event.into()).unwrap();
931    self.manager().listen(event, EventTarget::Any, handler)
932  }
933
934  /// Listens once to an emitted event to any [target](EventTarget) .
935  ///
936  /// See [`Self::listen_any`] for more information.
937  /// # Panics
938  /// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
939  fn once_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
940  where
941    F: FnOnce(Event) + Send + 'static,
942  {
943    let event = EventName::new(event.into()).unwrap();
944    self.manager().once(event, EventTarget::Any, handler)
945  }
946}
947
948/// Emit events.
949pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
950  /// Emits an event to all [targets](EventTarget).
951  ///
952  /// # Examples
953  /// ```
954  /// use tauri::Emitter;
955  ///
956  /// #[tauri::command]
957  /// fn synchronize(app: tauri::AppHandle) {
958  ///   // emits the synchronized event to all webviews
959  ///   app.emit("synchronized", ());
960  /// }
961  /// ```
962  fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
963    let event = EventName::new(event)?;
964    let payload = EmitPayload::Serialize(&payload);
965    self.manager().emit(event, payload)
966  }
967
968  /// Similar to [`Emitter::emit`] but the payload is json serialized.
969  fn emit_str(&self, event: &str, payload: String) -> Result<()> {
970    let event = EventName::new(event)?;
971    let payload = EmitPayload::<()>::Str(payload);
972    self.manager().emit(event, payload)
973  }
974
975  /// Emits an event to all [targets](EventTarget) matching the given target.
976  ///
977  /// # Examples
978  /// ```
979  /// use tauri::{Emitter, EventTarget};
980  ///
981  /// #[tauri::command]
982  /// fn download(app: tauri::AppHandle) {
983  ///   for i in 1..100 {
984  ///     std::thread::sleep(std::time::Duration::from_millis(150));
985  ///     // emit a download progress event to all listeners
986  ///     app.emit_to(EventTarget::any(), "download-progress", i);
987  ///     // emit an event to listeners that used App::listen or AppHandle::listen
988  ///     app.emit_to(EventTarget::app(), "download-progress", i);
989  ///     // emit an event to any webview/window/webviewWindow matching the given label
990  ///     app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
991  ///     app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
992  ///     // emit an event to listeners that used WebviewWindow::listen
993  ///     app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
994  ///   }
995  /// }
996  /// ```
997  fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
998  where
999    I: Into<EventTarget>,
1000    S: Serialize + Clone,
1001  {
1002    let event = EventName::new(event)?;
1003    let payload = EmitPayload::Serialize(&payload);
1004    self.manager().emit_to(target, event, payload)
1005  }
1006
1007  /// Similar to [`Emitter::emit_to`] but the payload is json serialized.
1008  fn emit_str_to<I>(&self, target: I, event: &str, payload: String) -> Result<()>
1009  where
1010    I: Into<EventTarget>,
1011  {
1012    let event = EventName::new(event)?;
1013    let payload = EmitPayload::<()>::Str(payload);
1014    self.manager().emit_to(target, event, payload)
1015  }
1016
1017  /// Emits an event to all [targets](EventTarget) based on the given filter.
1018  ///
1019  /// # Examples
1020  /// ```
1021  /// use tauri::{Emitter, EventTarget};
1022  ///
1023  /// #[tauri::command]
1024  /// fn download(app: tauri::AppHandle) {
1025  ///   for i in 1..100 {
1026  ///     std::thread::sleep(std::time::Duration::from_millis(150));
1027  ///     // emit a download progress event to the updater window
1028  ///     app.emit_filter("download-progress", i, |t| match t {
1029  ///       EventTarget::WebviewWindow { label } => label == "main",
1030  ///       _ => false,
1031  ///     });
1032  ///   }
1033  /// }
1034  /// ```
1035  fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
1036  where
1037    S: Serialize + Clone,
1038    F: Fn(&EventTarget) -> bool,
1039  {
1040    let event = EventName::new(event)?;
1041    let payload = EmitPayload::Serialize(&payload);
1042    self.manager().emit_filter(event, payload, filter)
1043  }
1044
1045  /// Similar to [`Emitter::emit_filter`] but the payload is json serialized.
1046  fn emit_str_filter<F>(&self, event: &str, payload: String, filter: F) -> Result<()>
1047  where
1048    F: Fn(&EventTarget) -> bool,
1049  {
1050    let event = EventName::new(event)?;
1051    let payload = EmitPayload::<()>::Str(payload);
1052    self.manager().emit_filter(event, payload, filter)
1053  }
1054}
1055
1056/// Prevent implementation details from leaking out of the [`Manager`] trait.
1057pub(crate) mod sealed {
1058  use super::Runtime;
1059  use crate::{app::AppHandle, manager::AppManager};
1060  use std::sync::Arc;
1061
1062  /// A running [`Runtime`] or a dispatcher to it.
1063  pub enum RuntimeOrDispatch<'r, R: Runtime> {
1064    /// Reference to the running [`Runtime`].
1065    Runtime(&'r R),
1066
1067    /// Handle to the running [`Runtime`].
1068    RuntimeHandle(R::Handle),
1069
1070    /// A dispatcher to the running [`Runtime`].
1071    Dispatch(R::WindowDispatcher),
1072  }
1073
1074  /// Managed handle to the application runtime.
1075  pub trait ManagerBase<R: Runtime> {
1076    fn manager(&self) -> &AppManager<R>;
1077    fn manager_owned(&self) -> Arc<AppManager<R>>;
1078    fn runtime(&self) -> RuntimeOrDispatch<'_, R>;
1079    fn managed_app_handle(&self) -> &AppHandle<R>;
1080  }
1081}
1082
1083struct UnsafeSend<T>(T);
1084unsafe impl<T> Send for UnsafeSend<T> {}
1085
1086impl<T> UnsafeSend<T> {
1087  fn take(self) -> T {
1088    self.0
1089  }
1090}
1091
1092#[allow(unused)]
1093macro_rules! run_main_thread {
1094  ($handle:ident, $ex:expr) => {{
1095    use std::sync::mpsc::channel;
1096    let (tx, rx) = channel();
1097    let task = move || {
1098      let f = $ex;
1099      let _ = tx.send(f());
1100    };
1101    $handle
1102      .run_on_main_thread(task)
1103      .and_then(|_| rx.recv().map_err(|_| crate::Error::FailedToReceiveMessage))
1104  }};
1105}
1106
1107#[allow(unused)]
1108pub(crate) use run_main_thread;
1109
1110#[cfg(any(test, feature = "test"))]
1111#[cfg_attr(docsrs, doc(cfg(feature = "test")))]
1112pub mod test;
1113
1114#[cfg(feature = "specta")]
1115const _: () = {
1116  use specta::{datatype::DataType, function::FunctionArg, TypeMap};
1117
1118  impl<T: Send + Sync + 'static> FunctionArg for crate::State<'_, T> {
1119    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1120      None
1121    }
1122  }
1123
1124  impl<R: crate::Runtime> FunctionArg for crate::AppHandle<R> {
1125    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1126      None
1127    }
1128  }
1129
1130  impl<R: crate::Runtime> FunctionArg for crate::Window<R> {
1131    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1132      None
1133    }
1134  }
1135
1136  impl<R: crate::Runtime> FunctionArg for crate::Webview<R> {
1137    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1138      None
1139    }
1140  }
1141
1142  impl<R: crate::Runtime> FunctionArg for crate::WebviewWindow<R> {
1143    fn to_datatype(_: &mut TypeMap) -> Option<DataType> {
1144      None
1145    }
1146  }
1147};
1148
1149#[cfg(test)]
1150mod tests {
1151  use cargo_toml::Manifest;
1152  use std::{env::var, fs::read_to_string, path::PathBuf, sync::OnceLock};
1153
1154  static MANIFEST: OnceLock<Manifest> = OnceLock::new();
1155  const CHECKED_FEATURES: &str = include_str!(concat!(env!("OUT_DIR"), "/checked_features"));
1156
1157  fn get_manifest() -> &'static Manifest {
1158    MANIFEST.get_or_init(|| {
1159      let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
1160      Manifest::from_path(manifest_dir.join("Cargo.toml")).expect("failed to parse Cargo manifest")
1161    })
1162  }
1163
1164  #[test]
1165  fn features_are_documented() {
1166    let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
1167    let lib_code = read_to_string(manifest_dir.join("src/lib.rs")).expect("failed to read lib.rs");
1168
1169    for f in get_manifest().features.keys() {
1170      if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{f}**"))) {
1171        panic!("Feature {f} is not documented");
1172      }
1173    }
1174  }
1175
1176  #[test]
1177  fn aliased_features_exist() {
1178    let checked_features = CHECKED_FEATURES.split(',');
1179    let manifest = get_manifest();
1180    for checked_feature in checked_features {
1181      if !manifest.features.iter().any(|(f, _)| f == checked_feature) {
1182        panic!(
1183          "Feature {checked_feature} was checked in the alias build step but it does not exist in crates/tauri/Cargo.toml"
1184        );
1185      }
1186    }
1187  }
1188}
1189
1190#[cfg(test)]
1191mod test_utils {
1192  use proptest::prelude::*;
1193
1194  pub fn assert_send<T: Send>() {}
1195  pub fn assert_sync<T: Sync>() {}
1196
1197  #[allow(dead_code)]
1198  pub fn assert_not_allowlist_error<T>(res: anyhow::Result<T>) {
1199    if let Err(e) = res {
1200      assert!(!e.to_string().contains("not on the allowlist"));
1201    }
1202  }
1203
1204  proptest! {
1205    #![proptest_config(ProptestConfig::with_cases(10000))]
1206    #[test]
1207    // check to see if spawn executes a function.
1208    fn check_spawn_task(task in "[a-z]+") {
1209      // create dummy task function
1210      let dummy_task = async move {
1211        let _ = format!("{task}-run-dummy-task");
1212      };
1213      // call spawn
1214      crate::async_runtime::spawn(dummy_task);
1215    }
1216  }
1217}
1218
1219/// Simple dependency-free string encoder using [Z85].
1220mod z85 {
1221  const TABLE: &[u8; 85] =
1222    b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#";
1223
1224  /// Encode bytes with [Z85].
1225  ///
1226  /// # Panics
1227  ///
1228  /// Will panic if the input bytes are not a multiple of 4.
1229  pub fn encode(bytes: &[u8]) -> String {
1230    assert_eq!(bytes.len() % 4, 0);
1231
1232    let mut buf = String::with_capacity(bytes.len() * 5 / 4);
1233    for chunk in bytes.chunks_exact(4) {
1234      let mut chars = [0u8; 5];
1235      let mut chunk = u32::from_be_bytes(chunk.try_into().unwrap()) as usize;
1236      for byte in chars.iter_mut().rev() {
1237        *byte = TABLE[chunk % 85];
1238        chunk /= 85;
1239      }
1240
1241      buf.push_str(std::str::from_utf8(&chars).unwrap());
1242    }
1243
1244    buf
1245  }
1246
1247  #[cfg(test)]
1248  mod tests {
1249    #[test]
1250    fn encode() {
1251      assert_eq!(
1252        super::encode(&[0x86, 0x4F, 0xD2, 0x6F, 0xB5, 0x59, 0xF7, 0x5B]),
1253        "HelloWorld"
1254      );
1255    }
1256  }
1257}
1258
1259/// Generate a random 128-bit [Z85] encoded [`String`].
1260///
1261/// [Z85]: https://rfc.zeromq.org/spec/32/
1262pub(crate) fn generate_invoke_key() -> Result<String> {
1263  let mut bytes = [0u8; 16];
1264  getrandom::getrandom(&mut bytes)?;
1265  Ok(z85::encode(&bytes))
1266}