From 348a727da7c0db3a99241fd3853bf4796d3c2b8b Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Sun, 17 Aug 2025 12:45:58 +0400 Subject: [PATCH] chore: formatting --- src-tauri/src/browser_runner.rs | 548 +++++++++++++++++++++++------- src-tauri/src/lib.rs | 2 +- src-tauri/src/platform_browser.rs | 222 ++++++------ 3 files changed, 550 insertions(+), 222 deletions(-) diff --git a/src-tauri/src/browser_runner.rs b/src-tauri/src/browser_runner.rs index db91b4c..afb5190 100644 --- a/src-tauri/src/browser_runner.rs +++ b/src-tauri/src/browser_runner.rs @@ -151,8 +151,8 @@ impl BrowserRunner { pub fn list_profiles(&self) -> Result, Box> { let profile_manager = ProfileManager::instance(); - let profiles = profile_manager.list_profiles(); - profiles + + profile_manager.list_profiles() } pub async fn launch_browser( @@ -433,15 +433,20 @@ impl BrowserRunner { } "firefox-developer" => { // More flexible detection for Firefox Developer Edition - (exe_name_lower.contains("firefox") && exe_name_lower.contains("developer")) || - (exe_name_lower.contains("firefox") && cmd.iter().any(|arg| { - let arg_str = arg.to_str().unwrap_or(""); - arg_str.contains("Developer") || arg_str.contains("developer") || - arg_str.contains("FirefoxDeveloperEdition") || arg_str.contains("firefox-developer") - })) || - exe_name_lower == "firefox" // Firefox Developer might just show as "firefox" + (exe_name_lower.contains("firefox") && exe_name_lower.contains("developer")) + || (exe_name_lower.contains("firefox") + && cmd.iter().any(|arg| { + let arg_str = arg.to_str().unwrap_or(""); + arg_str.contains("Developer") + || arg_str.contains("developer") + || arg_str.contains("FirefoxDeveloperEdition") + || arg_str.contains("firefox-developer") + })) + || exe_name_lower == "firefox" // Firefox Developer might just show as "firefox" + } + "mullvad-browser" => { + self.is_tor_or_mullvad_browser(&exe_name_lower, cmd, "mullvad-browser") } - "mullvad-browser" => self.is_tor_or_mullvad_browser(&exe_name_lower, cmd, "mullvad-browser"), "tor-browser" => self.is_tor_or_mullvad_browser(&exe_name_lower, cmd, "tor-browser"), "zen" => exe_name_lower.contains("zen"), "chromium" => exe_name_lower.contains("chromium") || exe_name_lower.contains("chrome"), @@ -454,7 +459,10 @@ impl BrowserRunner { } // Check for profile path match - let profile_path_match = if matches!(profile.browser.as_str(), "firefox" | "firefox-developer" | "tor-browser" | "mullvad-browser" | "zen") { + let profile_path_match = if matches!( + profile.browser.as_str(), + "firefox" | "firefox-developer" | "tor-browser" | "mullvad-browser" | "zen" + ) { // Firefox-based browsers: look for -profile argument followed by path let mut found_profile_arg = false; for (i, arg) in cmd.iter().enumerate() { @@ -468,7 +476,7 @@ impl BrowserRunner { } } // Also check for combined -profile=path format - if arg_str == format!("-profile={}", profile_data_path_str) { + if arg_str == format!("-profile={profile_data_path_str}") { found_profile_arg = true; break; } @@ -484,7 +492,8 @@ impl BrowserRunner { // Chromium-based browsers: look for --user-data-dir argument cmd.iter().any(|s| { if let Some(arg) = s.to_str() { - arg == format!("--user-data-dir={}", profile_data_path_str) || arg == profile_data_path_str + arg == format!("--user-data-dir={profile_data_path_str}") + || arg == profile_data_path_str } else { false } @@ -772,22 +781,44 @@ impl BrowserRunner { url: Option, internal_proxy_settings: Option<&ProxySettings>, ) -> Result> { - println!("launch_or_open_url called for profile: {} (ID: {})", profile.name, profile.id); - - // Get the most up-to-date profile data - let profiles = self.list_profiles().map_err(|e| format!("Failed to list profiles in launch_or_open_url: {e}"))?; - let updated_profile = profiles.into_iter().find(|p| p.id == profile.id).unwrap_or_else(|| profile.clone()); + println!( + "launch_or_open_url called for profile: {} (ID: {})", + profile.name, profile.id + ); - println!("Checking browser status for profile: {} (ID: {})", updated_profile.name, updated_profile.id); + // Get the most up-to-date profile data + let profiles = self + .list_profiles() + .map_err(|e| format!("Failed to list profiles in launch_or_open_url: {e}"))?; + let updated_profile = profiles + .into_iter() + .find(|p| p.id == profile.id) + .unwrap_or_else(|| profile.clone()); + + println!( + "Checking browser status for profile: {} (ID: {})", + updated_profile.name, updated_profile.id + ); // Check if browser is already running - let is_running = self.check_browser_status(app_handle.clone(), &updated_profile).await.map_err(|e| format!("Failed to check browser status: {}", e))?; + let is_running = self + .check_browser_status(app_handle.clone(), &updated_profile) + .await + .map_err(|e| format!("Failed to check browser status: {e}"))?; // Get the updated profile again after status check (PID might have been updated) - let profiles = self.list_profiles().map_err(|e| format!("Failed to list profiles after status check: {e}"))?; - let final_profile = profiles.into_iter().find(|p| p.id == profile.id).unwrap_or_else(|| updated_profile.clone()); + let profiles = self + .list_profiles() + .map_err(|e| format!("Failed to list profiles after status check: {e}"))?; + let final_profile = profiles + .into_iter() + .find(|p| p.id == profile.id) + .unwrap_or_else(|| updated_profile.clone()); - println!("Browser status check - Profile: {} (ID: {}), Running: {}, URL: {:?}, PID: {:?}", final_profile.name, final_profile.id, is_running, url, final_profile.process_id); + println!( + "Browser status check - Profile: {} (ID: {}), Running: {}, URL: {:?}, PID: {:?}", + final_profile.name, final_profile.id, is_running, url, final_profile.process_id + ); if is_running && url.is_some() { // Browser is running and we have a URL to open @@ -795,14 +826,27 @@ impl BrowserRunner { println!("Opening URL in existing browser: {url_ref}"); // For TOR/Mullvad browsers, add extra verification - if matches!(final_profile.browser.as_str(), "tor-browser" | "mullvad-browser") { + if matches!( + final_profile.browser.as_str(), + "tor-browser" | "mullvad-browser" + ) { println!("TOR/Mullvad browser detected - ensuring we have correct PID"); if final_profile.process_id.is_none() { - println!("ERROR: No PID found for running TOR/Mullvad browser - this should not happen"); + println!( + "ERROR: No PID found for running TOR/Mullvad browser - this should not happen" + ); return Err("No PID found for running browser".into()); } } - match self.open_url_in_existing_browser(app_handle.clone(), &final_profile, url_ref, internal_proxy_settings).await { + match self + .open_url_in_existing_browser( + app_handle.clone(), + &final_profile, + url_ref, + internal_proxy_settings, + ) + .await + { Ok(()) => { println!("Successfully opened URL in existing browser"); Ok(final_profile) @@ -827,7 +871,14 @@ impl BrowserRunner { } else { // This case shouldn't happen since we checked is_some() above, but handle it gracefully println!("URL was unexpectedly None, launching new browser instance"); - self.launch_browser(app_handle.clone(), &final_profile, url, internal_proxy_settings).await + self + .launch_browser( + app_handle.clone(), + &final_profile, + url, + internal_proxy_settings, + ) + .await } } else { // Browser is not running or no URL provided, launch new instance @@ -836,7 +887,14 @@ impl BrowserRunner { } else { println!("Launching new browser instance - no URL provided"); } - self.launch_browser(app_handle.clone(), &final_profile, url, internal_proxy_settings).await + self + .launch_browser( + app_handle.clone(), + &final_profile, + url, + internal_proxy_settings, + ) + .await } } @@ -893,14 +951,20 @@ impl BrowserRunner { let profile_data_path = profile.get_profile_data_path(&profiles_dir); let profile_path_str = profile_data_path.to_string_lossy(); - println!("Attempting to kill Camoufox process for profile: {} (ID: {})", profile.name, profile.id); + println!( + "Attempting to kill Camoufox process for profile: {} (ID: {})", + profile.name, profile.id + ); match camoufox_launcher .find_camoufox_by_profile(&profile_path_str) .await { Ok(Some(camoufox_process)) => { - println!("Found Camoufox process: {} (PID: {:?})", camoufox_process.id, camoufox_process.processId); + println!( + "Found Camoufox process: {} (PID: {:?})", + camoufox_process.id, camoufox_process.processId + ); match camoufox_launcher .stop_camoufox(&app_handle, &camoufox_process.id) @@ -908,30 +972,50 @@ impl BrowserRunner { { Ok(stopped) => { if stopped { - println!("Successfully stopped Camoufox process: {} (PID: {:?})", camoufox_process.id, camoufox_process.processId); + println!( + "Successfully stopped Camoufox process: {} (PID: {:?})", + camoufox_process.id, camoufox_process.processId + ); } else { - println!("Failed to stop Camoufox process: {} (PID: {:?})", camoufox_process.id, camoufox_process.processId); + println!( + "Failed to stop Camoufox process: {} (PID: {:?})", + camoufox_process.id, camoufox_process.processId + ); } } Err(e) => { - println!("Error stopping Camoufox process {}: {}", camoufox_process.id, e); + println!( + "Error stopping Camoufox process {}: {}", + camoufox_process.id, e + ); } } } Ok(None) => { - println!("No running Camoufox process found for profile: {} (ID: {})", profile.name, profile.id); + println!( + "No running Camoufox process found for profile: {} (ID: {})", + profile.name, profile.id + ); } Err(e) => { - println!("Error finding Camoufox process for profile {}: {}", profile.name, e); + println!( + "Error finding Camoufox process for profile {}: {}", + profile.name, e + ); } } // Clear the process ID from the profile let mut updated_profile = profile.clone(); updated_profile.process_id = None; - self.save_process_info(&updated_profile).map_err(|e| format!("Failed to update profile: {e}"))?; + self + .save_process_info(&updated_profile) + .map_err(|e| format!("Failed to update profile: {e}"))?; - println!("Emitting profile events for successful Camoufox kill: {}", updated_profile.name); + println!( + "Emitting profile events for successful Camoufox kill: {}", + updated_profile.name + ); // Emit profile update event to frontend if let Err(e) = app_handle.emit("profile-updated", &updated_profile) { @@ -952,10 +1036,16 @@ impl BrowserRunner { if let Err(e) = app_handle.emit("profile-running-changed", &payload) { println!("Warning: Failed to emit profile running changed event: {e}"); } else { - println!("Successfully emitted profile-running-changed event for Camoufox {}: running={}", updated_profile.name, payload.is_running); + println!( + "Successfully emitted profile-running-changed event for Camoufox {}: running={}", + updated_profile.name, payload.is_running + ); } - println!("Camoufox process cleanup completed for profile: {} (ID: {})", profile.name, profile.id); + println!( + "Camoufox process cleanup completed for profile: {} (ID: {})", + profile.name, profile.id + ); return Ok(()); } @@ -978,13 +1068,16 @@ impl BrowserRunner { } "firefox-developer" => { // More flexible detection for Firefox Developer Edition - (exe_name.contains("firefox") && exe_name.contains("developer")) || - (exe_name.contains("firefox") && cmd.iter().any(|arg| { - let arg_str = arg.to_str().unwrap_or(""); - arg_str.contains("Developer") || arg_str.contains("developer") || - arg_str.contains("FirefoxDeveloperEdition") || arg_str.contains("firefox-developer") - })) || - exe_name == "firefox" // Firefox Developer might just show as "firefox" + (exe_name.contains("firefox") && exe_name.contains("developer")) + || (exe_name.contains("firefox") + && cmd.iter().any(|arg| { + let arg_str = arg.to_str().unwrap_or(""); + arg_str.contains("Developer") + || arg_str.contains("developer") + || arg_str.contains("FirefoxDeveloperEdition") + || arg_str.contains("firefox-developer") + })) + || exe_name == "firefox" // Firefox Developer might just show as "firefox" } "mullvad-browser" => self.is_tor_or_mullvad_browser(&exe_name, cmd, "mullvad-browser"), "tor-browser" => self.is_tor_or_mullvad_browser(&exe_name, cmd, "tor-browser"), @@ -1000,7 +1093,10 @@ impl BrowserRunner { let profile_data_path = profile.get_profile_data_path(&profiles_dir); let profile_data_path_str = profile_data_path.to_string_lossy(); - let profile_path_match = if matches!(profile.browser.as_str(), "firefox" | "firefox-developer" | "tor-browser" | "mullvad-browser" | "zen") { + let profile_path_match = if matches!( + profile.browser.as_str(), + "firefox" | "firefox-developer" | "tor-browser" | "mullvad-browser" | "zen" + ) { // Firefox-based browsers: look for -profile argument followed by path let mut found_profile_arg = false; for (i, arg) in cmd.iter().enumerate() { @@ -1014,7 +1110,7 @@ impl BrowserRunner { } } // Also check for combined -profile=path format - if arg_str == format!("-profile={}", profile_data_path_str) { + if arg_str == format!("-profile={profile_data_path_str}") { found_profile_arg = true; break; } @@ -1030,7 +1126,8 @@ impl BrowserRunner { // Chromium-based browsers: look for --user-data-dir argument cmd.iter().any(|s| { if let Some(arg) = s.to_str() { - arg == format!("--user-data-dir={}", profile_data_path_str) || arg == profile_data_path_str + arg == format!("--user-data-dir={profile_data_path_str}") + || arg == profile_data_path_str } else { false } @@ -1038,7 +1135,10 @@ impl BrowserRunner { }; if profile_path_match { - println!("Verified stored PID {} is valid for profile {} (ID: {})", pid, profile.name, profile.id); + println!( + "Verified stored PID {} is valid for profile {} (ID: {})", + pid, profile.name, profile.id + ); pid } else { println!("Stored PID {} doesn't match profile path for {} (ID: {}), searching for correct process", pid, profile.name, profile.id); @@ -1051,7 +1151,10 @@ impl BrowserRunner { self.find_browser_process_by_profile(profile)? } } else { - println!("Stored PID {} is no longer valid for profile {} (ID: {}), searching for correct process", pid, profile.name, profile.id); + println!( + "Stored PID {} is no longer valid for profile {} (ID: {}), searching for correct process", + pid, profile.name, profile.id + ); // Fall through to search for correct process self.find_browser_process_by_profile(profile)? } @@ -1083,9 +1186,14 @@ impl BrowserRunner { // Clear the process ID from the profile let mut updated_profile = profile.clone(); updated_profile.process_id = None; - self.save_process_info(&updated_profile).map_err(|e| format!("Failed to update profile: {e}"))?; + self + .save_process_info(&updated_profile) + .map_err(|e| format!("Failed to update profile: {e}"))?; - println!("Emitting profile events for successful kill: {}", updated_profile.name); + println!( + "Emitting profile events for successful kill: {}", + updated_profile.name + ); // Emit profile update event to frontend if let Err(e) = app_handle.emit("profile-updated", &updated_profile) { @@ -1106,7 +1214,10 @@ impl BrowserRunner { if let Err(e) = app_handle.emit("profile-running-changed", &payload) { println!("Warning: Failed to emit profile running changed event: {e}"); } else { - println!("Successfully emitted profile-running-changed event for {}: running={}", updated_profile.name, payload.is_running); + println!( + "Successfully emitted profile-running-changed event for {}: running={}", + updated_profile.name, payload.is_running + ); } Ok(()) @@ -1122,7 +1233,10 @@ impl BrowserRunner { let profile_data_path = profile.get_profile_data_path(&profiles_dir); let profile_data_path_str = profile_data_path.to_string_lossy(); - println!("Searching for {} browser process with profile path: {}", profile.browser, profile_data_path_str); + println!( + "Searching for {} browser process with profile path: {}", + profile.browser, profile_data_path_str + ); for (pid, process) in system.processes() { let cmd = process.cmd(); @@ -1142,13 +1256,16 @@ impl BrowserRunner { } "firefox-developer" => { // More flexible detection for Firefox Developer Edition - (exe_name.contains("firefox") && exe_name.contains("developer")) || - (exe_name.contains("firefox") && cmd.iter().any(|arg| { - let arg_str = arg.to_str().unwrap_or(""); - arg_str.contains("Developer") || arg_str.contains("developer") || - arg_str.contains("FirefoxDeveloperEdition") || arg_str.contains("firefox-developer") - })) || - exe_name == "firefox" // Firefox Developer might just show as "firefox" + (exe_name.contains("firefox") && exe_name.contains("developer")) + || (exe_name.contains("firefox") + && cmd.iter().any(|arg| { + let arg_str = arg.to_str().unwrap_or(""); + arg_str.contains("Developer") + || arg_str.contains("developer") + || arg_str.contains("FirefoxDeveloperEdition") + || arg_str.contains("firefox-developer") + })) + || exe_name == "firefox" // Firefox Developer might just show as "firefox" } "mullvad-browser" => self.is_tor_or_mullvad_browser(&exe_name, cmd, "mullvad-browser"), "tor-browser" => self.is_tor_or_mullvad_browser(&exe_name, cmd, "tor-browser"), @@ -1163,7 +1280,10 @@ impl BrowserRunner { } // Check for profile path match with improved logic - let profile_path_match = if matches!(profile.browser.as_str(), "firefox" | "firefox-developer" | "tor-browser" | "mullvad-browser" | "zen") { + let profile_path_match = if matches!( + profile.browser.as_str(), + "firefox" | "firefox-developer" | "tor-browser" | "mullvad-browser" | "zen" + ) { // Firefox-based browsers: look for -profile argument followed by path let mut found_profile_arg = false; for (i, arg) in cmd.iter().enumerate() { @@ -1177,7 +1297,7 @@ impl BrowserRunner { } } // Also check for combined -profile=path format - if arg_str == format!("-profile={}", profile_data_path_str) { + if arg_str == format!("-profile={profile_data_path_str}") { found_profile_arg = true; break; } @@ -1193,7 +1313,8 @@ impl BrowserRunner { // Chromium-based browsers: look for --user-data-dir argument cmd.iter().any(|s| { if let Some(arg) = s.to_str() { - arg == format!("--user-data-dir={}", profile_data_path_str) || arg == profile_data_path_str + arg == format!("--user-data-dir={profile_data_path_str}") + || arg == profile_data_path_str } else { false } @@ -1202,12 +1323,21 @@ impl BrowserRunner { if profile_path_match { let pid_u32 = pid.as_u32(); - println!("Found matching {} browser process with PID: {} for profile: {} (ID: {})", profile.browser, pid_u32, profile.name, profile.id); + println!( + "Found matching {} browser process with PID: {} for profile: {} (ID: {})", + profile.browser, pid_u32, profile.name, profile.id + ); return Ok(pid_u32); } } - Err(format!("No running {} browser process found for profile: {} (ID: {})", profile.browser, profile.name, profile.id).into()) + Err( + format!( + "No running {} browser process found for profile: {} (ID: {})", + profile.browser, profile.name, profile.id + ) + .into(), + ) } /// Check if browser binaries exist for all profiles and return missing binaries @@ -1215,21 +1345,29 @@ impl BrowserRunner { &self, ) -> Result, Box> { // Get all profiles - let profiles = self.list_profiles().map_err(|e| format!("Failed to list profiles: {e}"))?; + let profiles = self + .list_profiles() + .map_err(|e| format!("Failed to list profiles: {e}"))?; let mut missing_binaries = Vec::new(); for profile in profiles { let browser_type = match BrowserType::from_str(&profile.browser) { Ok(bt) => bt, Err(_) => { - println!("Warning: Invalid browser type '{}' for profile '{}'", profile.browser, profile.name); + println!( + "Warning: Invalid browser type '{}' for profile '{}'", + profile.browser, profile.name + ); continue; } }; let browser = create_browser(browser_type.clone()); let binaries_dir = self.get_binaries_dir(); - println!("binaries_dir: {binaries_dir:?} for profile: {}", profile.name); + println!( + "binaries_dir: {binaries_dir:?} for profile: {}", + profile.name + ); // Check if the version is downloaded if !browser.is_version_downloaded(&profile.version, &binaries_dir) { @@ -1245,7 +1383,9 @@ impl BrowserRunner { &self, ) -> Result> { // Get all profiles - let profiles = self.list_profiles().map_err(|e| format!("Failed to list profiles: {e}"))?; + let profiles = self + .list_profiles() + .map_err(|e| format!("Failed to list profiles: {e}"))?; // Check if there are any Camoufox profiles let has_camoufox_profiles = profiles.iter().any(|profile| profile.browser == "camoufox"); @@ -1268,7 +1408,11 @@ impl BrowserRunner { let registry = DownloadedBrowsersRegistry::instance(); if let Ok(cleaned_up) = registry.verify_and_cleanup_stale_entries(self) { if !cleaned_up.is_empty() { - println!("Cleaned up {} stale registry entries: {}", cleaned_up.len(), cleaned_up.join(", ")); + println!( + "Cleaned up {} stale registry entries: {}", + cleaned_up.len(), + cleaned_up.join(", ") + ); } } @@ -1278,9 +1422,14 @@ impl BrowserRunner { for (profile_name, browser, version) in missing_binaries { println!("Downloading missing binary for profile '{profile_name}': {browser} {version}"); - match self.download_browser_impl(app_handle.clone(), browser.clone(), version.clone()).await { + match self + .download_browser_impl(app_handle.clone(), browser.clone(), version.clone()) + .await + { Ok(_) => { - downloaded.push(format!("{} {} (for profile '{profile_name}')", browser, version)); + downloaded.push(format!( + "{browser} {version} (for profile '{profile_name}')" + )); } Err(e) => { eprintln!("Failed to download {browser} {version} for profile '{profile_name}': {e}"); @@ -1328,7 +1477,8 @@ impl BrowserRunner { downloading.insert(download_key.clone()); } - let browser_type = BrowserType::from_str(&browser_str).map_err(|e| format!("Invalid browser type: {e}"))?; + let browser_type = + BrowserType::from_str(&browser_str).map_err(|e| format!("Invalid browser type: {e}"))?; let browser = create_browser(browser_type.clone()); // Get registry instance and check if already downloaded @@ -1345,18 +1495,34 @@ impl BrowserRunner { // Registry says it's downloaded but files don't exist - clean up registry println!("Registry indicates {browser_str} {version} is downloaded, but files are missing. Cleaning up registry entry."); registry.remove_browser(&browser_str, &version); - registry.save().map_err(|e| format!("Failed to save cleaned registry: {e}"))?; + registry + .save() + .map_err(|e| format!("Failed to save cleaned registry: {e}"))?; } } // Check if browser is supported on current platform before attempting download let version_service = BrowserVersionManager::instance(); - if !version_service.is_browser_supported(&browser_str).unwrap_or(false) { - return Err(format!("Browser '{}' is not supported on your platform ({} {}). Supported browsers: {}", browser_str, std::env::consts::OS, std::env::consts::ARCH, version_service.get_supported_browsers().join(", ")).into()); + if !version_service + .is_browser_supported(&browser_str) + .unwrap_or(false) + { + return Err( + format!( + "Browser '{}' is not supported on your platform ({} {}). Supported browsers: {}", + browser_str, + std::env::consts::OS, + std::env::consts::ARCH, + version_service.get_supported_browsers().join(", ") + ) + .into(), + ); } - let download_info = version_service.get_download_info(&browser_str, &version).map_err(|e| format!("Failed to get download info: {e}"))?; + let download_info = version_service + .get_download_info(&browser_str, &version) + .map_err(|e| format!("Failed to get download info: {e}"))?; // Create browser directory let mut browser_dir = self.get_binaries_dir(); @@ -1367,13 +1533,24 @@ impl BrowserRunner { // Mark download as started in registry registry.mark_download_started(&browser_str, &version, browser_dir.clone()); - registry.save().map_err(|e| format!("Failed to save registry: {e}"))?; + registry + .save() + .map_err(|e| format!("Failed to save registry: {e}"))?; // Use the download module let downloader = crate::download::Downloader::instance(); // Attempt to download the archive. If the download fails but an archive with the // expected filename already exists (manual download), continue using that file. - let download_path: PathBuf = match downloader.download_browser(&app_handle, browser_type.clone(), &version, &download_info, &browser_dir).await { + let download_path: PathBuf = match downloader + .download_browser( + &app_handle, + browser_type.clone(), + &version, + &download_info, + &browser_dir, + ) + .await + { Ok(path) => path, Err(e) => { // Do NOT continue with extraction on failed downloads. Partial files may exist but are invalid. @@ -1389,7 +1566,16 @@ impl BrowserRunner { // Use the extraction module if download_info.is_archive { let extractor = crate::extraction::Extractor::instance(); - match extractor.extract_browser(&app_handle, browser_type.clone(), &version, &download_path, &browser_dir).await { + match extractor + .extract_browser( + &app_handle, + browser_type.clone(), + &version, + &download_path, + &browser_dir, + ) + .await + { Ok(_) => { // Do not remove the archive here. We keep it until verification succeeds. } @@ -1432,7 +1618,12 @@ impl BrowserRunner { if !browser.is_version_downloaded(&version, &binaries_dir) { // Provide detailed error information for debugging let browser_dir = binaries_dir.join(&browser_str).join(&version); - let mut error_details = format!("Browser download completed but verification failed for {} {}. Expected directory: {}", browser_str, version, browser_dir.display()); + let mut error_details = format!( + "Browser download completed but verification failed for {} {}. Expected directory: {}", + browser_str, + version, + browser_dir.display() + ); // List what files actually exist if browser_dir.exists() { @@ -1458,7 +1649,10 @@ impl BrowserRunner { error_details.push_str(&format!("\n {}/camoufox", camoufox_subdir.display())); if camoufox_subdir.exists() { - error_details.push_str(&format!("\nCamoufox subdirectory exists: {}", camoufox_subdir.display())); + error_details.push_str(&format!( + "\nCamoufox subdirectory exists: {}", + camoufox_subdir.display() + )); if let Ok(entries) = std::fs::read_dir(&camoufox_subdir) { error_details.push_str("\nFiles in camoufox subdirectory:"); for entry in entries.flatten() { @@ -1468,7 +1662,10 @@ impl BrowserRunner { } } } else { - error_details.push_str(&format!("\nCamoufox subdirectory does not exist: {}", camoufox_subdir.display())); + error_details.push_str(&format!( + "\nCamoufox subdirectory does not exist: {}", + camoufox_subdir.display() + )); } } @@ -1487,7 +1684,9 @@ impl BrowserRunner { if let Err(e) = registry.mark_download_completed(&browser_str, &version) { eprintln!("Warning: Could not mark {browser_str} {version} as completed in registry: {e}"); } - registry.save().map_err(|e| format!("Failed to save registry: {e}"))?; + registry + .save() + .map_err(|e| format!("Failed to save registry: {e}"))?; // Now that verification succeeded, remove the archive file if it exists if download_info.is_archive { @@ -1620,7 +1819,10 @@ pub async fn launch_browser_profile( profile: BrowserProfile, url: Option, ) -> Result { - println!("Launch request received for profile: {} (ID: {})", profile.name, profile.id); + println!( + "Launch request received for profile: {} (ID: {})", + profile.name, profile.id + ); let browser_runner = BrowserRunner::instance(); @@ -1628,32 +1830,57 @@ pub async fn launch_browser_profile( let mut internal_proxy_settings: Option = None; // Resolve the most up-to-date profile from disk by ID to avoid using stale proxy_id/browser state - let profile_for_launch = match browser_runner.list_profiles().map_err(|e| format!("Failed to list profiles: {e}")) { - Ok(profiles) => profiles.into_iter().find(|p| p.id == profile.id).unwrap_or_else(|| profile.clone()), + let profile_for_launch = match browser_runner + .list_profiles() + .map_err(|e| format!("Failed to list profiles: {e}")) + { + Ok(profiles) => profiles + .into_iter() + .find(|p| p.id == profile.id) + .unwrap_or_else(|| profile.clone()), Err(e) => { return Err(e); } }; - println!("Resolved profile for launch: {} (ID: {})", profile_for_launch.name, profile_for_launch.id); + println!( + "Resolved profile for launch: {} (ID: {})", + profile_for_launch.name, profile_for_launch.id + ); // Always start a local proxy before launching (non-Camoufox handled here; Camoufox has its own flow) if profile.browser != "camoufox" { // Determine upstream proxy if configured; otherwise use DIRECT - let upstream_proxy = profile_for_launch.proxy_id.as_ref().and_then(|id| PROXY_MANAGER.get_proxy_settings_by_id(id)); + let upstream_proxy = profile_for_launch + .proxy_id + .as_ref() + .and_then(|id| PROXY_MANAGER.get_proxy_settings_by_id(id)); // Use a temporary PID (1) to start the proxy, we'll update it after browser launch let temp_pid = 1u32; - match PROXY_MANAGER.start_proxy(app_handle.clone(), upstream_proxy.as_ref(), temp_pid, Some(&profile.name)).await { + match PROXY_MANAGER + .start_proxy( + app_handle.clone(), + upstream_proxy.as_ref(), + temp_pid, + Some(&profile.name), + ) + .await + { Ok(internal_proxy) => { // Use internal proxy for subsequent launch internal_proxy_settings = Some(internal_proxy.clone()); // For Firefox-based browsers, apply PAC/user.js to point to the local proxy - if matches!(profile_for_launch.browser.as_str(), "firefox" | "firefox-developer" | "zen" | "tor-browser" | "mullvad-browser") { + if matches!( + profile_for_launch.browser.as_str(), + "firefox" | "firefox-developer" | "zen" | "tor-browser" | "mullvad-browser" + ) { let profiles_dir = browser_runner.get_profiles_dir(); - let profile_path = profiles_dir.join(profile_for_launch.id.to_string()).join("profile"); + let profile_path = profiles_dir + .join(profile_for_launch.id.to_string()) + .join("profile"); // Provide a dummy upstream (ignored when internal proxy is provided) let dummy_upstream = ProxySettings { @@ -1664,10 +1891,20 @@ pub async fn launch_browser_profile( password: None, }; - browser_runner.apply_proxy_settings_to_profile(&profile_path, &dummy_upstream, Some(&internal_proxy)).map_err(|e| format!("Failed to update profile proxy: {e}"))?; + browser_runner + .apply_proxy_settings_to_profile(&profile_path, &dummy_upstream, Some(&internal_proxy)) + .map_err(|e| format!("Failed to update profile proxy: {e}"))?; } - println!("Local proxy prepared for profile: {} on port: {} (upstream: {})", profile_for_launch.name, internal_proxy.port, upstream_proxy.as_ref().map(|p| format!("{}:{}", p.host, p.port)).unwrap_or_else(|| "DIRECT".to_string())); + println!( + "Local proxy prepared for profile: {} on port: {} (upstream: {})", + profile_for_launch.name, + internal_proxy.port, + upstream_proxy + .as_ref() + .map(|p| format!("{}:{}", p.host, p.port)) + .unwrap_or_else(|| "DIRECT".to_string()) + ); } Err(e) => { eprintln!("Failed to start local proxy (will launch without it): {e}"); @@ -1675,7 +1912,10 @@ pub async fn launch_browser_profile( } } - println!("Starting browser launch for profile: {} (ID: {})", profile_for_launch.name, profile_for_launch.id); + println!( + "Starting browser launch for profile: {} (ID: {})", + profile_for_launch.name, profile_for_launch.id + ); // Launch browser or open URL in existing instance let updated_profile = browser_runner.launch_or_open_url(app_handle.clone(), &profile_for_launch, url, internal_proxy_settings.as_ref()).await.map_err(|e| { @@ -1705,7 +1945,10 @@ pub async fn launch_browser_profile( format!("Failed to launch browser or open URL: {e}") })?; - println!("Browser launch completed for profile: {} (ID: {})", updated_profile.name, updated_profile.id); + println!( + "Browser launch completed for profile: {} (ID: {})", + updated_profile.name, updated_profile.id + ); // Now update the proxy with the correct PID if we have one if let Some(actual_pid) = updated_profile.process_id { @@ -1723,7 +1966,10 @@ pub async fn update_profile_proxy( proxy_id: Option, ) -> Result { let profile_manager = ProfileManager::instance(); - profile_manager.update_profile_proxy(app_handle, &profile_id, proxy_id).await.map_err(|e| format!("Failed to update profile: {e}")) + profile_manager + .update_profile_proxy(app_handle, &profile_id, proxy_id) + .await + .map_err(|e| format!("Failed to update profile: {e}")) } #[tauri::command] @@ -1732,7 +1978,9 @@ pub fn update_profile_tags( tags: Vec, ) -> Result { let profile_manager = ProfileManager::instance(); - profile_manager.update_profile_tags(&profile_id, tags).map_err(|e| format!("Failed to update profile tags: {e}")) + profile_manager + .update_profile_tags(&profile_id, tags) + .map_err(|e| format!("Failed to update profile tags: {e}")) } #[tauri::command] @@ -1741,7 +1989,10 @@ pub async fn check_browser_status( profile: BrowserProfile, ) -> Result { let profile_manager = ProfileManager::instance(); - profile_manager.check_browser_status(app_handle, &profile).await.map_err(|e| format!("Failed to check browser status: {e}")) + profile_manager + .check_browser_status(app_handle, &profile) + .await + .map_err(|e| format!("Failed to check browser status: {e}")) } #[tauri::command] @@ -1751,13 +2002,17 @@ pub fn rename_profile( new_name: &str, ) -> Result { let profile_manager = ProfileManager::instance(); - profile_manager.rename_profile(old_id, new_name).map_err(|e| format!("Failed to rename profile: {e}")) + profile_manager + .rename_profile(old_id, new_name) + .map_err(|e| format!("Failed to rename profile: {e}")) } #[tauri::command] pub fn delete_profile(_app_handle: tauri::AppHandle, profile_id: String) -> Result<(), String> { let browser_runner = BrowserRunner::instance(); - browser_runner.delete_profile(profile_id.as_str()).map_err(|e| format!("Failed to delete profile: {e}")) + browser_runner + .delete_profile(profile_id.as_str()) + .map_err(|e| format!("Failed to delete profile: {e}")) } #[tauri::command] @@ -1769,7 +2024,9 @@ pub fn get_supported_browsers() -> Result, String> { #[tauri::command] pub fn is_browser_supported_on_platform(browser_str: String) -> Result { let service = BrowserVersionManager::instance(); - service.is_browser_supported(&browser_str).map_err(|e| format!("Failed to check browser support: {e}")) + service + .is_browser_supported(&browser_str) + .map_err(|e| format!("Failed to check browser support: {e}")) } #[tauri::command] @@ -1786,7 +2043,10 @@ pub async fn fetch_browser_versions_cached_first( let service_clone = BrowserVersionManager::instance(); let browser_str_clone = browser_str.clone(); tokio::spawn(async move { - if let Err(e) = service_clone.fetch_browser_versions_detailed(&browser_str_clone, false).await { + if let Err(e) = service_clone + .fetch_browser_versions_detailed(&browser_str_clone, false) + .await + { eprintln!("Background version update failed for {browser_str_clone}: {e}"); } }); @@ -1794,7 +2054,10 @@ pub async fn fetch_browser_versions_cached_first( Ok(cached_versions) } else { // No cache available, fetch fresh - service.fetch_browser_versions_detailed(&browser_str, false).await.map_err(|e| format!("Failed to fetch detailed browser versions: {e}")) + service + .fetch_browser_versions_detailed(&browser_str, false) + .await + .map_err(|e| format!("Failed to fetch detailed browser versions: {e}")) } } @@ -1812,7 +2075,10 @@ pub async fn fetch_browser_versions_with_count_cached_first( let service_clone = BrowserVersionManager::instance(); let browser_str_clone = browser_str.clone(); tokio::spawn(async move { - if let Err(e) = service_clone.fetch_browser_versions_with_count(&browser_str_clone, false).await { + if let Err(e) = service_clone + .fetch_browser_versions_with_count(&browser_str_clone, false) + .await + { eprintln!("Background version update failed for {browser_str_clone}: {e}"); } }); @@ -1826,7 +2092,10 @@ pub async fn fetch_browser_versions_with_count_cached_first( }) } else { // No cache available, fetch fresh - service.fetch_browser_versions_with_count(&browser_str, false).await.map_err(|e| format!("Failed to fetch browser versions: {e}")) + service + .fetch_browser_versions_with_count(&browser_str, false) + .await + .map_err(|e| format!("Failed to fetch browser versions: {e}")) } } @@ -1837,7 +2106,10 @@ pub async fn download_browser( version: String, ) -> Result { let browser_runner = BrowserRunner::instance(); - browser_runner.download_browser_impl(app_handle, browser_str, version).await.map_err(|e| format!("Failed to download browser: {e}")) + browser_runner + .download_browser_impl(app_handle, browser_str, version) + .await + .map_err(|e| format!("Failed to download browser: {e}")) } #[tauri::command] @@ -1849,7 +2121,9 @@ pub fn is_browser_downloaded(browser_str: String, version: String) -> bool { #[tauri::command] pub fn get_all_tags() -> Result, String> { let browser_runner = BrowserRunner::instance(); - browser_runner.get_all_tags().map_err(|e| format!("Failed to get tags: {e}")) + browser_runner + .get_all_tags() + .map_err(|e| format!("Failed to get tags: {e}")) } #[tauri::command] @@ -1863,13 +2137,22 @@ pub async fn kill_browser_profile( app_handle: tauri::AppHandle, profile: BrowserProfile, ) -> Result<(), String> { - println!("Kill request received for profile: {} (ID: {})", profile.name, profile.id); + println!( + "Kill request received for profile: {} (ID: {})", + profile.name, profile.id + ); let browser_runner = BrowserRunner::instance(); - match browser_runner.kill_browser_process(app_handle.clone(), &profile).await { + match browser_runner + .kill_browser_process(app_handle.clone(), &profile) + .await + { Ok(()) => { - println!("Successfully killed browser profile: {} (ID: {})", profile.name, profile.id); + println!( + "Successfully killed browser profile: {} (ID: {})", + profile.name, profile.id + ); Ok(()) } Err(e) => { @@ -1908,8 +2191,19 @@ pub async fn create_browser_profile_new( camoufox_config: Option, group_id: Option, ) -> Result { - let browser_type = BrowserType::from_str(&browser_str).map_err(|e| format!("Invalid browser type: {e}"))?; - create_browser_profile_with_group(app_handle, name, browser_type.as_str().to_string(), version, release_type, proxy_id, camoufox_config, group_id).await + let browser_type = + BrowserType::from_str(&browser_str).map_err(|e| format!("Invalid browser type: {e}"))?; + create_browser_profile_with_group( + app_handle, + name, + browser_type.as_str().to_string(), + version, + release_type, + proxy_id, + camoufox_config, + group_id, + ) + .await } #[tauri::command] @@ -1919,7 +2213,10 @@ pub async fn update_camoufox_config( config: CamoufoxConfig, ) -> Result<(), String> { let profile_manager = ProfileManager::instance(); - profile_manager.update_camoufox_config(app_handle, &profile_id, config).await.map_err(|e| format!("Failed to update Camoufox config: {e}")) + profile_manager + .update_camoufox_config(app_handle, &profile_id, config) + .await + .map_err(|e| format!("Failed to update Camoufox config: {e}")) } #[tauri::command] @@ -1927,7 +2224,10 @@ pub async fn fetch_browser_versions_with_count( browser_str: String, ) -> Result { let service = BrowserVersionManager::instance(); - service.fetch_browser_versions_with_count(&browser_str, false).await.map_err(|e| format!("Failed to fetch browser versions: {e}")) + service + .fetch_browser_versions_with_count(&browser_str, false) + .await + .map_err(|e| format!("Failed to fetch browser versions: {e}")) } #[tauri::command] @@ -1939,13 +2239,18 @@ pub fn get_downloaded_browser_versions(browser_str: String) -> Result Result, String> { let browser_runner = BrowserRunner::instance(); - browser_runner.check_missing_binaries().await.map_err(|e| format!("Failed to check missing binaries: {e}")) + browser_runner + .check_missing_binaries() + .await + .map_err(|e| format!("Failed to check missing binaries: {e}")) } #[tauri::command] pub async fn check_missing_geoip_database() -> Result { let browser_runner = BrowserRunner::instance(); - browser_runner.check_missing_geoip_database().map_err(|e| format!("Failed to check missing GeoIP database: {e}")) + browser_runner + .check_missing_geoip_database() + .map_err(|e| format!("Failed to check missing GeoIP database: {e}")) } #[tauri::command] @@ -1953,7 +2258,10 @@ pub async fn ensure_all_binaries_exist( app_handle: tauri::AppHandle, ) -> Result, String> { let browser_runner = BrowserRunner::instance(); - browser_runner.ensure_all_binaries_exist(&app_handle).await.map_err(|e| format!("Failed to ensure all binaries exist: {e}")) + browser_runner + .ensure_all_binaries_exist(&app_handle) + .await + .map_err(|e| format!("Failed to ensure all binaries exist: {e}")) } #[cfg(test)] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2f84cee..74d6ae2 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -503,7 +503,7 @@ pub fn run() { let profiles = match runner.list_profiles() { Ok(p) => p, Err(e) => { - println!("Warning: Failed to list profiles in status checker: {}", e); + println!("Warning: Failed to list profiles in status checker: {e}"); continue; } }; diff --git a/src-tauri/src/platform_browser.rs b/src-tauri/src/platform_browser.rs index 1483927..7dd7a43 100644 --- a/src-tauri/src/platform_browser.rs +++ b/src-tauri/src/platform_browser.rs @@ -221,7 +221,7 @@ end try // For Chromium-based browsers, use immediate aggressive termination // Chromium browsers are notoriously difficult to kill on macOS due to process spawning - + // Step 1: Immediate SIGKILL on main process (no graceful shutdown for Chromium) println!("Starting immediate SIGKILL for PID: {pid}"); let _ = Command::new("kill") @@ -236,13 +236,13 @@ end try // Step 3: Use multiple kill strategies in parallel let pid_str = pid.to_string(); - + // Kill by parent PID with SIGKILL let _ = Command::new("pkill") .args(["-KILL", "-P", &pid_str]) .output(); - - // Kill by process group with SIGKILL + + // Kill by process group with SIGKILL let _ = Command::new("pkill") .args(["-KILL", "-g", &pid_str]) .output(); @@ -258,17 +258,25 @@ end try // Step 4: Verify and retry with pattern-based killing for common Chromium process names use sysinfo::{Pid, System}; let system = System::new_all(); - + // Check if main process still exists if system.process(Pid::from(pid as usize)).is_some() { println!("Main process {pid} still running, using pattern-based termination"); - + // Kill by common Chromium process patterns let chromium_patterns = [ - "Chrome", "Chromium", "Brave", "chrome", "chromium", "brave", - "Google Chrome", "Brave Browser", "Chrome Helper", "Chromium Helper" + "Chrome", + "Chromium", + "Brave", + "chrome", + "chromium", + "brave", + "Google Chrome", + "Brave Browser", + "Chrome Helper", + "Chromium Helper", ]; - + for pattern in &chromium_patterns { let _ = Command::new("pkill") .args(["-KILL", "-f", pattern]) @@ -278,12 +286,12 @@ end try // Step 5: Final aggressive cleanup - kill any remaining processes tokio::time::sleep(tokio::time::Duration::from_millis(300)).await; - + // One more round of comprehensive killing let _ = Command::new("pkill") .args(["-KILL", "-P", &pid_str]) .output(); - + let _ = Command::new("pkill") .args(["-KILL", "-g", &pid_str]) .output(); @@ -291,30 +299,28 @@ end try // Final verification with extended wait tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; let system = System::new_all(); - + if system.process(Pid::from(pid as usize)).is_some() { // Last resort: try system kill command with different signals println!("Process {pid} extremely persistent, trying system-level termination"); - - let _ = Command::new("/bin/kill") - .args(["-KILL", &pid_str]) - .output(); - + + let _ = Command::new("/bin/kill").args(["-KILL", &pid_str]).output(); + let _ = Command::new("/usr/bin/killall") .args(["-KILL", "-m", "Chrome"]) .output(); - + let _ = Command::new("/usr/bin/killall") - .args(["-KILL", "-m", "Chromium"]) + .args(["-KILL", "-m", "Chromium"]) .output(); - + let _ = Command::new("/usr/bin/killall") .args(["-KILL", "-m", "Brave"]) .output(); tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; let system = System::new_all(); - + if system.process(Pid::from(pid as usize)).is_some() { println!("WARNING: Process {pid} could not be terminated despite aggressive attempts"); // Don't return error - let the UI update anyway since we tried everything @@ -325,95 +331,109 @@ end try Ok(()) } -// Helper function to kill process tree (Chromium browsers often spawn child processes) -async fn kill_chromium_process_tree_aggressive(pid: u32) -> Result<(), Box> { - println!("Killing comprehensive process tree for PID: {pid}"); - - // Get all descendant processes using recursive process tree discovery - let descendant_pids = get_all_descendant_pids(pid).await; - println!("Found {} descendant processes to terminate", descendant_pids.len()); - - // Kill all descendants first (reverse order - children before parents) - for &desc_pid in descendant_pids.iter().rev() { - if desc_pid != pid { - println!("Terminating descendant process: {desc_pid}"); - let _ = Command::new("kill") - .args(["-KILL", &desc_pid.to_string()]) - .output(); - } - } - - // No delay for initial termination - - // Force kill any remaining descendants - for &desc_pid in descendant_pids.iter().rev() { - if desc_pid != pid { - let _ = Command::new("kill") - .args(["-KILL", &desc_pid.to_string()]) - .output(); - } - } - - // Also use pkill as a backup to catch any processes we might have missed - let _ = Command::new("pkill") - .args(["-KILL", "-P", &pid.to_string()]) - .output(); - - // On macOS, also try killing by process group for Chromium browsers - let _ = Command::new("pkill") - .args(["-KILL", "-g", &pid.to_string()]) - .output(); - - Ok(()) -} + // Helper function to kill process tree (Chromium browsers often spawn child processes) + async fn kill_chromium_process_tree_aggressive( + pid: u32, + ) -> Result<(), Box> { + println!("Killing comprehensive process tree for PID: {pid}"); -// Helper function to kill all Chromium-related processes by name patterns -async fn kill_all_chromium_processes_by_name() -> Result<(), Box> { - println!("Killing all Chromium-related processes by name patterns"); - - let chromium_patterns = [ - "Chrome", "Chromium", "Brave", "chrome", "chromium", "brave", - "Google Chrome", "Brave Browser", "Chrome Helper", "Chromium Helper" - ]; - - for pattern in &chromium_patterns { + // Get all descendant processes using recursive process tree discovery + let descendant_pids = get_all_descendant_pids(pid).await; + println!( + "Found {} descendant processes to terminate", + descendant_pids.len() + ); + + // Kill all descendants first (reverse order - children before parents) + for &desc_pid in descendant_pids.iter().rev() { + if desc_pid != pid { + println!("Terminating descendant process: {desc_pid}"); + let _ = Command::new("kill") + .args(["-KILL", &desc_pid.to_string()]) + .output(); + } + } + + // No delay for initial termination + + // Force kill any remaining descendants + for &desc_pid in descendant_pids.iter().rev() { + if desc_pid != pid { + let _ = Command::new("kill") + .args(["-KILL", &desc_pid.to_string()]) + .output(); + } + } + + // Also use pkill as a backup to catch any processes we might have missed let _ = Command::new("pkill") - .args(["-KILL", "-f", pattern]) + .args(["-KILL", "-P", &pid.to_string()]) .output(); - } - - Ok(()) -} -// Recursively find all descendant processes -async fn get_all_descendant_pids(parent_pid: u32) -> Vec { - use sysinfo::{Pid, System}; - - let system = System::new_all(); - let mut descendants = Vec::new(); - let mut to_check = vec![parent_pid]; - let mut checked = std::collections::HashSet::new(); - - while let Some(current_pid) = to_check.pop() { - if checked.contains(¤t_pid) { - continue; + // On macOS, also try killing by process group for Chromium browsers + let _ = Command::new("pkill") + .args(["-KILL", "-g", &pid.to_string()]) + .output(); + + Ok(()) + } + + // Helper function to kill all Chromium-related processes by name patterns + async fn kill_all_chromium_processes_by_name( + ) -> Result<(), Box> { + println!("Killing all Chromium-related processes by name patterns"); + + let chromium_patterns = [ + "Chrome", + "Chromium", + "Brave", + "chrome", + "chromium", + "brave", + "Google Chrome", + "Brave Browser", + "Chrome Helper", + "Chromium Helper", + ]; + + for pattern in &chromium_patterns { + let _ = Command::new("pkill") + .args(["-KILL", "-f", pattern]) + .output(); } - checked.insert(current_pid); - - // Find direct children of current_pid - for (pid, process) in system.processes() { - let pid_u32 = pid.as_u32(); - if let Some(parent) = process.parent() { - if parent.as_u32() == current_pid && !checked.contains(&pid_u32) { - descendants.push(pid_u32); - to_check.push(pid_u32); + + Ok(()) + } + + // Recursively find all descendant processes + async fn get_all_descendant_pids(parent_pid: u32) -> Vec { + use sysinfo::System; + + let system = System::new_all(); + let mut descendants = Vec::new(); + let mut to_check = vec![parent_pid]; + let mut checked = std::collections::HashSet::new(); + + while let Some(current_pid) = to_check.pop() { + if checked.contains(¤t_pid) { + continue; + } + checked.insert(current_pid); + + // Find direct children of current_pid + for (pid, process) in system.processes() { + let pid_u32 = pid.as_u32(); + if let Some(parent) = process.parent() { + if parent.as_u32() == current_pid && !checked.contains(&pid_u32) { + descendants.push(pid_u32); + to_check.push(pid_u32); + } } } } + + descendants } - - descendants -} pub async fn open_url_in_existing_browser_tor_mullvad( profile: &BrowserProfile,