Skip to main content

chromiumoxide/
detection.rs

1use std::env;
2use std::path::{Path, PathBuf};
3
4#[derive(Debug, Clone)]
5pub struct DetectionOptions {
6    /// Detect Microsoft Edge
7    pub msedge: bool,
8    /// Detect unstable installations (beta, dev, unstable)
9    pub unstable: bool,
10}
11
12impl Default for DetectionOptions {
13    fn default() -> Self {
14        Self {
15            msedge: true,
16            unstable: false,
17        }
18    }
19}
20
21/// Returns the path to Chrome's executable.
22///
23/// The following elements will be checked:
24///   - `CHROME` environment variable
25///   - Usual filenames in the user path
26///   - (Windows) Registry
27///   - (Windows & MacOS) Usual installations paths
28///     If all of the above fail, an error is returned.
29pub fn default_executable(options: DetectionOptions) -> Result<std::path::PathBuf, String> {
30    if let Some(path) = get_by_env_var() {
31        return Ok(path);
32    }
33
34    if let Some(path) = get_by_name(&options) {
35        return Ok(path);
36    }
37
38    #[cfg(windows)]
39    if let Some(path) = get_by_registry() {
40        return Ok(path);
41    }
42
43    if let Some(path) = get_by_path(&options) {
44        return Ok(path);
45    }
46
47    Err("Could not auto detect a chrome executable".to_string())
48}
49
50fn get_by_env_var() -> Option<PathBuf> {
51    if let Ok(path) = env::var("CHROME") {
52        if Path::new(&path).exists() {
53            return Some(path.into());
54        }
55    }
56
57    None
58}
59
60#[cfg(feature = "auto-detect-executable")]
61fn get_by_name(options: &DetectionOptions) -> Option<PathBuf> {
62    let default_apps = [
63        ("chrome", true),
64        ("chrome-browser", true),
65        ("google-chrome-stable", true),
66        ("google-chrome-beta", options.unstable),
67        ("google-chrome-dev", options.unstable),
68        ("google-chrome-unstable", options.unstable),
69        ("chromium", true),
70        ("chromium-browser", true),
71        ("brave", true),
72        ("msedge", options.msedge),
73        ("microsoft-edge", options.msedge),
74        ("microsoft-edge-stable", options.msedge),
75        ("microsoft-edge-beta", options.msedge && options.unstable),
76        ("microsoft-edge-dev", options.msedge && options.unstable),
77    ];
78    for (app, allowed) in default_apps {
79        if !allowed {
80            continue;
81        }
82        if let Ok(path) = which::which(app) {
83            return Some(path);
84        }
85    }
86
87    None
88}
89
90#[cfg(not(feature = "auto-detect-executable"))]
91fn get_by_name(_options: &DetectionOptions) -> Option<PathBuf> {
92    None
93}
94
95#[allow(unused_variables)]
96fn get_by_path(options: &DetectionOptions) -> Option<PathBuf> {
97    #[cfg(all(unix, not(target_os = "macos")))]
98    let default_paths: [(&str, bool); 3] = [
99        ("/opt/chromium.org/chromium", true),
100        ("/opt/google/chrome", true),
101        // test for lambda
102        ("/tmp/aws/lib", true),
103    ];
104    #[cfg(windows)]
105    let default_paths = [(
106        r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
107        options.msedge,
108    )];
109    #[cfg(target_os = "macos")]
110    let default_paths = [
111        (
112            "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
113            true,
114        ),
115        (
116            "/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta",
117            options.unstable,
118        ),
119        (
120            "/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev",
121            options.unstable,
122        ),
123        (
124            "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
125            options.unstable,
126        ),
127        ("/Applications/Chromium.app/Contents/MacOS/Chromium", true),
128        (
129            "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
130            options.msedge,
131        ),
132        (
133            "/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta",
134            options.msedge && options.unstable,
135        ),
136        (
137            "/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev",
138            options.msedge && options.unstable,
139        ),
140        (
141            "/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary",
142            options.msedge && options.unstable,
143        ),
144    ];
145
146    for (path, allowed) in default_paths {
147        if !allowed {
148            continue;
149        }
150        if Path::new(path).exists() {
151            return Some(path.into());
152        }
153    }
154
155    None
156}
157
158#[cfg(windows)]
159fn get_by_registry() -> Option<PathBuf> {
160    winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE)
161        .open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe")
162        .or_else(|_| {
163            winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER)
164                .open_subkey("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe")
165        })
166        .and_then(|key| key.get_value::<String, _>(""))
167        .map(PathBuf::from)
168        .ok()
169}