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