app.rs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use crate::{
  5. api::{assets::Assets, config::WindowUrl},
  6. hooks::{InvokeHandler, InvokeMessage, OnPageLoad, PageLoadPayload, SetupHook},
  7. plugin::{Plugin, PluginStore},
  8. runtime::{
  9. flavors::wry::Wry,
  10. manager::{Args, WindowManager},
  11. tag::Tag,
  12. webview::{Attributes, CustomProtocol},
  13. window::PendingWindow,
  14. Dispatch, Runtime,
  15. },
  16. sealed::{ManagerBase, RuntimeOrDispatch},
  17. Context, Manager, Params, Window,
  18. };
  19. use std::{collections::HashMap, sync::Arc};
  20. #[cfg(feature = "updater")]
  21. use crate::updater;
  22. /// A handle to the currently running application.
  23. ///
  24. /// This type implements [`Manager`] which allows for manipulation of global application items.
  25. pub struct App<P: Params> {
  26. runtime: P::Runtime,
  27. manager: WindowManager<P>,
  28. }
  29. impl<P: Params> Manager<P> for App<P> {}
  30. impl<P: Params> ManagerBase<P> for App<P> {
  31. fn manager(&self) -> &WindowManager<P> {
  32. &self.manager
  33. }
  34. fn runtime(&mut self) -> RuntimeOrDispatch<'_, P> {
  35. RuntimeOrDispatch::Runtime(&mut self.runtime)
  36. }
  37. }
  38. #[cfg(feature = "updater")]
  39. impl<M: Params> App<M> {
  40. /// Runs the updater hook with built-in dialog.
  41. fn run_updater_dialog(&self, window: Window<M>) {
  42. let updater_config = self.manager.config().tauri.updater.clone();
  43. let package_info = self.manager.package_info().clone();
  44. crate::async_runtime::spawn(async move {
  45. updater::check_update_with_dialog(updater_config, package_info, window).await
  46. });
  47. }
  48. /// Listen updater events when dialog are disabled.
  49. fn listen_updater_events(&self, window: Window<M>) {
  50. let updater_config = self.manager.config().tauri.updater.clone();
  51. updater::listener(updater_config, self.manager.package_info().clone(), &window);
  52. }
  53. fn run_updater(&self, main_window: Option<Window<M>>) {
  54. if let Some(main_window) = main_window {
  55. let event_window = main_window.clone();
  56. let updater_config = self.manager.config().tauri.updater.clone();
  57. // check if updater is active or not
  58. if updater_config.dialog && updater_config.active {
  59. // if updater dialog is enabled spawn a new task
  60. self.run_updater_dialog(main_window.clone());
  61. let config = self.manager.config().tauri.updater.clone();
  62. let package_info = self.manager.package_info().clone();
  63. // When dialog is enabled, if user want to recheck
  64. // if an update is available after first start
  65. // invoke the Event `tauri://update` from JS or rust side.
  66. main_window.listen(
  67. updater::EVENT_CHECK_UPDATE
  68. .parse()
  69. .unwrap_or_else(|_| panic!("bad label")),
  70. move |_msg| {
  71. let window = event_window.clone();
  72. let package_info = package_info.clone();
  73. let config = config.clone();
  74. // re-spawn task inside tokyo to launch the download
  75. // we don't need to emit anything as everything is handled
  76. // by the process (user is asked to restart at the end)
  77. // and it's handled by the updater
  78. crate::async_runtime::spawn(async move {
  79. updater::check_update_with_dialog(config, package_info, window).await
  80. });
  81. },
  82. );
  83. } else if updater_config.active {
  84. // we only listen for `tauri://update`
  85. // once we receive the call, we check if an update is available or not
  86. // if there is a new update we emit `tauri://update-available` with details
  87. // this is the user responsabilities to display dialog and ask if user want to install
  88. // to install the update you need to invoke the Event `tauri://update-install`
  89. self.listen_updater_events(main_window);
  90. }
  91. }
  92. }
  93. }
  94. /// Builds a Tauri application.
  95. pub struct Builder<E, L, A, R>
  96. where
  97. E: Tag,
  98. L: Tag,
  99. A: Assets,
  100. R: Runtime,
  101. {
  102. /// The JS message handler.
  103. invoke_handler: Box<InvokeHandler<Args<E, L, A, R>>>,
  104. /// The setup hook.
  105. setup: SetupHook<Args<E, L, A, R>>,
  106. /// Page load hook.
  107. on_page_load: Box<OnPageLoad<Args<E, L, A, R>>>,
  108. /// windows to create when starting up.
  109. pending_windows: Vec<PendingWindow<Args<E, L, A, R>>>,
  110. /// All passed plugins
  111. plugins: PluginStore<Args<E, L, A, R>>,
  112. /// The webview protocols available to all windows.
  113. uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
  114. }
  115. impl<E, L, A, R> Builder<E, L, A, R>
  116. where
  117. E: Tag,
  118. L: Tag,
  119. A: Assets,
  120. R: Runtime,
  121. {
  122. /// Creates a new App builder.
  123. pub fn new() -> Self {
  124. Self {
  125. setup: Box::new(|_| Ok(())),
  126. invoke_handler: Box::new(|_| ()),
  127. on_page_load: Box::new(|_, _| ()),
  128. pending_windows: Default::default(),
  129. plugins: PluginStore::default(),
  130. uri_scheme_protocols: Default::default(),
  131. }
  132. }
  133. /// Defines the JS message handler callback.
  134. pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
  135. where
  136. F: Fn(InvokeMessage<Args<E, L, A, R>>) + Send + Sync + 'static,
  137. {
  138. self.invoke_handler = Box::new(invoke_handler);
  139. self
  140. }
  141. /// Defines the setup hook.
  142. pub fn setup<F>(mut self, setup: F) -> Self
  143. where
  144. F: Fn(&mut App<Args<E, L, A, R>>) -> Result<(), Box<dyn std::error::Error>> + Send + 'static,
  145. {
  146. self.setup = Box::new(setup);
  147. self
  148. }
  149. /// Defines the page load hook.
  150. pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
  151. where
  152. F: Fn(Window<Args<E, L, A, R>>, PageLoadPayload) + Send + Sync + 'static,
  153. {
  154. self.on_page_load = Box::new(on_page_load);
  155. self
  156. }
  157. /// Adds a plugin to the runtime.
  158. pub fn plugin<P: Plugin<Args<E, L, A, R>> + 'static>(mut self, plugin: P) -> Self {
  159. self.plugins.register(plugin);
  160. self
  161. }
  162. /// Creates a new webview.
  163. pub fn create_window<F>(mut self, label: L, url: WindowUrl, setup: F) -> Self
  164. where
  165. F: FnOnce(<R::Dispatcher as Dispatch>::Attributes) -> <R::Dispatcher as Dispatch>::Attributes,
  166. {
  167. let attributes = setup(<R::Dispatcher as Dispatch>::Attributes::new());
  168. self
  169. .pending_windows
  170. .push(PendingWindow::new(attributes, label, url));
  171. self
  172. }
  173. /// Registers a URI scheme protocol available to all webviews.
  174. /// Leverages [setURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler) on macOS,
  175. /// [AddWebResourceRequestedFilter](https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addwebresourcerequestedfilter?view=webview2-dotnet-1.0.774.44) on Windows
  176. /// and [webkit-web-context-register-uri-scheme](https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebContext.html#webkit-web-context-register-uri-scheme) on Linux.
  177. ///
  178. /// # Arguments
  179. ///
  180. /// * `uri_scheme` The URI scheme to register, such as `example`.
  181. /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`.
  182. pub fn register_global_uri_scheme_protocol<
  183. N: Into<String>,
  184. H: Fn(&str) -> crate::Result<Vec<u8>> + Send + Sync + 'static,
  185. >(
  186. mut self,
  187. uri_scheme: N,
  188. protocol: H,
  189. ) -> Self {
  190. self.uri_scheme_protocols.insert(
  191. uri_scheme.into(),
  192. Arc::new(CustomProtocol {
  193. protocol: Box::new(protocol),
  194. }),
  195. );
  196. self
  197. }
  198. /// Runs the configured Tauri application.
  199. pub fn run(mut self, context: Context<A>) -> crate::Result<()> {
  200. let manager = WindowManager::with_handlers(
  201. context,
  202. self.plugins,
  203. self.invoke_handler,
  204. self.on_page_load,
  205. self.uri_scheme_protocols,
  206. );
  207. // set up all the windows defined in the config
  208. for config in manager.config().tauri.windows.clone() {
  209. let url = config.url.clone();
  210. let label = config
  211. .label
  212. .parse()
  213. .unwrap_or_else(|_| panic!("bad label found in config: {}", config.label));
  214. self
  215. .pending_windows
  216. .push(PendingWindow::with_config(config, label, url));
  217. }
  218. manager.initialize_plugins()?;
  219. let mut app = App {
  220. runtime: R::new()?,
  221. manager,
  222. };
  223. let pending_labels = self
  224. .pending_windows
  225. .iter()
  226. .map(|p| p.label.clone())
  227. .collect::<Vec<_>>();
  228. #[cfg(feature = "updater")]
  229. let mut main_window = None;
  230. for pending in self.pending_windows {
  231. let pending = app.manager.prepare_window(pending, &pending_labels)?;
  232. let detached = app.runtime.create_window(pending)?;
  233. let _window = app.manager.attach_window(detached);
  234. #[cfg(feature = "updater")]
  235. if main_window.is_none() {
  236. main_window = Some(_window);
  237. }
  238. }
  239. #[cfg(feature = "updater")]
  240. app.run_updater(main_window);
  241. (self.setup)(&mut app).map_err(|e| crate::Error::Setup(e.to_string()))?;
  242. app.runtime.run();
  243. Ok(())
  244. }
  245. }
  246. /// Make `Wry` the default `Runtime` for `Builder`
  247. impl<A: Assets> Default for Builder<String, String, A, Wry> {
  248. fn default() -> Self {
  249. Self::new()
  250. }
  251. }