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        ("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#[cfg(not(feature = "auto-detect-executable"))]
90fn get_by_name(_options: &DetectionOptions) -> Option<PathBuf> {
91    None
92}
93
94#[allow(unused_variables)]
95fn get_by_path(options: &DetectionOptions) -> Option<PathBuf> {
96    #[cfg(all(unix, not(target_os = "macos")))]
97    let default_paths: [(&str, bool); 3] = [
98        ("/opt/chromium.org/chromium", true),
99        ("/opt/google/chrome", true),
100        // test for lambda
101        ("/tmp/aws/lib", true),
102    ];
103    #[cfg(windows)]
104    let default_paths = [(
105        r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
106        options.msedge,
107    )];
108    #[cfg(target_os = "macos")]
109    let default_paths = [
110        (
111            "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
112            true,
113        ),
114        (
115            "/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta",
116            options.unstable,
117        ),
118        (
119            "/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev",
120            options.unstable,
121        ),
122        (
123            "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
124            options.unstable,
125        ),
126        ("/Applications/Chromium.app/Contents/MacOS/Chromium", true),
127        (
128            "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
129            options.msedge,
130        ),
131        (
132            "/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta",
133            options.msedge && options.unstable,
134        ),
135        (
136            "/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev",
137            options.msedge && options.unstable,
138        ),
139        (
140            "/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary",
141            options.msedge && options.unstable,
142        ),
143    ];
144
145    for (path, allowed) in default_paths {
146        if !allowed {
147            continue;
148        }
149        if Path::new(path).exists() {
150            return Some(path.into());
151        }
152    }
153
154    None
155}
156
157#[cfg(windows)]
158fn get_by_registry() -> Option<PathBuf> {
159    winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE)
160        .open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe")
161        .or_else(|_| {
162            winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER)
163                .open_subkey("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe")
164        })
165        .and_then(|key| key.get_value::<String, _>(""))
166        .map(PathBuf::from)
167        .ok()
168}