use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use log::debug;
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_json::Value;
use tokio::net::TcpStream;
use tokio::sync::{Mutex, oneshot};
use tokio::task;
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream, connect_async};
use crate::command_sender;
use crate::commands;
use crate::error::{CommandError, SessionError};
use crate::events::EventType;
use crate::message_handler;
use crate::model::browser::ClientWindowInfo;
use crate::model::browser::*;
use crate::model::browsing_context::*;
use crate::model::common::EmptyParams;
use crate::model::emulation::*;
use crate::model::input::*;
use crate::model::network::*;
use crate::model::result::EmptyResult;
use crate::model::script::EvaluateResult;
use crate::model::script::*;
use crate::model::session::*;
use crate::model::storage::*;
use crate::model::web_extension::*;
use crate::webdriver::capabilities::CapabilitiesRequest;
use crate::webdriver::session;
pub type EventHandler =
Box<dyn Fn(Value) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
#[derive(Clone)]
pub struct WebDriverBiDiSession {
pub host: String,
pub port: u16,
pub base_url: String,
pub session_id: String,
pub capabilities: CapabilitiesRequest,
pub websocket_url: String,
pub websocket_stream: Option<Arc<Mutex<WebSocketStream<MaybeTlsStream<TcpStream>>>>>,
pub pending_commands: Arc<Mutex<HashMap<u64, oneshot::Sender<Value>>>>,
event_handlers: Arc<Mutex<HashMap<EventType, EventHandler>>>,
}
impl WebDriverBiDiSession {
pub fn new(host: String, port: u16, capabilities: CapabilitiesRequest) -> Self {
let base_url = format!("http://{}:{}", host, port);
debug!("Constructed base URL: {}", base_url);
Self {
host,
port,
base_url,
session_id: String::new(),
capabilities,
websocket_url: String::new(),
websocket_stream: None,
pending_commands: Arc::new(Mutex::new(HashMap::new())),
event_handlers: Arc::new(Mutex::new(HashMap::new())),
}
}
pub async fn start(&mut self) -> Result<(), SessionError> {
let session = session::start_session(&self.base_url, &self.capabilities)
.await
.map_err(|e| SessionError::Other(format!("Failed to start session: {}", e)))?;
self.session_id = session.session_id;
self.websocket_url = session.websocket_url;
debug!("Establishing the WebSocket connection");
let (stream, _) = connect_async(&self.websocket_url)
.await
.map_err(|e| SessionError::Other(format!("Failed to connect to WebSocket: {}", e)))?;
let websocket_stream = Arc::new(Mutex::new(stream));
self.websocket_stream = Some(websocket_stream.clone());
let pending_commands = self.pending_commands.clone();
let event_handlers = self.event_handlers.clone();
debug!("Starting the incoming messages management loop");
self.spawn_message_handler_task(websocket_stream, pending_commands, event_handlers);
Ok(())
}
pub async fn close(&mut self) -> Result<(), SessionError> {
session::close_session(&self.base_url, &self.session_id).await?;
Ok(())
}
pub async fn send_command<T: Serialize, U: DeserializeOwned>(
&mut self,
command: T,
) -> Result<U, CommandError> {
if let Some(websocket_stream) = &self.websocket_stream {
command_sender::send_command(
websocket_stream.clone(),
self.pending_commands.clone(),
command,
)
.await
} else {
let error_msg = "WebSocket stream not initialized.";
Err(CommandError::Other(error_msg.into()))
}
}
fn spawn_message_handler_task(
&self,
websocket_stream: Arc<Mutex<WebSocketStream<MaybeTlsStream<TcpStream>>>>,
pending_commands: Arc<Mutex<HashMap<u64, oneshot::Sender<Value>>>>,
event_handlers: Arc<Mutex<HashMap<EventType, EventHandler>>>,
) {
task::spawn(message_handler::handle_messages(
websocket_stream,
pending_commands,
event_handlers,
));
}
pub async fn register_event_handler<F, Fut>(&mut self, event_type: EventType, handler: F)
where
F: Fn(Value) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
debug!("Registring event handler for event: {:?}", event_type);
let mut handlers = self.event_handlers.lock().await;
handlers.insert(event_type, Box::new(move |event| Box::pin(handler(event))));
}
pub async fn unregister_event_handler(&mut self, event_type: EventType) {
let mut handlers = self.event_handlers.lock().await;
handlers.remove(&event_type);
}
}
impl WebDriverBiDiSession {
pub async fn browsing_context_activate(
&mut self,
params: ActivateParameters,
) -> Result<EmptyResult, CommandError> {
commands::browsing_context::activate(self, params).await
}
pub async fn browsing_context_capture_screenshot(
&mut self,
params: CaptureScreenshotParameters,
) -> Result<CaptureScreenshotResult, CommandError> {
commands::browsing_context::capture_screenshot(self, params).await
}
pub async fn browsing_context_close(
&mut self,
params: CloseParameters,
) -> Result<EmptyResult, CommandError> {
commands::browsing_context::close(self, params).await
}
pub async fn browsing_context_create(
&mut self,
params: CreateParameters,
) -> Result<CreateResult, CommandError> {
commands::browsing_context::create(self, params).await
}
pub async fn browsing_context_get_tree(
&mut self,
params: GetTreeParameters,
) -> Result<GetTreeResult, CommandError> {
commands::browsing_context::get_tree(self, params).await
}
pub async fn browsing_context_handle_user_prompt(
&mut self,
params: HandleUserPromptParameters,
) -> Result<EmptyResult, CommandError> {
commands::browsing_context::handle_user_prompt(self, params).await
}
pub async fn browsing_context_locate_nodes(
&mut self,
params: LocateNodesParameters,
) -> Result<LocateNodesResult, CommandError> {
commands::browsing_context::locate_nodes(self, params).await
}
pub async fn browsing_context_navigate(
&mut self,
params: NavigateParameters,
) -> Result<NavigateResult, CommandError> {
commands::browsing_context::navigate(self, params).await
}
pub async fn browsing_context_print(
&mut self,
params: PrintParameters,
) -> Result<PrintResult, CommandError> {
commands::browsing_context::print(self, params).await
}
pub async fn browsing_context_reload(
&mut self,
params: ReloadParameters,
) -> Result<NavigateResult, CommandError> {
commands::browsing_context::reload(self, params).await
}
pub async fn browsing_context_set_viewport(
&mut self,
params: SetViewportParameters,
) -> Result<EmptyResult, CommandError> {
commands::browsing_context::set_viewport(self, params).await
}
pub async fn browsing_context_traverse_history(
&mut self,
params: TraverseHistoryParameters,
) -> Result<TraverseHistoryResult, CommandError> {
commands::browsing_context::traverse_history(self, params).await
}
}
impl WebDriverBiDiSession {
pub async fn session_status(
&mut self,
params: EmptyParams,
) -> Result<StatusResult, CommandError> {
commands::session::status(self, params).await
}
pub async fn session_new(&mut self, params: NewParameters) -> Result<NewResult, CommandError> {
commands::session::new(self, params).await
}
pub async fn session_end(&mut self, params: EmptyParams) -> Result<EmptyResult, CommandError> {
commands::session::end(self, params).await
}
pub async fn session_subscribe(
&mut self,
params: SubscriptionRequest,
) -> Result<SubscribeResult, CommandError> {
commands::session::subscribe(self, params).await
}
pub async fn session_unsubscribe(
&mut self,
params: UnsubscribeParameters,
) -> Result<EmptyResult, CommandError> {
commands::session::unsubscribe(self, params).await
}
}
impl WebDriverBiDiSession {
pub async fn browser_close(
&mut self,
params: EmptyParams,
) -> Result<EmptyResult, CommandError> {
commands::browser::close(self, params).await
}
pub async fn browser_create_user_context(
&mut self,
params: CreateUserContextParameters,
) -> Result<CreateUserContextResult, CommandError> {
commands::browser::create_user_context(self, params).await
}
pub async fn browser_get_client_windows(
&mut self,
params: EmptyParams,
) -> Result<GetClientWindowsResult, CommandError> {
commands::browser::get_client_windows(self, params).await
}
pub async fn browser_get_user_contexts(
&mut self,
params: EmptyParams,
) -> Result<GetUserContextsResult, CommandError> {
commands::browser::get_user_contexts(self, params).await
}
pub async fn browser_remove_user_context(
&mut self,
params: RemoveUserContextParameters,
) -> Result<EmptyResult, CommandError> {
commands::browser::remove_user_context(self, params).await
}
pub async fn browser_set_client_window_state(
&mut self,
params: SetClientWindowStateParameters,
) -> Result<ClientWindowInfo, CommandError> {
commands::browser::set_client_window_state(self, params).await
}
}
impl WebDriverBiDiSession {
pub async fn set_geolocation_override(
&mut self,
params: SetGeolocationOverrideParameters,
) -> Result<EmptyResult, CommandError> {
commands::emulation::set_geolocation_override(self, params).await
}
pub async fn set_locale_override(
&mut self,
params: SetLocaleOverrideParameters,
) -> Result<EmptyResult, CommandError> {
commands::emulation::set_locale_override(self, params).await
}
pub async fn set_screen_orientation_override(
&mut self,
params: SetScreenOrientationOverrideParameters,
) -> Result<EmptyResult, CommandError> {
commands::emulation::set_screen_orientation_override(self, params).await
}
pub async fn set_timezone_override(
&mut self,
params: SetTimezoneOverrideParameters,
) -> Result<EmptyResult, CommandError> {
commands::emulation::set_timezone_override(self, params).await
}
}
impl WebDriverBiDiSession {
pub async fn network_add_data_collector(
&mut self,
params: AddDataCollectorParameters,
) -> Result<AddDataCollectorResult, CommandError> {
commands::network::add_data_collector(self, params).await
}
pub async fn network_add_intercept(
&mut self,
params: AddInterceptParameters,
) -> Result<AddInterceptResult, CommandError> {
commands::network::add_intercept(self, params).await
}
pub async fn network_continue_request(
&mut self,
params: ContinueRequestParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::continue_request(self, params).await
}
pub async fn network_continue_response(
&mut self,
params: ContinueResponseParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::continue_response(self, params).await
}
pub async fn network_continue_with_auth(
&mut self,
params: ContinueWithAuthParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::continue_with_auth(self, params).await
}
pub async fn network_disown_data(
&mut self,
params: DisownDataParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::disown_data(self, params).await
}
pub async fn network_fail_request(
&mut self,
params: FailRequestParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::fail_request(self, params).await
}
pub async fn network_get_data(
&mut self,
params: GetDataParameters,
) -> Result<GetDataResult, CommandError> {
commands::network::get_data(self, params).await
}
pub async fn network_provide_response(
&mut self,
params: ProvideResponseParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::provide_response(self, params).await
}
pub async fn network_remove_data_collector(
&mut self,
params: RemoveDataCollectorParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::remove_data_collector(self, params).await
}
pub async fn network_remove_intercept(
&mut self,
params: RemoveInterceptParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::remove_intercept(self, params).await
}
pub async fn network_set_cache_behavior(
&mut self,
params: SetCacheBehaviorParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::set_cache_behavior(self, params).await
}
pub async fn network_set_extra_headers(
&mut self,
params: SetExtraHeadersParameters,
) -> Result<EmptyResult, CommandError> {
commands::network::set_extra_headers(self, params).await
}
}
impl WebDriverBiDiSession {
pub async fn script_add_preload_script(
&mut self,
params: AddPreloadScriptParameters,
) -> Result<AddPreloadScriptResult, CommandError> {
commands::script::add_preload_script(self, params).await
}
pub async fn script_disown(
&mut self,
params: DisownParameters,
) -> Result<EmptyResult, CommandError> {
commands::script::disown(self, params).await
}
pub async fn script_call_function(
&mut self,
params: CallFunctionParameters,
) -> Result<EvaluateResult, CommandError> {
commands::script::call_function(self, params).await
}
pub async fn script_evaluate(
&mut self,
params: EvaluateParameters,
) -> Result<EvaluateResult, CommandError> {
commands::script::evaluate(self, params).await
}
pub async fn script_get_realms(
&mut self,
params: GetRealmsParameters,
) -> Result<GetRealmsResult, CommandError> {
commands::script::get_realms(self, params).await
}
pub async fn script_remove_preload_script(
&mut self,
params: RemovePreloadScriptParameters,
) -> Result<EmptyResult, CommandError> {
commands::script::remove_preload_script(self, params).await
}
}
impl WebDriverBiDiSession {
pub async fn storage_get_cookies(
&mut self,
params: GetCookiesParameters,
) -> Result<GetCookiesResult, CommandError> {
commands::storage::get_cookies(self, params).await
}
pub async fn storage_set_cookie(
&mut self,
params: SetCookieParameters,
) -> Result<SetCookieResult, CommandError> {
commands::storage::set_cookie(self, params).await
}
pub async fn storage_delete_cookies(
&mut self,
params: DeleteCookiesParameters,
) -> Result<DeleteCookiesResult, CommandError> {
commands::storage::delete_cookies(self, params).await
}
}
impl WebDriverBiDiSession {
pub async fn input_perform_actions(
&mut self,
params: PerformActionsParameters,
) -> Result<EmptyResult, CommandError> {
commands::input::perform_actions(self, params).await
}
pub async fn input_release_actions(
&mut self,
params: ReleaseActionsParameters,
) -> Result<EmptyResult, CommandError> {
commands::input::release_actions(self, params).await
}
pub async fn input_set_files(
&mut self,
params: SetFilesParameters,
) -> Result<EmptyResult, CommandError> {
commands::input::set_files(self, params).await
}
}
impl WebDriverBiDiSession {
pub async fn web_extension_install(
&mut self,
params: InstallParameters,
) -> Result<InstallResult, CommandError> {
commands::web_extension::install(self, params).await
}
pub async fn web_extension_uninstall(
&mut self,
params: UninstallParameters,
) -> Result<EmptyResult, CommandError> {
commands::web_extension::uninstall(self, params).await
}
}