1#![crate_name = "webdriver_server"]
6#![crate_type = "rlib"]
7#![deny(unsafe_code)]
8
9mod actions;
10mod capabilities;
11mod script_argument_extraction;
12mod session;
13mod timeout;
14mod user_prompt;
15
16use std::borrow::ToOwned;
17use std::cell::{Cell, LazyCell, RefCell};
18use std::collections::{BTreeMap, HashMap};
19use std::io::Cursor;
20use std::net::{SocketAddr, SocketAddrV4};
21use std::thread::sleep;
22use std::time::{Duration, Instant};
23use std::{env, fmt, process, thread};
24
25use base::generic_channel::{self, GenericSender, RoutedReceiver};
26use base::id::{BrowsingContextId, WebViewId};
27use base64::Engine;
28use capabilities::ServoCapabilities;
29use cookie::{CookieBuilder, Expiration, SameSite};
30use crossbeam_channel::{Receiver, RecvTimeoutError, Sender, after, select, unbounded};
31use embedder_traits::{
32 CustomHandlersAutomationMode, EventLoopWaker, ImeEvent, InputEvent, JSValue,
33 JavaScriptEvaluationError, JavaScriptEvaluationResultSerializationError, MouseButton,
34 WebDriverCommandMsg, WebDriverFrameId, WebDriverJSResult, WebDriverLoadStatus,
35 WebDriverScriptCommand,
36};
37use euclid::{Point2D, Rect, Size2D};
38use http::method::Method;
39use image::{DynamicImage, ImageFormat};
40use ipc_channel::ipc::{self, IpcReceiver, TryRecvError};
41use keyboard_types::webdriver::{Event as DispatchStringEvent, KeyInputState, send_keys};
42use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, NamedKey};
43use log::{debug, error, info};
44use serde::de::{Deserializer, MapAccess, Visitor};
45use serde::ser::Serializer;
46use serde::{Deserialize, Serialize};
47use serde_json::{Value, json};
48use servo_config::prefs::{self, PrefValue, Preferences};
49use servo_geometry::DeviceIndependentIntRect;
50use servo_url::ServoUrl;
51use style_traits::CSSPixel;
52use time::OffsetDateTime;
53use uuid::Uuid;
54use webdriver::actions::{
55 ActionSequence, ActionsType, KeyAction, KeyActionItem, KeyDownAction, KeyUpAction,
56 PointerAction, PointerActionItem, PointerActionParameters, PointerDownAction,
57 PointerMoveAction, PointerOrigin, PointerType, PointerUpAction,
58};
59use webdriver::capabilities::CapabilitiesMatching;
60use webdriver::command::{
61 ActionsParameters, AddCookieParameters, GetParameters, JavascriptCommandParameters,
62 LocatorParameters, NewSessionParameters, NewWindowParameters, SendKeysParameters,
63 SwitchToFrameParameters, SwitchToWindowParameters, TimeoutsParameters, WebDriverCommand,
64 WebDriverExtensionCommand, WebDriverMessage, WindowRectParameters,
65};
66use webdriver::common::{
67 Cookie, Date, LocatorStrategy, Parameters, ShadowRoot, WebElement, WebFrame, WebWindow,
68};
69use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
70use webdriver::httpapi::WebDriverExtensionRoute;
71use webdriver::response::{
72 CloseWindowResponse, CookieResponse, CookiesResponse, ElementRectResponse, NewSessionResponse,
73 NewWindowResponse, TimeoutsResponse, ValueResponse, WebDriverResponse, WindowRectResponse,
74};
75use webdriver::server::{self, Session, SessionTeardownKind, WebDriverHandler};
76
77use crate::actions::{InputSourceState, PointerInputState};
78use crate::session::{PageLoadStrategy, WebDriverSession};
79use crate::timeout::{DEFAULT_PAGE_LOAD_TIMEOUT, SCREENSHOT_TIMEOUT};
80
81fn extension_routes() -> Vec<(Method, &'static str, ServoExtensionRoute)> {
82 vec![
83 (
84 Method::GET,
85 "/session/{sessionId}/servo/prefs/get",
86 ServoExtensionRoute::GetPrefs,
87 ),
88 (
89 Method::POST,
90 "/session/{sessionId}/servo/prefs/set",
91 ServoExtensionRoute::SetPrefs,
92 ),
93 (
94 Method::POST,
95 "/session/{sessionId}/servo/prefs/reset",
96 ServoExtensionRoute::ResetPrefs,
97 ),
98 (
99 Method::DELETE,
100 "/session/{sessionId}/servo/shutdown",
101 ServoExtensionRoute::Shutdown,
102 ),
103 (
105 Method::POST,
106 "/session/{sessionId}/custom-handlers/set-mode",
107 ServoExtensionRoute::CustomHandlersSetMode,
108 ),
109 (
110 Method::POST,
111 "/session/{sessionId}/servo/cookies/reset",
112 ServoExtensionRoute::ResetAllCookies,
113 ),
114 ]
115}
116
117fn cookie_msg_to_cookie(cookie: cookie::Cookie) -> Cookie {
118 Cookie {
119 name: cookie.name().to_owned(),
120 value: cookie.value().to_owned(),
121 path: cookie.path().map(|s| s.to_owned()),
122 domain: cookie.domain().map(|s| s.to_owned()),
123 expiry: cookie.expires().and_then(|expiration| match expiration {
124 Expiration::DateTime(date_time) => Some(Date(date_time.unix_timestamp() as u64)),
125 Expiration::Session => None,
126 }),
127 secure: cookie.secure().unwrap_or(false),
128 http_only: cookie.http_only().unwrap_or(false),
129 same_site: cookie.same_site().map(|s| s.to_string()),
130 }
131}
132
133pub fn start_server(
134 port: u16,
135 embedder_sender: Sender<WebDriverCommandMsg>,
136 event_loop_waker: Box<dyn EventLoopWaker>,
137) {
138 let handler = Handler::new(embedder_sender, event_loop_waker);
139
140 thread::Builder::new()
141 .name("WebDriverHttpServer".to_owned())
142 .spawn(move || {
143 let address = SocketAddrV4::new("0.0.0.0".parse().unwrap(), port);
144 match server::start(
145 SocketAddr::V4(address),
146 vec![],
147 vec![],
148 handler,
149 extension_routes(),
150 ) {
151 Ok(listening) => info!("WebDriver server listening on {}", listening.socket),
152 Err(e) => panic!("Unable to start WebDriver HTTP server {e:?}"),
153 }
154 })
155 .expect("Thread spawning failed");
156}
157
158struct Handler {
159 load_status_receiver: RoutedReceiver<WebDriverLoadStatus>,
163 load_status_sender: GenericSender<WebDriverLoadStatus>,
167
168 session: Option<WebDriverSession>,
169
170 embedder_sender: Sender<WebDriverCommandMsg>,
174
175 event_loop_waker: Box<dyn EventLoopWaker>,
177
178 pending_input_event_receivers: RefCell<Vec<Receiver<()>>>,
183
184 num_pending_actions: Cell<u32>,
186}
187
188#[derive(Clone, Copy, Debug, PartialEq)]
189#[allow(clippy::enum_variant_names)]
190enum ServoExtensionRoute {
191 GetPrefs,
192 SetPrefs,
193 ResetPrefs,
194 Shutdown,
200 CustomHandlersSetMode,
201 ResetAllCookies,
202}
203
204impl WebDriverExtensionRoute for ServoExtensionRoute {
205 type Command = ServoExtensionCommand;
206
207 fn command(
208 &self,
209 _parameters: &Parameters,
210 body_data: &Value,
211 ) -> WebDriverResult<WebDriverCommand<ServoExtensionCommand>> {
212 let command = match *self {
213 ServoExtensionRoute::GetPrefs => {
214 let parameters: GetPrefsParameters = serde_json::from_value(body_data.clone())?;
215 ServoExtensionCommand::GetPrefs(parameters)
216 },
217 ServoExtensionRoute::SetPrefs => {
218 let parameters: SetPrefsParameters = serde_json::from_value(body_data.clone())?;
219 ServoExtensionCommand::SetPrefs(parameters)
220 },
221 ServoExtensionRoute::ResetPrefs => {
222 let parameters: GetPrefsParameters = serde_json::from_value(body_data.clone())?;
223 ServoExtensionCommand::ResetPrefs(parameters)
224 },
225 ServoExtensionRoute::CustomHandlersSetMode => {
226 let parameters: CustomHandlersSetModeParameters =
227 serde_json::from_value(body_data.clone())?;
228 ServoExtensionCommand::CustomHandlersSetMode(parameters)
229 },
230 ServoExtensionRoute::Shutdown => ServoExtensionCommand::Shutdown,
231 ServoExtensionRoute::ResetAllCookies => ServoExtensionCommand::ResetAllCookies,
232 };
233 Ok(WebDriverCommand::Extension(command))
234 }
235}
236
237#[derive(Clone, Debug)]
238#[allow(clippy::enum_variant_names)]
239enum ServoExtensionCommand {
240 GetPrefs(GetPrefsParameters),
241 SetPrefs(SetPrefsParameters),
242 ResetPrefs(GetPrefsParameters),
243 CustomHandlersSetMode(CustomHandlersSetModeParameters),
244 Shutdown,
245 ResetAllCookies,
246}
247
248impl WebDriverExtensionCommand for ServoExtensionCommand {
249 fn parameters_json(&self) -> Option<Value> {
250 match *self {
251 ServoExtensionCommand::GetPrefs(ref x) => serde_json::to_value(x).ok(),
252 ServoExtensionCommand::SetPrefs(ref x) => serde_json::to_value(x).ok(),
253 ServoExtensionCommand::ResetPrefs(ref x) => serde_json::to_value(x).ok(),
254 ServoExtensionCommand::CustomHandlersSetMode(ref x) => serde_json::to_value(x).ok(),
255 ServoExtensionCommand::Shutdown | ServoExtensionCommand::ResetAllCookies => None,
256 }
257 }
258}
259
260#[derive(Clone)]
261struct SendableJSValue(JSValue);
262
263impl Serialize for SendableJSValue {
264 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
265 where
266 S: Serializer,
267 {
268 match self.0 {
269 JSValue::Undefined | JSValue::Null => serializer.serialize_unit(),
270 JSValue::Boolean(x) => serializer.serialize_bool(x),
271 JSValue::Number(x) => {
272 if x.fract() == 0.0 {
273 serializer.serialize_i64(x as i64)
274 } else {
275 serializer.serialize_f64(x)
276 }
277 },
278 JSValue::String(ref x) => serializer.serialize_str(x),
279 JSValue::Element(ref x) => WebElement(x.clone()).serialize(serializer),
280 JSValue::ShadowRoot(ref x) => ShadowRoot(x.clone()).serialize(serializer),
281 JSValue::Frame(ref x) => WebFrame(x.clone()).serialize(serializer),
282 JSValue::Window(ref x) => WebWindow(x.clone()).serialize(serializer),
283 JSValue::Array(ref x) => x
284 .iter()
285 .map(|element| SendableJSValue(element.clone()))
286 .collect::<Vec<SendableJSValue>>()
287 .serialize(serializer),
288 JSValue::Object(ref x) => x
289 .iter()
290 .map(|(k, v)| (k.clone(), SendableJSValue(v.clone())))
291 .collect::<HashMap<String, SendableJSValue>>()
292 .serialize(serializer),
293 }
294 }
295}
296
297#[derive(Clone, Debug, PartialEq)]
298struct WebDriverPrefValue(PrefValue);
299
300impl Serialize for WebDriverPrefValue {
301 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
302 where
303 S: Serializer,
304 {
305 match self.0 {
306 PrefValue::Bool(b) => serializer.serialize_bool(b),
307 PrefValue::Str(ref s) => serializer.serialize_str(s),
308 PrefValue::Float(f) => serializer.serialize_f64(f),
309 PrefValue::Int(i) => serializer.serialize_i64(i),
310 PrefValue::Array(ref v) => v
311 .iter()
312 .map(|value| WebDriverPrefValue(value.clone()))
313 .collect::<Vec<WebDriverPrefValue>>()
314 .serialize(serializer),
315 }
316 }
317}
318
319impl<'de> Deserialize<'de> for WebDriverPrefValue {
320 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
321 where
322 D: Deserializer<'de>,
323 {
324 struct Visitor;
325
326 impl ::serde::de::Visitor<'_> for Visitor {
327 type Value = WebDriverPrefValue;
328
329 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
330 formatter.write_str("preference value")
331 }
332
333 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
334 where
335 E: ::serde::de::Error,
336 {
337 Ok(WebDriverPrefValue(PrefValue::Float(value)))
338 }
339
340 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
341 where
342 E: ::serde::de::Error,
343 {
344 Ok(WebDriverPrefValue(PrefValue::Int(value)))
345 }
346
347 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
348 where
349 E: ::serde::de::Error,
350 {
351 Ok(WebDriverPrefValue(PrefValue::Int(value as i64)))
352 }
353
354 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
355 where
356 E: ::serde::de::Error,
357 {
358 Ok(WebDriverPrefValue(PrefValue::Str(String::from(value))))
359 }
360
361 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
362 where
363 E: ::serde::de::Error,
364 {
365 Ok(WebDriverPrefValue(PrefValue::Bool(value)))
366 }
367 }
368
369 deserializer.deserialize_any(Visitor)
370 }
371}
372
373#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
374struct GetPrefsParameters {
375 prefs: Vec<String>,
376}
377
378#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
379struct SetPrefsParameters {
380 #[serde(deserialize_with = "map_to_vec")]
381 prefs: Vec<(String, WebDriverPrefValue)>,
382}
383
384#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
385struct CustomHandlersSetModeParameters {
386 mode: String,
387}
388
389fn map_to_vec<'de, D>(de: D) -> Result<Vec<(String, WebDriverPrefValue)>, D::Error>
390where
391 D: Deserializer<'de>,
392{
393 de.deserialize_map(TupleVecMapVisitor)
394}
395
396struct TupleVecMapVisitor;
397
398impl<'de> Visitor<'de> for TupleVecMapVisitor {
399 type Value = Vec<(String, WebDriverPrefValue)>;
400
401 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
402 formatter.write_str("a map")
403 }
404
405 #[inline]
406 fn visit_unit<E>(self) -> Result<Self::Value, E> {
407 Ok(Vec::new())
408 }
409
410 #[inline]
411 fn visit_map<T>(self, mut access: T) -> Result<Self::Value, T::Error>
412 where
413 T: MapAccess<'de>,
414 {
415 let mut values = Vec::new();
416
417 while let Some((key, value)) = access.next_entry()? {
418 values.push((key, value));
419 }
420
421 Ok(values)
422 }
423}
424
425enum VerifyBrowsingContextIsOpen {
426 Yes,
427 No,
428}
429
430enum ImplicitWait {
431 Return,
432 #[expect(dead_code, reason = "This will be used in the next patch")]
433 Continue,
434}
435
436impl From<ImplicitWait> for bool {
437 fn from(implicit_wait: ImplicitWait) -> Self {
438 match implicit_wait {
439 ImplicitWait::Return => true,
440 ImplicitWait::Continue => false,
441 }
442 }
443}
444
445impl Handler {
446 fn new(
447 embedder_sender: Sender<WebDriverCommandMsg>,
448 event_loop_waker: Box<dyn EventLoopWaker>,
449 ) -> Handler {
450 let (load_status_sender, receiver) = generic_channel::channel().unwrap();
456 let load_status_receiver = receiver.route_preserving_errors();
457
458 Handler {
459 load_status_sender,
460 load_status_receiver,
461 session: None,
462 embedder_sender,
463 event_loop_waker,
464 pending_input_event_receivers: Default::default(),
465 num_pending_actions: Cell::new(0),
466 }
467 }
468
469 fn browsing_context_id(&self) -> WebDriverResult<BrowsingContextId> {
470 self.session()?
471 .current_browsing_context_id()
472 .ok_or_else(|| {
473 WebDriverError::new(ErrorStatus::UnknownError, "No browsing context available")
474 })
475 }
476
477 fn webview_id(&self) -> WebDriverResult<WebViewId> {
478 self.session()?
479 .current_webview_id()
480 .ok_or_else(|| WebDriverError::new(ErrorStatus::UnknownError, "No webview available"))
481 }
482
483 fn send_input_event_to_embedder(&self, input_event: InputEvent) {
484 let _ = self.send_message_to_embedder(WebDriverCommandMsg::InputEvent(
485 self.verified_webview_id(),
486 input_event,
487 None,
488 ));
489 }
490
491 fn send_blocking_input_event_to_embedder(&self, input_event: InputEvent) {
492 let (result_sender, result_receiver) = unbounded();
493 if self
494 .send_message_to_embedder(WebDriverCommandMsg::InputEvent(
495 self.verified_webview_id(),
496 input_event,
497 Some(result_sender),
498 ))
499 .is_ok()
500 {
501 self.pending_input_event_receivers
502 .borrow_mut()
503 .push(result_receiver);
504 }
505 }
506
507 fn send_message_to_embedder(&self, msg: WebDriverCommandMsg) -> WebDriverResult<()> {
508 self.embedder_sender.send(msg).map_err(|_| {
509 WebDriverError::new(
510 ErrorStatus::UnknownError,
511 "Failed to send message to embedder",
512 )
513 })?;
514 self.event_loop_waker.wake();
515 Ok(())
516 }
517
518 fn add_load_status_sender(&self) -> WebDriverResult<()> {
519 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
520 self.browsing_context_id()?,
521 WebDriverScriptCommand::AddLoadStatusSender(
522 self.webview_id()?,
523 self.load_status_sender.clone(),
524 ),
525 ))
526 }
527
528 fn clear_load_status_sender(&self) -> WebDriverResult<()> {
529 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
530 self.browsing_context_id()?,
531 WebDriverScriptCommand::RemoveLoadStatusSender(self.webview_id()?),
532 ))
533 }
534
535 fn verified_webview_id(&self) -> WebViewId {
537 self.session().unwrap().current_webview_id().unwrap()
538 }
539
540 fn focused_webview_id(&self) -> WebDriverResult<Option<WebViewId>> {
541 let (sender, receiver) = ipc::channel().unwrap();
542 self.send_message_to_embedder(WebDriverCommandMsg::GetFocusedWebView(sender.clone()))?;
543 wait_for_ipc_response(receiver)
545 }
546
547 fn session(&self) -> WebDriverResult<&WebDriverSession> {
548 match self.session {
549 Some(ref x) => Ok(x),
550 None => Err(WebDriverError::new(
552 ErrorStatus::InvalidSessionId,
553 "Session not created",
554 )),
555 }
556 }
557
558 fn session_mut(&mut self) -> WebDriverResult<&mut WebDriverSession> {
559 match self.session {
560 Some(ref mut x) => Ok(x),
561 None => Err(WebDriverError::new(
563 ErrorStatus::InvalidSessionId,
564 "Session not created",
565 )),
566 }
567 }
568
569 fn handle_new_session(
571 &mut self,
572 parameters: &NewSessionParameters,
573 ) -> WebDriverResult<WebDriverResponse> {
574 if let Ok(value) = env::var("DELAY_AFTER_ACCEPT") {
575 let seconds = value.parse::<u64>().unwrap_or_default();
576 println!("Waiting for {} seconds...", seconds);
577 println!("lldb -p {}", process::id());
578 thread::sleep(Duration::from_secs(seconds));
579 }
580
581 if self.session.is_some() {
584 return Err(WebDriverError::new(
585 ErrorStatus::SessionNotCreated,
586 "Session already created",
587 ));
588 }
589
590 let mut servo_capabilities = ServoCapabilities::new();
595 let processed_capabilities = parameters.match_browser(&mut servo_capabilities)?;
596
597 let mut capabilities = match processed_capabilities {
599 Some(capabilities) => capabilities,
600 None => {
601 return Err(WebDriverError::new(
602 ErrorStatus::SessionNotCreated,
603 "Session not created due to invalid capabilities",
604 ));
605 },
606 };
607
608 let session_id = self.create_session(&mut capabilities, &servo_capabilities)?;
610
611 let response = NewSessionResponse::new(session_id.to_string(), Value::Object(capabilities));
613
614 match self.focused_webview_id()? {
616 Some(webview_id) => {
617 self.session_mut()?.set_webview_id(webview_id);
618 self.session_mut()?
619 .set_browsing_context_id(BrowsingContextId::from(webview_id));
620 },
621 None => {
622 let (sender, receiver) = ipc::channel().unwrap();
625
626 self.send_message_to_embedder(WebDriverCommandMsg::NewWebView(
627 sender,
628 Some(self.load_status_sender.clone()),
629 ))?;
630 let webview_id = receiver
631 .recv()
632 .expect("IPC failure when creating new webview for new session");
633 self.focus_webview(webview_id)?;
634 self.session_mut()?.set_webview_id(webview_id);
635 self.session_mut()?
636 .set_browsing_context_id(BrowsingContextId::from(webview_id));
637 let _ = self.wait_document_ready(Some(3000));
638 },
639 };
640
641 Ok(WebDriverResponse::NewSession(response))
646 }
647
648 fn handle_delete_session(&mut self) -> WebDriverResult<WebDriverResponse> {
650 self.session = None;
652
653 Ok(WebDriverResponse::DeleteSession)
655 }
656
657 fn handle_status(&self) -> WebDriverResult<WebDriverResponse> {
659 Ok(WebDriverResponse::Generic(ValueResponse(
660 if self.session.is_none() {
661 json!({ "ready": true, "message": "Ready for a new session" })
662 } else {
663 json!({ "ready": false, "message": "Not ready for a new session" })
664 },
665 )))
666 }
667
668 fn browsing_context_script_command(
672 &self,
673 cmd_msg: WebDriverScriptCommand,
674 verify: VerifyBrowsingContextIsOpen,
675 ) -> WebDriverResult<()> {
676 let browsing_context_id = self.browsing_context_id()?;
677 if let VerifyBrowsingContextIsOpen::Yes = verify {
678 self.verify_browsing_context_is_open(browsing_context_id)?;
679 }
680 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
681 browsing_context_id,
682 cmd_msg,
683 ))?;
684 Ok(())
685 }
686
687 fn top_level_script_command(
691 &self,
692 cmd_msg: WebDriverScriptCommand,
693 verify: VerifyBrowsingContextIsOpen,
694 ) -> WebDriverResult<()> {
695 let webview_id = self.webview_id()?;
696 if let VerifyBrowsingContextIsOpen::Yes = verify {
697 self.verify_top_level_browsing_context_is_open(webview_id)?;
698 }
699 let browsing_context_id = BrowsingContextId::from(webview_id);
700 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
701 browsing_context_id,
702 cmd_msg,
703 ))?;
704 Ok(())
705 }
706
707 fn handle_get(&mut self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> {
709 let webview_id = self.webview_id()?;
710 self.verify_top_level_browsing_context_is_open(webview_id)?;
713 let url = ServoUrl::parse(¶meters.url)
716 .map(|url| url.into_url())
717 .map_err(|_| WebDriverError::new(ErrorStatus::InvalidArgument, "Invalid URL"))?;
718
719 self.handle_any_user_prompts(webview_id)?;
721
722 let cmd_msg =
723 WebDriverCommandMsg::LoadUrl(webview_id, url, self.load_status_sender.clone());
724 self.send_message_to_embedder(cmd_msg)?;
725
726 self.wait_for_navigation_complete()?;
728
729 self.session_mut()?
731 .set_browsing_context_id(BrowsingContextId::from(webview_id));
732
733 Ok(WebDriverResponse::Void)
734 }
735
736 fn wait_document_ready(&self, timeout: Option<u64>) -> WebDriverResult<WebDriverResponse> {
737 let timeout_channel = match timeout {
738 Some(timeout) => after(Duration::from_millis(timeout)),
739 None => crossbeam_channel::never(),
740 };
741
742 select! {
743 recv(self.load_status_receiver) -> res => {
744 match res {
745 Ok(Ok(WebDriverLoadStatus::Blocked)) => {
747 Ok(WebDriverResponse::Void)
754 },
755 Ok(Ok(WebDriverLoadStatus::Complete)) |
756 Ok(Ok(WebDriverLoadStatus::NavigationStop)) =>
757 Ok(WebDriverResponse::Void)
758 ,
759 _ => Err(WebDriverError::new(
760 ErrorStatus::UnknownError,
761 "Unexpected load status received while waiting for document ready state",
762 )),
763 }
764 },
765 recv(timeout_channel) -> _ => Err(
766 WebDriverError::new(ErrorStatus::Timeout, "Load timed out")
767 ),
768 }
769 }
770
771 fn wait_for_navigation_complete(&self) -> WebDriverResult<WebDriverResponse> {
773 debug!("waiting for load");
774
775 let session = self.session()?;
776
777 if session.page_loading_strategy() == PageLoadStrategy::None {
780 return Ok(WebDriverResponse::Void);
781 }
782
783 if self
786 .verify_browsing_context_is_open(self.browsing_context_id()?)
787 .is_err()
788 {
789 return Ok(WebDriverResponse::Void);
790 }
791
792 let timeout = session.session_timeouts().page_load;
794
795 let result = self.wait_document_ready(timeout);
798 debug!("finished waiting for load with {:?}", result);
799 result
800 }
801
802 fn wait_for_navigation(&self) -> WebDriverResult<WebDriverResponse> {
804 let navigation_status = match self.load_status_receiver.try_recv() {
805 Ok(Ok(status)) => status,
806 Err(crossbeam_channel::TryRecvError::Empty) => {
808 return Ok(WebDriverResponse::Void);
809 },
810 Err(crossbeam_channel::TryRecvError::Disconnected) => {
811 return Err(WebDriverError::new(
812 ErrorStatus::UnknownError,
813 "Load status channel disconnected",
814 ));
815 },
816 Ok(Err(ipc_error)) => {
817 return Err(WebDriverError::new(
818 ErrorStatus::UnknownError,
819 format!("Load status channel ipc error: {ipc_error}"),
820 ));
821 },
822 };
823
824 match navigation_status {
825 WebDriverLoadStatus::NavigationStart => self.wait_for_navigation_complete(),
826 WebDriverLoadStatus::Timeout => Err(WebDriverError::new(
828 ErrorStatus::Timeout,
829 "Navigation timed out",
830 )),
831 WebDriverLoadStatus::Blocked => Ok(WebDriverResponse::Void),
834 WebDriverLoadStatus::NavigationStop | WebDriverLoadStatus::Complete => {
835 unreachable!("Unexpected load status received")
836 },
837 }
838 }
839
840 fn handle_current_url(&self) -> WebDriverResult<WebDriverResponse> {
842 let webview_id = self.webview_id()?;
843
844 self.verify_top_level_browsing_context_is_open(webview_id)?;
847
848 self.handle_any_user_prompts(webview_id)?;
850
851 let (sender, receiver) = ipc::channel().unwrap();
852 self.top_level_script_command(
853 WebDriverScriptCommand::GetUrl(sender),
854 VerifyBrowsingContextIsOpen::No,
855 )?;
856
857 let url = wait_for_ipc_response(receiver)?;
858
859 Ok(WebDriverResponse::Generic(ValueResponse(
860 serde_json::to_value(url)?,
861 )))
862 }
863
864 fn handle_window_rect(
866 &self,
867 verify: VerifyBrowsingContextIsOpen,
868 ) -> WebDriverResult<WebDriverResponse> {
869 let (sender, receiver) = ipc::channel().unwrap();
870 let webview_id = self.webview_id()?;
871 if let VerifyBrowsingContextIsOpen::Yes = verify {
874 self.verify_top_level_browsing_context_is_open(webview_id)?;
875 }
876
877 self.handle_any_user_prompts(webview_id)?;
879
880 self.send_message_to_embedder(WebDriverCommandMsg::GetWindowRect(webview_id, sender))?;
881
882 let window_rect = wait_for_ipc_response(receiver)?;
883 let window_size_response = WindowRectResponse {
884 x: window_rect.min.x,
885 y: window_rect.min.y,
886 width: window_rect.width(),
887 height: window_rect.height(),
888 };
889 Ok(WebDriverResponse::WindowRect(window_size_response))
890 }
891
892 fn handle_set_window_rect(
894 &self,
895 params: &WindowRectParameters,
896 ) -> WebDriverResult<WebDriverResponse> {
897 let webview_id = self.webview_id()?;
905 self.verify_top_level_browsing_context_is_open(webview_id)?;
908
909 self.handle_any_user_prompts(webview_id)?;
911
912 let current = LazyCell::new(|| {
916 let WebDriverResponse::WindowRect(current) = self
917 .handle_window_rect(VerifyBrowsingContextIsOpen::No)
918 .unwrap()
919 else {
920 unreachable!("handle_window_size() must return WindowRect");
921 };
922 current
923 });
924
925 let (x, y, width, height) = (
926 params.x.unwrap_or_else(|| current.x),
927 params.y.unwrap_or_else(|| current.y),
928 params.width.unwrap_or_else(|| current.width),
929 params.height.unwrap_or_else(|| current.height),
930 );
931 let (sender, receiver) = ipc::channel().unwrap();
932 self.send_message_to_embedder(WebDriverCommandMsg::SetWindowRect(
938 webview_id,
939 DeviceIndependentIntRect::from_origin_and_size(
940 Point2D::new(x, y),
941 Size2D::new(width, height),
942 ),
943 sender,
944 ))?;
945
946 let window_rect = wait_for_ipc_response(receiver)?;
947 debug!("Result window_rect: {window_rect:?}");
948 let window_size_response = WindowRectResponse {
949 x: window_rect.min.x,
950 y: window_rect.min.y,
951 width: window_rect.width(),
952 height: window_rect.height(),
953 };
954 Ok(WebDriverResponse::WindowRect(window_size_response))
955 }
956
957 fn handle_maximize_window(&mut self) -> WebDriverResult<WebDriverResponse> {
959 let webview_id = self.webview_id()?;
963 self.verify_top_level_browsing_context_is_open(webview_id)?;
966
967 self.handle_any_user_prompts(self.webview_id()?)?;
969
970 let (sender, receiver) = ipc::channel().unwrap();
976 self.send_message_to_embedder(WebDriverCommandMsg::MaximizeWebView(webview_id, sender))?;
977
978 let window_rect = wait_for_ipc_response(receiver)?;
979 debug!("Result window_rect: {window_rect:?}");
980 let window_size_response = WindowRectResponse {
981 x: window_rect.min.x,
982 y: window_rect.min.y,
983 width: window_rect.width(),
984 height: window_rect.height(),
985 };
986 Ok(WebDriverResponse::WindowRect(window_size_response))
987 }
988
989 fn handle_is_enabled(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
990 let browsing_context = self.browsing_context_id()?;
993 self.verify_browsing_context_is_open(browsing_context)?;
994
995 let webview_id = self.webview_id()?;
997 self.handle_any_user_prompts(webview_id)?;
998
999 let (sender, receiver) = ipc::channel().unwrap();
1000 self.browsing_context_script_command(
1001 WebDriverScriptCommand::IsEnabled(element.to_string(), sender),
1002 VerifyBrowsingContextIsOpen::No,
1003 )?;
1004
1005 Ok(WebDriverResponse::Generic(ValueResponse(
1006 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1007 )))
1008 }
1009
1010 fn handle_is_selected(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1011 let browsing_context = self.browsing_context_id()?;
1014 self.verify_browsing_context_is_open(browsing_context)?;
1015
1016 let webview_id = self.webview_id()?;
1018 self.handle_any_user_prompts(webview_id)?;
1019
1020 let (sender, receiver) = ipc::channel().unwrap();
1021 self.browsing_context_script_command(
1022 WebDriverScriptCommand::IsSelected(element.to_string(), sender),
1023 VerifyBrowsingContextIsOpen::No,
1024 )?;
1025
1026 Ok(WebDriverResponse::Generic(ValueResponse(
1027 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1028 )))
1029 }
1030
1031 fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> {
1033 let webview_id = self.webview_id()?;
1034 self.verify_top_level_browsing_context_is_open(webview_id)?;
1037
1038 self.handle_any_user_prompts(webview_id)?;
1040
1041 self.send_message_to_embedder(WebDriverCommandMsg::GoBack(
1042 webview_id,
1043 self.load_status_sender.clone(),
1044 ))?;
1045 self.wait_for_navigation_complete()
1046 }
1047
1048 fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> {
1050 let webview_id = self.webview_id()?;
1051 self.verify_top_level_browsing_context_is_open(webview_id)?;
1054
1055 self.handle_any_user_prompts(webview_id)?;
1057
1058 self.send_message_to_embedder(WebDriverCommandMsg::GoForward(
1059 webview_id,
1060 self.load_status_sender.clone(),
1061 ))?;
1062 self.wait_for_navigation_complete()
1063 }
1064
1065 fn handle_refresh(&mut self) -> WebDriverResult<WebDriverResponse> {
1067 let webview_id = self.webview_id()?;
1068 self.verify_top_level_browsing_context_is_open(webview_id)?;
1071
1072 self.handle_any_user_prompts(webview_id)?;
1074
1075 let cmd_msg = WebDriverCommandMsg::Refresh(webview_id, self.load_status_sender.clone());
1076 self.send_message_to_embedder(cmd_msg)?;
1077
1078 self.wait_for_navigation_complete()?;
1080
1081 self.session_mut()?
1083 .set_browsing_context_id(BrowsingContextId::from(webview_id));
1084
1085 Ok(WebDriverResponse::Void)
1086 }
1087
1088 fn handle_title(&self) -> WebDriverResult<WebDriverResponse> {
1090 let webview_id = self.webview_id()?;
1091
1092 self.verify_top_level_browsing_context_is_open(webview_id)?;
1095 self.handle_any_user_prompts(webview_id)?;
1097
1098 let (sender, receiver) = ipc::channel().unwrap();
1099
1100 self.top_level_script_command(
1101 WebDriverScriptCommand::GetTitle(sender),
1102 VerifyBrowsingContextIsOpen::No,
1103 )?;
1104
1105 let title = wait_for_ipc_response(receiver)?;
1108 Ok(WebDriverResponse::Generic(ValueResponse(
1109 serde_json::to_value(title)?,
1110 )))
1111 }
1112
1113 fn handle_window_handle(&mut self) -> WebDriverResult<WebDriverResponse> {
1115 let webview_id = self.webview_id()?;
1116
1117 self.verify_top_level_browsing_context_is_open(webview_id)?;
1120
1121 let handle = self
1123 .get_window_handle(webview_id)
1124 .expect("Failed to get window handle of an existing webview");
1125
1126 Ok(WebDriverResponse::Generic(ValueResponse(
1127 serde_json::to_value(handle)?,
1128 )))
1129 }
1130
1131 fn handle_window_handles(&mut self) -> WebDriverResult<WebDriverResponse> {
1133 let mut handles = self.get_window_handles();
1134 handles.sort_unstable();
1135
1136 Ok(WebDriverResponse::Generic(ValueResponse(
1137 serde_json::to_value(handles)?,
1138 )))
1139 }
1140
1141 fn get_window_handle(&mut self, webview_id: WebViewId) -> Option<String> {
1142 self.get_window_handles()
1143 .iter()
1144 .find(|id| id == &&webview_id.to_string())
1145 .cloned()
1146 }
1147
1148 fn get_window_handles(&self) -> Vec<String> {
1149 self.get_all_webview_ids()
1150 .into_iter()
1151 .map(|id| id.to_string())
1152 .collect()
1153 }
1154
1155 fn get_all_webview_ids(&self) -> Vec<WebViewId> {
1156 let (sender, receiver) = ipc::channel().unwrap();
1157 self.send_message_to_embedder(WebDriverCommandMsg::GetAllWebViews(sender))
1158 .unwrap();
1159 wait_for_ipc_response(receiver).unwrap_or_default()
1160 }
1161
1162 fn handle_close_window(&mut self) -> WebDriverResult<WebDriverResponse> {
1164 let webview_id = self.webview_id()?;
1165 self.verify_top_level_browsing_context_is_open(webview_id)?;
1168
1169 self.handle_any_user_prompts(webview_id)?;
1171
1172 let (sender, receiver) = ipc::channel().unwrap();
1174
1175 let cmd_msg = WebDriverCommandMsg::CloseWebView(webview_id, sender);
1176 self.send_message_to_embedder(cmd_msg)?;
1177
1178 wait_for_ipc_response(receiver)?;
1179
1180 let window_handles = self.get_window_handles();
1182
1183 if window_handles.is_empty() {
1184 self.session = None;
1185 }
1186
1187 Ok(WebDriverResponse::CloseWindow(CloseWindowResponse(
1189 window_handles,
1190 )))
1191 }
1192
1193 fn handle_new_window(
1195 &mut self,
1196 _parameters: &NewWindowParameters,
1197 ) -> WebDriverResult<WebDriverResponse> {
1198 let (sender, receiver) = ipc::channel().unwrap();
1199
1200 let webview_id = self.webview_id()?;
1201
1202 self.verify_top_level_browsing_context_is_open(webview_id)?;
1205
1206 self.handle_any_user_prompts(webview_id)?;
1208
1209 let cmd_msg =
1210 WebDriverCommandMsg::NewWebView(sender, Some(self.load_status_sender.clone()));
1211 self.send_message_to_embedder(cmd_msg)?;
1214
1215 if let Ok(webview_id) = receiver.recv() {
1216 let _ = self.wait_for_navigation_complete();
1217 let handle = self
1218 .get_window_handle(webview_id)
1219 .expect("Failed to get window handle of an existing webview");
1220
1221 Ok(WebDriverResponse::NewWindow(NewWindowResponse {
1222 handle,
1223 typ: "tab".to_string(),
1224 }))
1225 } else {
1226 Err(WebDriverError::new(
1227 ErrorStatus::UnknownError,
1228 "No webview ID received",
1229 ))
1230 }
1231 }
1232
1233 fn handle_switch_to_frame(
1235 &mut self,
1236 parameters: &SwitchToFrameParameters,
1237 ) -> WebDriverResult<WebDriverResponse> {
1238 use webdriver::common::FrameId;
1239 let frame_id = match parameters.id {
1240 FrameId::Top => {
1242 let webview_id = self.webview_id()?;
1243 self.verify_top_level_browsing_context_is_open(webview_id)?;
1246 self.handle_any_user_prompts(webview_id)?;
1248 let browsing_context_id = BrowsingContextId::from(webview_id);
1251 self.session_mut()?
1252 .set_browsing_context_id(browsing_context_id);
1253
1254 self.focus_browsing_context(browsing_context_id)?;
1258 return Ok(WebDriverResponse::Void);
1259 },
1260 FrameId::Short(ref x) => {
1262 WebDriverFrameId::Short(*x)
1266 },
1267 FrameId::Element(ref x) => WebDriverFrameId::Element(x.to_string()),
1268 };
1269
1270 self.switch_to_frame(frame_id)
1271 }
1272
1273 fn handle_switch_to_parent_frame(&mut self) -> WebDriverResult<WebDriverResponse> {
1275 let webview_id = self.webview_id()?;
1276 let browsing_context = self.browsing_context_id()?;
1277
1278 if browsing_context == webview_id {
1280 self.verify_browsing_context_is_open(browsing_context)?;
1283 return Ok(WebDriverResponse::Void);
1285 }
1286
1287 let (sender, receiver) = ipc::channel().unwrap();
1290 let cmd = WebDriverScriptCommand::GetParentFrameId(sender);
1291 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1292
1293 self.handle_any_user_prompts(webview_id)?;
1295
1296 let browsing_context_id = wait_for_ipc_response_flatten(receiver)?;
1299 self.session_mut()?
1300 .set_browsing_context_id(browsing_context_id);
1301 self.focus_browsing_context(browsing_context_id)?;
1305 Ok(WebDriverResponse::Void)
1306 }
1307
1308 fn handle_switch_to_window(
1310 &mut self,
1311 parameters: &SwitchToWindowParameters,
1312 ) -> WebDriverResult<WebDriverResponse> {
1313 let Some(webview_id) = self
1314 .get_all_webview_ids()
1315 .into_iter()
1316 .find(|id| id.to_string() == parameters.handle)
1317 else {
1318 return Err(WebDriverError::new(
1319 ErrorStatus::NoSuchWindow,
1320 "No such window while switching to window",
1321 ));
1322 };
1323
1324 let session = self.session_mut()?;
1325 session.set_webview_id(webview_id);
1326 session.set_browsing_context_id(BrowsingContextId::from(webview_id));
1327
1328 self.focus_webview(webview_id)?;
1332
1333 Ok(WebDriverResponse::Void)
1334 }
1335
1336 fn switch_to_frame(
1337 &mut self,
1338 frame_id: WebDriverFrameId,
1339 ) -> WebDriverResult<WebDriverResponse> {
1340 let (sender, receiver) = ipc::channel().unwrap();
1341 let cmd = WebDriverScriptCommand::GetBrowsingContextId(frame_id, sender);
1342 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1343 self.handle_any_user_prompts(self.webview_id()?)?;
1344
1345 let browsing_context_id = wait_for_ipc_response_flatten(receiver)?;
1346 self.session_mut()?
1347 .set_browsing_context_id(browsing_context_id);
1348 self.focus_browsing_context(browsing_context_id)?;
1352 Ok(WebDriverResponse::Void)
1353 }
1354
1355 fn handle_find_element(
1357 &self,
1358 parameters: &LocatorParameters,
1359 ) -> WebDriverResult<WebDriverResponse> {
1360 let res = self.handle_find_elements(parameters)?;
1362 unwrap_first_element_response(res)
1365 }
1366
1367 fn implicit_wait<T>(
1370 &self,
1371 callback: impl Fn() -> Result<(bool, T), (bool, WebDriverError)>,
1372 ) -> Result<T, WebDriverError> {
1373 let now = Instant::now();
1374 let (implicit_wait, sleep_interval) = {
1375 let timeouts = self.session()?.session_timeouts();
1376 (
1377 Duration::from_millis(timeouts.implicit_wait.unwrap_or(0)),
1378 Duration::from_millis(timeouts.sleep_interval),
1379 )
1380 };
1381
1382 loop {
1383 match callback() {
1384 Ok((can_early_return, value)) => {
1385 if can_early_return || now.elapsed() >= implicit_wait {
1386 return Ok(value);
1387 }
1388 },
1389 Err((can_early_return, error)) => {
1390 if can_early_return || now.elapsed() >= implicit_wait {
1391 return Err(error);
1392 }
1393 },
1394 }
1395 sleep(sleep_interval);
1396 }
1397 }
1398
1399 fn handle_find_elements(
1401 &self,
1402 parameters: &LocatorParameters,
1403 ) -> WebDriverResult<WebDriverResponse> {
1404 if parameters.value.is_empty() {
1406 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1407 }
1408 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1411
1412 self.handle_any_user_prompts(self.webview_id()?)?;
1414
1415 self.implicit_wait(|| {
1416 let (sender, receiver) = ipc::channel().unwrap();
1417 let cmd = match parameters.using {
1418 LocatorStrategy::CSSSelector => WebDriverScriptCommand::FindElementsCSSSelector(
1419 parameters.value.clone(),
1420 sender,
1421 ),
1422 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1423 WebDriverScriptCommand::FindElementsLinkText(
1424 parameters.value.clone(),
1425 parameters.using == LocatorStrategy::PartialLinkText,
1426 sender,
1427 )
1428 },
1429 LocatorStrategy::TagName => {
1430 WebDriverScriptCommand::FindElementsTagName(parameters.value.clone(), sender)
1431 },
1432 LocatorStrategy::XPath => WebDriverScriptCommand::FindElementsXpathSelector(
1433 parameters.value.clone(),
1434 sender,
1435 ),
1436 };
1437 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)
1438 .map_err(|error| (ImplicitWait::Return.into(), error))?;
1439 wait_for_ipc_response_flatten(receiver)
1440 .map(|value| (!value.is_empty(), value))
1441 .map_err(|error| (ImplicitWait::Return.into(), error))
1442 })
1443 .and_then(|response| {
1444 let resp_value: Vec<WebElement> = response.into_iter().map(WebElement).collect();
1445 Ok(WebDriverResponse::Generic(ValueResponse(
1446 serde_json::to_value(resp_value)?,
1447 )))
1448 })
1449 }
1450
1451 fn handle_find_element_from_element(
1453 &self,
1454 element: &WebElement,
1455 parameters: &LocatorParameters,
1456 ) -> WebDriverResult<WebDriverResponse> {
1457 let res = self.handle_find_elements_from_element(element, parameters)?;
1459 unwrap_first_element_response(res)
1462 }
1463
1464 fn handle_find_elements_from_element(
1466 &self,
1467 element: &WebElement,
1468 parameters: &LocatorParameters,
1469 ) -> WebDriverResult<WebDriverResponse> {
1470 if parameters.value.is_empty() {
1472 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1473 }
1474 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1477
1478 self.handle_any_user_prompts(self.webview_id()?)?;
1480
1481 self.implicit_wait(|| {
1482 let (sender, receiver) = ipc::channel().unwrap();
1483
1484 let cmd = match parameters.using {
1485 LocatorStrategy::CSSSelector => {
1486 WebDriverScriptCommand::FindElementElementsCSSSelector(
1487 parameters.value.clone(),
1488 element.to_string(),
1489 sender,
1490 )
1491 },
1492 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1493 WebDriverScriptCommand::FindElementElementsLinkText(
1494 parameters.value.clone(),
1495 element.to_string(),
1496 parameters.using == LocatorStrategy::PartialLinkText,
1497 sender,
1498 )
1499 },
1500 LocatorStrategy::TagName => WebDriverScriptCommand::FindElementElementsTagName(
1501 parameters.value.clone(),
1502 element.to_string(),
1503 sender,
1504 ),
1505 LocatorStrategy::XPath => WebDriverScriptCommand::FindElementElementsXPathSelector(
1506 parameters.value.clone(),
1507 element.to_string(),
1508 sender,
1509 ),
1510 };
1511 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)
1512 .map_err(|error| (ImplicitWait::Return.into(), error))?;
1513 wait_for_ipc_response_flatten(receiver)
1514 .map(|value| (!value.is_empty(), value))
1515 .map_err(|error| (ImplicitWait::Return.into(), error))
1516 })
1517 .and_then(|response| {
1518 let resp_value: Vec<Value> = response
1519 .into_iter()
1520 .map(|x| serde_json::to_value(WebElement(x)).unwrap())
1521 .collect();
1522 Ok(WebDriverResponse::Generic(ValueResponse(
1523 serde_json::to_value(resp_value)?,
1524 )))
1525 })
1526 }
1527
1528 fn handle_find_elements_from_shadow_root(
1530 &self,
1531 shadow_root: &ShadowRoot,
1532 parameters: &LocatorParameters,
1533 ) -> WebDriverResult<WebDriverResponse> {
1534 if parameters.value.is_empty() {
1536 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1537 }
1538
1539 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1542
1543 self.handle_any_user_prompts(self.webview_id()?)?;
1545
1546 self.implicit_wait(|| {
1547 let (sender, receiver) = ipc::channel().unwrap();
1548
1549 let cmd = match parameters.using {
1550 LocatorStrategy::CSSSelector => {
1551 WebDriverScriptCommand::FindShadowElementsCSSSelector(
1552 parameters.value.clone(),
1553 shadow_root.to_string(),
1554 sender,
1555 )
1556 },
1557 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1558 WebDriverScriptCommand::FindShadowElementsLinkText(
1559 parameters.value.clone(),
1560 shadow_root.to_string(),
1561 parameters.using == LocatorStrategy::PartialLinkText,
1562 sender,
1563 )
1564 },
1565 LocatorStrategy::TagName => WebDriverScriptCommand::FindShadowElementsTagName(
1566 parameters.value.clone(),
1567 shadow_root.to_string(),
1568 sender,
1569 ),
1570 LocatorStrategy::XPath => WebDriverScriptCommand::FindShadowElementsXPathSelector(
1571 parameters.value.clone(),
1572 shadow_root.to_string(),
1573 sender,
1574 ),
1575 };
1576 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)
1577 .map_err(|error| (ImplicitWait::Return.into(), error))?;
1578 wait_for_ipc_response_flatten(receiver)
1579 .map(|value| (!value.is_empty(), value))
1580 .map_err(|error| (ImplicitWait::Return.into(), error))
1581 })
1582 .and_then(|response| {
1583 let resp_value: Vec<Value> = response
1584 .into_iter()
1585 .map(|x| serde_json::to_value(WebElement(x)).unwrap())
1586 .collect();
1587 Ok(WebDriverResponse::Generic(ValueResponse(
1588 serde_json::to_value(resp_value)?,
1589 )))
1590 })
1591 }
1592
1593 fn handle_find_element_from_shadow_root(
1595 &self,
1596 shadow_root: &ShadowRoot,
1597 parameters: &LocatorParameters,
1598 ) -> WebDriverResult<WebDriverResponse> {
1599 let res = self.handle_find_elements_from_shadow_root(shadow_root, parameters)?;
1601 unwrap_first_element_response(res)
1604 }
1605
1606 fn handle_get_shadow_root(&self, element: WebElement) -> WebDriverResult<WebDriverResponse> {
1608 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1611 self.handle_any_user_prompts(self.webview_id()?)?;
1613 let (sender, receiver) = ipc::channel().unwrap();
1614 let cmd = WebDriverScriptCommand::GetElementShadowRoot(element.to_string(), sender);
1615 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1616 let Some(value) = wait_for_ipc_response_flatten(receiver)? else {
1618 return Err(WebDriverError::new(ErrorStatus::NoSuchShadowRoot, ""));
1619 };
1620 Ok(WebDriverResponse::Generic(ValueResponse(
1621 serde_json::to_value(ShadowRoot(value))?,
1622 )))
1623 }
1624
1625 fn handle_element_rect(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1627 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1630 self.handle_any_user_prompts(self.webview_id()?)?;
1632 let (sender, receiver) = ipc::channel().unwrap();
1633 let cmd = WebDriverScriptCommand::GetElementRect(element.to_string(), sender);
1634 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1635 let rect = wait_for_ipc_response_flatten(receiver)?;
1636 let response = ElementRectResponse {
1637 x: rect.origin.x,
1638 y: rect.origin.y,
1639 width: rect.size.width,
1640 height: rect.size.height,
1641 };
1642 Ok(WebDriverResponse::ElementRect(response))
1643 }
1644
1645 fn handle_element_text(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1647 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1650 self.handle_any_user_prompts(self.webview_id()?)?;
1652 let (sender, receiver) = ipc::channel().unwrap();
1653 let cmd = WebDriverScriptCommand::GetElementText(element.to_string(), sender);
1654 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1655 Ok(WebDriverResponse::Generic(ValueResponse(
1656 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1657 )))
1658 }
1659
1660 fn handle_active_element(&self) -> WebDriverResult<WebDriverResponse> {
1662 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1665 self.handle_any_user_prompts(self.webview_id()?)?;
1667 let (sender, receiver) = ipc::channel().unwrap();
1668 let cmd = WebDriverScriptCommand::GetActiveElement(sender);
1669 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1670 let value =
1671 wait_for_ipc_response(receiver)?.map(|x| serde_json::to_value(WebElement(x)).unwrap());
1672 if value.is_some() {
1676 Ok(WebDriverResponse::Generic(ValueResponse(
1677 serde_json::to_value(value)?,
1678 )))
1679 } else {
1680 Err(WebDriverError::new(
1681 ErrorStatus::NoSuchElement,
1682 "No active element found",
1683 ))
1684 }
1685 }
1686
1687 fn handle_computed_role(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1689 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1692 self.handle_any_user_prompts(self.webview_id()?)?;
1694 let (sender, receiver) = ipc::channel().unwrap();
1695 let cmd = WebDriverScriptCommand::GetComputedRole(element.to_string(), sender);
1696 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1697 Ok(WebDriverResponse::Generic(ValueResponse(
1698 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1699 )))
1700 }
1701
1702 fn handle_element_tag_name(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1704 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1707 self.handle_any_user_prompts(self.webview_id()?)?;
1709 let (sender, receiver) = ipc::channel().unwrap();
1710 let cmd = WebDriverScriptCommand::GetElementTagName(element.to_string(), sender);
1711 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1712 Ok(WebDriverResponse::Generic(ValueResponse(
1713 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1714 )))
1715 }
1716
1717 fn handle_element_attribute(
1719 &self,
1720 element: &WebElement,
1721 name: &str,
1722 ) -> WebDriverResult<WebDriverResponse> {
1723 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1726 self.handle_any_user_prompts(self.webview_id()?)?;
1728 let (sender, receiver) = ipc::channel().unwrap();
1729 let cmd = WebDriverScriptCommand::GetElementAttribute(
1730 element.to_string(),
1731 name.to_owned(),
1732 sender,
1733 );
1734 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1735 Ok(WebDriverResponse::Generic(ValueResponse(
1736 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1737 )))
1738 }
1739
1740 fn handle_element_property(
1742 &self,
1743 element: &WebElement,
1744 name: &str,
1745 ) -> WebDriverResult<WebDriverResponse> {
1746 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1749 self.handle_any_user_prompts(self.webview_id()?)?;
1751 let (sender, receiver) = ipc::channel().unwrap();
1752
1753 let cmd = WebDriverScriptCommand::GetElementProperty(
1754 element.to_string(),
1755 name.to_owned(),
1756 sender,
1757 );
1758 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1759
1760 Ok(WebDriverResponse::Generic(ValueResponse(
1761 serde_json::to_value(SendableJSValue(wait_for_ipc_response_flatten(receiver)?))?,
1762 )))
1763 }
1764
1765 fn handle_element_css(
1767 &self,
1768 element: &WebElement,
1769 name: &str,
1770 ) -> WebDriverResult<WebDriverResponse> {
1771 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1774 self.handle_any_user_prompts(self.webview_id()?)?;
1776 let (sender, receiver) = ipc::channel().unwrap();
1777 let cmd =
1778 WebDriverScriptCommand::GetElementCSS(element.to_string(), name.to_owned(), sender);
1779 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1780 Ok(WebDriverResponse::Generic(ValueResponse(
1781 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1782 )))
1783 }
1784
1785 fn handle_get_cookies(&self) -> WebDriverResult<WebDriverResponse> {
1787 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1790 self.handle_any_user_prompts(self.webview_id()?)?;
1792 let (sender, receiver) = ipc::channel().unwrap();
1793 let cmd = WebDriverScriptCommand::GetCookies(sender);
1794 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1795 let cookies = wait_for_ipc_response_flatten(receiver)?;
1796 let response = cookies
1797 .into_iter()
1798 .map(|cookie| cookie_msg_to_cookie(cookie.into_inner()))
1799 .collect::<Vec<Cookie>>();
1800 Ok(WebDriverResponse::Cookies(CookiesResponse(response)))
1801 }
1802
1803 fn handle_get_cookie(&self, name: String) -> WebDriverResult<WebDriverResponse> {
1805 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1808 self.handle_any_user_prompts(self.webview_id()?)?;
1810 let (sender, receiver) = ipc::channel().unwrap();
1811 let cmd = WebDriverScriptCommand::GetCookie(name, sender);
1812 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1813 let cookies = wait_for_ipc_response_flatten(receiver)?;
1814 let Some(response) = cookies
1815 .into_iter()
1816 .map(|cookie| cookie_msg_to_cookie(cookie.into_inner()))
1817 .next()
1818 else {
1819 return Err(WebDriverError::new(ErrorStatus::NoSuchCookie, ""));
1820 };
1821 Ok(WebDriverResponse::Cookie(CookieResponse(response)))
1822 }
1823
1824 fn handle_add_cookie(
1826 &self,
1827 params: &AddCookieParameters,
1828 ) -> WebDriverResult<WebDriverResponse> {
1829 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1832 self.handle_any_user_prompts(self.webview_id()?)?;
1834 let (sender, receiver) = ipc::channel().unwrap();
1835
1836 let mut cookie_builder =
1837 CookieBuilder::new(params.name.to_owned(), params.value.to_owned())
1838 .secure(params.secure)
1839 .http_only(params.httpOnly);
1840 if let Some(ref domain) = params.domain {
1841 cookie_builder = cookie_builder.domain(domain.clone());
1842 }
1843 if let Some(ref path) = params.path {
1844 cookie_builder = cookie_builder.path(path.clone());
1845 }
1846 if let Some(ref expiry) = params.expiry {
1847 if let Ok(datetime) = OffsetDateTime::from_unix_timestamp(expiry.0 as i64) {
1848 cookie_builder = cookie_builder.expires(datetime);
1849 }
1850 }
1851 if let Some(ref same_site) = params.sameSite {
1852 cookie_builder = match same_site.as_str() {
1853 "None" => Ok(cookie_builder.same_site(SameSite::None)),
1854 "Lax" => Ok(cookie_builder.same_site(SameSite::Lax)),
1855 "Strict" => Ok(cookie_builder.same_site(SameSite::Strict)),
1856 _ => Err(WebDriverError::new(
1857 ErrorStatus::InvalidArgument,
1858 "invalid argument",
1859 )),
1860 }?;
1861 }
1862
1863 let cmd = WebDriverScriptCommand::AddCookie(cookie_builder.build(), sender);
1864 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1865 wait_for_ipc_response_flatten(receiver)?;
1866 Ok(WebDriverResponse::Void)
1867 }
1868
1869 fn handle_delete_cookie(&self, name: String) -> WebDriverResult<WebDriverResponse> {
1871 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1874 self.handle_any_user_prompts(self.webview_id()?)?;
1876 let (sender, receiver) = ipc::channel().unwrap();
1877 let cmd = WebDriverScriptCommand::DeleteCookie(name, sender);
1878 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1879 wait_for_ipc_response_flatten(receiver)?;
1880 Ok(WebDriverResponse::Void)
1881 }
1882
1883 fn handle_delete_cookies(&self) -> WebDriverResult<WebDriverResponse> {
1885 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1888 self.handle_any_user_prompts(self.webview_id()?)?;
1890 let (sender, receiver) = ipc::channel().unwrap();
1891 let cmd = WebDriverScriptCommand::DeleteCookies(sender);
1892 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1893 wait_for_ipc_response_flatten(receiver)?;
1894 Ok(WebDriverResponse::Void)
1895 }
1896
1897 fn handle_get_timeouts(&mut self) -> WebDriverResult<WebDriverResponse> {
1899 let timeouts = self.session()?.session_timeouts();
1900
1901 let timeouts = TimeoutsResponse {
1905 script: timeouts.script,
1906 page_load: timeouts.page_load.unwrap_or(DEFAULT_PAGE_LOAD_TIMEOUT),
1907 implicit: timeouts.implicit_wait.unwrap_or(0),
1908 };
1909
1910 Ok(WebDriverResponse::Timeouts(timeouts))
1911 }
1912
1913 fn handle_set_timeouts(
1915 &mut self,
1916 parameters: &TimeoutsParameters,
1917 ) -> WebDriverResult<WebDriverResponse> {
1918 let session = self.session_mut()?;
1919
1920 if let Some(timeout) = parameters.script {
1921 session.session_timeouts_mut().script = timeout;
1922 }
1923 if let Some(timeout) = parameters.page_load {
1924 session.session_timeouts_mut().page_load = Some(timeout);
1925 }
1926 if let Some(timeout) = parameters.implicit {
1927 session.session_timeouts_mut().implicit_wait = Some(timeout);
1928 }
1929
1930 Ok(WebDriverResponse::Void)
1931 }
1932
1933 fn handle_get_page_source(&self) -> WebDriverResult<WebDriverResponse> {
1935 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1938 self.handle_any_user_prompts(self.webview_id()?)?;
1940 let (sender, receiver) = ipc::channel().unwrap();
1941
1942 let cmd = WebDriverScriptCommand::GetPageSource(sender);
1943 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1944
1945 Ok(WebDriverResponse::Generic(ValueResponse(
1946 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1947 )))
1948 }
1949
1950 fn handle_perform_actions(
1952 &mut self,
1953 parameters: ActionsParameters,
1954 ) -> WebDriverResult<WebDriverResponse> {
1955 let browsing_context = self.browsing_context_id()?;
1956 self.verify_browsing_context_is_open(browsing_context)?;
1959
1960 self.handle_any_user_prompts(self.webview_id()?)?;
1962
1963 let actions_by_tick = self.extract_an_action_sequence(parameters.actions);
1965
1966 match self.dispatch_actions(actions_by_tick, browsing_context) {
1968 Ok(_) => Ok(WebDriverResponse::Void),
1969 Err(error) => Err(WebDriverError::new(error, "")),
1970 }
1971 }
1972
1973 fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
1975 let browsing_context_id = self.browsing_context_id()?;
1976 self.verify_browsing_context_is_open(browsing_context_id)?;
1979
1980 self.handle_any_user_prompts(self.webview_id()?)?;
1982
1983 let undo_actions = self
1990 .session_mut()?
1991 .input_cancel_list
1992 .drain(..)
1993 .rev()
1994 .map(|(id, action_item)| Vec::from([(id, action_item)]))
1995 .collect();
1996 if let Err(err) = self.dispatch_actions(undo_actions, browsing_context_id) {
1998 return Err(WebDriverError::new(err, "Failed to dispatch undo actions"));
1999 }
2000
2001 self.session_mut()?.input_state_table.clear();
2003
2004 Ok(WebDriverResponse::Void)
2005 }
2006
2007 fn handle_execute_script(
2009 &self,
2010 parameters: JavascriptCommandParameters,
2011 ) -> WebDriverResult<WebDriverResponse> {
2012 let (func_body, args_string) = self.extract_script_arguments(parameters)?;
2015 let script = format!(
2019 "(function() {{ {}\n }})({})",
2020 func_body,
2021 args_string.join(", ")
2022 );
2023 debug!("{}", script);
2024
2025 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2028
2029 self.handle_any_user_prompts(self.webview_id()?)?;
2031
2032 let (sender, receiver) = ipc::channel().unwrap();
2033 let cmd = WebDriverScriptCommand::ExecuteScript(script, sender);
2034 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2035
2036 let timeout_duration = self
2037 .session()?
2038 .session_timeouts()
2039 .script
2040 .map(Duration::from_millis);
2041 let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2042
2043 self.javascript_evaluation_result_to_webdriver_response(result)
2044 }
2045
2046 fn handle_execute_async_script(
2047 &self,
2048 parameters: JavascriptCommandParameters,
2049 ) -> WebDriverResult<WebDriverResponse> {
2050 let (function_body, mut args_string) = self.extract_script_arguments(parameters)?;
2053 args_string.push("resolve".to_string());
2054
2055 let joined_args = args_string.join(", ");
2056 let script = format!(
2057 r#"(function() {{
2058 let webdriverPromise = new Promise(function(resolve, reject) {{
2059 (async function() {{
2060 {function_body}
2061 }})({joined_args})
2062 .then((v) => {{}}, (err) => reject(err))
2063 }})
2064 .then((v) => window.webdriverCallback(v), (r) => window.webdriverException(r))
2065 .catch((r) => window.webdriverException(r));
2066 }})();"#,
2067 );
2068 debug!("{}", script);
2069
2070 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2073
2074 self.handle_any_user_prompts(self.webview_id()?)?;
2076
2077 let (sender, receiver) = ipc::channel().unwrap();
2078 self.browsing_context_script_command(
2079 WebDriverScriptCommand::ExecuteAsyncScript(script, sender),
2080 VerifyBrowsingContextIsOpen::No,
2081 )?;
2082
2083 let timeout_duration = self
2084 .session()?
2085 .session_timeouts()
2086 .script
2087 .map(Duration::from_millis);
2088 let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2089
2090 self.javascript_evaluation_result_to_webdriver_response(result)
2091 }
2092
2093 fn javascript_evaluation_result_to_webdriver_response(
2094 &self,
2095 result: WebDriverJSResult,
2096 ) -> WebDriverResult<WebDriverResponse> {
2097 match result {
2098 Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
2099 serde_json::to_value(SendableJSValue(value))?,
2100 ))),
2101 Err(error) => {
2102 let message = format!("{error:?}");
2103 let status = match error {
2104 JavaScriptEvaluationError::DocumentNotFound => ErrorStatus::NoSuchWindow,
2105 JavaScriptEvaluationError::CompilationFailure => ErrorStatus::JavascriptError,
2106 JavaScriptEvaluationError::EvaluationFailure(Some(error_info)) => {
2107 return Err(WebDriverError::new_with_data(
2108 ErrorStatus::JavascriptError,
2109 error_info.message,
2110 None,
2111 error_info.stack,
2112 ));
2113 },
2114 JavaScriptEvaluationError::EvaluationFailure(None) => {
2115 ErrorStatus::JavascriptError
2116 },
2117 JavaScriptEvaluationError::InternalError => ErrorStatus::JavascriptError,
2118 JavaScriptEvaluationError::SerializationError(serialization_error) => {
2119 match serialization_error {
2120 JavaScriptEvaluationResultSerializationError::DetachedShadowRoot => {
2121 ErrorStatus::DetachedShadowRoot
2122 },
2123 JavaScriptEvaluationResultSerializationError::OtherJavaScriptError => {
2124 ErrorStatus::JavascriptError
2125 },
2126 JavaScriptEvaluationResultSerializationError::StaleElementReference => {
2127 ErrorStatus::StaleElementReference
2128 },
2129 JavaScriptEvaluationResultSerializationError::UnknownType => {
2130 ErrorStatus::UnsupportedOperation
2131 },
2132 }
2133 },
2134 JavaScriptEvaluationError::WebViewNotReady => ErrorStatus::NoSuchWindow,
2135 };
2136 Err(WebDriverError::new(status, message))
2137 },
2138 }
2139 }
2140
2141 fn handle_element_send_keys(
2143 &mut self,
2144 element: &WebElement,
2145 keys: &SendKeysParameters,
2146 ) -> WebDriverResult<WebDriverResponse> {
2147 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2150 self.handle_any_user_prompts(self.webview_id()?)?;
2152
2153 let (sender, receiver) = ipc::channel().unwrap();
2154 let cmd = WebDriverScriptCommand::WillSendKeys(
2155 element.to_string(),
2156 keys.text.to_string(),
2157 self.session()?.strict_file_interactability(),
2158 sender,
2159 );
2160 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2161
2162 if !wait_for_ipc_response_flatten(receiver)? {
2165 return Ok(WebDriverResponse::Void);
2166 }
2167
2168 let id = Uuid::new_v4().to_string();
2170
2171 self.session_mut()?
2173 .input_state_table
2174 .insert(id.clone(), InputSourceState::Key(KeyInputState::new()));
2175
2176 let input_events = send_keys(&keys.text);
2179
2180 for event in input_events {
2181 match event {
2182 DispatchStringEvent::Keyboard(event) => {
2183 let raw_string = convert_keyboard_event_to_string(&event);
2184 let key_action = match event.state {
2185 KeyState::Down => KeyAction::Down(KeyDownAction { value: raw_string }),
2186 KeyState::Up => KeyAction::Up(KeyUpAction { value: raw_string }),
2187 };
2188 let action_sequence = ActionSequence {
2189 id: id.clone(),
2190 actions: ActionsType::Key {
2191 actions: vec![KeyActionItem::Key(key_action)],
2192 },
2193 };
2194
2195 let actions_by_tick = self.extract_an_action_sequence(vec![action_sequence]);
2196 if let Err(e) =
2197 self.dispatch_actions(actions_by_tick, self.browsing_context_id()?)
2198 {
2199 error!("handle_element_send_keys: dispatch_actions failed: {:?}", e);
2200 }
2201 },
2202 DispatchStringEvent::Composition(event) => {
2203 self.send_input_event_to_embedder(InputEvent::Ime(ImeEvent::Composition(
2204 event,
2205 )));
2206 },
2207 }
2208 }
2209
2210 if self
2215 .session()?
2216 .input_cancel_list
2217 .iter()
2218 .all(|(cancel_item_id, _)| &id != cancel_item_id)
2219 {
2220 self.session_mut()?.input_state_table.remove(&id);
2221 }
2222
2223 Ok(WebDriverResponse::Void)
2224 }
2225
2226 fn handle_element_clear(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2228 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2231
2232 self.handle_any_user_prompts(self.webview_id()?)?;
2234
2235 let (sender, receiver) = ipc::channel().unwrap();
2237 let cmd = WebDriverScriptCommand::ElementClear(element.to_string(), sender);
2238 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2239
2240 wait_for_ipc_response_flatten(receiver)?;
2241 Ok(WebDriverResponse::Void)
2242 }
2243
2244 fn handle_element_click(&mut self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2246 let browsing_context_id = self.browsing_context_id()?;
2249 self.verify_browsing_context_is_open(browsing_context_id)?;
2250
2251 self.handle_any_user_prompts(self.webview_id()?)?;
2253
2254 let (sender, receiver) = ipc::channel().unwrap();
2255
2256 let cmd = WebDriverScriptCommand::ElementClick(element.to_string(), sender);
2258 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2259
2260 match wait_for_ipc_response_flatten(receiver)? {
2261 Some(element_id) => {
2262 self.add_load_status_sender()?;
2265
2266 self.perform_element_click(element_id)?;
2267
2268 let res = self.wait_for_navigation()?;
2271
2272 self.clear_load_status_sender()?;
2274
2275 Ok(res)
2276 },
2277 None => Ok(WebDriverResponse::Void),
2279 }
2280 }
2281
2282 fn perform_element_click(&mut self, element: String) -> WebDriverResult<WebDriverResponse> {
2285 let id = Uuid::new_v4().to_string();
2287
2288 let pointer_ids = self.session()?.pointer_ids();
2289 self.session_mut()?.input_state_table.insert(
2290 id.clone(),
2291 InputSourceState::Pointer(PointerInputState::new(PointerType::Mouse, pointer_ids)),
2292 );
2293
2294 let pointer_move_action = PointerMoveAction {
2299 duration: None,
2300 origin: PointerOrigin::Element(WebElement(element)),
2301 x: 0.0,
2302 y: 0.0,
2303 ..Default::default()
2304 };
2305
2306 let pointer_down_action = PointerDownAction {
2309 button: i16::from(MouseButton::Left) as u64,
2310 ..Default::default()
2311 };
2312
2313 let pointer_up_action = PointerUpAction {
2316 button: i16::from(MouseButton::Left) as u64,
2317 ..Default::default()
2318 };
2319
2320 let action_sequence = ActionSequence {
2321 id: id.clone(),
2322 actions: ActionsType::Pointer {
2323 parameters: PointerActionParameters {
2324 pointer_type: PointerType::Mouse,
2325 },
2326 actions: vec![
2327 PointerActionItem::Pointer(PointerAction::Move(pointer_move_action)),
2328 PointerActionItem::Pointer(PointerAction::Down(pointer_down_action)),
2329 PointerActionItem::Pointer(PointerAction::Up(pointer_up_action)),
2330 ],
2331 },
2332 };
2333
2334 let actions_by_tick = self.extract_an_action_sequence(vec![action_sequence]);
2336 if let Err(e) = self.dispatch_actions(actions_by_tick, self.browsing_context_id()?) {
2337 error!("handle_element_click: dispatch_actions failed: {:?}", e);
2338 }
2339
2340 self.session_mut()?.input_state_table.remove(&id);
2342
2343 Ok(WebDriverResponse::Void)
2344 }
2345
2346 fn take_screenshot(&self, rect: Option<Rect<f32, CSSPixel>>) -> WebDriverResult<String> {
2347 let _ = self.handle_execute_async_script(JavascriptCommandParameters {
2349 script: "requestAnimationFrame(() => arguments[0]());".to_string(),
2350 args: None,
2351 });
2352 if rect.as_ref().is_some_and(Rect::is_empty) {
2353 return Err(WebDriverError::new(
2354 ErrorStatus::UnknownError,
2355 "The requested `rect` has zero width and/or height",
2356 ));
2357 }
2358
2359 let webview_id = self.webview_id()?;
2360 let (sender, receiver) = crossbeam_channel::unbounded();
2361 self.send_message_to_embedder(WebDriverCommandMsg::TakeScreenshot(
2362 webview_id, rect, sender,
2363 ))?;
2364
2365 let result = match receiver.recv_timeout(SCREENSHOT_TIMEOUT) {
2366 Ok(result) => Ok(result),
2367 Err(RecvTimeoutError::Timeout) => Err(WebDriverError::new(
2368 ErrorStatus::Timeout,
2369 "Timed out waiting to take screenshot. Test likely didn't finish.",
2370 )),
2371 Err(RecvTimeoutError::Disconnected) => Err(WebDriverError::new(
2372 ErrorStatus::UnknownError,
2373 "Could not take screenshot because channel disconnected.",
2374 )),
2375 }?;
2376
2377 let image = result.map_err(|error| {
2378 WebDriverError::new(
2379 ErrorStatus::UnknownError,
2380 format!("Failed to take screenshot: {error:?}"),
2381 )
2382 })?;
2383
2384 let mut png_data = Cursor::new(Vec::new());
2385 DynamicImage::ImageRgba8(image)
2386 .write_to(&mut png_data, ImageFormat::Png)
2387 .unwrap();
2388
2389 Ok(base64::engine::general_purpose::STANDARD.encode(png_data.get_ref()))
2390 }
2391
2392 fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> {
2393 let webview_id = self.webview_id()?;
2396 self.verify_top_level_browsing_context_is_open(webview_id)?;
2397
2398 self.handle_any_user_prompts(webview_id)?;
2399
2400 let encoded = self.take_screenshot(None)?;
2402
2403 Ok(WebDriverResponse::Generic(ValueResponse(
2404 serde_json::to_value(encoded)?,
2405 )))
2406 }
2407
2408 fn handle_take_element_screenshot(
2409 &self,
2410 element: &WebElement,
2411 ) -> WebDriverResult<WebDriverResponse> {
2412 let webview_id = self.webview_id()?;
2415 self.verify_top_level_browsing_context_is_open(webview_id)?;
2416
2417 self.handle_any_user_prompts(webview_id)?;
2419
2420 let (sender, receiver) = ipc::channel().unwrap();
2423 let cmd =
2424 WebDriverScriptCommand::ScrollAndGetBoundingClientRect(element.to_string(), sender);
2425 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
2426
2427 let rect = wait_for_ipc_response_flatten(receiver)?;
2428
2429 let encoded = self.take_screenshot(Some(Rect::from_untyped(&rect)))?;
2431
2432 Ok(WebDriverResponse::Generic(ValueResponse(
2434 serde_json::to_value(encoded)?,
2435 )))
2436 }
2437
2438 fn handle_custom_handlers_set_mode(
2440 &self,
2441 parameters: &CustomHandlersSetModeParameters,
2442 ) -> WebDriverResult<WebDriverResponse> {
2443 let mode = match parameters.mode.as_str() {
2446 "autoAccept" => CustomHandlersAutomationMode::AutoAccept,
2447 "autoReject" => CustomHandlersAutomationMode::AutoReject,
2448 "none" => CustomHandlersAutomationMode::None,
2449 _ => {
2450 return Err(WebDriverError::new(
2451 ErrorStatus::InvalidArgument,
2452 "invalid argument",
2453 ));
2454 },
2455 };
2456 self.top_level_script_command(
2459 WebDriverScriptCommand::SetProtocolHandlerAutomationMode(mode),
2460 VerifyBrowsingContextIsOpen::Yes,
2461 )?;
2462 Ok(WebDriverResponse::Void)
2464 }
2465
2466 fn handle_get_prefs(
2467 &self,
2468 parameters: &GetPrefsParameters,
2469 ) -> WebDriverResult<WebDriverResponse> {
2470 let prefs = parameters
2471 .prefs
2472 .iter()
2473 .map(|item| {
2474 (
2475 item.clone(),
2476 serde_json::to_value(prefs::get().get_value(item)).unwrap(),
2477 )
2478 })
2479 .collect::<BTreeMap<_, _>>();
2480
2481 Ok(WebDriverResponse::Generic(ValueResponse(
2482 serde_json::to_value(prefs)?,
2483 )))
2484 }
2485
2486 fn handle_set_prefs(
2487 &self,
2488 parameters: &SetPrefsParameters,
2489 ) -> WebDriverResult<WebDriverResponse> {
2490 let mut current_preferences = prefs::get().clone();
2491 for (key, value) in parameters.prefs.iter() {
2492 current_preferences.set_value(key, value.0.clone());
2493 }
2494 prefs::set(current_preferences);
2495
2496 Ok(WebDriverResponse::Void)
2497 }
2498
2499 fn handle_reset_prefs(
2500 &self,
2501 parameters: &GetPrefsParameters,
2502 ) -> WebDriverResult<WebDriverResponse> {
2503 let (new_preferences, map) = if parameters.prefs.is_empty() {
2504 (Preferences::default(), BTreeMap::new())
2505 } else {
2506 let mut new_preferences = prefs::get().clone();
2508 let default_preferences = Preferences::default();
2509 for key in parameters.prefs.iter() {
2510 new_preferences.set_value(key, default_preferences.get_value(key))
2511 }
2512
2513 let map = parameters
2514 .prefs
2515 .iter()
2516 .map(|item| (item.clone(), new_preferences.get_value(item)))
2517 .collect::<BTreeMap<_, _>>();
2518
2519 (new_preferences, map)
2520 };
2521
2522 prefs::set(new_preferences);
2523
2524 Ok(WebDriverResponse::Generic(ValueResponse(
2525 serde_json::to_value(map)?,
2526 )))
2527 }
2528
2529 fn handle_shutdown(&self) -> WebDriverResult<WebDriverResponse> {
2530 self.send_message_to_embedder(WebDriverCommandMsg::Shutdown)?;
2531 Ok(WebDriverResponse::Void)
2532 }
2533
2534 fn handle_reset_all_cookies(&self) -> WebDriverResult<WebDriverResponse> {
2535 let (sender, receiver) = unbounded();
2536 self.send_message_to_embedder(WebDriverCommandMsg::ResetAllCookies(sender))?;
2537 if receiver.recv().is_err() {
2538 log::warn!("Communication failure while clearing cookies; status unknown");
2539 }
2540 Ok(WebDriverResponse::Void)
2541 }
2542
2543 fn verify_top_level_browsing_context_is_open(
2544 &self,
2545 webview_id: WebViewId,
2546 ) -> Result<(), WebDriverError> {
2547 let (sender, receiver) = ipc::channel().unwrap();
2548 self.send_message_to_embedder(WebDriverCommandMsg::IsWebViewOpen(webview_id, sender))?;
2549 if wait_for_ipc_response(receiver)? {
2550 Ok(())
2551 } else {
2552 Err(WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2553 }
2554 }
2555
2556 fn verify_browsing_context_is_open(
2557 &self,
2558 browsing_context_id: BrowsingContextId,
2559 ) -> Result<(), WebDriverError> {
2560 let (sender, receiver) = ipc::channel().unwrap();
2561 self.send_message_to_embedder(WebDriverCommandMsg::IsBrowsingContextOpen(
2562 browsing_context_id,
2563 sender,
2564 ))?;
2565 if !receiver.recv().unwrap_or(false) {
2566 Err(WebDriverError::new(
2567 ErrorStatus::NoSuchWindow,
2568 "No such window",
2569 ))
2570 } else {
2571 Ok(())
2572 }
2573 }
2574
2575 fn focus_webview(&self, webview_id: WebViewId) -> WebDriverResult<()> {
2576 self.send_message_to_embedder(WebDriverCommandMsg::FocusWebView(webview_id))
2577 }
2578
2579 fn focus_browsing_context(&self, browsing_cotext_id: BrowsingContextId) -> WebDriverResult<()> {
2580 self.send_message_to_embedder(WebDriverCommandMsg::FocusBrowsingContext(
2581 browsing_cotext_id,
2582 ))
2583 }
2584}
2585
2586impl WebDriverHandler<ServoExtensionRoute> for Handler {
2587 fn handle_command(
2588 &mut self,
2589 _session: &Option<Session>,
2590 msg: WebDriverMessage<ServoExtensionRoute>,
2591 ) -> WebDriverResult<WebDriverResponse> {
2592 info!("{:?}", msg.command);
2593
2594 while self.load_status_receiver.try_recv().is_ok() {}
2596
2597 match msg.command {
2600 WebDriverCommand::NewSession(_) |
2601 WebDriverCommand::Status |
2602 WebDriverCommand::DeleteSession |
2603 WebDriverCommand::Extension(ServoExtensionCommand::Shutdown) |
2604 WebDriverCommand::Extension(ServoExtensionCommand::ResetAllCookies) => {},
2605 _ => {
2606 self.session()?;
2607 },
2608 }
2609
2610 match msg.command {
2611 WebDriverCommand::NewSession(ref parameters) => self.handle_new_session(parameters),
2612 WebDriverCommand::DeleteSession => self.handle_delete_session(),
2613 WebDriverCommand::Status => self.handle_status(),
2614 WebDriverCommand::AddCookie(ref parameters) => self.handle_add_cookie(parameters),
2615 WebDriverCommand::Get(ref parameters) => self.handle_get(parameters),
2616 WebDriverCommand::GetCurrentUrl => self.handle_current_url(),
2617 WebDriverCommand::GetWindowRect => {
2618 self.handle_window_rect(VerifyBrowsingContextIsOpen::Yes)
2619 },
2620 WebDriverCommand::SetWindowRect(ref size) => self.handle_set_window_rect(size),
2621 WebDriverCommand::IsEnabled(ref element) => self.handle_is_enabled(element),
2622 WebDriverCommand::IsSelected(ref element) => self.handle_is_selected(element),
2623 WebDriverCommand::GoBack => self.handle_go_back(),
2624 WebDriverCommand::GoForward => self.handle_go_forward(),
2625 WebDriverCommand::Refresh => self.handle_refresh(),
2626 WebDriverCommand::GetTitle => self.handle_title(),
2627 WebDriverCommand::GetWindowHandle => self.handle_window_handle(),
2628 WebDriverCommand::GetWindowHandles => self.handle_window_handles(),
2629 WebDriverCommand::NewWindow(ref parameters) => self.handle_new_window(parameters),
2630 WebDriverCommand::CloseWindow => self.handle_close_window(),
2631 WebDriverCommand::MaximizeWindow => self.handle_maximize_window(),
2632 WebDriverCommand::SwitchToFrame(ref parameters) => {
2633 self.handle_switch_to_frame(parameters)
2634 },
2635 WebDriverCommand::SwitchToParentFrame => self.handle_switch_to_parent_frame(),
2636 WebDriverCommand::SwitchToWindow(ref parameters) => {
2637 self.handle_switch_to_window(parameters)
2638 },
2639 WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
2640 WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
2641 WebDriverCommand::FindElementElement(ref element, ref parameters) => {
2642 self.handle_find_element_from_element(element, parameters)
2643 },
2644 WebDriverCommand::FindElementElements(ref element, ref parameters) => {
2645 self.handle_find_elements_from_element(element, parameters)
2646 },
2647 WebDriverCommand::FindShadowRootElements(ref shadow_root, ref parameters) => {
2648 self.handle_find_elements_from_shadow_root(shadow_root, parameters)
2649 },
2650 WebDriverCommand::FindShadowRootElement(ref shadow_root, ref parameters) => {
2651 self.handle_find_element_from_shadow_root(shadow_root, parameters)
2652 },
2653 WebDriverCommand::GetShadowRoot(element) => self.handle_get_shadow_root(element),
2654 WebDriverCommand::GetNamedCookie(name) => self.handle_get_cookie(name),
2655 WebDriverCommand::GetCookies => self.handle_get_cookies(),
2656 WebDriverCommand::GetActiveElement => self.handle_active_element(),
2657 WebDriverCommand::GetComputedRole(ref element) => self.handle_computed_role(element),
2658 WebDriverCommand::GetElementRect(ref element) => self.handle_element_rect(element),
2659 WebDriverCommand::GetElementText(ref element) => self.handle_element_text(element),
2660 WebDriverCommand::GetElementTagName(ref element) => {
2661 self.handle_element_tag_name(element)
2662 },
2663 WebDriverCommand::GetElementAttribute(ref element, ref name) => {
2664 self.handle_element_attribute(element, name)
2665 },
2666 WebDriverCommand::GetElementProperty(ref element, ref name) => {
2667 self.handle_element_property(element, name)
2668 },
2669 WebDriverCommand::GetCSSValue(ref element, ref name) => {
2670 self.handle_element_css(element, name)
2671 },
2672 WebDriverCommand::GetPageSource => self.handle_get_page_source(),
2673 WebDriverCommand::PerformActions(actions_parameters) => {
2674 self.handle_perform_actions(actions_parameters)
2675 },
2676 WebDriverCommand::ReleaseActions => self.handle_release_actions(),
2677 WebDriverCommand::ExecuteScript(x) => self.handle_execute_script(x),
2678 WebDriverCommand::ExecuteAsyncScript(x) => self.handle_execute_async_script(x),
2679 WebDriverCommand::ElementSendKeys(ref element, ref keys) => {
2680 self.handle_element_send_keys(element, keys)
2681 },
2682 WebDriverCommand::ElementClear(ref element) => self.handle_element_clear(element),
2683 WebDriverCommand::ElementClick(ref element) => self.handle_element_click(element),
2684 WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
2685 WebDriverCommand::AcceptAlert => self.handle_accept_alert(),
2686 WebDriverCommand::GetAlertText => self.handle_get_alert_text(),
2687 WebDriverCommand::SendAlertText(text) => self.handle_send_alert_text(text.text),
2688 WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
2689 WebDriverCommand::DeleteCookie(name) => self.handle_delete_cookie(name),
2690 WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),
2691 WebDriverCommand::SetTimeouts(ref x) => self.handle_set_timeouts(x),
2692 WebDriverCommand::TakeScreenshot => self.handle_take_screenshot(),
2693 WebDriverCommand::TakeElementScreenshot(ref x) => {
2694 self.handle_take_element_screenshot(x)
2695 },
2696 WebDriverCommand::Extension(extension) => match extension {
2697 ServoExtensionCommand::GetPrefs(ref x) => self.handle_get_prefs(x),
2698 ServoExtensionCommand::SetPrefs(ref x) => self.handle_set_prefs(x),
2699 ServoExtensionCommand::ResetPrefs(ref x) => self.handle_reset_prefs(x),
2700 ServoExtensionCommand::CustomHandlersSetMode(ref x) => {
2701 self.handle_custom_handlers_set_mode(x)
2702 },
2703 ServoExtensionCommand::Shutdown => self.handle_shutdown(),
2704 ServoExtensionCommand::ResetAllCookies => self.handle_reset_all_cookies(),
2705 },
2706 _ => Err(WebDriverError::new(
2707 ErrorStatus::UnsupportedOperation,
2708 format!("Command not implemented: {:?}", msg.command),
2709 )),
2710 }
2711 }
2712
2713 fn teardown_session(&mut self, _session: SessionTeardownKind) {
2714 self.session = None;
2715 }
2716}
2717
2718fn wait_for_ipc_response<T>(receiver: IpcReceiver<T>) -> Result<T, WebDriverError>
2719where
2720 T: for<'de> Deserialize<'de> + Serialize,
2721{
2722 receiver
2723 .recv()
2724 .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2725}
2726
2727fn wait_for_ipc_response_flatten<T>(
2730 receiver: IpcReceiver<Result<T, ErrorStatus>>,
2731) -> Result<T, WebDriverError>
2732where
2733 T: for<'de> Deserialize<'de> + Serialize,
2734{
2735 match receiver.recv() {
2736 Ok(Ok(value)) => Ok(value),
2737 Ok(Err(error_status)) => Err(WebDriverError::new(error_status, "")),
2738 Err(_) => Err(WebDriverError::new(ErrorStatus::NoSuchWindow, "")),
2739 }
2740}
2741
2742fn wait_for_script_ipc_response_with_timeout<T>(
2743 receiver: IpcReceiver<T>,
2744 timeout: Option<Duration>,
2745) -> Result<T, WebDriverError>
2746where
2747 T: for<'de> Deserialize<'de> + Serialize,
2748{
2749 let Some(timeout) = timeout else {
2750 return wait_for_ipc_response(receiver);
2751 };
2752 receiver
2753 .try_recv_timeout(timeout)
2754 .map_err(|error| match error {
2755 TryRecvError::IpcError(_) => WebDriverError::new(ErrorStatus::NoSuchWindow, ""),
2756 TryRecvError::Empty => WebDriverError::new(ErrorStatus::ScriptTimeout, ""),
2757 })
2758}
2759
2760fn unwrap_first_element_response(res: WebDriverResponse) -> WebDriverResult<WebDriverResponse> {
2761 if let WebDriverResponse::Generic(ValueResponse(values)) = res {
2762 let arr = values.as_array().unwrap();
2763 if let Some(first) = arr.first() {
2764 Ok(WebDriverResponse::Generic(ValueResponse(first.clone())))
2765 } else {
2766 Err(WebDriverError::new(ErrorStatus::NoSuchElement, ""))
2767 }
2768 } else {
2769 unreachable!()
2770 }
2771}
2772
2773fn convert_keyboard_event_to_string(event: &KeyboardEvent) -> String {
2774 let key = &event.key;
2775 let named_key = match key {
2776 Key::Character(s) => return s.to_string(),
2777 Key::Named(named_key) => named_key,
2778 };
2779
2780 match event.location {
2781 Location::Left | Location::Standard => match named_key {
2782 NamedKey::Unidentified => '\u{E000}'.to_string(),
2783 NamedKey::Cancel => '\u{E001}'.to_string(),
2784 NamedKey::Help => '\u{E002}'.to_string(),
2785 NamedKey::Backspace => '\u{E003}'.to_string(),
2786 NamedKey::Tab => '\u{E004}'.to_string(),
2787 NamedKey::Clear => '\u{E005}'.to_string(),
2788 NamedKey::Enter => match event.code {
2789 Code::NumpadEnter => '\u{E007}'.to_string(),
2790 _ => '\u{E006}'.to_string(),
2791 },
2792 NamedKey::Shift => '\u{E008}'.to_string(),
2793 NamedKey::Control => '\u{E009}'.to_string(),
2794 NamedKey::Alt => '\u{E00A}'.to_string(),
2795 NamedKey::Pause => '\u{E00B}'.to_string(),
2796 NamedKey::Escape => '\u{E00C}'.to_string(),
2797 NamedKey::PageUp => '\u{E00E}'.to_string(),
2798 NamedKey::PageDown => '\u{E00F}'.to_string(),
2799 NamedKey::End => '\u{E010}'.to_string(),
2800 NamedKey::Home => '\u{E011}'.to_string(),
2801 NamedKey::ArrowLeft => '\u{E012}'.to_string(),
2802 NamedKey::ArrowUp => '\u{E013}'.to_string(),
2803 NamedKey::ArrowRight => '\u{E014}'.to_string(),
2804 NamedKey::ArrowDown => '\u{E015}'.to_string(),
2805 NamedKey::Insert => '\u{E016}'.to_string(),
2806 NamedKey::Delete => '\u{E017}'.to_string(),
2807 NamedKey::F1 => '\u{E031}'.to_string(),
2808 NamedKey::F2 => '\u{E032}'.to_string(),
2809 NamedKey::F3 => '\u{E033}'.to_string(),
2810 NamedKey::F4 => '\u{E034}'.to_string(),
2811 NamedKey::F5 => '\u{E035}'.to_string(),
2812 NamedKey::F6 => '\u{E036}'.to_string(),
2813 NamedKey::F7 => '\u{E037}'.to_string(),
2814 NamedKey::F8 => '\u{E038}'.to_string(),
2815 NamedKey::F9 => '\u{E039}'.to_string(),
2816 NamedKey::F10 => '\u{E03A}'.to_string(),
2817 NamedKey::F11 => '\u{E03B}'.to_string(),
2818 NamedKey::F12 => '\u{E03C}'.to_string(),
2819 NamedKey::Meta => '\u{E03D}'.to_string(),
2820 NamedKey::ZenkakuHankaku => '\u{E040}'.to_string(),
2821 _ => {
2822 error!("Unexpected NamedKey on send_keys");
2823 '\u{E000}'.to_string()
2824 },
2825 },
2826 Location::Right | Location::Numpad => match named_key {
2827 NamedKey::Shift => '\u{E050}'.to_string(),
2828 NamedKey::Control => '\u{E051}'.to_string(),
2829 NamedKey::Alt => '\u{E052}'.to_string(),
2830 NamedKey::Meta => '\u{E053}'.to_string(),
2831 NamedKey::PageUp => '\u{E054}'.to_string(),
2832 NamedKey::PageDown => '\u{E055}'.to_string(),
2833 NamedKey::End => '\u{E056}'.to_string(),
2834 NamedKey::Home => '\u{E057}'.to_string(),
2835 NamedKey::ArrowLeft => '\u{E058}'.to_string(),
2836 NamedKey::ArrowUp => '\u{E059}'.to_string(),
2837 NamedKey::ArrowRight => '\u{E05A}'.to_string(),
2838 NamedKey::ArrowDown => '\u{E05B}'.to_string(),
2839 NamedKey::Insert => '\u{E05C}'.to_string(),
2840 NamedKey::Delete => '\u{E05D}'.to_string(),
2841 _ => {
2842 error!("Unexpected NamedKey on send_keys");
2843 '\u{E000}'.to_string()
2844 },
2845 },
2846 }
2847}