Skip to content

Commit

Permalink
Add extension command for finding anonymous nodes (#203)
Browse files Browse the repository at this point in the history
* add extension command for finding anonymous nodes

XBL has the concept of anonymous nodes that are not returned by the usual
WebDriver element-finding methods. However there are two Gecko-specific
methods of finding them; either by getting all the anonymous children of
a reference element, or getting a single anonymous child of a reference
element with specified attribute values.

This commit adds two endpoints corresponding to those methods:

	/session/{sessionId}/moz/xbl/{elementId}/anonymous_children

to return all anonymous children, and

	/session/{sessionId}/moz/xbl/{elementId}/anonymous_by_attribute

to return an anonymous element with the given attribute value, provided
as a body of the form:

	{
		"name": <attribute name>,
		"value": <attribute value>
	}
  • Loading branch information
andreastt authored and jgraham committed Aug 31, 2016
1 parent 6e6335a commit e2a6c30
Showing 1 changed file with 104 additions and 6 deletions.
110 changes: 104 additions & 6 deletions src/marionette.rs
Expand Up @@ -117,20 +117,25 @@ lazy_static! {

pub fn extension_routes() -> Vec<(Method, &'static str, GeckoExtensionRoute)> {
return vec![(Method::Get, "/session/{sessionId}/moz/context", GeckoExtensionRoute::GetContext),
(Method::Post, "/session/{sessionId}/moz/context", GeckoExtensionRoute::SetContext)]
(Method::Post, "/session/{sessionId}/moz/context", GeckoExtensionRoute::SetContext),
(Method::Post, "/session/{sessionId}/moz/xbl/{elementId}/anonymous_children", GeckoExtensionRoute::XblAnonymousChildren),
(Method::Post, "/session/{sessionId}/moz/xbl/{elementId}/anonymous_by_attribute", GeckoExtensionRoute::XblAnonymousByAttribute),
]
}

#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, PartialEq)]
pub enum GeckoExtensionRoute {
GetContext,
SetContext,
XblAnonymousChildren,
XblAnonymousByAttribute,
}

impl WebDriverExtensionRoute for GeckoExtensionRoute {
type Command = GeckoExtensionCommand;

fn command(&self,
_captures: &Captures,
captures: &Captures,
body_data: &Json) -> WebDriverResult<WebDriverCommand<GeckoExtensionCommand>> {
let command = match self {
&GeckoExtensionRoute::GetContext => {
Expand All @@ -139,6 +144,21 @@ impl WebDriverExtensionRoute for GeckoExtensionRoute {
&GeckoExtensionRoute::SetContext => {
let parameters: GeckoContextParameters = try!(Parameters::from_json(&body_data));
GeckoExtensionCommand::SetContext(parameters)
},
&GeckoExtensionRoute::XblAnonymousChildren => {
let element_id = try!(captures.name("elementId")
.ok_or(WebDriverError::new(
ErrorStatus::InvalidArgument,
"Missing elementId parameter")));
GeckoExtensionCommand::XblAnonymousChildren(element_id.into())
},
&GeckoExtensionRoute::XblAnonymousByAttribute => {
let element_id = try!(captures.name("elementId")
.ok_or(WebDriverError::new(
ErrorStatus::InvalidArgument,
"Missing elementId parameter")));
let parameters: AttributeParameters = try!(Parameters::from_json(&body_data));
GeckoExtensionCommand::XblAnonymousByAttribute(element_id.into(), parameters)
}
};
Ok(WebDriverCommand::Extension(command))
Expand All @@ -149,13 +169,17 @@ impl WebDriverExtensionRoute for GeckoExtensionRoute {
pub enum GeckoExtensionCommand {
GetContext,
SetContext(GeckoContextParameters),
XblAnonymousChildren(WebElement),
XblAnonymousByAttribute(WebElement, AttributeParameters),
}

impl WebDriverExtensionCommand for GeckoExtensionCommand {
fn parameters_json(&self) -> Option<Json> {
match self {
&GeckoExtensionCommand::GetContext => None,
&GeckoExtensionCommand::SetContext(ref x) => Some(x.to_json()),
&GeckoExtensionCommand::XblAnonymousChildren(_) => None,
&GeckoExtensionCommand::XblAnonymousByAttribute(_, ref x) => Some(x.to_json()),
}
}
}
Expand Down Expand Up @@ -205,6 +229,14 @@ impl Parameters for GeckoContextParameters {
}
}

impl ToMarionette for GeckoContextParameters {
fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
let mut data = BTreeMap::new();
data.insert("value".to_owned(), self.context.to_json());
Ok(data)
}
}

impl ToJson for GeckoContextParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
Expand All @@ -213,10 +245,51 @@ impl ToJson for GeckoContextParameters {
}
}

impl ToMarionette for GeckoContextParameters {

#[derive(Clone, Debug, PartialEq)]
pub struct AttributeParameters {
name: String,
value: String
}

impl Parameters for AttributeParameters {
fn from_json(body: &Json) -> WebDriverResult<AttributeParameters> {
let data = try!(body.as_object().ok_or(
WebDriverError::new(ErrorStatus::InvalidArgument,
"Message body was not an object")));
let name = try!(try!(data.get("name").ok_or(
WebDriverError::new(ErrorStatus::InvalidArgument,
"Missing 'name' parameter"))).as_string().
ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
"'name' parameter is not a string")));
let value = try!(try!(data.get("value").ok_or(
WebDriverError::new(ErrorStatus::InvalidArgument,
"Missing 'value' parameter"))).as_string().
ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
"'value' parameter is not a string")));
Ok(AttributeParameters {
name: name.to_owned(),
value: value.to_owned(),
})
}
}

impl ToJson for AttributeParameters {
fn to_json(&self) -> Json {
let mut data = BTreeMap::new();
data.insert("name".to_owned(), self.name.to_json());
data.insert("value".to_owned(), self.value.to_json());
Json::Object(data)
}
}

impl ToMarionette for AttributeParameters {
fn to_marionette(&self) -> WebDriverResult<BTreeMap<String, Json>> {
let mut data = BTreeMap::new();
data.insert("value".to_owned(), self.context.to_json());
data.insert("using".to_owned(), "anon attribute".to_json());
let mut value = BTreeMap::new();
value.insert(self.name.to_owned(), self.value.to_json());
data.insert("value".to_owned(), Json::Object(value));
Ok(data)
}
}
Expand Down Expand Up @@ -789,8 +862,21 @@ impl MarionetteSession {
ErrorStatus::UnknownError,
"Failed to find value field");
WebDriverResponse::Generic(ValueResponse::new(value.clone()))
},
&GeckoExtensionCommand::SetContext(_) => WebDriverResponse::Void,
&GeckoExtensionCommand::XblAnonymousChildren(_) => {
let els_vec = try_opt!(resp.result.as_array(),
ErrorStatus::UnknownError, "Failed to interpret body as array");
let els = try!(els_vec.iter().map(|x| self.to_web_element(x))
.collect::<Result<Vec<_>, _>>());
WebDriverResponse::Generic(ValueResponse::new(
Json::Array(els.iter().map(|el| el.to_json()).collect())))
},
&GeckoExtensionCommand::XblAnonymousByAttribute(_, _) => {
let el = try!(self.to_web_element(try_opt!(resp.result.find("value"),
ErrorStatus::UnknownError, "Failed to find value field")));
WebDriverResponse::Generic(ValueResponse::new(el.to_json()))
}
&GeckoExtensionCommand::SetContext(_) => WebDriverResponse::Void
}
}
})
Expand Down Expand Up @@ -1001,6 +1087,18 @@ impl MarionetteCommand {
&GeckoExtensionCommand::GetContext => (Some("getContext"), None),
&GeckoExtensionCommand::SetContext(ref x) => {
(Some("setContext"), Some(x.to_marionette()))
},
&GeckoExtensionCommand::XblAnonymousChildren(ref e) => {
let mut data = BTreeMap::new();
data.insert("using".to_owned(), "anon".to_json());
data.insert("value".to_owned(), Json::Null);
data.insert("element".to_string(), e.id.to_json());
(Some("findElements"), Some(Ok(data)))
},
&GeckoExtensionCommand::XblAnonymousByAttribute(ref e, ref x) => {
let mut data = try!(x.to_marionette());
data.insert("element".to_string(), e.id.to_json());
(Some("findElement"), Some(Ok(data)))
}
}
}
Expand Down

0 comments on commit e2a6c30

Please sign in to comment.