diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2019-08-26 22:17:52 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-26 22:17:52 -0400 |
commit | b2a7fc9046b8488525adea93d8869b264280bfc8 (patch) | |
tree | de93128ab307a811d81abea540f5624b29eaaa17 /components/webdriver_server | |
parent | 2d9e170157b9cc137c686baf8896681be056ea85 (diff) | |
parent | f064883e07e7ad27a44d93d03a46a05bb3b6a24a (diff) | |
download | servo-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.rs | 233 | ||||
-rw-r--r-- | components/webdriver_server/lib.rs | 12 |
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( - ¶meters.pointer_type, - ))); + .or_insert(InputSourceState::Pointer(Arc::new(Mutex::new( + PointerInputState::new(¶meters.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(¶meters.actions); - - Ok(WebDriverResponse::Void) + match self.dispatch_actions(¶meters.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(); |