//! The Tauri commands [`crate::app::run_app`] registers for every app, and who may call them. //! //! An app's `build.rs` lists its own commands plus these in the app-command manifest //! ([`manifest`]), and its capability files grant [`SHELL`] to the local `shell` webview only, //! [`TAB`] to tab webviews only and [`SITE`] to tab webviews on the site's origin only; //! [`crate::testkit`] checks them. Every command re-checks its caller natively //! (`host::shell_only` / `host::tab_only` / `host::site_tab_only`) before anything else. /// Shell-only: the local `shell` webview (debug builds also accept the dev server). pub const SHELL: &[&str] = &[ "tabs_sync", "tab_navigate", "tab_action", "tab_zoom", "tab_find", "safe_mode_get", "safe_mode_set", "diag_get", "diag_log", "open_external_url", "shell_focus", "shell_ready", "tab_still_ready", "shell_raise", "window_system_menu", "profiles_get", "profiles_set", "i18n_get", "i18n_set", "plugins_list", "plugin_set_enabled", "plugins_open_folder", "custom_css_get", "custom_css_set", "features_get", "features_set", "mode_get", "mode_set", "settings_get", "settings_set", ]; /// Tab-only: any tab webview, speaking only about itself. pub const TAB: &[&str] = &["tab_shortcut", "tab_link_drag"]; /// Site-tab-only: a tab webview that is on the site's origin right now (the page runtime asks /// the shell to open the app's panel). Grant these in a capability limited to the site's URLs. pub const SITE: &[&str] = &["open_panel"]; /// Shell-only, optional: the store's commands (install links, dropped files, the signed Store and /// the install screen), served when the app sets `AppConfig::store`. An app that uses the store /// grants these to the shell (`testkit::Capabilities::assert_shell_only_group`). pub const STORE: &[&str] = &[ "install_pending", "install_stage_link", "install_stage_file", "install_cancel", "install_commit", "plugin_remove", "install_records", "install_forget", "store_catalog", "store_stage", "strings_list", "strings_read", "strings_set", "strings_remove", "strings_apply", ]; /// Shell-only, optional: the media panel's commands (gallery folders, previews, the clipboard /// history), served when the app sets `AppConfig::media`. An app that uses them grants these to the /// shell (`testkit::Capabilities::assert_shell_only_group`). pub const MEDIA: &[&str] = &[ "media_get", "media_set", "gallery_add_folder", "gallery_list", "gallery_thumb", "clip_list", "clip_thumb", "clip_remove", ]; /// Shell-only, optional: Google Fonts (what is installed, install, remove, the faces of a family and /// a preview), served when the app sets `AppConfig::fonts`; needs the `net` feature. pub const GFONTS: &[&str] = &["gfonts_installed", "gfonts_install", "gfonts_remove", "gfonts_faces", "gfonts_preview"]; /// Whether `command` is one of the host's (dispatched to the host, never to the app's handler). pub fn is_host(command: &str) -> bool { SHELL.contains(&command) || TAB.contains(&command) || SITE.contains(&command) || STORE.contains(&command) || MEDIA.contains(&command) || GFONTS.contains(&command) } /// The capability permission tauri-build generates for `command` (`tabs_sync` → `allow-tabs-sync`). pub fn permission(command: &str) -> String { format!("allow-{}", command.replace('_', "-")) } /// The app-command manifest for `build.rs`: the app's own commands followed by the host's (each /// once). Leaked on purpose: tauri-build wants `&'static` and a build script runs once. /// /// ```ignore /// tauri_build::AppManifest::new().commands(waybetter_host::commands::manifest(&["draft_list"])) /// ``` pub fn manifest(app: &[&'static str]) -> &'static [&'static str] { let mut all: Vec<&'static str> = Vec::with_capacity(app.len() + SHELL.len() + TAB.len() + SITE.len() + STORE.len() + MEDIA.len() + GFONTS.len()); for command in app.iter().chain(SHELL).chain(TAB).chain(SITE).chain(STORE).chain(MEDIA).chain(GFONTS) { if !all.contains(command) { all.push(command); } } Box::leak(all.into_boxed_slice()) } #[cfg(test)] mod tests { use super::*; #[test] fn manifest_adds_the_host_commands_once() { let all = manifest(&["draft_list", "tabs_sync"]); assert_eq!(all[0], "draft_list"); assert_eq!(all.iter().filter(|c| **c == "tabs_sync").count(), 1); for command in SHELL.iter().chain(TAB).chain(SITE).chain(STORE).chain(MEDIA).chain(GFONTS) { assert!(all.contains(command), "{command}"); assert!(is_host(command)); } assert!(!is_host("draft_list")); assert_eq!(permission("tab_link_drag"), "allow-tab-link-drag"); } #[test] fn shell_and_tab_commands_do_not_overlap() { assert!(!SHELL.iter().any(|command| TAB.contains(command) || SITE.contains(command) || STORE.contains(command) || MEDIA.contains(command) || GFONTS.contains(command))); assert!(!TAB.iter().any(|command| SITE.contains(command))); } }