|
Packit |
f0b94e |
use command::Parameters;
|
|
Packit |
f0b94e |
use error::{ErrorStatus, WebDriverError, WebDriverResult};
|
|
Packit |
f0b94e |
use rustc_serialize::json::{Json, ToJson};
|
|
Packit |
f0b94e |
use std::collections::BTreeMap;
|
|
Packit |
f0b94e |
use url::Url;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
pub type Capabilities = BTreeMap<String, Json>;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/// Trait for objects that can be used to inspect browser capabilities
|
|
Packit |
f0b94e |
///
|
|
Packit |
f0b94e |
/// The main methods in this trait are called with a Capabilites object
|
|
Packit |
f0b94e |
/// resulting from a full set of potential capabilites for the session.
|
|
Packit |
f0b94e |
/// Given those Capabilities they return a property of the browser instance
|
|
Packit |
f0b94e |
/// that would be initiated. In many cases this will be independent of the
|
|
Packit |
f0b94e |
/// input, but in the case of e.g. browser version, it might depend on a
|
|
Packit |
f0b94e |
/// path to the binary provided as a capability.
|
|
Packit |
f0b94e |
pub trait BrowserCapabilities {
|
|
Packit |
f0b94e |
/// Set up the Capabilites object
|
|
Packit |
f0b94e |
///
|
|
Packit |
f0b94e |
/// Typically used to create any internal caches
|
|
Packit |
f0b94e |
fn init(&mut self, &Capabilities);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/// Name of the browser
|
|
Packit |
f0b94e |
fn browser_name(&mut self, &Capabilities) -> WebDriverResult<Option<String>>;
|
|
Packit |
f0b94e |
/// Version number of the browser
|
|
Packit |
f0b94e |
fn browser_version(&mut self, &Capabilities) -> WebDriverResult<Option<String>>;
|
|
Packit |
f0b94e |
/// Compare actual browser version to that provided in a version specifier
|
|
Packit |
f0b94e |
///
|
|
Packit |
f0b94e |
/// Parameters are the actual browser version and the comparison string,
|
|
Packit |
f0b94e |
/// respectively. The format of the comparison string is implementation-defined.
|
|
Packit |
f0b94e |
fn compare_browser_version(&mut self, version: &str, comparison: &str) -> WebDriverResult<bool>;
|
|
Packit |
f0b94e |
/// Name of the platform/OS
|
|
Packit |
f0b94e |
fn platform_name(&mut self, &Capabilities) -> WebDriverResult<Option<String>>;
|
|
Packit |
f0b94e |
/// Whether insecure certificates are supported
|
|
Packit |
f0b94e |
fn accept_insecure_certs(&mut self, &Capabilities) -> WebDriverResult<bool>;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
fn accept_proxy(&mut self, proxy_settings: &BTreeMap<String, Json>, &Capabilities) -> WebDriverResult<bool>;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/// Type check custom properties
|
|
Packit |
f0b94e |
///
|
|
Packit |
f0b94e |
/// Check that custom properties containing ":" have the correct data types.
|
|
Packit |
f0b94e |
/// Properties that are unrecognised must be ignored i.e. return without
|
|
Packit |
f0b94e |
/// error.
|
|
Packit |
f0b94e |
fn validate_custom(&self, name: &str, value: &Json) -> WebDriverResult<()>;
|
|
Packit |
f0b94e |
/// Check if custom properties are accepted capabilites
|
|
Packit |
f0b94e |
///
|
|
Packit |
f0b94e |
/// Check that custom properties containing ":" are compatible with
|
|
Packit |
f0b94e |
/// the implementation.
|
|
Packit |
f0b94e |
fn accept_custom(&mut self, name: &str, value: &Json, merged: &Capabilities) -> WebDriverResult<bool>;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/// Trait to abstract over various version of the new session parameters
|
|
Packit |
f0b94e |
///
|
|
Packit |
f0b94e |
/// This trait is expected to be implemented on objects holding the capabilities
|
|
Packit |
f0b94e |
/// from a new session command.
|
|
Packit |
f0b94e |
pub trait CapabilitiesMatching {
|
|
Packit |
f0b94e |
/// Match the BrowserCapabilities against some candidate capabilites
|
|
Packit |
f0b94e |
///
|
|
Packit |
f0b94e |
/// Takes a BrowserCapabilites object and returns a set of capabilites that
|
|
Packit |
f0b94e |
/// are valid for that browser, if any, or None if there are no matching
|
|
Packit |
f0b94e |
/// capabilities.
|
|
Packit |
f0b94e |
fn match_browser<T: BrowserCapabilities>(&self, browser_capabilities: &mut T)
|
|
Packit |
f0b94e |
-> WebDriverResult<Option<Capabilities>>;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#[derive(Debug, PartialEq)]
|
|
Packit |
f0b94e |
pub struct SpecNewSessionParameters {
|
|
Packit |
f0b94e |
pub alwaysMatch: Capabilities,
|
|
Packit |
f0b94e |
pub firstMatch: Vec<Capabilities>,
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
impl SpecNewSessionParameters {
|
|
Packit |
f0b94e |
fn validate<T: BrowserCapabilities>(&self,
|
|
Packit |
f0b94e |
mut capabilities: Capabilities,
|
|
Packit |
f0b94e |
browser_capabilities: &T) -> WebDriverResult<Capabilities> {
|
|
Packit |
f0b94e |
// Filter out entries with the value `null`
|
|
Packit |
f0b94e |
let null_entries = capabilities
|
|
Packit |
f0b94e |
.iter()
|
|
Packit |
f0b94e |
.filter(|&(_, ref value)| **value == Json::Null)
|
|
Packit |
f0b94e |
.map(|(k, _)| k.clone())
|
|
Packit |
f0b94e |
.collect::<Vec<String>>();
|
|
Packit |
f0b94e |
for key in null_entries {
|
|
Packit |
f0b94e |
capabilities.remove(&key);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (key, value) in capabilities.iter() {
|
|
Packit |
f0b94e |
match &**key {
|
|
Packit |
f0b94e |
"acceptInsecureCerts" => if !value.is_boolean() {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("acceptInsecureCerts is not boolean: {}", value)))
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
x @ "browserName" |
|
|
Packit |
f0b94e |
x @ "browserVersion" |
|
|
Packit |
f0b94e |
x @ "platformName" => if !value.is_string() {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("{} is not a string: {}", x, value)))
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
"pageLoadStrategy" => {
|
|
Packit |
f0b94e |
try!(SpecNewSessionParameters::validate_page_load_strategy(value))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
"proxy" => {
|
|
Packit |
f0b94e |
try!(SpecNewSessionParameters::validate_proxy(value))
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
"timeouts" => {
|
|
Packit |
f0b94e |
try!(SpecNewSessionParameters::validate_timeouts(value))
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
"unhandledPromptBehavior" => {
|
|
Packit |
f0b94e |
try!(SpecNewSessionParameters::validate_unhandled_prompt_behaviour(value))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
x => {
|
|
Packit |
f0b94e |
if !x.contains(":") {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("{} is not the name of a known capability or extension capability", x)))
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
try!(browser_capabilities.validate_custom(x, value));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
Ok(capabilities)
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
fn validate_page_load_strategy(value: &Json) -> WebDriverResult<()> {
|
|
Packit |
f0b94e |
match value {
|
|
Packit |
f0b94e |
&Json::String(ref x) => {
|
|
Packit |
f0b94e |
match &**x {
|
|
Packit |
f0b94e |
"normal" |
|
|
Packit |
f0b94e |
"eager" |
|
|
Packit |
f0b94e |
"none" => {},
|
|
Packit |
f0b94e |
x => {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("Invalid page load strategy: {}", x)))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
_ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"pageLoadStrategy is not a string"))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
Ok(())
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
fn validate_proxy(proxy_value: &Json) -> WebDriverResult<()> {
|
|
Packit |
f0b94e |
let obj = try_opt!(proxy_value.as_object(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"proxy is not an object");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (key, value) in obj.iter() {
|
|
Packit |
f0b94e |
match &**key {
|
|
Packit |
f0b94e |
"proxyType" => match value.as_string() {
|
|
Packit |
f0b94e |
Some("pac") |
|
|
Packit |
f0b94e |
Some("direct") |
|
|
Packit |
f0b94e |
Some("autodetect") |
|
|
Packit |
f0b94e |
Some("system") |
|
|
Packit |
f0b94e |
Some("manual") => {},
|
|
Packit |
f0b94e |
Some(x) => return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("Invalid proxyType value: {}", x))),
|
|
Packit |
f0b94e |
None => return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("proxyType is not a string: {}", value))),
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
"proxyAutoconfigUrl" => match value.as_string() {
|
|
Packit |
f0b94e |
Some(x) => {
|
|
Packit |
f0b94e |
Url::parse(x).or(Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("proxyAutoconfigUrl is not a valid URL: {}", x))))?;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
None => return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"proxyAutoconfigUrl is not a string"
|
|
Packit |
f0b94e |
))
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
"ftpProxy" => SpecNewSessionParameters::validate_host(value, "ftpProxy")?,
|
|
Packit |
f0b94e |
"httpProxy" => SpecNewSessionParameters::validate_host(value, "httpProxy")?,
|
|
Packit |
f0b94e |
"noProxy" => SpecNewSessionParameters::validate_no_proxy(value)?,
|
|
Packit |
f0b94e |
"sslProxy" => SpecNewSessionParameters::validate_host(value, "sslProxy")?,
|
|
Packit |
f0b94e |
"socksProxy" => SpecNewSessionParameters::validate_host(value, "socksProxy")?,
|
|
Packit |
f0b94e |
"socksVersion" => if !value.is_number() {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("socksVersion is not a number: {}", value)
|
|
Packit |
f0b94e |
))
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
x => return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("Invalid proxy configuration entry: {}", x)))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Ok(())
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
fn validate_no_proxy(value: &Json) -> WebDriverResult<()> {
|
|
Packit |
f0b94e |
match value.as_array() {
|
|
Packit |
f0b94e |
Some(hosts) => {
|
|
Packit |
f0b94e |
for host in hosts {
|
|
Packit |
f0b94e |
match host.as_string() {
|
|
Packit |
f0b94e |
Some(_) => {},
|
|
Packit |
f0b94e |
None => return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("noProxy item is not a string: {}", host)
|
|
Packit |
f0b94e |
))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
None => return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("noProxy is not an array: {}", value)
|
|
Packit |
f0b94e |
))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Ok(())
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/// Validate whether a named capability is JSON value is a string containing a host
|
|
Packit |
f0b94e |
/// and possible port
|
|
Packit |
f0b94e |
fn validate_host(value: &Json, entry: &str) -> WebDriverResult<()> {
|
|
Packit |
f0b94e |
match value.as_string() {
|
|
Packit |
f0b94e |
Some(host) => {
|
|
Packit |
f0b94e |
if host.contains("://") {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("{} must not contain a scheme: {}", entry, host)));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Temporarily add a scheme so the host can be parsed as URL
|
|
Packit |
f0b94e |
let s = String::from(format!("http://{}", host));
|
|
Packit |
f0b94e |
let url = Url::parse(s.as_str()).or(Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("{} is not a valid URL: {}", entry, host))))?;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if url.username() != "" ||
|
|
Packit |
f0b94e |
url.password() != None ||
|
|
Packit |
f0b94e |
url.path() != "/" ||
|
|
Packit |
f0b94e |
url.query() != None ||
|
|
Packit |
f0b94e |
url.fragment() != None {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("{} is not of the form host[:port]: {}", entry, host)));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
None => return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("{} is not a string: {}", entry, value)
|
|
Packit |
f0b94e |
))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Ok(())
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
fn validate_timeouts(value: &Json) -> WebDriverResult<()> {
|
|
Packit |
f0b94e |
let obj = try_opt!(
|
|
Packit |
f0b94e |
value.as_object(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"timeouts capability is not an object"
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (key, value) in obj.iter() {
|
|
Packit |
f0b94e |
match &**key {
|
|
Packit |
f0b94e |
x @ "script" | x @ "pageLoad" | x @ "implicit" => {
|
|
Packit |
f0b94e |
let timeout = try_opt!(
|
|
Packit |
f0b94e |
value.as_i64(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("{} timeouts value is not an integer: {}", x, value)
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
if timeout < 0 {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("{} timeouts value is negative: {}", x, timeout),
|
|
Packit |
f0b94e |
));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
x => {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("Invalid timeouts capability entry: {}", x),
|
|
Packit |
f0b94e |
))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Ok(())
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
fn validate_unhandled_prompt_behaviour(value: &Json) -> WebDriverResult<()> {
|
|
Packit |
f0b94e |
let behaviour = try_opt!(
|
|
Packit |
f0b94e |
value.as_string(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("unhandledPromptBehavior is not a string: {}", value)
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
match behaviour {
|
|
Packit |
f0b94e |
"dismiss" | "accept" => {}
|
|
Packit |
f0b94e |
x => {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
format!("Invalid unhandledPromptBehavior value: {}", x),
|
|
Packit |
f0b94e |
))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Ok(())
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
impl Parameters for SpecNewSessionParameters {
|
|
Packit |
f0b94e |
fn from_json(body: &Json) -> WebDriverResult<SpecNewSessionParameters> {
|
|
Packit |
f0b94e |
let data = try_opt!(
|
|
Packit |
f0b94e |
body.as_object(),
|
|
Packit |
f0b94e |
ErrorStatus::UnknownError,
|
|
Packit |
f0b94e |
format!("Malformed capabilities, message body is not an object: {}", body)
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let capabilities = try_opt!(
|
|
Packit |
f0b94e |
try_opt!(
|
|
Packit |
f0b94e |
data.get("capabilities"),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"Malformed capabilities, missing \"capabilities\" field"
|
|
Packit |
f0b94e |
).as_object(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"Malformed capabilities, \"capabilities\" field is not an object}"
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let default_always_match = Json::Object(Capabilities::new());
|
|
Packit |
f0b94e |
let always_match = try_opt!(
|
|
Packit |
f0b94e |
capabilities
|
|
Packit |
f0b94e |
.get("alwaysMatch")
|
|
Packit |
f0b94e |
.unwrap_or(&default_always_match)
|
|
Packit |
f0b94e |
.as_object(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"Malformed capabilities, alwaysMatch field is not an object"
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
let default_first_matches = Json::Array(vec![]);
|
|
Packit |
f0b94e |
let first_matches = try_opt!(
|
|
Packit |
f0b94e |
capabilities
|
|
Packit |
f0b94e |
.get("firstMatch")
|
|
Packit |
f0b94e |
.unwrap_or(&default_first_matches)
|
|
Packit |
f0b94e |
.as_array(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"Malformed capabilities, firstMatch field is not an array"
|
|
Packit |
f0b94e |
).iter()
|
|
Packit |
f0b94e |
.map(|x| {
|
|
Packit |
f0b94e |
x.as_object().map(|x| x.clone()).ok_or(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"Malformed capabilities, firstMatch entry is not an object",
|
|
Packit |
f0b94e |
))
|
|
Packit |
f0b94e |
})
|
|
Packit |
f0b94e |
.collect::<WebDriverResult<Vec<Capabilities>>>()?;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return Ok(SpecNewSessionParameters {
|
|
Packit |
f0b94e |
alwaysMatch: always_match.clone(),
|
|
Packit |
f0b94e |
firstMatch: first_matches,
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
impl ToJson for SpecNewSessionParameters {
|
|
Packit |
f0b94e |
fn to_json(&self) -> Json {
|
|
Packit |
f0b94e |
let mut body = BTreeMap::new();
|
|
Packit |
f0b94e |
let mut capabilities = BTreeMap::new();
|
|
Packit |
f0b94e |
capabilities.insert("alwaysMatch".into(), self.alwaysMatch.to_json());
|
|
Packit |
f0b94e |
capabilities.insert("firstMatch".into(), self.firstMatch.to_json());
|
|
Packit |
f0b94e |
body.insert("capabilities".into(), capabilities.to_json());
|
|
Packit |
f0b94e |
Json::Object(body)
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
impl CapabilitiesMatching for SpecNewSessionParameters {
|
|
Packit |
f0b94e |
fn match_browser<T: BrowserCapabilities>(
|
|
Packit |
f0b94e |
&self,
|
|
Packit |
f0b94e |
browser_capabilities: &mut T,
|
|
Packit |
f0b94e |
) -> WebDriverResult<Option<Capabilities>> {
|
|
Packit |
f0b94e |
let default = vec![BTreeMap::new()];
|
|
Packit |
f0b94e |
let capabilities_list = if self.firstMatch.len() > 0 {
|
|
Packit |
f0b94e |
&self.firstMatch
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
&default
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let merged_capabilities = capabilities_list
|
|
Packit |
f0b94e |
.iter()
|
|
Packit |
f0b94e |
.map(|first_match_entry| {
|
|
Packit |
f0b94e |
if first_match_entry.keys().any(|k| self.alwaysMatch.contains_key(k)) {
|
|
Packit |
f0b94e |
return Err(WebDriverError::new(
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"firstMatch key shadowed a value in alwaysMatch",
|
|
Packit |
f0b94e |
));
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
let mut merged = self.alwaysMatch.clone();
|
|
Packit |
f0b94e |
merged.append(&mut first_match_entry.clone());
|
|
Packit |
f0b94e |
Ok(merged)
|
|
Packit |
f0b94e |
})
|
|
Packit |
f0b94e |
.map(|merged| {
|
|
Packit |
f0b94e |
merged.and_then(|x| self.validate(x, browser_capabilities))
|
|
Packit |
f0b94e |
})
|
|
Packit |
f0b94e |
.collect::<WebDriverResult<Vec<Capabilities>>>()?;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let selected = merged_capabilities
|
|
Packit |
f0b94e |
.iter()
|
|
Packit |
f0b94e |
.filter_map(|merged| {
|
|
Packit |
f0b94e |
browser_capabilities.init(merged);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (key, value) in merged.iter() {
|
|
Packit |
f0b94e |
match &**key {
|
|
Packit |
f0b94e |
"browserName" => {
|
|
Packit |
f0b94e |
let browserValue = browser_capabilities
|
|
Packit |
f0b94e |
.browser_name(merged)
|
|
Packit |
f0b94e |
.ok()
|
|
Packit |
f0b94e |
.and_then(|x| x);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if value.as_string() != browserValue.as_ref().map(|x| &**x) {
|
|
Packit |
f0b94e |
return None;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
"browserVersion" => {
|
|
Packit |
f0b94e |
let browserValue = browser_capabilities
|
|
Packit |
f0b94e |
.browser_version(merged)
|
|
Packit |
f0b94e |
.ok()
|
|
Packit |
f0b94e |
.and_then(|x| x);
|
|
Packit |
f0b94e |
// We already validated this was a string
|
|
Packit |
f0b94e |
let version_cond = value.as_string().unwrap_or("");
|
|
Packit |
f0b94e |
if let Some(version) = browserValue {
|
|
Packit |
f0b94e |
if !browser_capabilities
|
|
Packit |
f0b94e |
.compare_browser_version(&*version, version_cond)
|
|
Packit |
f0b94e |
.unwrap_or(false)
|
|
Packit |
f0b94e |
{
|
|
Packit |
f0b94e |
return None;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
return None;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
"platformName" => {
|
|
Packit |
f0b94e |
let browserValue = browser_capabilities
|
|
Packit |
f0b94e |
.platform_name(merged)
|
|
Packit |
f0b94e |
.ok()
|
|
Packit |
f0b94e |
.and_then(|x| x);
|
|
Packit |
f0b94e |
if value.as_string() != browserValue.as_ref().map(|x| &**x) {
|
|
Packit |
f0b94e |
return None;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
"acceptInsecureCerts" => {
|
|
Packit |
f0b94e |
if value.as_boolean().unwrap_or(false) &&
|
|
Packit |
f0b94e |
!browser_capabilities
|
|
Packit |
f0b94e |
.accept_insecure_certs(merged)
|
|
Packit |
f0b94e |
.unwrap_or(false)
|
|
Packit |
f0b94e |
{
|
|
Packit |
f0b94e |
return None;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
"proxy" => {
|
|
Packit |
f0b94e |
let default = BTreeMap::new();
|
|
Packit |
f0b94e |
let proxy = value.as_object().unwrap_or(&default);
|
|
Packit |
f0b94e |
if !browser_capabilities
|
|
Packit |
f0b94e |
.accept_proxy(&proxy, merged)
|
|
Packit |
f0b94e |
.unwrap_or(false)
|
|
Packit |
f0b94e |
{
|
|
Packit |
f0b94e |
return None;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
name => {
|
|
Packit |
f0b94e |
if name.contains(":") {
|
|
Packit |
f0b94e |
if !browser_capabilities
|
|
Packit |
f0b94e |
.accept_custom(name, value, merged)
|
|
Packit |
f0b94e |
.unwrap_or(false)
|
|
Packit |
f0b94e |
{
|
|
Packit |
f0b94e |
return None;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
// Accept the capability
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return Some(merged);
|
|
Packit |
f0b94e |
})
|
|
Packit |
f0b94e |
.next()
|
|
Packit |
f0b94e |
.map(|x| x.clone());
|
|
Packit |
f0b94e |
Ok(selected)
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#[derive(Debug, PartialEq)]
|
|
Packit |
f0b94e |
pub struct LegacyNewSessionParameters {
|
|
Packit |
f0b94e |
pub desired: Capabilities,
|
|
Packit |
f0b94e |
pub required: Capabilities,
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
impl CapabilitiesMatching for LegacyNewSessionParameters {
|
|
Packit |
f0b94e |
fn match_browser<T: BrowserCapabilities>(
|
|
Packit |
f0b94e |
&self,
|
|
Packit |
f0b94e |
browser_capabilities: &mut T,
|
|
Packit |
f0b94e |
) -> WebDriverResult<Option<Capabilities>> {
|
|
Packit |
f0b94e |
// For now don't do anything much, just merge the
|
|
Packit |
f0b94e |
// desired and required and return the merged list.
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let mut capabilities: Capabilities = BTreeMap::new();
|
|
Packit |
f0b94e |
self.required.iter().chain(self.desired.iter()).fold(
|
|
Packit |
f0b94e |
&mut capabilities,
|
|
Packit |
f0b94e |
|caps, (key, value)| {
|
|
Packit |
f0b94e |
if !caps.contains_key(key) {
|
|
Packit |
f0b94e |
caps.insert(key.clone(), value.clone());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
caps
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
browser_capabilities.init(&capabilities);
|
|
Packit |
f0b94e |
Ok(Some(capabilities))
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
impl Parameters for LegacyNewSessionParameters {
|
|
Packit |
f0b94e |
fn from_json(body: &Json) -> WebDriverResult<LegacyNewSessionParameters> {
|
|
Packit |
f0b94e |
let data = try_opt!(
|
|
Packit |
f0b94e |
body.as_object(),
|
|
Packit |
f0b94e |
ErrorStatus::UnknownError,
|
|
Packit |
f0b94e |
format!("Malformed legacy capabilities, message body is not an object: {}", body)
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let desired = if let Some(capabilities) = data.get("desiredCapabilities") {
|
|
Packit |
f0b94e |
try_opt!(
|
|
Packit |
f0b94e |
capabilities.as_object(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"Malformed legacy capabilities, desiredCapabilities field is not an object"
|
|
Packit |
f0b94e |
).clone()
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
BTreeMap::new()
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let required = if let Some(capabilities) = data.get("requiredCapabilities") {
|
|
Packit |
f0b94e |
try_opt!(
|
|
Packit |
f0b94e |
capabilities.as_object(),
|
|
Packit |
f0b94e |
ErrorStatus::InvalidArgument,
|
|
Packit |
f0b94e |
"Malformed legacy capabilities, requiredCapabilities field is not an object"
|
|
Packit |
f0b94e |
).clone()
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
BTreeMap::new()
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Ok(LegacyNewSessionParameters { desired, required })
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
impl ToJson for LegacyNewSessionParameters {
|
|
Packit |
f0b94e |
fn to_json(&self) -> Json {
|
|
Packit |
f0b94e |
let mut data = BTreeMap::new();
|
|
Packit |
f0b94e |
data.insert("desiredCapabilities".to_owned(), self.desired.to_json());
|
|
Packit |
f0b94e |
data.insert("requiredCapabilities".to_owned(), self.required.to_json());
|
|
Packit |
f0b94e |
Json::Object(data)
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#[cfg(test)]
|
|
Packit |
f0b94e |
mod tests {
|
|
Packit |
f0b94e |
use rustc_serialize::json::Json;
|
|
Packit |
f0b94e |
use super::{SpecNewSessionParameters, WebDriverResult};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
fn validate_proxy(value: &str) -> WebDriverResult<()> {
|
|
Packit |
f0b94e |
let data = Json::from_str(value).unwrap();
|
|
Packit |
f0b94e |
SpecNewSessionParameters::validate_proxy(&data)
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#[test]
|
|
Packit |
f0b94e |
fn test_validate_proxy() {
|
|
Packit |
f0b94e |
// proxy hosts
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"127.0.0.1\"}").unwrap();
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"127.0.0.1:\"}").unwrap();
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"127.0.0.1:3128\"}").unwrap();
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"localhost\"}").unwrap();
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"localhost:3128\"}").unwrap();
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"[2001:db8::1]\"}").unwrap();
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"[2001:db8::1]:3128\"}").unwrap();
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"example.org\"}").unwrap();
|
|
Packit |
f0b94e |
validate_proxy("{\"httpProxy\": \"example.org:3128\"}").unwrap();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
assert!(validate_proxy("{\"httpProxy\": \"http://example.org\"}").is_err());
|
|
Packit |
f0b94e |
assert!(validate_proxy("{\"httpProxy\": \"example.org:-1\"}").is_err());
|
|
Packit |
f0b94e |
assert!(validate_proxy("{\"httpProxy\": \"2001:db8::1\"}").is_err());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// no proxy for manual proxy type
|
|
Packit |
f0b94e |
validate_proxy("{\"noProxy\": [\"foo\"]}").unwrap();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
assert!(validate_proxy("{\"noProxy\": \"foo\"}").is_err());
|
|
Packit |
f0b94e |
assert!(validate_proxy("{\"noProxy\": [42]}").is_err());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|