use base64::{Engine as _, engine::general_purpose};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Cookie {
pub name: String,
pub value: String,
pub domain: String,
pub path: String,
pub secure: bool,
#[serde(rename = "httpOnly")]
pub http_only: bool,
#[serde(rename = "expires")]
pub expires: Option<f64>,
#[serde(rename = "sameSite")]
pub same_site: Option<String>,
#[serde(skip)]
pub size: Option<usize>,
#[serde(skip)]
pub third_party: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ViewportConfig {
pub width: u32,
pub height: u32,
pub device_scale_factor: f64,
pub mobile: bool,
pub touch: bool,
pub orientation: Option<String>,
pub scale: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DevicePreset {
pub name: String,
pub viewport: ViewportConfig,
pub user_agent: String,
pub accept_language: Option<String>,
}
impl Default for ViewportConfig {
fn default() -> Self {
Self {
width: 1280,
height: 720,
device_scale_factor: 1.0,
mobile: false,
touch: false,
orientation: None,
scale: None,
}
}
}
impl DevicePreset {
pub fn iphone_15() -> Self {
Self {
name: "iPhone 15".to_string(),
viewport: ViewportConfig {
width: 393,
height: 852,
device_scale_factor: 3.0,
mobile: true,
touch: true,
orientation: Some("portraitPrimary".to_string()),
scale: None,
},
user_agent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1".to_string(),
accept_language: Some("en-US".to_string()),
}
}
pub fn iphone_15_pro_max() -> Self {
Self {
name: "iPhone 15 Pro Max".to_string(),
viewport: ViewportConfig {
width: 430,
height: 932,
device_scale_factor: 3.0,
mobile: true,
touch: true,
orientation: Some("portraitPrimary".to_string()),
scale: None,
},
user_agent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1".to_string(),
accept_language: Some("en-US".to_string()),
}
}
pub fn ipad_air() -> Self {
Self {
name: "iPad Air".to_string(),
viewport: ViewportConfig {
width: 820,
height: 1180,
device_scale_factor: 2.0,
mobile: true,
touch: true,
orientation: Some("portraitPrimary".to_string()),
scale: None,
},
user_agent: "Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1".to_string(),
accept_language: Some("en-US".to_string()),
}
}
pub fn pixel_8() -> Self {
Self {
name: "Pixel 8".to_string(),
viewport: ViewportConfig {
width: 412,
height: 915,
device_scale_factor: 2.625,
mobile: true,
touch: true,
orientation: Some("portraitPrimary".to_string()),
scale: None,
},
user_agent: "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36".to_string(),
accept_language: Some("en-US".to_string()),
}
}
pub fn galaxy_s24() -> Self {
Self {
name: "Galaxy S24".to_string(),
viewport: ViewportConfig {
width: 384,
height: 824,
device_scale_factor: 3.0,
mobile: true,
touch: true,
orientation: Some("portraitPrimary".to_string()),
scale: None,
},
user_agent: "Mozilla/5.0 (Linux; Android 14; SM-S921B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36".to_string(),
accept_language: Some("en-US".to_string()),
}
}
pub fn desktop_1080p() -> Self {
Self {
name: "Desktop 1080p".to_string(),
viewport: ViewportConfig::default(),
user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36".to_string(),
accept_language: Some("en-US".to_string()),
}
}
pub fn by_name(name: &str) -> Option<Self> {
match name.to_lowercase().as_str() {
"iphone 15" | "iphone15" => Some(Self::iphone_15()),
"iphone 15 pro max" | "iphone15promax" | "iphone 15 pro" => Some(Self::iphone_15_pro_max()),
"ipad air" | "ipadair" | "ipad" => Some(Self::ipad_air()),
"pixel 8" | "pixel8" => Some(Self::pixel_8()),
"galaxy s24" | "galaxys24" | "s24" => Some(Self::galaxy_s24()),
"desktop" | "desktop 1080p" | "1080p" => Some(Self::desktop_1080p()),
_ => None,
}
}
pub fn list_names() -> Vec<&'static str> {
vec![
"iPhone 15",
"iPhone 15 Pro Max",
"iPad Air",
"Pixel 8",
"Galaxy S24",
"Desktop 1080p",
]
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PageMargins {
pub top: Option<f64>,
pub bottom: Option<f64>,
pub left: Option<f64>,
pub right: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PdfConfig {
pub paper_width: Option<f64>,
pub paper_height: Option<f64>,
pub landscape: Option<bool>,
pub display_header_footer: Option<bool>,
pub print_background: Option<bool>,
pub scale: Option<f64>,
pub margins: Option<PageMargins>,
pub page_ranges: Option<String>,
pub header_template: Option<String>,
pub footer_template: Option<String>,
pub prefer_css_page_size: Option<bool>,
}
impl Default for PdfConfig {
fn default() -> Self {
Self {
paper_width: None,
paper_height: None,
landscape: Some(false),
display_header_footer: Some(false),
print_background: Some(false),
scale: None,
margins: None,
page_ranges: None,
header_template: None,
footer_template: None,
prefer_css_page_size: Some(false),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceMetrics {
pub width: u32,
pub height: u32,
pub device_scale_factor: f64,
pub mobile: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DownloadState {
#[serde(rename = "inProgress")]
InProgress,
#[serde(rename = "completed")]
Completed,
#[serde(rename = "cancelled")]
Cancelled,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DownloadInfo {
pub url: String,
pub filename: String,
pub guid: String,
pub total_bytes: Option<u64>,
pub received_bytes: Option<u64>,
pub state: Option<DownloadState>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthCredentials {
pub username: String,
pub password: String,
pub realm: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct HarRequest {
pub method: String,
pub url: String,
pub headers: Vec<HarHeader>,
pub body_size: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub post_data: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct HarResponse {
pub status: u16,
pub status_text: String,
pub headers: Vec<HarHeader>,
pub body_size: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HarHeader {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HarEntry {
pub started_date_time: String,
pub time: f64,
pub request: HarRequest,
pub response: Option<HarResponse>,
#[serde(skip)]
pub request_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HarLog {
pub version: String,
pub creator: HarCreator,
pub entries: Vec<HarEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HarCreator {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
pub timestamp: Option<f64>,
pub js_heap_used_size: Option<u64>,
pub js_heap_total_size: Option<u64>,
pub documents: Option<u32>,
pub nodes: Option<u32>,
pub js_event_listeners: Option<u32>,
pub layout_duration: Option<f64>,
pub recalc_style_duration: Option<f64>,
pub script_duration: Option<f64>,
pub task_duration: Option<f64>,
#[serde(flatten)]
pub raw: std::collections::HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsoleLog {
pub level: String,
pub text: String,
pub url: Option<String>,
pub line: Option<u32>,
pub column: Option<u32>,
pub timestamp: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CookieParam {
pub name: String,
pub value: String,
pub domain: Option<String>,
pub path: Option<String>,
pub secure: Option<bool>,
#[serde(rename = "httpOnly")]
pub http_only: Option<bool>,
#[serde(rename = "expires")]
pub expires: Option<f64>,
#[serde(rename = "sameSite")]
pub same_site: Option<String>,
pub url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TabInfo {
pub url: String,
pub title: String,
#[serde(alias = "tab_id")]
pub target_id: String,
#[serde(alias = "parent_tab_id")]
pub parent_target_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageInfo {
pub viewport_width: u32,
pub viewport_height: u32,
pub page_width: u32,
pub page_height: u32,
pub scroll_x: i32,
pub scroll_y: i32,
pub pixels_above: u32,
pub pixels_below: u32,
pub pixels_left: u32,
pub pixels_right: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkRequest {
pub url: String,
pub method: String,
pub loading_duration_ms: f64,
pub resource_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaginationButton {
pub button_type: String,
pub backend_node_id: u32,
pub text: String,
pub selector: String,
pub is_disabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionInfo {
pub url: String,
pub title: String,
pub target_id: String,
pub session_id: String,
}
impl SessionInfo {
pub fn new(url: String, title: String, target_id: String, session_id: String) -> Self {
Self {
url,
title,
target_id,
session_id,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowserStateSummary {
pub dom_state: crate::dom::views::SerializedDOMState,
pub url: String,
pub title: String,
pub tabs: Vec<TabInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub screenshot: Option<String>,
pub page_info: Option<PageInfo>,
pub pixels_above: u32,
pub pixels_below: u32,
pub browser_errors: Vec<String>,
pub is_pdf_viewer: bool,
pub recent_events: Option<String>,
pub pending_network_requests: Vec<NetworkRequest>,
pub pagination_buttons: Vec<PaginationButton>,
pub closed_popup_messages: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoverageRange {
pub start_offset: u32,
pub end_offset: u32,
pub count: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoverageResult {
pub url: String,
pub content_type: String,
pub text_length: u32,
pub ranges: Vec<CoverageRange>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterceptPattern {
pub url_pattern: String,
pub resource_type: Option<String>,
pub interception_stage: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterceptedRequest {
pub request_id: String,
pub url: String,
pub method: String,
pub headers: Vec<HarHeader>,
pub post_data: Option<String>,
pub resource_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MockResponse {
pub status: u16,
pub status_text: String,
pub headers: Vec<HarHeader>,
pub body: String,
pub base64_encoded: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TraceConfig {
pub categories: Vec<String>,
pub streaming: Option<bool>,
pub buffer_size: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceResult {
pub success: bool,
pub events: Vec<serde_json::Value>,
pub start_time: Option<f64>,
pub end_time: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TouchPoint {
pub x: f64,
pub y: f64,
pub radius_x: Option<f64>,
pub radius_y: Option<f64>,
pub rotation_angle: Option<f64>,
pub force: Option<f64>,
pub id: Option<u32>,
}
impl TouchPoint {
pub fn at(x: f64, y: f64) -> Self {
Self {
x,
y,
radius_x: None,
radius_y: None,
rotation_angle: None,
force: None,
id: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ColorScheme {
#[serde(rename = "light")]
Light,
#[serde(rename = "dark")]
Dark,
#[serde(rename = "no-preference")]
NoPreference,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MediaFeature {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum VisionDeficiency {
#[serde(rename = "none")]
None,
#[serde(rename = "achromatopsia")]
Achromatopsia,
#[serde(rename = "blurredVision")]
BlurredVision,
#[serde(rename = "deuteranopia")]
Deuteranopia,
#[serde(rename = "protanopia")]
Protanopia,
#[serde(rename = "tritanopia")]
Tritanopia,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkerInfo {
pub target_id: String,
pub url: String,
pub worker_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceWorkerRegistration {
pub registration_id: String,
pub scope_url: String,
pub is_deleted: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceWorkerVersion {
pub version_id: String,
pub registration_id: String,
pub script_url: String,
pub status: String,
pub running_status: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PermissionType {
#[serde(rename = "videoCapture")]
VideoCapture,
#[serde(rename = "audioCapture")]
AudioCapture,
#[serde(rename = "geolocation")]
Geolocation,
#[serde(rename = "notifications")]
Notifications,
#[serde(rename = "clipboardReadWrite")]
ClipboardReadWrite,
#[serde(rename = "displayCapture")]
DisplayCapture,
#[serde(rename = "backgroundSync")]
BackgroundSync,
#[serde(rename = "paymentHandler")]
PaymentHandler,
#[serde(rename = "sensors")]
Sensors,
#[serde(rename = "nfc")]
Nfc,
#[serde(rename = "midi")]
Midi,
#[serde(rename = "midiSysex")]
MidiSysex,
#[serde(rename = "wakeLockScreen")]
WakeLockScreen,
#[serde(rename = "wakeLockSystem")]
WakeLockSystem,
#[serde(rename = "storageAccess")]
StorageAccess,
#[serde(rename = "windowManagement")]
WindowManagement,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DialogType {
#[serde(rename = "alert")]
Alert,
#[serde(rename = "confirm")]
Confirm,
#[serde(rename = "prompt")]
Prompt,
#[serde(rename = "beforeunload")]
Beforeunload,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileChooserInfo {
pub frame_id: String,
pub backend_node_id: u32,
pub multiple: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DialogInfo {
pub dialog_type: DialogType,
pub message: String,
pub default_prompt: Option<String>,
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowserStateHistory {
pub url: String,
pub title: String,
pub tabs: Vec<TabInfo>,
pub interacted_element: Vec<Option<crate::dom::views::DOMInteractedElement>>,
pub screenshot_path: Option<String>,
}
impl BrowserStateHistory {
pub fn get_screenshot(&self) -> Option<String> {
if let Some(ref path) = self.screenshot_path
&& let Ok(data) = std::fs::read(path) {
return Some(general_purpose::STANDARD.encode(&data));
}
None
}
pub fn to_dict(&self) -> HashMap<String, serde_json::Value> {
let val = serde_json::to_value(self).unwrap();
match val {
serde_json::Value::Object(map) => map.into_iter().collect(),
other => {
let mut map = HashMap::new();
map.insert("value".to_string(), other);
map
}
}
}
}