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
60fn get_by_name(options: &DetectionOptions) -> Option<PathBuf> {
61    let default_apps = [
62        ("chrome", true),
63        ("chrome-browser", true),
64        ("google-chrome-stable", true),
65        ("google-chrome-beta", options.unstable),
66        ("google-chrome-dev", options.unstable),
67        ("google-chrome-unstable", options.unstable),
68        ("chromium", true),
69        ("chromium-browser", true),
70        ("msedge", options.msedge),
71        ("microsoft-edge", options.msedge),
72        ("microsoft-edge-stable", options.msedge),
73        ("microsoft-edge-beta", options.msedge && options.unstable),
74        ("microsoft-edge-dev", options.msedge && options.unstable),
75    ];
76    for (app, allowed) in default_apps {
77        if !allowed {
78            continue;
79        }
80        if let Ok(path) = which::which(app) {
81            return Some(path);
82        }
83    }
84
85    None
86}
87
88#[allow(unused_variables)]
89fn get_by_path(options: &DetectionOptions) -> Option<PathBuf> {
90    #[cfg(all(unix, not(target_os = "macos")))]
91    let default_paths: [(&str, bool); 3] = [
92        ("/opt/chromium.org/chromium", true),
93        ("/opt/google/chrome", true),
94        // test for lambda
95        ("/tmp/aws/lib", true),
96    ];
97    #[cfg(windows)]
98    let default_paths = [(
99        r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
100        options.msedge,
101    )];
102    #[cfg(target_os = "macos")]
103    let default_paths = [
104        (
105            "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
106            true,
107        ),
108        (
109            "/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta",
110            options.unstable,
111        ),
112        (
113            "/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev",
114            options.unstable,
115        ),
116        (
117            "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
118            options.unstable,
119        ),
120        ("/Applications/Chromium.app/Contents/MacOS/Chromium", true),
121        (
122            "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
123            options.msedge,
124        ),
125        (
126            "/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta",
127            options.msedge && options.unstable,
128        ),
129        (
130            "/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev",
131            options.msedge && options.unstable,
132        ),
133        (
134            "/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary",
135            options.msedge && options.unstable,
136        ),
137    ];
138
139    for (path, allowed) in default_paths {
140        if !allowed {
141            continue;
142        }
143        if Path::new(path).exists() {
144            return Some(path.into());
145        }
146    }
147
148    None
149}
150
151#[cfg(windows)]
152fn get_by_registry() -> Option<PathBuf> {
153    winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE)
154        .open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe")
155        .or_else(|_| {
156            winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER)
157                .open_subkey("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe")
158        })
159        .and_then(|key| key.get_value::<String, _>(""))
160        .map(PathBuf::from)
161        .ok()
162}