darkwing/server/services/browser_profile_services/config/
hints.rsuse crate::{
database::browser_profile::Platform,
server::{
dtos::{
browser_profile_dto::BrowserProfileFullData, start_dto::StartRequest,
},
error::Error,
},
};
use serde::{Deserialize, Serialize};
use super::{
consts::{
CHROME_VERSION_MAP, LATEST_CHROME_FULL_VERSION, LATEST_CHROME_VERSION,
},
FromStartRequest, Navigator, Screen,
};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Hints {
pub(super) architecture: String,
pub(super) bitness: String,
pub(super) model: String,
pub(super) platform: String,
pub(super) platform_version: String,
pub(super) ua_full_version: String,
pub(super) mobile: bool,
pub(super) ua_header: String,
pub(super) viewport_width: i16,
}
impl Hints {
pub fn make_architecture(
bp: &BrowserProfileFullData,
) -> Result<String, Error> {
let mut architecture = "x86".to_string();
if let Some(renderer) = &bp.webgl_info.renderer {
if renderer.contains("Apple M") || renderer.contains("AppleM") {
architecture = "arm".to_string();
}
};
Ok(architecture)
}
pub fn make_platform(bp: &BrowserProfileFullData) -> Result<String, Error> {
let platform = match bp.platform {
Platform::Windows => "Windows".to_string(),
Platform::Macos => "macOS".to_string(),
Platform::Linux => "Linux".to_string(),
};
Ok(platform)
}
pub fn make_platform_version(
bp: &BrowserProfileFullData,
) -> Result<String, Error> {
let platform_version = if !bp.platform_version.is_empty() {
bp.platform_version.clone()
} else {
match bp.platform {
Platform::Windows => "10.0.0".to_string(),
Platform::Macos => "15.0.0".to_string(),
Platform::Linux => "6.5.0".to_string(),
}
};
Ok(platform_version)
}
pub fn make_ua_full_version(
bp: &BrowserProfileFullData,
navigator: &Navigator,
) -> Result<String, Error> {
let major_version: String = navigator
.user_agent
.chars()
.rev()
.take(24)
.skip(21)
.collect();
let latest_major_version = LATEST_CHROME_VERSION.to_string();
let latest_full_version = LATEST_CHROME_FULL_VERSION.as_str();
let version = CHROME_VERSION_MAP
.get(&(major_version.as_str(), bp.platform.clone()))
.unwrap_or(
CHROME_VERSION_MAP
.get(&(latest_major_version.as_str(), bp.platform.clone()))
.unwrap_or(&latest_full_version),
);
Ok(version.to_string())
}
}
impl FromStartRequest<Hints> for Hints {
fn from_start_request(
bp: &BrowserProfileFullData,
_request: &StartRequest,
navigator: &Navigator,
screen: &Screen,
_token: &str,
) -> Result<Self, Error> {
let architecture = Self::make_architecture(bp)?;
let platform_version = Self::make_platform_version(bp)?;
let platform = Self::make_platform(bp)?;
let ua_full_version = Self::make_ua_full_version(bp, navigator)?;
let ua_header =
r#"" Not A;Brand";v="99", "Chromium";v="91", "Google Chrome";v="91""#
.to_string();
let viewport_width = screen.width;
Ok(Self {
architecture,
bitness: "64".to_string(),
model: "".to_string(),
platform,
platform_version,
ua_full_version,
mobile: false,
ua_header,
viewport_width,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::server::dtos::browser_profile_dto::WebglInfo;
use crate::server::services::browser_profile_services::config::consts::LINUX_MOCK_PROFILE;
fn create_base_profile() -> BrowserProfileFullData {
LINUX_MOCK_PROFILE.clone()
}
#[test]
fn test_make_architecture_x86() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.webgl_info = WebglInfo {
renderer: Some("NVIDIA GeForce RTX 3080".to_string()),
..LINUX_MOCK_PROFILE.clone().webgl_info
};
let architecture = Hints::make_architecture(&profile)?;
assert_eq!(architecture, "x86");
Ok(())
}
#[test]
fn test_make_architecture_arm() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.webgl_info = WebglInfo {
renderer: Some("Apple M1".to_string()),
..LINUX_MOCK_PROFILE.clone().webgl_info
};
let architecture = Hints::make_architecture(&profile)?;
assert_eq!(architecture, "arm");
profile.webgl_info.renderer = Some("AppleM2".to_string());
let architecture = Hints::make_architecture(&profile)?;
assert_eq!(architecture, "arm");
Ok(())
}
#[test]
fn test_make_platform() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.platform = Platform::Windows;
assert_eq!(Hints::make_platform(&profile)?, "Windows");
profile.platform = Platform::Macos;
assert_eq!(Hints::make_platform(&profile)?, "macOS");
profile.platform = Platform::Linux;
assert_eq!(Hints::make_platform(&profile)?, "Linux");
Ok(())
}
#[test]
fn test_make_platform_version_custom() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.platform_version = "11.5.2".to_string();
let version = Hints::make_platform_version(&profile)?;
assert_eq!(version, "11.5.2");
Ok(())
}
#[test]
fn test_make_platform_version_default() -> Result<(), Error> {
let mut profile = create_base_profile();
profile.platform_version = "".to_string();
profile.platform = Platform::Windows;
assert_eq!(Hints::make_platform_version(&profile)?, "10.0.0");
profile.platform = Platform::Macos;
assert_eq!(Hints::make_platform_version(&profile)?, "15.0.0");
profile.platform = Platform::Linux;
assert_eq!(Hints::make_platform_version(&profile)?, "6.5.0");
Ok(())
}
#[test]
fn test_make_ua_full_version_from_map() -> Result<(), Error> {
let profile = create_base_profile();
let mut navigator = Navigator::default();
navigator.user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36".to_string();
let version = Hints::make_ua_full_version(&profile, &navigator)?;
println!("version: {}", version);
assert!(CHROME_VERSION_MAP.values().any(|&v| v == version));
Ok(())
}
#[test]
fn test_from_start_request() -> Result<(), Error> {
let profile = create_base_profile();
let request = StartRequest::get_mock();
let navigator = Navigator::default();
let screen = Screen {
width: 1920,
height: 1080,
is_real: false,
dpr: 1.,
depth: 24,
};
let token = String::new();
let hints = Hints::from_start_request(
&profile, &request, &navigator, &screen, &token,
)?;
assert_eq!(hints.bitness, "64");
assert_eq!(hints.model, "");
assert!(!hints.mobile);
assert_eq!(hints.viewport_width, 1920);
assert_eq!(
hints.ua_header,
r#"" Not A;Brand";v="99", "Chromium";v="91", "Google Chrome";v="91""#
);
Ok(())
}
}