mirror of
https://github.com/servo/servo.git
synced 2025-09-18 02:48:20 +01:00
webdriver: Support "scroll into view" for commands (#38508)
Implement scroll into view steps for all WebDriver command that requires it (element click, element send keys, element clear, and take element screenshot). Testing: `element_send_keys/scroll_into_view.py`, `element_click/scroll_into_view.py`, `element_clear/clear.py` --------- Signed-off-by: PotatoCP <Kenzie.Raditya.Tirtarahardja@huawei.com>
This commit is contained in:
parent
9e9bd80bba
commit
097a69169a
7 changed files with 73 additions and 25 deletions
|
@ -2413,8 +2413,8 @@ impl ScriptThread {
|
|||
WebDriverScriptCommand::GetElementRect(node_id, reply) => {
|
||||
webdriver_handlers::handle_get_rect(&documents, pipeline_id, node_id, reply, can_gc)
|
||||
},
|
||||
WebDriverScriptCommand::GetBoundingClientRect(node_id, reply) => {
|
||||
webdriver_handlers::handle_get_bounding_client_rect(
|
||||
WebDriverScriptCommand::ScrollAndGetBoundingClientRect(node_id, reply) => {
|
||||
webdriver_handlers::handle_scroll_and_get_bounding_client_rect(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
node_id,
|
||||
|
|
|
@ -39,7 +39,9 @@ use crate::dom::attr::is_boolean_attribute;
|
|||
use crate::dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ElementBinding::{
|
||||
ElementMethods, ScrollIntoViewOptions, ScrollLogicalPosition,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
|
||||
|
@ -52,6 +54,7 @@ use crate::dom::bindings::codegen::Bindings::XMLSerializerBinding::XMLSerializer
|
|||
use crate::dom::bindings::codegen::Bindings::XPathResultBinding::{
|
||||
XPathResultConstants, XPathResultMethods,
|
||||
};
|
||||
use crate::dom::bindings::codegen::UnionTypes::BooleanOrScrollIntoViewOptions;
|
||||
use crate::dom::bindings::conversions::{
|
||||
ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior,
|
||||
get_property, get_property_jsval, jsid_to_string, root_from_object,
|
||||
|
@ -1264,7 +1267,9 @@ pub(crate) fn handle_will_send_keys(
|
|||
|
||||
// Step 7. If file is false or the session's strict file interactability
|
||||
if !is_file_input || strict_file_interactability {
|
||||
// TODO(24059): Step 7.1. Scroll Into View
|
||||
// Step 7.1. Scroll into view the element
|
||||
scroll_into_view(&element, documents, &pipeline, can_gc);
|
||||
|
||||
// TODO: Step 7.2 - 7.5
|
||||
// Wait until element become keyboard-interactable
|
||||
|
||||
|
@ -1280,7 +1285,12 @@ pub(crate) fn handle_will_send_keys(
|
|||
if !element.is_active_element() {
|
||||
// TODO: "Focusing steps" has a different meaning from the focus() method.
|
||||
// The actual focusing steps should be implemented
|
||||
html_element.Focus(&FocusOptions::default(), can_gc);
|
||||
html_element.Focus(
|
||||
&FocusOptions {
|
||||
preventScroll: true,
|
||||
},
|
||||
can_gc,
|
||||
);
|
||||
} else {
|
||||
element_has_focus = element.focus_state();
|
||||
}
|
||||
|
@ -1599,7 +1609,7 @@ pub(crate) fn handle_get_rect(
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_get_bounding_client_rect(
|
||||
pub(crate) fn handle_scroll_and_get_bounding_client_rect(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
|
@ -1609,6 +1619,8 @@ pub(crate) fn handle_get_bounding_client_rect(
|
|||
reply
|
||||
.send(
|
||||
get_known_element(documents, pipeline, element_id).map(|element| {
|
||||
scroll_into_view(&element, documents, &pipeline, can_gc);
|
||||
|
||||
let rect = element.GetBoundingClientRect(can_gc);
|
||||
Rect::new(
|
||||
Point2D::new(rect.X() as f32, rect.Y() as f32),
|
||||
|
@ -1818,7 +1830,12 @@ fn clear_a_resettable_element(element: &Element, can_gc: CanGc) -> Result<(), Er
|
|||
// Step 3. Invoke the focusing steps for the element.
|
||||
// TODO: "Focusing steps" has a different meaning from the focus() method.
|
||||
// The actual focusing steps should be implemented
|
||||
html_element.Focus(&FocusOptions::default(), can_gc);
|
||||
html_element.Focus(
|
||||
&FocusOptions {
|
||||
preventScroll: true,
|
||||
},
|
||||
can_gc,
|
||||
);
|
||||
|
||||
// Step 4. Run clear algorithm for element.
|
||||
if let Some(input_element) = element.downcast::<HTMLInputElement>() {
|
||||
|
@ -1857,7 +1874,9 @@ pub(crate) fn handle_element_clear(
|
|||
return Err(ErrorStatus::InvalidElementState);
|
||||
}
|
||||
|
||||
// TODO: Step 5. Scroll Into View
|
||||
// Step 5. Scroll Into View
|
||||
scroll_into_view(&element, documents, &pipeline, can_gc);
|
||||
|
||||
// TODO: Step 6 - 10
|
||||
// Wait until element become interactable and check.
|
||||
|
||||
|
@ -1926,7 +1945,7 @@ pub(crate) fn handle_element_click(
|
|||
};
|
||||
|
||||
// Step 5
|
||||
// TODO: scroll into view is not implemented in Servo
|
||||
scroll_into_view(&container, documents, &pipeline, can_gc);
|
||||
|
||||
// Step 6. If element's container is still not in view
|
||||
// return error with error code element not interactable.
|
||||
|
@ -1969,7 +1988,12 @@ pub(crate) fn handle_element_click(
|
|||
Some(html_element) => {
|
||||
// TODO: "Focusing steps" has a different meaning from the focus() method.
|
||||
// The actual focusing steps should be implemented
|
||||
html_element.Focus(&FocusOptions::default(), can_gc);
|
||||
html_element.Focus(
|
||||
&FocusOptions {
|
||||
preventScroll: true,
|
||||
},
|
||||
can_gc,
|
||||
);
|
||||
},
|
||||
None => return Err(ErrorStatus::UnknownError),
|
||||
}
|
||||
|
@ -2124,3 +2148,35 @@ pub(crate) fn handle_remove_load_status_sender(
|
|||
window.set_webdriver_load_status_sender(None);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/webdriver/#dfn-scrolls-into-view>
|
||||
fn scroll_into_view(
|
||||
element: &Element,
|
||||
documents: &DocumentCollection,
|
||||
pipeline: &PipelineId,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
// Check if element is already in view
|
||||
let paint_tree = get_element_pointer_interactable_paint_tree(
|
||||
element,
|
||||
&documents
|
||||
.find_document(*pipeline)
|
||||
.expect("Document existence guaranteed by `get_known_element`"),
|
||||
can_gc,
|
||||
);
|
||||
if is_element_in_view(element, &paint_tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1. Let options be the following ScrollIntoViewOptions:
|
||||
// - Logical scroll position "block": end
|
||||
// - Logical scroll position "inline": nearest
|
||||
let options = BooleanOrScrollIntoViewOptions::ScrollIntoViewOptions(ScrollIntoViewOptions {
|
||||
parent: Default::default(),
|
||||
block: ScrollLogicalPosition::End,
|
||||
inline: ScrollLogicalPosition::Nearest,
|
||||
container: Default::default(),
|
||||
});
|
||||
// Step 2. Run scrollIntoView
|
||||
element.ScrollIntoView(options);
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ pub enum WebDriverScriptCommand {
|
|||
GetElementTagName(String, IpcSender<Result<String, ErrorStatus>>),
|
||||
GetElementText(String, IpcSender<Result<String, ErrorStatus>>),
|
||||
GetElementInViewCenterPoint(String, IpcSender<Result<Option<(i64, i64)>, ErrorStatus>>),
|
||||
GetBoundingClientRect(String, IpcSender<Result<UntypedRect<f32>, ErrorStatus>>),
|
||||
ScrollAndGetBoundingClientRect(String, IpcSender<Result<UntypedRect<f32>, ErrorStatus>>),
|
||||
GetBrowsingContextId(
|
||||
WebDriverFrameId,
|
||||
IpcSender<Result<BrowsingContextId, ErrorStatus>>,
|
||||
|
|
|
@ -2400,8 +2400,9 @@ impl Handler {
|
|||
self.handle_any_user_prompts(webview_id)?;
|
||||
|
||||
// Step 3 - 4
|
||||
let cmd = WebDriverScriptCommand::GetBoundingClientRect(element.to_string(), sender);
|
||||
self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
|
||||
let cmd =
|
||||
WebDriverScriptCommand::ScrollAndGetBoundingClientRect(element.to_string(), sender);
|
||||
self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
|
||||
|
||||
match wait_for_ipc_response(receiver)? {
|
||||
Ok(rect) => {
|
||||
|
|
3
tests/wpt/meta/webdriver/tests/classic/element_click/interactability.py.ini
vendored
Normal file
3
tests/wpt/meta/webdriver/tests/classic/element_click/interactability.py.ini
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[interactability.py]
|
||||
[test_element_not_visible_overflow_hidden]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[scroll_into_view.py]
|
||||
[test_scroll_into_view]
|
||||
expected: FAIL
|
|
@ -1,12 +1,3 @@
|
|||
[scroll_into_view.py]
|
||||
[test_contenteditable_element_outside_of_scrollable_viewport]
|
||||
expected: FAIL
|
||||
|
||||
[test_element_already_in_viewport[{block: 'start'}\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_element_already_in_viewport[{block: 'end'}\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_element_already_in_viewport[{block: 'nearest'}\]]
|
||||
expected: FAIL
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue