use crate::DriverError;
use crate::result::DriverResult;
use headless_chrome::{Browser, LaunchOptions, Tab};
use std::sync::{Arc, Mutex, OnceLock};
use std::time::Duration;
use tracing::{debug, info, warn};
static BROWSER: OnceLock<Arc<Mutex<Option<Arc<Browser>>>>> = OnceLock::new();
static CURRENT_TAB: OnceLock<Arc<Mutex<Option<Arc<Tab>>>>> = OnceLock::new();
pub fn get_or_create_browser() -> DriverResult<Arc<Browser>> {
debug!("Getting or creating browser instance");
let browser_opt = BROWSER.get_or_init(|| Arc::new(Mutex::new(None)));
let mut browser_guard = browser_opt.lock().unwrap();
if browser_guard.is_none() {
info!("Launching new browser window (headful mode)");
let options = LaunchOptions::default_builder().headless(false).window_size(Some((1280, 720))).sandbox(false).build().map_err(|e| {
let err_msg = format!("Failed to build launch options: {}", e);
warn!("{}", err_msg);
return DriverError::execution(err_msg);
})?;
let browser = Browser::new(options).map_err(|e| {
let err_msg = format!("Failed to create browser: {}", e);
warn!("{}", err_msg);
return DriverError::execution(err_msg);
})?;
*browser_guard = Some(Arc::new(browser));
info!("Browser launched successfully");
}
let result = browser_guard
.as_ref()
.ok_or_else(|| {
let err_msg = "Browser not available".to_string();
warn!("{}", err_msg);
return DriverError::execution(err_msg);
})
.map(|b| b.clone());
debug!("Browser instance retrieved");
return result;
}
pub fn get_current_tab() -> DriverResult<Arc<Tab>> {
debug!("Getting current tab");
let _ = get_or_create_browser()?;
let tab_opt = CURRENT_TAB.get_or_init(|| Arc::new(Mutex::new(None)));
let mut tab_guard = tab_opt.lock().unwrap();
if tab_guard.is_none() {
info!("Creating new tab");
let browser = get_or_create_browser()?;
let tab = browser.new_tab().map_err(|e| {
let err_msg = format!("Failed to create new tab: {}", e);
warn!("{}", err_msg);
return DriverError::execution(err_msg);
})?;
*tab_guard = Some(tab);
info!("New tab created");
}
let result = tab_guard
.as_ref()
.ok_or_else(|| {
let err_msg = "No tab available".to_string();
warn!("{}", err_msg);
return DriverError::execution(err_msg);
})
.map(|t| t.clone());
debug!("Current tab retrieved");
return result;
}
pub fn set_current_tab(tab: Arc<Tab>) {
debug!("Setting current tab");
let tab_opt = CURRENT_TAB.get_or_init(|| Arc::new(Mutex::new(None)));
let mut tab_guard = tab_opt.lock().unwrap();
*tab_guard = Some(tab);
debug!("Current tab set");
}
pub fn clear_current_tab() {
debug!("Clearing current tab");
let tab_opt = CURRENT_TAB.get_or_init(|| Arc::new(Mutex::new(None)));
let mut tab_guard = tab_opt.lock().unwrap();
*tab_guard = None;
debug!("Current tab cleared");
}
pub fn close_browser() -> DriverResult<()> {
debug!("Closing browser");
let browser_opt = BROWSER.get_or_init(|| Arc::new(Mutex::new(None)));
let mut browser_guard = browser_opt.lock().unwrap();
if browser_guard.is_some() {
info!("Browser window closed");
*browser_guard = None;
}
clear_current_tab();
debug!("Browser closed successfully");
return Ok(());
}
pub async fn wait_for_stable(_tab: &Tab, wait_ms: u64) {
debug!("Waiting for {}ms for page to stabilize", wait_ms);
tokio::time::sleep(Duration::from_millis(wait_ms)).await;
debug!("Wait complete");
}