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 <Kenzie.Raditya.Tirtarahardja@huawei.com>
This commit is contained in:
Kenzie Raditya Tirtarahardja 2025-07-23 17:11:44 +08:00 committed by GitHub
parent 3cb16eb864
commit 0970a99b7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 65 additions and 5 deletions

View file

@ -520,6 +520,24 @@ impl HTMLInputElement {
self.input_type.get() self.input_type.get()
} }
#[inline]
/// <https://w3c.github.io/webdriver/#dfn-non-typeable-form-control>
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] #[inline]
pub(crate) fn is_submit_button(&self) -> bool { pub(crate) fn is_submit_button(&self) -> bool {
let input_type = self.input_type.get(); let input_type = self.input_type.get();
@ -2154,7 +2172,7 @@ impl HTMLInputElement {
} }
// https://html.spec.whatwg.org/multipage/#concept-fe-mutable // 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-input-element:concept-fe-mutable
// https://html.spec.whatwg.org/multipage/#the-readonly-attribute:concept-fe-mutable // https://html.spec.whatwg.org/multipage/#the-readonly-attribute:concept-fe-mutable
!(self.upcast::<Element>().disabled_state() || self.ReadOnly()) !(self.upcast::<Element>().disabled_state() || self.ReadOnly())

View file

@ -71,6 +71,7 @@ use crate::dom::htmlselectelement::HTMLSelectElement;
use crate::dom::node::{Node, NodeTraits, ShadowIncluding}; use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
use crate::dom::nodelist::NodeList; use crate::dom::nodelist::NodeList;
use crate::dom::types::ShadowRoot; use crate::dom::types::ShadowRoot;
use crate::dom::validitystate::ValidationFlags;
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::dom::xmlserializer::XMLSerializer; use crate::dom::xmlserializer::XMLSerializer;
use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
@ -1099,6 +1100,40 @@ fn handle_send_keys_file(
} }
// Step 8. Return success with data null. // 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<bool, ErrorStatus> {
// 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` // This is done in `webdriver_server:lib.rs`
Ok(false) Ok(false)
} }
@ -1121,10 +1156,11 @@ pub(crate) fn handle_will_send_keys(
.send( .send(
// Set 5. Let element be the result of trying to get a known element. // Set 5. Let element be the result of trying to get a known element.
get_known_element(documents, pipeline, element_id).and_then(|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::<HTMLInputElement>();
// Step 6: Let file be true if element is input element
// in the file upload state, or false otherwise // in the file upload state, or false otherwise
let file_input = element let file_input = input_element
.downcast::<HTMLInputElement>()
.filter(|&input_element| input_element.input_type() == InputType::File); .filter(|&input_element| input_element.input_type() == InputType::File);
// Step 7. If file is false or the session's strict file interactability // 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); 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 // TODO: Check content editable
Ok(true) Ok(true)