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.extract_an_action_sequence(parameters.actions);
2012
2013 match self.dispatch_actions(actions_by_tick, browsing_context) {
2015 Ok(_) => Ok(WebDriverResponse::Void),
2016 Err(error) => Err(WebDriverError::new(error, "")),
2017 }
2018 }
2019
2020 fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
2022 let browsing_context_id = self.browsing_context_id()?;
2023 self.verify_browsing_context_is_open(browsing_context_id)?;
2026
2027 self.handle_any_user_prompts(self.webview_id()?)?;
2029
2030 let undo_actions = self
2037 .session_mut()?
2038 .input_cancel_list
2039 .drain(..)
2040 .rev()
2041 .map(|(id, action_item)| Vec::from([(id, action_item)]))
2042 .collect();
2043 if let Err(err) = self.dispatch_actions(undo_actions, browsing_context_id) {
2045 return Err(WebDriverError::new(err, "Failed to dispatch undo actions"));
2046 }
2047
2048 self.session_mut()?.input_state_table.clear();
2050
2051 Ok(WebDriverResponse::Void)
2052 }
2053
2054 fn handle_execute_script(
2056 &self,
2057 parameters: JavascriptCommandParameters,
2058 ) -> WebDriverResult<WebDriverResponse> {
2059 let (func_body, args_string) = self.extract_script_arguments(parameters)?;
2062
2063 let script = format!(
2067 r#"(async function(__wd_eid) {{
2068 try {{
2069 let result = (async function() {{
2070 {func_body}
2071 }})({});
2072 let value = await result;
2073 if (window.__wd_eid === __wd_eid) {{
2074 window.webdriverCallback(value);
2075 }}
2076 }} catch (err) {{
2077 if (window.__wd_eid === __wd_eid) {{
2078 window.webdriverException(err);
2079 }}
2080 }}
2081 }})(window.__wd_eid = (window.__wd_eid || 0) + 1);"#,
2082 args_string.join(", ")
2083 );
2084
2085 debug!("{}", script);
2086 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2089
2090 self.handle_any_user_prompts(self.webview_id()?)?;
2092
2093 let (sender, receiver) = generic_channel::channel().unwrap();
2094 let cmd = WebDriverScriptCommand::ExecuteScriptWithCallback(script, sender);
2095
2096 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2097
2098 let timeout_duration = self
2099 .session()?
2100 .session_timeouts()
2101 .script
2102 .map_or(Duration::MAX, Duration::from_millis);
2103
2104 let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2105
2106 self.javascript_evaluation_result_to_webdriver_response(result)
2107 }
2108
2109 fn handle_execute_async_script(
2110 &self,
2111 parameters: JavascriptCommandParameters,
2112 ) -> WebDriverResult<WebDriverResponse> {
2113 let (function_body, mut args_string) = self.extract_script_arguments(parameters)?;
2116 args_string.push("resolve".to_string());
2117
2118 let joined_args = args_string.join(", ");
2119 let script = format!(
2120 r#"(async function(__wd_eid) {{
2121 try {{
2122 let result = new Promise(function(resolve, reject) {{
2123 (async function() {{
2124 {function_body}
2125 }})({joined_args})
2126 .catch(reject)
2127 }});
2128 let value = await result;
2129 if (window.__wd_eid === __wd_eid) {{
2130 window.webdriverCallback(value);
2131 }}
2132 }} catch (err) {{
2133 if (window.__wd_eid === __wd_eid) {{
2134 window.webdriverException(err);
2135 }}
2136 }}
2137 }})(window.__wd_eid = (window.__wd_eid || 0) + 1);"#,
2138 );
2139 debug!("{}", script);
2140
2141 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2144
2145 self.handle_any_user_prompts(self.webview_id()?)?;
2147
2148 let (sender, receiver) = generic_channel::channel().unwrap();
2149 self.browsing_context_script_command(
2150 WebDriverScriptCommand::ExecuteScriptWithCallback(script, sender),
2151 VerifyBrowsingContextIsOpen::No,
2152 )?;
2153
2154 let timeout_duration = self
2155 .session()?
2156 .session_timeouts()
2157 .script
2158 .map_or(Duration::MAX, Duration::from_millis);
2159 let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2160
2161 self.javascript_evaluation_result_to_webdriver_response(result)
2162 }
2163
2164 fn javascript_evaluation_result_to_webdriver_response(
2165 &self,
2166 result: WebDriverJSResult,
2167 ) -> WebDriverResult<WebDriverResponse> {
2168 match result {
2169 Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
2170 serde_json::to_value(SendableJSValue(value))?,
2171 ))),
2172 Err(error) => {
2173 let message = format!("{error:?}");
2174 let status = match error {
2175 JavaScriptEvaluationError::DocumentNotFound => ErrorStatus::NoSuchWindow,
2176 JavaScriptEvaluationError::CompilationFailure => ErrorStatus::JavascriptError,
2177 JavaScriptEvaluationError::EvaluationFailure(Some(error_info)) => {
2178 return Err(WebDriverError::new_with_data(
2179 ErrorStatus::JavascriptError,
2180 error_info.message,
2181 None,
2182 error_info.stack,
2183 ));
2184 },
2185 JavaScriptEvaluationError::EvaluationFailure(None) => {
2186 ErrorStatus::JavascriptError
2187 },
2188 JavaScriptEvaluationError::InternalError => ErrorStatus::JavascriptError,
2189 JavaScriptEvaluationError::SerializationError(serialization_error) => {
2190 match serialization_error {
2191 JavaScriptEvaluationResultSerializationError::DetachedShadowRoot => {
2192 ErrorStatus::DetachedShadowRoot
2193 },
2194 JavaScriptEvaluationResultSerializationError::OtherJavaScriptError => {
2195 ErrorStatus::JavascriptError
2196 },
2197 JavaScriptEvaluationResultSerializationError::StaleElementReference => {
2198 ErrorStatus::StaleElementReference
2199 },
2200 JavaScriptEvaluationResultSerializationError::UnknownType => {
2201 ErrorStatus::UnsupportedOperation
2202 },
2203 }
2204 },
2205 JavaScriptEvaluationError::WebViewNotReady => ErrorStatus::NoSuchWindow,
2206 };
2207 Err(WebDriverError::new(status, message))
2208 },
2209 }
2210 }
2211
2212 fn handle_element_send_keys(
2214 &mut self,
2215 element: &WebElement,
2216 keys: &SendKeysParameters,
2217 ) -> WebDriverResult<WebDriverResponse> {
2218 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2221 self.handle_any_user_prompts(self.webview_id()?)?;
2223
2224 let (sender, receiver) = generic_channel::channel().unwrap();
2225 let cmd = WebDriverScriptCommand::WillSendKeys(
2226 element.to_string(),
2227 keys.text.to_string(),
2228 self.session()?.strict_file_interactability(),
2229 sender,
2230 );
2231 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2232
2233 if !wait_for_ipc_response_flatten(receiver)? {
2236 return Ok(WebDriverResponse::Void);
2237 }
2238
2239 let id = Uuid::new_v4().to_string();
2241
2242 self.session_mut()?
2244 .input_state_table
2245 .insert(id.clone(), InputSourceState::Key(KeyInputState::new()));
2246
2247 let input_events = send_keys(&keys.text);
2250
2251 for event in input_events {
2252 match event {
2253 DispatchStringEvent::Keyboard(event) => {
2254 let raw_string = convert_keyboard_event_to_string(&event);
2255 let key_action = match event.state {
2256 KeyState::Down => KeyAction::Down(KeyDownAction { value: raw_string }),
2257 KeyState::Up => KeyAction::Up(KeyUpAction { value: raw_string }),
2258 };
2259 let action_sequence = ActionSequence {
2260 id: id.clone(),
2261 actions: ActionsType::Key {
2262 actions: vec![KeyActionItem::Key(key_action)],
2263 },
2264 };
2265
2266 let actions_by_tick = self.extract_an_action_sequence(vec![action_sequence]);
2267 if let Err(e) =
2268 self.dispatch_actions(actions_by_tick, self.browsing_context_id()?)
2269 {
2270 error!("handle_element_send_keys: dispatch_actions failed: {:?}", e);
2271 }
2272 },
2273 DispatchStringEvent::Composition(event) => {
2274 self.send_blocking_input_event_to_embedder(InputEvent::Ime(
2275 ImeEvent::Composition(event),
2276 ));
2277 },
2278 }
2279 }
2280
2281 if self
2286 .session()?
2287 .input_cancel_list
2288 .iter()
2289 .all(|(cancel_item_id, _)| &id != cancel_item_id)
2290 {
2291 self.session_mut()?.input_state_table.remove(&id);
2292 }
2293
2294 Ok(WebDriverResponse::Void)
2295 }
2296
2297 fn handle_element_clear(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2299 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2302
2303 self.handle_any_user_prompts(self.webview_id()?)?;
2305
2306 let (sender, receiver) = generic_channel::channel().unwrap();
2308 let cmd = WebDriverScriptCommand::ElementClear(element.to_string(), sender);
2309 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2310
2311 wait_for_ipc_response_flatten(receiver)?;
2312 Ok(WebDriverResponse::Void)
2313 }
2314
2315 fn handle_element_click(&mut self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2317 let browsing_context_id = self.browsing_context_id()?;
2320 self.verify_browsing_context_is_open(browsing_context_id)?;
2321
2322 self.handle_any_user_prompts(self.webview_id()?)?;
2324
2325 let (sender, receiver) = generic_channel::channel().unwrap();
2326
2327 let cmd = WebDriverScriptCommand::ElementClick(element.to_string(), sender);
2329 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2330
2331 match wait_for_ipc_response_flatten(receiver)? {
2332 Some(element_id) => {
2333 self.add_load_status_sender()?;
2336
2337 self.perform_element_click(element_id)?;
2338
2339 let res = self.wait_for_navigation()?;
2342
2343 self.clear_load_status_sender()?;
2345
2346 Ok(res)
2347 },
2348 None => Ok(WebDriverResponse::Void),
2350 }
2351 }
2352
2353 fn perform_element_click(&mut self, element: String) -> WebDriverResult<WebDriverResponse> {
2356 let id = Uuid::new_v4().to_string();
2358
2359 let pointer_ids = self.session()?.pointer_ids();
2360 #[cfg(not(any(target_env = "ohos", target_os = "android")))]
2361 let pointer_type = PointerType::Mouse;
2362
2363 #[cfg(any(target_env = "ohos", target_os = "android"))]
2368 let pointer_type = PointerType::Touch;
2369
2370 self.session_mut()?.input_state_table.insert(
2371 id.clone(),
2372 InputSourceState::Pointer(PointerInputState::new(pointer_type, pointer_ids, 0.0, 0.0)),
2373 );
2374
2375 let pointer_move_action = PointerMoveAction {
2380 duration: None,
2381 origin: PointerOrigin::Element(WebElement(element)),
2382 x: 0.0,
2383 y: 0.0,
2384 ..Default::default()
2385 };
2386
2387 let pointer_down_action = PointerDownAction {
2390 button: ELEMENT_CLICK_BUTTON,
2391 ..Default::default()
2392 };
2393
2394 let pointer_up_action = PointerUpAction {
2397 button: ELEMENT_CLICK_BUTTON,
2398 ..Default::default()
2399 };
2400
2401 let action_sequence = ActionSequence {
2402 id: id.clone(),
2403 actions: ActionsType::Pointer {
2404 parameters: PointerActionParameters {
2405 pointer_type: PointerType::Mouse,
2406 },
2407 actions: vec![
2408 PointerActionItem::Pointer(PointerAction::Move(pointer_move_action)),
2409 PointerActionItem::Pointer(PointerAction::Down(pointer_down_action)),
2410 PointerActionItem::Pointer(PointerAction::Up(pointer_up_action)),
2411 ],
2412 },
2413 };
2414
2415 let actions_by_tick = self.extract_an_action_sequence(vec![action_sequence]);
2417 if let Err(e) = self.dispatch_actions(actions_by_tick, self.browsing_context_id()?) {
2418 error!("handle_element_click: dispatch_actions failed: {:?}", e);
2419 }
2420
2421 self.session_mut()?.input_state_table.remove(&id);
2423
2424 Ok(WebDriverResponse::Void)
2425 }
2426
2427 fn take_screenshot(&self, rect: Option<Rect<f32, CSSPixel>>) -> WebDriverResult<String> {
2428 let _ = self.handle_execute_async_script(JavascriptCommandParameters {
2430 script: "requestAnimationFrame(() => arguments[0]());".to_string(),
2431 args: None,
2432 });
2433 if rect.as_ref().is_some_and(Rect::is_empty) {
2434 return Err(WebDriverError::new(
2435 ErrorStatus::UnknownError,
2436 "The requested `rect` has zero width and/or height",
2437 ));
2438 }
2439
2440 let webview_id = self.webview_id()?;
2441 let (sender, receiver) = crossbeam_channel::unbounded();
2442 self.send_message_to_embedder(WebDriverCommandMsg::TakeScreenshot(
2443 webview_id, rect, sender,
2444 ))?;
2445
2446 let result = match receiver.recv_timeout(SCREENSHOT_TIMEOUT) {
2447 Ok(result) => Ok(result),
2448 Err(RecvTimeoutError::Timeout) => Err(WebDriverError::new(
2449 ErrorStatus::Timeout,
2450 "Timed out waiting to take screenshot. Test likely didn't finish.",
2451 )),
2452 Err(RecvTimeoutError::Disconnected) => Err(WebDriverError::new(
2453 ErrorStatus::UnknownError,
2454 "Could not take screenshot because channel disconnected.",
2455 )),
2456 }?;
2457
2458 let image = result.map_err(|error| {
2459 WebDriverError::new(
2460 ErrorStatus::UnknownError,
2461 format!("Failed to take screenshot: {error:?}"),
2462 )
2463 })?;
2464
2465 let mut png_data = Cursor::new(Vec::new());
2466 DynamicImage::ImageRgba8(image)
2467 .write_to(&mut png_data, ImageFormat::Png)
2468 .unwrap();
2469
2470 Ok(base64::engine::general_purpose::STANDARD.encode(png_data.get_ref()))
2471 }
2472
2473 fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> {
2474 let webview_id = self.webview_id()?;
2477 self.verify_top_level_browsing_context_is_open(webview_id)?;
2478
2479 self.handle_any_user_prompts(webview_id)?;
2480
2481 let encoded = self.take_screenshot(None)?;
2483
2484 Ok(WebDriverResponse::Generic(ValueResponse(
2485 serde_json::to_value(encoded)?,
2486 )))
2487 }
2488
2489 fn handle_take_element_screenshot(
2490 &self,
2491 element: &WebElement,
2492 ) -> WebDriverResult<WebDriverResponse> {
2493 let webview_id = self.webview_id()?;
2496 self.verify_top_level_browsing_context_is_open(webview_id)?;
2497
2498 self.handle_any_user_prompts(webview_id)?;
2500
2501 let (sender, receiver) = generic_channel::channel().unwrap();
2504 let cmd =
2505 WebDriverScriptCommand::ScrollAndGetBoundingClientRect(element.to_string(), sender);
2506 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
2507
2508 let rect = wait_for_ipc_response_flatten(receiver)?;
2509
2510 let encoded = self.take_screenshot(Some(Rect::from_untyped(&rect)))?;
2512
2513 Ok(WebDriverResponse::Generic(ValueResponse(
2515 serde_json::to_value(encoded)?,
2516 )))
2517 }
2518
2519 fn handle_custom_handlers_set_mode(
2521 &self,
2522 parameters: &CustomHandlersSetModeParameters,
2523 ) -> WebDriverResult<WebDriverResponse> {
2524 let mode = match parameters.mode.as_str() {
2527 "autoAccept" => CustomHandlersAutomationMode::AutoAccept,
2528 "autoReject" => CustomHandlersAutomationMode::AutoReject,
2529 "none" => CustomHandlersAutomationMode::None,
2530 _ => {
2531 return Err(WebDriverError::new(
2532 ErrorStatus::InvalidArgument,
2533 "invalid argument",
2534 ));
2535 },
2536 };
2537 self.top_level_script_command(
2540 WebDriverScriptCommand::SetProtocolHandlerAutomationMode(mode),
2541 VerifyBrowsingContextIsOpen::Yes,
2542 )?;
2543 Ok(WebDriverResponse::Void)
2545 }
2546
2547 fn handle_get_prefs(
2548 &self,
2549 parameters: &GetPrefsParameters,
2550 ) -> WebDriverResult<WebDriverResponse> {
2551 let prefs = parameters
2552 .prefs
2553 .iter()
2554 .map(|item| {
2555 (
2556 item.clone(),
2557 serde_json::to_value(prefs::get().get_value(item)).unwrap(),
2558 )
2559 })
2560 .collect::<BTreeMap<_, _>>();
2561
2562 Ok(WebDriverResponse::Generic(ValueResponse(
2563 serde_json::to_value(prefs)?,
2564 )))
2565 }
2566
2567 fn handle_set_prefs(
2568 &self,
2569 parameters: &SetPrefsParameters,
2570 ) -> WebDriverResult<WebDriverResponse> {
2571 let mut current_preferences = prefs::get().clone();
2572 for (key, value) in parameters.prefs.iter() {
2573 current_preferences.set_value(key, value.0.clone());
2574 }
2575 prefs::set(current_preferences);
2576
2577 Ok(WebDriverResponse::Void)
2578 }
2579
2580 fn handle_reset_prefs(
2581 &self,
2582 parameters: &GetPrefsParameters,
2583 ) -> WebDriverResult<WebDriverResponse> {
2584 let (new_preferences, map) = if parameters.prefs.is_empty() {
2585 (self.default_preferences.clone(), BTreeMap::new())
2586 } else {
2587 let mut new_preferences = prefs::get().clone();
2589 for key in parameters.prefs.iter() {
2590 new_preferences.set_value(key, self.default_preferences.get_value(key))
2591 }
2592
2593 let map = parameters
2594 .prefs
2595 .iter()
2596 .map(|item| (item.clone(), new_preferences.get_value(item)))
2597 .collect::<BTreeMap<_, _>>();
2598
2599 (new_preferences, map)
2600 };
2601
2602 prefs::set(new_preferences);
2603
2604 Ok(WebDriverResponse::Generic(ValueResponse(
2605 serde_json::to_value(map)?,
2606 )))
2607 }
2608
2609 fn handle_shutdown(&self) -> WebDriverResult<WebDriverResponse> {
2610 self.send_message_to_embedder(WebDriverCommandMsg::Shutdown)?;
2611 Ok(WebDriverResponse::Void)
2612 }
2613
2614 fn handle_reset_all_cookies(&self) -> WebDriverResult<WebDriverResponse> {
2615 let (sender, receiver) = unbounded();
2616 self.send_message_to_embedder(WebDriverCommandMsg::ResetAllCookies(sender))?;
2617 if receiver.recv().is_err() {
2618 log::warn!("Communication failure while clearing cookies; status unknown");
2619 }
2620 Ok(WebDriverResponse::Void)
2621 }
2622
2623 fn verify_top_level_browsing_context_is_open(
2624 &self,
2625 webview_id: WebViewId,
2626 ) -> Result<(), WebDriverError> {
2627 let (sender, receiver) = generic_channel::oneshot().unwrap();
2628 self.send_message_to_embedder(WebDriverCommandMsg::IsWebViewOpen(webview_id, sender))?;
2629 if wait_for_oneshot_response(receiver)? {
2630 Ok(())
2631 } else {
2632 Err(WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2633 }
2634 }
2635
2636 fn verify_browsing_context_is_open(
2637 &self,
2638 browsing_context_id: BrowsingContextId,
2639 ) -> Result<(), WebDriverError> {
2640 let (sender, receiver) = generic_channel::oneshot().unwrap();
2641 self.send_message_to_embedder(WebDriverCommandMsg::IsBrowsingContextOpen(
2642 browsing_context_id,
2643 sender,
2644 ))?;
2645 if !receiver.recv().unwrap_or(false) {
2646 Err(WebDriverError::new(
2647 ErrorStatus::NoSuchWindow,
2648 "No such window",
2649 ))
2650 } else {
2651 Ok(())
2652 }
2653 }
2654
2655 fn wait_until_browsing_context_is_open(
2656 &self,
2657 browsing_context_id: BrowsingContextId,
2658 ) -> WebDriverResult<()> {
2659 const OPEN_BROWSING_CONTEXT_TIMEOUT: Duration =
2662 Duration::from_millis(DEFAULT_PAGE_LOAD_TIMEOUT);
2663 let now = Instant::now();
2664 let timeouts = self.session()?.session_timeouts();
2665
2666 let sleep_interval = Duration::from_millis(timeouts.sleep_interval);
2667
2668 while now.elapsed() < OPEN_BROWSING_CONTEXT_TIMEOUT {
2669 if self
2670 .verify_browsing_context_is_open(browsing_context_id)
2671 .is_ok()
2672 {
2673 return Ok(());
2674 }
2675
2676 sleep(sleep_interval);
2677 }
2678 Err(WebDriverError::new(
2679 ErrorStatus::Timeout,
2680 format!(
2681 "Timed out waiting for the top-level browsing context {browsing_context_id} to be ready"
2682 ),
2683 ))
2684 }
2685
2686 fn focus_webview(&self, webview_id: WebViewId) -> WebDriverResult<()> {
2687 self.send_message_to_embedder(WebDriverCommandMsg::FocusWebView(webview_id))
2688 }
2689
2690 fn focus_browsing_context(&self, browsing_cotext_id: BrowsingContextId) -> WebDriverResult<()> {
2691 self.send_message_to_embedder(WebDriverCommandMsg::FocusBrowsingContext(
2692 browsing_cotext_id,
2693 ))
2694 }
2695}
2696
2697impl WebDriverHandler<ServoExtensionRoute> for Handler {
2698 fn handle_command(
2699 &mut self,
2700 _session: &Option<Session>,
2701 msg: WebDriverMessage<ServoExtensionRoute>,
2702 ) -> WebDriverResult<WebDriverResponse> {
2703 info!("{:?}", msg.command);
2704
2705 while self.load_status_receiver.try_recv().is_ok() {}
2707
2708 match msg.command {
2711 WebDriverCommand::NewSession(_) |
2712 WebDriverCommand::Status |
2713 WebDriverCommand::DeleteSession |
2714 WebDriverCommand::Extension(ServoExtensionCommand::Shutdown) |
2715 WebDriverCommand::Extension(ServoExtensionCommand::ResetAllCookies) => {},
2716 _ => {
2717 self.session()?;
2718 },
2719 }
2720
2721 match msg.command {
2722 WebDriverCommand::NewSession(ref parameters) => self.handle_new_session(parameters),
2723 WebDriverCommand::DeleteSession => self.handle_delete_session(),
2724 WebDriverCommand::Status => self.handle_status(),
2725 WebDriverCommand::AddCookie(ref parameters) => self.handle_add_cookie(parameters),
2726 WebDriverCommand::Get(ref parameters) => self.handle_get(parameters),
2727 WebDriverCommand::GetCurrentUrl => self.handle_current_url(),
2728 WebDriverCommand::GetWindowRect => {
2729 self.handle_window_rect(VerifyBrowsingContextIsOpen::Yes)
2730 },
2731 WebDriverCommand::SetWindowRect(ref size) => self.handle_set_window_rect(size),
2732 WebDriverCommand::IsEnabled(ref element) => self.handle_is_enabled(element),
2733 WebDriverCommand::IsSelected(ref element) => self.handle_is_selected(element),
2734 WebDriverCommand::GoBack => self.handle_go_back(),
2735 WebDriverCommand::GoForward => self.handle_go_forward(),
2736 WebDriverCommand::Refresh => self.handle_refresh(),
2737 WebDriverCommand::GetTitle => self.handle_title(),
2738 WebDriverCommand::GetWindowHandle => self.handle_window_handle(),
2739 WebDriverCommand::GetWindowHandles => self.handle_window_handles(),
2740 WebDriverCommand::NewWindow(ref parameters) => self.handle_new_window(parameters),
2741 WebDriverCommand::CloseWindow => self.handle_close_window(),
2742 WebDriverCommand::MaximizeWindow => self.handle_maximize_window(),
2743 WebDriverCommand::SwitchToFrame(ref parameters) => {
2744 self.handle_switch_to_frame(parameters)
2745 },
2746 WebDriverCommand::SwitchToParentFrame => self.handle_switch_to_parent_frame(),
2747 WebDriverCommand::SwitchToWindow(ref parameters) => {
2748 self.handle_switch_to_window(parameters)
2749 },
2750 WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
2751 WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
2752 WebDriverCommand::FindElementElement(ref element, ref parameters) => {
2753 self.handle_find_element_from_element(element, parameters)
2754 },
2755 WebDriverCommand::FindElementElements(ref element, ref parameters) => {
2756 self.handle_find_elements_from_element(element, parameters)
2757 },
2758 WebDriverCommand::FindShadowRootElements(ref shadow_root, ref parameters) => {
2759 self.handle_find_elements_from_shadow_root(shadow_root, parameters)
2760 },
2761 WebDriverCommand::FindShadowRootElement(ref shadow_root, ref parameters) => {
2762 self.handle_find_element_from_shadow_root(shadow_root, parameters)
2763 },
2764 WebDriverCommand::GetShadowRoot(element) => self.handle_get_shadow_root(element),
2765 WebDriverCommand::GetNamedCookie(name) => self.handle_get_cookie(name),
2766 WebDriverCommand::GetCookies => self.handle_get_cookies(),
2767 WebDriverCommand::GetActiveElement => self.handle_active_element(),
2768 WebDriverCommand::GetComputedRole(ref element) => self.handle_computed_role(element),
2769 WebDriverCommand::GetElementRect(ref element) => self.handle_element_rect(element),
2770 WebDriverCommand::GetElementText(ref element) => self.handle_element_text(element),
2771 WebDriverCommand::GetElementTagName(ref element) => {
2772 self.handle_element_tag_name(element)
2773 },
2774 WebDriverCommand::GetElementAttribute(ref element, ref name) => {
2775 self.handle_element_attribute(element, name)
2776 },
2777 WebDriverCommand::GetElementProperty(ref element, ref name) => {
2778 self.handle_element_property(element, name)
2779 },
2780 WebDriverCommand::GetCSSValue(ref element, ref name) => {
2781 self.handle_element_css(element, name)
2782 },
2783 WebDriverCommand::GetPageSource => self.handle_get_page_source(),
2784 WebDriverCommand::PerformActions(actions_parameters) => {
2785 self.handle_perform_actions(actions_parameters)
2786 },
2787 WebDriverCommand::ReleaseActions => self.handle_release_actions(),
2788 WebDriverCommand::ExecuteScript(x) => self.handle_execute_script(x),
2789 WebDriverCommand::ExecuteAsyncScript(x) => self.handle_execute_async_script(x),
2790 WebDriverCommand::ElementSendKeys(ref element, ref keys) => {
2791 self.handle_element_send_keys(element, keys)
2792 },
2793 WebDriverCommand::ElementClear(ref element) => self.handle_element_clear(element),
2794 WebDriverCommand::ElementClick(ref element) => self.handle_element_click(element),
2795 WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
2796 WebDriverCommand::AcceptAlert => self.handle_accept_alert(),
2797 WebDriverCommand::GetAlertText => self.handle_get_alert_text(),
2798 WebDriverCommand::SendAlertText(text) => self.handle_send_alert_text(text.text),
2799 WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
2800 WebDriverCommand::DeleteCookie(name) => self.handle_delete_cookie(name),
2801 WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),
2802 WebDriverCommand::SetTimeouts(ref x) => self.handle_set_timeouts(x),
2803 WebDriverCommand::TakeScreenshot => self.handle_take_screenshot(),
2804 WebDriverCommand::TakeElementScreenshot(ref x) => {
2805 self.handle_take_element_screenshot(x)
2806 },
2807 WebDriverCommand::Extension(extension) => match extension {
2808 ServoExtensionCommand::GetPrefs(ref x) => self.handle_get_prefs(x),
2809 ServoExtensionCommand::SetPrefs(ref x) => self.handle_set_prefs(x),
2810 ServoExtensionCommand::ResetPrefs(ref x) => self.handle_reset_prefs(x),
2811 ServoExtensionCommand::CustomHandlersSetMode(ref x) => {
2812 self.handle_custom_handlers_set_mode(x)
2813 },
2814 ServoExtensionCommand::Shutdown => self.handle_shutdown(),
2815 ServoExtensionCommand::ResetAllCookies => self.handle_reset_all_cookies(),
2816 },
2817 _ => Err(WebDriverError::new(
2818 ErrorStatus::UnsupportedOperation,
2819 format!("Command not implemented: {:?}", msg.command),
2820 )),
2821 }
2822 }
2823
2824 fn teardown_session(&mut self, _session: SessionTeardownKind) {
2825 self.session = None;
2826 }
2827}
2828
2829fn wait_for_oneshot_response<T>(
2830 receiver: generic_channel::GenericOneshotReceiver<T>,
2831) -> Result<T, WebDriverError>
2832where
2833 T: for<'de> Deserialize<'de> + Serialize,
2834{
2835 receiver
2836 .recv()
2837 .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2838}
2839
2840fn wait_for_ipc_response<T>(receiver: GenericReceiver<T>) -> Result<T, WebDriverError>
2841where
2842 T: for<'de> Deserialize<'de> + Serialize,
2843{
2844 receiver
2845 .recv()
2846 .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2847}
2848
2849fn wait_for_ipc_response_flatten<T>(
2852 receiver: GenericReceiver<Result<T, ErrorStatus>>,
2853) -> Result<T, WebDriverError>
2854where
2855 T: for<'de> Deserialize<'de> + Serialize,
2856{
2857 match receiver.recv() {
2858 Ok(Ok(value)) => Ok(value),
2859 Ok(Err(error_status)) => Err(WebDriverError::new(error_status, "")),
2860 Err(_) => Err(WebDriverError::new(ErrorStatus::NoSuchWindow, "")),
2861 }
2862}
2863
2864fn wait_for_script_ipc_response_with_timeout<T>(
2865 receiver: GenericReceiver<T>,
2866 timeout: Duration,
2867) -> Result<T, WebDriverError>
2868where
2869 T: for<'de> Deserialize<'de> + Serialize,
2870{
2871 receiver
2872 .try_recv_timeout(timeout)
2873 .map_err(|error| match error {
2874 generic_channel::TryReceiveError::ReceiveError(_) => {
2875 WebDriverError::new(ErrorStatus::NoSuchWindow, "")
2876 },
2877 generic_channel::TryReceiveError::Empty => {
2878 WebDriverError::new(ErrorStatus::ScriptTimeout, "")
2879 },
2880 })
2881}
2882
2883fn unwrap_first_element_response(res: WebDriverResponse) -> WebDriverResult<WebDriverResponse> {
2884 if let WebDriverResponse::Generic(ValueResponse(values)) = res {
2885 let arr = values.as_array().unwrap();
2886 if let Some(first) = arr.first() {
2887 Ok(WebDriverResponse::Generic(ValueResponse(first.clone())))
2888 } else {
2889 Err(WebDriverError::new(ErrorStatus::NoSuchElement, ""))
2890 }
2891 } else {
2892 unreachable!()
2893 }
2894}
2895
2896fn convert_keyboard_event_to_string(event: &KeyboardEvent) -> String {
2897 let key = &event.key;
2898 let named_key = match key {
2899 Key::Character(s) => return s.to_string(),
2900 Key::Named(named_key) => named_key,
2901 };
2902
2903 match event.location {
2904 Location::Left | Location::Standard => match named_key {
2905 NamedKey::Unidentified => '\u{E000}'.to_string(),
2906 NamedKey::Cancel => '\u{E001}'.to_string(),
2907 NamedKey::Help => '\u{E002}'.to_string(),
2908 NamedKey::Backspace => '\u{E003}'.to_string(),
2909 NamedKey::Tab => '\u{E004}'.to_string(),
2910 NamedKey::Clear => '\u{E005}'.to_string(),
2911 NamedKey::Enter => match event.code {
2912 Code::NumpadEnter => '\u{E007}'.to_string(),
2913 _ => '\u{E006}'.to_string(),
2914 },
2915 NamedKey::Shift => '\u{E008}'.to_string(),
2916 NamedKey::Control => '\u{E009}'.to_string(),
2917 NamedKey::Alt => '\u{E00A}'.to_string(),
2918 NamedKey::Pause => '\u{E00B}'.to_string(),
2919 NamedKey::Escape => '\u{E00C}'.to_string(),
2920 NamedKey::PageUp => '\u{E00E}'.to_string(),
2921 NamedKey::PageDown => '\u{E00F}'.to_string(),
2922 NamedKey::End => '\u{E010}'.to_string(),
2923 NamedKey::Home => '\u{E011}'.to_string(),
2924 NamedKey::ArrowLeft => '\u{E012}'.to_string(),
2925 NamedKey::ArrowUp => '\u{E013}'.to_string(),
2926 NamedKey::ArrowRight => '\u{E014}'.to_string(),
2927 NamedKey::ArrowDown => '\u{E015}'.to_string(),
2928 NamedKey::Insert => '\u{E016}'.to_string(),
2929 NamedKey::Delete => '\u{E017}'.to_string(),
2930 NamedKey::F1 => '\u{E031}'.to_string(),
2931 NamedKey::F2 => '\u{E032}'.to_string(),
2932 NamedKey::F3 => '\u{E033}'.to_string(),
2933 NamedKey::F4 => '\u{E034}'.to_string(),
2934 NamedKey::F5 => '\u{E035}'.to_string(),
2935 NamedKey::F6 => '\u{E036}'.to_string(),
2936 NamedKey::F7 => '\u{E037}'.to_string(),
2937 NamedKey::F8 => '\u{E038}'.to_string(),
2938 NamedKey::F9 => '\u{E039}'.to_string(),
2939 NamedKey::F10 => '\u{E03A}'.to_string(),
2940 NamedKey::F11 => '\u{E03B}'.to_string(),
2941 NamedKey::F12 => '\u{E03C}'.to_string(),
2942 NamedKey::Meta => '\u{E03D}'.to_string(),
2943 NamedKey::ZenkakuHankaku => '\u{E040}'.to_string(),
2944 _ => {
2945 error!("Unexpected NamedKey on send_keys");
2946 '\u{E000}'.to_string()
2947 },
2948 },
2949 Location::Right | Location::Numpad => match named_key {
2950 NamedKey::Shift => '\u{E050}'.to_string(),
2951 NamedKey::Control => '\u{E051}'.to_string(),
2952 NamedKey::Alt => '\u{E052}'.to_string(),
2953 NamedKey::Meta => '\u{E053}'.to_string(),
2954 NamedKey::PageUp => '\u{E054}'.to_string(),
2955 NamedKey::PageDown => '\u{E055}'.to_string(),
2956 NamedKey::End => '\u{E056}'.to_string(),
2957 NamedKey::Home => '\u{E057}'.to_string(),
2958 NamedKey::ArrowLeft => '\u{E058}'.to_string(),
2959 NamedKey::ArrowUp => '\u{E059}'.to_string(),
2960 NamedKey::ArrowRight => '\u{E05A}'.to_string(),
2961 NamedKey::ArrowDown => '\u{E05B}'.to_string(),
2962 NamedKey::Insert => '\u{E05C}'.to_string(),
2963 NamedKey::Delete => '\u{E05D}'.to_string(),
2964 _ => {
2965 error!("Unexpected NamedKey on send_keys");
2966 '\u{E000}'.to_string()
2967 },
2968 },
2969 }
2970}