Browse Source

refactor(core): read tray icon only on desktop, refactor Context (#6719)

Lucas Fernandes Nogueira 2 years ago
parent
commit
ae102980fc

+ 6 - 0
.changes/remove-tray-icon-mobile.md

@@ -0,0 +1,6 @@
+---
+"tauri-codegen": patch
+"tauri": patch
+---
+
+Refactor the `Context` conditional fields and only parse the tray icon on desktop.

+ 64 - 44
core/tauri-codegen/src/context.rs

@@ -125,6 +125,16 @@ enum Target {
   Ios,
 }
 
+impl Target {
+  fn is_mobile(&self) -> bool {
+    matches!(self, Target::Android | Target::Ios)
+  }
+
+  fn is_desktop(&self) -> bool {
+    !self.is_mobile()
+  }
+}
+
 /// Build a `tauri::Context` for including in application code.
 pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> {
   let ContextData {
@@ -247,7 +257,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
         "icons/icon.ico",
       );
       if icon_path.exists() {
-        ico_icon(&root, &out_dir, icon_path)?
+        ico_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
       } else {
         let icon_path = find_icon(
           &config,
@@ -255,7 +265,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
           |i| i.ends_with(".png"),
           "icons/icon.png",
         );
-        png_icon(&root, &out_dir, icon_path)?
+        png_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
       }
     } else if target == Target::Linux {
       // handle default window icons for Linux targets
@@ -265,9 +275,9 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
         |i| i.ends_with(".png"),
         "icons/icon.png",
       );
-      png_icon(&root, &out_dir, icon_path)?
+      png_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
     } else {
-      quote!(None)
+      quote!(::std::option::Option::None)
     }
   };
 
@@ -288,7 +298,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
     }
     raw_icon(&out_dir, icon_path)?
   } else {
-    quote!(None)
+    quote!(::std::option::Option::None)
   };
 
   let package_name = if let Some(product_name) = &config.package.product_name {
@@ -312,20 +322,26 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
     }
   );
 
-  let system_tray_icon = if let Some(tray) = &config.tauri.system_tray {
-    let system_tray_icon_path = config_parent.join(&tray.icon_path);
-    let ext = system_tray_icon_path.extension();
-    if ext.map_or(false, |e| e == "ico") {
-      ico_icon(&root, &out_dir, system_tray_icon_path)?
-    } else if ext.map_or(false, |e| e == "png") {
-      png_icon(&root, &out_dir, system_tray_icon_path)?
+  let with_system_tray_icon_code = if target.is_desktop() {
+    if let Some(tray) = &config.tauri.system_tray {
+      let system_tray_icon_path = config_parent.join(&tray.icon_path);
+      let ext = system_tray_icon_path.extension();
+      if ext.map_or(false, |e| e == "ico") {
+        ico_icon(&root, &out_dir, system_tray_icon_path)
+          .map(|i| quote!(context.set_system_tray_icon(#i);))?
+      } else if ext.map_or(false, |e| e == "png") {
+        png_icon(&root, &out_dir, system_tray_icon_path)
+          .map(|i| quote!(context.set_system_tray_icon(#i);))?
+      } else {
+        quote!(compile_error!(
+          "The tray icon extension must be either `.ico` or `.png`."
+        ))
+      }
     } else {
-      quote!(compile_error!(
-        "The tray icon extension must be either `.ico` or `.png`."
-      ))
+      quote!()
     }
   } else {
-    quote!(None)
+    quote!()
   };
 
   #[cfg(target_os = "macos")]
@@ -409,50 +425,52 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
   };
 
   #[cfg(feature = "shell-scope")]
-  let shell_scope_config = {
+  let with_shell_scope_code = {
     use regex::Regex;
     use tauri_utils::config::ShellAllowlistOpen;
 
     let shell_scopes = get_allowed_clis(&root, &config.tauri.allowlist.shell.scope);
 
-    let shell_scope_open = match &config.tauri.allowlist.shell.open {
-      ShellAllowlistOpen::Flag(false) => quote!(::std::option::Option::None),
-      ShellAllowlistOpen::Flag(true) => {
-        quote!(::std::option::Option::Some(#root::regex::Regex::new(r#"^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+"#).unwrap()))
-      }
+    let shell_scope_constructor = match &config.tauri.allowlist.shell.open {
+      ShellAllowlistOpen::Flag(false) => quote!(#root::ShellScopeConfig::new().skip_validation()),
+      ShellAllowlistOpen::Flag(true) => quote!(#root::ShellScopeConfig::new()),
       ShellAllowlistOpen::Validate(regex) => match Regex::new(regex) {
-        Ok(_) => quote!(::std::option::Option::Some(#root::regex::Regex::new(#regex).unwrap())),
+        Ok(_) => {
+          quote!(#root::ShellScopeConfig::with_validator(#root::regex::Regex::new(#regex).unwrap()))
+        }
         Err(error) => {
           let error = error.to_string();
           quote!({
             compile_error!(#error);
-            ::std::option::Option::Some(#root::regex::Regex::new(#regex).unwrap())
+            #root::ShellScopeConfig::with_validator(#root::regex::Regex::new(#regex).unwrap())
           })
         }
       },
       _ => panic!("unknown shell open format, unable to prepare"),
     };
+    let shell_scope = quote!(#shell_scope_constructor.set_allowed_commands(#shell_scopes));
 
-    quote!(#root::ShellScopeConfig {
-      open: #shell_scope_open,
-      scopes: #shell_scopes
-    })
+    quote!(context.set_shell_scope(#shell_scope);)
   };
 
   #[cfg(not(feature = "shell-scope"))]
-  let shell_scope_config = quote!();
-
-  Ok(quote!(#root::Context::new(
-    #config,
-    ::std::sync::Arc::new(#assets),
-    #default_window_icon,
-    #app_icon,
-    #system_tray_icon,
-    #package_info,
-    #info_plist,
-    #pattern,
-    #shell_scope_config
-  )))
+  let with_shell_scope_code = quote!();
+
+  Ok(quote!({
+    #[allow(unused_mut, clippy::let_and_return)]
+    let mut context = #root::Context::new(
+      #config,
+      ::std::sync::Arc::new(#assets),
+      #default_window_icon,
+      #app_icon,
+      #package_info,
+      #info_plist,
+      #pattern,
+    );
+    #with_system_tray_icon_code
+    #with_shell_scope_code
+    context
+  }))
 }
 
 fn ico_icon<P: AsRef<Path>>(
@@ -483,7 +501,7 @@ fn ico_icon<P: AsRef<Path>>(
 
   let out_path = out_path.display().to_string();
 
-  let icon = quote!(Some(#root::Icon::Rgba { rgba: include_bytes!(#out_path).to_vec(), width: #width, height: #height }));
+  let icon = quote!(#root::Icon::Rgba { rgba: include_bytes!(#out_path).to_vec(), width: #width, height: #height });
   Ok(icon)
 }
 
@@ -501,7 +519,9 @@ fn raw_icon<P: AsRef<Path>>(out_dir: &Path, path: P) -> Result<TokenStream, Embe
 
   let out_path = out_path.display().to_string();
 
-  let icon = quote!(Some(include_bytes!(#out_path).to_vec()));
+  let icon = quote!(::std::option::Option::Some(
+    include_bytes!(#out_path).to_vec()
+  ));
   Ok(icon)
 }
 
@@ -533,7 +553,7 @@ fn png_icon<P: AsRef<Path>>(
 
   let out_path = out_path.display().to_string();
 
-  let icon = quote!(Some(#root::Icon::Rgba { rgba: include_bytes!(#out_path).to_vec(), width: #width, height: #height }));
+  let icon = quote!(#root::Icon::Rgba { rgba: include_bytes!(#out_path).to_vec(), width: #width, height: #height });
   Ok(icon)
 }
 

+ 24 - 5
core/tauri/src/lib.rs

@@ -539,6 +539,7 @@ pub struct Context<A: Assets> {
   pub(crate) assets: Arc<A>,
   pub(crate) default_window_icon: Option<Icon>,
   pub(crate) app_icon: Option<Vec<u8>>,
+  #[cfg(desktop)]
   pub(crate) system_tray_icon: Option<Icon>,
   pub(crate) package_info: PackageInfo,
   pub(crate) _info_plist: (),
@@ -553,9 +554,12 @@ impl<A: Assets> fmt::Debug for Context<A> {
     d.field("config", &self.config)
       .field("default_window_icon", &self.default_window_icon)
       .field("app_icon", &self.app_icon)
-      .field("system_tray_icon", &self.system_tray_icon)
       .field("package_info", &self.package_info)
       .field("pattern", &self.pattern);
+
+    #[cfg(desktop)]
+    d.field("system_tray_icon", &self.system_tray_icon);
+
     #[cfg(shell_scope)]
     d.field("shell_scope", &self.shell_scope);
     d.finish()
@@ -600,12 +604,14 @@ impl<A: Assets> Context<A> {
   }
 
   /// The icon to use on the system tray UI.
+  #[cfg(desktop)]
   #[inline(always)]
   pub fn system_tray_icon(&self) -> Option<&Icon> {
     self.system_tray_icon.as_ref()
   }
 
   /// A mutable reference to the icon to use on the system tray UI.
+  #[cfg(desktop)]
   #[inline(always)]
   pub fn system_tray_icon_mut(&mut self) -> &mut Option<Icon> {
     &mut self.system_tray_icon
@@ -644,25 +650,38 @@ impl<A: Assets> Context<A> {
     assets: Arc<A>,
     default_window_icon: Option<Icon>,
     app_icon: Option<Vec<u8>>,
-    system_tray_icon: Option<Icon>,
     package_info: PackageInfo,
     info_plist: (),
     pattern: Pattern,
-    #[cfg(shell_scope)] shell_scope: scope::ShellScopeConfig,
   ) -> Self {
     Self {
       config,
       assets,
       default_window_icon,
       app_icon,
-      system_tray_icon,
+      #[cfg(desktop)]
+      system_tray_icon: None,
       package_info,
       _info_plist: info_plist,
       pattern,
       #[cfg(shell_scope)]
-      shell_scope,
+      shell_scope: Default::default(),
     }
   }
+
+  /// Sets the app tray icon.
+  #[cfg(desktop)]
+  #[inline(always)]
+  pub fn set_system_tray_icon(&mut self, icon: Icon) {
+    self.system_tray_icon.replace(icon);
+  }
+
+  /// Sets the app shell scope.
+  #[cfg(shell_scope)]
+  #[inline(always)]
+  pub fn set_shell_scope(&mut self, scope: scope::ShellScopeConfig) {
+    self.shell_scope = scope;
+  }
 }
 
 // TODO: expand these docs

+ 11 - 5
core/tauri/src/manager.rs

@@ -219,6 +219,7 @@ pub struct InnerWindowManager<R: Runtime> {
   assets: Arc<dyn Assets>,
   pub(crate) default_window_icon: Option<Icon>,
   pub(crate) app_icon: Option<Vec<u8>>,
+  #[cfg(desktop)]
   pub(crate) tray_icon: Option<Icon>,
 
   package_info: PackageInfo,
@@ -240,17 +241,21 @@ pub struct InnerWindowManager<R: Runtime> {
 
 impl<R: Runtime> fmt::Debug for InnerWindowManager<R> {
   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    f.debug_struct("InnerWindowManager")
-      .field("plugins", &self.plugins)
+    let mut d = f.debug_struct("InnerWindowManager");
+
+    d.field("plugins", &self.plugins)
       .field("state", &self.state)
       .field("config", &self.config)
       .field("default_window_icon", &self.default_window_icon)
       .field("app_icon", &self.app_icon)
-      .field("tray_icon", &self.tray_icon)
       .field("package_info", &self.package_info)
       .field("menu", &self.menu)
-      .field("pattern", &self.pattern)
-      .finish()
+      .field("pattern", &self.pattern);
+
+    #[cfg(desktop)]
+    d.field("tray_icon", &self.tray_icon);
+
+    d.finish()
   }
 }
 
@@ -322,6 +327,7 @@ impl<R: Runtime> WindowManager<R> {
         assets: context.assets,
         default_window_icon: context.default_window_icon,
         app_icon: context.app_icon,
+        #[cfg(desktop)]
         tray_icon: context.system_tray_icon,
         package_info: context.package_info,
         pattern: context.pattern,

+ 41 - 0
core/tauri/src/scope/shell.rs

@@ -12,6 +12,8 @@ use regex::Regex;
 
 use std::collections::HashMap;
 
+const DEFAULT_OPEN_REGEX: &str = r#"^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+"#;
+
 /// Allowed representation of `Execute` command arguments.
 #[derive(Debug, Clone, serde::Deserialize)]
 #[serde(untagged, deny_unknown_fields)]
@@ -67,6 +69,45 @@ pub struct ScopeConfig {
   pub scopes: HashMap<String, ScopeAllowedCommand>,
 }
 
+impl Default for ScopeConfig {
+  fn default() -> Self {
+    Self {
+      open: Some(Regex::new(DEFAULT_OPEN_REGEX).unwrap()),
+      scopes: Default::default(),
+    }
+  }
+}
+
+impl ScopeConfig {
+  /// Creates a new scope configuration with the default validation regex ^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+.
+  pub fn new() -> Self {
+    Self::default()
+  }
+
+  /// Creates a new scope configuration with the specified validation regex.
+  pub fn with_validator(regex: Regex) -> Self {
+    Self {
+      open: Some(regex),
+      scopes: Default::default(),
+    }
+  }
+
+  /// Unsets the validator regex, allowing any path to be opened.
+  pub fn skip_validation(mut self) -> Self {
+    self.open = None;
+    self
+  }
+
+  /// Sets the commands that are allowed to be executed.
+  pub fn set_allowed_commands<I: IntoIterator<Item = (String, ScopeAllowedCommand)>>(
+    mut self,
+    scopes: I,
+  ) -> Self {
+    self.scopes = scopes.into_iter().collect();
+    self
+  }
+}
+
 /// A configured scoped shell command.
 #[derive(Debug, Clone)]
 pub struct ScopeAllowedCommand {

+ 1 - 0
core/tauri/src/test/mod.rs

@@ -60,6 +60,7 @@ pub fn mock_context<A: Assets>(assets: A) -> crate::Context<A> {
     assets: Arc::new(assets),
     default_window_icon: None,
     app_icon: None,
+    #[cfg(desktop)]
     system_tray_icon: None,
     package_info: crate::PackageInfo {
       name: "test".into(),