Browse Source

feat(bundler): extend webview2 installation options, closes #2882 #2452 (#4466)

Co-authored-by: Fabian-Lars <fabianlars@fabianlars.de>
Lucas Fernandes Nogueira 3 năm trước cách đây
mục cha
commit
2ca762d207

+ 6 - 0
.changes/webview-install-mode.md

@@ -0,0 +1,6 @@
+---
+"tauri-utils": patch
+"tauri-bundler": patch
+---
+
+Added webview install mode options.

+ 1 - 1
core/tauri-runtime-wry/src/lib.rs

@@ -482,7 +482,7 @@ impl From<NativeImage> for NativeImageWrapper {
   }
 }
 
-/// Wrapper around a [`wry::application::window::Icon`] that can be created from an [`WindowIcon`].
+/// Wrapper around a [`wry::application::window::Icon`] that can be created from an [`Icon`].
 pub struct WryIcon(WryWindowIcon);
 
 fn icon_err<E: std::error::Error + Send + Sync + 'static>(e: E) -> Error {

+ 1 - 1
core/tauri-runtime/src/lib.rs

@@ -54,7 +54,7 @@ impl SystemTray {
     self.menu.as_ref()
   }
 
-  /// Sets the tray icon. Must be a [`TrayIcon::File`] on Linux and a [`TrayIcon::Raw`] on Windows and macOS.
+  /// Sets the tray icon.
   #[must_use]
   pub fn with_icon(mut self, icon: Icon) -> Self {
     self.icon.replace(icon);

+ 96 - 9
core/tauri-utils/src/config.rs

@@ -384,6 +384,8 @@ pub struct WixConfig {
   #[serde(default)]
   pub merge_refs: Vec<String>,
   /// Disables the Webview2 runtime installation after app install.
+  ///
+  /// Will be removed in v2, prefer the [`WindowsConfig::webview_install_mode`] option.
   #[serde(default)]
   pub skip_webview_install: bool,
   /// The path to the license file to render on the installer.
@@ -405,6 +407,61 @@ pub struct WixConfig {
   pub dialog_image_path: Option<PathBuf>,
 }
 
+/// Install modes for the Webview2 runtime.
+/// Note that for the updater bundle [`Self::DownloadBootstrapper`] is used.
+#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
+#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+pub enum WebviewInstallMode {
+  /// Do not install the Webview2 as part of the Windows Installer.
+  Skip,
+  /// Download the bootstrapper and run it.
+  /// Requires internet connection.
+  /// Results in a smaller installer size, but is not recommended on Windows 7.
+  DownloadBootstrapper {
+    /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
+    #[serde(default = "default_webview_install_silent")]
+    silent: bool,
+  },
+  /// Embed the bootstrapper and run it.
+  /// Requires internet connection.
+  /// Increases the installer size by around 1.8MB, but offers better support on Windows 7.
+  EmbedBootstrapper {
+    /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.
+    #[serde(default = "default_webview_install_silent")]
+    silent: bool,
+  },
+  /// Embed the offline installer and run it.
+  /// Does not require internet connection.
+  /// Increases the installer size by around 127MB.
+  OfflineInstaller {
+    /// Instructs the installer to run the installer in silent mode. Defaults to `true`.
+    #[serde(default = "default_webview_install_silent")]
+    silent: bool,
+  },
+  /// Embed a fixed webview2 version and use it at runtime.
+  /// Increases the installer size by around 180MB.
+  FixedRuntime {
+    /// The path to the fixed runtime to use.
+    ///
+    /// The fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
+    /// The `.cab` file must be extracted to a folder and this folder path must be defined on this field.
+    path: PathBuf,
+  },
+}
+
+fn default_webview_install_silent() -> bool {
+  true
+}
+
+impl Default for WebviewInstallMode {
+  fn default() -> Self {
+    Self::DownloadBootstrapper {
+      silent: default_webview_install_silent(),
+    }
+  }
+}
+
 /// Windows bundler configuration.
 #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
 #[cfg_attr(feature = "schema", derive(JsonSchema))]
@@ -421,7 +478,12 @@ pub struct WindowsConfig {
   /// use a TSP timestamp server, like e.g. SSL.com does. If so, enable TSP by setting to true.
   #[serde(default)]
   pub tsp: bool,
-  /// Path to the webview fixed runtime to use.
+  /// The installation mode for the Webview2 runtime.
+  #[serde(default)]
+  pub webview_install_mode: WebviewInstallMode,
+  /// Path to the webview fixed runtime to use. Overwrites [`Self::webview_install_mode`] if set.
+  ///
+  /// Will be removed in v2, prefer the [`Self::webview_install_mode`] option.
   ///
   /// The fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
   /// The `.cab` file must be extracted to a folder and this folder path must be defined on this field.
@@ -444,6 +506,7 @@ impl Default for WindowsConfig {
       certificate_thumbprint: None,
       timestamp_url: None,
       tsp: false,
+      webview_install_mode: Default::default(),
       webview_fixed_runtime_path: None,
       allow_downgrades: default_allow_downgrades(),
       wix: None,
@@ -2873,17 +2936,41 @@ mod build {
     }
   }
 
+  impl ToTokens for WebviewInstallMode {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+      let prefix = quote! { ::tauri::utils::config::WebviewInstallMode };
+
+      tokens.append_all(match self {
+        Self::Skip => quote! { #prefix::Skip },
+        Self::DownloadBootstrapper { silent } => {
+          quote! { #prefix::DownloadBootstrapper { silent: #silent } }
+        }
+        Self::EmbedBootstrapper { silent } => {
+          quote! { #prefix::EmbedBootstrapper { silent: #silent } }
+        }
+        Self::OfflineInstaller { silent } => {
+          quote! { #prefix::OfflineInstaller { silent: #silent } }
+        }
+        Self::FixedRuntime { path } => {
+          let path = path_buf_lit(&path);
+          quote! { #prefix::FixedRuntime { path: #path } }
+        }
+      })
+    }
+  }
+
   impl ToTokens for WindowsConfig {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-      let webview_fixed_runtime_path = opt_lit(
-        self
-          .webview_fixed_runtime_path
-          .as_ref()
-          .map(path_buf_lit)
-          .as_ref(),
-      );
+      let webview_install_mode = if let Some(fixed_runtime_path) = &self.webview_fixed_runtime_path
+      {
+        WebviewInstallMode::FixedRuntime {
+          path: fixed_runtime_path.clone(),
+        }
+      } else {
+        self.webview_install_mode.clone()
+      };
       tokens.append_all(quote! { ::tauri::utils::config::WindowsConfig {
-        webview_fixed_runtime_path: #webview_fixed_runtime_path,
+        webview_install_mode: #webview_install_mode,
         ..Default::default()
       }})
     }

+ 6 - 3
core/tauri/src/app.rs

@@ -1441,16 +1441,19 @@ impl<R: Runtime> Builder<R> {
 
     #[cfg(windows)]
     {
-      if let Some(w) = &app
+      if let crate::utils::config::WebviewInstallMode::FixedRuntime { path } = &app
         .manager
         .config()
         .tauri
         .bundle
         .windows
-        .webview_fixed_runtime_path
+        .webview_install_mode
       {
         if let Some(resource_dir) = app.path_resolver().resource_dir() {
-          std::env::set_var("WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", resource_dir.join(w));
+          std::env::set_var(
+            "WEBVIEW2_BROWSER_EXECUTABLE_FOLDER",
+            resource_dir.join(path),
+          );
         } else {
           #[cfg(debug_assertions)]
           eprintln!(

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
examples/api/dist/assets/index.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
examples/api/dist/assets/vendor.js


+ 6 - 6
examples/api/yarn.lock

@@ -23,9 +23,9 @@
     svelte-hmr "^0.14.7"
 
 "@tauri-apps/api@../../tooling/api/dist":
-  version "1.0.0"
+  version "1.0.0-rc.6"
   dependencies:
-    type-fest "2.13.1"
+    type-fest "2.13.0"
 
 "@zerodevx/svelte-json-view@0.2.0":
   version "0.2.0"
@@ -267,10 +267,10 @@ svelte@3.35.0:
   resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.35.0.tgz#e0d0ba60c4852181c2b4fd851194be6fda493e65"
   integrity sha512-gknlZkR2sXheu/X+B7dDImwANVvK1R0QGQLd8CNIfxxGPeXBmePnxfzb6fWwTQRsYQG7lYkZXvpXJvxvpsoB7g==
 
-type-fest@2.13.1:
-  version "2.13.1"
-  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.13.1.tgz#621c84220df0e01a8469002594fc005714f0cfba"
-  integrity sha512-hXYyrPFwETT2swFLHeoKtJrvSF/ftG/sA15/8nGaLuaDGfVAaq8DYFpu4yOyV4tzp082WqnTEoMsm3flKMI2FQ==
+type-fest@2.13.0:
+  version "2.13.0"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.13.0.tgz#d1ecee38af29eb2e863b22299a3d68ef30d2abfb"
+  integrity sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==
 
 vite@^2.6.4:
   version "2.6.14"

+ 1 - 1
tooling/bundler/src/bundle.rs

@@ -49,7 +49,7 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
       #[cfg(target_os = "macos")]
       PackageType::IosBundle => macos::ios::bundle_project(&settings)?,
       #[cfg(target_os = "windows")]
-      PackageType::WindowsMsi => windows::msi::bundle_project(&settings)?,
+      PackageType::WindowsMsi => windows::msi::bundle_project(&settings, false)?,
       #[cfg(target_os = "linux")]
       PackageType::Deb => linux::debian::bundle_project(&settings)?,
       #[cfg(target_os = "linux")]

+ 13 - 5
tooling/bundler/src/bundle/settings.rs

@@ -4,6 +4,7 @@
 
 use super::category::AppCategory;
 use crate::bundle::{common, platform::target_triple};
+pub use tauri_utils::config::WebviewInstallMode;
 use tauri_utils::{
   config::BundleType,
   resources::{external_binaries, ResourcePaths},
@@ -186,7 +187,7 @@ pub struct MacOsSettings {
 /// Configuration for a target language for the WiX build.
 #[derive(Debug, Clone, Default)]
 pub struct WixLanguageConfig {
-  /// The path to a locale (`.wxl`) file. See https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html.
+  /// The path to a locale (`.wxl`) file. See <https://wixtoolset.org/documentation/manual/v3/howtos/ui_and_localization/build_a_localized_version.html>.
   pub locale_path: Option<PathBuf>,
 }
 
@@ -203,7 +204,7 @@ impl Default for WixLanguage {
 /// Settings specific to the WiX implementation.
 #[derive(Clone, Debug, Default)]
 pub struct WixSettings {
-  /// The app languages to build. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.
+  /// The app languages to build. See <https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables>.
   pub language: WixLanguage,
   /// By default, the bundler uses an internal template.
   /// This option allows you to define your own wix file.
@@ -220,7 +221,7 @@ pub struct WixSettings {
   pub feature_refs: Vec<String>,
   /// The Merge element ids you want to reference from the fragments.
   pub merge_refs: Vec<String>,
-  /// Disables the Webview2 runtime installation after app install.
+  /// Disables the Webview2 runtime installation after app install. Will be removed in v2, use [`WindowsSettings::webview_install_mode`] instead.
   pub skip_webview_install: bool,
   /// The path to the LICENSE file.
   pub license: Option<PathBuf>,
@@ -254,7 +255,13 @@ pub struct WindowsSettings {
   pub wix: Option<WixSettings>,
   /// The path to the application icon. Defaults to `./icons/icon.ico`.
   pub icon_path: PathBuf,
+  /// The installation mode for the Webview2 runtime.
+  pub webview_install_mode: WebviewInstallMode,
   /// Path to the webview fixed runtime to use.
+  ///
+  /// Overwrites [`Self::webview_install_mode`] if set.
+  ///
+  /// Will be removed in v2, use [`Self::webview_install_mode`] instead.
   pub webview_fixed_runtime_path: Option<PathBuf>,
   /// Validates a second app installation, blocking the user from installing an older version if set to `false`.
   ///
@@ -273,6 +280,7 @@ impl Default for WindowsSettings {
       tsp: false,
       wix: None,
       icon_path: PathBuf::from("icons/icon.ico"),
+      webview_install_mode: Default::default(),
       webview_fixed_runtime_path: None,
       allow_downgrades: true,
     }
@@ -301,7 +309,7 @@ pub struct BundleSettings {
   /// the app's long description.
   pub long_description: Option<String>,
   // Bundles for other binaries:
-  /// Configuration map for the possible [bin] apps to bundle.
+  /// Configuration map for the apps to bundle.
   pub bin: Option<HashMap<String, BundleSettings>>,
   /// External binaries to add to the bundle.
   ///
@@ -316,7 +324,7 @@ pub struct BundleSettings {
   /// If you are building a universal binary for MacOS, the bundler expects
   /// your external binary to also be universal, and named after the target triple,
   /// e.g. `sqlite3-universal-apple-darwin`. See
-  /// https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary
+  /// <https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary>
   pub external_bin: Option<Vec<String>>,
   /// Debian-specific settings.
   pub deb: DebianSettings,

+ 36 - 14
tooling/bundler/src/bundle/updater_bundle.rs

@@ -125,26 +125,48 @@ fn bundle_update(settings: &Settings, bundles: &[Bundle]) -> crate::Result<Vec<P
 // No assets are replaced
 #[cfg(target_os = "windows")]
 fn bundle_update(settings: &Settings, bundles: &[Bundle]) -> crate::Result<Vec<PathBuf>> {
-  // find our .msi or rebuild
-  let mut bundle_paths = bundles
-    .iter()
-    .find(|bundle| bundle.package_type == crate::PackageType::WindowsMsi)
-    .map(|bundle| bundle.bundle_paths.clone())
-    .unwrap_or_default();
+  use crate::bundle::settings::WebviewInstallMode;
 
-  // we expect our .msi files to be on `bundle_paths`
-  if bundle_paths.is_empty() {
-    bundle_paths.extend(msi::bundle_project(settings)?);
-  }
+  // find our .msi or rebuild
+  let bundle_paths = if matches!(
+    settings.windows().webview_install_mode,
+    WebviewInstallMode::OfflineInstaller { .. } | WebviewInstallMode::EmbedBootstrapper { .. }
+  ) {
+    msi::bundle_project(settings, true)?
+  } else {
+    let paths = bundles
+      .iter()
+      .find(|bundle| bundle.package_type == crate::PackageType::WindowsMsi)
+      .map(|bundle| bundle.bundle_paths.clone())
+      .unwrap_or_default();
+
+    // we expect our .msi files to be on `bundle_paths`
+    if paths.is_empty() {
+      msi::bundle_project(settings, false)?
+    } else {
+      paths
+    }
+  };
 
   let mut msi_archived_paths = Vec::new();
 
   for source_path in bundle_paths {
     // add .zip to our path
-    let msi_archived = format!("{}.zip", source_path.display());
-    let msi_archived_path = PathBuf::from(&msi_archived);
-
-    info!(action = "Bundling"; "{} ({})", msi_archived, msi_archived_path.display());
+    let msi_archived_path = source_path
+      .components()
+      .fold(PathBuf::new(), |mut p, c| {
+        if let std::path::Component::Normal(name) = c {
+          if name == msi::MSI_UPDATER_FOLDER_NAME {
+            p.push(msi::MSI_FOLDER_NAME);
+            return p;
+          }
+        }
+        p.push(c);
+        p
+      })
+      .with_extension("msi.zip");
+
+    info!(action = "Bundling"; "{}", msi_archived_path.display());
 
     // Create our gzip file
     create_zip(&source_path, &msi_archived_path).with_context(|| "Failed to zip update MSI")?;

+ 4 - 2
tooling/bundler/src/bundle/windows/msi.rs

@@ -4,13 +4,15 @@
 
 mod wix;
 
+pub use wix::{MSI_FOLDER_NAME, MSI_UPDATER_FOLDER_NAME};
+
 use crate::Settings;
 
 use std::{self, path::PathBuf};
 
 /// Runs all of the commands to build the MSI installer.
 /// Returns a vector of PathBuf that shows where the MSI was created.
-pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
+pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<PathBuf>> {
   let mut wix_path = dirs_next::cache_dir().unwrap();
   wix_path.push("tauri/WixTools");
 
@@ -18,5 +20,5 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
     wix::get_and_extract_wix(&wix_path)?;
   }
 
-  wix::build_wix_app_installer(settings, &wix_path)
+  wix::build_wix_app_installer(settings, &wix_path, updater)
 }

+ 126 - 32
tooling/bundler/src/bundle/windows/msi/wix.rs

@@ -21,7 +21,7 @@ use std::{
   path::{Path, PathBuf},
   process::Command,
 };
-use tauri_utils::resources::resource_relpath;
+use tauri_utils::{config::WebviewInstallMode, resources::resource_relpath};
 use uuid::Uuid;
 use zip::ZipArchive;
 
@@ -29,6 +29,11 @@ use zip::ZipArchive;
 pub const WIX_URL: &str =
   "https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip";
 pub const WIX_SHA256: &str = "2c1888d5d1dba377fc7fa14444cf556963747ff9a0a289a3599cf09da03b9e2e";
+pub const MSI_FOLDER_NAME: &str = "msi";
+pub const MSI_UPDATER_FOLDER_NAME: &str = "msi-updater";
+const WEBVIEW2_BOOTSTRAPPER_URL: &str = "https://go.microsoft.com/fwlink/p/?LinkId=2124703";
+const WEBVIEW2_X86_INSTALLER_GUID: &str = "a17bde80-b5ab-47b5-8bbb-1cbe93fc6ec9";
+const WEBVIEW2_X64_INSTALLER_GUID: &str = "aa5fd9b3-dc11-4cbc-8343-a50f57b311e1";
 
 // For Cross Platform Compilation.
 
@@ -151,7 +156,7 @@ fn copy_icon(settings: &Settings, filename: &str, path: &Path) -> crate::Result<
   let base_dir = settings.project_out_directory();
 
   let resource_dir = base_dir.join("resources");
-  std::fs::create_dir_all(&resource_dir)?;
+  create_dir_all(&resource_dir)?;
   let icon_target_path = resource_dir.join(filename);
 
   let icon_path = std::env::current_dir()?.join(&path);
@@ -168,14 +173,15 @@ fn copy_icon(settings: &Settings, filename: &str, path: &Path) -> crate::Result<
   Ok(icon_target_path)
 }
 
-/// Function used to download Wix and VC_REDIST. Checks SHA256 to verify the download.
-fn download_and_verify(url: &str, hash: &str) -> crate::Result<Vec<u8>> {
+fn download(url: &str) -> crate::Result<Vec<u8>> {
   info!(action = "Downloading"; "{}", url);
-
   let response = attohttpc::get(url).send()?;
+  response.bytes().map_err(Into::into)
+}
 
-  let data: Vec<u8> = response.bytes()?;
-
+/// Function used to download Wix. Checks SHA256 to verify the download.
+fn download_and_verify(url: &str, hash: &str) -> crate::Result<Vec<u8>> {
+  let data = download(url)?;
   info!("validating hash");
 
   let mut hasher = sha2::Sha256::new();
@@ -192,7 +198,11 @@ fn download_and_verify(url: &str, hash: &str) -> crate::Result<Vec<u8>> {
 }
 
 /// The app installer output path.
-fn app_installer_output_path(settings: &Settings, language: &str) -> crate::Result<PathBuf> {
+fn app_installer_output_path(
+  settings: &Settings,
+  language: &str,
+  updater: bool,
+) -> crate::Result<PathBuf> {
   let arch = match settings.binary_arch() {
     "x86" => "x86",
     "x86_64" => "x64",
@@ -212,12 +222,15 @@ fn app_installer_output_path(settings: &Settings, language: &str) -> crate::Resu
     language,
   );
 
-  Ok(
-    settings
-      .project_out_directory()
-      .to_path_buf()
-      .join(format!("bundle/msi/{}.msi", package_base_name)),
-  )
+  Ok(settings.project_out_directory().to_path_buf().join(format!(
+    "bundle/{}/{}.msi",
+    if updater {
+      MSI_UPDATER_FOLDER_NAME
+    } else {
+      MSI_FOLDER_NAME
+    },
+    package_base_name
+  )))
 }
 
 /// Extracts the zips from Wix and VC_REDIST into a useable path.
@@ -370,6 +383,7 @@ fn validate_version(version: &str) -> anyhow::Result<()> {
 pub fn build_wix_app_installer(
   settings: &Settings,
   wix_toolset_path: &Path,
+  updater: bool,
 ) -> crate::Result<Vec<PathBuf>> {
   let arch = match settings.binary_arch() {
     "x86_64" => "x64",
@@ -421,13 +435,107 @@ pub fn build_wix_app_installer(
 
   try_sign(&app_exe_source)?;
 
-  // ensure that `target/{release, debug}/wix` folder exists
-  std::fs::create_dir_all(settings.project_out_directory().join("wix"))?;
-
   let output_path = settings.project_out_directory().join("wix").join(arch);
 
+  if output_path.exists() {
+    remove_dir_all(&output_path)?;
+  }
+  create_dir_all(&output_path)?;
+
   let mut data = BTreeMap::new();
 
+  let silent_webview_install = if let WebviewInstallMode::DownloadBootstrapper { silent }
+  | WebviewInstallMode::EmbedBootstrapper { silent }
+  | WebviewInstallMode::OfflineInstaller { silent } =
+    settings.windows().webview_install_mode
+  {
+    silent
+  } else {
+    true
+  };
+
+  let webview_install_mode = if updater {
+    WebviewInstallMode::DownloadBootstrapper {
+      silent: silent_webview_install,
+    }
+  } else {
+    let mut webview_install_mode = settings.windows().webview_install_mode.clone();
+    if let Some(fixed_runtime_path) = settings.windows().webview_fixed_runtime_path.clone() {
+      webview_install_mode = WebviewInstallMode::FixedRuntime {
+        path: fixed_runtime_path,
+      };
+    } else if let Some(wix) = &settings.windows().wix {
+      if wix.skip_webview_install {
+        webview_install_mode = WebviewInstallMode::Skip;
+      }
+    }
+    webview_install_mode
+  };
+
+  data.insert("install_webview", to_json(true));
+  data.insert(
+    "webview_installer_args",
+    to_json(if silent_webview_install {
+      "/silent"
+    } else {
+      ""
+    }),
+  );
+
+  match webview_install_mode {
+    WebviewInstallMode::Skip | WebviewInstallMode::FixedRuntime { .. } => {
+      data.insert("install_webview", to_json(false));
+    }
+    WebviewInstallMode::DownloadBootstrapper { silent: _ } => {
+      data.insert("download_bootstrapper", to_json(true));
+      data.insert(
+        "webview_installer_args",
+        to_json(if silent_webview_install {
+          "&apos;/silent&apos;,"
+        } else {
+          ""
+        }),
+      );
+    }
+    WebviewInstallMode::EmbedBootstrapper { silent: _ } => {
+      let webview2_bootstrapper_path = output_path.join("MicrosoftEdgeWebview2Setup.exe");
+      std::fs::write(
+        &webview2_bootstrapper_path,
+        download(WEBVIEW2_BOOTSTRAPPER_URL)?,
+      )?;
+      data.insert(
+        "webview2_bootstrapper_path",
+        to_json(webview2_bootstrapper_path),
+      );
+    }
+    WebviewInstallMode::OfflineInstaller { silent: _ } => {
+      let guid = if arch == "x64" {
+        WEBVIEW2_X64_INSTALLER_GUID
+      } else {
+        WEBVIEW2_X86_INSTALLER_GUID
+      };
+      let mut offline_installer_path = dirs_next::cache_dir().unwrap();
+      offline_installer_path.push("tauri");
+      offline_installer_path.push(guid);
+      offline_installer_path.push(arch);
+      create_dir_all(&offline_installer_path)?;
+      let webview2_installer_path =
+        offline_installer_path.join("MicrosoftEdgeWebView2RuntimeInstaller.exe");
+      if !webview2_installer_path.exists() {
+        std::fs::write(
+          &webview2_installer_path,
+          download(
+            &format!("https://msedge.sf.dl.delivery.mp.microsoft.com/filestreamingservice/files/{}/MicrosoftEdgeWebView2RuntimeInstaller{}.exe",
+              guid,
+              arch.to_uppercase(),
+            ),
+          )?,
+        )?;
+      }
+      data.insert("webview2_installer_path", to_json(webview2_installer_path));
+    }
+  }
+
   let language_map: HashMap<String, LanguageMetadata> =
     serde_json::from_str(include_str!("./languages.json")).unwrap();
 
@@ -521,7 +629,6 @@ pub fn build_wix_app_installer(
   let mut fragment_paths = Vec::new();
   let mut handlebars = Handlebars::new();
   let mut has_custom_template = false;
-  let mut install_webview = settings.windows().webview_fixed_runtime_path.is_none();
   let mut enable_elevated_update_task = false;
 
   if let Some(wix) = &settings.windows().wix {
@@ -531,9 +638,6 @@ pub fn build_wix_app_installer(
     data.insert("feature_refs", to_json(&wix.feature_refs));
     data.insert("merge_refs", to_json(&wix.merge_refs));
     fragment_paths = wix.fragment_paths.clone();
-    if wix.skip_webview_install {
-      install_webview = false;
-    }
     enable_elevated_update_task = wix.enable_elevated_update_task;
 
     if let Some(temp_path) = &wix.template {
@@ -577,16 +681,6 @@ pub fn build_wix_app_installer(
       .expect("Failed to setup handlebar template");
   }
 
-  if install_webview {
-    data.insert("install_webview", to_json(true));
-  }
-
-  if output_path.exists() {
-    remove_dir_all(&output_path)?;
-  }
-
-  create_dir_all(&output_path)?;
-
   if enable_elevated_update_task {
     data.insert(
       "msiexec_args",
@@ -715,7 +809,7 @@ pub fn build_wix_app_installer(
       "*.wixobj".into(),
     ];
     let msi_output_path = output_path.join("output.msi");
-    let msi_path = app_installer_output_path(settings, &language)?;
+    let msi_path = app_installer_output_path(settings, &language, updater)?;
     create_dir_all(msi_path.parent().unwrap())?;
 
     info!(action = "Running"; "light to produce {}", msi_path.display());

+ 27 - 1
tooling/bundler/src/bundle/windows/templates/main.wxs

@@ -244,7 +244,9 @@
             <RegistrySearch Id="WVRTInstalledSystem" Root="HKLM" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw" Win64="no" />
             <RegistrySearch Id="WVRTInstalledUser" Root="HKCU" Key="SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Name="pv" Type="raw"/>
         </Property>
-        <CustomAction Id='DownloadAndInvokeBootstrapper' Directory="INSTALLDIR" Execute="deferred" ExeCommand='powershell.exe -NoProfile -windowstyle hidden Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" ; &amp; $env:TEMP\MicrosoftEdgeWebview2Setup.exe /install' Return='check'/>
+
+        {{#if download_bootstrapper}}
+        <CustomAction Id='DownloadAndInvokeBootstrapper' Directory="INSTALLDIR" Execute="deferred" ExeCommand='powershell.exe -NoProfile -windowstyle hidden Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" ; Start-Process -FilePath "$env:TEMP\MicrosoftEdgeWebview2Setup.exe" -ArgumentList ({{{webview_installer_args}}} &apos;/install&apos;) -Wait' Return='check'/>
         <InstallExecuteSequence>
             <Custom Action='DownloadAndInvokeBootstrapper' Before='InstallFinalize'>
                 <![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
@@ -252,6 +254,30 @@
         </InstallExecuteSequence>
         {{/if}}
 
+        <!-- Embedded webview bootstrapper mode -->
+        {{#if webview2_bootstrapper_path}}
+        <Binary Id="MicrosoftEdgeWebview2Setup.exe" SourceFile="{{{webview2_bootstrapper_path}}}"/>
+        <CustomAction Id='InvokeBootstrapper' BinaryKey='MicrosoftEdgeWebview2Setup.exe' Execute="deferred" ExeCommand='{{{webview_installer_args}}} /install' Return='check' />
+        <InstallExecuteSequence>
+            <Custom Action='InvokeBootstrapper' Before='InstallFinalize'>
+                <![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
+            </Custom>
+        </InstallExecuteSequence>
+        {{/if}}
+
+        <!-- Embedded offline installer -->
+        {{#if webview2_installer_path}}
+        <Binary Id="MicrosoftEdgeWebView2RuntimeInstaller.exe" SourceFile="{{{webview2_installer_path}}}"/>
+        <CustomAction Id='InvokeStandalone' BinaryKey='MicrosoftEdgeWebView2RuntimeInstaller.exe' Execute="deferred" ExeCommand='{{{webview_installer_args}}} /install' Return='check' />
+        <InstallExecuteSequence>
+            <Custom Action='InvokeStandalone' Before='InstallFinalize'>
+                <![CDATA[NOT(REMOVE OR WVRTINSTALLED)]]>
+            </Custom>
+        </InstallExecuteSequence>
+        {{/if}}
+
+        {{/if}}
+
         {{#if enable_elevated_update_task}}
         <!-- Install an elevated update task within Windows Task Scheduler -->
         <CustomAction

+ 131 - 2
tooling/cli/schema.json

@@ -137,6 +137,10 @@
             "timestampUrl": null,
             "tsp": false,
             "webviewFixedRuntimePath": null,
+            "webviewInstallMode": {
+              "silent": true,
+              "type": "downloadBootstrapper"
+            },
             "wix": null
           }
         },
@@ -268,6 +272,10 @@
               "timestampUrl": null,
               "tsp": false,
               "webviewFixedRuntimePath": null,
+              "webviewInstallMode": {
+                "silent": true,
+                "type": "downloadBootstrapper"
+              },
               "wix": null
             }
           },
@@ -1013,6 +1021,10 @@
             "timestampUrl": null,
             "tsp": false,
             "webviewFixedRuntimePath": null,
+            "webviewInstallMode": {
+              "silent": true,
+              "type": "downloadBootstrapper"
+            },
             "wix": null
           },
           "allOf": [
@@ -1189,8 +1201,20 @@
           "default": false,
           "type": "boolean"
         },
+        "webviewInstallMode": {
+          "description": "The installation mode for the Webview2 runtime.",
+          "default": {
+            "silent": true,
+            "type": "downloadBootstrapper"
+          },
+          "allOf": [
+            {
+              "$ref": "#/definitions/WebviewInstallMode"
+            }
+          ]
+        },
         "webviewFixedRuntimePath": {
-          "description": "Path to the webview fixed runtime to use.\n\nThe fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section). The `.cab` file must be extracted to a folder and this folder path must be defined on this field.",
+          "description": "Path to the webview fixed runtime to use. Overwrites [`Self::webview_install_mode`] if set.\n\nWill be removed in v2, prefer the [`Self::webview_install_mode`] option.\n\nThe fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section). The `.cab` file must be extracted to a folder and this folder path must be defined on this field.",
           "type": [
             "string",
             "null"
@@ -1215,6 +1239,111 @@
       },
       "additionalProperties": false
     },
+    "WebviewInstallMode": {
+      "description": "Install modes for the Webview2 runtime. Note that for the updater bundle [`Self::DownloadBootstrapper`] is used.",
+      "oneOf": [
+        {
+          "description": "Do not install the Webview2 as part of the Windows Installer.",
+          "type": "object",
+          "required": [
+            "type"
+          ],
+          "properties": {
+            "type": {
+              "type": "string",
+              "enum": [
+                "skip"
+              ]
+            }
+          },
+          "additionalProperties": false
+        },
+        {
+          "description": "Download the bootstrapper and run it. Requires internet connection. Results in a smaller installer size, but is not recommended on Windows 7.",
+          "type": "object",
+          "required": [
+            "type"
+          ],
+          "properties": {
+            "type": {
+              "type": "string",
+              "enum": [
+                "downloadBootstrapper"
+              ]
+            },
+            "silent": {
+              "description": "Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.",
+              "default": true,
+              "type": "boolean"
+            }
+          },
+          "additionalProperties": false
+        },
+        {
+          "description": "Embed the bootstrapper and run it. Requires internet connection. Increases the installer size by around 1.8MB, but offers better support on Windows 7.",
+          "type": "object",
+          "required": [
+            "type"
+          ],
+          "properties": {
+            "type": {
+              "type": "string",
+              "enum": [
+                "embedBootstrapper"
+              ]
+            },
+            "silent": {
+              "description": "Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`.",
+              "default": true,
+              "type": "boolean"
+            }
+          },
+          "additionalProperties": false
+        },
+        {
+          "description": "Embed the offline installer and run it. Does not require internet connection. Increases the installer size by around 127MB.",
+          "type": "object",
+          "required": [
+            "type"
+          ],
+          "properties": {
+            "type": {
+              "type": "string",
+              "enum": [
+                "offlineInstaller"
+              ]
+            },
+            "silent": {
+              "description": "Instructs the installer to run the installer in silent mode. Defaults to `true`.",
+              "default": true,
+              "type": "boolean"
+            }
+          },
+          "additionalProperties": false
+        },
+        {
+          "description": "Embed a fixed webview2 version and use it at runtime. Increases the installer size by around 180MB.",
+          "type": "object",
+          "required": [
+            "path",
+            "type"
+          ],
+          "properties": {
+            "type": {
+              "type": "string",
+              "enum": [
+                "fixedRuntime"
+              ]
+            },
+            "path": {
+              "description": "The path to the fixed runtime to use.\n\nThe fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section). The `.cab` file must be extracted to a folder and this folder path must be defined on this field.",
+              "type": "string"
+            }
+          },
+          "additionalProperties": false
+        }
+      ]
+    },
     "WixConfig": {
       "description": "Configuration for the MSI bundle using WiX.",
       "type": "object",
@@ -1284,7 +1413,7 @@
           }
         },
         "skipWebviewInstall": {
-          "description": "Disables the Webview2 runtime installation after app install.",
+          "description": "Disables the Webview2 runtime installation after app install.\n\nWill be removed in v2, prefer the [`WindowsConfig::webview_install_mode`] option.",
           "default": false,
           "type": "boolean"
         },

+ 5 - 0
tooling/cli/src/interface/rust.rs

@@ -880,6 +880,10 @@ fn tauri_config_to_bundle_settings(
   {
     if let Some(webview_fixed_runtime_path) = &config.windows.webview_fixed_runtime_path {
       resources.push(webview_fixed_runtime_path.display().to_string());
+    } else if let crate::helpers::config::WebviewInstallMode::FixedRuntime { path } =
+      &config.windows.webview_install_mode
+    {
+      resources.push(path.display().to_string());
     }
   }
 
@@ -958,6 +962,7 @@ fn tauri_config_to_bundle_settings(
         wix
       }),
       icon_path: windows_icon_path,
+      webview_install_mode: config.windows.webview_install_mode,
       webview_fixed_runtime_path: config.windows.webview_fixed_runtime_path,
       allow_downgrades: config.windows.allow_downgrades,
     },

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác