browser_info/platform/
chrome_devtools.rs1use 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 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, tabs_count: None, is_incognito: false, process_id: 0, window_position: Default::default(), })
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}