Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ElementClick wd command #24090

Merged
merged 1 commit into from Sep 4, 2019
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -59,7 +59,10 @@ message
message
monospace
month
mousedown
mousemove
mouseover
mouseup
negotiationneeded
none
number
@@ -2159,6 +2159,14 @@ impl ScriptThread {
reply,
)
},
WebDriverScriptCommand::ElementClick(element_id, reply) => {
webdriver_handlers::handle_element_click(
&*documents,
pipeline_id,
element_id,
reply,
)
},
WebDriverScriptCommand::GetActiveElement(reply) => {
webdriver_handlers::handle_get_active_element(&*documents, pipeline_id, reply)
},
@@ -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

This comment has been minimized.

Copy link
@jdm

jdm Aug 29, 2019

Member

We should file an issue about extract this into a function that supports optgroup as well (per the spec).

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"));

This comment has been minimized.

Copy link
@jdm

jdm Aug 29, 2019

Member

The spec says to fire events at the parent node throughout this method.


// 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,
@@ -58,6 +58,7 @@ pub enum WebDriverScriptCommand {
),
FindElementElementsTagName(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
FocusElement(String, IpcSender<Result<(), ErrorStatus>>),
ElementClick(String, IpcSender<Result<Option<String>, ErrorStatus>>),
GetActiveElement(IpcSender<Option<String>>),
GetCookie(String, IpcSender<Vec<Serde<Cookie<'static>>>>),
GetCookies(IpcSender<Vec<Serde<Cookie<'static>>>>),
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.