From 165ede59cdabaf2a2aa34cb06de519bc2947b73a Mon Sep 17 00:00:00 2001 From: Kenzie Raditya Tirtarahardja Date: Fri, 25 Jul 2025 15:24:48 +0800 Subject: [PATCH] script(webdriver): Send keys set caret at the end of content of text input (#38250) Remote end step of [element send keys](https://w3c.github.io/webdriver/#element-send-keys) > Step 8.2. Set the text insertion caret using [set selection range](https://w3c.github.io/webdriver/#dfn-set-selection-range) using current text length for both the start and end parameters. Also check if the element is already an active element before focusing (Step 7.7). Testing: `./tests/wpt/tests/webdriver/tests/classic/element_send_keys/form_controls.py` --------- Signed-off-by: PotatoCP Signed-off-by: Kenzie Raditya Tirtarahardja Co-authored-by: Euclid Ye --- components/script/dom/element.rs | 13 ++++- components/script/webdriver_handlers.rs | 51 +++++++++++++------ .../element_send_keys/form_controls.py.ini | 6 --- 3 files changed, 47 insertions(+), 23 deletions(-) delete mode 100644 tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 5eae2c06ccd..e060ada1d6a 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -1733,8 +1733,17 @@ impl Element { /// pub(crate) fn is_document_element(&self) -> bool { - if let Some(ref document_element) = self.owner_document().GetDocumentElement() { - **document_element == *self + if let Some(document_element) = self.owner_document().GetDocumentElement() { + *document_element == *self + } else { + false + } + } + + /// + pub(crate) fn is_active_element(&self) -> bool { + if let Some(active_element) = self.owner_document().GetActiveElement() { + *active_element == *self } else { false } diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 46fb6231bb9..cd319977bd0 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -1170,14 +1170,15 @@ pub(crate) fn handle_will_send_keys( // Set 5. Let element be the result of trying to get a known element. get_known_element(documents, pipeline, element_id).and_then(|element| { let input_element = element.downcast::(); + let mut element_has_focus = false; // Step 6: Let file be true if element is input element // in the file upload state, or false otherwise - let file_input = input_element - .filter(|&input_element| input_element.input_type() == InputType::File); + let is_file_input = + input_element.is_some_and(|e| e.input_type() == InputType::File); // Step 7. If file is false or the session's strict file interactability - if file_input.is_none() || strict_file_interactability { + if !is_file_input || strict_file_interactability { // TODO(24059): Step 7.1. Scroll Into View // TODO: Step 7.2 - 7.5 // Wait until element become keyboard-interactable @@ -1188,22 +1189,26 @@ pub(crate) fn handle_will_send_keys( return Err(ErrorStatus::ElementNotInteractable); } - match element.downcast::() { - Some(element) => { - // Need a way to find if this actually succeeded - element.Focus(can_gc); - }, - None => return Err(ErrorStatus::UnknownError), + // Step 7.7. If element is not the active element + // run the focusing steps for the element. + if let Some(html_element) = element.downcast::() { + if !element.is_active_element() { + html_element.Focus(can_gc); + } else { + element_has_focus = element.focus_state(); + } + } else { + return Err(ErrorStatus::UnknownError); } } - // Step 8 (file input) - if let Some(file_input) = file_input { - return handle_send_keys_file(file_input, &text, can_gc); - } - - // Step 8 (non-typeable form control) if let Some(input_element) = input_element { + // Step 8 (Handle file upload) + if is_file_input { + return handle_send_keys_file(input_element, &text, can_gc); + } + + // Step 8 (Handle non-typeable form control) if input_element.is_nontypeable() { return handle_send_keys_non_typeable(input_element, &text, can_gc); } @@ -1211,6 +1216,22 @@ pub(crate) fn handle_will_send_keys( // TODO: Check content editable + // Step 8 (Other type of elements) + // Step 8.1. If element does not currently have focus, + // let current text length be the length of element's API value. + // Step 8.2. Set the text insertion caret using set selection range + // using current text length for both the start and end parameters. + if !element_has_focus { + if let Some(input_element) = input_element { + let length = input_element.Value().len() as u32; + let _ = input_element.SetSelectionRange(length, length, None); + } else if let Some(textarea_element) = element.downcast::() + { + let length = textarea_element.Value().len() as u32; + let _ = textarea_element.SetSelectionRange(length, length, None); + } + } + Ok(true) }), ) diff --git a/tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini b/tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini deleted file mode 100644 index 5d4a3bd4de5..00000000000 --- a/tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini +++ /dev/null @@ -1,6 +0,0 @@ -[form_controls.py] - [test_input_append] - expected: FAIL - - [test_textarea_append] - expected: FAIL