Skip to main content

browser_info/
lib.rs

1//! # browser-info
2//!
3//! Cross-platform library for retrieving active browser URL and detailed information.
4//!
5//! Built on top of `active-win-pos-rs` for reliable window detection, with specialized
6//! browser information extraction capabilities.
7//!
8//! ## Quick Start
9//!
10//! ```rust
11//! use browser_info::get_active_browser_info;
12//!
13//! match get_active_browser_info() {
14//!     Ok(info) => {
15//!         println!("Current URL: {}", info.url);
16//!         println!("Browser: {}", info.browser_name);
17//!         println!("Title: {}", info.title);
18//!     }
19//!     Err(e) => eprintln!("Error: {}", e),
20//! }
21//! ```
22
23//================================================================================================
24// Import Section
25//================================================================================================
26
27use active_win_pos_rs::get_active_window;
28use serde::{Deserialize, Serialize};
29
30pub mod browser_detection;
31pub mod error;
32pub mod url_extraction;
33
34pub mod platform;
35
36pub use error::BrowserInfoError;
37
38#[cfg(any(
39    all(feature = "devtools", target_os = "windows"),
40    all(doc, feature = "devtools")
41))]
42pub use platform::chrome_devtools::ChromeDevToolsExtractor;
43
44//================================================================================================
45// Data Types & Module Variables
46//================================================================================================
47
48#[derive(Debug, Clone, Copy)]
49pub enum ExtractionMethod {
50    /// Auto decision (PowerShell優先 - 推奨)
51    Auto,
52    /// Chrome DevTools Protocol (詳細情報取得 - デバッグモード必要)
53    DevTools,
54    /// PowerShell (高速・互換性重視)
55    PowerShell,
56}
57
58/// [derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
59pub struct BrowserInfo {
60    /// Current URL displayed in the browser
61    pub url: String,
62    pub title: String,
63    pub browser_name: String,
64    pub browser_type: BrowserType,
65    pub version: Option<String>,
66    pub tabs_count: Option<u32>,
67    pub is_incognito: bool,
68    /// Process ID
69    pub process_id: u64,
70    /// Window position and size
71    pub window_position: WindowPosition,
72}
73
74/// Browser type classification
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
76pub enum BrowserType {
77    Chrome,
78    Firefox,
79    Edge,
80    Safari,
81    Brave,
82    Opera,
83    Vivaldi,
84    Unknown(String),
85}
86
87/// Window position and dimensions
88#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
89pub struct WindowPosition {
90    pub x: f64,
91    pub y: f64,
92    pub width: f64,
93    pub height: f64,
94}
95
96//================================================================================================
97// procedure
98//================================================================================================
99
100/// Retrieve information about the currently active browser
101///
102/// This function combines window detection (via `active-win-pos-rs`) with
103/// specialized browser information extraction.
104///
105/// # Examples
106///
107/// ```rust
108/// use browser_info::get_active_browser_info;
109///
110/// match get_active_browser_info() {
111///     Ok(info) => {
112///         println!("URL: {}", info.url);
113///         println!("Browser: {:?}", info.browser_type);
114///     }
115///     Err(e) => eprintln!("Failed to get browser info: {}", e),
116/// }
117/// ```
118pub fn get_active_browser_info() -> Result<BrowserInfo, BrowserInfoError> {
119    // Step 0: Check if the active window is browser
120    if !is_browser_active() {
121        return Err(BrowserInfoError::NotABrowser);
122    }
123
124    // Step 1: Definitely browser. Get active window using active-win-pos-rs
125    let window = get_active_window().map_err(|_| BrowserInfoError::WindowNotFound)?;
126
127    // Step 2: Verify it's a browser window
128    let browser_type = browser_detection::classify_browser(&window)?;
129
130    // Step 3: Extract URL (and, where the platform provides it, the page title)
131    let page = url_extraction::extract_page(&window, &browser_type)?;
132
133    // タイトルの出所: プラットフォーム抽出(macOSのAppleScript等)を優先し、
134    // 無いときだけウィンドウ名。macOSのウィンドウ名は画面収録の権限が無いと
135    // 空文字になるので、ウィンドウ名を第一の出所にしない。
136    let title = match page.title {
137        Some(title) => title,
138        None => {
139            if window.title.is_empty() {
140                println!(
141                    "⚠️ No page title available: platform extraction gave none and the window title is empty"
142                );
143            }
144            window.title.clone()
145        }
146    };
147
148    // Step 4: Get additional browser metadata
149    let metadata = browser_detection::get_browser_metadata(&window, &browser_type)?;
150
151    Ok(BrowserInfo {
152        url: page.url,
153        title,
154        browser_name: window.app_name,
155        browser_type,
156        version: metadata.version,
157        tabs_count: metadata.tabs_count,
158        is_incognito: metadata.is_incognito,
159        process_id: window.process_id,
160        window_position: WindowPosition {
161            x: window.position.x,
162            y: window.position.y,
163            width: window.position.width,
164            height: window.position.height,
165        },
166    })
167}
168
169/// Get only the URL from the active browser (lightweight version)
170pub fn get_active_browser_url() -> Result<String, BrowserInfoError> {
171    // Step 0: 高速事前チェック
172    if !is_browser_active() {
173        return Err(BrowserInfoError::NotABrowser);
174    }
175
176    let window = get_active_window().map_err(|_| BrowserInfoError::WindowNotFound)?;
177
178    let browser_type = browser_detection::classify_browser(&window)?;
179    url_extraction::extract_url(&window, &browser_type)
180}
181
182/// Check if the currently active window is a browser
183pub fn is_browser_active() -> bool {
184    if let Ok(window) = get_active_window() {
185        browser_detection::classify_browser(&window).is_ok()
186    } else {
187        false
188    }
189}
190
191/// 高速・互換性重視(PowerShell方式)
192pub fn get_browser_info_safe() -> Result<BrowserInfo, BrowserInfoError> {
193    get_active_browser_info()
194}
195
196/// 詳細情報重視(Chrome DevTools - デバッグモード必要)
197#[cfg(any(
198    all(feature = "devtools", target_os = "windows"),
199    all(doc, feature = "devtools")
200))]
201pub async fn get_browser_info_detailed() -> Result<BrowserInfo, BrowserInfoError> {
202    ChromeDevToolsExtractor::extract_browser_info().await
203}
204
205/// 後方互換性のためのエイリアス
206#[cfg(any(
207    all(feature = "devtools", target_os = "windows"),
208    all(doc, feature = "devtools")
209))]
210pub async fn get_browser_info_fast() -> Result<BrowserInfo, BrowserInfoError> {
211    get_browser_info_detailed().await
212}
213
214/// デフォルト(自動判定・推奨)- PowerShell優先
215pub async fn get_browser_info() -> Result<BrowserInfo, BrowserInfoError> {
216    // 1. PowerShell方式を最優先(高速・確実)
217    match get_browser_info_safe() {
218        Ok(info) => {
219            println!("✅ Using PowerShell method (fastest)");
220            return Ok(info);
221        }
222        Err(e) => {
223            println!("⚠️ PowerShell failed: {e}, trying DevTools...");
224        }
225    }
226
227    // 2. PowerShell失敗時のみDevTools
228    #[cfg(all(feature = "devtools", target_os = "windows"))]
229    if ChromeDevToolsExtractor::is_available().await {
230        println!("🔄 Fallback to Chrome DevTools Protocol");
231        return ChromeDevToolsExtractor::extract_browser_info().await;
232    }
233
234    Err(BrowserInfoError::Other(
235        "All extraction methods failed".to_string(),
236    ))
237}
238
239/// 明示的な方法指定
240pub async fn get_browser_info_with_method(
241    method: ExtractionMethod,
242) -> Result<BrowserInfo, BrowserInfoError> {
243    match method {
244        ExtractionMethod::Auto => get_browser_info().await,
245        #[cfg(any(
246            all(feature = "devtools", target_os = "windows"),
247            all(doc, feature = "devtools")
248        ))]
249        ExtractionMethod::DevTools => get_browser_info_detailed().await,
250        #[cfg(not(any(
251            all(feature = "devtools", target_os = "windows"),
252            all(doc, feature = "devtools")
253        )))]
254        ExtractionMethod::DevTools => Err(BrowserInfoError::Other(
255            "DevTools feature not available on this platform".to_string(),
256        )),
257        ExtractionMethod::PowerShell => get_browser_info_safe(),
258    }
259}