1#![crate_name = "webdriver_server"]
6#![crate_type = "rlib"]
7#![deny(unsafe_code)]
8
9mod actions;
10mod capabilities;
11mod script_argument_extraction;
12mod server;
13mod session;
14mod timeout;
15mod user_prompt;
16
17use std::borrow::ToOwned;
18use std::cell::LazyCell;
19use std::collections::{BTreeMap, HashMap};
20use std::io::Cursor;
21use std::net::{SocketAddr, SocketAddrV4};
22use std::thread::sleep;
23use std::time::{Duration, Instant};
24use std::{env, fmt, process, thread};
25
26use base64::Engine;
27use capabilities::ServoCapabilities;
28use cookie::{CookieBuilder, Expiration, SameSite};
29use crossbeam_channel::{Receiver, RecvTimeoutError, Sender, after, select, unbounded};
30use embedder_traits::{
31 CustomHandlersAutomationMode, EventLoopWaker, ImeEvent, InputEvent, JSValue,
32 JavaScriptEvaluationError, JavaScriptEvaluationResultSerializationError, NewWindowTypeHint,
33 WebDriverCommandMsg, WebDriverFrameId, WebDriverJSResult, WebDriverLoadStatus,
34 WebDriverScriptCommand,
35};
36use euclid::{Point2D, Rect, Size2D};
37use http::method::Method;
38use image::{DynamicImage, ImageFormat};
39use keyboard_types::webdriver::{Event as DispatchStringEvent, KeyInputState, send_keys};
40use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, NamedKey};
41use log::{debug, error, info};
42use serde::de::{Deserializer, MapAccess, Visitor};
43use serde::ser::Serializer;
44use serde::{Deserialize, Serialize};
45use serde_json::{Value, json};
46use server::{Session, SessionTeardownKind, WebDriverHandler};
47use servo_base::generic_channel::{self, GenericReceiver, GenericSender, RoutedReceiver};
48use servo_base::id::{BrowsingContextId, WebViewId};
49use servo_config::prefs::{self, PrefValue, Preferences};
50use servo_geometry::DeviceIndependentIntRect;
51use servo_url::ServoUrl;
52use style_traits::CSSPixel;
53use time::OffsetDateTime;
54use uuid::Uuid;
55use webdriver::actions::{
56 ActionSequence, ActionsType, KeyAction, KeyActionItem, KeyDownAction, KeyUpAction,
57 PointerAction, PointerActionItem, PointerActionParameters, PointerDownAction,
58 PointerMoveAction, PointerOrigin, PointerType, PointerUpAction,
59};
60use webdriver::capabilities::CapabilitiesMatching;
61use webdriver::command::{
62 ActionsParameters, AddCookieParameters, GetParameters, JavascriptCommandParameters,
63 LocatorParameters, NewSessionParameters, NewWindowParameters, SendKeysParameters,
64 SwitchToFrameParameters, SwitchToWindowParameters, TimeoutsParameters, WebDriverCommand,
65 WebDriverExtensionCommand, WebDriverMessage, WindowRectParameters,
66};
67use webdriver::common::{
68 Cookie, Date, LocatorStrategy, Parameters, ShadowRoot, WebElement, WebFrame, WebWindow,
69};
70use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
71use webdriver::httpapi::WebDriverExtensionRoute;
72use webdriver::response::{
73 CloseWindowResponse, CookieResponse, CookiesResponse, ElementRectResponse, NewSessionResponse,
74 NewWindowResponse, TimeoutsResponse, ValueResponse, WebDriverResponse, WindowRectResponse,
75};
76
77use crate::actions::{ELEMENT_CLICK_BUTTON, InputSourceState, PendingActions, PointerInputState};
78use crate::session::{PageLoadStrategy, WebDriverSession};
79use crate::timeout::{DEFAULT_IMPLICIT_WAIT, DEFAULT_PAGE_LOAD_TIMEOUT, SCREENSHOT_TIMEOUT};
80
81pub(crate) static MAXIMUM_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
84
85fn extension_routes() -> Vec<(Method, &'static str, ServoExtensionRoute)> {
86 vec![
87 (
88 Method::GET,
89 "/session/{sessionId}/servo/prefs/get",
90 ServoExtensionRoute::GetPrefs,
91 ),
92 (
93 Method::POST,
94 "/session/{sessionId}/servo/prefs/set",
95 ServoExtensionRoute::SetPrefs,
96 ),
97 (
98 Method::POST,
99 "/session/{sessionId}/servo/prefs/reset",
100 ServoExtensionRoute::ResetPrefs,
101 ),
102 (
103 Method::DELETE,
104 "/session/{sessionId}/servo/shutdown",
105 ServoExtensionRoute::Shutdown,
106 ),
107 (
109 Method::POST,
110 "/session/{sessionId}/custom-handlers/set-mode",
111 ServoExtensionRoute::CustomHandlersSetMode,
112 ),
113 (
114 Method::POST,
115 "/session/{sessionId}/servo/cookies/reset",
116 ServoExtensionRoute::ResetAllCookies,
117 ),
118 ]
119}
120
121fn cookie_msg_to_cookie(cookie: cookie::Cookie) -> Cookie {
122 Cookie {
123 name: cookie.name().to_owned(),
124 value: cookie.value().to_owned(),
125 path: cookie.path().map(|s| s.to_owned()),
126 domain: cookie.domain().map(|s| s.to_owned()),
127 expiry: cookie.expires().and_then(|expiration| match expiration {
128 Expiration::DateTime(date_time) => Some(Date(date_time.unix_timestamp() as u64)),
129 Expiration::Session => None,
130 }),
131 secure: cookie.secure().unwrap_or(false),
132 http_only: cookie.http_only().unwrap_or(false),
133 same_site: cookie.same_site().map(|s| s.to_string()),
134 }
135}
136
137pub fn start_server(
138 port: u16,
139 embedder_sender: Sender<WebDriverCommandMsg>,
140 event_loop_waker: Box<dyn EventLoopWaker>,
141 default_preferences: Preferences,
142) {
143 let handler = Handler::new(embedder_sender, event_loop_waker, default_preferences);
144
145 thread::Builder::new()
146 .name("WebDriverHttpServer".to_owned())
147 .spawn(move || {
148 let address = SocketAddrV4::new("0.0.0.0".parse().unwrap(), port);
149 match server::start(
150 SocketAddr::V4(address),
151 vec![],
152 vec![],
153 handler,
154 extension_routes(),
155 ) {
156 Ok(listening) => info!("WebDriver server listening on {}", listening.socket),
157 Err(e) => panic!("Unable to start WebDriver HTTP server {e:?}"),
158 }
159 })
160 .expect("Thread spawning failed");
161}
162
163struct Handler {
164 load_status_receiver: RoutedReceiver<WebDriverLoadStatus>,
168 load_status_sender: GenericSender<WebDriverLoadStatus>,
172
173 session: Option<WebDriverSession>,
174
175 embedder_sender: Sender<WebDriverCommandMsg>,
179
180 event_loop_waker: Box<dyn EventLoopWaker>,
182
183 pending_input_event_receivers: Vec<Receiver<()>>,
188
189 pending_actions: Vec<PendingActions>,
192
193 default_preferences: Preferences,
195}
196
197#[derive(Clone, Copy, Debug, PartialEq)]
198enum ServoExtensionRoute {
199 GetPrefs,
200 SetPrefs,
201 ResetPrefs,
202 Shutdown,
208 CustomHandlersSetMode,
209 ResetAllCookies,
210}
211
212impl WebDriverExtensionRoute for ServoExtensionRoute {
213 type Command = ServoExtensionCommand;
214
215 fn command(
216 &self,
217 _parameters: &Parameters,
218 body_data: &Value,
219 ) -> WebDriverResult<WebDriverCommand<ServoExtensionCommand>> {
220 let command = match *self {
221 ServoExtensionRoute::GetPrefs => {
222 let parameters: GetPrefsParameters = serde_json::from_value(body_data.clone())?;
223 ServoExtensionCommand::GetPrefs(parameters)
224 },
225 ServoExtensionRoute::SetPrefs => {
226 let parameters: SetPrefsParameters = serde_json::from_value(body_data.clone())?;
227 ServoExtensionCommand::SetPrefs(parameters)
228 },
229 ServoExtensionRoute::ResetPrefs => {
230 let parameters: GetPrefsParameters = serde_json::from_value(body_data.clone())?;
231 ServoExtensionCommand::ResetPrefs(parameters)
232 },
233 ServoExtensionRoute::CustomHandlersSetMode => {
234 let parameters: CustomHandlersSetModeParameters =
235 serde_json::from_value(body_data.clone())?;
236 ServoExtensionCommand::CustomHandlersSetMode(parameters)
237 },
238 ServoExtensionRoute::Shutdown => ServoExtensionCommand::Shutdown,
239 ServoExtensionRoute::ResetAllCookies => ServoExtensionCommand::ResetAllCookies,
240 };
241 Ok(WebDriverCommand::Extension(command))
242 }
243}
244
245#[derive(Clone, Debug)]
246enum ServoExtensionCommand {
247 GetPrefs(GetPrefsParameters),
248 SetPrefs(SetPrefsParameters),
249 ResetPrefs(GetPrefsParameters),
250 CustomHandlersSetMode(CustomHandlersSetModeParameters),
251 Shutdown,
252 ResetAllCookies,
253}
254
255impl WebDriverExtensionCommand for ServoExtensionCommand {
256 fn parameters_json(&self) -> Option<Value> {
257 match *self {
258 ServoExtensionCommand::GetPrefs(ref x) => serde_json::to_value(x).ok(),
259 ServoExtensionCommand::SetPrefs(ref x) => serde_json::to_value(x).ok(),
260 ServoExtensionCommand::ResetPrefs(ref x) => serde_json::to_value(x).ok(),
261 ServoExtensionCommand::CustomHandlersSetMode(ref x) => serde_json::to_value(x).ok(),
262 ServoExtensionCommand::Shutdown | ServoExtensionCommand::ResetAllCookies => None,
263 }
264 }
265}
266
267#[derive(Clone)]
268struct SendableJSValue(JSValue);
269
270impl Serialize for SendableJSValue {
271 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
272 where
273 S: Serializer,
274 {
275 match self.0 {
276 JSValue::Undefined | JSValue::Null => serializer.serialize_unit(),
277 JSValue::Boolean(x) => serializer.serialize_bool(x),
278 JSValue::Number(x) => {
279 if x.fract() == 0.0 {
280 serializer.serialize_i64(x as i64)
281 } else {
282 serializer.serialize_f64(x)
283 }
284 },
285 JSValue::String(ref x) => serializer.serialize_str(x),
286 JSValue::Element(ref x) => WebElement(x.clone()).serialize(serializer),
287 JSValue::ShadowRoot(ref x) => ShadowRoot(x.clone()).serialize(serializer),
288 JSValue::Frame(ref x) => WebFrame(x.clone()).serialize(serializer),
289 JSValue::Window(ref x) => WebWindow(x.clone()).serialize(serializer),
290 JSValue::Array(ref x) => x
291 .iter()
292 .map(|element| SendableJSValue(element.clone()))
293 .collect::<Vec<SendableJSValue>>()
294 .serialize(serializer),
295 JSValue::Object(ref x) => x
296 .iter()
297 .map(|(k, v)| (k.clone(), SendableJSValue(v.clone())))
298 .collect::<HashMap<String, SendableJSValue>>()
299 .serialize(serializer),
300 }
301 }
302}
303
304#[derive(Clone, Debug, PartialEq)]
305struct WebDriverPrefValue(PrefValue);
306
307impl Serialize for WebDriverPrefValue {
308 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
309 where
310 S: Serializer,
311 {
312 match self.0 {
313 PrefValue::Bool(b) => serializer.serialize_bool(b),
314 PrefValue::Str(ref s) => serializer.serialize_str(s),
315 PrefValue::Float(f) => serializer.serialize_f64(f),
316 PrefValue::Int(i) => serializer.serialize_i64(i),
317 PrefValue::Array(ref v) => v
318 .iter()
319 .map(|value| WebDriverPrefValue(value.clone()))
320 .collect::<Vec<WebDriverPrefValue>>()
321 .serialize(serializer),
322 PrefValue::UInt(u) => serializer.serialize_u64(u),
323 }
324 }
325}
326
327impl<'de> Deserialize<'de> for WebDriverPrefValue {
328 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
329 where
330 D: Deserializer<'de>,
331 {
332 struct Visitor;
333
334 impl ::serde::de::Visitor<'_> for Visitor {
335 type Value = WebDriverPrefValue;
336
337 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
338 formatter.write_str("preference value")
339 }
340
341 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
342 where
343 E: ::serde::de::Error,
344 {
345 Ok(WebDriverPrefValue(PrefValue::Float(value)))
346 }
347
348 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
349 where
350 E: ::serde::de::Error,
351 {
352 Ok(WebDriverPrefValue(PrefValue::Int(value)))
353 }
354
355 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
356 where
357 E: ::serde::de::Error,
358 {
359 Ok(WebDriverPrefValue(PrefValue::Int(value as i64)))
360 }
361
362 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
363 where
364 E: ::serde::de::Error,
365 {
366 Ok(WebDriverPrefValue(PrefValue::Str(String::from(value))))
367 }
368
369 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
370 where
371 E: ::serde::de::Error,
372 {
373 Ok(WebDriverPrefValue(PrefValue::Bool(value)))
374 }
375 }
376
377 deserializer.deserialize_any(Visitor)
378 }
379}
380
381#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
382struct GetPrefsParameters {
383 prefs: Vec<String>,
384}
385
386#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
387struct SetPrefsParameters {
388 #[serde(deserialize_with = "map_to_vec")]
389 prefs: Vec<(String, WebDriverPrefValue)>,
390}
391
392#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
393struct CustomHandlersSetModeParameters {
394 mode: String,
395}
396
397fn map_to_vec<'de, D>(de: D) -> Result<Vec<(String, WebDriverPrefValue)>, D::Error>
398where
399 D: Deserializer<'de>,
400{
401 de.deserialize_map(TupleVecMapVisitor)
402}
403
404struct TupleVecMapVisitor;
405
406impl<'de> Visitor<'de> for TupleVecMapVisitor {
407 type Value = Vec<(String, WebDriverPrefValue)>;
408
409 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
410 formatter.write_str("a map")
411 }
412
413 #[inline]
414 fn visit_unit<E>(self) -> Result<Self::Value, E> {
415 Ok(Vec::new())
416 }
417
418 #[inline]
419 fn visit_map<T>(self, mut access: T) -> Result<Self::Value, T::Error>
420 where
421 T: MapAccess<'de>,
422 {
423 let mut values = Vec::new();
424
425 while let Some((key, value)) = access.next_entry()? {
426 values.push((key, value));
427 }
428
429 Ok(values)
430 }
431}
432
433enum VerifyBrowsingContextIsOpen {
434 Yes,
435 No,
436}
437
438enum ImplicitWait {
439 Return,
440 #[expect(dead_code, reason = "This will be used in the next patch")]
441 Continue,
442}
443
444impl From<ImplicitWait> for bool {
445 fn from(implicit_wait: ImplicitWait) -> Self {
446 match implicit_wait {
447 ImplicitWait::Return => true,
448 ImplicitWait::Continue => false,
449 }
450 }
451}
452
453impl Handler {
454 fn new(
455 embedder_sender: Sender<WebDriverCommandMsg>,
456 event_loop_waker: Box<dyn EventLoopWaker>,
457 default_preferences: Preferences,
458 ) -> Handler {
459 let (load_status_sender, receiver) = generic_channel::channel().unwrap();
465 let load_status_receiver = receiver.route_preserving_errors();
466
467 Handler {
468 load_status_sender,
469 load_status_receiver,
470 session: None,
471 embedder_sender,
472 event_loop_waker,
473 default_preferences,
474 pending_input_event_receivers: Default::default(),
475 pending_actions: Default::default(),
476 }
477 }
478
479 fn browsing_context_id(&self) -> WebDriverResult<BrowsingContextId> {
480 self.session()?
481 .current_browsing_context_id()
482 .ok_or_else(|| {
483 WebDriverError::new(ErrorStatus::UnknownError, "No browsing context available")
484 })
485 }
486
487 fn webview_id(&self) -> WebDriverResult<WebViewId> {
488 self.session()?
489 .current_webview_id()
490 .ok_or_else(|| WebDriverError::new(ErrorStatus::UnknownError, "No webview available"))
491 }
492
493 fn send_input_event_to_embedder(&self, input_event: InputEvent) {
495 let _ = self.send_message_to_embedder(WebDriverCommandMsg::InputEvent(
496 self.verified_webview_id(),
497 input_event,
498 None,
499 ));
500 }
501
502 fn send_blocking_input_event_to_embedder(&mut self, input_event: InputEvent) {
503 let (result_sender, result_receiver) = unbounded();
504 if self
505 .send_message_to_embedder(WebDriverCommandMsg::InputEvent(
506 self.verified_webview_id(),
507 input_event,
508 Some(result_sender),
509 ))
510 .is_ok()
511 {
512 self.pending_input_event_receivers.push(result_receiver);
513 }
514 }
515
516 fn send_message_to_embedder(&self, msg: WebDriverCommandMsg) -> WebDriverResult<()> {
517 self.embedder_sender.send(msg).map_err(|_| {
518 WebDriverError::new(
519 ErrorStatus::UnknownError,
520 "Failed to send message to embedder",
521 )
522 })?;
523 self.event_loop_waker.wake();
524 Ok(())
525 }
526
527 fn add_load_status_sender(&self) -> WebDriverResult<()> {
528 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
529 self.browsing_context_id()?,
530 WebDriverScriptCommand::AddLoadStatusSender(
531 self.webview_id()?,
532 self.load_status_sender.clone(),
533 ),
534 ))
535 }
536
537 fn clear_load_status_sender(&self) -> WebDriverResult<()> {
538 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
539 self.browsing_context_id()?,
540 WebDriverScriptCommand::RemoveLoadStatusSender(self.webview_id()?),
541 ))
542 }
543
544 fn verified_webview_id(&self) -> WebViewId {
546 self.session().unwrap().current_webview_id().unwrap()
547 }
548
549 fn focused_webview_id(&self) -> WebDriverResult<Option<WebViewId>> {
550 let (sender, receiver) = generic_channel::oneshot().unwrap();
551 self.send_message_to_embedder(WebDriverCommandMsg::GetFocusedWebView(sender))?;
552 wait_for_oneshot_response(receiver)
554 }
555
556 fn session(&self) -> WebDriverResult<&WebDriverSession> {
557 match self.session {
558 Some(ref x) => Ok(x),
559 None => Err(WebDriverError::new(
561 ErrorStatus::InvalidSessionId,
562 "Session not created",
563 )),
564 }
565 }
566
567 fn session_mut(&mut self) -> WebDriverResult<&mut WebDriverSession> {
568 match self.session {
569 Some(ref mut x) => Ok(x),
570 None => Err(WebDriverError::new(
572 ErrorStatus::InvalidSessionId,
573 "Session not created",
574 )),
575 }
576 }
577
578 fn handle_new_session(
580 &mut self,
581 parameters: &NewSessionParameters,
582 ) -> WebDriverResult<WebDriverResponse> {
583 if let Ok(value) = env::var("DELAY_AFTER_ACCEPT") {
584 let seconds = value.parse::<u64>().unwrap_or_default();
585 println!("Waiting for {} seconds...", seconds);
586 println!("lldb -p {}", process::id());
587 thread::sleep(Duration::from_secs(seconds));
588 }
589
590 if self.session.is_some() {
593 return Err(WebDriverError::new(
594 ErrorStatus::SessionNotCreated,
595 "Session already created",
596 ));
597 }
598
599 let mut servo_capabilities = ServoCapabilities::new();
604 let processed_capabilities = parameters.match_browser(&mut servo_capabilities)?;
605
606 let mut capabilities = match processed_capabilities {
608 Some(capabilities) => capabilities,
609 None => {
610 return Err(WebDriverError::new(
611 ErrorStatus::SessionNotCreated,
612 "Session not created due to invalid capabilities",
613 ));
614 },
615 };
616
617 let session_id = self.create_session(&mut capabilities, &servo_capabilities)?;
619
620 let response = NewSessionResponse::new(session_id.to_string(), Value::Object(capabilities));
622
623 match self.focused_webview_id()? {
625 Some(webview_id) => {
626 self.session_mut()?.set_webview_id(webview_id);
627 self.wait_until_browsing_context_is_open(BrowsingContextId::from(webview_id))?;
628 self.session_mut()?
629 .set_browsing_context_id(BrowsingContextId::from(webview_id));
630 },
631 None => {
632 let (sender, receiver) = generic_channel::oneshot().unwrap();
635
636 self.send_message_to_embedder(WebDriverCommandMsg::NewWindow(
637 NewWindowTypeHint::Auto,
638 sender,
639 Some(self.load_status_sender.clone()),
640 ))?;
641 let webview_id = receiver
642 .recv()
643 .expect("IPC failure when creating new webview for new session");
644 self.focus_webview(webview_id)?;
645 self.session_mut()?.set_webview_id(webview_id);
646 self.wait_until_browsing_context_is_open(BrowsingContextId::from(webview_id))?;
647 self.session_mut()?
648 .set_browsing_context_id(BrowsingContextId::from(webview_id));
649 let _ = self.wait_document_ready(Some(DEFAULT_PAGE_LOAD_TIMEOUT));
650 },
651 };
652
653 Ok(WebDriverResponse::NewSession(response))
658 }
659
660 fn handle_delete_session(&mut self) -> WebDriverResult<WebDriverResponse> {
662 self.session = None;
664
665 Ok(WebDriverResponse::DeleteSession)
667 }
668
669 fn handle_status(&self) -> WebDriverResult<WebDriverResponse> {
671 Ok(WebDriverResponse::Generic(ValueResponse(
672 if self.session.is_none() {
673 json!({ "ready": true, "message": "Ready for a new session" })
674 } else {
675 json!({ "ready": false, "message": "Not ready for a new session" })
676 },
677 )))
678 }
679
680 fn browsing_context_script_command(
684 &self,
685 cmd_msg: WebDriverScriptCommand,
686 verify: VerifyBrowsingContextIsOpen,
687 ) -> WebDriverResult<()> {
688 let browsing_context_id = self.browsing_context_id()?;
689 if let VerifyBrowsingContextIsOpen::Yes = verify {
690 self.verify_browsing_context_is_open(browsing_context_id)?;
691 }
692 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
693 browsing_context_id,
694 cmd_msg,
695 ))?;
696 Ok(())
697 }
698
699 fn top_level_script_command(
703 &self,
704 cmd_msg: WebDriverScriptCommand,
705 verify: VerifyBrowsingContextIsOpen,
706 ) -> WebDriverResult<()> {
707 let webview_id = self.webview_id()?;
708 if let VerifyBrowsingContextIsOpen::Yes = verify {
709 self.verify_top_level_browsing_context_is_open(webview_id)?;
710 }
711 let browsing_context_id = BrowsingContextId::from(webview_id);
712 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
713 browsing_context_id,
714 cmd_msg,
715 ))?;
716 Ok(())
717 }
718
719 fn handle_get(&mut self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> {
721 let webview_id = self.webview_id()?;
722 self.verify_top_level_browsing_context_is_open(webview_id)?;
725 let url = ServoUrl::parse(¶meters.url)
728 .map(|url| url.into_url())
729 .map_err(|_| WebDriverError::new(ErrorStatus::InvalidArgument, "Invalid URL"))?;
730
731 self.handle_any_user_prompts(webview_id)?;
733
734 let cmd_msg =
735 WebDriverCommandMsg::LoadUrl(webview_id, url, self.load_status_sender.clone());
736 self.send_message_to_embedder(cmd_msg)?;
737
738 self.wait_for_navigation_complete()?;
740
741 self.session_mut()?
743 .set_browsing_context_id(BrowsingContextId::from(webview_id));
744
745 Ok(WebDriverResponse::Void)
746 }
747
748 fn wait_document_ready(&self, timeout: Option<u64>) -> WebDriverResult<WebDriverResponse> {
749 let timeout_channel = match timeout {
750 Some(timeout) => after(Duration::from_millis(timeout)),
751 None => crossbeam_channel::never(),
752 };
753
754 select! {
755 recv(self.load_status_receiver) -> res => {
756 match res {
757 Ok(Ok(WebDriverLoadStatus::Blocked)) => {
759 Ok(WebDriverResponse::Void)
766 },
767 Ok(Ok(WebDriverLoadStatus::Complete)) |
768 Ok(Ok(WebDriverLoadStatus::NavigationStop)) =>
769 Ok(WebDriverResponse::Void)
770 ,
771 _ => Err(WebDriverError::new(
772 ErrorStatus::UnknownError,
773 "Unexpected load status received while waiting for document ready state",
774 )),
775 }
776 },
777 recv(timeout_channel) -> _ => Err(
778 WebDriverError::new(ErrorStatus::Timeout, "Load timed out")
779 ),
780 }
781 }
782
783 fn wait_for_navigation_complete(&self) -> WebDriverResult<WebDriverResponse> {
785 debug!("waiting for load");
786
787 let session = self.session()?;
788
789 if session.page_loading_strategy() == PageLoadStrategy::None {
792 return Ok(WebDriverResponse::Void);
793 }
794
795 if self
798 .verify_browsing_context_is_open(self.browsing_context_id()?)
799 .is_err()
800 {
801 return Ok(WebDriverResponse::Void);
802 }
803
804 let timeout = session.session_timeouts().page_load;
806
807 let result = self.wait_document_ready(timeout);
810 debug!("finished waiting for load with {:?}", result);
811 result
812 }
813
814 fn wait_for_navigation(&self) -> WebDriverResult<WebDriverResponse> {
816 let navigation_status = match self.load_status_receiver.try_recv() {
817 Ok(Ok(status)) => status,
818 Err(crossbeam_channel::TryRecvError::Empty) => {
820 return Ok(WebDriverResponse::Void);
821 },
822 Err(crossbeam_channel::TryRecvError::Disconnected) => {
823 return Err(WebDriverError::new(
824 ErrorStatus::UnknownError,
825 "Load status channel disconnected",
826 ));
827 },
828 Ok(Err(ipc_error)) => {
829 return Err(WebDriverError::new(
830 ErrorStatus::UnknownError,
831 format!("Load status channel ipc error: {ipc_error}"),
832 ));
833 },
834 };
835
836 match navigation_status {
837 WebDriverLoadStatus::NavigationStart => self.wait_for_navigation_complete(),
838 WebDriverLoadStatus::Timeout => Err(WebDriverError::new(
840 ErrorStatus::Timeout,
841 "Navigation timed out",
842 )),
843 WebDriverLoadStatus::Blocked => Ok(WebDriverResponse::Void),
846 WebDriverLoadStatus::NavigationStop | WebDriverLoadStatus::Complete => {
847 unreachable!("Unexpected load status received")
848 },
849 }
850 }
851
852 fn handle_current_url(&self) -> WebDriverResult<WebDriverResponse> {
854 let webview_id = self.webview_id()?;
855
856 self.verify_top_level_browsing_context_is_open(webview_id)?;
859
860 self.handle_any_user_prompts(webview_id)?;
862
863 let (sender, receiver) = generic_channel::channel().unwrap();
864 self.top_level_script_command(
865 WebDriverScriptCommand::GetUrl(sender),
866 VerifyBrowsingContextIsOpen::No,
867 )?;
868
869 let url = wait_for_ipc_response(receiver)?;
870
871 Ok(WebDriverResponse::Generic(ValueResponse(
872 serde_json::to_value(url)?,
873 )))
874 }
875
876 fn handle_window_rect(
878 &self,
879 verify: VerifyBrowsingContextIsOpen,
880 ) -> WebDriverResult<WebDriverResponse> {
881 let (sender, receiver) = generic_channel::oneshot().unwrap();
882 let webview_id = self.webview_id()?;
883 if let VerifyBrowsingContextIsOpen::Yes = verify {
886 self.verify_top_level_browsing_context_is_open(webview_id)?;
887 }
888
889 self.handle_any_user_prompts(webview_id)?;
891
892 self.send_message_to_embedder(WebDriverCommandMsg::GetWindowRect(webview_id, sender))?;
893
894 let window_rect = wait_for_oneshot_response(receiver)?;
895 let window_size_response = WindowRectResponse {
896 x: window_rect.min.x,
897 y: window_rect.min.y,
898 width: window_rect.width(),
899 height: window_rect.height(),
900 };
901 Ok(WebDriverResponse::WindowRect(window_size_response))
902 }
903
904 fn handle_set_window_rect(
906 &self,
907 params: &WindowRectParameters,
908 ) -> WebDriverResult<WebDriverResponse> {
909 let webview_id = self.webview_id()?;
917 self.verify_top_level_browsing_context_is_open(webview_id)?;
920
921 self.handle_any_user_prompts(webview_id)?;
923
924 let current = LazyCell::new(|| {
928 let WebDriverResponse::WindowRect(current) = self
929 .handle_window_rect(VerifyBrowsingContextIsOpen::No)
930 .unwrap()
931 else {
932 unreachable!("handle_window_size() must return WindowRect");
933 };
934 current
935 });
936
937 let (x, y, width, height) = (
938 params.x.unwrap_or_else(|| current.x),
939 params.y.unwrap_or_else(|| current.y),
940 params.width.unwrap_or_else(|| current.width),
941 params.height.unwrap_or_else(|| current.height),
942 );
943 let (sender, receiver) = generic_channel::oneshot().unwrap();
944 self.send_message_to_embedder(WebDriverCommandMsg::SetWindowRect(
950 webview_id,
951 DeviceIndependentIntRect::from_origin_and_size(
952 Point2D::new(x, y),
953 Size2D::new(width, height),
954 ),
955 sender,
956 ))?;
957
958 let window_rect = wait_for_oneshot_response(receiver)?;
959 debug!("Result window_rect: {window_rect:?}");
960 let window_size_response = WindowRectResponse {
961 x: window_rect.min.x,
962 y: window_rect.min.y,
963 width: window_rect.width(),
964 height: window_rect.height(),
965 };
966 Ok(WebDriverResponse::WindowRect(window_size_response))
967 }
968
969 fn handle_maximize_window(&mut self) -> WebDriverResult<WebDriverResponse> {
971 let webview_id = self.webview_id()?;
975 self.verify_top_level_browsing_context_is_open(webview_id)?;
978
979 self.handle_any_user_prompts(self.webview_id()?)?;
981
982 let (sender, receiver) = generic_channel::oneshot().unwrap();
988 self.send_message_to_embedder(WebDriverCommandMsg::MaximizeWebView(webview_id, sender))?;
989
990 let window_rect = wait_for_oneshot_response(receiver)?;
991 debug!("Result window_rect: {window_rect:?}");
992 let window_size_response = WindowRectResponse {
993 x: window_rect.min.x,
994 y: window_rect.min.y,
995 width: window_rect.width(),
996 height: window_rect.height(),
997 };
998 Ok(WebDriverResponse::WindowRect(window_size_response))
999 }
1000
1001 fn handle_is_enabled(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1002 let browsing_context = self.browsing_context_id()?;
1005 self.verify_browsing_context_is_open(browsing_context)?;
1006
1007 let webview_id = self.webview_id()?;
1009 self.handle_any_user_prompts(webview_id)?;
1010
1011 let (sender, receiver) = generic_channel::channel().unwrap();
1012 self.browsing_context_script_command(
1013 WebDriverScriptCommand::IsEnabled(element.to_string(), sender),
1014 VerifyBrowsingContextIsOpen::No,
1015 )?;
1016
1017 Ok(WebDriverResponse::Generic(ValueResponse(
1018 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1019 )))
1020 }
1021
1022 fn handle_is_selected(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1023 let browsing_context = self.browsing_context_id()?;
1026 self.verify_browsing_context_is_open(browsing_context)?;
1027
1028 let webview_id = self.webview_id()?;
1030 self.handle_any_user_prompts(webview_id)?;
1031
1032 let (sender, receiver) = generic_channel::channel().unwrap();
1033 self.browsing_context_script_command(
1034 WebDriverScriptCommand::IsSelected(element.to_string(), sender),
1035 VerifyBrowsingContextIsOpen::No,
1036 )?;
1037
1038 Ok(WebDriverResponse::Generic(ValueResponse(
1039 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1040 )))
1041 }
1042
1043 fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> {
1045 let webview_id = self.webview_id()?;
1046 self.verify_top_level_browsing_context_is_open(webview_id)?;
1049
1050 self.handle_any_user_prompts(webview_id)?;
1052
1053 self.send_message_to_embedder(WebDriverCommandMsg::GoBack(
1054 webview_id,
1055 self.load_status_sender.clone(),
1056 ))?;
1057 self.wait_for_navigation_complete()
1058 }
1059
1060 fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> {
1062 let webview_id = self.webview_id()?;
1063 self.verify_top_level_browsing_context_is_open(webview_id)?;
1066
1067 self.handle_any_user_prompts(webview_id)?;
1069
1070 self.send_message_to_embedder(WebDriverCommandMsg::GoForward(
1071 webview_id,
1072 self.load_status_sender.clone(),
1073 ))?;
1074 self.wait_for_navigation_complete()
1075 }
1076
1077 fn handle_refresh(&mut self) -> WebDriverResult<WebDriverResponse> {
1079 let webview_id = self.webview_id()?;
1080 self.verify_top_level_browsing_context_is_open(webview_id)?;
1083
1084 self.handle_any_user_prompts(webview_id)?;
1086
1087 let cmd_msg = WebDriverCommandMsg::Refresh(webview_id, self.load_status_sender.clone());
1088 self.send_message_to_embedder(cmd_msg)?;
1089
1090 self.wait_for_navigation_complete()?;
1092
1093 self.session_mut()?
1095 .set_browsing_context_id(BrowsingContextId::from(webview_id));
1096
1097 Ok(WebDriverResponse::Void)
1098 }
1099
1100 fn handle_title(&self) -> WebDriverResult<WebDriverResponse> {
1102 let webview_id = self.webview_id()?;
1103
1104 self.verify_top_level_browsing_context_is_open(webview_id)?;
1107 self.handle_any_user_prompts(webview_id)?;
1109
1110 let (sender, receiver) = generic_channel::channel().unwrap();
1111
1112 self.top_level_script_command(
1113 WebDriverScriptCommand::GetTitle(sender),
1114 VerifyBrowsingContextIsOpen::No,
1115 )?;
1116
1117 let title = wait_for_ipc_response(receiver)?;
1120 Ok(WebDriverResponse::Generic(ValueResponse(
1121 serde_json::to_value(title)?,
1122 )))
1123 }
1124
1125 fn handle_window_handle(&mut self) -> WebDriverResult<WebDriverResponse> {
1127 let webview_id = self.webview_id()?;
1128
1129 self.verify_top_level_browsing_context_is_open(webview_id)?;
1132
1133 let handle = self
1135 .get_window_handle(webview_id)
1136 .expect("Failed to get window handle of an existing webview");
1137
1138 Ok(WebDriverResponse::Generic(ValueResponse(
1139 serde_json::to_value(handle)?,
1140 )))
1141 }
1142
1143 fn handle_window_handles(&mut self) -> WebDriverResult<WebDriverResponse> {
1145 let mut handles = self.get_window_handles();
1146 handles.sort_unstable();
1147
1148 Ok(WebDriverResponse::Generic(ValueResponse(
1149 serde_json::to_value(handles)?,
1150 )))
1151 }
1152
1153 fn get_window_handle(&mut self, webview_id: WebViewId) -> Option<String> {
1154 self.get_window_handles()
1155 .iter()
1156 .find(|id| id == &&webview_id.to_string())
1157 .cloned()
1158 }
1159
1160 fn get_window_handles(&self) -> Vec<String> {
1161 self.get_all_webview_ids()
1162 .into_iter()
1163 .map(|id| id.to_string())
1164 .collect()
1165 }
1166
1167 fn get_all_webview_ids(&self) -> Vec<WebViewId> {
1168 let (sender, receiver) = generic_channel::oneshot().unwrap();
1169 self.send_message_to_embedder(WebDriverCommandMsg::GetAllWebViews(sender))
1170 .unwrap();
1171 wait_for_oneshot_response(receiver).unwrap_or_default()
1172 }
1173
1174 fn handle_close_window(&mut self) -> WebDriverResult<WebDriverResponse> {
1176 let webview_id = self.webview_id()?;
1177 self.verify_top_level_browsing_context_is_open(webview_id)?;
1180
1181 self.handle_any_user_prompts(webview_id)?;
1183
1184 let (sender, receiver) = generic_channel::oneshot().unwrap();
1186
1187 let cmd_msg = WebDriverCommandMsg::CloseWebView(webview_id, sender);
1188 self.send_message_to_embedder(cmd_msg)?;
1189
1190 wait_for_oneshot_response(receiver)?;
1191
1192 let window_handles = self.get_window_handles();
1194
1195 if window_handles.is_empty() {
1196 self.session = None;
1197 }
1198
1199 Ok(WebDriverResponse::CloseWindow(CloseWindowResponse(
1201 window_handles,
1202 )))
1203 }
1204
1205 fn handle_new_window(
1207 &mut self,
1208 parameters: &NewWindowParameters,
1209 ) -> WebDriverResult<WebDriverResponse> {
1210 let (sender, receiver) = generic_channel::oneshot().unwrap();
1211
1212 let webview_id = self.webview_id()?;
1213
1214 self.verify_top_level_browsing_context_is_open(webview_id)?;
1217
1218 self.handle_any_user_prompts(webview_id)?;
1220
1221 let type_hint = match parameters.type_hint.as_deref() {
1224 Some("tab") => NewWindowTypeHint::Tab,
1225 Some("window") => NewWindowTypeHint::Window,
1226 _ => NewWindowTypeHint::Auto,
1227 };
1228
1229 self.send_message_to_embedder(WebDriverCommandMsg::NewWindow(
1241 type_hint,
1242 sender,
1243 Some(self.load_status_sender.clone()),
1244 ))?;
1245
1246 if let Ok(webview_id) = receiver.recv() {
1247 let _ = self.wait_for_navigation_complete();
1248 let handle = self
1249 .get_window_handle(webview_id)
1250 .expect("Failed to get window handle of an existing webview");
1251
1252 Ok(WebDriverResponse::NewWindow(NewWindowResponse {
1253 handle,
1254 typ: "tab".to_string(),
1255 }))
1256 } else {
1257 Err(WebDriverError::new(
1258 ErrorStatus::UnknownError,
1259 "No webview ID received",
1260 ))
1261 }
1262 }
1263
1264 fn handle_switch_to_frame(
1266 &mut self,
1267 parameters: &SwitchToFrameParameters,
1268 ) -> WebDriverResult<WebDriverResponse> {
1269 use webdriver::common::FrameId;
1270 let frame_id = match parameters.id {
1271 FrameId::Top => {
1273 let webview_id = self.webview_id()?;
1274 self.verify_top_level_browsing_context_is_open(webview_id)?;
1277 self.handle_any_user_prompts(webview_id)?;
1279 let browsing_context_id = BrowsingContextId::from(webview_id);
1282 self.session_mut()?
1283 .set_browsing_context_id(browsing_context_id);
1284
1285 self.focus_browsing_context(browsing_context_id)?;
1289 return Ok(WebDriverResponse::Void);
1290 },
1291 FrameId::Short(ref x) => {
1293 WebDriverFrameId::Short(*x)
1297 },
1298 FrameId::Element(ref x) => WebDriverFrameId::Element(x.to_string()),
1299 };
1300
1301 self.switch_to_frame(frame_id)
1302 }
1303
1304 fn handle_switch_to_parent_frame(&mut self) -> WebDriverResult<WebDriverResponse> {
1306 let webview_id = self.webview_id()?;
1307 let browsing_context = self.browsing_context_id()?;
1308
1309 if browsing_context == webview_id {
1311 self.verify_browsing_context_is_open(browsing_context)?;
1314 return Ok(WebDriverResponse::Void);
1316 }
1317
1318 let (sender, receiver) = generic_channel::channel().unwrap();
1321 let cmd = WebDriverScriptCommand::GetParentFrameId(sender);
1322 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1323
1324 self.handle_any_user_prompts(webview_id)?;
1326
1327 let browsing_context_id = wait_for_ipc_response_flatten(receiver)?;
1330 self.session_mut()?
1331 .set_browsing_context_id(browsing_context_id);
1332 self.focus_browsing_context(browsing_context_id)?;
1336 Ok(WebDriverResponse::Void)
1337 }
1338
1339 fn handle_switch_to_window(
1341 &mut self,
1342 parameters: &SwitchToWindowParameters,
1343 ) -> WebDriverResult<WebDriverResponse> {
1344 let Some(webview_id) = self
1345 .get_all_webview_ids()
1346 .into_iter()
1347 .find(|id| id.to_string() == parameters.handle)
1348 else {
1349 return Err(WebDriverError::new(
1350 ErrorStatus::NoSuchWindow,
1351 "No such window while switching to window",
1352 ));
1353 };
1354
1355 let session = self.session_mut()?;
1356 session.set_webview_id(webview_id);
1357 session.set_browsing_context_id(BrowsingContextId::from(webview_id));
1358
1359 self.focus_webview(webview_id)?;
1363
1364 Ok(WebDriverResponse::Void)
1365 }
1366
1367 fn switch_to_frame(
1368 &mut self,
1369 frame_id: WebDriverFrameId,
1370 ) -> WebDriverResult<WebDriverResponse> {
1371 let (sender, receiver) = generic_channel::channel().unwrap();
1372 let cmd = WebDriverScriptCommand::GetBrowsingContextId(frame_id, sender);
1373 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1374 self.handle_any_user_prompts(self.webview_id()?)?;
1375
1376 let browsing_context_id = wait_for_ipc_response_flatten(receiver)?;
1377 self.session_mut()?
1378 .set_browsing_context_id(browsing_context_id);
1379 self.focus_browsing_context(browsing_context_id)?;
1383 Ok(WebDriverResponse::Void)
1384 }
1385
1386 fn handle_find_element(
1388 &self,
1389 parameters: &LocatorParameters,
1390 ) -> WebDriverResult<WebDriverResponse> {
1391 let res = self.handle_find_elements(parameters)?;
1393 unwrap_first_element_response(res)
1396 }
1397
1398 fn implicit_wait<T>(
1401 &self,
1402 callback: impl Fn() -> Result<(bool, T), (bool, WebDriverError)>,
1403 ) -> Result<T, WebDriverError> {
1404 let now = Instant::now();
1405 let (implicit_wait, sleep_interval) = {
1406 let timeouts = self.session()?.session_timeouts();
1407 (
1408 timeouts
1409 .implicit_wait
1410 .map_or(Duration::MAX, Duration::from_millis),
1411 Duration::from_millis(timeouts.sleep_interval),
1412 )
1413 };
1414
1415 loop {
1416 match callback() {
1417 Ok((can_early_return, value)) => {
1418 if can_early_return || now.elapsed() >= implicit_wait {
1419 return Ok(value);
1420 }
1421 },
1422 Err((can_early_return, error)) => {
1423 if can_early_return || now.elapsed() >= implicit_wait {
1424 return Err(error);
1425 }
1426 },
1427 }
1428 sleep(sleep_interval);
1429 }
1430 }
1431
1432 fn handle_find_elements(
1434 &self,
1435 parameters: &LocatorParameters,
1436 ) -> WebDriverResult<WebDriverResponse> {
1437 if parameters.value.is_empty() {
1439 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1440 }
1441 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1444
1445 self.handle_any_user_prompts(self.webview_id()?)?;
1447
1448 self.implicit_wait(|| {
1449 let (sender, receiver) = generic_channel::channel().unwrap();
1450 let cmd = match parameters.using {
1451 LocatorStrategy::CSSSelector => WebDriverScriptCommand::FindElementsCSSSelector(
1452 parameters.value.clone(),
1453 sender,
1454 ),
1455 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1456 WebDriverScriptCommand::FindElementsLinkText(
1457 parameters.value.clone(),
1458 parameters.using == LocatorStrategy::PartialLinkText,
1459 sender,
1460 )
1461 },
1462 LocatorStrategy::TagName => {
1463 WebDriverScriptCommand::FindElementsTagName(parameters.value.clone(), sender)
1464 },
1465 LocatorStrategy::XPath => WebDriverScriptCommand::FindElementsXpathSelector(
1466 parameters.value.clone(),
1467 sender,
1468 ),
1469 };
1470 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)
1471 .map_err(|error| (ImplicitWait::Return.into(), error))?;
1472 wait_for_ipc_response_flatten(receiver)
1473 .map(|value| (!value.is_empty(), value))
1474 .map_err(|error| (ImplicitWait::Return.into(), error))
1475 })
1476 .and_then(|response| {
1477 let resp_value: Vec<WebElement> = response.into_iter().map(WebElement).collect();
1478 Ok(WebDriverResponse::Generic(ValueResponse(
1479 serde_json::to_value(resp_value)?,
1480 )))
1481 })
1482 }
1483
1484 fn handle_find_element_from_element(
1486 &self,
1487 element: &WebElement,
1488 parameters: &LocatorParameters,
1489 ) -> WebDriverResult<WebDriverResponse> {
1490 let res = self.handle_find_elements_from_element(element, parameters)?;
1492 unwrap_first_element_response(res)
1495 }
1496
1497 fn handle_find_elements_from_element(
1499 &self,
1500 element: &WebElement,
1501 parameters: &LocatorParameters,
1502 ) -> WebDriverResult<WebDriverResponse> {
1503 if parameters.value.is_empty() {
1505 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1506 }
1507 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1510
1511 self.handle_any_user_prompts(self.webview_id()?)?;
1513
1514 self.implicit_wait(|| {
1515 let (sender, receiver) = generic_channel::channel().unwrap();
1516
1517 let cmd = match parameters.using {
1518 LocatorStrategy::CSSSelector => {
1519 WebDriverScriptCommand::FindElementElementsCSSSelector(
1520 parameters.value.clone(),
1521 element.to_string(),
1522 sender,
1523 )
1524 },
1525 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1526 WebDriverScriptCommand::FindElementElementsLinkText(
1527 parameters.value.clone(),
1528 element.to_string(),
1529 parameters.using == LocatorStrategy::PartialLinkText,
1530 sender,
1531 )
1532 },
1533 LocatorStrategy::TagName => WebDriverScriptCommand::FindElementElementsTagName(
1534 parameters.value.clone(),
1535 element.to_string(),
1536 sender,
1537 ),
1538 LocatorStrategy::XPath => WebDriverScriptCommand::FindElementElementsXPathSelector(
1539 parameters.value.clone(),
1540 element.to_string(),
1541 sender,
1542 ),
1543 };
1544 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)
1545 .map_err(|error| (ImplicitWait::Return.into(), error))?;
1546 wait_for_ipc_response_flatten(receiver)
1547 .map(|value| (!value.is_empty(), value))
1548 .map_err(|error| (ImplicitWait::Return.into(), error))
1549 })
1550 .and_then(|response| {
1551 let resp_value: Vec<Value> = response
1552 .into_iter()
1553 .map(|x| serde_json::to_value(WebElement(x)).unwrap())
1554 .collect();
1555 Ok(WebDriverResponse::Generic(ValueResponse(
1556 serde_json::to_value(resp_value)?,
1557 )))
1558 })
1559 }
1560
1561 fn handle_find_elements_from_shadow_root(
1563 &self,
1564 shadow_root: &ShadowRoot,
1565 parameters: &LocatorParameters,
1566 ) -> WebDriverResult<WebDriverResponse> {
1567 if parameters.value.is_empty() {
1569 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1570 }
1571
1572 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1575
1576 self.handle_any_user_prompts(self.webview_id()?)?;
1578
1579 self.implicit_wait(|| {
1580 let (sender, receiver) = generic_channel::channel().unwrap();
1581
1582 let cmd = match parameters.using {
1583 LocatorStrategy::CSSSelector => {
1584 WebDriverScriptCommand::FindShadowElementsCSSSelector(
1585 parameters.value.clone(),
1586 shadow_root.to_string(),
1587 sender,
1588 )
1589 },
1590 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1591 WebDriverScriptCommand::FindShadowElementsLinkText(
1592 parameters.value.clone(),
1593 shadow_root.to_string(),
1594 parameters.using == LocatorStrategy::PartialLinkText,
1595 sender,
1596 )
1597 },
1598 LocatorStrategy::TagName => WebDriverScriptCommand::FindShadowElementsTagName(
1599 parameters.value.clone(),
1600 shadow_root.to_string(),
1601 sender,
1602 ),
1603 LocatorStrategy::XPath => WebDriverScriptCommand::FindShadowElementsXPathSelector(
1604 parameters.value.clone(),
1605 shadow_root.to_string(),
1606 sender,
1607 ),
1608 };
1609 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)
1610 .map_err(|error| (ImplicitWait::Return.into(), error))?;
1611 wait_for_ipc_response_flatten(receiver)
1612 .map(|value| (!value.is_empty(), value))
1613 .map_err(|error| (ImplicitWait::Return.into(), error))
1614 })
1615 .and_then(|response| {
1616 let resp_value: Vec<Value> = response
1617 .into_iter()
1618 .map(|x| serde_json::to_value(WebElement(x)).unwrap())
1619 .collect();
1620 Ok(WebDriverResponse::Generic(ValueResponse(
1621 serde_json::to_value(resp_value)?,
1622 )))
1623 })
1624 }
1625
1626 fn handle_find_element_from_shadow_root(
1628 &self,
1629 shadow_root: &ShadowRoot,
1630 parameters: &LocatorParameters,
1631 ) -> WebDriverResult<WebDriverResponse> {
1632 let res = self.handle_find_elements_from_shadow_root(shadow_root, parameters)?;
1634 unwrap_first_element_response(res)
1637 }
1638
1639 fn handle_get_shadow_root(&self, element: WebElement) -> WebDriverResult<WebDriverResponse> {
1641 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1644 self.handle_any_user_prompts(self.webview_id()?)?;
1646 let (sender, receiver) = generic_channel::channel().unwrap();
1647 let cmd = WebDriverScriptCommand::GetElementShadowRoot(element.to_string(), sender);
1648 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1649 let Some(value) = wait_for_ipc_response_flatten(receiver)? else {
1651 return Err(WebDriverError::new(ErrorStatus::NoSuchShadowRoot, ""));
1652 };
1653 Ok(WebDriverResponse::Generic(ValueResponse(
1654 serde_json::to_value(ShadowRoot(value))?,
1655 )))
1656 }
1657
1658 fn handle_element_rect(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1660 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1663 self.handle_any_user_prompts(self.webview_id()?)?;
1665 let (sender, receiver) = generic_channel::channel().unwrap();
1666 let cmd = WebDriverScriptCommand::GetElementRect(element.to_string(), sender);
1667 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1668 let rect = wait_for_ipc_response_flatten(receiver)?;
1669 let response = ElementRectResponse {
1670 x: rect.origin.x,
1671 y: rect.origin.y,
1672 width: rect.size.width,
1673 height: rect.size.height,
1674 };
1675 Ok(WebDriverResponse::ElementRect(response))
1676 }
1677
1678 fn handle_element_text(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1680 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1683 self.handle_any_user_prompts(self.webview_id()?)?;
1685 let (sender, receiver) = generic_channel::channel().unwrap();
1686 let cmd = WebDriverScriptCommand::GetElementText(element.to_string(), sender);
1687 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1688 Ok(WebDriverResponse::Generic(ValueResponse(
1689 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1690 )))
1691 }
1692
1693 fn handle_active_element(&self) -> WebDriverResult<WebDriverResponse> {
1695 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1698 self.handle_any_user_prompts(self.webview_id()?)?;
1700 let (sender, receiver) = generic_channel::channel().unwrap();
1701 let cmd = WebDriverScriptCommand::GetActiveElement(sender);
1702 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1703 let value =
1704 wait_for_ipc_response(receiver)?.map(|x| serde_json::to_value(WebElement(x)).unwrap());
1705 if value.is_some() {
1709 Ok(WebDriverResponse::Generic(ValueResponse(
1710 serde_json::to_value(value)?,
1711 )))
1712 } else {
1713 Err(WebDriverError::new(
1714 ErrorStatus::NoSuchElement,
1715 "No active element found",
1716 ))
1717 }
1718 }
1719
1720 fn handle_computed_role(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1722 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1725 self.handle_any_user_prompts(self.webview_id()?)?;
1727 let (sender, receiver) = generic_channel::channel().unwrap();
1728 let cmd = WebDriverScriptCommand::GetComputedRole(element.to_string(), sender);
1729 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1730 Ok(WebDriverResponse::Generic(ValueResponse(
1731 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1732 )))
1733 }
1734
1735 fn handle_element_tag_name(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1737 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1740 self.handle_any_user_prompts(self.webview_id()?)?;
1742 let (sender, receiver) = generic_channel::channel().unwrap();
1743 let cmd = WebDriverScriptCommand::GetElementTagName(element.to_string(), sender);
1744 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1745 Ok(WebDriverResponse::Generic(ValueResponse(
1746 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1747 )))
1748 }
1749
1750 fn handle_element_attribute(
1752 &self,
1753 element: &WebElement,
1754 name: &str,
1755 ) -> WebDriverResult<WebDriverResponse> {
1756 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1759 self.handle_any_user_prompts(self.webview_id()?)?;
1761 let (sender, receiver) = generic_channel::channel().unwrap();
1762 let cmd = WebDriverScriptCommand::GetElementAttribute(
1763 element.to_string(),
1764 name.to_owned(),
1765 sender,
1766 );
1767 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1768 Ok(WebDriverResponse::Generic(ValueResponse(
1769 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1770 )))
1771 }
1772
1773 fn handle_element_property(
1775 &self,
1776 element: &WebElement,
1777 name: &str,
1778 ) -> WebDriverResult<WebDriverResponse> {
1779 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1782 self.handle_any_user_prompts(self.webview_id()?)?;
1784 let (sender, receiver) = generic_channel::channel().unwrap();
1785
1786 let cmd = WebDriverScriptCommand::GetElementProperty(
1787 element.to_string(),
1788 name.to_owned(),
1789 sender,
1790 );
1791 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1792
1793 Ok(WebDriverResponse::Generic(ValueResponse(
1794 serde_json::to_value(SendableJSValue(wait_for_ipc_response_flatten(receiver)?))?,
1795 )))
1796 }
1797
1798 fn handle_element_css(
1800 &self,
1801 element: &WebElement,
1802 name: &str,
1803 ) -> WebDriverResult<WebDriverResponse> {
1804 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1807 self.handle_any_user_prompts(self.webview_id()?)?;
1809 let (sender, receiver) = generic_channel::channel().unwrap();
1810 let cmd =
1811 WebDriverScriptCommand::GetElementCSS(element.to_string(), name.to_owned(), sender);
1812 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1813 Ok(WebDriverResponse::Generic(ValueResponse(
1814 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1815 )))
1816 }
1817
1818 fn handle_get_cookies(&self) -> WebDriverResult<WebDriverResponse> {
1820 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1823 self.handle_any_user_prompts(self.webview_id()?)?;
1825 let (sender, receiver) = generic_channel::channel().unwrap();
1826 let cmd = WebDriverScriptCommand::GetCookies(sender);
1827 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1828 let cookies = wait_for_ipc_response_flatten(receiver)?;
1829 let response = cookies
1830 .into_iter()
1831 .map(|cookie| cookie_msg_to_cookie(cookie.into_inner()))
1832 .collect::<Vec<Cookie>>();
1833 Ok(WebDriverResponse::Cookies(CookiesResponse(response)))
1834 }
1835
1836 fn handle_get_cookie(&self, name: String) -> WebDriverResult<WebDriverResponse> {
1838 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1841 self.handle_any_user_prompts(self.webview_id()?)?;
1843 let (sender, receiver) = generic_channel::channel().unwrap();
1844 let cmd = WebDriverScriptCommand::GetCookie(name, sender);
1845 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1846 let cookies = wait_for_ipc_response_flatten(receiver)?;
1847 let Some(response) = cookies
1848 .into_iter()
1849 .map(|cookie| cookie_msg_to_cookie(cookie.into_inner()))
1850 .next()
1851 else {
1852 return Err(WebDriverError::new(ErrorStatus::NoSuchCookie, ""));
1853 };
1854 Ok(WebDriverResponse::Cookie(CookieResponse(response)))
1855 }
1856
1857 fn handle_add_cookie(
1859 &self,
1860 params: &AddCookieParameters,
1861 ) -> WebDriverResult<WebDriverResponse> {
1862 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1865 self.handle_any_user_prompts(self.webview_id()?)?;
1867 let (sender, receiver) = generic_channel::channel().unwrap();
1868
1869 if let Some(ref expiry) = params.expiry &&
1873 expiry.0 > MAXIMUM_SAFE_INTEGER
1874 {
1875 return Err(WebDriverError::new(
1876 ErrorStatus::InvalidArgument,
1877 "expiry time greater than maximum safe integer",
1878 ));
1879 }
1880
1881 let mut cookie_builder =
1882 CookieBuilder::new(params.name.to_owned(), params.value.to_owned())
1883 .secure(params.secure)
1884 .http_only(params.httpOnly);
1885 if let Some(ref domain) = params.domain {
1886 cookie_builder = cookie_builder.domain(domain.clone());
1887 }
1888 if let Some(ref path) = params.path {
1889 cookie_builder = cookie_builder.path(path.clone());
1890 }
1891 if let Some(ref expiry) = params.expiry {
1892 let datetime = OffsetDateTime::from_unix_timestamp(expiry.0 as i64).map_err(|_| {
1893 WebDriverError::new(ErrorStatus::InvalidArgument, "invalid expiry time")
1894 })?;
1895 cookie_builder = cookie_builder.expires(datetime);
1896 }
1897 if let Some(ref same_site) = params.sameSite {
1898 cookie_builder = match same_site.as_str() {
1899 "None" => Ok(cookie_builder.same_site(SameSite::None)),
1900 "Lax" => Ok(cookie_builder.same_site(SameSite::Lax)),
1901 "Strict" => Ok(cookie_builder.same_site(SameSite::Strict)),
1902 _ => Err(WebDriverError::new(
1903 ErrorStatus::InvalidArgument,
1904 "invalid argument",
1905 )),
1906 }?;
1907 }
1908
1909 let cmd = WebDriverScriptCommand::AddCookie(cookie_builder.build(), sender);
1910 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1911 wait_for_ipc_response_flatten(receiver)?;
1912 Ok(WebDriverResponse::Void)
1913 }
1914
1915 fn handle_delete_cookie(&self, name: String) -> WebDriverResult<WebDriverResponse> {
1917 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1920 self.handle_any_user_prompts(self.webview_id()?)?;
1922 let (sender, receiver) = generic_channel::channel().unwrap();
1923 let cmd = WebDriverScriptCommand::DeleteCookie(name, sender);
1924 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1925 wait_for_ipc_response_flatten(receiver)?;
1926 Ok(WebDriverResponse::Void)
1927 }
1928
1929 fn handle_delete_cookies(&self) -> WebDriverResult<WebDriverResponse> {
1931 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1934 self.handle_any_user_prompts(self.webview_id()?)?;
1936 let (sender, receiver) = generic_channel::channel().unwrap();
1937 let cmd = WebDriverScriptCommand::DeleteCookies(sender);
1938 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1939 wait_for_ipc_response_flatten(receiver)?;
1940 Ok(WebDriverResponse::Void)
1941 }
1942
1943 fn handle_get_timeouts(&mut self) -> WebDriverResult<WebDriverResponse> {
1945 let timeouts = self.session()?.session_timeouts();
1946
1947 let timeouts = TimeoutsResponse {
1952 script: timeouts.script,
1953 page_load: timeouts.page_load.unwrap_or(DEFAULT_PAGE_LOAD_TIMEOUT),
1954 implicit: timeouts.implicit_wait.unwrap_or(DEFAULT_IMPLICIT_WAIT),
1955 };
1956
1957 Ok(WebDriverResponse::Timeouts(timeouts))
1958 }
1959
1960 fn handle_set_timeouts(
1962 &mut self,
1963 parameters: &TimeoutsParameters,
1964 ) -> WebDriverResult<WebDriverResponse> {
1965 let session = self.session_mut()?;
1966
1967 if let Some(timeout) = parameters.script {
1968 session.session_timeouts_mut().script = timeout;
1969 }
1970 if let Some(timeout) = parameters.page_load {
1971 session.session_timeouts_mut().page_load = Some(timeout);
1972 }
1973 if let Some(timeout) = parameters.implicit {
1974 session.session_timeouts_mut().implicit_wait = Some(timeout);
1975 }
1976
1977 Ok(WebDriverResponse::Void)
1978 }
1979
1980 fn handle_get_page_source(&self) -> WebDriverResult<WebDriverResponse> {
1982 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1985 self.handle_any_user_prompts(self.webview_id()?)?;
1987 let (sender, receiver) = generic_channel::channel().unwrap();
1988
1989 let cmd = WebDriverScriptCommand::GetPageSource(sender);
1990 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1991
1992 Ok(WebDriverResponse::Generic(ValueResponse(
1993 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1994 )))
1995 }
1996
1997 fn handle_perform_actions(
1999 &mut self,
2000 parameters: ActionsParameters,
2001 ) -> WebDriverResult<WebDriverResponse> {
2002 let browsing_context = self.browsing_context_id()?;
2003 self.verify_browsing_context_is_open(browsing_context)?;
2006
2007 self.handle_any_user_prompts(self.webview_id()?)?;
2009
2010 let actions_by_tick = self
2012 .extract_an_action_sequence(parameters.actions)
2013 .map_err(|error| WebDriverError::new(error, ""))?;
2014
2015 match self.dispatch_actions(actions_by_tick, browsing_context) {
2017 Ok(_) => Ok(WebDriverResponse::Void),
2018 Err(error) => Err(WebDriverError::new(error, "")),
2019 }
2020 }
2021
2022 fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
2024 let browsing_context_id = self.browsing_context_id()?;
2025 self.verify_browsing_context_is_open(browsing_context_id)?;
2028
2029 self.handle_any_user_prompts(self.webview_id()?)?;
2031
2032 let undo_actions = self
2039 .session_mut()?
2040 .input_cancel_list
2041 .drain(..)
2042 .rev()
2043 .map(|(id, action_item)| Vec::from([(id, action_item)]))
2044 .collect();
2045 if let Err(err) = self.dispatch_actions(undo_actions, browsing_context_id) {
2047 return Err(WebDriverError::new(err, "Failed to dispatch undo actions"));
2048 }
2049
2050 self.session_mut()?.input_state_table.clear();
2052
2053 Ok(WebDriverResponse::Void)
2054 }
2055
2056 fn handle_execute_script(
2058 &self,
2059 parameters: JavascriptCommandParameters,
2060 ) -> WebDriverResult<WebDriverResponse> {
2061 let (func_body, args_string) = self.extract_script_arguments(parameters)?;
2064
2065 let script = format!(
2069 r#"(async function(__wd_eid) {{
2070 try {{
2071 let result = (async function() {{
2072 {func_body}
2073 }})({});
2074 let value = await result;
2075 if (window.__wd_eid === __wd_eid) {{
2076 window.webdriverCallback(value);
2077 }}
2078 }} catch (err) {{
2079 if (window.__wd_eid === __wd_eid) {{
2080 window.webdriverException(err);
2081 }}
2082 }}
2083 }})(window.__wd_eid = (window.__wd_eid || 0) + 1);"#,
2084 args_string.join(", ")
2085 );
2086
2087 debug!("{}", script);
2088 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2091
2092 self.handle_any_user_prompts(self.webview_id()?)?;
2094
2095 let (sender, receiver) = generic_channel::channel().unwrap();
2096 let cmd = WebDriverScriptCommand::ExecuteScriptWithCallback(script, sender);
2097
2098 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2099
2100 let timeout_duration = self
2101 .session()?
2102 .session_timeouts()
2103 .script
2104 .map_or(Duration::MAX, Duration::from_millis);
2105
2106 let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2107
2108 self.javascript_evaluation_result_to_webdriver_response(result)
2109 }
2110
2111 fn handle_execute_async_script(
2112 &self,
2113 parameters: JavascriptCommandParameters,
2114 ) -> WebDriverResult<WebDriverResponse> {
2115 let (function_body, mut args_string) = self.extract_script_arguments(parameters)?;
2118 args_string.push("resolve".to_string());
2119
2120 let joined_args = args_string.join(", ");
2121 let script = format!(
2122 r#"(async function(__wd_eid) {{
2123 try {{
2124 let result = new Promise(function(resolve, reject) {{
2125 (async function() {{
2126 {function_body}
2127 }})({joined_args})
2128 .catch(reject)
2129 }});
2130 let value = await result;
2131 if (window.__wd_eid === __wd_eid) {{
2132 window.webdriverCallback(value);
2133 }}
2134 }} catch (err) {{
2135 if (window.__wd_eid === __wd_eid) {{
2136 window.webdriverException(err);
2137 }}
2138 }}
2139 }})(window.__wd_eid = (window.__wd_eid || 0) + 1);"#,
2140 );
2141 debug!("{}", script);
2142
2143 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2146
2147 self.handle_any_user_prompts(self.webview_id()?)?;
2149
2150 let (sender, receiver) = generic_channel::channel().unwrap();
2151 self.browsing_context_script_command(
2152 WebDriverScriptCommand::ExecuteScriptWithCallback(script, sender),
2153 VerifyBrowsingContextIsOpen::No,
2154 )?;
2155
2156 let timeout_duration = self
2157 .session()?
2158 .session_timeouts()
2159 .script
2160 .map_or(Duration::MAX, Duration::from_millis);
2161 let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2162
2163 self.javascript_evaluation_result_to_webdriver_response(result)
2164 }
2165
2166 fn javascript_evaluation_result_to_webdriver_response(
2167 &self,
2168 result: WebDriverJSResult,
2169 ) -> WebDriverResult<WebDriverResponse> {
2170 match result {
2171 Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
2172 serde_json::to_value(SendableJSValue(value))?,
2173 ))),
2174 Err(error) => {
2175 let message = format!("{error:?}");
2176 let status = match error {
2177 JavaScriptEvaluationError::DocumentNotFound => ErrorStatus::NoSuchWindow,
2178 JavaScriptEvaluationError::CompilationFailure => ErrorStatus::JavascriptError,
2179 JavaScriptEvaluationError::EvaluationFailure(Some(error_info)) => {
2180 return Err(WebDriverError::new_with_data(
2181 ErrorStatus::JavascriptError,
2182 error_info.message,
2183 None,
2184 error_info.stack,
2185 ));
2186 },
2187 JavaScriptEvaluationError::EvaluationFailure(None) => {
2188 ErrorStatus::JavascriptError
2189 },
2190 JavaScriptEvaluationError::InternalError => ErrorStatus::JavascriptError,
2191 JavaScriptEvaluationError::SerializationError(serialization_error) => {
2192 match serialization_error {
2193 JavaScriptEvaluationResultSerializationError::DetachedShadowRoot => {
2194 ErrorStatus::DetachedShadowRoot
2195 },
2196 JavaScriptEvaluationResultSerializationError::OtherJavaScriptError => {
2197 ErrorStatus::JavascriptError
2198 },
2199 JavaScriptEvaluationResultSerializationError::StaleElementReference => {
2200 ErrorStatus::StaleElementReference
2201 },
2202 JavaScriptEvaluationResultSerializationError::UnknownType => {
2203 ErrorStatus::UnsupportedOperation
2204 },
2205 }
2206 },
2207 JavaScriptEvaluationError::WebViewNotReady => ErrorStatus::NoSuchWindow,
2208 };
2209 Err(WebDriverError::new(status, message))
2210 },
2211 }
2212 }
2213
2214 fn handle_element_send_keys(
2216 &mut self,
2217 element: &WebElement,
2218 keys: &SendKeysParameters,
2219 ) -> WebDriverResult<WebDriverResponse> {
2220 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2223 self.handle_any_user_prompts(self.webview_id()?)?;
2225
2226 let (sender, receiver) = generic_channel::channel().unwrap();
2227 let cmd = WebDriverScriptCommand::WillSendKeys(
2228 element.to_string(),
2229 keys.text.to_string(),
2230 self.session()?.strict_file_interactability(),
2231 sender,
2232 );
2233 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2234
2235 if !wait_for_ipc_response_flatten(receiver)? {
2238 return Ok(WebDriverResponse::Void);
2239 }
2240
2241 let id = Uuid::new_v4().to_string();
2243
2244 self.session_mut()?
2246 .input_state_table
2247 .insert(id.clone(), InputSourceState::Key(KeyInputState::new()));
2248
2249 let input_events = send_keys(&keys.text);
2252
2253 for event in input_events {
2254 match event {
2255 DispatchStringEvent::Keyboard(event) => {
2256 let raw_string = convert_keyboard_event_to_string(&event);
2257 let key_action = match event.state {
2258 KeyState::Down => KeyAction::Down(KeyDownAction { value: raw_string }),
2259 KeyState::Up => KeyAction::Up(KeyUpAction { value: raw_string }),
2260 };
2261 let action_sequence = ActionSequence {
2262 id: id.clone(),
2263 actions: ActionsType::Key {
2264 actions: vec![KeyActionItem::Key(key_action)],
2265 },
2266 };
2267
2268 match self.extract_an_action_sequence(vec![action_sequence]) {
2269 Ok(actions_by_tick) => {
2270 if let Err(e) =
2271 self.dispatch_actions(actions_by_tick, self.browsing_context_id()?)
2272 {
2273 error!(
2274 "handle_element_send_keys: dispatch_actions failed: {:?}",
2275 e
2276 );
2277 }
2278 },
2279 Err(e) => error!(
2280 "handle_element_send_keys: extract_an_action_sequence failed: {:?}",
2281 e
2282 ),
2283 }
2284 },
2285 DispatchStringEvent::Composition(event) => {
2286 self.send_blocking_input_event_to_embedder(InputEvent::Ime(
2287 ImeEvent::Composition(event),
2288 ));
2289 },
2290 }
2291 }
2292
2293 if self
2298 .session()?
2299 .input_cancel_list
2300 .iter()
2301 .all(|(cancel_item_id, _)| &id != cancel_item_id)
2302 {
2303 self.session_mut()?.input_state_table.remove(&id);
2304 }
2305
2306 Ok(WebDriverResponse::Void)
2307 }
2308
2309 fn handle_element_clear(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2311 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2314
2315 self.handle_any_user_prompts(self.webview_id()?)?;
2317
2318 let (sender, receiver) = generic_channel::channel().unwrap();
2320 let cmd = WebDriverScriptCommand::ElementClear(element.to_string(), sender);
2321 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2322
2323 wait_for_ipc_response_flatten(receiver)?;
2324 Ok(WebDriverResponse::Void)
2325 }
2326
2327 fn handle_element_click(&mut self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2329 let browsing_context_id = self.browsing_context_id()?;
2332 self.verify_browsing_context_is_open(browsing_context_id)?;
2333
2334 self.handle_any_user_prompts(self.webview_id()?)?;
2336
2337 let (sender, receiver) = generic_channel::channel().unwrap();
2338
2339 let cmd = WebDriverScriptCommand::ElementClick(element.to_string(), sender);
2341 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2342
2343 match wait_for_ipc_response_flatten(receiver)? {
2344 Some(element_id) => {
2345 self.add_load_status_sender()?;
2348
2349 self.perform_element_click(element_id)?;
2350
2351 let res = self.wait_for_navigation()?;
2354
2355 self.clear_load_status_sender()?;
2357
2358 Ok(res)
2359 },
2360 None => Ok(WebDriverResponse::Void),
2362 }
2363 }
2364
2365 fn perform_element_click(&mut self, element: String) -> WebDriverResult<WebDriverResponse> {
2368 let id = Uuid::new_v4().to_string();
2370
2371 let pointer_ids = self.session()?.pointer_ids();
2372 #[cfg(not(any(target_env = "ohos", target_os = "android")))]
2373 let pointer_type = PointerType::Mouse;
2374
2375 #[cfg(any(target_env = "ohos", target_os = "android"))]
2380 let pointer_type = PointerType::Touch;
2381
2382 self.session_mut()?.input_state_table.insert(
2383 id.clone(),
2384 InputSourceState::Pointer(PointerInputState::new(pointer_type, pointer_ids, 0.0, 0.0)),
2385 );
2386
2387 let pointer_move_action = PointerMoveAction {
2392 duration: None,
2393 origin: PointerOrigin::Element(WebElement(element)),
2394 x: 0.0,
2395 y: 0.0,
2396 ..Default::default()
2397 };
2398
2399 let pointer_down_action = PointerDownAction {
2402 button: ELEMENT_CLICK_BUTTON,
2403 ..Default::default()
2404 };
2405
2406 let pointer_up_action = PointerUpAction {
2409 button: ELEMENT_CLICK_BUTTON,
2410 ..Default::default()
2411 };
2412
2413 let action_sequence = ActionSequence {
2414 id: id.clone(),
2415 actions: ActionsType::Pointer {
2416 parameters: PointerActionParameters {
2417 pointer_type: PointerType::Mouse,
2418 },
2419 actions: vec![
2420 PointerActionItem::Pointer(PointerAction::Move(pointer_move_action)),
2421 PointerActionItem::Pointer(PointerAction::Down(pointer_down_action)),
2422 PointerActionItem::Pointer(PointerAction::Up(pointer_up_action)),
2423 ],
2424 },
2425 };
2426
2427 match self.extract_an_action_sequence(vec![action_sequence]) {
2429 Ok(actions_by_tick) => {
2430 if let Err(e) = self.dispatch_actions(actions_by_tick, self.browsing_context_id()?)
2431 {
2432 error!("handle_element_click: dispatch_actions failed: {:?}", e);
2433 }
2434 },
2435 Err(e) => error!(
2436 "handle_element_click: extract_an_action_sequence failed: {:?}",
2437 e
2438 ),
2439 }
2440
2441 self.session_mut()?.input_state_table.remove(&id);
2443
2444 Ok(WebDriverResponse::Void)
2445 }
2446
2447 fn take_screenshot(&self, rect: Option<Rect<f32, CSSPixel>>) -> WebDriverResult<String> {
2448 let _ = self.handle_execute_async_script(JavascriptCommandParameters {
2450 script: "requestAnimationFrame(() => arguments[0]());".to_string(),
2451 args: None,
2452 });
2453 if rect.as_ref().is_some_and(Rect::is_empty) {
2454 return Err(WebDriverError::new(
2455 ErrorStatus::UnknownError,
2456 "The requested `rect` has zero width and/or height",
2457 ));
2458 }
2459
2460 let webview_id = self.webview_id()?;
2461 let (sender, receiver) = crossbeam_channel::unbounded();
2462 self.send_message_to_embedder(WebDriverCommandMsg::TakeScreenshot(
2463 webview_id, rect, sender,
2464 ))?;
2465
2466 let result = match receiver.recv_timeout(SCREENSHOT_TIMEOUT) {
2467 Ok(result) => Ok(result),
2468 Err(RecvTimeoutError::Timeout) => Err(WebDriverError::new(
2469 ErrorStatus::Timeout,
2470 "Timed out waiting to take screenshot. Test likely didn't finish.",
2471 )),
2472 Err(RecvTimeoutError::Disconnected) => Err(WebDriverError::new(
2473 ErrorStatus::UnknownError,
2474 "Could not take screenshot because channel disconnected.",
2475 )),
2476 }?;
2477
2478 let image = result.map_err(|error| {
2479 WebDriverError::new(
2480 ErrorStatus::UnknownError,
2481 format!("Failed to take screenshot: {error:?}"),
2482 )
2483 })?;
2484
2485 let mut png_data = Cursor::new(Vec::new());
2486 DynamicImage::ImageRgba8(image)
2487 .write_to(&mut png_data, ImageFormat::Png)
2488 .unwrap();
2489
2490 Ok(base64::engine::general_purpose::STANDARD.encode(png_data.get_ref()))
2491 }
2492
2493 fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> {
2494 let webview_id = self.webview_id()?;
2497 self.verify_top_level_browsing_context_is_open(webview_id)?;
2498
2499 self.handle_any_user_prompts(webview_id)?;
2500
2501 let encoded = self.take_screenshot(None)?;
2503
2504 Ok(WebDriverResponse::Generic(ValueResponse(
2505 serde_json::to_value(encoded)?,
2506 )))
2507 }
2508
2509 fn handle_take_element_screenshot(
2510 &self,
2511 element: &WebElement,
2512 ) -> WebDriverResult<WebDriverResponse> {
2513 let webview_id = self.webview_id()?;
2516 self.verify_top_level_browsing_context_is_open(webview_id)?;
2517
2518 self.handle_any_user_prompts(webview_id)?;
2520
2521 let (sender, receiver) = generic_channel::channel().unwrap();
2524 let cmd =
2525 WebDriverScriptCommand::ScrollAndGetBoundingClientRect(element.to_string(), sender);
2526 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
2527
2528 let rect = wait_for_ipc_response_flatten(receiver)?;
2529
2530 let encoded = self.take_screenshot(Some(Rect::from_untyped(&rect)))?;
2532
2533 Ok(WebDriverResponse::Generic(ValueResponse(
2535 serde_json::to_value(encoded)?,
2536 )))
2537 }
2538
2539 fn handle_custom_handlers_set_mode(
2541 &self,
2542 parameters: &CustomHandlersSetModeParameters,
2543 ) -> WebDriverResult<WebDriverResponse> {
2544 let mode = match parameters.mode.as_str() {
2547 "autoAccept" => CustomHandlersAutomationMode::AutoAccept,
2548 "autoReject" => CustomHandlersAutomationMode::AutoReject,
2549 "none" => CustomHandlersAutomationMode::None,
2550 _ => {
2551 return Err(WebDriverError::new(
2552 ErrorStatus::InvalidArgument,
2553 "invalid argument",
2554 ));
2555 },
2556 };
2557 self.top_level_script_command(
2560 WebDriverScriptCommand::SetProtocolHandlerAutomationMode(mode),
2561 VerifyBrowsingContextIsOpen::Yes,
2562 )?;
2563 Ok(WebDriverResponse::Void)
2565 }
2566
2567 fn handle_get_prefs(
2568 &self,
2569 parameters: &GetPrefsParameters,
2570 ) -> WebDriverResult<WebDriverResponse> {
2571 let prefs = parameters
2572 .prefs
2573 .iter()
2574 .map(|item| {
2575 (
2576 item.clone(),
2577 serde_json::to_value(prefs::get().get_value(item)).unwrap(),
2578 )
2579 })
2580 .collect::<BTreeMap<_, _>>();
2581
2582 Ok(WebDriverResponse::Generic(ValueResponse(
2583 serde_json::to_value(prefs)?,
2584 )))
2585 }
2586
2587 fn handle_set_prefs(
2588 &self,
2589 parameters: &SetPrefsParameters,
2590 ) -> WebDriverResult<WebDriverResponse> {
2591 let mut current_preferences = prefs::get().clone();
2592 for (key, value) in parameters.prefs.iter() {
2593 current_preferences.set_value(key, value.0.clone());
2594 }
2595 prefs::set(current_preferences);
2596
2597 Ok(WebDriverResponse::Void)
2598 }
2599
2600 fn handle_reset_prefs(
2601 &self,
2602 parameters: &GetPrefsParameters,
2603 ) -> WebDriverResult<WebDriverResponse> {
2604 let (new_preferences, map) = if parameters.prefs.is_empty() {
2605 (self.default_preferences.clone(), BTreeMap::new())
2606 } else {
2607 let mut new_preferences = prefs::get().clone();
2609 for key in parameters.prefs.iter() {
2610 new_preferences.set_value(key, self.default_preferences.get_value(key))
2611 }
2612
2613 let map = parameters
2614 .prefs
2615 .iter()
2616 .map(|item| (item.clone(), new_preferences.get_value(item)))
2617 .collect::<BTreeMap<_, _>>();
2618
2619 (new_preferences, map)
2620 };
2621
2622 prefs::set(new_preferences);
2623
2624 Ok(WebDriverResponse::Generic(ValueResponse(
2625 serde_json::to_value(map)?,
2626 )))
2627 }
2628
2629 fn handle_shutdown(&self) -> WebDriverResult<WebDriverResponse> {
2630 self.send_message_to_embedder(WebDriverCommandMsg::Shutdown)?;
2631 Ok(WebDriverResponse::Void)
2632 }
2633
2634 fn handle_reset_all_cookies(&self) -> WebDriverResult<WebDriverResponse> {
2635 let (sender, receiver) = unbounded();
2636 self.send_message_to_embedder(WebDriverCommandMsg::ResetAllCookies(sender))?;
2637 if receiver.recv().is_err() {
2638 log::warn!("Communication failure while clearing cookies; status unknown");
2639 }
2640 Ok(WebDriverResponse::Void)
2641 }
2642
2643 fn verify_top_level_browsing_context_is_open(
2644 &self,
2645 webview_id: WebViewId,
2646 ) -> Result<(), WebDriverError> {
2647 let (sender, receiver) = generic_channel::oneshot().unwrap();
2648 self.send_message_to_embedder(WebDriverCommandMsg::IsWebViewOpen(webview_id, sender))?;
2649 if wait_for_oneshot_response(receiver)? {
2650 Ok(())
2651 } else {
2652 Err(WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2653 }
2654 }
2655
2656 fn verify_browsing_context_is_open(
2657 &self,
2658 browsing_context_id: BrowsingContextId,
2659 ) -> Result<(), WebDriverError> {
2660 let (sender, receiver) = generic_channel::oneshot().unwrap();
2661 self.send_message_to_embedder(WebDriverCommandMsg::IsBrowsingContextOpen(
2662 browsing_context_id,
2663 sender,
2664 ))?;
2665 if !receiver.recv().unwrap_or(false) {
2666 Err(WebDriverError::new(
2667 ErrorStatus::NoSuchWindow,
2668 "No such window",
2669 ))
2670 } else {
2671 Ok(())
2672 }
2673 }
2674
2675 fn wait_until_browsing_context_is_open(
2676 &self,
2677 browsing_context_id: BrowsingContextId,
2678 ) -> WebDriverResult<()> {
2679 const OPEN_BROWSING_CONTEXT_TIMEOUT: Duration =
2682 Duration::from_millis(DEFAULT_PAGE_LOAD_TIMEOUT);
2683 let now = Instant::now();
2684 let timeouts = self.session()?.session_timeouts();
2685
2686 let sleep_interval = Duration::from_millis(timeouts.sleep_interval);
2687
2688 while now.elapsed() < OPEN_BROWSING_CONTEXT_TIMEOUT {
2689 if self
2690 .verify_browsing_context_is_open(browsing_context_id)
2691 .is_ok()
2692 {
2693 return Ok(());
2694 }
2695
2696 sleep(sleep_interval);
2697 }
2698 Err(WebDriverError::new(
2699 ErrorStatus::Timeout,
2700 format!(
2701 "Timed out waiting for the top-level browsing context {browsing_context_id} to be ready"
2702 ),
2703 ))
2704 }
2705
2706 fn focus_webview(&self, webview_id: WebViewId) -> WebDriverResult<()> {
2707 self.send_message_to_embedder(WebDriverCommandMsg::FocusWebView(webview_id))
2708 }
2709
2710 fn focus_browsing_context(&self, browsing_cotext_id: BrowsingContextId) -> WebDriverResult<()> {
2711 self.send_message_to_embedder(WebDriverCommandMsg::FocusBrowsingContext(
2712 browsing_cotext_id,
2713 ))
2714 }
2715}
2716
2717impl WebDriverHandler<ServoExtensionRoute> for Handler {
2718 fn handle_command(
2719 &mut self,
2720 _session: &Option<Session>,
2721 msg: WebDriverMessage<ServoExtensionRoute>,
2722 ) -> WebDriverResult<WebDriverResponse> {
2723 info!("{:?}", msg.command);
2724
2725 while self.load_status_receiver.try_recv().is_ok() {}
2727
2728 match msg.command {
2731 WebDriverCommand::NewSession(_) |
2732 WebDriverCommand::Status |
2733 WebDriverCommand::DeleteSession |
2734 WebDriverCommand::Extension(ServoExtensionCommand::Shutdown) |
2735 WebDriverCommand::Extension(ServoExtensionCommand::ResetAllCookies) => {},
2736 _ => {
2737 self.session()?;
2738 },
2739 }
2740
2741 match msg.command {
2742 WebDriverCommand::NewSession(ref parameters) => self.handle_new_session(parameters),
2743 WebDriverCommand::DeleteSession => self.handle_delete_session(),
2744 WebDriverCommand::Status => self.handle_status(),
2745 WebDriverCommand::AddCookie(ref parameters) => self.handle_add_cookie(parameters),
2746 WebDriverCommand::Get(ref parameters) => self.handle_get(parameters),
2747 WebDriverCommand::GetCurrentUrl => self.handle_current_url(),
2748 WebDriverCommand::GetWindowRect => {
2749 self.handle_window_rect(VerifyBrowsingContextIsOpen::Yes)
2750 },
2751 WebDriverCommand::SetWindowRect(ref size) => self.handle_set_window_rect(size),
2752 WebDriverCommand::IsEnabled(ref element) => self.handle_is_enabled(element),
2753 WebDriverCommand::IsSelected(ref element) => self.handle_is_selected(element),
2754 WebDriverCommand::GoBack => self.handle_go_back(),
2755 WebDriverCommand::GoForward => self.handle_go_forward(),
2756 WebDriverCommand::Refresh => self.handle_refresh(),
2757 WebDriverCommand::GetTitle => self.handle_title(),
2758 WebDriverCommand::GetWindowHandle => self.handle_window_handle(),
2759 WebDriverCommand::GetWindowHandles => self.handle_window_handles(),
2760 WebDriverCommand::NewWindow(ref parameters) => self.handle_new_window(parameters),
2761 WebDriverCommand::CloseWindow => self.handle_close_window(),
2762 WebDriverCommand::MaximizeWindow => self.handle_maximize_window(),
2763 WebDriverCommand::SwitchToFrame(ref parameters) => {
2764 self.handle_switch_to_frame(parameters)
2765 },
2766 WebDriverCommand::SwitchToParentFrame => self.handle_switch_to_parent_frame(),
2767 WebDriverCommand::SwitchToWindow(ref parameters) => {
2768 self.handle_switch_to_window(parameters)
2769 },
2770 WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
2771 WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
2772 WebDriverCommand::FindElementElement(ref element, ref parameters) => {
2773 self.handle_find_element_from_element(element, parameters)
2774 },
2775 WebDriverCommand::FindElementElements(ref element, ref parameters) => {
2776 self.handle_find_elements_from_element(element, parameters)
2777 },
2778 WebDriverCommand::FindShadowRootElements(ref shadow_root, ref parameters) => {
2779 self.handle_find_elements_from_shadow_root(shadow_root, parameters)
2780 },
2781 WebDriverCommand::FindShadowRootElement(ref shadow_root, ref parameters) => {
2782 self.handle_find_element_from_shadow_root(shadow_root, parameters)
2783 },
2784 WebDriverCommand::GetShadowRoot(element) => self.handle_get_shadow_root(element),
2785 WebDriverCommand::GetNamedCookie(name) => self.handle_get_cookie(name),
2786 WebDriverCommand::GetCookies => self.handle_get_cookies(),
2787 WebDriverCommand::GetActiveElement => self.handle_active_element(),
2788 WebDriverCommand::GetComputedRole(ref element) => self.handle_computed_role(element),
2789 WebDriverCommand::GetElementRect(ref element) => self.handle_element_rect(element),
2790 WebDriverCommand::GetElementText(ref element) => self.handle_element_text(element),
2791 WebDriverCommand::GetElementTagName(ref element) => {
2792 self.handle_element_tag_name(element)
2793 },
2794 WebDriverCommand::GetElementAttribute(ref element, ref name) => {
2795 self.handle_element_attribute(element, name)
2796 },
2797 WebDriverCommand::GetElementProperty(ref element, ref name) => {
2798 self.handle_element_property(element, name)
2799 },
2800 WebDriverCommand::GetCSSValue(ref element, ref name) => {
2801 self.handle_element_css(element, name)
2802 },
2803 WebDriverCommand::GetPageSource => self.handle_get_page_source(),
2804 WebDriverCommand::PerformActions(actions_parameters) => {
2805 self.handle_perform_actions(actions_parameters)
2806 },
2807 WebDriverCommand::ReleaseActions => self.handle_release_actions(),
2808 WebDriverCommand::ExecuteScript(x) => self.handle_execute_script(x),
2809 WebDriverCommand::ExecuteAsyncScript(x) => self.handle_execute_async_script(x),
2810 WebDriverCommand::ElementSendKeys(ref element, ref keys) => {
2811 self.handle_element_send_keys(element, keys)
2812 },
2813 WebDriverCommand::ElementClear(ref element) => self.handle_element_clear(element),
2814 WebDriverCommand::ElementClick(ref element) => self.handle_element_click(element),
2815 WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
2816 WebDriverCommand::AcceptAlert => self.handle_accept_alert(),
2817 WebDriverCommand::GetAlertText => self.handle_get_alert_text(),
2818 WebDriverCommand::SendAlertText(text) => self.handle_send_alert_text(text.text),
2819 WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
2820 WebDriverCommand::DeleteCookie(name) => self.handle_delete_cookie(name),
2821 WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),
2822 WebDriverCommand::SetTimeouts(ref x) => self.handle_set_timeouts(x),
2823 WebDriverCommand::TakeScreenshot => self.handle_take_screenshot(),
2824 WebDriverCommand::TakeElementScreenshot(ref x) => {
2825 self.handle_take_element_screenshot(x)
2826 },
2827 WebDriverCommand::Extension(extension) => match extension {
2828 ServoExtensionCommand::GetPrefs(ref x) => self.handle_get_prefs(x),
2829 ServoExtensionCommand::SetPrefs(ref x) => self.handle_set_prefs(x),
2830 ServoExtensionCommand::ResetPrefs(ref x) => self.handle_reset_prefs(x),
2831 ServoExtensionCommand::CustomHandlersSetMode(ref x) => {
2832 self.handle_custom_handlers_set_mode(x)
2833 },
2834 ServoExtensionCommand::Shutdown => self.handle_shutdown(),
2835 ServoExtensionCommand::ResetAllCookies => self.handle_reset_all_cookies(),
2836 },
2837 _ => Err(WebDriverError::new(
2838 ErrorStatus::UnsupportedOperation,
2839 format!("Command not implemented: {:?}", msg.command),
2840 )),
2841 }
2842 }
2843
2844 fn teardown_session(&mut self, _session: SessionTeardownKind) {
2845 self.session = None;
2846 }
2847}
2848
2849fn wait_for_oneshot_response<T>(
2850 receiver: generic_channel::GenericOneshotReceiver<T>,
2851) -> Result<T, WebDriverError>
2852where
2853 T: for<'de> Deserialize<'de> + Serialize,
2854{
2855 receiver
2856 .recv()
2857 .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2858}
2859
2860fn wait_for_ipc_response<T>(receiver: GenericReceiver<T>) -> Result<T, WebDriverError>
2861where
2862 T: for<'de> Deserialize<'de> + Serialize,
2863{
2864 receiver
2865 .recv()
2866 .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2867}
2868
2869fn wait_for_ipc_response_flatten<T>(
2872 receiver: GenericReceiver<Result<T, ErrorStatus>>,
2873) -> Result<T, WebDriverError>
2874where
2875 T: for<'de> Deserialize<'de> + Serialize,
2876{
2877 match receiver.recv() {
2878 Ok(Ok(value)) => Ok(value),
2879 Ok(Err(error_status)) => Err(WebDriverError::new(error_status, "")),
2880 Err(_) => Err(WebDriverError::new(ErrorStatus::NoSuchWindow, "")),
2881 }
2882}
2883
2884fn wait_for_script_ipc_response_with_timeout<T>(
2885 receiver: GenericReceiver<T>,
2886 timeout: Duration,
2887) -> Result<T, WebDriverError>
2888where
2889 T: for<'de> Deserialize<'de> + Serialize,
2890{
2891 receiver
2892 .try_recv_timeout(timeout)
2893 .map_err(|error| match error {
2894 generic_channel::TryReceiveError::ReceiveError(_) => {
2895 WebDriverError::new(ErrorStatus::NoSuchWindow, "")
2896 },
2897 generic_channel::TryReceiveError::Empty => {
2898 WebDriverError::new(ErrorStatus::ScriptTimeout, "")
2899 },
2900 })
2901}
2902
2903fn unwrap_first_element_response(res: WebDriverResponse) -> WebDriverResult<WebDriverResponse> {
2904 if let WebDriverResponse::Generic(ValueResponse(values)) = res {
2905 let arr = values.as_array().unwrap();
2906 if let Some(first) = arr.first() {
2907 Ok(WebDriverResponse::Generic(ValueResponse(first.clone())))
2908 } else {
2909 Err(WebDriverError::new(ErrorStatus::NoSuchElement, ""))
2910 }
2911 } else {
2912 unreachable!()
2913 }
2914}
2915
2916fn convert_keyboard_event_to_string(event: &KeyboardEvent) -> String {
2917 let key = &event.key;
2918 let named_key = match key {
2919 Key::Character(s) => return s.to_string(),
2920 Key::Named(named_key) => named_key,
2921 };
2922
2923 match event.location {
2924 Location::Left | Location::Standard => match named_key {
2925 NamedKey::Unidentified => '\u{E000}'.to_string(),
2926 NamedKey::Cancel => '\u{E001}'.to_string(),
2927 NamedKey::Help => '\u{E002}'.to_string(),
2928 NamedKey::Backspace => '\u{E003}'.to_string(),
2929 NamedKey::Tab => '\u{E004}'.to_string(),
2930 NamedKey::Clear => '\u{E005}'.to_string(),
2931 NamedKey::Enter => match event.code {
2932 Code::NumpadEnter => '\u{E007}'.to_string(),
2933 _ => '\u{E006}'.to_string(),
2934 },
2935 NamedKey::Shift => '\u{E008}'.to_string(),
2936 NamedKey::Control => '\u{E009}'.to_string(),
2937 NamedKey::Alt => '\u{E00A}'.to_string(),
2938 NamedKey::Pause => '\u{E00B}'.to_string(),
2939 NamedKey::Escape => '\u{E00C}'.to_string(),
2940 NamedKey::PageUp => '\u{E00E}'.to_string(),
2941 NamedKey::PageDown => '\u{E00F}'.to_string(),
2942 NamedKey::End => '\u{E010}'.to_string(),
2943 NamedKey::Home => '\u{E011}'.to_string(),
2944 NamedKey::ArrowLeft => '\u{E012}'.to_string(),
2945 NamedKey::ArrowUp => '\u{E013}'.to_string(),
2946 NamedKey::ArrowRight => '\u{E014}'.to_string(),
2947 NamedKey::ArrowDown => '\u{E015}'.to_string(),
2948 NamedKey::Insert => '\u{E016}'.to_string(),
2949 NamedKey::Delete => '\u{E017}'.to_string(),
2950 NamedKey::F1 => '\u{E031}'.to_string(),
2951 NamedKey::F2 => '\u{E032}'.to_string(),
2952 NamedKey::F3 => '\u{E033}'.to_string(),
2953 NamedKey::F4 => '\u{E034}'.to_string(),
2954 NamedKey::F5 => '\u{E035}'.to_string(),
2955 NamedKey::F6 => '\u{E036}'.to_string(),
2956 NamedKey::F7 => '\u{E037}'.to_string(),
2957 NamedKey::F8 => '\u{E038}'.to_string(),
2958 NamedKey::F9 => '\u{E039}'.to_string(),
2959 NamedKey::F10 => '\u{E03A}'.to_string(),
2960 NamedKey::F11 => '\u{E03B}'.to_string(),
2961 NamedKey::F12 => '\u{E03C}'.to_string(),
2962 NamedKey::Meta => '\u{E03D}'.to_string(),
2963 NamedKey::ZenkakuHankaku => '\u{E040}'.to_string(),
2964 _ => {
2965 error!("Unexpected NamedKey on send_keys");
2966 '\u{E000}'.to_string()
2967 },
2968 },
2969 Location::Right | Location::Numpad => match named_key {
2970 NamedKey::Shift => '\u{E050}'.to_string(),
2971 NamedKey::Control => '\u{E051}'.to_string(),
2972 NamedKey::Alt => '\u{E052}'.to_string(),
2973 NamedKey::Meta => '\u{E053}'.to_string(),
2974 NamedKey::PageUp => '\u{E054}'.to_string(),
2975 NamedKey::PageDown => '\u{E055}'.to_string(),
2976 NamedKey::End => '\u{E056}'.to_string(),
2977 NamedKey::Home => '\u{E057}'.to_string(),
2978 NamedKey::ArrowLeft => '\u{E058}'.to_string(),
2979 NamedKey::ArrowUp => '\u{E059}'.to_string(),
2980 NamedKey::ArrowRight => '\u{E05A}'.to_string(),
2981 NamedKey::ArrowDown => '\u{E05B}'.to_string(),
2982 NamedKey::Insert => '\u{E05C}'.to_string(),
2983 NamedKey::Delete => '\u{E05D}'.to_string(),
2984 _ => {
2985 error!("Unexpected NamedKey on send_keys");
2986 '\u{E000}'.to_string()
2987 },
2988 },
2989 }
2990}