use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::{Value, from_value, json, to_value};
use crate::ChromiumCapabilities;
use crate::common::capabilities::chrome::ChromeCapabilities;
use crate::common::capabilities::edge::EdgeCapabilities;
use crate::common::capabilities::firefox::FirefoxCapabilities;
use crate::common::capabilities::ie::InternetExplorerCapabilities;
use crate::common::capabilities::opera::OperaCapabilities;
use crate::common::capabilities::safari::SafariCapabilities;
use crate::error::WebDriverResult;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Capabilities(serde_json::Map<String, Value>);
impl Capabilities {
pub fn new() -> Self {
Self::default()
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.0.get(key)
}
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
self.0.get_mut(key)
}
pub fn set<T>(&mut self, key: impl Into<String>, value: T) -> WebDriverResult<()>
where
T: Serialize,
{
self.0.insert(key.into(), to_value(value)?);
Ok(())
}
pub fn remove(&mut self, key: &str) -> Option<Value> {
self.0.remove(key)
}
pub fn contains_key(&self, key: &str) -> bool {
self.0.contains_key(key)
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> serde_json::map::Iter<'_> {
self.0.iter()
}
}
impl AsRef<Capabilities> for Capabilities {
fn as_ref(&self) -> &Capabilities {
self
}
}
impl AsMut<Capabilities> for Capabilities {
fn as_mut(&mut self) -> &mut Capabilities {
self
}
}
impl From<Capabilities> for Value {
fn from(caps: Capabilities) -> Value {
Value::Object(caps.0)
}
}
impl From<serde_json::Map<String, Value>> for Capabilities {
fn from(map: serde_json::Map<String, Value>) -> Self {
Self(map)
}
}
const W3C_CAPABILITY_NAMES: &[&str] = &[
"acceptInsecureCerts",
"browserName",
"browserVersion",
"platformName",
"pageLoadStrategy",
"proxy",
"setWindowRect",
"timeouts",
"unhandledPromptBehavior",
"strictFileInteractability",
"webSocketUrl",
];
const OSS_W3C_CONVERSION: &[(&str, &str)] = &[
("acceptSslCerts", "acceptInsecureCerts"),
("version", "browserVersion"),
("platform", "platformName"),
];
pub fn make_w3c_caps(caps: &Value) -> Value {
let mut always_match = json!({});
if let Some(caps_map) = caps.as_object() {
for (k, v) in caps_map.iter() {
if !v.is_null() {
for (k_from, k_to) in OSS_W3C_CONVERSION {
if k_from == k {
always_match[k_to] = v.clone();
}
}
}
if W3C_CAPABILITY_NAMES.contains(&k.as_str()) || k.contains(':') {
always_match[k] = v.clone();
}
}
}
json!({
"firstMatch": [{}], "alwaysMatch": always_match
})
}
#[derive(Debug)]
pub struct DesiredCapabilities;
impl DesiredCapabilities {
pub fn chrome() -> ChromeCapabilities {
ChromeCapabilities::new()
}
pub fn chromium() -> ChromiumCapabilities {
ChromiumCapabilities::new()
}
pub fn edge() -> EdgeCapabilities {
EdgeCapabilities::new()
}
pub fn firefox() -> FirefoxCapabilities {
FirefoxCapabilities::new()
}
pub fn internet_explorer() -> InternetExplorerCapabilities {
InternetExplorerCapabilities::new()
}
pub fn opera() -> OperaCapabilities {
OperaCapabilities::new()
}
pub fn safari() -> SafariCapabilities {
SafariCapabilities::new()
}
}
pub trait CapabilitiesHelper: AsRef<Capabilities> + AsMut<Capabilities> {
fn get(&self, key: &str) -> Option<&Value> {
self.as_ref().get(key)
}
fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
self.as_mut().get_mut(key)
}
fn set<T>(&mut self, key: impl Into<String>, value: T) -> WebDriverResult<()>
where
T: Serialize,
{
self.as_mut().set(key, value)
}
fn set_version(&mut self, version: &str) -> WebDriverResult<()> {
self.set("version", version)
}
fn set_platform(&mut self, platform: &str) -> WebDriverResult<()> {
self.set("platform", platform)
}
fn set_javascript_enabled(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("javascriptEnabled", enabled)
}
fn set_database_enabled(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("databaseEnabled", enabled)
}
fn set_location_context_enabled(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("locationContextEnabled", enabled)
}
fn set_application_cache_enabled(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("applicationCacheEnabled", enabled)
}
fn set_browser_connection_enabled(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("browserConnectionEnabled", enabled)
}
fn set_web_storage_enabled(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("webStorageEnabled", enabled)
}
fn accept_insecure_certs(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("acceptInsecureCerts", enabled)
}
fn set_rotatable(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("rotatable", enabled)
}
fn set_native_events(&mut self, enabled: bool) -> WebDriverResult<()> {
self.set("nativeEvents", enabled)
}
fn set_proxy(&mut self, proxy: Proxy) -> WebDriverResult<()> {
self.set("proxy", proxy)
}
fn set_unexpected_alert_behaviour(&mut self, behaviour: AlertBehaviour) -> WebDriverResult<()> {
self.set("unexpectedAlertBehaviour", behaviour)
}
fn set_element_scroll_behaviour(&mut self, behaviour: ScrollBehaviour) -> WebDriverResult<()> {
self.set("elementScrollBehavior", behaviour)
}
fn handles_alerts(&self) -> Option<bool> {
self.as_ref().get("handlesAlerts").and_then(|x| x.as_bool())
}
fn css_selectors_enabled(&self) -> Option<bool> {
self.as_ref().get("cssSelectorsEnabled").and_then(|x| x.as_bool())
}
fn page_load_strategy(&self) -> WebDriverResult<PageLoadStrategy> {
let strategy =
self.as_ref().get("pageLoadStrategy").map(|x| from_value(x.clone())).transpose()?;
Ok(strategy.unwrap_or_default())
}
fn set_page_load_strategy(&mut self, strategy: PageLoadStrategy) -> WebDriverResult<()> {
self.set("pageLoadStrategy", strategy)
}
fn enable_bidi(&mut self) -> WebDriverResult<()> {
self.set("webSocketUrl", true)
}
}
impl<T: AsRef<Capabilities> + AsMut<Capabilities>> CapabilitiesHelper for T {}
pub trait BrowserCapabilitiesHelper: CapabilitiesHelper {
const KEY: &'static str;
fn set_browser_option(
&mut self,
key: impl Into<String>,
value: impl Serialize,
) -> WebDriverResult<()> {
let key = key.into();
let value = to_value(value)?;
match self.as_mut().get_mut(Self::KEY) {
Some(Value::Object(v)) => {
v.insert(key, value);
}
_ => {
self.as_mut().set(Self::KEY, json!({ key: value }))?;
}
}
Ok(())
}
fn remove_browser_option(&mut self, key: &str) {
if let Some(Value::Object(v)) = self.as_mut().get_mut(Self::KEY) {
v.remove(key);
}
}
fn browser_option<T>(&self, key: &str) -> Option<T>
where
T: DeserializeOwned,
{
self.as_ref()
.get(Self::KEY)
.and_then(|options| options.get(key))
.and_then(|option| from_value(option.clone()).ok())
}
fn args(&self) -> Vec<String> {
self.browser_option("args").unwrap_or_default()
}
fn remove_arg(&mut self, arg: &str) -> WebDriverResult<()> {
let mut args = self.args();
if args.is_empty() {
Ok(())
} else {
args.retain(|v| v != arg);
self.set_browser_option("args", to_value(args)?)
}
}
fn has_arg(&self, arg: &str) -> bool {
self.args().iter().any(|s| s == arg)
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "proxyType", rename_all = "lowercase")]
pub enum Proxy {
Direct,
#[serde(rename_all = "camelCase")]
Manual {
#[serde(skip_serializing_if = "Option::is_none")]
ftp_proxy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
http_proxy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
ssl_proxy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
socks_proxy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
socks_version: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
socks_username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
socks_password: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
no_proxy: Option<Vec<String>>,
},
#[serde(rename = "pac")]
AutoConfig {
#[serde(rename = "proxyAutoconfigUrl")]
url: String,
},
AutoDetect,
System,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AlertBehaviour {
Accept,
Dismiss,
Ignore,
}
#[derive(Debug, Clone, Serialize)]
#[repr(u8)]
pub enum ScrollBehaviour {
Top = 0,
Bottom = 1,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PageLoadStrategy {
#[default]
Normal,
Eager,
None,
}