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:
James Graham 2015-01-02 20:44:54 +00:00
Родитель 2924471963
Коммит 97d7fef217
7 изменённых файлов: 839 добавлений и 193 удалений

Просмотреть файл

@ -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
}
}
}