Skip to content

Commit 9412ae2

Browse files
stop ignoring cef commands
Signed-off-by: NikitaSkrynnik <[email protected]>
1 parent 9e04feb commit 9412ae2

File tree

3 files changed

+198
-1
lines changed

3 files changed

+198
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
node_modules
22
dist
3-
cef
3+
src-tauri/cef
44

55
.DS_Store

src-tauri/src/cef/mod.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use std::{
2+
fs,
3+
process::Command,
4+
sync::{Arc, Mutex},
5+
};
6+
7+
use tauri::Manager;
8+
9+
use crate::{
10+
BrowserState,
11+
cef::utils::{
12+
compare_checksums, copy_dir_all, download_and_extract, find_available_port, get_cef_paths,
13+
wait_for_cef,
14+
},
15+
};
16+
17+
mod utils;
18+
19+
#[cfg(target_os = "linux")]
20+
const CEF_URL: &str =
21+
"https://github.com/hulylabs/huly-cef/releases/latest/download/huly-cef-linux.zip";
22+
#[cfg(target_os = "macos")]
23+
const CEF_URL: &str =
24+
"https://github.com/hulylabs/huly-cef/releases/latest/download/huly-cef-macos.zip";
25+
#[cfg(target_os = "windows")]
26+
const CEF_URL: &str =
27+
"https://github.com/hulylabs/huly-cef/releases/latest/download/huly-cef-windows.zip";
28+
29+
#[cfg(target_os = "linux")]
30+
const CEF_EXE: &str = "huly-cef-websockets";
31+
#[cfg(target_os = "macos")]
32+
const CEF_EXE: &str = "huly-cef-websockets.app/Contents/MacOS/huly-cef-websockets";
33+
#[cfg(target_os = "windows")]
34+
const CEF_EXE: &str = "huly-cef-websockets.exe";
35+
36+
#[tauri::command]
37+
pub fn is_cef_present(app_handle: tauri::AppHandle) -> Result<bool, String> {
38+
let (cef_dir, _) = get_cef_paths(&app_handle)?;
39+
compare_checksums(CEF_URL, cef_dir.join("checksum"))
40+
}
41+
42+
#[tauri::command]
43+
pub fn download_cef(app_handle: tauri::AppHandle) -> Result<(), String> {
44+
let (cef_dir, _) = get_cef_paths(&app_handle)?;
45+
let result = download_and_extract(CEF_URL, &cef_dir);
46+
if result.is_err() {
47+
_ = fs::remove_dir_all(&cef_dir);
48+
return result;
49+
}
50+
51+
Ok(())
52+
}
53+
54+
#[tauri::command]
55+
pub async fn launch_cef(app_handle: tauri::AppHandle) -> Result<String, String> {
56+
let (cef_dir, cef_cache) = get_cef_paths(&app_handle)?;
57+
58+
let temp_cef_dir = std::env::temp_dir().join("huly-cef");
59+
_ = fs::remove_dir_all(&temp_cef_dir);
60+
copy_dir_all(&cef_dir, &temp_cef_dir).map_err(|e| format!("failed to copy CEF: {e}"))?;
61+
62+
let cef_exe = temp_cef_dir.join(CEF_EXE);
63+
64+
let port = find_available_port().map_err(|e| format!("couldn't find available port: {e}"))?;
65+
let mut command = Command::new(cef_exe);
66+
command
67+
.args(["--port", &port.to_string()])
68+
.args(["--cache-path", cef_cache.to_str().unwrap()]);
69+
70+
#[cfg(target_os = "windows")]
71+
{
72+
use std::os::windows::process::CommandExt;
73+
command.creation_flags(0x08000000);
74+
}
75+
76+
let cef = command.spawn().map_err(|e| {
77+
format!("failed to launch CEF with params port {port} cache-dir {cef_cache:?}: {e}")
78+
})?;
79+
80+
wait_for_cef(port).map_err(|e| {
81+
format!("CEF failed to start with params port {port} cache-dir {cef_cache:?}: {e}")
82+
})?;
83+
84+
app_handle
85+
.state::<Arc<Mutex<BrowserState>>>()
86+
.lock()
87+
.unwrap()
88+
.cef_process = Some(cef);
89+
90+
Ok(format!("ws://localhost:{port}/browser"))
91+
}

src-tauri/src/cef/utils.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::{
2+
fs,
3+
io::{self, Cursor},
4+
net::TcpListener,
5+
path::{Path, PathBuf},
6+
};
7+
8+
use tauri::Manager;
9+
use tungstenite::connect;
10+
use zip::ZipArchive;
11+
12+
pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
13+
fs::create_dir_all(&dst)?;
14+
for entry in fs::read_dir(src)? {
15+
let entry = entry?;
16+
let ty = entry.file_type()?;
17+
if ty.is_dir() {
18+
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
19+
} else {
20+
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
21+
}
22+
}
23+
Ok(())
24+
}
25+
26+
pub fn get_cef_paths(app_handle: &tauri::AppHandle) -> Result<(PathBuf, PathBuf), String> {
27+
let app_resource_dir = app_handle
28+
.path()
29+
.resource_dir()
30+
.map_err(|_| "app resource directory not found")?;
31+
let app_cache_dir = app_handle
32+
.path()
33+
.app_cache_dir()
34+
.map_err(|_| "app cache directory not found")?;
35+
36+
let cef_dir = app_resource_dir.join("cef");
37+
let cef_cache = app_cache_dir.join("cefcache");
38+
39+
Ok((cef_dir, cef_cache))
40+
}
41+
42+
pub fn wait_for_cef(port: u16) -> Result<(), String> {
43+
for _ in 0..10 {
44+
std::thread::sleep(std::time::Duration::from_millis(500));
45+
if healthcheck("localhost", port) {
46+
return Ok(());
47+
}
48+
}
49+
50+
Err("CEF healthcheck failed".into())
51+
}
52+
53+
pub fn healthcheck(host: &str, port: u16) -> bool {
54+
let address = format!("ws://{}:{}", host, port);
55+
connect(address.clone()).is_ok()
56+
}
57+
58+
pub fn find_available_port() -> Result<u16, String> {
59+
for port in 10000..50000 {
60+
if let Ok(_) = TcpListener::bind(format!("0.0.0.0:{}", port)) {
61+
return Ok(port);
62+
}
63+
}
64+
Err("No available ports found".into())
65+
}
66+
67+
pub fn compare_checksums(url: &str, checksum_path: impl AsRef<Path>) -> Result<bool, String> {
68+
let existing = fs::read_to_string(checksum_path)
69+
.map_err(|e| format!("failed to read existing checksum: {e}"))?;
70+
let new = download_checksum(url)?;
71+
72+
Ok(existing == new)
73+
}
74+
75+
pub fn download_checksum(url: &str) -> Result<String, String> {
76+
let checksum = reqwest::blocking::get(format!("{url}.sha256"))
77+
.map_err(|e| format!("failed to download checksum: {e}"))?
78+
.text()
79+
.map_err(|e| format!("failed to read checksum response: {e}"))?;
80+
Ok(checksum)
81+
}
82+
83+
pub fn download_and_extract(url: &str, dir: impl AsRef<Path>) -> Result<(), String> {
84+
_ = fs::remove_dir_all(&dir);
85+
fs::create_dir_all(&dir).map_err(|e| format!("failed to create CEF dir: {e}"))?;
86+
87+
let data = reqwest::blocking::get(url)
88+
.and_then(|r| r.bytes())
89+
.and_then(|b| Ok(b.to_vec()))
90+
.map_err(|e| format!("failed to download CEF: {e}"))?;
91+
92+
ZipArchive::new(Cursor::new(data))
93+
.and_then(|mut archive| archive.extract(&dir))
94+
.map_err(|e| {
95+
format!(
96+
"failed to extract CEF archive to {}: {e}",
97+
dir.as_ref().display()
98+
)
99+
})?;
100+
101+
let checksum = download_checksum(url)?;
102+
fs::write(dir.as_ref().join("checksum"), checksum)
103+
.map_err(|e| format!("failed to save checksum: {e}"))?;
104+
105+
Ok(())
106+
}

0 commit comments

Comments
 (0)