From 0970a99b7c7ae7bff41b779f2c497c00a51dcc06 Mon Sep 17 00:00:00 2001 From: Kenzie Raditya Tirtarahardja Date: Wed, 23 Jul 2025 17:11:44 +0800 Subject: [PATCH] webdriver: Implement element send keys command for non-typeable form control (#38189) Implement Step 8 of remote end steps of [Element Send keys](https://w3c.github.io/webdriver/#element-send-keys), specifically for non-typeable form control. --------- Signed-off-by: PotatoCP --- components/script/dom/htmlinputelement.rs | 20 ++++++++- components/script/webdriver_handlers.rs | 50 +++++++++++++++++++++-- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 377828d2f07..9f10c21f419 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -520,6 +520,24 @@ impl HTMLInputElement { self.input_type.get() } + #[inline] + /// + pub(crate) fn is_nontypeable(&self) -> bool { + matches!( + self.input_type(), + InputType::Button | + InputType::Checkbox | + InputType::Color | + InputType::File | + InputType::Hidden | + InputType::Image | + InputType::Radio | + InputType::Range | + InputType::Reset | + InputType::Submit + ) + } + #[inline] pub(crate) fn is_submit_button(&self) -> bool { let input_type = self.input_type.get(); @@ -2154,7 +2172,7 @@ impl HTMLInputElement { } // https://html.spec.whatwg.org/multipage/#concept-fe-mutable - fn is_mutable(&self) -> bool { + pub(crate) fn is_mutable(&self) -> bool { // https://html.spec.whatwg.org/multipage/#the-input-element:concept-fe-mutable // https://html.spec.whatwg.org/multipage/#the-readonly-attribute:concept-fe-mutable !(self.upcast::().disabled_state() || self.ReadOnly()) diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index b00ad60cb8a..b30ec0b5330 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -71,6 +71,7 @@ use crate::dom::htmlselectelement::HTMLSelectElement; use crate::dom::node::{Node, NodeTraits, ShadowIncluding}; use crate::dom::nodelist::NodeList; use crate::dom::types::ShadowRoot; +use crate::dom::validitystate::ValidationFlags; use crate::dom::window::Window; use crate::dom::xmlserializer::XMLSerializer; use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; @@ -1099,6 +1100,40 @@ fn handle_send_keys_file( } // Step 8. Return success with data null. + Ok(false) +} + +/// We have verify previously that input element is not textual. +fn handle_send_keys_non_typeable( + input_element: &HTMLInputElement, + text: &str, + can_gc: CanGc, +) -> Result { + // Step 1. If element does not have an own property named value, + // Return ErrorStatus::ElementNotInteractable. + // Currently, we only support HTMLInputElement for non-typeable + // form controls. Hence, it should always have value property. + + // Step 2. If element is not mutable, return ErrorStatus::ElementNotInteractable. + if !input_element.is_mutable() { + return Err(ErrorStatus::ElementNotInteractable); + } + + // Step 3. Set a property value to text on element. + if input_element.SetValue(text.into(), can_gc).is_err() { + return Err(ErrorStatus::UnknownError); + } + + // Step 4. If element is suffering from bad input, return ErrorStatus::InvalidArgument. + if input_element + .Validity() + .invalid_flags() + .contains(ValidationFlags::BAD_INPUT) + { + return Err(ErrorStatus::InvalidArgument); + } + + // Step 5. Return success with data null. // This is done in `webdriver_server:lib.rs` Ok(false) } @@ -1121,10 +1156,11 @@ pub(crate) fn handle_will_send_keys( .send( // Set 5. Let element be the result of trying to get a known element. get_known_element(documents, pipeline, element_id).and_then(|element| { - // Step 6. Let file be true if element is input element + let input_element = element.downcast::(); + + // Step 6: Let file be true if element is input element // in the file upload state, or false otherwise - let file_input = element - .downcast::() + let file_input = input_element .filter(|&input_element| input_element.input_type() == InputType::File); // Step 7. If file is false or the session's strict file interactability @@ -1148,7 +1184,13 @@ pub(crate) fn handle_will_send_keys( return handle_send_keys_file(file_input, &text, can_gc); } - // TODO: Check non-typeable form control + // Step 8 (non-typeable form control) + if let Some(input_element) = input_element { + if input_element.is_nontypeable() { + return handle_send_keys_non_typeable(input_element, &text, can_gc); + } + } + // TODO: Check content editable Ok(true)