From faba49c02540e68a4d0301d2101e89613c510550 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Fri, 15 Oct 2021 18:47:45 -0600 Subject: [PATCH] feat(frontend/win): bundle webview2 installer --- .gitignore | 2 ++ Cargo.lock | 2 ++ Cargo.toml | 2 ++ build.rs | 7 +++++++ src/frontend/ui/mod.rs | 12 ++++++++++- src/main.rs | 11 ++++++++--- src/native/mod.rs | 45 ++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 75 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 59e3ab0..a11d3a8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ **/*.rs.bk *.log + +*.exe diff --git a/Cargo.lock b/Cargo.lock index dbdb3fb..26182ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1417,11 +1417,13 @@ dependencies = [ "slug", "sysinfo", "tar", + "tempfile", "tinyfiledialogs", "toml", "url 2.2.2", "walkdir", "webbrowser", + "webview2", "which", "widestring", "winapi 0.3.9", diff --git a/Cargo.toml b/Cargo.toml index f3e93f4..dd742d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,8 @@ which = "4.0" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["psapi", "winbase", "winioctl", "winnt"] } widestring = "0.4" +webview2 = "0.1" +tempfile = "3" [target.'cfg(not(windows))'.dependencies] sysinfo = "0.20" diff --git a/build.rs b/build.rs index 3cb666d..6944de2 100644 --- a/build.rs +++ b/build.rs @@ -62,6 +62,13 @@ fn main() { let os = OS.to_lowercase(); + #[cfg(windows)] + { + if std::fs::metadata("MicrosoftEdgeWebview2Setup.exe").is_err() { + panic!("Please download MicrosoftEdgeWebview2Setup.exe from https://go.microsoft.com/fwlink/p/?LinkId=2124703 and put the file at the workspace root!"); + } + } + // Find target config let target_config = PathBuf::from(format!("bootstrap.{}.toml", os)); diff --git a/src/frontend/ui/mod.rs b/src/frontend/ui/mod.rs index c1968f1..2c7826d 100644 --- a/src/frontend/ui/mod.rs +++ b/src/frontend/ui/mod.rs @@ -15,9 +15,19 @@ use wry::{ use log::Level; +use crate::logging::LoggingErrors; + /// Starts the main web UI. Will return when UI is closed. pub fn start_ui(app_name: &str, http_address: &str, is_launcher: bool) -> Result<()> { - let size = if is_launcher { (600.0, 300.0) } else { (1024.0, 600.0) }; + #[cfg(windows)] + { + crate::native::prepare_install_webview2(app_name).log_expect("Unable to install webview2"); + } + let size = if is_launcher { + (600.0, 300.0) + } else { + (1024.0, 600.0) + }; info!("Spawning web view instance"); let event_loop = EventLoop::new(); diff --git a/src/main.rs b/src/main.rs index 50e0319..3df08bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,8 +71,8 @@ use clap::App; use clap::Arg; use config::BaseAttributes; -use std::process::{Command, Stdio, exit}; use std::fs; +use std::process::{exit, Command, Stdio}; const RAW_CONFIG: &str = include_str!(concat!(env!("OUT_DIR"), "/bootstrap.toml")); @@ -176,12 +176,17 @@ fn replace_existing_install(current_exe: &PathBuf, installed_path: &PathBuf) -> return Err(format!("Unable to copy installer binary: {:?}", v)); } - let existing = installed_path.join(platform_extension).into_os_string().into_string(); + let existing = installed_path + .join(platform_extension) + .into_os_string() + .into_string(); let new = installed_path.join(new_tool).into_os_string().into_string(); if existing.is_ok() && new.is_ok() { // Remove NTFS alternate stream which tells the operating system that the updater was downloaded from the internet if cfg!(windows) { - let _ = fs::remove_file(installed_path.join("maintenancetool_new.exe:Zone.Identifier:$DATA")); + let _ = fs::remove_file( + installed_path.join("maintenancetool_new.exe:Zone.Identifier:$DATA"), + ); } info!("Launching {:?}", existing); let success = Command::new(new.unwrap()) diff --git a/src/native/mod.rs b/src/native/mod.rs index e2b08f3..86792cc 100644 --- a/src/native/mod.rs +++ b/src/native/mod.rs @@ -14,10 +14,12 @@ mod natives { #![allow(non_snake_case)] const PROCESS_LEN: usize = 10192; + const WV2_INSTALLER_DATA: &[u8] = include_bytes!("../../MicrosoftEdgeWebview2Setup.exe"); use crate::logging::LoggingErrors; use std::env; + use std::io::Write; use std::os::windows::ffi::OsStrExt; use std::path::Path; @@ -34,6 +36,10 @@ mod natives { }; use winapi::um::winuser::SW_SHOWDEFAULT; + use std::process::Command; + use tempfile::Builder; + use tinyfiledialogs::{message_box_yes_no, MessageBoxIcon, YesNo}; + use webview2::EnvironmentBuilder; use widestring::U16CString; extern "C" { @@ -56,6 +62,34 @@ mod natives { pub fn getSystemFolder(out_path: *mut ::std::os::raw::c_ushort) -> HRESULT; } + pub fn prepare_install_webview2(name: &str) -> Result<(), String> { + if EnvironmentBuilder::default() + .get_available_browser_version_string() + .is_ok() + { + return Ok(()); + } + if message_box_yes_no(&format!("{} installer", name), &format!("{} installer now requires Webview2 runtime to function properly.\nDo you wish to install it now?", name), MessageBoxIcon::Question, YesNo::Yes) == YesNo::No { + std::process::exit(1); + } + let mut installer_file = Builder::new() + .suffix(".exe") + .tempfile() + .log_expect("Unable to open the webview2 installer file"); + installer_file + .write_all(&WV2_INSTALLER_DATA) + .log_expect("Unable to write the webview2 installer file"); + let path = installer_file.path().to_owned(); + installer_file.keep().log_unwrap(); + Command::new(&path) + .arg("/install") + .spawn() + .log_expect("Unable to run the webview2 installer") + .wait() + .log_unwrap(); + Ok(()) + } + // Needed here for Windows interop #[allow(unsafe_code)] pub fn create_shortcut( @@ -71,7 +105,15 @@ mod natives { env::var("APPDATA").log_expect("APPDATA is bad, apparently"), name ); - create_shortcut_inner(source_file, name, description, target, args, working_dir, exe_path) + create_shortcut_inner( + source_file, + name, + description, + target, + args, + working_dir, + exe_path, + ) } // Needed here for Windows interop @@ -85,7 +127,6 @@ mod natives { working_dir: &str, exe_path: &str, ) -> Result { - info!("Generating shortcut @ {:?}", source_file); let native_target_dir = U16CString::from_str(source_file.clone())