зеркало из https://github.com/mozilla/gecko-dev.git
geckodriver: Get more things working.
Source-Repo: https://github.com/mozilla/geckodriver Source-Revision: 9246c7b56439826ce7c18766ce8edea44e255ae2 --HG-- extra : rebase_source : ceb39f8c1cc1baa5230795123a925047afa6cea1
This commit is contained in:
Родитель
2924471963
Коммит
97d7fef217
|
@ -1,7 +1,7 @@
|
||||||
|
use core::u16;
|
||||||
use std::collections::TreeMap;
|
use std::collections::TreeMap;
|
||||||
use serialize::json;
|
use serialize::json;
|
||||||
use serialize::{Encodable};
|
use serialize::json::{ToJson, Json};
|
||||||
use serialize::json::{ToJson};
|
|
||||||
use regex::Captures;
|
use regex::Captures;
|
||||||
|
|
||||||
use common::{WebDriverResult, WebDriverError, ErrorStatus};
|
use common::{WebDriverResult, WebDriverError, ErrorStatus};
|
||||||
|
@ -22,7 +22,24 @@ pub enum WebDriverCommand {
|
||||||
GetWindowHandle,
|
GetWindowHandle,
|
||||||
GetWindowHandles,
|
GetWindowHandles,
|
||||||
Close,
|
Close,
|
||||||
Timeouts(TimeoutsParameters)
|
Timeouts(TimeoutsParameters),
|
||||||
|
SetWindowSize(WindowSizeParameters),
|
||||||
|
GetWindowSize,
|
||||||
|
MaximizeWindow,
|
||||||
|
// FullscreenWindow // Not supported in marionette
|
||||||
|
SwitchToWindow(SwitchToWindowParameters),
|
||||||
|
SwitchToFrame(SwitchToFrameParameters),
|
||||||
|
SwitchToParentFrame,
|
||||||
|
IsDisplayed(WebElement),
|
||||||
|
IsSelected(WebElement),
|
||||||
|
GetElementAttribute(WebElement, String),
|
||||||
|
GetCSSValue(WebElement, String),
|
||||||
|
GetElementText(WebElement),
|
||||||
|
GetElementTagName(WebElement),
|
||||||
|
GetElementRect(WebElement),
|
||||||
|
IsEnabled(WebElement),
|
||||||
|
ExecuteScript(JavascriptCommandParameters),
|
||||||
|
ExecuteAsyncScript(JavascriptCommandParameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(PartialEq)]
|
#[deriving(PartialEq)]
|
||||||
|
@ -41,22 +58,22 @@ impl WebDriverMessage {
|
||||||
|
|
||||||
pub fn from_http(match_type: MatchType, params: &Captures, body: &str) -> WebDriverResult<WebDriverMessage> {
|
pub fn from_http(match_type: MatchType, params: &Captures, body: &str) -> WebDriverResult<WebDriverMessage> {
|
||||||
let session_id = WebDriverMessage::get_session_id(params);
|
let session_id = WebDriverMessage::get_session_id(params);
|
||||||
let body_data = match json::from_str(body) {
|
let body_data = if body != "" {
|
||||||
Ok(x) => x,
|
debug!("Got request body {}", body);
|
||||||
Err(_) => return Err(WebDriverError::new(ErrorStatus::UnknownError,
|
match json::from_str(body) {
|
||||||
"Failed to decode request body"))
|
Ok(x) => x,
|
||||||
|
Err(_) => return Err(WebDriverError::new(ErrorStatus::UnknownError,
|
||||||
|
format!("Failed to decode request body as json: {}", body).as_slice()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
json::Null
|
||||||
};
|
};
|
||||||
let command = match match_type {
|
let command = match match_type {
|
||||||
MatchType::NewSession => WebDriverCommand::NewSession,
|
MatchType::NewSession => WebDriverCommand::NewSession,
|
||||||
MatchType::DeleteSession => WebDriverCommand::DeleteSession,
|
MatchType::DeleteSession => WebDriverCommand::DeleteSession,
|
||||||
MatchType::Get => {
|
MatchType::Get => {
|
||||||
match GetParameters::from_json(&body_data) {
|
let parameters: GetParameters = try!(Parameters::from_json(&body_data));
|
||||||
Ok(parameters) => {
|
WebDriverCommand::Get(parameters)
|
||||||
WebDriverCommand::Get(parameters)
|
|
||||||
},
|
|
||||||
Err(_) => return Err(WebDriverError::new(ErrorStatus::UnknownError,
|
|
||||||
"Failed to decode request body"))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
MatchType::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
|
MatchType::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
|
||||||
MatchType::GoBack => WebDriverCommand::GoBack,
|
MatchType::GoBack => WebDriverCommand::GoBack,
|
||||||
|
@ -67,52 +84,201 @@ impl WebDriverMessage {
|
||||||
MatchType::GetWindowHandles => WebDriverCommand::GetWindowHandles,
|
MatchType::GetWindowHandles => WebDriverCommand::GetWindowHandles,
|
||||||
MatchType::Close => WebDriverCommand::Close,
|
MatchType::Close => WebDriverCommand::Close,
|
||||||
MatchType::Timeouts => {
|
MatchType::Timeouts => {
|
||||||
let parameters_result = TimeoutsParameters::from_json(&body_data);
|
let parameters: TimeoutsParameters = try!(Parameters::from_json(&body_data));
|
||||||
match parameters_result {
|
WebDriverCommand::Timeouts(parameters)
|
||||||
Ok(parameters) => WebDriverCommand::Timeouts(parameters),
|
},
|
||||||
Err(_) => return Err(WebDriverError::new(ErrorStatus::UnknownError,
|
MatchType::SetWindowSize => {
|
||||||
"Failed to decode request body"))
|
let parameters: WindowSizeParameters = try!(Parameters::from_json(&body_data));
|
||||||
}
|
WebDriverCommand::SetWindowSize(parameters)
|
||||||
|
},
|
||||||
|
MatchType::GetWindowSize => WebDriverCommand::GetWindowSize,
|
||||||
|
MatchType::MaximizeWindow => WebDriverCommand::MaximizeWindow,
|
||||||
|
MatchType::SwitchToWindow => {
|
||||||
|
let parameters: SwitchToWindowParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::SwitchToWindow(parameters)
|
||||||
|
}
|
||||||
|
MatchType::SwitchToFrame => {
|
||||||
|
let parameters: SwitchToFrameParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::SwitchToFrame(parameters)
|
||||||
|
},
|
||||||
|
MatchType::SwitchToParentFrame => WebDriverCommand::SwitchToParentFrame,
|
||||||
|
MatchType::IsDisplayed => {
|
||||||
|
let element = WebElement::new(params.name("elementId").to_string());
|
||||||
|
WebDriverCommand::IsDisplayed(element)
|
||||||
|
},
|
||||||
|
MatchType::IsSelected => {
|
||||||
|
let element = WebElement::new(params.name("elementId").to_string());
|
||||||
|
WebDriverCommand::IsSelected(element)
|
||||||
|
},
|
||||||
|
MatchType::GetElementAttribute => {
|
||||||
|
let element = WebElement::new(params.name("elementId").to_string());
|
||||||
|
let attr = params.name("name").to_string();
|
||||||
|
WebDriverCommand::GetElementAttribute(element, attr)
|
||||||
|
},
|
||||||
|
MatchType::GetCSSValue => {
|
||||||
|
let element = WebElement::new(params.name("elementId").to_string());
|
||||||
|
let property = params.name("propertyName").to_string();
|
||||||
|
WebDriverCommand::GetCSSValue(element, property)
|
||||||
|
},
|
||||||
|
MatchType::GetElementText => {
|
||||||
|
let element = WebElement::new(params.name("elementId").to_string());
|
||||||
|
WebDriverCommand::GetElementText(element)
|
||||||
|
},
|
||||||
|
MatchType::GetElementTagName => {
|
||||||
|
let element = WebElement::new(params.name("elementId").to_string());
|
||||||
|
WebDriverCommand::GetElementTagName(element)
|
||||||
|
},
|
||||||
|
MatchType::GetElementRect => {
|
||||||
|
let element = WebElement::new(params.name("elementId").to_string());
|
||||||
|
WebDriverCommand::GetElementText(element)
|
||||||
|
},
|
||||||
|
MatchType::IsEnabled => {
|
||||||
|
let element = WebElement::new(params.name("elementId").to_string());
|
||||||
|
WebDriverCommand::IsEnabled(element)
|
||||||
|
},
|
||||||
|
MatchType::ExecuteScript => {
|
||||||
|
let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::ExecuteScript(parameters)
|
||||||
|
}
|
||||||
|
MatchType::ExecuteAsyncScript => {
|
||||||
|
let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
|
||||||
|
WebDriverCommand::ExecuteAsyncScript(parameters)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(WebDriverMessage::new(session_id, command))
|
Ok(WebDriverMessage::new(session_id, command))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_session_id(params: &Captures) -> Option<String> {
|
fn get_session_id(params: &Captures) -> Option<String> {
|
||||||
let session_id_str = params.name("sessionId");
|
match params.name("sessionId") {
|
||||||
if session_id_str == "" {
|
"" => None,
|
||||||
None
|
x => Some(x.to_string())
|
||||||
} else {
|
|
||||||
Some(session_id_str.to_string())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToJson for WebDriverMessage {
|
impl ToJson for WebDriverMessage {
|
||||||
fn to_json(&self) -> json::Json {
|
fn to_json(&self) -> json::Json {
|
||||||
match self.command {
|
let mut data = TreeMap::new();
|
||||||
WebDriverCommand::Get(ref x) => {
|
let parameters = match self.command {
|
||||||
x.to_json()
|
WebDriverCommand::GetMarionetteId | WebDriverCommand::NewSession |
|
||||||
|
WebDriverCommand::DeleteSession | WebDriverCommand::GetCurrentUrl |
|
||||||
|
WebDriverCommand::GoBack | WebDriverCommand::GoForward | WebDriverCommand::Refresh |
|
||||||
|
WebDriverCommand::GetTitle | WebDriverCommand::GetWindowHandle |
|
||||||
|
WebDriverCommand::GetWindowHandles | WebDriverCommand::Close |
|
||||||
|
WebDriverCommand::GetWindowSize | WebDriverCommand::MaximizeWindow |
|
||||||
|
WebDriverCommand::SwitchToParentFrame | WebDriverCommand::IsDisplayed(_) |
|
||||||
|
WebDriverCommand::IsSelected(_) | WebDriverCommand::GetElementAttribute(_, _) |
|
||||||
|
WebDriverCommand::GetCSSValue(_, _) | WebDriverCommand::GetElementText(_) |
|
||||||
|
WebDriverCommand::GetElementTagName(_) | WebDriverCommand::GetElementRect(_) |
|
||||||
|
WebDriverCommand::IsEnabled(_) => {
|
||||||
|
None
|
||||||
},
|
},
|
||||||
WebDriverCommand::Timeouts(ref x) => {
|
WebDriverCommand::Get(ref x) => Some(x.to_json()),
|
||||||
x.to_json()
|
WebDriverCommand::Timeouts(ref x) => Some(x.to_json()),
|
||||||
}
|
WebDriverCommand::SetWindowSize(ref x) => Some(x.to_json()),
|
||||||
_ => {
|
WebDriverCommand::SwitchToWindow(ref x) => Some(x.to_json()),
|
||||||
json::Object(TreeMap::new())
|
WebDriverCommand::SwitchToFrame(ref x) => Some(x.to_json()),
|
||||||
|
WebDriverCommand::ExecuteScript(ref x) |
|
||||||
|
WebDriverCommand::ExecuteAsyncScript(ref x) => Some(x.to_json())
|
||||||
|
};
|
||||||
|
if parameters.is_some() {
|
||||||
|
data.insert("parameters".to_string(), parameters.unwrap());
|
||||||
|
}
|
||||||
|
json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
struct WebElement {
|
||||||
|
id: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebElement {
|
||||||
|
fn new(id: String) -> WebElement {
|
||||||
|
WebElement {
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for WebElement {
|
||||||
|
fn to_json(&self) -> json::Json {
|
||||||
|
let mut data = TreeMap::new();
|
||||||
|
data.insert("element-6066-11e4-a52e-4f735466cecf".to_string(), self.id.to_json());
|
||||||
|
json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
enum FrameId {
|
||||||
|
Short(u16),
|
||||||
|
Element(WebElement),
|
||||||
|
Null
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for FrameId {
|
||||||
|
fn to_json(&self) -> json::Json {
|
||||||
|
match *self {
|
||||||
|
FrameId::Short(x) => {
|
||||||
|
json::Json::U64(x as u64)
|
||||||
|
},
|
||||||
|
FrameId::Element(ref x) => {
|
||||||
|
json::Json::String(x.id.clone())
|
||||||
|
},
|
||||||
|
FrameId::Null => {
|
||||||
|
json::Json::Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq, Clone)]
|
||||||
|
enum Nullable<T: ToJson> {
|
||||||
|
Value(T),
|
||||||
|
Null
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToJson> Nullable<T> {
|
||||||
|
//This is not very pretty
|
||||||
|
fn from_json<F: FnOnce(&json::Json) -> WebDriverResult<T>>(value: &json::Json, f: F) -> WebDriverResult<Nullable<T>> {
|
||||||
|
if value.is_null() {
|
||||||
|
Ok(Nullable::Null)
|
||||||
|
} else {
|
||||||
|
Ok(Nullable::Value(try!(f(value))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:ToJson> ToJson for Nullable<T> {
|
||||||
|
fn to_json(&self) -> json::Json {
|
||||||
|
match *self {
|
||||||
|
Nullable::Value(ref x) => x.to_json(),
|
||||||
|
Nullable::Null => json::Json::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Parameters {
|
||||||
|
fn from_json(body: &json::Json) -> WebDriverResult<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
#[deriving(PartialEq)]
|
#[deriving(PartialEq)]
|
||||||
struct GetParameters {
|
struct GetParameters {
|
||||||
url: String
|
url: String
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetParameters {
|
impl Parameters for GetParameters {
|
||||||
pub fn from_json(body: &json::Json) -> Result<GetParameters, String> {
|
fn from_json(body: &json::Json) -> WebDriverResult<GetParameters> {
|
||||||
|
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
|
||||||
|
"Message body was not an object");
|
||||||
|
let url = try_opt!(
|
||||||
|
try_opt!(data.get("url"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'url' parameter").as_string(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'url' not a string");
|
||||||
return Ok(GetParameters {
|
return Ok(GetParameters {
|
||||||
url: body.find("url").unwrap().as_string().unwrap().to_string()
|
url: url.to_string()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,14 +294,29 @@ impl ToJson for GetParameters {
|
||||||
#[deriving(PartialEq)]
|
#[deriving(PartialEq)]
|
||||||
struct TimeoutsParameters {
|
struct TimeoutsParameters {
|
||||||
type_: String,
|
type_: String,
|
||||||
ms: u32
|
ms: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeoutsParameters {
|
impl Parameters for TimeoutsParameters {
|
||||||
pub fn from_json(body: &json::Json) -> Result<TimeoutsParameters, String> {
|
fn from_json(body: &json::Json) -> WebDriverResult<TimeoutsParameters> {
|
||||||
|
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
|
||||||
|
"Message body was not an object");
|
||||||
|
let type_ = try_opt!(
|
||||||
|
try_opt!(data.get("type"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'type' parameter").as_string(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'type' not a string");
|
||||||
|
|
||||||
|
let ms = try_opt!(
|
||||||
|
try_opt!(data.get("ms"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'ms' parameter").as_u64(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'ms' not an integer");
|
||||||
return Ok(TimeoutsParameters {
|
return Ok(TimeoutsParameters {
|
||||||
type_: body.find("type").unwrap().as_string().unwrap().to_string(),
|
type_: type_.to_string(),
|
||||||
ms: body.find("ms").unwrap().as_i64().unwrap() as u32
|
ms: ms
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,3 +329,164 @@ impl ToJson for TimeoutsParameters {
|
||||||
json::Object(data)
|
json::Object(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
struct WindowSizeParameters {
|
||||||
|
width: u64,
|
||||||
|
height: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameters for WindowSizeParameters {
|
||||||
|
fn from_json(body: &json::Json) -> WebDriverResult<WindowSizeParameters> {
|
||||||
|
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
|
||||||
|
"Message body was not an object");
|
||||||
|
let height = try_opt!(
|
||||||
|
try_opt!(data.get("height"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'height' parameter").as_u64(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'height' is not a positive integer");
|
||||||
|
let width = try_opt!(
|
||||||
|
try_opt!(data.get("width"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing width parameter").as_u64(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'width' is not a positive integer");
|
||||||
|
return Ok(WindowSizeParameters {
|
||||||
|
height: height,
|
||||||
|
width: width
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for WindowSizeParameters {
|
||||||
|
fn to_json(&self) -> json::Json {
|
||||||
|
let mut data = TreeMap::new();
|
||||||
|
data.insert("width".to_string(), self.width.to_json());
|
||||||
|
data.insert("height".to_string(), self.height.to_json());
|
||||||
|
json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
struct SwitchToWindowParameters {
|
||||||
|
handle: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameters for SwitchToWindowParameters {
|
||||||
|
fn from_json(body: &json::Json) -> WebDriverResult<SwitchToWindowParameters> {
|
||||||
|
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
|
||||||
|
"Message body was not an object");
|
||||||
|
let handle = try_opt!(
|
||||||
|
try_opt!(data.get("handle"),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"Missing 'handle' parameter").as_string(),
|
||||||
|
ErrorStatus::InvalidArgument,
|
||||||
|
"'handle' not a string");
|
||||||
|
return Ok(SwitchToWindowParameters {
|
||||||
|
handle: handle.to_string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for SwitchToWindowParameters {
|
||||||
|
fn to_json(&self) -> json::Json {
|
||||||
|
let mut data = TreeMap::new();
|
||||||
|
data.insert("handle".to_string(), self.handle.to_json());
|
||||||
|
json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
struct SwitchToFrameParameters {
|
||||||
|
id: FrameId
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameters for SwitchToFrameParameters {
|
||||||
|
fn from_json(body: &json::Json) -> WebDriverResult<SwitchToFrameParameters> {
|
||||||
|
let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
|
||||||
|
"Message body was not an object");
|
||||||
|
let id_json = try_opt!(data.get("id"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Missing 'id' parameter");
|
||||||
|
let id = if id_json.is_u64() {
|
||||||
|
let value = id_json.as_u64().unwrap();
|
||||||
|
if value <= u16::MAX as u64 {
|
||||||
|
FrameId::Short(value as u16)
|
||||||
|
} else {
|
||||||
|
return Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
|
||||||
|
"frame id out of range"))
|
||||||
|
}
|
||||||
|
} else if id_json.is_null() {
|
||||||
|
FrameId::Null
|
||||||
|
} else if id_json.is_string() {
|
||||||
|
let value = id_json.as_string().unwrap();
|
||||||
|
FrameId::Element(WebElement::new(value.to_string()))
|
||||||
|
} else {
|
||||||
|
return Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
|
||||||
|
"frame id has unexpected type"))
|
||||||
|
};
|
||||||
|
Ok(SwitchToFrameParameters {
|
||||||
|
id: id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for SwitchToFrameParameters {
|
||||||
|
fn to_json(&self) -> json::Json {
|
||||||
|
let mut data = TreeMap::new();
|
||||||
|
data.insert("id".to_string(), self.id.to_json());
|
||||||
|
json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
struct JavascriptCommandParameters {
|
||||||
|
script: String,
|
||||||
|
args: Nullable<Vec<json::Json>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameters for JavascriptCommandParameters {
|
||||||
|
fn from_json(body: &json::Json) -> WebDriverResult<JavascriptCommandParameters> {
|
||||||
|
let data = try_opt!(body.as_object(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Message body was not an object");
|
||||||
|
|
||||||
|
let args_json = try_opt!(data.get("args"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Missing args parameter");
|
||||||
|
|
||||||
|
let args = try!(Nullable::from_json(
|
||||||
|
args_json,
|
||||||
|
|x| {
|
||||||
|
Ok((try_opt!(x.as_array(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to convert args to Array")).clone())
|
||||||
|
}));
|
||||||
|
|
||||||
|
//TODO: Look for WebElements in args?
|
||||||
|
let script = try_opt!(
|
||||||
|
try_opt!(data.get("script"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Missing script parameter").as_string(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to convert script to String");
|
||||||
|
Ok(JavascriptCommandParameters {
|
||||||
|
script: script.to_string(),
|
||||||
|
args: args.clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for JavascriptCommandParameters {
|
||||||
|
fn to_json(&self) -> json::Json {
|
||||||
|
let mut data = TreeMap::new();
|
||||||
|
//TODO: Wrap script so that it becomes marionette-compatible
|
||||||
|
data.insert("script".to_string(), self.script.to_json());
|
||||||
|
data.insert("args".to_string(), self.args.to_json());
|
||||||
|
data.insert("newSandbox".to_string(), false.to_json());
|
||||||
|
data.insert("specialPowers".to_string(), false.to_json());
|
||||||
|
data.insert("scriptTimeout".to_string(), json::Json::Null);
|
||||||
|
json::Object(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::collections::TreeMap;
|
|
||||||
use serialize::json;
|
use serialize::json;
|
||||||
use serialize::json::ToJson;
|
use serialize::json::{ToJson, ParserError};
|
||||||
|
use std::collections::TreeMap;
|
||||||
|
use std::error::{Error, FromError};
|
||||||
|
|
||||||
#[deriving(PartialEq)]
|
#[deriving(PartialEq, Show)]
|
||||||
pub enum ErrorStatus {
|
pub enum ErrorStatus {
|
||||||
ElementNotSelectable,
|
ElementNotSelectable,
|
||||||
ElementNotVisible,
|
ElementNotVisible,
|
||||||
|
@ -13,7 +14,7 @@ pub enum ErrorStatus {
|
||||||
InvalidSelector,
|
InvalidSelector,
|
||||||
InvalidSessionId,
|
InvalidSessionId,
|
||||||
JavascriptError,
|
JavascriptError,
|
||||||
MoveTagetOutOfBounds,
|
MoveTargetOutOfBounds,
|
||||||
NoSuchAlert,
|
NoSuchAlert,
|
||||||
NoSuchElement,
|
NoSuchElement,
|
||||||
NoSuchFrame,
|
NoSuchFrame,
|
||||||
|
@ -32,6 +33,7 @@ pub enum ErrorStatus {
|
||||||
|
|
||||||
pub type WebDriverResult<T> = Result<T, WebDriverError>;
|
pub type WebDriverResult<T> = Result<T, WebDriverError>;
|
||||||
|
|
||||||
|
#[deriving(Show)]
|
||||||
pub struct WebDriverError {
|
pub struct WebDriverError {
|
||||||
pub status: ErrorStatus,
|
pub status: ErrorStatus,
|
||||||
pub message: String
|
pub message: String
|
||||||
|
@ -45,7 +47,7 @@ impl WebDriverError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status_code(&self) -> String {
|
pub fn status_code(&self) -> &str {
|
||||||
match self.status {
|
match self.status {
|
||||||
ErrorStatus::ElementNotSelectable => "element not selectable",
|
ErrorStatus::ElementNotSelectable => "element not selectable",
|
||||||
ErrorStatus::ElementNotVisible => "element not visible",
|
ErrorStatus::ElementNotVisible => "element not visible",
|
||||||
|
@ -56,7 +58,7 @@ impl WebDriverError {
|
||||||
ErrorStatus::InvalidSelector => "invalid selector",
|
ErrorStatus::InvalidSelector => "invalid selector",
|
||||||
ErrorStatus::InvalidSessionId => "invalid session id",
|
ErrorStatus::InvalidSessionId => "invalid session id",
|
||||||
ErrorStatus::JavascriptError => "javascript error",
|
ErrorStatus::JavascriptError => "javascript error",
|
||||||
ErrorStatus::MoveTagetOutOfBounds => "move target out of bounds",
|
ErrorStatus::MoveTargetOutOfBounds => "move target out of bounds",
|
||||||
ErrorStatus::NoSuchAlert => "no such alert",
|
ErrorStatus::NoSuchAlert => "no such alert",
|
||||||
ErrorStatus::NoSuchElement => "no such element",
|
ErrorStatus::NoSuchElement => "no such element",
|
||||||
ErrorStatus::NoSuchFrame => "no such frame",
|
ErrorStatus::NoSuchFrame => "no such frame",
|
||||||
|
@ -71,7 +73,7 @@ impl WebDriverError {
|
||||||
ErrorStatus::UnknownPath => "unknown command",
|
ErrorStatus::UnknownPath => "unknown command",
|
||||||
ErrorStatus::UnknownMethod => "unknown command",
|
ErrorStatus::UnknownMethod => "unknown command",
|
||||||
ErrorStatus::UnsupportedOperation => "unsupported operation",
|
ErrorStatus::UnsupportedOperation => "unsupported operation",
|
||||||
}.to_string()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn http_status(&self) -> int {
|
pub fn http_status(&self) -> int {
|
||||||
|
@ -81,6 +83,10 @@ impl WebDriverError {
|
||||||
_ => 500
|
_ => 500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_json_string(&self) -> String {
|
||||||
|
self.to_json().to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToJson for WebDriverError {
|
impl ToJson for WebDriverError {
|
||||||
|
@ -91,3 +97,24 @@ impl ToJson for WebDriverError {
|
||||||
json::Object(data)
|
json::Object(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Error for WebDriverError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
self.status_code()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detail(&self) -> Option<String> {
|
||||||
|
Some(self.message.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromError<ParserError> for WebDriverError {
|
||||||
|
fn from_error(err: ParserError) -> WebDriverError {
|
||||||
|
let msg = format!("{}", err);
|
||||||
|
WebDriverError::new(ErrorStatus::UnknownError, msg.as_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use std::io::net::ip::IpAddr;
|
use std::io::net::ip::IpAddr;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::collections::HashMap;
|
|
||||||
use serialize::json::ToJson;
|
|
||||||
|
|
||||||
use hyper::header::common::ContentLength;
|
use hyper::header::common::ContentLength;
|
||||||
use hyper::method::Post;
|
use hyper::method::Post;
|
||||||
|
@ -9,24 +7,24 @@ use hyper::server::{Server, Handler, Request, Response};
|
||||||
use hyper::uri::AbsolutePath;
|
use hyper::uri::AbsolutePath;
|
||||||
|
|
||||||
use response::WebDriverResponse;
|
use response::WebDriverResponse;
|
||||||
use messagebuilder::{get_builder};
|
use messagebuilder::{get_builder, MessageBuilder};
|
||||||
use marionette::MarionetteConnection;
|
use marionette::MarionetteConnection;
|
||||||
use command::WebDriverMessage;
|
use command::WebDriverMessage;
|
||||||
use common::WebDriverResult;
|
use common::WebDriverResult;
|
||||||
|
|
||||||
enum DispatchMessage {
|
enum DispatchMessage {
|
||||||
HandleWebDriver(WebDriverMessage, Sender<Option<WebDriverResult<WebDriverResponse>>>),
|
HandleWebDriver(WebDriverMessage, Sender<WebDriverResult<Option<WebDriverResponse>>>),
|
||||||
Quit
|
Quit
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Dispatcher {
|
struct Dispatcher {
|
||||||
connections: HashMap<String, MarionetteConnection>
|
connection: Option<MarionetteConnection>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dispatcher {
|
impl Dispatcher {
|
||||||
fn new() -> Dispatcher {
|
fn new() -> Dispatcher {
|
||||||
Dispatcher {
|
Dispatcher {
|
||||||
connections: HashMap::new()
|
connection: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,25 +32,53 @@ impl Dispatcher {
|
||||||
loop {
|
loop {
|
||||||
match msg_chan.recv() {
|
match msg_chan.recv() {
|
||||||
DispatchMessage::HandleWebDriver(msg, resp_chan) => {
|
DispatchMessage::HandleWebDriver(msg, resp_chan) => {
|
||||||
let opt_session_id = msg.session_id.clone();
|
match msg.session_id {
|
||||||
if opt_session_id.is_some() {
|
Some(ref x) => {
|
||||||
let session_id = opt_session_id.unwrap();
|
match self.connection {
|
||||||
let mut connection = match self.connections.get_mut(&session_id) {
|
Some(ref conn) => {
|
||||||
Some(x) => x,
|
if conn.session.session_id != *x {
|
||||||
None => break
|
error!("Got unexpected session id {} expected {}",
|
||||||
};
|
x, conn.session.session_id);
|
||||||
let resp = connection.send_message(&msg);
|
continue
|
||||||
resp_chan.send(resp);
|
}
|
||||||
return;
|
},
|
||||||
|
None => {
|
||||||
|
match self.create_connection(Some(x.clone())) {
|
||||||
|
Err(msg) => {
|
||||||
|
error!("{}", msg);
|
||||||
|
continue
|
||||||
|
},
|
||||||
|
Ok(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
if self.connection.is_some() {
|
||||||
|
error!("Missing session id for established connection");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match self.create_connection(None) {
|
||||||
|
Err(msg) => {
|
||||||
|
error!("{}", msg);
|
||||||
|
continue
|
||||||
|
},
|
||||||
|
Ok(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let resp = {
|
||||||
|
let mut connection = self.connection.as_mut().unwrap();
|
||||||
|
connection.send_message(&msg)
|
||||||
|
};
|
||||||
|
debug!("{}", resp);
|
||||||
|
match resp {
|
||||||
|
Ok(Some(WebDriverResponse::DeleteSession)) => {
|
||||||
|
debug!("Deleting session");
|
||||||
|
self.connection = None;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
let mut connection = MarionetteConnection::new();
|
|
||||||
if connection.connect().is_err() {
|
|
||||||
error!("Failed to start marionette connection");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let resp = connection.send_message(&msg);
|
|
||||||
self.connections.insert(connection.session.session_id.clone(),
|
|
||||||
connection);
|
|
||||||
resp_chan.send(resp);
|
resp_chan.send(resp);
|
||||||
},
|
},
|
||||||
DispatchMessage::Quit => {
|
DispatchMessage::Quit => {
|
||||||
|
@ -61,25 +87,33 @@ impl Dispatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_connection(&mut self, session_id: Option<String>) -> Result<(), String> {
|
||||||
|
let mut connection = MarionetteConnection::new(session_id);
|
||||||
|
if connection.connect().is_err() {
|
||||||
|
return Err("Failed to start marionette connection".to_string());
|
||||||
|
}
|
||||||
|
self.connection = Some(connection);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MarionetteHandler {
|
struct MarionetteHandler {
|
||||||
chan: Mutex<Sender<DispatchMessage>>
|
chan: Mutex<Sender<DispatchMessage>>,
|
||||||
|
builder: Mutex<MessageBuilder>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MarionetteHandler {
|
impl MarionetteHandler {
|
||||||
fn new(chan: Sender<DispatchMessage>) -> MarionetteHandler {
|
fn new(builder: MessageBuilder, chan: Sender<DispatchMessage>) -> MarionetteHandler {
|
||||||
MarionetteHandler {
|
MarionetteHandler {
|
||||||
chan: Mutex::new(chan)
|
chan: Mutex::new(chan),
|
||||||
|
builder: Mutex::new(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler for MarionetteHandler {
|
impl Handler for MarionetteHandler {
|
||||||
fn handle(&self, req: Request, res: Response) {
|
fn handle(&self, req: Request, res: Response) {
|
||||||
let builder = get_builder();
|
|
||||||
println!("{}", req.uri);;
|
|
||||||
|
|
||||||
let mut req = req;
|
let mut req = req;
|
||||||
let mut res = res;
|
let mut res = res;
|
||||||
|
|
||||||
|
@ -87,10 +121,16 @@ impl Handler for MarionetteHandler {
|
||||||
Post => req.read_to_string().unwrap(),
|
Post => req.read_to_string().unwrap(),
|
||||||
_ => "".to_string()
|
_ => "".to_string()
|
||||||
};
|
};
|
||||||
println!("Got request {} {}", req.method, req.uri);
|
debug!("Got request {} {}", req.method, req.uri);
|
||||||
match req.uri {
|
match req.uri {
|
||||||
AbsolutePath(path) => {
|
AbsolutePath(path) => {
|
||||||
let (status, resp_data) = match builder.from_http(req.method, path[], body[]) {
|
let msg_result = {
|
||||||
|
// The fact that this locks for basically the whole request doesn't
|
||||||
|
// matter as long as we are only handling one request at a time.
|
||||||
|
let builder = self.builder.lock();
|
||||||
|
builder.from_http(req.method, path[], body[])
|
||||||
|
};
|
||||||
|
let (status, resp_body) = match msg_result {
|
||||||
Ok(message) => {
|
Ok(message) => {
|
||||||
let (send_res, recv_res) = channel();
|
let (send_res, recv_res) = channel();
|
||||||
{
|
{
|
||||||
|
@ -98,30 +138,32 @@ impl Handler for MarionetteHandler {
|
||||||
c.send(DispatchMessage::HandleWebDriver(message, send_res));
|
c.send(DispatchMessage::HandleWebDriver(message, send_res));
|
||||||
}
|
}
|
||||||
match recv_res.recv() {
|
match recv_res.recv() {
|
||||||
Some(x) => {
|
Ok(None) => return,
|
||||||
match x {
|
Ok(Some(response)) => {
|
||||||
Ok(response) => {
|
(200, response.to_json_string())
|
||||||
(200, response.to_json())
|
|
||||||
}
|
|
||||||
Err(err) => (err.http_status(), err.to_json())
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None => return
|
Err(err) => (err.http_status(), err.to_json_string()),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
(err.http_status(), err.to_json())
|
(err.http_status(), err.to_json_string())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let body = format!("{}\n", resp_data.to_string());
|
if status != 200 {
|
||||||
|
error!("Returning status code {}", status);
|
||||||
|
error!("Returning body {}", resp_body);
|
||||||
|
} else {
|
||||||
|
debug!("Returning status code {}", status);
|
||||||
|
debug!("Returning body {}", resp_body);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
let status_code = res.status_mut();
|
let status_code = res.status_mut();
|
||||||
*status_code = FromPrimitive::from_int(status).unwrap();
|
*status_code = FromPrimitive::from_int(status).unwrap();
|
||||||
}
|
}
|
||||||
res.headers_mut().set(ContentLength(body.len()));
|
res.headers_mut().set(ContentLength(resp_body.len()));
|
||||||
let mut stream = res.start();
|
let mut stream = res.start();
|
||||||
stream.write_str(body.as_slice());
|
stream.write_str(resp_body.as_slice()).unwrap();
|
||||||
stream.unwrap().end();
|
stream.unwrap().end().unwrap();
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -137,6 +179,7 @@ pub fn start(ip_address: IpAddr, port: u16) {
|
||||||
spawn(proc() {
|
spawn(proc() {
|
||||||
dispatcher.run(msg_recv);
|
dispatcher.run(msg_recv);
|
||||||
});
|
});
|
||||||
let handler = MarionetteHandler::new(msg_send.clone());
|
let builder = get_builder();
|
||||||
|
let handler = MarionetteHandler::new(builder, msg_send.clone());
|
||||||
server.listen(handler).unwrap();
|
server.listen(handler).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#![feature(slicing_syntax)]
|
#![feature(slicing_syntax)]
|
||||||
#![feature(phase)]
|
#![feature(phase)]
|
||||||
|
#![feature(macro_rules)]
|
||||||
|
#![feature(unboxed_closures)]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
#[phase(plugin, link)] extern crate log;
|
#[phase(plugin, link)] extern crate log;
|
||||||
|
@ -13,6 +16,15 @@ use std::io::net::ip::SocketAddr;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::os;
|
use std::os;
|
||||||
|
|
||||||
|
macro_rules! try_opt {
|
||||||
|
($expr:expr, $err_type:expr, $err_msg:expr) => ({
|
||||||
|
match $expr {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return Err(WebDriverError::new($err_type, $err_msg))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
mod common;
|
mod common;
|
||||||
mod httpserver;
|
mod httpserver;
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
use serialize::json::ToJson;
|
use serialize::json::{Json, ToJson};
|
||||||
use serialize::json;
|
use serialize::json;
|
||||||
use std::io::{IoResult, TcpStream, IoError};
|
|
||||||
use std::collections::TreeMap;
|
use std::collections::TreeMap;
|
||||||
|
use std::io::{IoResult, TcpStream, IoError};
|
||||||
|
|
||||||
use command::{WebDriverMessage, WebDriverCommand};
|
use command::{WebDriverMessage};
|
||||||
use command::WebDriverCommand::{GetMarionetteId, NewSession, DeleteSession, Get, GetCurrentUrl,
|
use command::WebDriverCommand::{GetMarionetteId, NewSession, DeleteSession, Get, GetCurrentUrl,
|
||||||
GoBack, GoForward, Refresh, GetTitle, GetWindowHandle,
|
GoBack, GoForward, Refresh, GetTitle, GetWindowHandle,
|
||||||
GetWindowHandles, Close, Timeouts};
|
GetWindowHandles, Close, Timeouts, SetWindowSize,
|
||||||
use response::WebDriverResponse;
|
GetWindowSize, MaximizeWindow, SwitchToWindow, SwitchToFrame,
|
||||||
|
SwitchToParentFrame, IsDisplayed, IsSelected,
|
||||||
|
GetElementAttribute, GetCSSValue, GetElementText,
|
||||||
|
GetElementTagName, GetElementRect, IsEnabled, ExecuteScript,
|
||||||
|
ExecuteAsyncScript};
|
||||||
|
use response::{WebDriverResponse, NewSessionResponse, ValueResponse, WindowSizeResponse, ElementRectResponse};
|
||||||
use common::{WebDriverResult, WebDriverError, ErrorStatus};
|
use common::{WebDriverResult, WebDriverError, ErrorStatus};
|
||||||
|
|
||||||
pub struct MarionetteSession {
|
pub struct MarionetteSession {
|
||||||
|
@ -15,10 +20,17 @@ pub struct MarionetteSession {
|
||||||
pub to: String
|
pub to: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn object_from_json(data: &str) -> WebDriverResult<TreeMap<String, json::Json>> {
|
||||||
|
Ok(try_opt!(try!(json::from_str(data)).as_object(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Expected a json object").clone())
|
||||||
|
}
|
||||||
|
|
||||||
impl MarionetteSession {
|
impl MarionetteSession {
|
||||||
pub fn new() -> MarionetteSession {
|
pub fn new(session_id: Option<String>) -> MarionetteSession {
|
||||||
|
let initital_id = session_id.unwrap_or("".to_string());
|
||||||
MarionetteSession {
|
MarionetteSession {
|
||||||
session_id: "".to_string(),
|
session_id: initital_id,
|
||||||
to: String::from_str("root")
|
to: String::from_str("root")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,21 +38,24 @@ impl MarionetteSession {
|
||||||
pub fn update(&mut self, msg: &WebDriverMessage, resp: &TreeMap<String, json::Json>) -> WebDriverResult<()> {
|
pub fn update(&mut self, msg: &WebDriverMessage, resp: &TreeMap<String, json::Json>) -> WebDriverResult<()> {
|
||||||
match msg.command {
|
match msg.command {
|
||||||
GetMarionetteId => {
|
GetMarionetteId => {
|
||||||
let to = match resp.get(&"to".to_string()) {
|
let to = try_opt!(
|
||||||
Some(x) => x,
|
try_opt!(resp.get("to"),
|
||||||
None => return Err(WebDriverError::new(ErrorStatus::UnknownError,
|
ErrorStatus::UnknownError,
|
||||||
"Unable to get to value"))
|
"Unable to get to value").as_string(),
|
||||||
};
|
ErrorStatus::UnknownError,
|
||||||
self.to = to.to_string().clone();
|
"Unable to convert 'to' to a string");
|
||||||
|
|
||||||
|
self.to = to.to_string();
|
||||||
},
|
},
|
||||||
NewSession => {
|
NewSession => {
|
||||||
let session_id = match resp.get(&"value".to_string()) {
|
let session_id = try_opt!(
|
||||||
Some(x) => x,
|
try_opt!(resp.get("sessionId"),
|
||||||
None => return Err(WebDriverError::new(ErrorStatus::UnknownError,
|
ErrorStatus::SessionNotCreated,
|
||||||
"Unable to get session id"))
|
"Unable to get session id").as_string(),
|
||||||
};
|
ErrorStatus::SessionNotCreated,
|
||||||
|
"Unable to convert session id to string");
|
||||||
self.session_id = session_id.to_string().clone();
|
self.session_id = session_id.to_string().clone();
|
||||||
}
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -60,7 +75,23 @@ impl MarionetteSession {
|
||||||
GetWindowHandle => "getWindowHandle",
|
GetWindowHandle => "getWindowHandle",
|
||||||
GetWindowHandles => "getWindowHandles",
|
GetWindowHandles => "getWindowHandles",
|
||||||
Close => "close",
|
Close => "close",
|
||||||
Timeouts(_) => "timeouts"
|
Timeouts(_) => "timeouts",
|
||||||
|
SetWindowSize(_) => "setWindowSize",
|
||||||
|
GetWindowSize => "getWindowSize",
|
||||||
|
MaximizeWindow => "maximizeWindow",
|
||||||
|
SwitchToWindow(_) => "switchToWindow",
|
||||||
|
SwitchToFrame(_) => "switchToFrame",
|
||||||
|
SwitchToParentFrame => "switchToParentFrame",
|
||||||
|
IsDisplayed(_) => "isElementDisplayed",
|
||||||
|
IsSelected(_) => "isElementSelected",
|
||||||
|
GetElementAttribute(_, _) => "getElementAttribute",
|
||||||
|
GetCSSValue(_, _) => "getElementValueOfCssProperty",
|
||||||
|
GetElementText(_) => "getElementText",
|
||||||
|
GetElementTagName(_) => "getElementTagName",
|
||||||
|
GetElementRect(_) => "getElementRect",
|
||||||
|
IsEnabled(_) => "isElementEnabled",
|
||||||
|
ExecuteScript(_) => "executeScript",
|
||||||
|
ExecuteAsyncScript(_) => "executeAsyncScript"
|
||||||
}.to_string()
|
}.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,70 +102,163 @@ impl MarionetteSession {
|
||||||
None => None
|
None => None
|
||||||
};
|
};
|
||||||
data.insert("to".to_string(), self.to.to_json());
|
data.insert("to".to_string(), self.to.to_json());
|
||||||
data.insert("command".to_string(), MarionetteSession::command_name(msg).to_json());
|
data.insert("name".to_string(), MarionetteSession::command_name(msg).to_json());
|
||||||
json::Object(data)
|
json::Object(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn response_from_json(&mut self, message: &WebDriverMessage,
|
pub fn response_from_json(&mut self, message: &WebDriverMessage,
|
||||||
data: &str) -> Option<WebDriverResult<WebDriverResponse>> {
|
data: &str) -> WebDriverResult<Option<WebDriverResponse>> {
|
||||||
let decoded = match json::from_str(data) {
|
let json_data = try!(object_from_json(data));
|
||||||
Ok(data) => data,
|
|
||||||
Err(_) => {
|
|
||||||
return Some(Err(WebDriverError::new(ErrorStatus::UnknownError,
|
|
||||||
"Failed to decode marionette data as json")));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let json_data = match decoded {
|
|
||||||
json::Object(x) => x,
|
|
||||||
_ => {
|
|
||||||
return Some(Err(WebDriverError::new(ErrorStatus::UnknownError,
|
|
||||||
"Expected a json object")));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if json_data.contains_key(&"error".to_string()) {
|
if json_data.contains_key(&"error".to_string()) {
|
||||||
//TODO: convert the marionette error into the right webdriver error
|
//TODO: convert the marionette error into the right webdriver error
|
||||||
let err_msg = match json_data.get(&"error".to_string()).unwrap().as_string() {
|
let error = try_opt!(json_data.get("error").unwrap().as_object(),
|
||||||
Some(x) => x,
|
ErrorStatus::UnknownError,
|
||||||
None => "Unexpected error"
|
"Marionette error field was not an object");
|
||||||
};
|
let status_code = try_opt!(
|
||||||
return Some(Err(WebDriverError::new(ErrorStatus::UnknownError,
|
try_opt!(error.get("status"),
|
||||||
err_msg)));
|
ErrorStatus::UnknownError,
|
||||||
|
"Error dict doesn't have a status field").as_u64(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Error status isn't an integer");
|
||||||
|
let status = self.error_from_code(status_code);
|
||||||
|
let default_msg = Json::String("Unknown error".into_string());
|
||||||
|
let err_msg = try_opt!(
|
||||||
|
error.get("message").unwrap_or(&default_msg).as_string(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Error message was not a string");
|
||||||
|
return Err(WebDriverError::new(status, err_msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update(message, &json_data);
|
self.update(message, &json_data);
|
||||||
|
|
||||||
match message.command {
|
match message.command {
|
||||||
//Everything that doesn't have a response value
|
//Everything that doesn't have a response value
|
||||||
GetMarionetteId => None,
|
GetMarionetteId => Ok(None),
|
||||||
Get(_) | GoBack | GoForward | Refresh | Close | Timeouts(_) => {
|
Get(_) | GoBack | GoForward | Refresh | Close | Timeouts(_) |
|
||||||
Some(Ok(WebDriverResponse::new(json::Null)))
|
SetWindowSize(_) | MaximizeWindow | SwitchToWindow(_) | SwitchToFrame(_) |
|
||||||
|
SwitchToParentFrame => {
|
||||||
|
Ok(Some(WebDriverResponse::Void))
|
||||||
},
|
},
|
||||||
//Things that simply return the contents of the marionette "value" property
|
//Things that simply return the contents of the marionette "value" property
|
||||||
GetCurrentUrl | GetTitle | GetWindowHandle | GetWindowHandles => {
|
GetCurrentUrl | GetTitle | GetWindowHandle | GetWindowHandles | IsDisplayed(_) |
|
||||||
let value = match json_data.get(&"value".to_string()) {
|
IsSelected(_) | GetElementAttribute(_, _) | GetCSSValue(_, _) | GetElementText(_) |
|
||||||
Some(data) => data,
|
GetElementTagName(_) | IsEnabled(_) | ExecuteScript(_) | ExecuteAsyncScript(_) => {
|
||||||
None => {
|
let value = try_opt!(json_data.get("value"),
|
||||||
return Some(Err(WebDriverError::new(ErrorStatus::UnknownError,
|
ErrorStatus::UnknownError,
|
||||||
"Failed to find value field")));
|
"Failed to find value field");
|
||||||
}
|
Ok(Some(WebDriverResponse::Generic(ValueResponse::new(value.clone()))))
|
||||||
};
|
|
||||||
Some(Ok(WebDriverResponse::new(value.clone())))
|
|
||||||
},
|
},
|
||||||
|
GetWindowSize => {
|
||||||
|
let value = try_opt!(
|
||||||
|
try_opt!(json_data.get("value"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to find value field").as_object(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to interpret value as object");
|
||||||
|
|
||||||
|
let width = try_opt!(
|
||||||
|
try_opt!(value.get("width"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to find width field").as_u64(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to interpret width as integer");
|
||||||
|
|
||||||
|
let height = try_opt!(
|
||||||
|
try_opt!(value.get("height"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to find height field").as_u64(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to interpret width as integer");
|
||||||
|
|
||||||
|
Ok(Some(WebDriverResponse::WindowSize(WindowSizeResponse::new(width, height))))
|
||||||
|
},
|
||||||
|
GetElementRect(_) => {
|
||||||
|
let value = try_opt!(
|
||||||
|
try_opt!(json_data.get("value"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to find value field").as_object(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to interpret value as object");
|
||||||
|
|
||||||
|
let x = try_opt!(
|
||||||
|
try_opt!(value.get("x"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to find x field").as_u64(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to interpret x as integer");
|
||||||
|
|
||||||
|
let y = try_opt!(
|
||||||
|
try_opt!(value.get("y"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to find y field").as_u64(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to interpret y as integer");
|
||||||
|
|
||||||
|
let width = try_opt!(
|
||||||
|
try_opt!(value.get("width"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to find width field").as_u64(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to interpret width as integer");
|
||||||
|
|
||||||
|
let height = try_opt!(
|
||||||
|
try_opt!(value.get("height"),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to find height field").as_u64(),
|
||||||
|
ErrorStatus::UnknownError,
|
||||||
|
"Failed to interpret width as integer");
|
||||||
|
|
||||||
|
Ok(Some(WebDriverResponse::ElementRect(ElementRectResponse::new(x, y, width, height))))
|
||||||
|
}
|
||||||
NewSession => {
|
NewSession => {
|
||||||
let value = match json_data.get(&"value".to_string()) {
|
let session_id = try_opt!(
|
||||||
Some(data) => data,
|
try_opt!(json_data.get("sessionId"),
|
||||||
None => {
|
ErrorStatus::InvalidSessionId,
|
||||||
return Some(Err(WebDriverError::new(ErrorStatus::UnknownError,
|
"Failed to find sessionId field").as_string(),
|
||||||
"Failed to find value field")));
|
ErrorStatus::InvalidSessionId,
|
||||||
}
|
"sessionId was not a string");
|
||||||
};
|
|
||||||
Some(Ok(WebDriverResponse::new(value.clone())))
|
let value = try_opt!(
|
||||||
|
try_opt!(json_data.get("value"),
|
||||||
|
ErrorStatus::SessionNotCreated,
|
||||||
|
"Failed to find value field").as_object(),
|
||||||
|
ErrorStatus::SessionNotCreated,
|
||||||
|
"value field was not an Object");
|
||||||
|
|
||||||
|
Ok(Some(WebDriverResponse::NewSession(NewSessionResponse::new(
|
||||||
|
session_id.to_string(), json::Object(value.clone())))))
|
||||||
}
|
}
|
||||||
DeleteSession => {
|
DeleteSession => {
|
||||||
Some(Ok(WebDriverResponse::new(json::Null)))
|
Ok(Some(WebDriverResponse::DeleteSession))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn error_from_code(&self, error_code: u64) -> ErrorStatus {
|
||||||
|
match error_code {
|
||||||
|
7 => ErrorStatus::NoSuchElement,
|
||||||
|
8 => ErrorStatus::NoSuchFrame,
|
||||||
|
9 => ErrorStatus::UnsupportedOperation,
|
||||||
|
10 => ErrorStatus::StaleElementReference,
|
||||||
|
11 => ErrorStatus::ElementNotVisible,
|
||||||
|
12 => ErrorStatus::InvalidElementState,
|
||||||
|
15 => ErrorStatus::ElementNotSelectable,
|
||||||
|
17 => ErrorStatus::JavascriptError,
|
||||||
|
21 => ErrorStatus::Timeout,
|
||||||
|
23 => ErrorStatus::NoSuchWindow,
|
||||||
|
24 => ErrorStatus::InvalidCookieDomain,
|
||||||
|
25 => ErrorStatus::UnableToSetCookie,
|
||||||
|
26 => ErrorStatus::UnexpectedAlertOpen,
|
||||||
|
27 => ErrorStatus::NoSuchAlert,
|
||||||
|
28 => ErrorStatus::ScriptTimeout,
|
||||||
|
29 => ErrorStatus::InvalidElementCoordinates,
|
||||||
|
32 => ErrorStatus::InvalidSelector,
|
||||||
|
34 => ErrorStatus::MoveTargetOutOfBounds,
|
||||||
|
405 => ErrorStatus::UnsupportedOperation,
|
||||||
|
13 | 19 | 51 | 52 | 53 | 54 | 55 | 56 | 500 | _ => ErrorStatus::UnknownError
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MarionetteConnection {
|
pub struct MarionetteConnection {
|
||||||
|
@ -143,11 +267,11 @@ pub struct MarionetteConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MarionetteConnection {
|
impl MarionetteConnection {
|
||||||
pub fn new() -> MarionetteConnection {
|
pub fn new(session_id: Option<String>) -> MarionetteConnection {
|
||||||
let stream = TcpStream::connect("127.0.0.1:2828");
|
let stream = TcpStream::connect("127.0.0.1:2828");
|
||||||
MarionetteConnection {
|
MarionetteConnection {
|
||||||
stream: stream,
|
stream: stream,
|
||||||
session: MarionetteSession::new()
|
session: MarionetteSession::new(session_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,10 +279,23 @@ impl MarionetteConnection {
|
||||||
try!(self.read_resp());
|
try!(self.read_resp());
|
||||||
//Would get traits and application type here
|
//Would get traits and application type here
|
||||||
let mut msg = TreeMap::new();
|
let mut msg = TreeMap::new();
|
||||||
msg.insert("name".to_string(), "getMarionetteId".to_json());
|
msg.insert("name".to_string(), "getMarionetteID".to_json());
|
||||||
msg.insert("to".to_string(), "root".to_json());
|
msg.insert("to".to_string(), "root".to_json());
|
||||||
match self.send(&msg.to_json()) {
|
match self.send(&msg.to_json()) {
|
||||||
Ok(_) => Ok(()),
|
Ok(resp) => {
|
||||||
|
let json_data = match object_from_json(resp.as_slice()) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => panic!("Failed to connect to marionette")
|
||||||
|
};
|
||||||
|
match json_data.get(&"id".to_string()) {
|
||||||
|
Some(x) => match x.as_string() {
|
||||||
|
Some(id) => self.session.to = id.to_string(),
|
||||||
|
None => panic!("Failed to connect to marionette")
|
||||||
|
},
|
||||||
|
None => panic!("Failed to connect to marionette")
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Err(_) => panic!("Failed to connect to marionette")
|
Err(_) => panic!("Failed to connect to marionette")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,20 +309,20 @@ impl MarionetteConnection {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_message(&mut self, msg: &WebDriverMessage) -> Option<WebDriverResult<WebDriverResponse>> {
|
pub fn send_message(&mut self, msg: &WebDriverMessage) -> WebDriverResult<Option<WebDriverResponse>> {
|
||||||
let resp = {
|
let resp = {
|
||||||
self.session.msg_to_marionette(msg)
|
self.session.msg_to_marionette(msg)
|
||||||
};
|
};
|
||||||
let resp = match self.send(&resp) {
|
let resp = match self.send(&resp) {
|
||||||
Ok(resp_data) => self.session.response_from_json(msg, resp_data[]),
|
Ok(resp_data) => self.session.response_from_json(msg, resp_data[]),
|
||||||
Err(x) => Some(Err(x))
|
Err(x) => Err(x)
|
||||||
};
|
};
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&mut self, msg: &json::Json) -> WebDriverResult<String> {
|
fn send(&mut self, msg: &json::Json) -> WebDriverResult<String> {
|
||||||
let data = self.encode_msg(msg);
|
let data = self.encode_msg(msg);
|
||||||
println!("{}", data);
|
debug!("Sending {}", data);
|
||||||
match self.stream.write_str(data.as_slice()) {
|
match self.stream.write_str(data.as_slice()) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -195,6 +332,7 @@ impl MarionetteConnection {
|
||||||
}
|
}
|
||||||
match self.read_resp() {
|
match self.read_resp() {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
|
debug!("Marionette response {}", resp);
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
},
|
},
|
||||||
Err(_) => Err(WebDriverError::new(ErrorStatus::UnknownError,
|
Err(_) => Err(WebDriverError::new(ErrorStatus::UnknownError,
|
||||||
|
@ -221,5 +359,4 @@ impl MarionetteConnection {
|
||||||
//Need to handle the error here
|
//Need to handle the error here
|
||||||
Ok(String::from_utf8(data).unwrap())
|
Ok(String::from_utf8(data).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,23 @@ pub enum MatchType {
|
||||||
GetWindowHandle,
|
GetWindowHandle,
|
||||||
GetWindowHandles,
|
GetWindowHandles,
|
||||||
Close,
|
Close,
|
||||||
Timeouts
|
Timeouts,
|
||||||
|
SetWindowSize,
|
||||||
|
GetWindowSize,
|
||||||
|
MaximizeWindow,
|
||||||
|
SwitchToWindow,
|
||||||
|
SwitchToFrame,
|
||||||
|
SwitchToParentFrame,
|
||||||
|
IsDisplayed,
|
||||||
|
IsSelected,
|
||||||
|
GetElementAttribute,
|
||||||
|
GetCSSValue,
|
||||||
|
GetElementText,
|
||||||
|
GetElementTagName,
|
||||||
|
GetElementRect,
|
||||||
|
IsEnabled,
|
||||||
|
ExecuteScript,
|
||||||
|
ExecuteAsyncScript,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
|
@ -39,7 +55,6 @@ impl RequestMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_match<'t>(&'t self, method: Method, path: &'t str) -> (bool, Option<Captures>) {
|
pub fn get_match<'t>(&'t self, method: Method, path: &'t str) -> (bool, Option<Captures>) {
|
||||||
println!("{} {}", method, path);
|
|
||||||
let captures = self.path_regexp.captures(path);
|
let captures = self.path_regexp.captures(path);
|
||||||
(method == self.method, captures)
|
(method == self.method, captures)
|
||||||
}
|
}
|
||||||
|
@ -78,10 +93,8 @@ impl MessageBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_http(&self, method: Method, path: &str, body: &str) -> WebDriverResult<WebDriverMessage> {
|
pub fn from_http(&self, method: Method, path: &str, body: &str) -> WebDriverResult<WebDriverMessage> {
|
||||||
println!("{} {}", method, path);
|
|
||||||
let mut error = ErrorStatus::UnknownPath;
|
let mut error = ErrorStatus::UnknownPath;
|
||||||
for &(ref match_method, ref matcher) in self.http_matchers.iter() {
|
for &(ref match_method, ref matcher) in self.http_matchers.iter() {
|
||||||
println!("{} {}", match_method, matcher.path_regexp);
|
|
||||||
if method == *match_method {
|
if method == *match_method {
|
||||||
let (method_match, captures) = matcher.get_match(method.clone(), path);
|
let (method_match, captures) = matcher.get_match(method.clone(), path);
|
||||||
if captures.is_some() {
|
if captures.is_some() {
|
||||||
|
@ -96,7 +109,7 @@ impl MessageBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(WebDriverError::new(error,
|
Err(WebDriverError::new(error,
|
||||||
format!("{} did not match a known command", path)[]))
|
format!("{} {} did not match a known command", method, path)[]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, method: Method, path: &str, match_type: MatchType) {
|
pub fn add(&mut self, method: Method, path: &str, match_type: MatchType) {
|
||||||
|
@ -118,10 +131,26 @@ pub fn get_builder() -> MessageBuilder {
|
||||||
(Get, "/session/{sessionId}/window_handle", MatchType::GetWindowHandle),
|
(Get, "/session/{sessionId}/window_handle", MatchType::GetWindowHandle),
|
||||||
(Get, "/session/{sessionId}/window_handles", MatchType::GetWindowHandles),
|
(Get, "/session/{sessionId}/window_handles", MatchType::GetWindowHandles),
|
||||||
(Delete, "/session/{sessionId}/window_handle", MatchType::Close),
|
(Delete, "/session/{sessionId}/window_handle", MatchType::Close),
|
||||||
(Post, "/session/{sessionId}/timeouts", MatchType::Timeouts)
|
(Post, "/session/{sessionId}/timeouts", MatchType::Timeouts),
|
||||||
|
(Post, "/session/{sessionId}/window/size", MatchType::SetWindowSize),
|
||||||
|
(Get, "/session/{sessionId}/window/size", MatchType::GetWindowSize),
|
||||||
|
(Post, "/session/{sessionId}/window/maximize", MatchType::MaximizeWindow),
|
||||||
|
(Post, "/session/{sessionId}/window", MatchType::SwitchToWindow),
|
||||||
|
(Post, "/session/{sessionId}/frame", MatchType::SwitchToFrame),
|
||||||
|
(Post, "/session/{sessionId}/frame/parent", MatchType::SwitchToParentFrame),
|
||||||
|
(Get, "/session/{sessionId}/element/{element}/isDisplayed", MatchType::IsDisplayed),
|
||||||
|
(Get, "/session/{sessionId}/element/{element}/isSelected", MatchType::IsSelected),
|
||||||
|
(Get, "/session/{sessionId}/element/{element}/attribute/{name}", MatchType::GetElementAttribute),
|
||||||
|
(Get, "/session/{sessionId}/element/{element}/css/{propertyName}", MatchType::GetCSSValue),
|
||||||
|
(Get, "/session/{sessionId}/element/{element}/text", MatchType::GetElementText),
|
||||||
|
(Get, "/session/{sessionId}/element/{element}/name", MatchType::GetElementTagName),
|
||||||
|
(Get, "/session/{sessionId}/element/{element}/rect", MatchType::GetElementRect),
|
||||||
|
(Get, "/session/{sessionId}/element/{element}/enabled", MatchType::IsEnabled),
|
||||||
|
(Post, "/session/{sessionId}/execute", MatchType::ExecuteScript),
|
||||||
|
(Post, "/session/{sessionId}/execute_async", MatchType::ExecuteAsyncScript),
|
||||||
];
|
];
|
||||||
|
debug!("Creating routes");
|
||||||
for &(ref method, ref url, ref match_type) in matchers.iter() {
|
for &(ref method, ref url, ref match_type) in matchers.iter() {
|
||||||
println!("{} {}", method, url);
|
|
||||||
builder.add(method.clone(), *url, *match_type);
|
builder.add(method.clone(), *url, *match_type);
|
||||||
}
|
}
|
||||||
builder
|
builder
|
||||||
|
|
|
@ -1,30 +1,86 @@
|
||||||
use std::collections::TreeMap;
|
|
||||||
use serialize::json;
|
use serialize::json;
|
||||||
use serialize::json::{ToJson};
|
|
||||||
|
|
||||||
use command::WebDriverMessage;
|
#[deriving(Show)]
|
||||||
use command::WebDriverCommand::{GetMarionetteId, NewSession, DeleteSession, Get, GetCurrentUrl,
|
pub enum WebDriverResponse {
|
||||||
GoBack, GoForward, Refresh, GetTitle,
|
NewSession(NewSessionResponse),
|
||||||
GetWindowHandle, GetWindowHandles, Close, Timeouts};
|
DeleteSession,
|
||||||
use marionette::{MarionetteSession};
|
WindowSize(WindowSizeResponse),
|
||||||
|
ElementRect(ElementRectResponse),
|
||||||
use common::{ErrorStatus, WebDriverError, WebDriverResult};
|
Generic(ValueResponse),
|
||||||
|
Void
|
||||||
pub struct WebDriverResponse {
|
|
||||||
value: json::Json
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebDriverResponse {
|
impl WebDriverResponse {
|
||||||
pub fn new(value: json::Json) -> WebDriverResponse {
|
pub fn to_json_string(self) -> String {
|
||||||
WebDriverResponse {
|
match self {
|
||||||
value: value
|
WebDriverResponse::NewSession(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::DeleteSession => "".into_string(),
|
||||||
|
WebDriverResponse::WindowSize(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::ElementRect(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::Generic(x) => json::encode(&x),
|
||||||
|
WebDriverResponse::Void => "".into_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_json(&self) -> json::Json {
|
|
||||||
let mut data = TreeMap::new();
|
|
||||||
data.insert("value".to_string(), self.value.to_json());
|
|
||||||
json::Object(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deriving(Encodable, Show)]
|
||||||
|
pub struct NewSessionResponse {
|
||||||
|
sessionId: String,
|
||||||
|
value: json::Json
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NewSessionResponse {
|
||||||
|
pub fn new(session_id: String, value: json::Json) -> NewSessionResponse {
|
||||||
|
NewSessionResponse {
|
||||||
|
value: value,
|
||||||
|
sessionId: session_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Encodable, Show)]
|
||||||
|
pub struct ValueResponse {
|
||||||
|
value: json::Json
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValueResponse {
|
||||||
|
pub fn new(value: json::Json) -> ValueResponse {
|
||||||
|
ValueResponse {
|
||||||
|
value: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Encodable, Show)]
|
||||||
|
pub struct WindowSizeResponse {
|
||||||
|
width: u64,
|
||||||
|
height: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowSizeResponse {
|
||||||
|
pub fn new(width: u64, height: u64) -> WindowSizeResponse {
|
||||||
|
WindowSizeResponse {
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Encodable, Show)]
|
||||||
|
pub struct ElementRectResponse {
|
||||||
|
x: u64,
|
||||||
|
y: u64,
|
||||||
|
width: u64,
|
||||||
|
height: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElementRectResponse {
|
||||||
|
pub fn new(x: u64, y: u64, width: u64, height: u64) -> ElementRectResponse {
|
||||||
|
ElementRectResponse {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче