aboutsummaryrefslogtreecommitdiffstats
path: root/components/webdriver_server/actions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/webdriver_server/actions.rs')
-rw-r--r--components/webdriver_server/actions.rs243
1 files changed, 208 insertions, 35 deletions
diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs
index fbede5b5887..b18a6eaaf2e 100644
--- a/components/webdriver_server/actions.rs
+++ b/components/webdriver_server/actions.rs
@@ -13,20 +13,23 @@ use keyboard_types::webdriver::KeyInputState;
use webdriver::actions::{
ActionSequence, ActionsType, GeneralAction, KeyAction, KeyActionItem, KeyDownAction,
KeyUpAction, NullActionItem, PointerAction, PointerActionItem, PointerActionParameters,
- PointerDownAction, PointerMoveAction, PointerOrigin, PointerType, PointerUpAction,
+ PointerDownAction, PointerMoveAction, PointerOrigin, PointerType, PointerUpAction, WheelAction,
+ WheelActionItem, WheelScrollAction,
};
use webdriver::error::ErrorStatus;
use crate::Handler;
-// Interval between pointerMove increments in ms, based on common vsync
+// Interval between wheelScroll and pointerMove increments in ms, based on common vsync
static POINTERMOVE_INTERVAL: u64 = 17;
+static WHEELSCROLL_INTERVAL: u64 = 17;
// https://w3c.github.io/webdriver/#dfn-input-source-state
pub(crate) enum InputSourceState {
Null,
Key(KeyInputState),
Pointer(PointerInputState),
+ Wheel,
}
// https://w3c.github.io/webdriver/#dfn-pointer-input-source
@@ -76,7 +79,15 @@ fn compute_tick_duration(tick_actions: &ActionSequence) -> u64 {
}
},
ActionsType::Key { actions: _ } => (),
- ActionsType::Wheel { .. } => log::error!("not implemented"),
+ ActionsType::Wheel { actions } => {
+ for action in actions.iter() {
+ let action_duration = match action {
+ WheelActionItem::General(GeneralAction::Pause(action)) => action.duration,
+ WheelActionItem::Wheel(WheelAction::Scroll(action)) => action.duration,
+ };
+ duration = cmp::max(duration, action_duration.unwrap_or(0));
+ }
+ },
}
duration
}
@@ -176,9 +187,26 @@ impl Handler {
}
}
},
- ActionsType::Wheel { .. } => {
- log::error!("not yet implemented");
- return Err(ErrorStatus::UnsupportedOperation);
+ ActionsType::Wheel { actions } => {
+ for action in actions.iter() {
+ match action {
+ WheelActionItem::General(_action) => {
+ self.dispatch_general_action(source_id)
+ },
+ WheelActionItem::Wheel(action) => {
+ self.session_mut()
+ .unwrap()
+ .input_state_table
+ .entry(source_id.to_string())
+ .or_insert(InputSourceState::Wheel);
+ match action {
+ WheelAction::Scroll(action) => {
+ self.dispatch_scroll_action(action, tick_duration)?
+ },
+ }
+ },
+ }
+ }
},
}
@@ -191,9 +219,8 @@ impl Handler {
let raw_key = action.value.chars().next().unwrap();
let key_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Null => unreachable!(),
InputSourceState::Key(key_input_state) => key_input_state,
- InputSourceState::Pointer(_) => unreachable!(),
+ _ => unreachable!(),
};
session.input_cancel_list.push(ActionSequence {
@@ -219,9 +246,8 @@ impl Handler {
let raw_key = action.value.chars().next().unwrap();
let key_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Null => unreachable!(),
InputSourceState::Key(key_input_state) => key_input_state,
- InputSourceState::Pointer(_) => unreachable!(),
+ _ => unreachable!(),
};
session.input_cancel_list.push(ActionSequence {
@@ -251,9 +277,8 @@ impl Handler {
let session = self.session.as_mut().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,
+ _ => unreachable!(),
};
if pointer_input_state.pressed.contains(&action.button) {
@@ -280,11 +305,10 @@ impl Handler {
},
});
- let button = (action.button as u16).into();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
session.webview_id,
MouseButtonAction::Down,
- button,
+ action.button.into(),
pointer_input_state.x as f32,
pointer_input_state.y as f32,
);
@@ -298,9 +322,8 @@ impl Handler {
let session = self.session.as_mut().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,
+ _ => unreachable!(),
};
if !pointer_input_state.pressed.contains(&action.button) {
@@ -327,11 +350,10 @@ impl Handler {
},
});
- let button = (action.button as u16).into();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
session.webview_id,
MouseButtonAction::Up,
- button,
+ action.button.into(),
pointer_input_state.x as f32,
pointer_input_state.y as f32,
);
@@ -362,14 +384,12 @@ impl Handler {
.get(source_id)
.unwrap()
{
- InputSourceState::Null => unreachable!(),
- InputSourceState::Key(_) => unreachable!(),
InputSourceState::Pointer(pointer_input_state) => {
(pointer_input_state.x, pointer_input_state.y)
},
+ _ => unreachable!(),
};
- // 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),
@@ -387,18 +407,8 @@ impl Handler {
},
};
- let (sender, receiver) = ipc::channel().unwrap();
- let cmd_msg =
- WebDriverCommandMsg::GetWindowSize(self.session.as_ref().unwrap().webview_id, sender);
- self.constellation_chan
- .send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
- .unwrap();
-
- // Steps 7 - 8
- let viewport_size = receiver.recv().unwrap();
- if x < 0 || x as f32 > viewport_size.width || y < 0 || y as f32 > viewport_size.height {
- return Err(ErrorStatus::MoveTargetOutOfBounds);
- }
+ // Step 5 - 6
+ self.check_viewport_bound(x, y)?;
// Step 9
let duration = match action.duration {
@@ -432,9 +442,8 @@ impl Handler {
) {
let session = self.session.as_mut().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,
+ _ => unreachable!(),
};
loop {
@@ -487,4 +496,168 @@ impl Handler {
thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL));
}
}
+
+ /// <https://w3c.github.io/webdriver/#dfn-dispatch-a-scroll-action>
+ fn dispatch_scroll_action(
+ &mut self,
+ action: &WheelScrollAction,
+ tick_duration: u64,
+ ) -> Result<(), ErrorStatus> {
+ // Note: We have not implemented `extract an action sequence` which will calls
+ // `process a wheel action` that validate many of the variable used here.
+ // Hence, we do all the checking here until those functions is properly
+ // implemented.
+ // <https://w3c.github.io/webdriver/#dfn-process-a-wheel-action>
+
+ let tick_start = Instant::now();
+
+ // Step 1
+ let Some(x_offset) = action.x else {
+ return Err(ErrorStatus::InvalidArgument);
+ };
+
+ // Step 2
+ let Some(y_offset) = action.y else {
+ return Err(ErrorStatus::InvalidArgument);
+ };
+
+ // Step 3 - 4
+ // Get coordinates relative to an origin. Origin must be viewport.
+ let (x, y) = match action.origin {
+ PointerOrigin::Viewport => (x_offset, y_offset),
+ _ => return Err(ErrorStatus::InvalidArgument),
+ };
+
+ // Step 5 - 6
+ self.check_viewport_bound(x, y)?;
+
+ // Step 7 - 8
+ let Some(delta_x) = action.deltaX else {
+ return Err(ErrorStatus::InvalidArgument);
+ };
+
+ let Some(delta_y) = action.deltaY else {
+ return Err(ErrorStatus::InvalidArgument);
+ };
+
+ // Step 9
+ let duration = match action.duration {
+ Some(duration) => duration,
+ None => tick_duration,
+ };
+
+ // Step 10
+ if duration > 0 {
+ thread::sleep(Duration::from_millis(WHEELSCROLL_INTERVAL));
+ }
+
+ // Step 11
+ self.perform_scroll(duration, x, y, delta_x, delta_y, 0, 0, tick_start);
+
+ // Step 12
+ Ok(())
+ }
+
+ /// <https://w3c.github.io/webdriver/#dfn-perform-a-scroll>
+ #[allow(clippy::too_many_arguments)]
+ fn perform_scroll(
+ &mut self,
+ duration: u64,
+ x: i64,
+ y: i64,
+ target_delta_x: i64,
+ target_delta_y: i64,
+ mut curr_delta_x: i64,
+ mut curr_delta_y: i64,
+ tick_start: Instant,
+ ) {
+ let session = self.session.as_mut().unwrap();
+
+ // 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 = 1.0 - duration_ratio < 0.001;
+
+ // Step 4
+ let (delta_x, delta_y) = if last {
+ (target_delta_x - curr_delta_x, target_delta_y - curr_delta_y)
+ } else {
+ (
+ (duration_ratio * target_delta_x as f64) as i64 - curr_delta_x,
+ (duration_ratio * target_delta_y as f64) as i64 - curr_delta_y,
+ )
+ };
+
+ // Step 5
+ if delta_x != 0 || delta_y != 0 {
+ // Perform implementation-specific action dispatch steps
+ let cmd_msg = WebDriverCommandMsg::WheelScrollAction(
+ session.webview_id,
+ x as f32,
+ y as f32,
+ delta_x as f64,
+ delta_y as f64,
+ );
+ self.constellation_chan
+ .send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
+ .unwrap();
+
+ curr_delta_x += delta_x;
+ curr_delta_y += delta_y;
+ }
+
+ // Step 6
+ if last {
+ return;
+ }
+
+ // Step 7
+ // TODO: The two steps should be done in parallel
+ // 7.1. Asynchronously wait for an implementation defined amount of time to pass.
+ thread::sleep(Duration::from_millis(WHEELSCROLL_INTERVAL));
+ // 7.2. Perform a scroll with arguments duration, x, y, target delta x,
+ // target delta y, current delta x, current delta y.
+ self.perform_scroll(
+ duration,
+ x,
+ y,
+ target_delta_x,
+ target_delta_y,
+ curr_delta_x,
+ curr_delta_y,
+ tick_start,
+ );
+ }
+
+ fn check_viewport_bound(&self, x: i64, y: i64) -> Result<(), ErrorStatus> {
+ let (sender, receiver) = ipc::channel().unwrap();
+ let cmd_msg =
+ WebDriverCommandMsg::GetWindowSize(self.session.as_ref().unwrap().webview_id, sender);
+ self.constellation_chan
+ .send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
+ .unwrap();
+
+ match receiver.recv() {
+ Ok(viewport_size) => {
+ if x < 0 ||
+ x as f32 > viewport_size.width ||
+ y < 0 ||
+ y as f32 > viewport_size.height
+ {
+ Err(ErrorStatus::MoveTargetOutOfBounds)
+ } else {
+ Ok(())
+ }
+ },
+ Err(_) => Err(ErrorStatus::UnknownError),
+ }
+ }
}