Skip to main content

browser_info/platform/
chrome_devtools.rs

1//! Chrome DevTools Protocol integration for detailed browser information extraction.
2//!
3//! This module is only available on Windows with the `devtools` feature enabled.
4
5use crate::{BrowserInfo, BrowserInfoError, BrowserType};
6use serde::Deserialize;
7use std::time::Duration;
8
9#[derive(Debug, Deserialize)]
10struct ChromeTab {
11    #[allow(dead_code)]
12    id: String,
13    title: String,
14    url: String,
15    #[serde(rename = "type")]
16    tab_type: String,
17}
18
19pub struct ChromeDevToolsExtractor;
20
21impl ChromeDevToolsExtractor {
22    const DEFAULT_PORT: u16 = 9222;
23    const TIMEOUT_SECS: u64 = 3;
24
25    pub async fn is_available() -> bool {
26        Self::test_connection(Self::DEFAULT_PORT).await
27    }
28
29    async fn test_connection(port: u16) -> bool {
30        let client = reqwest::Client::builder()
31            .timeout(Duration::from_secs(Self::TIMEOUT_SECS))
32            .build()
33            .unwrap();
34
35        let url = format!("http://localhost:{port}/json/version");
36        client.get(&url).send().await.is_ok()
37    }
38
39    pub async fn extract_browser_info() -> Result<BrowserInfo, BrowserInfoError> {
40        let tabs = Self::get_tabs(Self::DEFAULT_PORT).await?;
41
42        // 最初に見つかったページタブを返す
43        let active_tab = tabs
44            .into_iter()
45            .find(|tab| tab.tab_type == "page")
46            .ok_or(BrowserInfoError::Other("No active tabs found".to_string()))?;
47
48        Ok(BrowserInfo {
49            url: active_tab.url,
50            title: active_tab.title,
51            browser_name: "Chrome".to_string(),
52            browser_type: BrowserType::Chrome,
53            version: None,       // DevTools APIからは簡単には取得できない
54            tabs_count: None,    // 今回は簡略化
55            is_incognito: false, // 今回は簡略化
56            process_id: 0,       // DevTools APIからは取得できない
57            window_position: Default::default(), // Default trait使用
58        })
59    }
60
61    async fn get_tabs(port: u16) -> Result<Vec<ChromeTab>, BrowserInfoError> {
62        let client = reqwest::Client::builder()
63            .timeout(Duration::from_secs(Self::TIMEOUT_SECS))
64            .build()
65            .map_err(|e| BrowserInfoError::Other(format!("Network error: {e}")))?;
66
67        let url = format!("http://localhost:{port}/json");
68        let response = client
69            .get(&url)
70            .send()
71            .await
72            .map_err(|e| BrowserInfoError::Other(format!("Network error: {e}")))?;
73
74        let tabs: Vec<ChromeTab> = response
75            .json()
76            .await
77            .map_err(|e| BrowserInfoError::Other(format!("Parse error: {e}")))?;
78
79        Ok(tabs)
80    }
81}