diff --git a/components/shared/embedder/webdriver.rs b/components/shared/embedder/webdriver.rs index 9144dfc584d..bf23d360054 100644 --- a/components/shared/embedder/webdriver.rs +++ b/components/shared/embedder/webdriver.rs @@ -86,8 +86,8 @@ pub enum WebDriverCommandMsg { /// Act as if the mouse wheel is scrolled in the browsing context given the given ID. WheelScrollAction( WebViewId, - f32, - f32, + f64, + f64, f64, f64, // None if it's not the last `perform_wheel_scroll` since we only diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs index 41aa868585c..1584d3cba8f 100644 --- a/components/webdriver_server/actions.rs +++ b/components/webdriver_server/actions.rs @@ -286,7 +286,7 @@ impl Handler { self.dispatch_general_action(input_id); }, ActionItem::Wheel(WheelActionItem::Wheel(WheelAction::Scroll(scroll_action))) => { - self.dispatch_scroll_action(scroll_action, tick_duration)?; + self.dispatch_scroll_action(input_id, scroll_action, tick_duration)?; }, _ => {}, } @@ -437,6 +437,46 @@ impl Handler { let _ = self.send_message_to_embedder(cmd_msg); } + /// + fn get_origin_relative_coordinates( + &self, + origin: &PointerOrigin, + x_offset: f64, + y_offset: f64, + source_id: &str, + ) -> Result<(f64, f64), ErrorStatus> { + match origin { + PointerOrigin::Viewport => Ok((x_offset, y_offset)), + PointerOrigin::Pointer => { + // Step 1. Let start x be equal to the x property of source. + // Step 2. Let start y be equal to the y property of source. + let (start_x, start_y) = match self + .session() + .unwrap() + .input_state_table + .borrow() + .get(source_id) + .unwrap() + { + InputSourceState::Pointer(pointer_input_state) => { + (pointer_input_state.x, pointer_input_state.y) + }, + _ => unreachable!(), + }; + // Step 3. Let x equal start x + x offset and y equal start y + y offset. + Ok((start_x + x_offset, start_y + y_offset)) + }, + PointerOrigin::Element(web_element) => { + // Steps 1 - 2: Check "no such element", covered in script thread handler. + + // Step 3. Let x element and y element be the result of calculating the in-view center point of element. + let (x_element, y_element) = self.get_element_in_view_center_point(web_element)?; + // Step 4. Let x equal x element + x offset, and y equal y element + y offset. + Ok((x_element as f64 + x_offset, y_element as f64 + y_offset)) + }, + } + } + /// pub(crate) fn dispatch_pointermove_action( &self, @@ -446,16 +486,40 @@ impl Handler { ) -> Result<(), ErrorStatus> { let tick_start = Instant::now(); - // Steps 1 - 2 + // Step 1. Let x offset be equal to the x property of action object. let x_offset = action.x; + // Step 2. Let y offset be equal to the y property of action object. let y_offset = action.y; - // Steps 3 - 4 + // Step 3. Let origin be equal to the origin property of action object. + let origin = &action.origin; + + // Step 4. Let (x, y) be the result of trying to get coordinates relative to an origin + // with source, x offset, y offset, origin, browsing context, and actions options. + + let (x, y) = self.get_origin_relative_coordinates(origin, x_offset, y_offset, source_id)?; + + // Step 5 - 6 + self.check_viewport_bound(x, y)?; + + // Step 7. Let duration be equal to action object's duration property + // if it is not undefined, or tick duration otherwise. + let duration = match action.duration { + Some(duration) => duration, + None => tick_duration, + }; + + // Step 8. If duration is greater than 0 and inside any implementation-defined bounds, + // asynchronously wait for an implementation defined amount of time to pass. + if duration > 0 { + thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL)); + } + let (start_x, start_y) = match self .session() .unwrap() .input_state_table - .borrow_mut() + .borrow() .get(source_id) .unwrap() { @@ -465,29 +529,6 @@ impl Handler { _ => unreachable!(), }; - 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 web_element) => { - let point = self.get_element_origin_relative_coordinates(web_element)?; - (point.0 as f64, point.1 as f64) - }, - }; - - // Step 5 - 6 - self.check_viewport_bound(x, y)?; - - // Step 7 - let duration = match action.duration { - Some(duration) => duration, - None => tick_duration, - }; - - // Step 8 - if duration > 0 { - thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL)); - } - // Step 9 - 18 self.perform_pointer_move(source_id, duration, start_x, start_y, x, y, tick_start); @@ -578,6 +619,7 @@ impl Handler { /// fn dispatch_scroll_action( &self, + source_id: &str, action: &WheelScrollAction, tick_duration: u64, ) -> Result<(), ErrorStatus> { @@ -589,51 +631,68 @@ impl Handler { let tick_start = Instant::now(); - // Step 1 + // Step 1. Let x offset be equal to the x property of action object. let Some(x_offset) = action.x else { return Err(ErrorStatus::InvalidArgument); }; - // Step 2 + // Step 2. Let y offset be equal to the y property of action object. let Some(y_offset) = action.y else { return Err(ErrorStatus::InvalidArgument); }; - // Step 3 - 4 - // Get coordinates relative to an origin. - let (x, y) = match action.origin { - PointerOrigin::Viewport => (x_offset, y_offset), - PointerOrigin::Pointer => return Err(ErrorStatus::InvalidArgument), - PointerOrigin::Element(ref web_element) => { - self.get_element_origin_relative_coordinates(web_element)? - }, - }; + // Step 3. Let origin be equal to the origin property of action object. + let origin = &action.origin; + + // Pointer origin isn't currently supported for wheel input source + // See: https://github.com/w3c/webdriver/issues/1758 + + if origin == &PointerOrigin::Pointer { + return Err(ErrorStatus::InvalidArgument); + } + + // Step 4. Let (x, y) be the result of trying to get coordinates relative to an origin + // with source, x offset, y offset, origin, browsing context, and actions options. + let (x, y) = + self.get_origin_relative_coordinates(origin, x_offset as _, y_offset as _, source_id)?; // Step 5 - 6 - self.check_viewport_bound(x as _, y as _)?; + self.check_viewport_bound(x, y)?; - // Step 7 - 8 + // Step 7. Let delta x be equal to the deltaX property of action object. let Some(delta_x) = action.deltaX else { return Err(ErrorStatus::InvalidArgument); }; + // Step 8. Let delta y be equal to the deltaY property of action object. let Some(delta_y) = action.deltaY else { return Err(ErrorStatus::InvalidArgument); }; - // Step 9 + // Step 9. Let duration be equal to action object's duration property + // if it is not undefined, or tick duration otherwise. let duration = match action.duration { Some(duration) => duration, None => tick_duration, }; - // Step 10 + // Step 10. If duration is greater than 0 and inside any implementation-defined bounds, + // asynchronously wait for an implementation defined amount of time to pass. 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 11. Perform a scroll with arguments global key state, duration, x, y, delta x, delta y, 0, 0. + self.perform_scroll( + duration, + x, + y, + delta_x as _, + delta_y as _, + 0.0, + 0.0, + tick_start, + ); // Step 12 Ok(()) @@ -644,27 +703,31 @@ impl Handler { fn perform_scroll( &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, + x: f64, + y: f64, + target_delta_x: f64, + target_delta_y: f64, + mut curr_delta_x: f64, + mut curr_delta_y: f64, tick_start: Instant, ) { let session = self.session().unwrap(); - // Step 1 + // Step 1. Let time delta be the time since the beginning of the current tick, + // measured in milliseconds on a monotonic clock. let time_delta = tick_start.elapsed().as_millis(); - // Step 2 + // Step 2. Let duration ratio be the ratio of time delta and duration, + // if duration is greater than 0, or 1 otherwise. let duration_ratio = if duration > 0 { time_delta as f64 / duration as f64 } else { 1.0 }; - // Step 3 + // Step 3. If duration ratio is 1, or close enough to 1 that + // the implementation will not further subdivide the move action, + // let last be true. Otherwise let last be false. let last = 1.0 - duration_ratio < 0.001; // Step 4 @@ -672,15 +735,15 @@ impl Handler { (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, + duration_ratio * target_delta_x - curr_delta_x, + duration_ratio * target_delta_y - curr_delta_y, ) }; // Step 5 // Actually "last" should not be checked here based on spec. // However, we need to send the webdriver id at the final perform. - if delta_x != 0 || delta_y != 0 || last { + if delta_x != 0.0 || delta_y != 0.0 || last { // Perform implementation-specific action dispatch steps let msg_id = if last { self.increment_num_pending_actions(); @@ -690,10 +753,10 @@ impl Handler { }; let cmd_msg = WebDriverCommandMsg::WheelScrollAction( session.webview_id, - x as f32, - y as f32, - delta_x as f64, - delta_y as f64, + x, + y, + delta_x, + delta_y, msg_id, ); let _ = self.send_message_to_embedder(cmd_msg); @@ -743,7 +806,8 @@ impl Handler { } } - fn get_element_origin_relative_coordinates( + /// + fn get_element_in_view_center_point( &self, web_element: &WebElement, ) -> Result<(i64, i64), ErrorStatus> { diff --git a/ports/servoshell/desktop/app.rs b/ports/servoshell/desktop/app.rs index a952fded83f..9b454c9d92f 100644 --- a/ports/servoshell/desktop/app.rs +++ b/ports/servoshell/desktop/app.rs @@ -560,7 +560,7 @@ impl App { ScrollLocation::Delta(Vector2D::new(dx as f32, dy as f32)); webview.notify_input_event( - InputEvent::Wheel(WheelEvent::new(delta, point)) + InputEvent::Wheel(WheelEvent::new(delta, point.to_f32())) .with_webdriver_message_id(webdriver_message_id), ); diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/key_shortcuts.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/key_shortcuts.py.ini deleted file mode 100644 index 43e9ac6cf32..00000000000 --- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/key_shortcuts.py.ini +++ /dev/null @@ -1,3 +0,0 @@ -[key_shortcuts.py] - [test_mod_a_mod_c_right_mod_v_pastes_text] - expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini index 40da9c238f3..d4e6444105b 100644 --- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini @@ -29,8 +29,5 @@ [test_move_to_origin_position_within_frame[element\]] expected: FAIL - [test_params_actions_origin_outside_viewport[element\]] - expected: FAIL - [test_move_to_position_in_viewport[default value\]] expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_origin.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_origin.py.ini deleted file mode 100644 index caf36b495c3..00000000000 --- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_origin.py.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pointer_origin.py] - [test_element_center_point_with_offset] - expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini index f3c58f364cb..6fe5287bdfd 100644 --- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini @@ -19,6 +19,3 @@ [test_pen_pointer_properties] expected: FAIL - - [test_params_actions_origin_outside_viewport[element\]] - expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini index 06740d20ebb..36ee85ecbae 100644 --- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini @@ -28,6 +28,3 @@ [test_move_to_fractional_position] expected: FAIL - - [test_params_actions_origin_outside_viewport[element\]] - expected: FAIL