Blame testing/webdriver/src/capabilities.rs

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
}