use std::sync::Arc;
use std::time::Duration;
use tokio::sync::oneshot;
use tracing::{debug, info, instrument};
use viewpoint_cdp::protocol::target_domain::{CreateTargetParams, CreateTargetResult};
use crate::error::ContextError;
use crate::page::Page;
use super::BrowserContext;
impl BrowserContext {
#[instrument(level = "info", skip(self), fields(context_id = %self.context_id))]
pub async fn new_page(&self) -> Result<Page, ContextError> {
if self.closed {
return Err(ContextError::Closed);
}
info!("Creating new page");
let (tx, rx) = oneshot::channel::<Page>();
let tx = Arc::new(tokio::sync::Mutex::new(Some(tx)));
let tx_clone = tx.clone();
let handler_id = self
.event_manager
.on_page(move |page| {
let tx = tx_clone.clone();
async move {
let mut guard = tx.lock().await;
if let Some(sender) = guard.take() {
let _ = sender.send(page);
}
}
})
.await;
let create_result: Result<CreateTargetResult, _> = self
.connection
.send_command(
"Target.createTarget",
Some(CreateTargetParams {
url: "about:blank".to_string(),
width: None,
height: None,
browser_context_id: Some(self.context_id.clone()),
background: None,
new_window: None,
}),
None,
)
.await;
if let Err(e) = create_result {
self.event_manager.off_page(handler_id).await;
return Err(e.into());
}
let timeout_duration = Duration::from_secs(30);
let page_result = tokio::time::timeout(timeout_duration, rx).await;
self.event_manager.off_page(handler_id).await;
match page_result {
Ok(Ok(page)) => {
if let Err(e) = self.apply_init_scripts_to_session(page.session_id()).await {
debug!("Failed to apply init scripts: {}", e);
}
info!(
target_id = %page.target_id(),
session_id = %page.session_id(),
"Page created successfully"
);
Ok(page)
}
Ok(Err(_)) => Err(ContextError::Internal(
"Page channel closed unexpectedly".to_string(),
)),
Err(_) => Err(ContextError::Timeout {
operation: "new_page".to_string(),
duration: timeout_duration,
}),
}
}
pub async fn pages(&self) -> Result<Vec<Page>, ContextError> {
if self.closed {
return Err(ContextError::Closed);
}
let pages_guard = self.pages.read().await;
Ok(pages_guard.iter().map(Page::clone_internal).collect())
}
pub async fn page_count(&self) -> Result<usize, ContextError> {
if self.closed {
return Err(ContextError::Closed);
}
let pages_guard = self.pages.read().await;
Ok(pages_guard.len())
}
}