aboutsummaryrefslogtreecommitdiffstats
path: root/components/webdriver_server
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2019-08-26 22:17:52 -0400
committerGitHub <noreply@github.com>2019-08-26 22:17:52 -0400
commitb2a7fc9046b8488525adea93d8869b264280bfc8 (patch)
treede93128ab307a811d81abea540f5624b29eaaa17 /components/webdriver_server
parent2d9e170157b9cc137c686baf8896681be056ea85 (diff)
parentf064883e07e7ad27a44d93d03a46a05bb3b6a24a (diff)
downloadservo-b2a7fc9046b8488525adea93d8869b264280bfc8.tar.gz
servo-b2a7fc9046b8488525adea93d8869b264280bfc8.zip
Auto merge of #23996 - georgeroman:implement_pointer_move_wd_action, r=jdm
Implement pointerMove webdriver action <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23996) <!-- Reviewable:end -->
Diffstat (limited to 'components/webdriver_server')
-rw-r--r--components/webdriver_server/actions.rs233
-rw-r--r--components/webdriver_server/lib.rs12
2 files changed, 226 insertions, 19 deletions
diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs
index 0182a16bac3..28255e44cd9 100644
--- a/components/webdriver_server/actions.rs
+++ b/components/webdriver_server/actions.rs
@@ -3,30 +3,40 @@
* 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};
use webdriver::actions::{KeyAction, KeyActionItem, KeyDownAction, KeyUpAction};
use webdriver::actions::{
PointerAction, PointerActionItem, PointerActionParameters, PointerDownAction,
};
-use webdriver::actions::{PointerType, PointerUpAction};
+use webdriver::actions::{PointerMoveAction, PointerOrigin, PointerType, PointerUpAction};
+use webdriver::error::ErrorStatus;
+
+// Interval between pointerMove increments in ms, based on common vsync
+static POINTERMOVE_INTERVAL: u64 = 17;
// https://w3c.github.io/webdriver/#dfn-input-source-state
pub(crate) enum InputSourceState {
Null,
Key(KeyInputState),
- Pointer(PointerInputState),
+ Pointer(Arc<Mutex<PointerInputState>>),
}
// https://w3c.github.io/webdriver/#dfn-pointer-input-source
pub(crate) struct PointerInputState {
subtype: PointerType,
pressed: HashSet<u64>,
- x: u64,
- y: u64,
+ x: i64,
+ y: i64,
}
impl PointerInputState {
@@ -72,6 +82,73 @@ 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)
@@ -86,11 +163,15 @@ fn u64_to_mouse_button(button: u64) -> Option<MouseButton> {
impl Handler {
// https://w3c.github.io/webdriver/#dfn-dispatch-actions
- pub(crate) fn dispatch_actions(&mut self, actions_by_tick: &[ActionSequence]) {
+ pub(crate) fn dispatch_actions(
+ &mut self,
+ actions_by_tick: &[ActionSequence],
+ ) -> Result<(), ErrorStatus> {
for tick_actions in actions_by_tick.iter() {
let tick_duration = compute_tick_duration(&tick_actions);
- self.dispatch_tick_actions(&tick_actions, tick_duration);
+ self.dispatch_tick_actions(&tick_actions, tick_duration)?;
}
+ Ok(())
}
fn dispatch_general_action(&mut self, source_id: &str) {
@@ -104,7 +185,11 @@ impl Handler {
}
// https://w3c.github.io/webdriver/#dfn-dispatch-tick-actions
- fn dispatch_tick_actions(&mut self, tick_actions: &ActionSequence, _tick_duration: u64) {
+ fn dispatch_tick_actions(
+ &mut self,
+ tick_actions: &ActionSequence,
+ tick_duration: u64,
+ ) -> Result<(), ErrorStatus> {
let source_id = &tick_actions.id;
match &tick_actions.actions {
ActionsType::Null { actions } => {
@@ -150,15 +235,19 @@ impl Handler {
.unwrap()
.input_state_table
.entry(source_id.to_string())
- .or_insert(InputSourceState::Pointer(PointerInputState::new(
- &parameters.pointer_type,
- )));
+ .or_insert(InputSourceState::Pointer(Arc::new(Mutex::new(
+ PointerInputState::new(&parameters.pointer_type),
+ ))));
match action {
PointerAction::Cancel => (),
PointerAction::Down(action) => {
self.dispatch_pointerdown_action(&source_id, &action)
},
- PointerAction::Move(_action) => (),
+ PointerAction::Move(action) => self.dispatch_pointermove_action(
+ &source_id,
+ &action,
+ tick_duration,
+ )?,
PointerAction::Up(action) => {
self.dispatch_pointerup_action(&source_id, &action)
},
@@ -168,6 +257,8 @@ impl Handler {
}
},
}
+
+ Ok(())
}
// https://w3c.github.io/webdriver/#dfn-dispatch-a-keydown-action
@@ -231,10 +322,10 @@ impl Handler {
fn dispatch_pointerdown_action(&mut self, source_id: &str, action: &PointerDownAction) {
let session = self.session.as_mut().unwrap();
- let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
+ let mut pointer_input_state = match session.input_state_table.get(source_id).unwrap() {
InputSourceState::Null => unreachable!(),
InputSourceState::Key(_) => unreachable!(),
- InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ InputSourceState::Pointer(pointer_input_state) => pointer_input_state.lock().unwrap(),
};
if pointer_input_state.pressed.contains(&action.button) {
@@ -277,10 +368,10 @@ impl Handler {
fn dispatch_pointerup_action(&mut self, source_id: &str, action: &PointerUpAction) {
let session = self.session.as_mut().unwrap();
- let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
+ let mut pointer_input_state = match session.input_state_table.get(source_id).unwrap() {
InputSourceState::Null => unreachable!(),
InputSourceState::Key(_) => unreachable!(),
- InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ InputSourceState::Pointer(pointer_input_state) => pointer_input_state.lock().unwrap(),
};
if !pointer_input_state.pressed.contains(&action.button) {
@@ -318,4 +409,116 @@ impl Handler {
.unwrap();
}
}
+
+ // https://w3c.github.io/webdriver/#dfn-dispatch-a-pointermove-action
+ fn dispatch_pointermove_action(
+ &mut self,
+ source_id: &str,
+ action: &PointerMoveAction,
+ tick_duration: u64,
+ ) -> Result<(), ErrorStatus> {
+ let tick_start = Instant::now();
+
+ // Steps 1 - 2
+ let x_offset = action.x.unwrap_or(0);
+ let y_offset = action.y.unwrap_or(0);
+
+ // Steps 3 - 4
+ let (start_x, start_y) = match self
+ .session
+ .as_ref()
+ .unwrap()
+ .input_state_table
+ .get(source_id)
+ .unwrap()
+ {
+ 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)
+ },
+ };
+
+ // Step 5 - 6
+ let (x, y) = match action.origin {
+ PointerOrigin::Viewport => (x_offset, y_offset),
+ PointerOrigin::Pointer => (start_x + x_offset, start_y + y_offset),
+ PointerOrigin::Element(ref x) => {
+ let (sender, receiver) = ipc::channel().unwrap();
+ self.top_level_script_command(WebDriverScriptCommand::GetElementInViewCenterPoint(
+ x.to_string(),
+ sender,
+ ))
+ .unwrap();
+
+ match receiver.recv().unwrap() {
+ Ok(point) => match point {
+ Some(point) => point,
+ None => return Err(ErrorStatus::UnknownError),
+ },
+ Err(_) => return Err(ErrorStatus::UnknownError),
+ }
+ },
+ };
+
+ let (sender, receiver) = ipc::channel().unwrap();
+ let cmd_msg = WebDriverCommandMsg::GetWindowSize(
+ self.session.as_ref().unwrap().top_level_browsing_context_id,
+ sender,
+ );
+ self.constellation_chan
+ .send(ConstellationMsg::WebDriverCommand(cmd_msg))
+ .unwrap();
+
+ // Steps 7 - 8
+ let viewport = receiver.recv().unwrap().initial_viewport;
+ if x < 0 || x as f32 > viewport.width || y < 0 || y as f32 > viewport.height {
+ return Err(ErrorStatus::MoveTargetOutOfBounds);
+ }
+
+ // Step 9
+ let duration = match action.duration {
+ Some(duration) => duration,
+ None => tick_duration,
+ };
+
+ // Step 10
+ if duration > 0 {
+ thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL));
+ }
+
+ let pointer_input_state = match self
+ .session
+ .as_ref()
+ .unwrap()
+ .input_state_table
+ .get(source_id)
+ .unwrap()
+ {
+ InputSourceState::Null => unreachable!(),
+ InputSourceState::Key(_) => unreachable!(),
+ InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ };
+
+ let constellation_chan = self.constellation_chan.clone();
+ let pointer_input_state = pointer_input_state.clone();
+
+ // Step 11
+ thread::spawn(move || {
+ perform_pointer_move(
+ constellation_chan,
+ pointer_input_state,
+ duration,
+ start_x,
+ start_y,
+ x,
+ y,
+ tick_start,
+ );
+ });
+
+ // Step 12
+ Ok(())
+ }
}
diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs
index 954f2fd0a52..431ac5eee9e 100644
--- a/components/webdriver_server/lib.rs
+++ b/components/webdriver_server/lib.rs
@@ -1345,9 +1345,10 @@ impl Handler {
&mut self,
parameters: &ActionsParameters,
) -> WebDriverResult<WebDriverResponse> {
- self.dispatch_actions(&parameters.actions);
-
- Ok(WebDriverResponse::Void)
+ match self.dispatch_actions(&parameters.actions) {
+ Ok(_) => Ok(WebDriverResponse::Void),
+ Err(error) => Err(WebDriverError::new(error, "")),
+ }
}
fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
@@ -1356,7 +1357,10 @@ impl Handler {
session.input_cancel_list.reverse();
mem::replace(&mut session.input_cancel_list, Vec::new())
};
- self.dispatch_actions(&input_cancel_list);
+
+ if let Err(error) = self.dispatch_actions(&input_cancel_list) {
+ return Err(WebDriverError::new(error, ""));
+ }
let session = self.session_mut()?;
session.input_state_table = HashMap::new();