diff options
author | George Roman <george.roman.99@gmail.com> | 2019-08-28 00:00:23 +0300 |
---|---|---|
committer | George Roman <george.roman.99@gmail.com> | 2019-08-31 10:14:40 +0300 |
commit | 32f7254883d573f0704fc885243f998e5f62c0b4 (patch) | |
tree | 200d6ebd35bbc3a93e06d9f3b2c5d57680af1bd6 /components/script/webdriver_handlers.rs | |
parent | b2a7fc9046b8488525adea93d8869b264280bfc8 (diff) | |
download | servo-32f7254883d573f0704fc885243f998e5f62c0b4.tar.gz servo-32f7254883d573f0704fc885243f998e5f62c0b4.zip |
Implement ElementClick wd command
Diffstat (limited to 'components/script/webdriver_handlers.rs')
-rw-r--r-- | components/script/webdriver_handlers.rs | 166 |
1 files changed, 135 insertions, 31 deletions
diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index fdb688b1f60..cea4cca5c71 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -9,7 +9,8 @@ use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods; -use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods; +use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods}; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::XMLSerializerBinding::XMLSerializerMethods; use crate::dom::bindings::conversions::{ @@ -24,11 +25,14 @@ use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::element::Element; +use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; +use crate::dom::htmldatalistelement::HTMLDataListElement; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmliframeelement::HTMLIFrameElement; -use crate::dom::htmlinputelement::HTMLInputElement; +use crate::dom::htmlinputelement::{HTMLInputElement, InputType}; use crate::dom::htmloptionelement::HTMLOptionElement; +use crate::dom::htmlselectelement::HTMLSelectElement; use crate::dom::node::{window_from_node, Node, ShadowIncluding}; use crate::dom::nodelist::NodeList; use crate::dom::window::Window; @@ -363,35 +367,38 @@ pub fn handle_get_browsing_context_id( // https://w3c.github.io/webdriver/#dfn-center-point fn get_element_in_view_center_point(element: &Element) -> Option<Point2D<i64>> { - element - .GetClientRects() - .iter() - // Step 1 - .next() - .map(|rectangle| { - let x = rectangle.X().round() as i64; - let y = rectangle.Y().round() as i64; - let width = rectangle.Width().round() as i64; - let height = rectangle.Height().round() as i64; - - let window = window_from_node(element.upcast::<Node>()); - let document = window.Document(); - let document_element = document.upcast::<Node>().downcast::<Element>().unwrap(); - let clientWidth = document_element.ClientWidth() as i64; - let clientHeight = document_element.ClientHeight() as i64; - - // Steps 2 - 5 - let left = cmp::max(0, cmp::min(x, x + width)); - let right = cmp::min(clientWidth, cmp::max(x, x + width)); - let top = cmp::max(0, cmp::min(y, y + height)); - let bottom = cmp::min(clientHeight, cmp::max(y, y + height)); - - // Steps 6 - 7 - let x = (left + right) / 2; - let y = (top + bottom) / 2; - - // Step 8 - Point2D::new(x, y) + window_from_node(element.upcast::<Node>()) + .Document() + .GetBody() + .map(DomRoot::upcast::<Element>) + .and_then(|body| { + element + .GetClientRects() + .iter() + // Step 1 + .next() + .map(|rectangle| { + let x = rectangle.X().round() as i64; + let y = rectangle.Y().round() as i64; + let width = rectangle.Width().round() as i64; + let height = rectangle.Height().round() as i64; + + let clientWidth = body.ClientWidth() as i64; + let clientHeight = body.ClientHeight() as i64; + + // Steps 2 - 5 + let left = cmp::max(0, cmp::min(x, x + width)); + let right = cmp::min(clientWidth, cmp::max(x, x + width)); + let top = cmp::max(0, cmp::min(y, y + height)); + let bottom = cmp::min(clientHeight, cmp::max(y, y + height)); + + // Steps 6 - 7 + let x = (left + right) / 2; + let y = (top + bottom) / 2; + + // Step 8 + Point2D::new(x, y) + }) }) } @@ -1050,6 +1057,103 @@ pub fn handle_get_url(documents: &Documents, pipeline: PipelineId, reply: IpcSen .unwrap(); } +// https://w3c.github.io/webdriver/#element-click +pub fn handle_element_click( + documents: &Documents, + pipeline: PipelineId, + element_id: String, + reply: IpcSender<Result<Option<String>, ErrorStatus>>, +) { + reply + .send( + // Step 3 + find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| { + // Step 4 + if let Some(input_element) = node.downcast::<HTMLInputElement>() { + if input_element.input_type() == InputType::File { + return Err(ErrorStatus::InvalidArgument); + } + } + + // Step 5 + // TODO: scroll into view + + // Step 6 + // TODO: return error if still not in view + + // Step 7 + // TODO: return error if obscured + + // Step 8 + match node.downcast::<HTMLOptionElement>() { + Some(option_element) => { + // https://w3c.github.io/webdriver/#dfn-container + let root_node = node.GetRootNode(&GetRootNodeOptions::empty()); + let datalist_parent = node + .preceding_nodes(&root_node) + .find(|preceding| preceding.is::<HTMLDataListElement>()); + let select_parent = node + .preceding_nodes(&root_node) + .find(|preceding| preceding.is::<HTMLSelectElement>()); + + // Step 8.1 + let parent_node = match datalist_parent { + Some(datalist_parent) => datalist_parent, + None => match select_parent { + Some(select_parent) => select_parent, + None => return Err(ErrorStatus::UnknownError), + }, + }; + + // Steps 8.2 - 8.4 + let event_target = parent_node.upcast::<EventTarget>(); + event_target.fire_event(atom!("mouseover")); + event_target.fire_event(atom!("mousemove")); + event_target.fire_event(atom!("mousedown")); + + // Step 8.5 + match parent_node.downcast::<HTMLElement>() { + Some(html_element) => html_element.Focus(), + None => return Err(ErrorStatus::UnknownError), + } + + // Step 8.6 + if !option_element.Disabled() { + // Step 8.6.1 + event_target.fire_event(atom!("input")); + + // Steps 8.6.2 + let previous_selectedness = option_element.Selected(); + + // Step 8.6.3 + match parent_node.downcast::<HTMLSelectElement>() { + Some(select_element) => { + if select_element.Multiple() { + option_element.SetSelected(!option_element.Selected()); + } + }, + None => option_element.SetSelected(true), + } + + // Step 8.6.4 + if !previous_selectedness { + event_target.fire_event(atom!("change")); + } + } + + // Steps 8.7 - 8.8 + event_target.fire_event(atom!("mouseup")); + event_target.fire_event(atom!("click")); + + Ok(None) + }, + None => Ok(Some(node.unique_id())), + } + }), + ) + .unwrap(); +} + pub fn handle_is_enabled( documents: &Documents, pipeline: PipelineId, |