1use std::borrow::Cow;
6use std::collections::{BTreeMap, HashMap};
7
8use base::id::WebViewId;
9use embedder_traits::{WebDriverCommandMsg, WebDriverUserPrompt, WebDriverUserPromptAction};
10use ipc_channel::ipc;
11use serde_json::{Map, Value};
12use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
13use webdriver::response::{ValueResponse, WebDriverResponse};
14
15use crate::{Handler, wait_for_ipc_response};
16
17const KNOWN_PROMPT_HANDLERS: [&str; 5] = [
18 "dismiss",
19 "accept",
20 "dismiss and notify",
21 "accept and notify",
22 "ignore",
23];
24
25const VALID_PROMPT_TYPES: [&str; 6] = [
26 "alert",
27 "beforeUnload",
28 "confirm",
29 "default",
30 "file",
31 "prompt",
32];
33
34#[derive(Clone, Debug)]
36pub(crate) struct PromptHandlerConfiguration {
37 handler: WebDriverUserPromptAction,
38 notify: bool,
39}
40
41pub(crate) type UserPromptHandler = HashMap<WebDriverUserPrompt, PromptHandlerConfiguration>;
42
43pub(crate) fn deserialize_unhandled_prompt_behaviour(
45 value_param: Value,
46) -> Result<UserPromptHandler, WebDriverError> {
47 let (value, is_string_value) = match value_param {
49 Value::Object(map) => (map, false),
50 Value::String(..) => {
51 let mut map = Map::new();
52 map.insert("fallbackDefault".to_string(), value_param);
53 (map, true)
54 },
55 _ => {
56 return Err(WebDriverError::new(
57 ErrorStatus::InvalidArgument,
58 "Expected an object or a string for unhandled prompt behavior.",
59 ));
60 },
61 };
62
63 let mut user_prompt_handler = UserPromptHandler::new();
65
66 for (prompt_type, handler) in value {
68 if !is_string_value && !VALID_PROMPT_TYPES.contains(&prompt_type.as_str()) {
71 return Err(WebDriverError::new(
72 ErrorStatus::InvalidArgument,
73 format!("Invalid prompt type: {}", prompt_type),
74 ));
75 }
76
77 let handle_str = match handler {
80 Value::String(s) => s,
81 _ => {
82 return Err(WebDriverError::new(
83 ErrorStatus::InvalidArgument,
84 format!("Expected a string for handler, got: {:?}", handler),
85 ));
86 },
87 };
88 if !KNOWN_PROMPT_HANDLERS.contains(&handle_str.as_str()) {
89 return Err(WebDriverError::new(
90 ErrorStatus::InvalidArgument,
91 format!("Unknown prompt handler: {}", handle_str),
92 ));
93 }
94
95 let (handler, notify) = match handle_str.as_str() {
97 "accept and notify" => (
98 WebDriverUserPromptAction::new_from_str("accept").unwrap(),
99 true,
100 ),
101 "dismiss and notify" => (
102 WebDriverUserPromptAction::new_from_str("dismiss").unwrap(),
103 true,
104 ),
105 "ignore" => (
106 WebDriverUserPromptAction::new_from_str("ignore").unwrap(),
107 false,
108 ),
109 "accept" => (
110 WebDriverUserPromptAction::new_from_str("accept").unwrap(),
111 false,
112 ),
113 "dismiss" => (
114 WebDriverUserPromptAction::new_from_str("dismiss").unwrap(),
115 false,
116 ),
117 _ => unreachable!(),
118 };
119
120 user_prompt_handler.insert(
122 WebDriverUserPrompt::new_from_str(&prompt_type).unwrap(),
123 PromptHandlerConfiguration { handler, notify },
124 );
125 }
126
127 Ok(user_prompt_handler)
128}
129
130pub(crate) fn default_unhandled_prompt_behavior() -> &'static str {
131 "dismiss and notify"
132}
133
134fn get_user_prompt_handler(
136 user_prompt_handler: &UserPromptHandler,
137 prompt_type: WebDriverUserPrompt,
138) -> PromptHandlerConfiguration {
139 if let Some(handler) = user_prompt_handler.get(&prompt_type) {
141 return (*handler).clone();
142 }
143
144 if let Some(handler) = user_prompt_handler.get(&WebDriverUserPrompt::Default) {
146 return (*handler).clone();
147 }
148
149 if prompt_type == WebDriverUserPrompt::BeforeUnload {
151 return PromptHandlerConfiguration {
152 handler: WebDriverUserPromptAction::Accept,
153 notify: false,
154 };
155 }
156
157 if let Some(handler) = user_prompt_handler.get(&WebDriverUserPrompt::FallbackDefault) {
159 return (*handler).clone();
160 }
161
162 PromptHandlerConfiguration {
164 handler: WebDriverUserPromptAction::Dismiss,
165 notify: true,
166 }
167}
168
169fn webdriver_response_single_data(
170 key: &'static str,
171 value: Value,
172) -> Option<BTreeMap<Cow<'static, str>, Value>> {
173 Some([(Cow::Borrowed(key), value)].into_iter().collect())
174}
175
176impl Handler {
177 pub(crate) fn handle_dismiss_alert(&self) -> WebDriverResult<WebDriverResponse> {
179 self.verify_top_level_browsing_context_is_open(self.webview_id()?)?;
182
183 let (sender, receiver) = ipc::channel().unwrap();
185 self.send_message_to_embedder(WebDriverCommandMsg::HandleUserPrompt(
186 self.verified_webview_id(),
187 WebDriverUserPromptAction::Dismiss,
188 sender,
189 ))?;
190
191 match wait_for_ipc_response(receiver)? {
192 Err(()) => Err(WebDriverError::new(
194 ErrorStatus::NoSuchAlert,
195 "No user prompt is currently active.",
196 )),
197 Ok(_) => Ok(WebDriverResponse::Void),
199 }
200 }
201
202 pub(crate) fn handle_accept_alert(&self) -> WebDriverResult<WebDriverResponse> {
204 self.verify_top_level_browsing_context_is_open(self.webview_id()?)?;
207
208 let (sender, receiver) = ipc::channel().unwrap();
210 self.send_message_to_embedder(WebDriverCommandMsg::HandleUserPrompt(
211 self.verified_webview_id(),
212 WebDriverUserPromptAction::Accept,
213 sender,
214 ))?;
215
216 match wait_for_ipc_response(receiver)? {
217 Err(()) => Err(WebDriverError::new(
219 ErrorStatus::NoSuchAlert,
220 "No user prompt is currently active.",
221 )),
222 Ok(_) => Ok(WebDriverResponse::Void),
224 }
225 }
226
227 pub(crate) fn handle_get_alert_text(&self) -> WebDriverResult<WebDriverResponse> {
229 self.verify_top_level_browsing_context_is_open(self.webview_id()?)?;
232
233 let (sender, receiver) = ipc::channel().unwrap();
234 self.send_message_to_embedder(WebDriverCommandMsg::GetAlertText(
235 self.verified_webview_id(),
236 sender,
237 ))?;
238
239 match wait_for_ipc_response(receiver)? {
240 Err(()) => Err(WebDriverError::new(
242 ErrorStatus::NoSuchAlert,
243 "No user prompt is currently active.",
244 )),
245 Ok(message) => Ok(WebDriverResponse::Generic(ValueResponse(
249 serde_json::to_value(message).map_err(|e| {
250 WebDriverError::new(
251 ErrorStatus::UnknownError,
252 format!("Failed to serialize alert text: {}", e),
253 )
254 })?,
255 ))),
256 }
257 }
258
259 pub(crate) fn handle_send_alert_text(
261 &self,
262 text: String,
263 ) -> WebDriverResult<WebDriverResponse> {
264 let webview_id = self.webview_id()?;
265
266 self.verify_top_level_browsing_context_is_open(webview_id)?;
269
270 let (sender, receiver) = ipc::channel().unwrap();
271
272 self.send_message_to_embedder(WebDriverCommandMsg::CurrentUserPrompt(webview_id, sender))?;
273
274 match wait_for_ipc_response(receiver)? {
275 None => Err(WebDriverError::new(
277 ErrorStatus::NoSuchAlert,
278 "No user prompt is currently active.",
279 )),
280 Some(prompt_type) => {
281 match prompt_type {
282 WebDriverUserPrompt::Alert | WebDriverUserPrompt::Confirm => {
285 Err(WebDriverError::new(
286 ErrorStatus::ElementNotInteractable,
287 "Cannot send text to an alert or confirm prompt.",
288 ))
289 },
290 WebDriverUserPrompt::Prompt => {
292 self.send_message_to_embedder(WebDriverCommandMsg::SendAlertText(
294 webview_id, text,
295 ))?;
296
297 Ok(WebDriverResponse::Void)
298 },
299 _ => Err(WebDriverError::new(
301 ErrorStatus::UnsupportedOperation,
302 "Current user prompt type is not supported.",
303 )),
304 }
305 },
306 }
307 }
308
309 pub(crate) fn handle_any_user_prompts(
311 &self,
312 webview_id: WebViewId,
313 ) -> WebDriverResult<WebDriverResponse> {
314 let (sender, receiver) = ipc::channel().unwrap();
315
316 self.send_message_to_embedder(WebDriverCommandMsg::CurrentUserPrompt(webview_id, sender))?;
317
318 match wait_for_ipc_response(receiver)? {
319 None => Ok(WebDriverResponse::Void),
321 Some(prompt_type) => {
322 let handler =
324 get_user_prompt_handler(self.session()?.user_prompt_handler(), prompt_type);
325
326 let (sender, receiver) = ipc::channel().unwrap();
328 self.send_message_to_embedder(WebDriverCommandMsg::HandleUserPrompt(
329 webview_id,
330 handler.handler.clone(),
331 sender,
332 ))?;
333
334 if handler.notify || handler.handler == WebDriverUserPromptAction::Ignore {
335 let alert_text = wait_for_ipc_response(receiver)?
337 .unwrap_or_default()
338 .unwrap_or_default();
339
340 Err(WebDriverError::new_with_data(
341 ErrorStatus::UnexpectedAlertOpen,
342 "Handle any user prompt: Unexpected alert open.",
343 webdriver_response_single_data("text", Value::String(alert_text)),
344 None,
345 ))
346 } else {
347 Ok(WebDriverResponse::Void)
349 }
350 },
351 }
352 }
353}