Implement wb element send keys for file input (#37224)

We can now send keys to file input, which results in uploading file with
given filename. Needs
`pref=dom_testing_html_input_element_select_files_enabled` flag to work.

https://w3c.github.io/webdriver/#element-send-keys

Testing:
`tests/wpt/meta/webdriver/tests/classic/element_send_keys/{events,
file_upload}.py.`

Signed-off-by: PotatoCP <kenzieradityatirtarahardja18@gmail.com>
This commit is contained in:
Kenzie Raditya Tirtarahardja 2025-06-05 14:48:14 +08:00 committed by GitHub
parent 165ac32f18
commit d66e4fc459
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 78 additions and 55 deletions

View file

@ -2280,15 +2280,6 @@ impl ScriptThread {
can_gc,
)
},
WebDriverScriptCommand::FocusElement(element_id, reply) => {
webdriver_handlers::handle_focus_element(
&documents,
pipeline_id,
element_id,
reply,
can_gc,
)
},
WebDriverScriptCommand::ElementClick(element_id, reply) => {
webdriver_handlers::handle_element_click(
&documents,
@ -2394,6 +2385,20 @@ impl ScriptThread {
WebDriverScriptCommand::GetTitle(reply) => {
webdriver_handlers::handle_get_title(&documents, pipeline_id, reply)
},
WebDriverScriptCommand::WillSendKeys(
element_id,
text,
strict_file_interactability,
reply,
) => webdriver_handlers::handle_will_send_keys(
&documents,
pipeline_id,
element_id,
text,
strict_file_interactability,
reply,
can_gc,
),
_ => (),
}
}

View file

@ -843,24 +843,66 @@ pub(crate) fn handle_find_element_elements_tag_name(
.unwrap();
}
pub(crate) fn handle_focus_element(
pub(crate) fn handle_will_send_keys(
documents: &DocumentCollection,
pipeline: PipelineId,
element_id: String,
reply: IpcSender<Result<(), ErrorStatus>>,
text: String,
strict_file_interactability: bool,
reply: IpcSender<Result<bool, ErrorStatus>>,
can_gc: CanGc,
) {
reply
.send(
find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| {
match node.downcast::<HTMLElement>() {
Some(element) => {
// Need a way to find if this actually succeeded
element.Focus(can_gc);
Ok(())
},
None => Err(ErrorStatus::UnknownError),
// Step 6: Let file be true if element is input element
// in the file upload state, or false otherwise
let file_input = node
.downcast::<HTMLInputElement>()
.filter(|&input_element| input_element.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 {
match node.downcast::<HTMLElement>() {
Some(element) => {
// Need a way to find if this actually succeeded
element.Focus(can_gc);
},
None => return Err(ErrorStatus::UnknownError),
}
}
// Step 8 (file input)
if let Some(file_input) = file_input {
// Step 8.1: Let files be the result of splitting text
// on the newline (\n) character.
let files: Vec<DOMString> = text.split("\n").map(|s| s.into()).collect();
// Step 8.2
if files.is_empty() {
return Err(ErrorStatus::InvalidArgument);
}
// Step 8.3 - 8.4
if !file_input.Multiple() && files.len() > 1 {
return Err(ErrorStatus::InvalidArgument);
}
// Step 8.5
// TODO: Should return invalid argument error if file doesn't exist
// Step 8.6 - 8.7
// Input and change event already fired in `htmlinputelement.rs`.
file_input.SelectFiles(files, can_gc);
// Step 8.8
return Ok(false);
}
// TODO: Check non-typeable form control
// TODO: Check content editable
Ok(true)
}),
)
.unwrap();

View file

@ -130,7 +130,6 @@ pub enum WebDriverScriptCommand {
IpcSender<Result<Vec<String>, ErrorStatus>>,
),
FindElementElementsTagName(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
FocusElement(String, IpcSender<Result<(), ErrorStatus>>),
ElementClick(String, IpcSender<Result<Option<String>, ErrorStatus>>),
GetActiveElement(IpcSender<Option<String>>),
GetComputedRole(String, IpcSender<Result<Option<String>, ErrorStatus>>),
@ -161,6 +160,8 @@ pub enum WebDriverScriptCommand {
IsEnabled(String, IpcSender<Result<bool, ErrorStatus>>),
IsSelected(String, IpcSender<Result<bool, ErrorStatus>>),
GetTitle(IpcSender<String>),
/// Match the element type before sending the event for webdriver `element send keys`.
WillSendKeys(String, String, bool, IpcSender<Result<bool, ErrorStatus>>),
}
#[derive(Debug, Deserialize, Serialize)]

View file

@ -1619,14 +1619,23 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::FocusElement(element.to_string(), sender);
let cmd = WebDriverScriptCommand::WillSendKeys(
element.to_string(),
keys.text.to_string(),
self.session()?.strict_file_interactability,
sender,
);
let cmd_msg = WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd);
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
// TODO: distinguish the not found and not focusable cases
wait_for_script_response(receiver)?.map_err(|error| WebDriverError::new(error, ""))?;
// File input and non-typeable form control should have
// been handled in `webdriver_handler.rs`.
if wait_for_script_response(receiver)?.map_err(|error| WebDriverError::new(error, ""))? {
return Ok(WebDriverResponse::Void);
}
let input_events = send_keys(&keys.text);