Implement ElementClick wd command

This commit is contained in:
George Roman 2019-08-28 00:00:23 +03:00
parent b2a7fc9046
commit 32f7254883
16 changed files with 336 additions and 142 deletions

View file

@ -3,14 +3,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::Handler;
use crossbeam_channel::Sender;
use ipc_channel::ipc;
use keyboard_types::webdriver::KeyInputState;
use script_traits::webdriver_msg::WebDriverScriptCommand;
use script_traits::{ConstellationMsg, MouseButton, MouseEventType, WebDriverCommandMsg};
use std::cmp;
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
use webdriver::actions::{ActionSequence, ActionsType, GeneralAction, NullActionItem};
@ -28,7 +26,7 @@ static POINTERMOVE_INTERVAL: u64 = 17;
pub(crate) enum InputSourceState {
Null,
Key(KeyInputState),
Pointer(Arc<Mutex<PointerInputState>>),
Pointer(PointerInputState),
}
// https://w3c.github.io/webdriver/#dfn-pointer-input-source
@ -82,73 +80,6 @@ fn compute_tick_duration(tick_actions: &ActionSequence) -> u64 {
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> {
if MouseButton::Left as u64 == button {
Some(MouseButton::Left)
@ -235,9 +166,9 @@ impl Handler {
.unwrap()
.input_state_table
.entry(source_id.to_string())
.or_insert(InputSourceState::Pointer(Arc::new(Mutex::new(
PointerInputState::new(&parameters.pointer_type),
))));
.or_insert(InputSourceState::Pointer(PointerInputState::new(
&parameters.pointer_type,
)));
match action {
PointerAction::Cancel => (),
PointerAction::Down(action) => {
@ -319,13 +250,17 @@ impl Handler {
}
// 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 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::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) {
@ -365,13 +300,13 @@ impl Handler {
}
// 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 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::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) {
@ -411,7 +346,7 @@ impl Handler {
}
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointermove-action
fn dispatch_pointermove_action(
pub(crate) fn dispatch_pointermove_action(
&mut self,
source_id: &str,
action: &PointerMoveAction,
@ -435,7 +370,6 @@ impl Handler {
InputSourceState::Null => unreachable!(),
InputSourceState::Key(_) => unreachable!(),
InputSourceState::Pointer(pointer_input_state) => {
let pointer_input_state = pointer_input_state.lock().unwrap();
(pointer_input_state.x, pointer_input_state.y)
},
};
@ -488,12 +422,30 @@ impl Handler {
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
.session
.as_ref()
.as_mut()
.unwrap()
.input_state_table
.get(source_id)
.get_mut(source_id)
.unwrap()
{
InputSourceState::Null => unreachable!(),
@ -501,24 +453,57 @@ impl Handler {
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
};
let constellation_chan = self.constellation_chan.clone();
let pointer_input_state = pointer_input_state.clone();
loop {
// Step 1
let time_delta = tick_start.elapsed().as_millis();
// Step 11
thread::spawn(move || {
perform_pointer_move(
constellation_chan,
pointer_input_state,
duration,
start_x,
start_y,
x,
y,
tick_start,
);
});
// Step 2
let duration_ratio = if duration > 0 {
time_delta as f64 / duration as f64
} else {
1.0
};
// Step 12
Ok(())
// 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);
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));
}
}
}

View file

@ -18,7 +18,7 @@ extern crate serde_json;
mod actions;
mod capabilities;
use crate::actions::InputSourceState;
use crate::actions::{InputSourceState, PointerInputState};
use base64;
use capabilities::ServoCapabilities;
use crossbeam_channel::{after, unbounded, Receiver, Sender};
@ -49,7 +49,10 @@ use std::thread;
use std::time::Duration;
use style_traits::CSSPixel;
use uuid::Uuid;
use webdriver::actions::ActionSequence;
use webdriver::actions::{
ActionSequence, PointerDownAction, PointerMoveAction, PointerOrigin, PointerType,
PointerUpAction,
};
use webdriver::capabilities::{Capabilities, CapabilitiesMatching};
use webdriver::command::{ActionsParameters, SwitchToWindowParameters};
use webdriver::command::{
@ -1474,6 +1477,65 @@ impl Handler {
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> {
let mut img = None;
@ -1694,6 +1756,7 @@ impl WebDriverHandler<ServoExtensionRoute> for Handler {
WebDriverCommand::ElementSendKeys(ref element, ref keys) => {
self.handle_element_send_keys(element, keys)
},
WebDriverCommand::ElementClick(ref element) => self.handle_element_click(element),
WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),