From f0e10e63e27d675612ef14b59f9429de4e906996 Mon Sep 17 00:00:00 2001 From: batu_hoang <55729155+longvatrong111@users.noreply.github.com> Date: Fri, 18 Jul 2025 05:24:50 +0800 Subject: [PATCH] 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 Signed-off-by: batu_hoang --- components/shared/embedder/lib.rs | 8 +++ components/shared/embedder/webdriver.rs | 1 + components/webdriver_server/lib.rs | 12 +++-- components/webdriver_server/user_prompt.rs | 51 +++++++++++++++++++ ports/servoshell/desktop/app.rs | 3 ++ ports/servoshell/desktop/app_state.rs | 8 +++ ports/servoshell/desktop/dialog.rs | 6 +++ .../tests/classic/accept_alert/accept.py.ini | 9 ++-- .../classic/dismiss_alert/dismiss.py.ini | 9 ++-- .../tests/classic/send_alert_text/send.py.ini | 30 ----------- 10 files changed, 91 insertions(+), 46 deletions(-) diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs index 10878cc4c69..10e32cdd939 100644 --- a/components/shared/embedder/lib.rs +++ b/components/shared/embedder/lib.rs @@ -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) { match self { SimpleDialog::Alert { diff --git a/components/shared/embedder/webdriver.rs b/components/shared/embedder/webdriver.rs index e333411a599..1243ac62917 100644 --- a/components/shared/embedder/webdriver.rs +++ b/components/shared/embedder/webdriver.rs @@ -166,6 +166,7 @@ pub enum WebDriverCommandMsg { IpcSender, ()>>, ), GetAlertText(WebViewId, IpcSender>), + SendAlertText(WebViewId, String), AddLoadStatusSender(WebViewId, IpcSender), RemoveLoadStatusSender(WebViewId), } diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 51c790bf8f2..9a3b5634db9 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -827,10 +827,13 @@ impl Handler { recv(self.load_status_receiver) -> res => { match res { Ok(WebDriverLoadStatus::Blocked) => { - Err(WebDriverError::new( - ErrorStatus::UnexpectedAlertOpen, - "Load is blocked", - )) + // TODO: evaluate the correctness later + // Load status is block means an user prompt is shown. + // 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) @@ -2519,6 +2522,7 @@ impl WebDriverHandler for Handler { WebDriverCommand::DismissAlert => self.handle_dismiss_alert(), WebDriverCommand::AcceptAlert => self.handle_accept_alert(), WebDriverCommand::GetAlertText => self.handle_get_alert_text(), + WebDriverCommand::SendAlertText(text) => self.handle_send_alert_text(text.text), WebDriverCommand::DeleteCookies => self.handle_delete_cookies(), WebDriverCommand::DeleteCookie(name) => self.handle_delete_cookie(name), WebDriverCommand::GetTimeouts => self.handle_get_timeouts(), diff --git a/components/webdriver_server/user_prompt.rs b/components/webdriver_server/user_prompt.rs index 1e6f403bfdb..04ae689f7e1 100644 --- a/components/webdriver_server/user_prompt.rs +++ b/components/webdriver_server/user_prompt.rs @@ -224,6 +224,7 @@ impl Handler { } } + /// pub(crate) fn handle_get_alert_text(&self) -> WebDriverResult { // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. @@ -255,6 +256,56 @@ impl Handler { } } + /// + pub(crate) fn handle_send_alert_text( + &self, + text: String, + ) -> WebDriverResult { + 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.", + )), + } + }, + } + } + /// pub(crate) fn handle_any_user_prompts( &self, diff --git a/ports/servoshell/desktop/app.rs b/ports/servoshell/desktop/app.rs index d086b2c483b..f42240c8d0e 100644 --- a/ports/servoshell/desktop/app.rs +++ b/ports/servoshell/desktop/app.rs @@ -620,6 +620,9 @@ impl App { 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(..) => { warn!( "WebDriverCommand {:?} is still not moved from constellation to embedder", diff --git a/ports/servoshell/desktop/app_state.rs b/ports/servoshell/desktop/app_state.rs index b965dadfa30..12e6e24268e 100644 --- a/ports/servoshell/desktop/app_state.rs +++ b/ports/servoshell/desktop/app_state.rs @@ -381,6 +381,14 @@ impl RunningAppState { .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 { let focused_id = self.inner().focused_webview_id?; self.webviews() diff --git a/ports/servoshell/desktop/dialog.rs b/ports/servoshell/desktop/dialog.rs index 26977cb76cd..7c86a49ae3e 100644 --- a/ports/servoshell/desktop/dialog.rs +++ b/ports/servoshell/desktop/dialog.rs @@ -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 { match self { Dialog::File { diff --git a/tests/wpt/meta/webdriver/tests/classic/accept_alert/accept.py.ini b/tests/wpt/meta/webdriver/tests/classic/accept_alert/accept.py.ini index 22738ef6923..c273ca48911 100644 --- a/tests/wpt/meta/webdriver/tests/classic/accept_alert/accept.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/accept_alert/accept.py.ini @@ -3,7 +3,7 @@ expected: ERROR [test_accept_confirm] - expected: FAIL + expected: ERROR [test_accept_prompt] expected: FAIL @@ -11,11 +11,8 @@ [test_accept_in_popup_window] expected: FAIL - [test_null_response_value] - expected: FAIL - [test_accept_alert] expected: FAIL - [test_unexpected_alert] - expected: FAIL + [test_no_browsing_context] + expected: ERROR diff --git a/tests/wpt/meta/webdriver/tests/classic/dismiss_alert/dismiss.py.ini b/tests/wpt/meta/webdriver/tests/classic/dismiss_alert/dismiss.py.ini index 02725c718e2..012a8188149 100644 --- a/tests/wpt/meta/webdriver/tests/classic/dismiss_alert/dismiss.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/dismiss_alert/dismiss.py.ini @@ -3,7 +3,7 @@ expected: ERROR [test_dismiss_confirm] - expected: FAIL + expected: ERROR [test_dismiss_prompt] expected: FAIL @@ -11,11 +11,8 @@ [test_dismiss_in_popup_window] expected: FAIL - [test_null_response_value] - expected: FAIL - [test_dismiss_alert] expected: FAIL - [test_unexpected_alert] - expected: FAIL + [test_no_browsing_context] + expected: ERROR diff --git a/tests/wpt/meta/webdriver/tests/classic/send_alert_text/send.py.ini b/tests/wpt/meta/webdriver/tests/classic/send_alert_text/send.py.ini index 722ac8793c4..9554d5169dd 100644 --- a/tests/wpt/meta/webdriver/tests/classic/send_alert_text/send.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/send_alert_text/send.py.ini @@ -1,31 +1,4 @@ [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\]] expected: FAIL @@ -43,6 +16,3 @@ [test_send_alert_text[Fed\\terer\]] expected: FAIL - - [test_unexpected_alert] - expected: FAIL