use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{Mutex, RwLock, oneshot, watch};
use tracing::debug;
use viewpoint_cdp::CdpConnection;
use super::super::download::{Download, DownloadState};
use super::super::file_chooser::FileChooser;
use super::types::{DownloadHandler, FileChooserHandler};
use crate::error::PageError;
pub(crate) struct DownloadTracker {
pub state_tx: watch::Sender<DownloadState>,
pub path_tx: watch::Sender<Option<PathBuf>>,
}
pub(super) async fn handle_download_begin(
_connection: &Arc<CdpConnection>,
_session_id: &str,
downloads: &Mutex<HashMap<String, DownloadTracker>>,
download_handler: &RwLock<Option<DownloadHandler>>,
wait_for_download_tx: &Mutex<Option<oneshot::Sender<Download>>>,
guid: String,
suggested_filename: String,
url: String,
) {
debug!(guid = %guid, filename = %suggested_filename, "Download started");
let (state_tx, state_rx) = watch::channel(DownloadState::InProgress);
let (path_tx, path_rx) = watch::channel(None);
let download = Download::new(guid.clone(), url, suggested_filename, state_rx, path_rx);
{
let mut downloads = downloads.lock().await;
downloads.insert(guid, DownloadTracker { state_tx, path_tx });
}
{
let mut waiter = wait_for_download_tx.lock().await;
if let Some(tx) = waiter.take() {
let _ = tx.send(download);
return;
}
}
let handler = download_handler.read().await;
if let Some(ref h) = *handler {
h(download).await;
}
}
pub(super) async fn handle_download_progress(
downloads: &Mutex<HashMap<String, DownloadTracker>>,
guid: String,
state: &str,
) {
let downloads = downloads.lock().await;
if let Some(tracker) = downloads.get(&guid) {
let new_state = match state {
"completed" => DownloadState::Completed,
"canceled" => DownloadState::Canceled,
_ => DownloadState::InProgress,
};
let _ = tracker.state_tx.send(new_state);
if state == "completed" {
}
}
}
pub(super) async fn handle_file_chooser_event(
connection: &Arc<CdpConnection>,
session_id: &str,
file_chooser_handler: &RwLock<Option<FileChooserHandler>>,
wait_for_file_chooser_tx: &Mutex<Option<oneshot::Sender<FileChooser>>>,
frame_id: String,
mode: viewpoint_cdp::protocol::page::FileChooserMode,
backend_node_id: Option<i32>,
) {
debug!(frame_id = %frame_id, mode = ?mode, "File chooser opened");
let is_multiple = matches!(
mode,
viewpoint_cdp::protocol::page::FileChooserMode::SelectMultiple
);
let chooser = FileChooser::new(
connection.clone(),
session_id.to_string(),
frame_id,
backend_node_id,
is_multiple,
);
{
let mut waiter = wait_for_file_chooser_tx.lock().await;
if let Some(tx) = waiter.take() {
let _ = tx.send(chooser);
return;
}
}
let handler = file_chooser_handler.read().await;
if let Some(ref h) = *handler {
h(chooser).await;
}
}
pub(super) async fn wait_for_download(
wait_for_download_tx: &Mutex<Option<oneshot::Sender<Download>>>,
timeout: Duration,
) -> Result<Download, PageError> {
let (tx, rx) = oneshot::channel();
{
let mut waiter = wait_for_download_tx.lock().await;
*waiter = Some(tx);
}
tokio::time::timeout(timeout, rx)
.await
.map_err(|_| PageError::EvaluationFailed("Timeout waiting for download".to_string()))?
.map_err(|_| PageError::EvaluationFailed("Download wait cancelled".to_string()))
}
pub(super) async fn register_download_waiter(
wait_for_download_tx: &Mutex<Option<oneshot::Sender<Download>>>,
) -> oneshot::Receiver<Download> {
let (tx, rx) = oneshot::channel();
{
let mut waiter = wait_for_download_tx.lock().await;
*waiter = Some(tx);
}
rx
}
pub(super) async fn await_download_waiter(
rx: oneshot::Receiver<Download>,
timeout: Duration,
) -> Result<Download, PageError> {
tokio::time::timeout(timeout, rx)
.await
.map_err(|_| PageError::EvaluationFailed("Timeout waiting for download".to_string()))?
.map_err(|_| PageError::EvaluationFailed("Download wait cancelled".to_string()))
}
pub(super) async fn wait_for_file_chooser(
wait_for_file_chooser_tx: &Mutex<Option<oneshot::Sender<FileChooser>>>,
timeout: Duration,
) -> Result<FileChooser, PageError> {
let (tx, rx) = oneshot::channel();
{
let mut waiter = wait_for_file_chooser_tx.lock().await;
*waiter = Some(tx);
}
tokio::time::timeout(timeout, rx)
.await
.map_err(|_| PageError::EvaluationFailed("Timeout waiting for file chooser".to_string()))?
.map_err(|_| PageError::EvaluationFailed("File chooser wait cancelled".to_string()))
}