mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-04-22 03:46:43 +02:00
refactor: remove mullvad and tor browser artifacts
This commit is contained in:
Vendored
-3
@@ -105,8 +105,6 @@
|
||||
"mstone",
|
||||
"msvc",
|
||||
"msys",
|
||||
"Mullvad",
|
||||
"mullvadbrowser",
|
||||
"mypy",
|
||||
"noarchive",
|
||||
"nobrowse",
|
||||
@@ -188,7 +186,6 @@
|
||||
"timedatectl",
|
||||
"titlebar",
|
||||
"tkinter",
|
||||
"Torbrowser",
|
||||
"tqdm",
|
||||
"trackingprotection",
|
||||
"trailhead",
|
||||
|
||||
@@ -292,7 +292,6 @@ pub fn is_browser_version_nightly(
|
||||
// This will be handled in the API parsing, so this fallback is for cached versions
|
||||
is_nightly_version(version)
|
||||
}
|
||||
"mullvad-browser" | "tor-browser" => is_nightly_version(version),
|
||||
"chromium" => {
|
||||
// Chromium builds are generally stable snapshots
|
||||
false
|
||||
@@ -349,7 +348,6 @@ pub struct ApiClient {
|
||||
firefox_dev_api_base: String,
|
||||
github_api_base: String,
|
||||
chromium_api_base: String,
|
||||
tor_archive_base: String,
|
||||
}
|
||||
|
||||
impl ApiClient {
|
||||
@@ -366,7 +364,6 @@ impl ApiClient {
|
||||
github_api_base: "https://api.github.com".to_string(),
|
||||
chromium_api_base: "https://commondatastorage.googleapis.com/chromium-browser-snapshots"
|
||||
.to_string(),
|
||||
tor_archive_base: "https://archive.torproject.org/tor-package-archive/torbrowser".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,7 +436,6 @@ impl ApiClient {
|
||||
firefox_dev_api_base: String,
|
||||
github_api_base: String,
|
||||
chromium_api_base: String,
|
||||
tor_archive_base: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
client: Client::new(),
|
||||
@@ -723,45 +719,6 @@ impl ApiClient {
|
||||
Ok(releases)
|
||||
}
|
||||
|
||||
pub async fn fetch_mullvad_releases_with_caching(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
) -> Result<Vec<GithubRelease>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// Check cache first (unless bypassing)
|
||||
if !no_caching {
|
||||
if let Some(cached_releases) = self.load_cached_github_releases("mullvad") {
|
||||
return Ok(cached_releases);
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Fetching Mullvad releases from GitHub API");
|
||||
let base_url = format!(
|
||||
"{}/repos/mullvad/mullvad-browser/releases",
|
||||
self.github_api_base
|
||||
);
|
||||
let releases = self.fetch_github_releases_multiple_pages(&base_url).await?;
|
||||
|
||||
let mut releases: Vec<GithubRelease> = releases
|
||||
.into_iter()
|
||||
.map(|mut release| {
|
||||
release.is_nightly = release.prerelease;
|
||||
release
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort releases using the new version sorting system
|
||||
sort_github_releases(&mut releases);
|
||||
|
||||
// Cache the results (unless bypassing cache)
|
||||
if !no_caching {
|
||||
if let Err(e) = self.save_cached_github_releases("mullvad", &releases) {
|
||||
log::error!("Failed to cache Mullvad releases: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(releases)
|
||||
}
|
||||
|
||||
pub async fn fetch_zen_releases_with_caching(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
@@ -1102,107 +1059,6 @@ impl ApiClient {
|
||||
Ok(compatible_releases)
|
||||
}
|
||||
|
||||
pub async fn fetch_tor_releases_with_caching(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
) -> Result<Vec<BrowserRelease>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
// Check cache first (unless bypassing)
|
||||
if !no_caching {
|
||||
if let Some(cached_releases) = self.load_cached_versions("tor-browser") {
|
||||
return Ok(cached_releases);
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Fetching TOR releases from archive...");
|
||||
let url = format!("{}/", self.tor_archive_base);
|
||||
let html = self
|
||||
.client
|
||||
.get(url)
|
||||
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36")
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
// Parse HTML to extract version directories
|
||||
let mut version_candidates = Vec::new();
|
||||
|
||||
// Look for directory links in the HTML
|
||||
for line in html.lines() {
|
||||
if line.contains("<a href=\"") && line.contains("/\">") {
|
||||
// Extract the directory name from the href attribute
|
||||
if let Some(start) = line.find("<a href=\"") {
|
||||
let start = start + 9; // Length of "<a href=\""
|
||||
if let Some(end) = line[start..].find("/\">") {
|
||||
let version = &line[start..start + end];
|
||||
|
||||
// Skip parent directory and non-version entries
|
||||
if version != ".."
|
||||
&& !version.is_empty()
|
||||
&& version.chars().next().unwrap_or('a').is_ascii_digit()
|
||||
{
|
||||
version_candidates.push(version.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort version candidates using the new version sorting system
|
||||
sort_versions(&mut version_candidates);
|
||||
|
||||
// Only check the first 10 versions to avoid being too slow
|
||||
let mut version_strings = Vec::new();
|
||||
for version in version_candidates.into_iter().take(10) {
|
||||
// Check if this version has a macOS DMG file
|
||||
if let Ok(has_macos) = self.check_tor_version_has_macos(&version).await {
|
||||
if has_macos {
|
||||
version_strings.push(version);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a small delay to avoid overwhelming the server
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
// Convert to BrowserRelease objects
|
||||
let releases: Vec<BrowserRelease> = version_strings
|
||||
.into_iter()
|
||||
.map(|version| BrowserRelease {
|
||||
version: version.clone(),
|
||||
date: "".to_string(), // TOR archive doesn't provide structured dates
|
||||
is_prerelease: false, // Assume all archived versions are stable
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Cache the results (unless bypassing cache)
|
||||
if !no_caching {
|
||||
if let Err(e) = self.save_cached_versions("tor-browser", &releases) {
|
||||
log::error!("Failed to cache TOR versions: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(releases)
|
||||
}
|
||||
|
||||
async fn check_tor_version_has_macos(
|
||||
&self,
|
||||
version: &str,
|
||||
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let url = format!("{}/{version}/", self.tor_archive_base);
|
||||
let html = self
|
||||
.client
|
||||
.get(&url)
|
||||
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36")
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
// Check if there's a macOS DMG file in this version directory
|
||||
Ok(html.contains("tor-browser-macos-") && html.contains(".dmg"))
|
||||
}
|
||||
|
||||
/// Check if a Zen twilight release has been updated by comparing file size
|
||||
pub async fn check_twilight_update(
|
||||
&self,
|
||||
@@ -1302,7 +1158,6 @@ mod tests {
|
||||
base_url.clone(), // firefox_dev_api_base
|
||||
base_url.clone(), // github_api_base
|
||||
base_url.clone(), // chromium_api_base
|
||||
base_url.clone(), // tor_archive_base
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1526,47 +1381,6 @@ mod tests {
|
||||
assert_eq!(releases[0].version, "140.0b1");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mullvad_api() {
|
||||
let server = setup_mock_server().await;
|
||||
let client = create_test_client(&server);
|
||||
|
||||
let mock_response = r#"[
|
||||
{
|
||||
"tag_name": "14.5a6",
|
||||
"name": "Mullvad Browser 14.5a6",
|
||||
"prerelease": true,
|
||||
"published_at": "2024-01-15T10:00:00Z",
|
||||
"assets": [
|
||||
{
|
||||
"name": "mullvad-browser-macos-14.5a6.dmg",
|
||||
"browser_download_url": "https://example.com/mullvad-14.5a6.dmg",
|
||||
"size": 100000000
|
||||
}
|
||||
]
|
||||
}
|
||||
]"#;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/repos/mullvad/mullvad-browser/releases"))
|
||||
.and(query_param("per_page", "100"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_string(mock_response)
|
||||
.insert_header("content-type", "application/json"),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let result = client.fetch_mullvad_releases_with_caching(true).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let releases = result.unwrap();
|
||||
assert!(!releases.is_empty());
|
||||
assert_eq!(releases[0].tag_name, "14.5a6");
|
||||
assert!(releases[0].is_nightly);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_zen_api() {
|
||||
let server = setup_mock_server().await;
|
||||
@@ -1720,125 +1534,6 @@ mod tests {
|
||||
assert!(!releases[0].is_prerelease);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tor_api() {
|
||||
let server = setup_mock_server().await;
|
||||
let client = create_test_client(&server);
|
||||
|
||||
let mock_html = r#"
|
||||
<html>
|
||||
<body>
|
||||
<a href="../">../</a>
|
||||
<a href="14.0.4/">14.0.4/</a>
|
||||
<a href="14.0.3/">14.0.3/</a>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
|
||||
let version_html = r#"
|
||||
<html>
|
||||
<body>
|
||||
<a href="tor-browser-macos-14.0.4.dmg">tor-browser-macos-14.0.4.dmg</a>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_string(mock_html)
|
||||
.insert_header("content-type", "text/html"),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/14.0.4/"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_string(version_html)
|
||||
.insert_header("content-type", "text/html"),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/14.0.3/"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_string(version_html.replace("14.0.4", "14.0.3"))
|
||||
.insert_header("content-type", "text/html"),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let result = client.fetch_tor_releases_with_caching(true).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let releases = result.unwrap();
|
||||
assert!(!releases.is_empty());
|
||||
assert_eq!(releases[0].version, "14.0.4");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tor_version_check() {
|
||||
let server = setup_mock_server().await;
|
||||
let client = create_test_client(&server);
|
||||
|
||||
let version_html = r#"
|
||||
<html>
|
||||
<body>
|
||||
<a href="tor-browser-macos-14.0.4.dmg">tor-browser-macos-14.0.4.dmg</a>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/14.0.4/"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_string(version_html)
|
||||
.insert_header("content-type", "text/html"),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let result = client.check_tor_version_has_macos("14.0.4").await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
assert!(result.unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tor_version_check_no_macos() {
|
||||
let server = setup_mock_server().await;
|
||||
let client = create_test_client(&server);
|
||||
|
||||
let version_html = r#"
|
||||
<html>
|
||||
<body>
|
||||
<a href="tor-browser-linux-14.0.4.tar.xz">tor-browser-linux-14.0.4.tar.xz</a>
|
||||
</body>
|
||||
</html>
|
||||
"#;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/14.0.5/"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_string(version_html)
|
||||
.insert_header("content-type", "text/html"),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let result = client.check_tor_version_has_macos("14.0.5").await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
assert!(!result.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_nightly_version() {
|
||||
assert!(is_nightly_version("1.2.3a1"));
|
||||
@@ -1910,84 +1605,6 @@ mod tests {
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mullvad_pagination_two_pages() {
|
||||
let server = setup_mock_server().await;
|
||||
let client = create_test_client(&server);
|
||||
|
||||
// Page 1 response with Link: rel="next" header
|
||||
let mock_page1 = r#"[
|
||||
{
|
||||
"tag_name": "100.0",
|
||||
"name": "Mullvad Browser 100.0",
|
||||
"prerelease": false,
|
||||
"published_at": "2024-07-01T00:00:00Z",
|
||||
"assets": [
|
||||
{ "name": "mullvad-browser-macos-100.0.dmg", "browser_download_url": "https://example.com/100.0.dmg", "size": 1 }
|
||||
]
|
||||
}
|
||||
]"#;
|
||||
|
||||
// Page 2 response
|
||||
let mock_page2 = r#"[
|
||||
{
|
||||
"tag_name": "99.0",
|
||||
"name": "Mullvad Browser 99.0",
|
||||
"prerelease": false,
|
||||
"published_at": "2024-06-01T00:00:00Z",
|
||||
"assets": [
|
||||
{ "name": "mullvad-browser-macos-99.0.dmg", "browser_download_url": "https://example.com/99.0.dmg", "size": 1 }
|
||||
]
|
||||
}
|
||||
]"#;
|
||||
|
||||
// Mock page 1
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/repos/mullvad/mullvad-browser/releases"))
|
||||
.and(query_param("per_page", "100"))
|
||||
.and(query_param("page", "1"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_string(mock_page1)
|
||||
.insert_header("content-type", "application/json")
|
||||
.insert_header(
|
||||
"link",
|
||||
format!(
|
||||
"<{}?per_page=100&page=2>; rel=\"next\", <{}?per_page=100&page=2>; rel=\"last\"",
|
||||
server.uri().to_string() + "/repos/mullvad/mullvad-browser/releases",
|
||||
server.uri().to_string() + "/repos/mullvad/mullvad-browser/releases"
|
||||
),
|
||||
),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
// Mock page 2
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/repos/mullvad/mullvad-browser/releases"))
|
||||
.and(query_param("per_page", "100"))
|
||||
.and(query_param("page", "2"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.set_body_string(mock_page2)
|
||||
.insert_header("content-type", "application/json"),
|
||||
)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let result = client.fetch_mullvad_releases_with_caching(true).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let releases = result.unwrap();
|
||||
// We currently only fetch 1 page intentionally; ensure we at least got page 1
|
||||
assert_eq!(
|
||||
releases.len(),
|
||||
1,
|
||||
"Should fetch only the first page of results"
|
||||
);
|
||||
assert_eq!(releases[0].tag_name, "100.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_camoufox_beta_version_parsing() {
|
||||
// Test specific Camoufox beta versions that are causing issues
|
||||
|
||||
@@ -13,39 +13,33 @@ pub struct ProxySettings {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum BrowserType {
|
||||
MullvadBrowser,
|
||||
Chromium,
|
||||
Firefox,
|
||||
FirefoxDeveloper,
|
||||
Brave,
|
||||
Zen,
|
||||
TorBrowser,
|
||||
Camoufox,
|
||||
}
|
||||
|
||||
impl BrowserType {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
BrowserType::MullvadBrowser => "mullvad-browser",
|
||||
BrowserType::Chromium => "chromium",
|
||||
BrowserType::Firefox => "firefox",
|
||||
BrowserType::FirefoxDeveloper => "firefox-developer",
|
||||
BrowserType::Brave => "brave",
|
||||
BrowserType::Zen => "zen",
|
||||
BrowserType::TorBrowser => "tor-browser",
|
||||
BrowserType::Camoufox => "camoufox",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str) -> Result<Self, String> {
|
||||
match s {
|
||||
"mullvad-browser" => Ok(BrowserType::MullvadBrowser),
|
||||
"chromium" => Ok(BrowserType::Chromium),
|
||||
"firefox" => Ok(BrowserType::Firefox),
|
||||
"firefox-developer" => Ok(BrowserType::FirefoxDeveloper),
|
||||
"brave" => Ok(BrowserType::Brave),
|
||||
"zen" => Ok(BrowserType::Zen),
|
||||
"tor-browser" => Ok(BrowserType::TorBrowser),
|
||||
"camoufox" => Ok(BrowserType::Camoufox),
|
||||
_ => Err(format!("Unknown browser type: {s}")),
|
||||
}
|
||||
@@ -92,9 +86,7 @@ mod macos {
|
||||
let binding = entry.file_name();
|
||||
let name = binding.to_string_lossy();
|
||||
name.starts_with("firefox")
|
||||
|| name.starts_with("mullvad")
|
||||
|| name.starts_with("zen")
|
||||
|| name.starts_with("tor")
|
||||
|| name.starts_with("camoufox")
|
||||
|| name.contains("Browser")
|
||||
})
|
||||
@@ -190,28 +182,9 @@ mod linux {
|
||||
browser_subdir.join("firefox"),
|
||||
browser_subdir.join("firefox-bin"),
|
||||
],
|
||||
BrowserType::MullvadBrowser => {
|
||||
vec![
|
||||
browser_subdir.join("firefox"),
|
||||
browser_subdir.join("mullvad-browser"),
|
||||
browser_subdir.join("firefox-bin"),
|
||||
]
|
||||
}
|
||||
BrowserType::Zen => {
|
||||
vec![browser_subdir.join("zen"), browser_subdir.join("zen-bin")]
|
||||
}
|
||||
BrowserType::TorBrowser => {
|
||||
vec![
|
||||
// Common Tor Browser launchers
|
||||
browser_subdir.join("tor-browser"),
|
||||
// Firefox-based binaries
|
||||
browser_subdir.join("firefox"),
|
||||
browser_subdir.join("firefox-bin"),
|
||||
// Sometimes packaged similarly to Firefox
|
||||
install_dir.join("firefox").join("firefox"),
|
||||
install_dir.join("firefox").join("firefox-bin"),
|
||||
]
|
||||
}
|
||||
BrowserType::Camoufox => {
|
||||
vec![
|
||||
install_dir.join("camoufox-bin"),
|
||||
@@ -303,23 +276,9 @@ mod linux {
|
||||
install_dir.join("firefox").join("firefox"),
|
||||
]
|
||||
}
|
||||
BrowserType::MullvadBrowser => {
|
||||
vec![
|
||||
browser_subdir.join("mullvad-browser"),
|
||||
browser_subdir.join("firefox-bin"),
|
||||
browser_subdir.join("firefox"),
|
||||
]
|
||||
}
|
||||
BrowserType::Zen => {
|
||||
vec![browser_subdir.join("zen"), browser_subdir.join("zen-bin")]
|
||||
}
|
||||
BrowserType::TorBrowser => {
|
||||
vec![
|
||||
browser_subdir.join("tor-browser"),
|
||||
browser_subdir.join("firefox-bin"),
|
||||
browser_subdir.join("firefox"),
|
||||
]
|
||||
}
|
||||
BrowserType::Camoufox => {
|
||||
vec![
|
||||
install_dir.join("camoufox-bin"),
|
||||
@@ -424,9 +383,7 @@ mod windows {
|
||||
if path.extension().is_some_and(|ext| ext == "exe") {
|
||||
let name = path.file_stem().unwrap_or_default().to_string_lossy();
|
||||
if name.starts_with("firefox")
|
||||
|| name.starts_with("mullvad")
|
||||
|| name.starts_with("zen")
|
||||
|| name.starts_with("tor")
|
||||
|| name.starts_with("camoufox")
|
||||
|| name.contains("browser")
|
||||
{
|
||||
@@ -510,9 +467,7 @@ mod windows {
|
||||
if path.extension().is_some_and(|ext| ext == "exe") {
|
||||
let name = path.file_stem().unwrap_or_default().to_string_lossy();
|
||||
if name.starts_with("firefox")
|
||||
|| name.starts_with("mullvad")
|
||||
|| name.starts_with("zen")
|
||||
|| name.starts_with("tor")
|
||||
|| name.starts_with("camoufox")
|
||||
|| name.contains("browser")
|
||||
{
|
||||
@@ -903,11 +858,9 @@ impl BrowserFactory {
|
||||
|
||||
pub fn create_browser(&self, browser_type: BrowserType) -> Box<dyn Browser> {
|
||||
match browser_type {
|
||||
BrowserType::MullvadBrowser
|
||||
| BrowserType::Firefox
|
||||
| BrowserType::FirefoxDeveloper
|
||||
| BrowserType::Zen
|
||||
| BrowserType::TorBrowser => Box::new(FirefoxBrowser::new(browser_type)),
|
||||
BrowserType::Firefox | BrowserType::FirefoxDeveloper | BrowserType::Zen => {
|
||||
Box::new(FirefoxBrowser::new(browser_type))
|
||||
}
|
||||
BrowserType::Chromium | BrowserType::Brave => Box::new(ChromiumBrowser::new(browser_type)),
|
||||
BrowserType::Camoufox => Box::new(CamoufoxBrowser::new()),
|
||||
}
|
||||
@@ -985,20 +938,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_browser_type_conversions() {
|
||||
// Test as_str
|
||||
assert_eq!(BrowserType::MullvadBrowser.as_str(), "mullvad-browser");
|
||||
assert_eq!(BrowserType::Firefox.as_str(), "firefox");
|
||||
assert_eq!(BrowserType::FirefoxDeveloper.as_str(), "firefox-developer");
|
||||
assert_eq!(BrowserType::Chromium.as_str(), "chromium");
|
||||
assert_eq!(BrowserType::Brave.as_str(), "brave");
|
||||
assert_eq!(BrowserType::Zen.as_str(), "zen");
|
||||
assert_eq!(BrowserType::TorBrowser.as_str(), "tor-browser");
|
||||
assert_eq!(BrowserType::Camoufox.as_str(), "camoufox");
|
||||
|
||||
// Test from_str - use expect with descriptive messages instead of unwrap
|
||||
assert_eq!(
|
||||
BrowserType::from_str("mullvad-browser").expect("mullvad-browser should be valid"),
|
||||
BrowserType::MullvadBrowser
|
||||
);
|
||||
assert_eq!(
|
||||
BrowserType::from_str("firefox").expect("firefox should be valid"),
|
||||
BrowserType::Firefox
|
||||
@@ -1019,10 +966,6 @@ mod tests {
|
||||
BrowserType::from_str("zen").expect("zen should be valid"),
|
||||
BrowserType::Zen
|
||||
);
|
||||
assert_eq!(
|
||||
BrowserType::from_str("tor-browser").expect("tor-browser should be valid"),
|
||||
BrowserType::TorBrowser
|
||||
);
|
||||
assert_eq!(
|
||||
BrowserType::from_str("camoufox").expect("camoufox should be valid"),
|
||||
BrowserType::Camoufox
|
||||
@@ -1089,20 +1032,6 @@ mod tests {
|
||||
"Firefox should include debugging port"
|
||||
);
|
||||
|
||||
// Test Mullvad Browser (no special flags without remote debugging)
|
||||
let browser = FirefoxBrowser::new(BrowserType::MullvadBrowser);
|
||||
let args = browser
|
||||
.create_launch_args("/path/to/profile", None, None, None, false)
|
||||
.expect("Failed to create launch args for Mullvad Browser");
|
||||
assert_eq!(args, vec!["-profile", "/path/to/profile"]);
|
||||
|
||||
// Test Tor Browser (no special flags without remote debugging)
|
||||
let browser = FirefoxBrowser::new(BrowserType::TorBrowser);
|
||||
let args = browser
|
||||
.create_launch_args("/path/to/profile", None, None, None, false)
|
||||
.expect("Failed to create launch args for Tor Browser");
|
||||
assert_eq!(args, vec!["-profile", "/path/to/profile"]);
|
||||
|
||||
// Test Zen Browser (no special flags without remote debugging)
|
||||
let browser = FirefoxBrowser::new(BrowserType::Zen);
|
||||
let args = browser
|
||||
|
||||
+24
-147
@@ -33,29 +33,6 @@ impl BrowserRunner {
|
||||
&BROWSER_RUNNER
|
||||
}
|
||||
|
||||
// Helper function to check if a process matches TOR/Mullvad browser
|
||||
fn is_tor_or_mullvad_browser(
|
||||
&self,
|
||||
exe_name: &str,
|
||||
cmd: &[std::ffi::OsString],
|
||||
browser_type: &str,
|
||||
) -> bool {
|
||||
#[cfg(target_os = "macos")]
|
||||
return platform_browser::macos::is_tor_or_mullvad_browser(exe_name, cmd, browser_type);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
return platform_browser::windows::is_tor_or_mullvad_browser(exe_name, cmd, browser_type);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
return platform_browser::linux::is_tor_or_mullvad_browser(exe_name, cmd, browser_type);
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
{
|
||||
let _ = (exe_name, cmd, browser_type);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_binaries_dir(&self) -> PathBuf {
|
||||
let mut path = self.base_dirs.data_local_dir().to_path_buf();
|
||||
path.push(if cfg!(debug_assertions) {
|
||||
@@ -395,42 +372,8 @@ impl BrowserRunner {
|
||||
profile.id
|
||||
);
|
||||
|
||||
// For TOR and Mullvad browsers, we need to find the actual browser process
|
||||
// because they use launcher scripts that spawn the real browser process
|
||||
let mut actual_pid = launcher_pid;
|
||||
|
||||
if matches!(
|
||||
browser_type,
|
||||
BrowserType::TorBrowser | BrowserType::MullvadBrowser
|
||||
) {
|
||||
// Wait a moment for the actual browser process to start
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
|
||||
|
||||
// Find the actual browser process
|
||||
let system = System::new_all();
|
||||
for (pid, process) in system.processes() {
|
||||
let process_name = process.name().to_str().unwrap_or("");
|
||||
let process_cmd = process.cmd();
|
||||
let pid_u32 = pid.as_u32();
|
||||
|
||||
// Skip if this is the launcher process itself
|
||||
if pid_u32 == launcher_pid {
|
||||
continue;
|
||||
}
|
||||
|
||||
if self.is_tor_or_mullvad_browser(process_name, process_cmd, &profile.browser) {
|
||||
log::info!(
|
||||
"Found actual {} browser process: PID {} ({})",
|
||||
profile.browser,
|
||||
pid_u32,
|
||||
process_name
|
||||
);
|
||||
actual_pid = pid_u32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On macOS, when launching via `open -a`, the child PID is the `open` helper.
|
||||
// Resolve and store the actual browser PID for all browser types.
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -455,8 +398,6 @@ impl BrowserRunner {
|
||||
"firefox" => {
|
||||
exe_name_lower.contains("firefox")
|
||||
&& !exe_name_lower.contains("developer")
|
||||
&& !exe_name_lower.contains("tor")
|
||||
&& !exe_name_lower.contains("mullvad")
|
||||
&& !exe_name_lower.contains("camoufox")
|
||||
}
|
||||
"firefox-developer" => {
|
||||
@@ -472,10 +413,6 @@ impl BrowserRunner {
|
||||
}))
|
||||
|| 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")
|
||||
}
|
||||
"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"),
|
||||
"brave" => exe_name_lower.contains("brave") || exe_name_lower.contains("Brave"),
|
||||
@@ -489,7 +426,7 @@ 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"
|
||||
"firefox" | "firefox-developer" | "zen"
|
||||
) {
|
||||
// Firefox-based browsers: look for -profile argument followed by path
|
||||
let mut found_profile_arg = false;
|
||||
@@ -552,11 +489,7 @@ impl BrowserRunner {
|
||||
if profile.proxy_id.is_some()
|
||||
&& matches!(
|
||||
browser_type,
|
||||
BrowserType::Firefox
|
||||
| BrowserType::FirefoxDeveloper
|
||||
| BrowserType::Zen
|
||||
| BrowserType::TorBrowser
|
||||
| BrowserType::MullvadBrowser
|
||||
BrowserType::Firefox | BrowserType::FirefoxDeveloper | BrowserType::Zen
|
||||
)
|
||||
{
|
||||
// Proxy settings for Firefox-based browsers are applied via user.js file
|
||||
@@ -714,48 +647,9 @@ impl BrowserRunner {
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
return Err("Unsupported platform".into());
|
||||
}
|
||||
BrowserType::MullvadBrowser | BrowserType::TorBrowser => {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let profiles_dir = self.profile_manager.get_profiles_dir();
|
||||
return platform_browser::macos::open_url_in_existing_browser_tor_mullvad(
|
||||
&updated_profile,
|
||||
url,
|
||||
browser_type,
|
||||
&browser_dir,
|
||||
&profiles_dir,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let profiles_dir = self.profile_manager.get_profiles_dir();
|
||||
return platform_browser::windows::open_url_in_existing_browser_tor_mullvad(
|
||||
&updated_profile,
|
||||
url,
|
||||
browser_type,
|
||||
&browser_dir,
|
||||
&profiles_dir,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let profiles_dir = self.profile_manager.get_profiles_dir();
|
||||
return platform_browser::linux::open_url_in_existing_browser_tor_mullvad(
|
||||
&updated_profile,
|
||||
url,
|
||||
browser_type,
|
||||
&browser_dir,
|
||||
&profiles_dir,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
return Err("Unsupported platform".into());
|
||||
BrowserType::Camoufox => {
|
||||
// Camoufox uses nodecar for launching, URL opening is handled differently
|
||||
return Err("URL opening in existing Camoufox instance is not supported".into());
|
||||
}
|
||||
BrowserType::Chromium | BrowserType::Brave => {
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -800,10 +694,6 @@ impl BrowserRunner {
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
return Err("Unsupported platform".into());
|
||||
}
|
||||
BrowserType::Camoufox => {
|
||||
// This should never be reached due to the early return above, but handle it just in case
|
||||
Err("Camoufox URL opening should be handled in the early return above".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,7 +738,7 @@ impl BrowserRunner {
|
||||
// For Firefox-based browsers, apply PAC/user.js to point to the local proxy
|
||||
if matches!(
|
||||
profile.browser.as_str(),
|
||||
"firefox" | "firefox-developer" | "zen" | "tor-browser" | "mullvad-browser"
|
||||
"firefox" | "firefox-developer" | "zen"
|
||||
) {
|
||||
let profiles_dir = self.profile_manager.get_profiles_dir();
|
||||
let profile_path = profiles_dir.join(profile.id.to_string()).join("profile");
|
||||
@@ -949,19 +839,6 @@ impl BrowserRunner {
|
||||
if let Some(url_ref) = url.as_ref() {
|
||||
log::info!("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"
|
||||
) {
|
||||
log::info!("TOR/Mullvad browser detected - ensuring we have correct PID");
|
||||
if final_profile.process_id.is_none() {
|
||||
log::info!(
|
||||
"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(),
|
||||
@@ -978,16 +855,24 @@ impl BrowserRunner {
|
||||
Err(e) => {
|
||||
log::info!("Failed to open URL in existing browser: {e}");
|
||||
|
||||
// For Mullvad and Tor browsers, don't fall back to new instance since they use -no-remote
|
||||
// and can't have multiple instances with the same profile
|
||||
// Fall back to launching a new instance
|
||||
match final_profile.browser.as_str() {
|
||||
"mullvad-browser" | "tor-browser" => {
|
||||
Err(format!("Failed to open URL in existing {} browser. Cannot launch new instance due to profile conflict: {}", final_profile.browser, e).into())
|
||||
}
|
||||
_ => {
|
||||
log::info!("Falling back to new instance for browser: {}", final_profile.browser);
|
||||
log::info!(
|
||||
"Falling back to new instance for browser: {}",
|
||||
final_profile.browser
|
||||
);
|
||||
// Fallback to launching a new instance for other browsers
|
||||
self.launch_browser_internal(app_handle.clone(), &final_profile, url, internal_proxy_settings, None, false).await
|
||||
self
|
||||
.launch_browser_internal(
|
||||
app_handle.clone(),
|
||||
&final_profile,
|
||||
url,
|
||||
internal_proxy_settings,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1316,8 +1201,6 @@ impl BrowserRunner {
|
||||
"firefox" => {
|
||||
exe_name.contains("firefox")
|
||||
&& !exe_name.contains("developer")
|
||||
&& !exe_name.contains("tor")
|
||||
&& !exe_name.contains("mullvad")
|
||||
&& !exe_name.contains("camoufox")
|
||||
}
|
||||
"firefox-developer" => {
|
||||
@@ -1333,8 +1216,6 @@ impl BrowserRunner {
|
||||
}))
|
||||
|| 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"),
|
||||
"zen" => exe_name.contains("zen"),
|
||||
"chromium" => exe_name.contains("chromium") || exe_name.contains("chrome"),
|
||||
"brave" => exe_name.contains("brave") || exe_name.contains("Brave"),
|
||||
@@ -1349,7 +1230,7 @@ impl BrowserRunner {
|
||||
|
||||
let profile_path_match = if matches!(
|
||||
profile.browser.as_str(),
|
||||
"firefox" | "firefox-developer" | "tor-browser" | "mullvad-browser" | "zen"
|
||||
"firefox" | "firefox-developer" | "zen"
|
||||
) {
|
||||
// Firefox-based browsers: look for -profile argument followed by path
|
||||
let mut found_profile_arg = false;
|
||||
@@ -1598,8 +1479,6 @@ impl BrowserRunner {
|
||||
"firefox" => {
|
||||
exe_name.contains("firefox")
|
||||
&& !exe_name.contains("developer")
|
||||
&& !exe_name.contains("tor")
|
||||
&& !exe_name.contains("mullvad")
|
||||
&& !exe_name.contains("camoufox")
|
||||
}
|
||||
"firefox-developer" => {
|
||||
@@ -1615,8 +1494,6 @@ impl BrowserRunner {
|
||||
}))
|
||||
|| 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"),
|
||||
"zen" => exe_name.contains("zen"),
|
||||
"chromium" => exe_name.contains("chromium") || exe_name.contains("chrome"),
|
||||
"brave" => exe_name.contains("brave") || exe_name.contains("Brave"),
|
||||
@@ -1630,7 +1507,7 @@ 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"
|
||||
"firefox" | "firefox-developer" | "zen"
|
||||
) {
|
||||
// Firefox-based browsers: look for -profile argument followed by path
|
||||
let mut found_profile_arg = false;
|
||||
@@ -1792,7 +1669,7 @@ pub async fn launch_browser_profile(
|
||||
// For Firefox-based browsers, always 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"
|
||||
"firefox" | "firefox-developer" | "zen"
|
||||
) {
|
||||
let profiles_dir = browser_runner.profile_manager.get_profiles_dir();
|
||||
let profile_path = profiles_dir
|
||||
|
||||
@@ -54,14 +54,6 @@ impl BrowserVersionManager {
|
||||
|
||||
match browser {
|
||||
"firefox" | "firefox-developer" => Ok(true),
|
||||
"mullvad-browser" => {
|
||||
// Mullvad doesn't support ARM64 on Windows and Linux
|
||||
if arch == "arm64" && (os == "windows" || os == "linux") {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
"zen" => {
|
||||
// Zen supports all platforms and architectures
|
||||
Ok(true)
|
||||
@@ -78,14 +70,6 @@ impl BrowserVersionManager {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
"tor-browser" => {
|
||||
// TOR Browser doesn't support ARM64 on Windows and Linux
|
||||
if arch == "arm64" && (os == "windows" || os == "linux") {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
"camoufox" => {
|
||||
// Camoufox supports all platforms and architectures according to the JS code
|
||||
Ok(true)
|
||||
@@ -99,11 +83,9 @@ impl BrowserVersionManager {
|
||||
let all_browsers = vec![
|
||||
"firefox",
|
||||
"firefox-developer",
|
||||
"mullvad-browser",
|
||||
"zen",
|
||||
"brave",
|
||||
"chromium",
|
||||
"tor-browser",
|
||||
"camoufox",
|
||||
];
|
||||
|
||||
@@ -238,11 +220,9 @@ impl BrowserVersionManager {
|
||||
let fresh_versions = match browser {
|
||||
"firefox" => self.fetch_firefox_versions(true).await?, // Always fetch fresh for merging
|
||||
"firefox-developer" => self.fetch_firefox_developer_versions(true).await?,
|
||||
"mullvad-browser" => self.fetch_mullvad_versions(true).await?,
|
||||
"zen" => self.fetch_zen_versions(true).await?,
|
||||
"brave" => self.fetch_brave_versions(true).await?,
|
||||
"chromium" => self.fetch_chromium_versions(true).await?,
|
||||
"tor-browser" => self.fetch_tor_versions(true).await?,
|
||||
"camoufox" => self.fetch_camoufox_versions(true).await?,
|
||||
_ => return Err(format!("Unsupported browser: {browser}").into()),
|
||||
};
|
||||
@@ -356,27 +336,6 @@ impl BrowserVersionManager {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
"mullvad-browser" => {
|
||||
let releases = self.fetch_mullvad_releases_detailed(true).await?;
|
||||
merged_versions
|
||||
.into_iter()
|
||||
.map(|version| {
|
||||
if let Some(release) = releases.iter().find(|r| r.tag_name == version) {
|
||||
BrowserVersionInfo {
|
||||
version: release.tag_name.clone(),
|
||||
is_prerelease: release.is_nightly,
|
||||
date: release.published_at.clone(),
|
||||
}
|
||||
} else {
|
||||
BrowserVersionInfo {
|
||||
version: version.clone(),
|
||||
is_prerelease: false, // Mullvad usually stable releases
|
||||
date: "".to_string(),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
"zen" => {
|
||||
let releases = self.fetch_zen_releases_detailed(true).await?;
|
||||
merged_versions
|
||||
@@ -444,31 +403,6 @@ impl BrowserVersionManager {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
"tor-browser" => {
|
||||
let releases = self.fetch_tor_releases_detailed(true).await?;
|
||||
merged_versions
|
||||
.into_iter()
|
||||
.map(|version| {
|
||||
if let Some(release) = releases.iter().find(|r| r.version == version) {
|
||||
BrowserVersionInfo {
|
||||
version: release.version.clone(),
|
||||
is_prerelease: crate::api_client::is_browser_version_nightly(
|
||||
"tor-browser",
|
||||
&release.version,
|
||||
None,
|
||||
),
|
||||
date: release.date.clone(),
|
||||
}
|
||||
} else {
|
||||
BrowserVersionInfo {
|
||||
version: version.clone(),
|
||||
is_prerelease: false, // TOR Browser usually stable releases
|
||||
date: "".to_string(),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
"camoufox" => {
|
||||
let releases = self.fetch_camoufox_releases_detailed(true).await?;
|
||||
merged_versions
|
||||
@@ -602,50 +536,6 @@ impl BrowserVersionManager {
|
||||
is_archive,
|
||||
})
|
||||
}
|
||||
"mullvad-browser" => {
|
||||
// Mullvad Browser doesn't support ARM64 on Windows and Linux
|
||||
if arch == "arm64" && (os == "windows" || os == "linux") {
|
||||
return Err(format!("Mullvad Browser doesn't support ARM64 on {os}").into());
|
||||
}
|
||||
|
||||
let (platform_str, filename, is_archive) = match os.as_str() {
|
||||
"windows" => {
|
||||
if arch == "arm64" {
|
||||
return Err("Mullvad Browser doesn't support ARM64 on Windows".into());
|
||||
}
|
||||
(
|
||||
"windows-x86_64",
|
||||
format!("mullvad-browser-windows-x86_64-{version}.exe"),
|
||||
false,
|
||||
)
|
||||
}
|
||||
"linux" => {
|
||||
if arch == "arm64" {
|
||||
return Err("Mullvad Browser doesn't support ARM64 on Linux".into());
|
||||
}
|
||||
(
|
||||
"x86_64",
|
||||
format!("mullvad-browser-x86_64-{version}.tar.xz"),
|
||||
true,
|
||||
)
|
||||
}
|
||||
"macos" => (
|
||||
"macos",
|
||||
format!("mullvad-browser-macos-{version}.dmg"),
|
||||
true,
|
||||
),
|
||||
_ => return Err(format!("Unsupported platform for Mullvad Browser: {os}").into()),
|
||||
};
|
||||
|
||||
Ok(DownloadInfo {
|
||||
url: format!(
|
||||
"https://github.com/mullvad/mullvad-browser/releases/download/{version}/mullvad-browser-{platform_str}-{version}{}",
|
||||
if os == "windows" { ".exe" } else if os == "linux" { ".tar.xz" } else { ".dmg" }
|
||||
),
|
||||
filename,
|
||||
is_archive,
|
||||
})
|
||||
}
|
||||
"zen" => {
|
||||
let (asset_name, filename, is_archive) = match (&os[..], &arch[..]) {
|
||||
("windows", "x64") => ("zen.installer.exe", format!("zen-{version}.exe"), false),
|
||||
@@ -731,46 +621,6 @@ impl BrowserVersionManager {
|
||||
is_archive: true,
|
||||
})
|
||||
}
|
||||
"tor-browser" => {
|
||||
// TOR Browser doesn't support ARM64 on Windows and Linux
|
||||
if arch == "arm64" && (os == "windows" || os == "linux") {
|
||||
return Err(format!("TOR Browser doesn't support ARM64 on {os}").into());
|
||||
}
|
||||
|
||||
let (platform_str, filename, is_archive) = match os.as_str() {
|
||||
"windows" => {
|
||||
if arch == "arm64" {
|
||||
return Err("TOR Browser doesn't support ARM64 on Windows".into());
|
||||
}
|
||||
(
|
||||
"windows-x86_64-portable",
|
||||
format!("tor-browser-windows-x86_64-portable-{version}.exe"),
|
||||
false,
|
||||
)
|
||||
}
|
||||
"linux" => {
|
||||
if arch == "arm64" {
|
||||
return Err("TOR Browser doesn't support ARM64 on Linux".into());
|
||||
}
|
||||
(
|
||||
"linux-x86_64",
|
||||
format!("tor-browser-linux-x86_64-{version}.tar.xz"),
|
||||
true,
|
||||
)
|
||||
}
|
||||
"macos" => ("macos", format!("tor-browser-macos-{version}.dmg"), true),
|
||||
_ => return Err(format!("Unsupported platform for TOR Browser: {os}").into()),
|
||||
};
|
||||
|
||||
Ok(DownloadInfo {
|
||||
url: format!(
|
||||
"https://archive.torproject.org/tor-package-archive/torbrowser/{version}/tor-browser-{platform_str}-{version}{}",
|
||||
if os == "windows" { ".exe" } else if os == "linux" { ".tar.xz" } else { ".dmg" }
|
||||
),
|
||||
filename,
|
||||
is_archive,
|
||||
})
|
||||
}
|
||||
"camoufox" => {
|
||||
// Camoufox downloads from GitHub releases with pattern: camoufox-{version}-{release}-{os}.{arch}.zip
|
||||
let (os_name, arch_name) = match (&os[..], &arch[..]) {
|
||||
@@ -864,24 +714,6 @@ impl BrowserVersionManager {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_mullvad_versions(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let releases = self.fetch_mullvad_releases_detailed(no_caching).await?;
|
||||
Ok(releases.into_iter().map(|r| r.tag_name).collect())
|
||||
}
|
||||
|
||||
async fn fetch_mullvad_releases_detailed(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
) -> Result<Vec<GithubRelease>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
self
|
||||
.api_client
|
||||
.fetch_mullvad_releases_with_caching(no_caching)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_zen_versions(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
@@ -971,24 +803,6 @@ impl BrowserVersionManager {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_tor_versions(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let releases = self.fetch_tor_releases_detailed(no_caching).await?;
|
||||
Ok(releases.into_iter().map(|r| r.version).collect())
|
||||
}
|
||||
|
||||
async fn fetch_tor_releases_detailed(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
) -> Result<Vec<BrowserRelease>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
self
|
||||
.api_client
|
||||
.fetch_tor_releases_with_caching(no_caching)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_camoufox_versions(
|
||||
&self,
|
||||
no_caching: bool,
|
||||
@@ -1130,40 +944,6 @@ mod tests {
|
||||
.url
|
||||
.contains("/pub/devedition/releases/139.0b1/"));
|
||||
|
||||
// Test Mullvad Browser
|
||||
let mullvad_info = service
|
||||
.get_download_info("mullvad-browser", "14.5a6")
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
assert_eq!(mullvad_info.filename, "mullvad-browser-macos-14.5a6.dmg");
|
||||
assert!(mullvad_info.url.contains("mullvad-browser-macos-14.5a6"));
|
||||
assert!(mullvad_info.is_archive);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
assert_eq!(
|
||||
mullvad_info.filename,
|
||||
"mullvad-browser-x86_64-14.5a6.tar.xz"
|
||||
);
|
||||
assert!(mullvad_info.url.contains("mullvad-browser-x86_64-14.5a6"));
|
||||
assert!(mullvad_info.is_archive);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
assert_eq!(
|
||||
mullvad_info.filename,
|
||||
"mullvad-browser-windows-x86_64-14.5a6.exe"
|
||||
);
|
||||
assert!(mullvad_info
|
||||
.url
|
||||
.contains("mullvad-browser-windows-x86_64-14.5a6"));
|
||||
assert!(!mullvad_info.is_archive);
|
||||
}
|
||||
|
||||
// Test Zen Browser
|
||||
let zen_info = service.get_download_info("zen", "1.11b").unwrap();
|
||||
|
||||
@@ -1188,35 +968,6 @@ mod tests {
|
||||
assert!(!zen_info.is_archive);
|
||||
}
|
||||
|
||||
// Test Tor Browser
|
||||
let tor_info = service.get_download_info("tor-browser", "14.0.4").unwrap();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
assert_eq!(tor_info.filename, "tor-browser-macos-14.0.4.dmg");
|
||||
assert!(tor_info.url.contains("tor-browser-macos-14.0.4"));
|
||||
assert!(tor_info.is_archive);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
assert_eq!(tor_info.filename, "tor-browser-linux-x86_64-14.0.4.tar.xz");
|
||||
assert!(tor_info.url.contains("tor-browser-linux-x86_64-14.0.4"));
|
||||
assert!(tor_info.is_archive);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
assert_eq!(
|
||||
tor_info.filename,
|
||||
"tor-browser-windows-x86_64-portable-14.0.4.exe"
|
||||
);
|
||||
assert!(tor_info
|
||||
.url
|
||||
.contains("tor-browser-windows-x86_64-portable-14.0.4"));
|
||||
assert!(!tor_info.is_archive);
|
||||
}
|
||||
|
||||
// Test Chromium
|
||||
let chromium_info = service.get_download_info("chromium", "1465660").unwrap();
|
||||
|
||||
|
||||
@@ -145,30 +145,6 @@ impl Downloader {
|
||||
|
||||
Ok(asset_url)
|
||||
}
|
||||
BrowserType::MullvadBrowser => {
|
||||
// For Mullvad, verify the asset exists
|
||||
let releases = self
|
||||
.api_client
|
||||
.fetch_mullvad_releases_with_caching(true)
|
||||
.await?;
|
||||
|
||||
let release = releases
|
||||
.iter()
|
||||
.find(|r| r.tag_name == version)
|
||||
.ok_or(format!("Mullvad version {version} not found"))?;
|
||||
|
||||
// Get platform and architecture info
|
||||
let (os, arch) = Self::get_platform_info();
|
||||
|
||||
// Find the appropriate asset
|
||||
let asset_url = self
|
||||
.find_mullvad_asset(&release.assets, &os, &arch)
|
||||
.ok_or(format!(
|
||||
"No compatible asset found for Mullvad version {version} on {os}/{arch}"
|
||||
))?;
|
||||
|
||||
Ok(asset_url)
|
||||
}
|
||||
BrowserType::Camoufox => {
|
||||
// For Camoufox, verify the asset exists and find the correct download URL
|
||||
let releases = self
|
||||
@@ -327,46 +303,6 @@ impl Downloader {
|
||||
asset.map(|a| a.browser_download_url.clone())
|
||||
}
|
||||
|
||||
/// Find the appropriate Mullvad asset for the current platform and architecture
|
||||
fn find_mullvad_asset(
|
||||
&self,
|
||||
assets: &[crate::browser::GithubAsset],
|
||||
os: &str,
|
||||
arch: &str,
|
||||
) -> Option<String> {
|
||||
// Mullvad asset naming patterns:
|
||||
// Windows: mullvad-browser-windows-x86_64-VERSION.exe
|
||||
// macOS: mullvad-browser-macos-VERSION.dmg
|
||||
// Linux: mullvad-browser-x86_64-VERSION.tar.xz
|
||||
|
||||
let asset = match (os, arch) {
|
||||
("windows", "x64") => assets.iter().find(|asset| {
|
||||
asset.name.contains("windows")
|
||||
&& asset.name.contains("x86_64")
|
||||
&& asset.name.ends_with(".exe")
|
||||
}),
|
||||
("windows", "arm64") => {
|
||||
// Mullvad doesn't support ARM64 on Windows
|
||||
None
|
||||
}
|
||||
("macos", _) => assets
|
||||
.iter()
|
||||
.find(|asset| asset.name.contains("macos") && asset.name.ends_with(".dmg")),
|
||||
("linux", "x64") => assets.iter().find(|asset| {
|
||||
asset.name.contains("x86_64")
|
||||
&& asset.name.ends_with(".tar.xz")
|
||||
&& !asset.name.contains("windows")
|
||||
}),
|
||||
("linux", "arm64") => {
|
||||
// Mullvad doesn't support ARM64 on Linux
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
asset.map(|a| a.browser_download_url.clone())
|
||||
}
|
||||
|
||||
/// Find the appropriate Camoufox asset for the current platform and architecture
|
||||
fn find_camoufox_asset(
|
||||
&self,
|
||||
@@ -937,7 +873,6 @@ mod tests {
|
||||
base_url.clone(), // firefox_dev_api_base
|
||||
base_url.clone(), // github_api_base
|
||||
base_url.clone(), // chromium_api_base
|
||||
base_url.clone(), // tor_archive_base
|
||||
)
|
||||
}
|
||||
|
||||
@@ -984,27 +919,6 @@ mod tests {
|
||||
assert_eq!(url, download_info.url);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_resolve_tor_download_url() {
|
||||
let server = setup_mock_server().await;
|
||||
let api_client = create_test_api_client(&server);
|
||||
let downloader = Downloader::new_with_api_client(api_client);
|
||||
|
||||
let download_info = DownloadInfo {
|
||||
url: "https://archive.torproject.org/tor-package-archive/torbrowser/14.0.4/tor-browser-macos-14.0.4.dmg".to_string(),
|
||||
filename: "tor-test.dmg".to_string(),
|
||||
is_archive: true,
|
||||
};
|
||||
|
||||
let result = downloader
|
||||
.resolve_download_url(BrowserType::TorBrowser, "14.0.4", &download_info)
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let url = result.unwrap();
|
||||
assert_eq!(url, download_info.url);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_download_browser_with_progress() {
|
||||
let server = setup_mock_server().await;
|
||||
|
||||
@@ -868,9 +868,6 @@ impl Extractor {
|
||||
"chromium.exe",
|
||||
"zen.exe",
|
||||
"brave.exe",
|
||||
"tor-browser.exe",
|
||||
"tor.exe",
|
||||
"mullvad-browser.exe",
|
||||
];
|
||||
|
||||
// First try priority executable names
|
||||
@@ -937,8 +934,6 @@ impl Extractor {
|
||||
|| file_name.contains("chromium")
|
||||
|| file_name.contains("zen")
|
||||
|| file_name.contains("brave")
|
||||
|| file_name.contains("tor")
|
||||
|| file_name.contains("mullvad")
|
||||
|| file_name.contains("browser")
|
||||
{
|
||||
return Ok(path);
|
||||
@@ -1012,15 +1007,6 @@ impl Extractor {
|
||||
"brave-browser-beta",
|
||||
"brave-browser-dev",
|
||||
"brave-bin",
|
||||
// Tor Browser variants
|
||||
"tor-browser",
|
||||
"torbrowser-launcher",
|
||||
"tor-browser_en-US",
|
||||
"start-tor-browser",
|
||||
"Browser/start-tor-browser",
|
||||
// Mullvad Browser
|
||||
"mullvad-browser",
|
||||
"mullvad-browser-bin",
|
||||
// Camoufox variants
|
||||
"camoufox",
|
||||
"camoufox-bin",
|
||||
@@ -1049,19 +1035,14 @@ impl Extractor {
|
||||
"chromium",
|
||||
"brave",
|
||||
"zen",
|
||||
"tor-browser",
|
||||
"mullvad-browser",
|
||||
"camoufox",
|
||||
".",
|
||||
"./",
|
||||
"firefox",
|
||||
"mullvad-browser",
|
||||
"tor-browser_en-US",
|
||||
"Browser",
|
||||
"browser",
|
||||
"opt/google/chrome",
|
||||
"opt/brave.com/brave",
|
||||
"opt/mullvad-browser",
|
||||
"opt/camoufox",
|
||||
"usr/lib/firefox",
|
||||
"usr/lib/chromium",
|
||||
@@ -1159,8 +1140,7 @@ impl Extractor {
|
||||
|| name_lower.contains("chrome")
|
||||
|| name_lower.contains("brave")
|
||||
|| name_lower.contains("zen")
|
||||
|| name_lower.contains("tor")
|
||||
|| name_lower.contains("mullvad")
|
||||
|| name_lower.contains("camoufox")
|
||||
|| name_lower.ends_with(".appimage")
|
||||
|| !name_lower.contains('.')
|
||||
{
|
||||
@@ -1215,8 +1195,6 @@ impl Extractor {
|
||||
|| name_lower.contains("chrome")
|
||||
|| name_lower.contains("brave")
|
||||
|| name_lower.contains("zen")
|
||||
|| name_lower.contains("tor")
|
||||
|| name_lower.contains("mullvad")
|
||||
|| name_lower.contains("camoufox")
|
||||
|| file_name.ends_with(".AppImage")
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::browser::{create_browser, BrowserType};
|
||||
use crate::profile::BrowserProfile;
|
||||
use std::ffi::OsString;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
@@ -10,40 +9,6 @@ pub mod macos {
|
||||
use super::*;
|
||||
use sysinfo::{Pid, System};
|
||||
|
||||
pub fn is_tor_or_mullvad_browser(exe_name: &str, cmd: &[OsString], browser_type: &str) -> bool {
|
||||
match browser_type {
|
||||
"mullvad-browser" => {
|
||||
let has_mullvad_in_exe = exe_name.contains("mullvad");
|
||||
let has_firefox_exe = exe_name == "firefox" || exe_name.contains("firefox-bin");
|
||||
let has_mullvad_in_cmd = cmd.iter().any(|arg| {
|
||||
let arg_str = arg.to_str().unwrap_or("");
|
||||
arg_str.contains("Mullvad Browser.app")
|
||||
|| arg_str.contains("mullvad")
|
||||
|| arg_str.contains("Mullvad")
|
||||
|| arg_str.contains("/Applications/Mullvad Browser.app/")
|
||||
|| arg_str.contains("MullvadBrowser")
|
||||
});
|
||||
|
||||
has_mullvad_in_exe || (has_firefox_exe && has_mullvad_in_cmd)
|
||||
}
|
||||
"tor-browser" => {
|
||||
let has_tor_in_exe = exe_name.contains("tor");
|
||||
let has_firefox_exe = exe_name == "firefox" || exe_name.contains("firefox-bin");
|
||||
let has_tor_in_cmd = cmd.iter().any(|arg| {
|
||||
let arg_str = arg.to_str().unwrap_or("");
|
||||
arg_str.contains("Tor Browser.app")
|
||||
|| arg_str.contains("tor-browser")
|
||||
|| arg_str.contains("TorBrowser")
|
||||
|| arg_str.contains("/Applications/Tor Browser.app/")
|
||||
|| arg_str.contains("TorBrowser-Data")
|
||||
});
|
||||
|
||||
has_tor_in_exe || (has_firefox_exe && has_tor_in_cmd)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn launch_browser_process(
|
||||
executable_path: &std::path::Path,
|
||||
args: &[String],
|
||||
@@ -375,122 +340,6 @@ end try
|
||||
descendants
|
||||
}
|
||||
|
||||
pub async fn open_url_in_existing_browser_tor_mullvad(
|
||||
profile: &BrowserProfile,
|
||||
url: &str,
|
||||
browser_type: BrowserType,
|
||||
browser_dir: &Path,
|
||||
_profiles_dir: &Path,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let pid = profile.process_id.unwrap();
|
||||
|
||||
log::info!("Opening URL in TOR/Mullvad browser using file-based approach (PID: {pid})");
|
||||
|
||||
// Method 1: Try using a temporary HTML file approach
|
||||
log::info!("Attempting file-based URL opening for TOR/Mullvad browser");
|
||||
|
||||
let temp_dir = std::env::temp_dir();
|
||||
let temp_file_name = format!("donut_browser_url_{}.html", std::process::id());
|
||||
let temp_file_path = temp_dir.join(&temp_file_name);
|
||||
|
||||
let html_content = format!(
|
||||
r#"<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="0; url={url}">
|
||||
<title>Redirecting...</title>
|
||||
<script>
|
||||
window.location.href = "{url}";
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="{url}">{url}</a>...</p>
|
||||
</body>
|
||||
</html>"#
|
||||
);
|
||||
|
||||
match std::fs::write(&temp_file_path, html_content) {
|
||||
Ok(()) => {
|
||||
log::info!("Created temporary HTML file: {temp_file_path:?}");
|
||||
|
||||
let browser = create_browser(browser_type.clone());
|
||||
if let Ok(executable_path) = browser.get_executable_path(browser_dir) {
|
||||
let open_result = Command::new("open")
|
||||
.args([
|
||||
"-a",
|
||||
executable_path.to_str().unwrap(),
|
||||
temp_file_path.to_str().unwrap(),
|
||||
])
|
||||
.output();
|
||||
|
||||
// Clean up the temporary file after a short delay
|
||||
let temp_file_path_clone = temp_file_path.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||
let _ = std::fs::remove_file(temp_file_path_clone);
|
||||
});
|
||||
|
||||
match open_result {
|
||||
Ok(output) if output.status.success() => {
|
||||
log::info!("Successfully opened URL using file-based approach");
|
||||
return Ok(());
|
||||
}
|
||||
Ok(output) => {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
log::info!("File-based approach failed: {stderr}");
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("File-based approach error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = std::fs::remove_file(&temp_file_path);
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("Failed to create temporary HTML file: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
// Method 2: Try using the 'open' command directly with the URL
|
||||
log::info!("Attempting direct URL opening with 'open' command");
|
||||
|
||||
let browser = create_browser(browser_type.clone());
|
||||
if let Ok(executable_path) = browser.get_executable_path(browser_dir) {
|
||||
let direct_open_result = Command::new("open")
|
||||
.args(["-a", executable_path.to_str().unwrap(), url])
|
||||
.output();
|
||||
|
||||
match direct_open_result {
|
||||
Ok(output) if output.status.success() => {
|
||||
log::info!("Successfully opened URL using direct 'open' command");
|
||||
return Ok(());
|
||||
}
|
||||
Ok(output) => {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
log::info!("Direct 'open' command failed: {stderr}");
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("Direct 'open' command error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If all methods fail, return a helpful error message
|
||||
Err(
|
||||
format!(
|
||||
"Failed to open URL in existing TOR/Mullvad browser (PID: {pid}). All methods failed:\n\
|
||||
1. File-based approach failed\n\
|
||||
2. Direct 'open' command failed\n\
|
||||
\n\
|
||||
This may be due to browser security restrictions or the browser process may have changed.\n\
|
||||
Try closing and reopening the browser, or manually paste the URL: {url}"
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn open_url_in_existing_browser_chromium(
|
||||
profile: &BrowserProfile,
|
||||
url: &str,
|
||||
@@ -622,42 +471,6 @@ end try
|
||||
pub mod windows {
|
||||
use super::*;
|
||||
|
||||
pub fn is_tor_or_mullvad_browser(exe_name: &str, cmd: &[OsString], browser_type: &str) -> bool {
|
||||
let exe_lower = exe_name.to_lowercase();
|
||||
|
||||
// Check for Firefox-based browsers first by executable name
|
||||
let is_firefox_family = exe_lower.contains("firefox") || exe_lower.contains(".exe");
|
||||
|
||||
if !is_firefox_family {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check command arguments for profile paths and browser-specific indicators
|
||||
let cmd_line = cmd
|
||||
.iter()
|
||||
.map(|s| s.to_string_lossy().to_lowercase())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
|
||||
match browser_type {
|
||||
"tor-browser" => {
|
||||
// Check for TOR browser specific paths and arguments
|
||||
cmd_line.contains("tor")
|
||||
|| cmd_line.contains("browser\\torbrowser")
|
||||
|| cmd_line.contains("tor-browser")
|
||||
|| cmd_line.contains("profile") && (cmd_line.contains("tor") || cmd_line.contains("tbb"))
|
||||
}
|
||||
"mullvad-browser" => {
|
||||
// Check for Mullvad browser specific paths and arguments
|
||||
cmd_line.contains("mullvad")
|
||||
|| cmd_line.contains("browser\\mullvadbrowser")
|
||||
|| cmd_line.contains("mullvad-browser")
|
||||
|| cmd_line.contains("profile") && cmd_line.contains("mullvad")
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn launch_browser_process(
|
||||
executable_path: &std::path::Path,
|
||||
args: &[String],
|
||||
@@ -782,48 +595,6 @@ pub mod windows {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn open_url_in_existing_browser_tor_mullvad(
|
||||
profile: &BrowserProfile,
|
||||
url: &str,
|
||||
browser_type: BrowserType,
|
||||
browser_dir: &Path,
|
||||
profiles_dir: &Path,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
// On Windows, TOR and Mullvad browsers can sometimes accept URLs via command line
|
||||
// even with -no-remote, by launching a new instance that hands off to existing one
|
||||
let browser = create_browser(browser_type.clone());
|
||||
let executable_path = browser
|
||||
.get_executable_path(browser_dir)
|
||||
.map_err(|e| format!("Failed to get executable path: {}", e))?;
|
||||
|
||||
let mut cmd = Command::new(&executable_path);
|
||||
let profile_data_path = profile.get_profile_data_path(profiles_dir);
|
||||
cmd.args(["-profile", &profile_data_path.to_string_lossy(), url]);
|
||||
|
||||
// Set working directory
|
||||
if let Some(parent_dir) = browser_dir
|
||||
.parent()
|
||||
.or_else(|| browser_dir.ancestors().nth(1))
|
||||
{
|
||||
cmd.current_dir(parent_dir);
|
||||
}
|
||||
|
||||
let output = cmd.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(
|
||||
format!(
|
||||
"Failed to open URL in existing {}: {}. Note: TOR and Mullvad browsers may require manual URL opening for security reasons.",
|
||||
browser_type.as_str(),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn open_url_in_existing_browser_chromium(
|
||||
profile: &BrowserProfile,
|
||||
url: &str,
|
||||
@@ -909,15 +680,6 @@ pub mod windows {
|
||||
pub mod linux {
|
||||
use super::*;
|
||||
|
||||
pub fn is_tor_or_mullvad_browser(
|
||||
_exe_name: &str,
|
||||
_cmd: &[OsString],
|
||||
_browser_type: &str,
|
||||
) -> bool {
|
||||
// Linux implementation would go here
|
||||
false
|
||||
}
|
||||
|
||||
pub async fn launch_browser_process(
|
||||
executable_path: &std::path::Path,
|
||||
args: &[String],
|
||||
@@ -1074,16 +836,6 @@ pub mod linux {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn open_url_in_existing_browser_tor_mullvad(
|
||||
_profile: &BrowserProfile,
|
||||
_url: &str,
|
||||
_browser_type: BrowserType,
|
||||
_browser_dir: &Path,
|
||||
_profiles_dir: &Path,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
Err("Opening URLs in existing Firefox-based browsers is not supported on Linux when using -no-remote".into())
|
||||
}
|
||||
|
||||
pub async fn open_url_in_existing_browser_chromium(
|
||||
profile: &BrowserProfile,
|
||||
url: &str,
|
||||
|
||||
@@ -765,10 +765,8 @@ impl ProfileManager {
|
||||
let profile_path_match = cmd.iter().any(|s| {
|
||||
let arg = s.to_str().unwrap_or("");
|
||||
// For Firefox-based browsers, check for exact profile path match
|
||||
if profile.browser == "tor-browser"
|
||||
|| profile.browser == "firefox"
|
||||
if profile.browser == "firefox"
|
||||
|| profile.browser == "firefox-developer"
|
||||
|| profile.browser == "mullvad-browser"
|
||||
|| profile.browser == "zen"
|
||||
{
|
||||
arg == profile_data_path_str
|
||||
@@ -803,13 +801,9 @@ impl ProfileManager {
|
||||
"firefox" => {
|
||||
exe_name.contains("firefox")
|
||||
&& !exe_name.contains("developer")
|
||||
&& !exe_name.contains("tor")
|
||||
&& !exe_name.contains("mullvad")
|
||||
&& !exe_name.contains("camoufox")
|
||||
}
|
||||
"firefox-developer" => exe_name.contains("firefox") && exe_name.contains("developer"),
|
||||
"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"),
|
||||
"zen" => exe_name.contains("zen"),
|
||||
"chromium" => exe_name.contains("chromium"),
|
||||
"brave" => exe_name.contains("brave"),
|
||||
@@ -832,10 +826,8 @@ impl ProfileManager {
|
||||
// Camoufox uses user_data_dir like Chromium browsers
|
||||
arg.contains(&format!("--user-data-dir={profile_data_path_str}"))
|
||||
|| arg == profile_data_path_str
|
||||
} else if profile.browser == "tor-browser"
|
||||
|| profile.browser == "firefox"
|
||||
} else if profile.browser == "firefox"
|
||||
|| profile.browser == "firefox-developer"
|
||||
|| profile.browser == "mullvad-browser"
|
||||
|| profile.browser == "zen"
|
||||
{
|
||||
arg == profile_data_path_str
|
||||
@@ -1042,33 +1034,6 @@ impl ProfileManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if a process matches TOR/Mullvad browser
|
||||
fn is_tor_or_mullvad_browser(
|
||||
&self,
|
||||
exe_name: &str,
|
||||
cmd: &[std::ffi::OsString],
|
||||
browser_type: &str,
|
||||
) -> bool {
|
||||
#[cfg(target_os = "macos")]
|
||||
return crate::platform_browser::macos::is_tor_or_mullvad_browser(exe_name, cmd, browser_type);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
return crate::platform_browser::windows::is_tor_or_mullvad_browser(
|
||||
exe_name,
|
||||
cmd,
|
||||
browser_type,
|
||||
);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
return crate::platform_browser::linux::is_tor_or_mullvad_browser(exe_name, cmd, browser_type);
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
{
|
||||
let _ = (exe_name, cmd, browser_type);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn get_common_firefox_preferences(&self) -> Vec<String> {
|
||||
vec![
|
||||
// Disable default browser check
|
||||
@@ -1180,7 +1145,7 @@ impl ProfileManager {
|
||||
);
|
||||
|
||||
// Use MANUAL proxy configuration (type 1) instead of PAC file (type 2)
|
||||
// PAC files with file:// URLs are blocked by privacy-focused browsers like Zen and Mullvad
|
||||
// PAC files with file:// URLs are blocked by privacy-focused browsers like Zen
|
||||
// Manual proxy configuration works reliably across all Firefox variants
|
||||
preferences.push("user_pref(\"network.proxy.type\", 1);".to_string());
|
||||
|
||||
|
||||
@@ -59,8 +59,6 @@ impl ProfileImporter {
|
||||
// Detect Zen Browser profiles
|
||||
detected_profiles.extend(self.detect_zen_browser_profiles()?);
|
||||
|
||||
// NOTE: Mullvad and Tor Browser profile imports are no longer supported.
|
||||
// We intentionally do not detect these profiles to avoid offering them in the UI.
|
||||
|
||||
// Remove duplicates based on path
|
||||
let mut seen_paths = HashSet::new();
|
||||
@@ -495,9 +493,7 @@ impl ProfileImporter {
|
||||
"firefox-developer" => "Firefox Developer",
|
||||
"chromium" => "Chrome/Chromium",
|
||||
"brave" => "Brave",
|
||||
"mullvad-browser" => "Mullvad Browser",
|
||||
"zen" => "Zen Browser",
|
||||
"tor-browser" => "Tor Browser",
|
||||
_ => "Unknown Browser",
|
||||
}
|
||||
}
|
||||
@@ -509,11 +505,6 @@ impl ProfileImporter {
|
||||
browser_type: &str,
|
||||
new_profile_name: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Disable imports for Mullvad and Tor browsers
|
||||
if browser_type == "mullvad-browser" || browser_type == "tor-browser" {
|
||||
return Err("Importing Mullvad Browser or Tor Browser profiles is not supported".into());
|
||||
}
|
||||
|
||||
// Validate that source path exists
|
||||
let source_path = Path::new(source_path);
|
||||
if !source_path.exists() {
|
||||
@@ -685,15 +676,7 @@ mod tests {
|
||||
"Chrome/Chromium"
|
||||
);
|
||||
assert_eq!(importer.get_browser_display_name("brave"), "Brave");
|
||||
assert_eq!(
|
||||
importer.get_browser_display_name("mullvad-browser"),
|
||||
"Mullvad Browser"
|
||||
);
|
||||
assert_eq!(importer.get_browser_display_name("zen"), "Zen Browser");
|
||||
assert_eq!(
|
||||
importer.get_browser_display_name("tor-browser"),
|
||||
"Tor Browser"
|
||||
);
|
||||
assert_eq!(
|
||||
importer.get_browser_display_name("unknown"),
|
||||
"Unknown Browser"
|
||||
|
||||
+2
-6
@@ -30,13 +30,11 @@ import { showErrorToast, showToast } from "@/lib/toast-utils";
|
||||
import type { BrowserProfile, CamoufoxConfig } from "@/types";
|
||||
|
||||
type BrowserTypeString =
|
||||
| "mullvad-browser"
|
||||
| "firefox"
|
||||
| "firefox-developer"
|
||||
| "chromium"
|
||||
| "brave"
|
||||
| "zen"
|
||||
| "tor-browser"
|
||||
| "camoufox";
|
||||
|
||||
interface PendingUrl {
|
||||
@@ -648,9 +646,7 @@ export default function Home() {
|
||||
if (profiles.length === 0) return;
|
||||
|
||||
const deprecatedProfiles = profiles.filter(
|
||||
(p) =>
|
||||
["tor-browser", "mullvad-browser"].includes(p.browser) ||
|
||||
(p.release_type === "nightly" && p.browser !== "firefox-developer"),
|
||||
(p) => p.release_type === "nightly" && p.browser !== "firefox-developer",
|
||||
);
|
||||
|
||||
if (deprecatedProfiles.length > 0) {
|
||||
@@ -661,7 +657,7 @@ export default function Home() {
|
||||
id: "deprecated-profiles-warning",
|
||||
type: "error",
|
||||
title: "Some profiles will be deprecated soon",
|
||||
description: `The following profiles will be deprecated soon: ${deprecatedNames}. Tor Browser, Mullvad Browser, and nightly profiles (except Firefox Developers Edition) will be removed in upcoming versions. Please check GitHub for migration instructions.`,
|
||||
description: `The following profiles will be deprecated soon: ${deprecatedNames}. Nightly profiles (except Firefox Developers Edition) will be removed in upcoming versions. Please check GitHub for migration instructions.`,
|
||||
duration: 15000,
|
||||
action: {
|
||||
label: "Learn more",
|
||||
|
||||
@@ -42,13 +42,11 @@ const getCurrentOS = (): CamoufoxOS => {
|
||||
import { RippleButton } from "./ui/ripple";
|
||||
|
||||
type BrowserTypeString =
|
||||
| "mullvad-browser"
|
||||
| "firefox"
|
||||
| "firefox-developer"
|
||||
| "chromium"
|
||||
| "brave"
|
||||
| "zen"
|
||||
| "tor-browser"
|
||||
| "camoufox";
|
||||
|
||||
interface CreateProfileDialogProps {
|
||||
@@ -92,14 +90,6 @@ const browserOptions: BrowserOption[] = [
|
||||
value: "zen",
|
||||
label: "Zen Browser",
|
||||
},
|
||||
{
|
||||
value: "mullvad-browser",
|
||||
label: "Mullvad Browser",
|
||||
},
|
||||
{
|
||||
value: "tor-browser",
|
||||
label: "Tor Browser",
|
||||
},
|
||||
];
|
||||
|
||||
export function CreateProfileDialog({
|
||||
@@ -429,12 +419,9 @@ export function CreateProfileDialog({
|
||||
isBrowserVersionAvailable,
|
||||
]);
|
||||
|
||||
// Filter supported browsers for regular browsers (excluding mullvad and tor)
|
||||
const regularBrowsers = browserOptions.filter(
|
||||
(browser) =>
|
||||
supportedBrowsers.includes(browser.value) &&
|
||||
browser.value !== "mullvad-browser" &&
|
||||
browser.value !== "tor-browser",
|
||||
// Filter supported browsers for regular browsers
|
||||
const regularBrowsers = browserOptions.filter((browser) =>
|
||||
supportedBrowsers.includes(browser.value),
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -62,10 +62,7 @@ export function ImportProfileDialog({
|
||||
const { supportedBrowsers, isLoading: isLoadingSupport } =
|
||||
useBrowserSupport();
|
||||
|
||||
// Exclude browsers that are no longer supported for import
|
||||
const importableBrowsers = supportedBrowsers.filter(
|
||||
(b) => b !== "mullvad-browser" && b !== "tor-browser",
|
||||
);
|
||||
const importableBrowsers = supportedBrowsers;
|
||||
|
||||
const loadDetectedProfiles = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
@@ -1680,19 +1680,12 @@ export function ProfilesDataTable({
|
||||
? (meta.storedProxies.find((p) => p.id === effectiveProxyId) ??
|
||||
null)
|
||||
: null;
|
||||
const displayName =
|
||||
profile.browser === "tor-browser"
|
||||
? "Not supported"
|
||||
: effectiveProxy
|
||||
? effectiveProxy.name
|
||||
: "Not Selected";
|
||||
const displayName = effectiveProxy
|
||||
? effectiveProxy.name
|
||||
: "Not Selected";
|
||||
const profileHasProxy = Boolean(effectiveProxy);
|
||||
const tooltipText =
|
||||
profile.browser === "tor-browser"
|
||||
? "Proxies are not supported for TOR browser"
|
||||
: profileHasProxy && effectiveProxy
|
||||
? effectiveProxy.name
|
||||
: null;
|
||||
profileHasProxy && effectiveProxy ? effectiveProxy.name : null;
|
||||
const isSelectorOpen = meta.openProxySelectorFor === profile.id;
|
||||
|
||||
// When profile is running, show bandwidth chart instead of proxy selector
|
||||
@@ -1717,23 +1710,6 @@ export function ProfilesDataTable({
|
||||
);
|
||||
}
|
||||
|
||||
if (profile.browser === "tor-browser") {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="flex gap-2 items-center">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Not supported
|
||||
</span>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
{(tooltipText || displayName.length > 10) && (
|
||||
<TooltipContent>{tooltipText || displayName}</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<Popover
|
||||
|
||||
@@ -142,11 +142,7 @@ export function ProfileSelectorDialog({
|
||||
const runningAvailableProfile = profiles.find((profile) => {
|
||||
const isRunning = runningProfiles.has(profile.id);
|
||||
// Simple check without browserState dependency
|
||||
return (
|
||||
isRunning &&
|
||||
profile.browser !== "tor-browser" &&
|
||||
profile.browser !== "mullvad-browser"
|
||||
);
|
||||
return isRunning;
|
||||
});
|
||||
|
||||
if (runningAvailableProfile) {
|
||||
|
||||
@@ -49,10 +49,9 @@ export function ProxyAssignmentDialog({
|
||||
setIsAssigning(true);
|
||||
setError(null);
|
||||
try {
|
||||
// Filter out TOR browser profiles as they don't support proxies
|
||||
const validProfiles = selectedProfiles.filter((profileId) => {
|
||||
const profile = profiles.find((p) => p.id === profileId);
|
||||
return profile && profile.browser !== "tor-browser";
|
||||
return profile;
|
||||
});
|
||||
|
||||
if (validProfiles.length === 0) {
|
||||
@@ -119,15 +118,9 @@ export function ProxyAssignmentDialog({
|
||||
(p: BrowserProfile) => p.id === profileId,
|
||||
);
|
||||
const displayName = profile ? profile.name : profileId;
|
||||
const isTorBrowser = profile?.browser === "tor-browser";
|
||||
return (
|
||||
<li key={profileId} className="truncate">
|
||||
• {displayName}
|
||||
{isTorBrowser && (
|
||||
<span className="ml-2 text-xs text-muted-foreground">
|
||||
(TOR - no proxy support)
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getBrowserDisplayName } from "@/lib/browser-utils";
|
||||
import type { BrowserProfile } from "@/types";
|
||||
|
||||
/**
|
||||
* Hook for managing browser state and enforcing single-instance rules for Tor and Mullvad browsers
|
||||
* Hook for managing browser state
|
||||
*/
|
||||
export function useBrowserState(
|
||||
profiles: BrowserProfile[],
|
||||
@@ -22,8 +22,8 @@ export function useBrowserState(
|
||||
* Check if a browser type allows only one instance to run at a time
|
||||
*/
|
||||
const isSingleInstanceBrowser = useCallback(
|
||||
(browserType: string): boolean => {
|
||||
return browserType === "tor-browser" || browserType === "mullvad-browser";
|
||||
(_browserType: string): boolean => {
|
||||
return false; // No browsers currently require single instance
|
||||
},
|
||||
[],
|
||||
);
|
||||
@@ -102,7 +102,7 @@ export function useBrowserState(
|
||||
return false;
|
||||
}
|
||||
|
||||
// For single-instance browsers (Tor and Mullvad)
|
||||
// For single-instance browsers
|
||||
if (isSingleInstanceBrowser(profile.browser)) {
|
||||
const runningInstancesOfType = profiles.filter(
|
||||
(p) => p.browser === profile.browser && runningProfiles.has(p.id),
|
||||
@@ -195,9 +195,7 @@ export function useBrowserState(
|
||||
isSingleInstanceBrowser(profile.browser) &&
|
||||
!canLaunchProfile(profile)
|
||||
) {
|
||||
const browserDisplayName =
|
||||
profile.browser === "tor-browser" ? "TOR" : "Mullvad";
|
||||
return `Only one ${browserDisplayName} browser instance can run at a time. Stop the running ${browserDisplayName} browser first.`;
|
||||
return `Only one instance of this browser can run at a time. Stop the running browser first.`;
|
||||
}
|
||||
|
||||
return "";
|
||||
@@ -242,8 +240,6 @@ export function useBrowserState(
|
||||
}
|
||||
|
||||
if (isSingleInstanceBrowser(profile.browser)) {
|
||||
const browserDisplayName =
|
||||
profile.browser === "tor-browser" ? "TOR" : "Mullvad";
|
||||
const runningInstancesOfType = profiles.filter(
|
||||
(p) => p.browser === profile.browser && runningProfiles.has(p.id),
|
||||
);
|
||||
@@ -252,7 +248,7 @@ export function useBrowserState(
|
||||
const runningProfileNames = runningInstancesOfType
|
||||
.map((p) => p.name)
|
||||
.join(", ");
|
||||
return `${browserDisplayName} browser is already running (${runningProfileNames}). Only one instance can run at a time.`;
|
||||
return `${getBrowserDisplayName(profile.browser)} browser is already running (${runningProfileNames}). Only one instance can run at a time.`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { FaChrome, FaFirefox, FaShieldAlt } from "react-icons/fa";
|
||||
import { SiBrave, SiMullvad, SiTorbrowser } from "react-icons/si";
|
||||
import { SiBrave } from "react-icons/si";
|
||||
import { ZenBrowser } from "@/components/icons/zen-browser";
|
||||
|
||||
/**
|
||||
@@ -14,11 +14,9 @@ export function getBrowserDisplayName(browserType: string): string {
|
||||
const browserNames: Record<string, string> = {
|
||||
firefox: "Firefox",
|
||||
"firefox-developer": "Firefox Developer Edition",
|
||||
"mullvad-browser": "Mullvad Browser",
|
||||
zen: "Zen Browser",
|
||||
brave: "Brave",
|
||||
chromium: "Chromium",
|
||||
"tor-browser": "Tor Browser",
|
||||
camoufox: "Anti-Detect",
|
||||
};
|
||||
|
||||
@@ -30,8 +28,6 @@ export function getBrowserDisplayName(browserType: string): string {
|
||||
*/
|
||||
export function getBrowserIcon(browserType: string) {
|
||||
switch (browserType) {
|
||||
case "mullvad-browser":
|
||||
return SiMullvad;
|
||||
case "chromium":
|
||||
return FaChrome;
|
||||
case "brave":
|
||||
@@ -41,8 +37,6 @@ export function getBrowserIcon(browserType: string) {
|
||||
return FaFirefox;
|
||||
case "zen":
|
||||
return ZenBrowser;
|
||||
case "tor-browser":
|
||||
return SiTorbrowser;
|
||||
case "camoufox":
|
||||
return FaShieldAlt;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user