browser_info/
browser_detection.rs

1// ================================================================================================
2// Logic to detect the active browser - ブラウザ検出ロジック
3// ================================================================================================
4
5use crate::{BrowserInfoError, BrowserType};
6use active_win_pos_rs::ActiveWindow;
7
8/// Browser metadata extracted from the window
9pub struct BrowserMetadata {
10    pub version: Option<String>,
11    pub tabs_count: Option<u32>,
12    pub is_incognito: bool,
13}
14
15/// Classify the browser type from window information
16pub fn classify_browser(window: &ActiveWindow) -> Result<BrowserType, BrowserInfoError> {
17    let app_name = window.app_name.to_lowercase();
18
19    let process_path = window.process_path.to_str().unwrap_or("").to_lowercase();
20
21    // Detailed browser classification
22    if app_name.contains("chrome") && !app_name.contains("edge") {
23        Ok(BrowserType::Chrome)
24    } else if app_name.contains("firefox") {
25        Ok(BrowserType::Firefox)
26    } else if app_name.contains("msedge") || app_name.contains("edge") {
27        Ok(BrowserType::Edge)
28    } else if app_name.contains("safari") {
29        Ok(BrowserType::Safari)
30    } else if app_name.contains("brave") {
31        Ok(BrowserType::Brave)
32    } else if app_name.contains("opera") {
33        Ok(BrowserType::Opera)
34    } else if app_name.contains("vivaldi") {
35        Ok(BrowserType::Vivaldi)
36    } else if is_browser_by_path(&process_path) {
37        // Fallback: check by process path
38        detect_browser_from_path(&process_path)
39    } else {
40        Err(BrowserInfoError::NotABrowser)
41    }
42}
43
44/// Get additional browser metadata
45pub fn get_browser_metadata(
46    window: &ActiveWindow,
47    browser_type: &BrowserType,
48) -> Result<BrowserMetadata, BrowserInfoError> {
49    Ok(BrowserMetadata {
50        version: get_browser_version(window, browser_type),
51        tabs_count: count_tabs(window, browser_type),
52        is_incognito: detect_incognito_mode(window, browser_type),
53    })
54}
55
56fn is_browser_by_path(path: &str) -> bool {
57    let browser_indicators = [
58        "chrome", "firefox", "edge", "safari", "brave", "opera", "vivaldi",
59    ];
60    browser_indicators
61        .iter()
62        .any(|&indicator| path.contains(indicator))
63}
64
65fn detect_browser_from_path(path: &str) -> Result<BrowserType, BrowserInfoError> {
66    if path.contains("chrome") {
67        Ok(BrowserType::Chrome)
68    } else if path.contains("firefox") {
69        Ok(BrowserType::Firefox)
70    } else if path.contains("edge") {
71        Ok(BrowserType::Edge)
72    } else {
73        Ok(BrowserType::Unknown("detected_from_path".to_string()))
74    }
75}
76
77fn get_browser_version(_window: &ActiveWindow, _browser_type: &BrowserType) -> Option<String> {
78    // TODO: Implement version detection(Not Essential)
79    None
80}
81
82fn count_tabs(_window: &ActiveWindow, _browser_type: &BrowserType) -> Option<u32> {
83    // TODO: Implement tab counting(Not Essential)
84    None
85}
86
87fn detect_incognito_mode(window: &ActiveWindow, _browser_type: &BrowserType) -> bool {
88    // Basic incognito detection from window title
89    let title = window.title.to_lowercase();
90    title.contains("incognito") || title.contains("private") || title.contains("inprivate")
91}