webdriver: Implement send alert text (#38140)

Implement webdriver `SendAlertText` command

Tests:
https://github.com/longvatrong111/servo/actions/runs/16342669929
https://github.com/longvatrong111/servo/actions/runs/16342671477

cc: @xiaochengh

---------

Signed-off-by: batu_hoang <hoang.binh.trong@huawei.com>
Signed-off-by: batu_hoang <longvatrong111@gmail.com>
This commit is contained in:
batu_hoang 2025-07-18 05:24:50 +08:00 committed by GitHub
parent 3ce95b2ba5
commit f0e10e63e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 91 additions and 46 deletions

View file

@ -172,6 +172,14 @@ impl SimpleDialog {
} }
} }
pub fn set_message(&mut self, text: String) {
match self {
SimpleDialog::Alert { message, .. } => *message = text,
SimpleDialog::Confirm { message, .. } => *message = text,
SimpleDialog::Prompt { message, .. } => *message = text,
}
}
pub fn dismiss(&self) { pub fn dismiss(&self) {
match self { match self {
SimpleDialog::Alert { SimpleDialog::Alert {

View file

@ -166,6 +166,7 @@ pub enum WebDriverCommandMsg {
IpcSender<Result<Option<String>, ()>>, IpcSender<Result<Option<String>, ()>>,
), ),
GetAlertText(WebViewId, IpcSender<Result<String, ()>>), GetAlertText(WebViewId, IpcSender<Result<String, ()>>),
SendAlertText(WebViewId, String),
AddLoadStatusSender(WebViewId, IpcSender<WebDriverLoadStatus>), AddLoadStatusSender(WebViewId, IpcSender<WebDriverLoadStatus>),
RemoveLoadStatusSender(WebViewId), RemoveLoadStatusSender(WebViewId),
} }

View file

@ -827,10 +827,13 @@ impl Handler {
recv(self.load_status_receiver) -> res => { recv(self.load_status_receiver) -> res => {
match res { match res {
Ok(WebDriverLoadStatus::Blocked) => { Ok(WebDriverLoadStatus::Blocked) => {
Err(WebDriverError::new( // TODO: evaluate the correctness later
ErrorStatus::UnexpectedAlertOpen, // Load status is block means an user prompt is shown.
"Load is blocked", // Alot of tests expect this to return success
)) // then the user prompt is handled in the next command.
// If user prompt can't be handler, next command returns
// an error anyway.
Ok(WebDriverResponse::Void)
} }
_ => { _ => {
Ok(WebDriverResponse::Void) Ok(WebDriverResponse::Void)
@ -2519,6 +2522,7 @@ impl WebDriverHandler<ServoExtensionRoute> for Handler {
WebDriverCommand::DismissAlert => self.handle_dismiss_alert(), WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
WebDriverCommand::AcceptAlert => self.handle_accept_alert(), WebDriverCommand::AcceptAlert => self.handle_accept_alert(),
WebDriverCommand::GetAlertText => self.handle_get_alert_text(), WebDriverCommand::GetAlertText => self.handle_get_alert_text(),
WebDriverCommand::SendAlertText(text) => self.handle_send_alert_text(text.text),
WebDriverCommand::DeleteCookies => self.handle_delete_cookies(), WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
WebDriverCommand::DeleteCookie(name) => self.handle_delete_cookie(name), WebDriverCommand::DeleteCookie(name) => self.handle_delete_cookie(name),
WebDriverCommand::GetTimeouts => self.handle_get_timeouts(), WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),

View file

@ -224,6 +224,7 @@ impl Handler {
} }
} }
/// <https://www.w3.org/TR/webdriver2/#dfn-get-alert-text>
pub(crate) fn handle_get_alert_text(&self) -> WebDriverResult<WebDriverResponse> { pub(crate) fn handle_get_alert_text(&self) -> WebDriverResult<WebDriverResponse> {
// Step 1. If session's current top-level browsing context is no longer open, // Step 1. If session's current top-level browsing context is no longer open,
// return error with error code no such window. // return error with error code no such window.
@ -255,6 +256,56 @@ impl Handler {
} }
} }
/// <https://www.w3.org/TR/webdriver2/#dfn-send-alert-text>
pub(crate) fn handle_send_alert_text(
&self,
text: String,
) -> WebDriverResult<WebDriverResponse> {
let webview_id = self.session()?.webview_id;
// Step 3. If session's current top-level browsing context is no longer open,
// return error with error code no such window.
self.verify_top_level_browsing_context_is_open(webview_id)?;
let (sender, receiver) = ipc::channel().unwrap();
self.send_message_to_embedder(WebDriverCommandMsg::CurrentUserPrompt(webview_id, sender))?;
match wait_for_script_response(receiver)? {
// Step 4. If the current user prompt is null, return error with error code no such alert.
None => Err(WebDriverError::new(
ErrorStatus::NoSuchAlert,
"No user prompt is currently active.",
)),
Some(prompt_type) => {
match prompt_type {
// Step 5. If the current user prompt is alert or confirm,
// return error with error code element not interactable.
WebDriverUserPrompt::Alert | WebDriverUserPrompt::Confirm => {
Err(WebDriverError::new(
ErrorStatus::ElementNotInteractable,
"Cannot send text to an alert or confirm prompt.",
))
},
// Step 5. If the current user prompt is prompt
WebDriverUserPrompt::Prompt => {
// Step 6. Send the text to the current user prompt.
self.send_message_to_embedder(WebDriverCommandMsg::SendAlertText(
webview_id, text,
))?;
Ok(WebDriverResponse::Void)
},
// Step 5. Otherwise, return error with error code unsupported operation.
_ => Err(WebDriverError::new(
ErrorStatus::UnsupportedOperation,
"Current user prompt type is not supported.",
)),
}
},
}
}
/// <https://w3c.github.io/webdriver/#dfn-handle-any-user-prompts> /// <https://w3c.github.io/webdriver/#dfn-handle-any-user-prompts>
pub(crate) fn handle_any_user_prompts( pub(crate) fn handle_any_user_prompts(
&self, &self,

View file

@ -620,6 +620,9 @@ impl App {
warn!("Failed to send response of GetAlertText: {error}"); warn!("Failed to send response of GetAlertText: {error}");
}; };
}, },
WebDriverCommandMsg::SendAlertText(webview_id, text) => {
running_state.set_alert_text_of_newest_dialog(webview_id, text);
},
WebDriverCommandMsg::TakeScreenshot(..) => { WebDriverCommandMsg::TakeScreenshot(..) => {
warn!( warn!(
"WebDriverCommand {:?} is still not moved from constellation to embedder", "WebDriverCommand {:?} is still not moved from constellation to embedder",

View file

@ -381,6 +381,14 @@ impl RunningAppState {
.and_then(|dialog| dialog.message()) .and_then(|dialog| dialog.message())
} }
pub(crate) fn set_alert_text_of_newest_dialog(&self, webview_id: WebViewId, text: String) {
if let Some(dialogs) = self.inner_mut().dialogs.get_mut(&webview_id) {
if let Some(dialog) = dialogs.last_mut() {
dialog.set_message(text);
}
}
}
pub(crate) fn get_focused_webview_index(&self) -> Option<usize> { pub(crate) fn get_focused_webview_index(&self) -> Option<usize> {
let focused_id = self.inner().focused_webview_id?; let focused_id = self.inner().focused_webview_id?;
self.webviews() self.webviews()

View file

@ -168,6 +168,12 @@ impl Dialog {
} }
} }
pub fn set_message(&mut self, text: String) {
if let Dialog::SimpleDialog(dialog) = self {
dialog.set_message(text);
}
}
pub fn update(&mut self, ctx: &egui::Context) -> bool { pub fn update(&mut self, ctx: &egui::Context) -> bool {
match self { match self {
Dialog::File { Dialog::File {

View file

@ -3,7 +3,7 @@
expected: ERROR expected: ERROR
[test_accept_confirm] [test_accept_confirm]
expected: FAIL expected: ERROR
[test_accept_prompt] [test_accept_prompt]
expected: FAIL expected: FAIL
@ -11,11 +11,8 @@
[test_accept_in_popup_window] [test_accept_in_popup_window]
expected: FAIL expected: FAIL
[test_null_response_value]
expected: FAIL
[test_accept_alert] [test_accept_alert]
expected: FAIL expected: FAIL
[test_unexpected_alert] [test_no_browsing_context]
expected: FAIL expected: ERROR

View file

@ -3,7 +3,7 @@
expected: ERROR expected: ERROR
[test_dismiss_confirm] [test_dismiss_confirm]
expected: FAIL expected: ERROR
[test_dismiss_prompt] [test_dismiss_prompt]
expected: FAIL expected: FAIL
@ -11,11 +11,8 @@
[test_dismiss_in_popup_window] [test_dismiss_in_popup_window]
expected: FAIL expected: FAIL
[test_null_response_value]
expected: FAIL
[test_dismiss_alert] [test_dismiss_alert]
expected: FAIL expected: FAIL
[test_unexpected_alert] [test_no_browsing_context]
expected: FAIL expected: ERROR

View file

@ -1,31 +1,4 @@
[send.py] [send.py]
[test_null_response_value]
expected: ERROR
[test_invalid_input[None\]]
expected: ERROR
[test_invalid_input[text1\]]
expected: ERROR
[test_invalid_input[42\]]
expected: ERROR
[test_no_top_browsing_context]
expected: FAIL
[test_no_browsing_context]
expected: FAIL
[test_no_user_prompt]
expected: FAIL
[test_alert_element_not_interactable[alert\]]
expected: FAIL
[test_alert_element_not_interactable[confirm\]]
expected: FAIL
[test_chained_alert_element_not_interactable[alert\]] [test_chained_alert_element_not_interactable[alert\]]
expected: FAIL expected: FAIL
@ -43,6 +16,3 @@
[test_send_alert_text[Fed\\terer\]] [test_send_alert_text[Fed\\terer\]]
expected: FAIL expected: FAIL
[test_unexpected_alert]
expected: FAIL