use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::cdp::Cdp;
use crate::cdp::command::{CdpCommand, CdpEvent, Empty};
use crate::cdp::ids::{LoaderId, RequestId};
use crate::common::protocol::string_enum;
use crate::error::WebDriverResult;
string_enum! {
pub enum ConnectionType {
None = "none",
Cellular2G = "cellular2g",
Cellular3G = "cellular3g",
Cellular4G = "cellular4g",
Bluetooth = "bluetooth",
Ethernet = "ethernet",
Wifi = "wifi",
Wimax = "wimax",
Other = "other",
}
}
string_enum! {
pub enum ErrorReason {
Failed = "Failed",
Aborted = "Aborted",
TimedOut = "TimedOut",
AccessDenied = "AccessDenied",
ConnectionClosed = "ConnectionClosed",
ConnectionReset = "ConnectionReset",
ConnectionRefused = "ConnectionRefused",
ConnectionAborted = "ConnectionAborted",
ConnectionFailed = "ConnectionFailed",
NameNotResolved = "NameNotResolved",
InternetDisconnected = "InternetDisconnected",
AddressUnreachable = "AddressUnreachable",
BlockedByClient = "BlockedByClient",
BlockedByResponse = "BlockedByResponse",
}
}
string_enum! {
pub enum ResourceType {
Document = "Document",
Stylesheet = "Stylesheet",
Image = "Image",
Media = "Media",
Font = "Font",
Script = "Script",
TextTrack = "TextTrack",
Xhr = "XHR",
Fetch = "Fetch",
Prefetch = "Prefetch",
EventSource = "EventSource",
WebSocket = "WebSocket",
Manifest = "Manifest",
SignedExchange = "SignedExchange",
Ping = "Ping",
CspViolationReport = "CSPViolationReport",
Preflight = "Preflight",
Other = "Other",
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkConditions {
pub offline: bool,
pub latency: u32,
pub download_throughput: i32,
pub upload_throughput: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub connection_type: Option<ConnectionType>,
}
impl Default for NetworkConditions {
fn default() -> Self {
Self {
offline: false,
latency: 0,
download_throughput: -1,
upload_throughput: -1,
connection_type: None,
}
}
}
impl NetworkConditions {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Enable {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_total_buffer_size: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_resource_buffer_size: Option<i64>,
}
impl CdpCommand for Enable {
const METHOD: &'static str = "Network.enable";
type Returns = Empty;
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct Disable;
impl CdpCommand for Disable {
const METHOD: &'static str = "Network.disable";
type Returns = Empty;
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct ClearBrowserCache;
impl CdpCommand for ClearBrowserCache {
const METHOD: &'static str = "Network.clearBrowserCache";
type Returns = Empty;
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct ClearBrowserCookies;
impl CdpCommand for ClearBrowserCookies {
const METHOD: &'static str = "Network.clearBrowserCookies";
type Returns = Empty;
}
#[derive(Debug, Clone, Serialize)]
pub struct SetExtraHttpHeaders {
pub headers: HashMap<String, String>,
}
impl CdpCommand for SetExtraHttpHeaders {
const METHOD: &'static str = "Network.setExtraHTTPHeaders";
type Returns = Empty;
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetUserAgentOverride {
pub user_agent: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub accept_language: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub platform: Option<String>,
}
impl CdpCommand for SetUserAgentOverride {
const METHOD: &'static str = "Network.setUserAgentOverride";
type Returns = Empty;
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EmulateNetworkConditions {
pub offline: bool,
pub latency: u32,
pub download_throughput: i32,
pub upload_throughput: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub connection_type: Option<ConnectionType>,
}
impl From<NetworkConditions> for EmulateNetworkConditions {
fn from(c: NetworkConditions) -> Self {
Self {
offline: c.offline,
latency: c.latency,
download_throughput: c.download_throughput,
upload_throughput: c.upload_throughput,
connection_type: c.connection_type,
}
}
}
impl CdpCommand for EmulateNetworkConditions {
const METHOD: &'static str = "Network.emulateNetworkConditions";
type Returns = Empty;
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetResponseBody {
pub request_id: RequestId,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResponseBody {
pub body: String,
pub base64_encoded: bool,
}
impl CdpCommand for GetResponseBody {
const METHOD: &'static str = "Network.getResponseBody";
type Returns = ResponseBody;
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RequestWillBeSent {
pub request_id: RequestId,
pub loader_id: LoaderId,
#[serde(rename = "documentURL")]
pub document_url: String,
pub request: serde_json::Value,
pub timestamp: f64,
pub wall_time: f64,
pub initiator: serde_json::Value,
}
impl CdpEvent for RequestWillBeSent {
const METHOD: &'static str = "Network.requestWillBeSent";
const ENABLE: Option<&'static str> = Some("Network.enable");
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResponseReceived {
pub request_id: RequestId,
pub loader_id: LoaderId,
pub timestamp: f64,
pub r#type: ResourceType,
pub response: serde_json::Value,
}
impl CdpEvent for ResponseReceived {
const METHOD: &'static str = "Network.responseReceived";
const ENABLE: Option<&'static str> = Some("Network.enable");
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LoadingFinished {
pub request_id: RequestId,
pub timestamp: f64,
pub encoded_data_length: f64,
}
impl CdpEvent for LoadingFinished {
const METHOD: &'static str = "Network.loadingFinished";
const ENABLE: Option<&'static str> = Some("Network.enable");
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LoadingFailed {
pub request_id: RequestId,
pub timestamp: f64,
pub r#type: ResourceType,
pub error_text: String,
pub canceled: Option<bool>,
}
impl CdpEvent for LoadingFailed {
const METHOD: &'static str = "Network.loadingFailed";
const ENABLE: Option<&'static str> = Some("Network.enable");
}
#[derive(Debug)]
pub struct NetworkDomain<'a> {
cdp: &'a Cdp,
}
impl<'a> NetworkDomain<'a> {
pub(crate) fn new(cdp: &'a Cdp) -> Self {
Self {
cdp,
}
}
pub async fn enable(&self) -> WebDriverResult<()> {
self.cdp.send(Enable::default()).await?;
Ok(())
}
pub async fn disable(&self) -> WebDriverResult<()> {
self.cdp.send(Disable).await?;
Ok(())
}
pub async fn clear_browser_cache(&self) -> WebDriverResult<()> {
self.cdp.send(ClearBrowserCache).await?;
Ok(())
}
pub async fn clear_browser_cookies(&self) -> WebDriverResult<()> {
self.cdp.send(ClearBrowserCookies).await?;
Ok(())
}
pub async fn set_extra_http_headers(
&self,
headers: HashMap<String, String>,
) -> WebDriverResult<()> {
self.cdp
.send(SetExtraHttpHeaders {
headers,
})
.await?;
Ok(())
}
pub async fn set_user_agent_override(
&self,
user_agent: impl Into<String>,
) -> WebDriverResult<()> {
self.cdp
.send(SetUserAgentOverride {
user_agent: user_agent.into(),
accept_language: None,
platform: None,
})
.await?;
Ok(())
}
pub async fn emulate_network_conditions(
&self,
conditions: NetworkConditions,
) -> WebDriverResult<()> {
self.cdp.send(EmulateNetworkConditions::from(conditions)).await?;
Ok(())
}
pub async fn get_response_body(
&self,
request_id: impl Into<RequestId>,
) -> WebDriverResult<ResponseBody> {
self.cdp
.send(GetResponseBody {
request_id: request_id.into(),
})
.await
}
}