mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
Implement ElementClick wd command
This commit is contained in:
parent
b2a7fc9046
commit
32f7254883
16 changed files with 336 additions and 142 deletions
|
@ -59,7 +59,10 @@ message
|
||||||
message
|
message
|
||||||
monospace
|
monospace
|
||||||
month
|
month
|
||||||
|
mousedown
|
||||||
|
mousemove
|
||||||
mouseover
|
mouseover
|
||||||
|
mouseup
|
||||||
negotiationneeded
|
negotiationneeded
|
||||||
none
|
none
|
||||||
number
|
number
|
||||||
|
|
|
@ -2159,6 +2159,14 @@ impl ScriptThread {
|
||||||
reply,
|
reply,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
WebDriverScriptCommand::ElementClick(element_id, reply) => {
|
||||||
|
webdriver_handlers::handle_element_click(
|
||||||
|
&*documents,
|
||||||
|
pipeline_id,
|
||||||
|
element_id,
|
||||||
|
reply,
|
||||||
|
)
|
||||||
|
},
|
||||||
WebDriverScriptCommand::GetActiveElement(reply) => {
|
WebDriverScriptCommand::GetActiveElement(reply) => {
|
||||||
webdriver_handlers::handle_get_active_element(&*documents, pipeline_id, 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::HTMLElementBinding::HTMLElementMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
|
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::WindowBinding::WindowMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::XMLSerializerBinding::XMLSerializerMethods;
|
use crate::dom::bindings::codegen::Bindings::XMLSerializerBinding::XMLSerializerMethods;
|
||||||
use crate::dom::bindings::conversions::{
|
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::root::DomRoot;
|
||||||
use crate::dom::bindings::str::DOMString;
|
use crate::dom::bindings::str::DOMString;
|
||||||
use crate::dom::element::Element;
|
use crate::dom::element::Element;
|
||||||
|
use crate::dom::eventtarget::EventTarget;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
|
use crate::dom::htmldatalistelement::HTMLDataListElement;
|
||||||
use crate::dom::htmlelement::HTMLElement;
|
use crate::dom::htmlelement::HTMLElement;
|
||||||
use crate::dom::htmliframeelement::HTMLIFrameElement;
|
use crate::dom::htmliframeelement::HTMLIFrameElement;
|
||||||
use crate::dom::htmlinputelement::HTMLInputElement;
|
use crate::dom::htmlinputelement::{HTMLInputElement, InputType};
|
||||||
use crate::dom::htmloptionelement::HTMLOptionElement;
|
use crate::dom::htmloptionelement::HTMLOptionElement;
|
||||||
|
use crate::dom::htmlselectelement::HTMLSelectElement;
|
||||||
use crate::dom::node::{window_from_node, Node, ShadowIncluding};
|
use crate::dom::node::{window_from_node, Node, ShadowIncluding};
|
||||||
use crate::dom::nodelist::NodeList;
|
use crate::dom::nodelist::NodeList;
|
||||||
use crate::dom::window::Window;
|
use crate::dom::window::Window;
|
||||||
|
@ -363,6 +367,11 @@ pub fn handle_get_browsing_context_id(
|
||||||
|
|
||||||
// https://w3c.github.io/webdriver/#dfn-center-point
|
// https://w3c.github.io/webdriver/#dfn-center-point
|
||||||
fn get_element_in_view_center_point(element: &Element) -> Option<Point2D<i64>> {
|
fn get_element_in_view_center_point(element: &Element) -> Option<Point2D<i64>> {
|
||||||
|
window_from_node(element.upcast::<Node>())
|
||||||
|
.Document()
|
||||||
|
.GetBody()
|
||||||
|
.map(DomRoot::upcast::<Element>)
|
||||||
|
.and_then(|body| {
|
||||||
element
|
element
|
||||||
.GetClientRects()
|
.GetClientRects()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -374,11 +383,8 @@ fn get_element_in_view_center_point(element: &Element) -> Option<Point2D<i64>> {
|
||||||
let width = rectangle.Width().round() as i64;
|
let width = rectangle.Width().round() as i64;
|
||||||
let height = rectangle.Height().round() as i64;
|
let height = rectangle.Height().round() as i64;
|
||||||
|
|
||||||
let window = window_from_node(element.upcast::<Node>());
|
let clientWidth = body.ClientWidth() as i64;
|
||||||
let document = window.Document();
|
let clientHeight = body.ClientHeight() as i64;
|
||||||
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
|
// Steps 2 - 5
|
||||||
let left = cmp::max(0, cmp::min(x, x + width));
|
let left = cmp::max(0, cmp::min(x, x + width));
|
||||||
|
@ -393,6 +399,7 @@ fn get_element_in_view_center_point(element: &Element) -> Option<Point2D<i64>> {
|
||||||
// Step 8
|
// Step 8
|
||||||
Point2D::new(x, y)
|
Point2D::new(x, y)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_get_element_in_view_center_point(
|
pub fn handle_get_element_in_view_center_point(
|
||||||
|
@ -1050,6 +1057,103 @@ pub fn handle_get_url(documents: &Documents, pipeline: PipelineId, reply: IpcSen
|
||||||
.unwrap();
|
.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(
|
pub fn handle_is_enabled(
|
||||||
documents: &Documents,
|
documents: &Documents,
|
||||||
pipeline: PipelineId,
|
pipeline: PipelineId,
|
||||||
|
|
|
@ -58,6 +58,7 @@ pub enum WebDriverScriptCommand {
|
||||||
),
|
),
|
||||||
FindElementElementsTagName(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
FindElementElementsTagName(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||||
FocusElement(String, IpcSender<Result<(), ErrorStatus>>),
|
FocusElement(String, IpcSender<Result<(), ErrorStatus>>),
|
||||||
|
ElementClick(String, IpcSender<Result<Option<String>, ErrorStatus>>),
|
||||||
GetActiveElement(IpcSender<Option<String>>),
|
GetActiveElement(IpcSender<Option<String>>),
|
||||||
GetCookie(String, IpcSender<Vec<Serde<Cookie<'static>>>>),
|
GetCookie(String, IpcSender<Vec<Serde<Cookie<'static>>>>),
|
||||||
GetCookies(IpcSender<Vec<Serde<Cookie<'static>>>>),
|
GetCookies(IpcSender<Vec<Serde<Cookie<'static>>>>),
|
||||||
|
|
|
@ -3,14 +3,12 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use crate::Handler;
|
use crate::Handler;
|
||||||
use crossbeam_channel::Sender;
|
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use keyboard_types::webdriver::KeyInputState;
|
use keyboard_types::webdriver::KeyInputState;
|
||||||
use script_traits::webdriver_msg::WebDriverScriptCommand;
|
use script_traits::webdriver_msg::WebDriverScriptCommand;
|
||||||
use script_traits::{ConstellationMsg, MouseButton, MouseEventType, WebDriverCommandMsg};
|
use script_traits::{ConstellationMsg, MouseButton, MouseEventType, WebDriverCommandMsg};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use webdriver::actions::{ActionSequence, ActionsType, GeneralAction, NullActionItem};
|
use webdriver::actions::{ActionSequence, ActionsType, GeneralAction, NullActionItem};
|
||||||
|
@ -28,7 +26,7 @@ static POINTERMOVE_INTERVAL: u64 = 17;
|
||||||
pub(crate) enum InputSourceState {
|
pub(crate) enum InputSourceState {
|
||||||
Null,
|
Null,
|
||||||
Key(KeyInputState),
|
Key(KeyInputState),
|
||||||
Pointer(Arc<Mutex<PointerInputState>>),
|
Pointer(PointerInputState),
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webdriver/#dfn-pointer-input-source
|
// https://w3c.github.io/webdriver/#dfn-pointer-input-source
|
||||||
|
@ -82,73 +80,6 @@ fn compute_tick_duration(tick_actions: &ActionSequence) -> u64 {
|
||||||
duration
|
duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webdriver/#dfn-perform-a-pointer-move
|
|
||||||
fn perform_pointer_move(
|
|
||||||
constellation_chan: Sender<ConstellationMsg>,
|
|
||||||
pointer_input_state: Arc<Mutex<PointerInputState>>,
|
|
||||||
duration: u64,
|
|
||||||
start_x: i64,
|
|
||||||
start_y: i64,
|
|
||||||
target_x: i64,
|
|
||||||
target_y: i64,
|
|
||||||
tick_start: Instant,
|
|
||||||
) {
|
|
||||||
let mut pointer_input_state = pointer_input_state.lock().unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Step 1
|
|
||||||
let time_delta = tick_start.elapsed().as_millis();
|
|
||||||
|
|
||||||
// Step 2
|
|
||||||
let duration_ratio = if duration > 0 {
|
|
||||||
time_delta as f64 / duration as f64
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
// Step 3
|
|
||||||
let last = if 1.0 - duration_ratio < 0.001 {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
// Step 4
|
|
||||||
let (x, y) = if last {
|
|
||||||
(target_x, target_y)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
(duration_ratio * (target_x - start_x) as f64) as i64 + start_x,
|
|
||||||
(duration_ratio * (target_y - start_y) as f64) as i64 + start_y,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Steps 5 - 6
|
|
||||||
let current_x = pointer_input_state.x;
|
|
||||||
let current_y = pointer_input_state.y;
|
|
||||||
|
|
||||||
// Step 7
|
|
||||||
if x != current_x || y != current_y {
|
|
||||||
// Step 7.2
|
|
||||||
let cmd_msg = WebDriverCommandMsg::MouseMoveAction(x as f32, y as f32);
|
|
||||||
constellation_chan
|
|
||||||
.send(ConstellationMsg::WebDriverCommand(cmd_msg))
|
|
||||||
.unwrap();
|
|
||||||
// Step 7.3
|
|
||||||
pointer_input_state.x = x;
|
|
||||||
pointer_input_state.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 8
|
|
||||||
if last {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 9
|
|
||||||
thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u64_to_mouse_button(button: u64) -> Option<MouseButton> {
|
fn u64_to_mouse_button(button: u64) -> Option<MouseButton> {
|
||||||
if MouseButton::Left as u64 == button {
|
if MouseButton::Left as u64 == button {
|
||||||
Some(MouseButton::Left)
|
Some(MouseButton::Left)
|
||||||
|
@ -235,9 +166,9 @@ impl Handler {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.input_state_table
|
.input_state_table
|
||||||
.entry(source_id.to_string())
|
.entry(source_id.to_string())
|
||||||
.or_insert(InputSourceState::Pointer(Arc::new(Mutex::new(
|
.or_insert(InputSourceState::Pointer(PointerInputState::new(
|
||||||
PointerInputState::new(¶meters.pointer_type),
|
¶meters.pointer_type,
|
||||||
))));
|
)));
|
||||||
match action {
|
match action {
|
||||||
PointerAction::Cancel => (),
|
PointerAction::Cancel => (),
|
||||||
PointerAction::Down(action) => {
|
PointerAction::Down(action) => {
|
||||||
|
@ -319,13 +250,17 @@ impl Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerdown-action
|
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerdown-action
|
||||||
fn dispatch_pointerdown_action(&mut self, source_id: &str, action: &PointerDownAction) {
|
pub(crate) fn dispatch_pointerdown_action(
|
||||||
|
&mut self,
|
||||||
|
source_id: &str,
|
||||||
|
action: &PointerDownAction,
|
||||||
|
) {
|
||||||
let session = self.session.as_mut().unwrap();
|
let session = self.session.as_mut().unwrap();
|
||||||
|
|
||||||
let mut pointer_input_state = match session.input_state_table.get(source_id).unwrap() {
|
let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
|
||||||
InputSourceState::Null => unreachable!(),
|
InputSourceState::Null => unreachable!(),
|
||||||
InputSourceState::Key(_) => unreachable!(),
|
InputSourceState::Key(_) => unreachable!(),
|
||||||
InputSourceState::Pointer(pointer_input_state) => pointer_input_state.lock().unwrap(),
|
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
if pointer_input_state.pressed.contains(&action.button) {
|
if pointer_input_state.pressed.contains(&action.button) {
|
||||||
|
@ -365,13 +300,13 @@ impl Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerup-action
|
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerup-action
|
||||||
fn dispatch_pointerup_action(&mut self, source_id: &str, action: &PointerUpAction) {
|
pub(crate) fn dispatch_pointerup_action(&mut self, source_id: &str, action: &PointerUpAction) {
|
||||||
let session = self.session.as_mut().unwrap();
|
let session = self.session.as_mut().unwrap();
|
||||||
|
|
||||||
let mut pointer_input_state = match session.input_state_table.get(source_id).unwrap() {
|
let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
|
||||||
InputSourceState::Null => unreachable!(),
|
InputSourceState::Null => unreachable!(),
|
||||||
InputSourceState::Key(_) => unreachable!(),
|
InputSourceState::Key(_) => unreachable!(),
|
||||||
InputSourceState::Pointer(pointer_input_state) => pointer_input_state.lock().unwrap(),
|
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !pointer_input_state.pressed.contains(&action.button) {
|
if !pointer_input_state.pressed.contains(&action.button) {
|
||||||
|
@ -411,7 +346,7 @@ impl Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointermove-action
|
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointermove-action
|
||||||
fn dispatch_pointermove_action(
|
pub(crate) fn dispatch_pointermove_action(
|
||||||
&mut self,
|
&mut self,
|
||||||
source_id: &str,
|
source_id: &str,
|
||||||
action: &PointerMoveAction,
|
action: &PointerMoveAction,
|
||||||
|
@ -435,7 +370,6 @@ impl Handler {
|
||||||
InputSourceState::Null => unreachable!(),
|
InputSourceState::Null => unreachable!(),
|
||||||
InputSourceState::Key(_) => unreachable!(),
|
InputSourceState::Key(_) => unreachable!(),
|
||||||
InputSourceState::Pointer(pointer_input_state) => {
|
InputSourceState::Pointer(pointer_input_state) => {
|
||||||
let pointer_input_state = pointer_input_state.lock().unwrap();
|
|
||||||
(pointer_input_state.x, pointer_input_state.y)
|
(pointer_input_state.x, pointer_input_state.y)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -488,12 +422,30 @@ impl Handler {
|
||||||
thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL));
|
thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 11
|
||||||
|
self.perform_pointer_move(source_id, duration, start_x, start_y, x, y, tick_start);
|
||||||
|
|
||||||
|
// Step 12
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#dfn-perform-a-pointer-move
|
||||||
|
fn perform_pointer_move(
|
||||||
|
&mut self,
|
||||||
|
source_id: &str,
|
||||||
|
duration: u64,
|
||||||
|
start_x: i64,
|
||||||
|
start_y: i64,
|
||||||
|
target_x: i64,
|
||||||
|
target_y: i64,
|
||||||
|
tick_start: Instant,
|
||||||
|
) {
|
||||||
let pointer_input_state = match self
|
let pointer_input_state = match self
|
||||||
.session
|
.session
|
||||||
.as_ref()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.input_state_table
|
.input_state_table
|
||||||
.get(source_id)
|
.get_mut(source_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
{
|
{
|
||||||
InputSourceState::Null => unreachable!(),
|
InputSourceState::Null => unreachable!(),
|
||||||
|
@ -501,24 +453,57 @@ impl Handler {
|
||||||
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
|
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
let constellation_chan = self.constellation_chan.clone();
|
loop {
|
||||||
let pointer_input_state = pointer_input_state.clone();
|
// Step 1
|
||||||
|
let time_delta = tick_start.elapsed().as_millis();
|
||||||
|
|
||||||
// Step 11
|
// Step 2
|
||||||
thread::spawn(move || {
|
let duration_ratio = if duration > 0 {
|
||||||
perform_pointer_move(
|
time_delta as f64 / duration as f64
|
||||||
constellation_chan,
|
} else {
|
||||||
pointer_input_state,
|
1.0
|
||||||
duration,
|
};
|
||||||
start_x,
|
|
||||||
start_y,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
tick_start,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Step 12
|
// Step 3
|
||||||
Ok(())
|
let last = if 1.0 - duration_ratio < 0.001 {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 4
|
||||||
|
let (x, y) = if last {
|
||||||
|
(target_x, target_y)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
(duration_ratio * (target_x - start_x) as f64) as i64 + start_x,
|
||||||
|
(duration_ratio * (target_y - start_y) as f64) as i64 + start_y,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Steps 5 - 6
|
||||||
|
let current_x = pointer_input_state.x;
|
||||||
|
let current_y = pointer_input_state.y;
|
||||||
|
|
||||||
|
// Step 7
|
||||||
|
if x != current_x || y != current_y {
|
||||||
|
// Step 7.2
|
||||||
|
let cmd_msg = WebDriverCommandMsg::MouseMoveAction(x as f32, y as f32);
|
||||||
|
self.constellation_chan
|
||||||
|
.send(ConstellationMsg::WebDriverCommand(cmd_msg))
|
||||||
|
.unwrap();
|
||||||
|
// Step 7.3
|
||||||
|
pointer_input_state.x = x;
|
||||||
|
pointer_input_state.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 8
|
||||||
|
if last {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 9
|
||||||
|
thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ extern crate serde_json;
|
||||||
mod actions;
|
mod actions;
|
||||||
mod capabilities;
|
mod capabilities;
|
||||||
|
|
||||||
use crate::actions::InputSourceState;
|
use crate::actions::{InputSourceState, PointerInputState};
|
||||||
use base64;
|
use base64;
|
||||||
use capabilities::ServoCapabilities;
|
use capabilities::ServoCapabilities;
|
||||||
use crossbeam_channel::{after, unbounded, Receiver, Sender};
|
use crossbeam_channel::{after, unbounded, Receiver, Sender};
|
||||||
|
@ -49,7 +49,10 @@ use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use webdriver::actions::ActionSequence;
|
use webdriver::actions::{
|
||||||
|
ActionSequence, PointerDownAction, PointerMoveAction, PointerOrigin, PointerType,
|
||||||
|
PointerUpAction,
|
||||||
|
};
|
||||||
use webdriver::capabilities::{Capabilities, CapabilitiesMatching};
|
use webdriver::capabilities::{Capabilities, CapabilitiesMatching};
|
||||||
use webdriver::command::{ActionsParameters, SwitchToWindowParameters};
|
use webdriver::command::{ActionsParameters, SwitchToWindowParameters};
|
||||||
use webdriver::command::{
|
use webdriver::command::{
|
||||||
|
@ -1474,6 +1477,65 @@ impl Handler {
|
||||||
Ok(WebDriverResponse::Void)
|
Ok(WebDriverResponse::Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#element-click
|
||||||
|
fn handle_element_click(&mut self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
|
||||||
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
|
|
||||||
|
// Steps 1 - 7
|
||||||
|
let command = WebDriverScriptCommand::ElementClick(element.to_string(), sender);
|
||||||
|
self.browsing_context_script_command(command)?;
|
||||||
|
|
||||||
|
match receiver.recv().unwrap() {
|
||||||
|
Ok(element_id) => match element_id {
|
||||||
|
Some(element_id) => {
|
||||||
|
let id = Uuid::new_v4().to_string();
|
||||||
|
|
||||||
|
// Step 8.1
|
||||||
|
self.session_mut()?.input_state_table.insert(
|
||||||
|
id.clone(),
|
||||||
|
InputSourceState::Pointer(PointerInputState::new(&PointerType::Mouse)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Steps 8.3 - 8.6
|
||||||
|
let pointer_move_action = PointerMoveAction {
|
||||||
|
duration: None,
|
||||||
|
origin: PointerOrigin::Element(WebElement(element_id)),
|
||||||
|
x: Some(0),
|
||||||
|
y: Some(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Steps 8.7 - 8.8
|
||||||
|
let pointer_down_action = PointerDownAction { button: 1 };
|
||||||
|
|
||||||
|
// Steps 8.9 - 8.10
|
||||||
|
let pointer_up_action = PointerUpAction { button: 1 };
|
||||||
|
|
||||||
|
// Step 8.11
|
||||||
|
if let Err(error) =
|
||||||
|
self.dispatch_pointermove_action(&id, &pointer_move_action, 0)
|
||||||
|
{
|
||||||
|
return Err(WebDriverError::new(error, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steps 8.12
|
||||||
|
self.dispatch_pointerdown_action(&id, &pointer_down_action);
|
||||||
|
|
||||||
|
// Steps 8.13
|
||||||
|
self.dispatch_pointerup_action(&id, &pointer_up_action);
|
||||||
|
|
||||||
|
// Step 8.14
|
||||||
|
self.session_mut()?.input_state_table.remove(&id);
|
||||||
|
|
||||||
|
// Step 13
|
||||||
|
Ok(WebDriverResponse::Void)
|
||||||
|
},
|
||||||
|
// Step 13
|
||||||
|
None => Ok(WebDriverResponse::Void),
|
||||||
|
},
|
||||||
|
Err(error) => Err(WebDriverError::new(error, "")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn take_screenshot(&self, rect: Option<Rect<f32, CSSPixel>>) -> WebDriverResult<String> {
|
fn take_screenshot(&self, rect: Option<Rect<f32, CSSPixel>>) -> WebDriverResult<String> {
|
||||||
let mut img = None;
|
let mut img = None;
|
||||||
|
|
||||||
|
@ -1694,6 +1756,7 @@ impl WebDriverHandler<ServoExtensionRoute> for Handler {
|
||||||
WebDriverCommand::ElementSendKeys(ref element, ref keys) => {
|
WebDriverCommand::ElementSendKeys(ref element, ref keys) => {
|
||||||
self.handle_element_send_keys(element, keys)
|
self.handle_element_send_keys(element, keys)
|
||||||
},
|
},
|
||||||
|
WebDriverCommand::ElementClick(ref element) => self.handle_element_click(element),
|
||||||
WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
|
WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
|
||||||
WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
|
WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
|
||||||
WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),
|
WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),
|
||||||
|
|
|
@ -1,2 +1,7 @@
|
||||||
[bubbling.py]
|
[bubbling.py]
|
||||||
disabled: Unimplemented WebDriver command
|
[test_click_event_bubbles_to_parents]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_spin_event_loop]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
[center_point.py]
|
[center_point.py]
|
||||||
disabled: Unimplemented WebDriver command
|
disabled: Intermittent failures
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
[click.py]
|
[click.py]
|
||||||
disabled: Unimplemented WebDriver command
|
[test_no_browsing_context]
|
||||||
|
expected: ERROR
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[file_upload.py]
|
|
||||||
disabled: Unimplemented WebDriver command
|
|
|
@ -1,2 +1,2 @@
|
||||||
[interactability.py]
|
[interactability.py]
|
||||||
disabled: Unimplemented WebDriver command
|
disabled: Interactability is not checked yet
|
||||||
|
|
|
@ -1,2 +1,16 @@
|
||||||
[navigate.py]
|
[navigate.py]
|
||||||
disabled: Unimplemented WebDriver command
|
[test_multi_line_link]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_link_unload_event]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_link_closes_window]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_link_hash]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_numbers_link]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
[scroll_into_view.py]
|
[scroll_into_view.py]
|
||||||
disabled: Unimplemented WebDriver command
|
disabled: scrollIntoView is not implemented
|
||||||
|
|
|
@ -1,2 +1,16 @@
|
||||||
[select.py]
|
[select.py]
|
||||||
disabled: Unimplemented WebDriver command
|
[test_click_selected_option]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_click_preselected_option]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_out_of_view_dropdown]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_click_deselects_others]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[test_click_option]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[stale.py]
|
|
||||||
disabled: Unimplemented WebDriver command
|
|
|
@ -3,5 +3,4 @@
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[test_element_in_collection]
|
[test_element_in_collection]
|
||||||
expected:
|
expected: FAIL
|
||||||
if os == "mac": FAIL
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue