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 EventLoopWaker, ImeEvent, InputEvent, JSValue, JavaScriptEvaluationError,
33 JavaScriptEvaluationResultSerializationError, MouseButton, WebDriverCommandMsg,
34 WebDriverFrameId, WebDriverJSResult, WebDriverLoadStatus, WebDriverScriptCommand,
35};
36use euclid::{Point2D, Rect, Size2D};
37use http::method::Method;
38use image::{DynamicImage, ImageFormat};
39use ipc_channel::ipc::{self, IpcReceiver, TryRecvError};
40use keyboard_types::webdriver::{Event as DispatchStringEvent, KeyInputState, send_keys};
41use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, NamedKey};
42use log::{debug, error, info};
43use serde::de::{Deserializer, MapAccess, Visitor};
44use serde::ser::Serializer;
45use serde::{Deserialize, Serialize};
46use serde_json::{Value, json};
47use servo_config::prefs::{self, PrefValue, Preferences};
48use servo_geometry::DeviceIndependentIntRect;
49use servo_url::ServoUrl;
50use style_traits::CSSPixel;
51use time::OffsetDateTime;
52use uuid::Uuid;
53use webdriver::actions::{
54 ActionSequence, ActionsType, KeyAction, KeyActionItem, KeyDownAction, KeyUpAction,
55 PointerAction, PointerActionItem, PointerActionParameters, PointerDownAction,
56 PointerMoveAction, PointerOrigin, PointerType, PointerUpAction,
57};
58use webdriver::capabilities::CapabilitiesMatching;
59use webdriver::command::{
60 ActionsParameters, AddCookieParameters, GetParameters, JavascriptCommandParameters,
61 LocatorParameters, NewSessionParameters, NewWindowParameters, SendKeysParameters,
62 SwitchToFrameParameters, SwitchToWindowParameters, TimeoutsParameters, WebDriverCommand,
63 WebDriverExtensionCommand, WebDriverMessage, WindowRectParameters,
64};
65use webdriver::common::{
66 Cookie, Date, LocatorStrategy, Parameters, ShadowRoot, WebElement, WebFrame, WebWindow,
67};
68use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
69use webdriver::httpapi::WebDriverExtensionRoute;
70use webdriver::response::{
71 CloseWindowResponse, CookieResponse, CookiesResponse, ElementRectResponse, NewSessionResponse,
72 NewWindowResponse, TimeoutsResponse, ValueResponse, WebDriverResponse, WindowRectResponse,
73};
74use webdriver::server::{self, Session, SessionTeardownKind, WebDriverHandler};
75
76use crate::actions::{InputSourceState, PointerInputState};
77use crate::session::{PageLoadStrategy, WebDriverSession};
78use crate::timeout::{DEFAULT_PAGE_LOAD_TIMEOUT, SCREENSHOT_TIMEOUT};
79
80fn extension_routes() -> Vec<(Method, &'static str, ServoExtensionRoute)> {
81 vec![
82 (
83 Method::GET,
84 "/session/{sessionId}/servo/prefs/get",
85 ServoExtensionRoute::GetPrefs,
86 ),
87 (
88 Method::POST,
89 "/session/{sessionId}/servo/prefs/set",
90 ServoExtensionRoute::SetPrefs,
91 ),
92 (
93 Method::POST,
94 "/session/{sessionId}/servo/prefs/reset",
95 ServoExtensionRoute::ResetPrefs,
96 ),
97 (
98 Method::DELETE,
99 "/session/{sessionId}/servo/shutdown",
100 ServoExtensionRoute::Shutdown,
101 ),
102 ]
103}
104
105fn cookie_msg_to_cookie(cookie: cookie::Cookie) -> Cookie {
106 Cookie {
107 name: cookie.name().to_owned(),
108 value: cookie.value().to_owned(),
109 path: cookie.path().map(|s| s.to_owned()),
110 domain: cookie.domain().map(|s| s.to_owned()),
111 expiry: cookie.expires().and_then(|expiration| match expiration {
112 Expiration::DateTime(date_time) => Some(Date(date_time.unix_timestamp() as u64)),
113 Expiration::Session => None,
114 }),
115 secure: cookie.secure().unwrap_or(false),
116 http_only: cookie.http_only().unwrap_or(false),
117 same_site: cookie.same_site().map(|s| s.to_string()),
118 }
119}
120
121pub fn start_server(
122 port: u16,
123 embedder_sender: Sender<WebDriverCommandMsg>,
124 event_loop_waker: Box<dyn EventLoopWaker>,
125) {
126 let handler = Handler::new(embedder_sender, event_loop_waker);
127
128 thread::Builder::new()
129 .name("WebDriverHttpServer".to_owned())
130 .spawn(move || {
131 let address = SocketAddrV4::new("0.0.0.0".parse().unwrap(), port);
132 match server::start(
133 SocketAddr::V4(address),
134 vec![],
135 vec![],
136 handler,
137 extension_routes(),
138 ) {
139 Ok(listening) => info!("WebDriver server listening on {}", listening.socket),
140 Err(e) => panic!("Unable to start WebDriver HTTP server {e:?}"),
141 }
142 })
143 .expect("Thread spawning failed");
144}
145
146struct Handler {
147 load_status_receiver: RoutedReceiver<WebDriverLoadStatus>,
151 load_status_sender: GenericSender<WebDriverLoadStatus>,
155
156 session: Option<WebDriverSession>,
157
158 embedder_sender: Sender<WebDriverCommandMsg>,
162
163 event_loop_waker: Box<dyn EventLoopWaker>,
165
166 pending_input_event_receivers: RefCell<Vec<Receiver<()>>>,
171
172 num_pending_actions: Cell<u32>,
174}
175
176#[derive(Clone, Copy, Debug, PartialEq)]
177#[allow(clippy::enum_variant_names)]
178enum ServoExtensionRoute {
179 GetPrefs,
180 SetPrefs,
181 ResetPrefs,
182 Shutdown,
188}
189
190impl WebDriverExtensionRoute for ServoExtensionRoute {
191 type Command = ServoExtensionCommand;
192
193 fn command(
194 &self,
195 _parameters: &Parameters,
196 body_data: &Value,
197 ) -> WebDriverResult<WebDriverCommand<ServoExtensionCommand>> {
198 let command = match *self {
199 ServoExtensionRoute::GetPrefs => {
200 let parameters: GetPrefsParameters = serde_json::from_value(body_data.clone())?;
201 ServoExtensionCommand::GetPrefs(parameters)
202 },
203 ServoExtensionRoute::SetPrefs => {
204 let parameters: SetPrefsParameters = serde_json::from_value(body_data.clone())?;
205 ServoExtensionCommand::SetPrefs(parameters)
206 },
207 ServoExtensionRoute::ResetPrefs => {
208 let parameters: GetPrefsParameters = serde_json::from_value(body_data.clone())?;
209 ServoExtensionCommand::ResetPrefs(parameters)
210 },
211 ServoExtensionRoute::Shutdown => ServoExtensionCommand::Shutdown,
212 };
213 Ok(WebDriverCommand::Extension(command))
214 }
215}
216
217#[derive(Clone, Debug)]
218#[allow(clippy::enum_variant_names)]
219enum ServoExtensionCommand {
220 GetPrefs(GetPrefsParameters),
221 SetPrefs(SetPrefsParameters),
222 ResetPrefs(GetPrefsParameters),
223 Shutdown,
224}
225
226impl WebDriverExtensionCommand for ServoExtensionCommand {
227 fn parameters_json(&self) -> Option<Value> {
228 match *self {
229 ServoExtensionCommand::GetPrefs(ref x) => serde_json::to_value(x).ok(),
230 ServoExtensionCommand::SetPrefs(ref x) => serde_json::to_value(x).ok(),
231 ServoExtensionCommand::ResetPrefs(ref x) => serde_json::to_value(x).ok(),
232 ServoExtensionCommand::Shutdown => None,
233 }
234 }
235}
236
237#[derive(Clone)]
238struct SendableJSValue(JSValue);
239
240impl Serialize for SendableJSValue {
241 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
242 where
243 S: Serializer,
244 {
245 match self.0 {
246 JSValue::Undefined | JSValue::Null => serializer.serialize_unit(),
247 JSValue::Boolean(x) => serializer.serialize_bool(x),
248 JSValue::Number(x) => {
249 if x.fract() == 0.0 {
250 serializer.serialize_i64(x as i64)
251 } else {
252 serializer.serialize_f64(x)
253 }
254 },
255 JSValue::String(ref x) => serializer.serialize_str(x),
256 JSValue::Element(ref x) => WebElement(x.clone()).serialize(serializer),
257 JSValue::ShadowRoot(ref x) => ShadowRoot(x.clone()).serialize(serializer),
258 JSValue::Frame(ref x) => WebFrame(x.clone()).serialize(serializer),
259 JSValue::Window(ref x) => WebWindow(x.clone()).serialize(serializer),
260 JSValue::Array(ref x) => x
261 .iter()
262 .map(|element| SendableJSValue(element.clone()))
263 .collect::<Vec<SendableJSValue>>()
264 .serialize(serializer),
265 JSValue::Object(ref x) => x
266 .iter()
267 .map(|(k, v)| (k.clone(), SendableJSValue(v.clone())))
268 .collect::<HashMap<String, SendableJSValue>>()
269 .serialize(serializer),
270 }
271 }
272}
273
274#[derive(Clone, Debug, PartialEq)]
275struct WebDriverPrefValue(PrefValue);
276
277impl Serialize for WebDriverPrefValue {
278 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
279 where
280 S: Serializer,
281 {
282 match self.0 {
283 PrefValue::Bool(b) => serializer.serialize_bool(b),
284 PrefValue::Str(ref s) => serializer.serialize_str(s),
285 PrefValue::Float(f) => serializer.serialize_f64(f),
286 PrefValue::Int(i) => serializer.serialize_i64(i),
287 PrefValue::Array(ref v) => v
288 .iter()
289 .map(|value| WebDriverPrefValue(value.clone()))
290 .collect::<Vec<WebDriverPrefValue>>()
291 .serialize(serializer),
292 }
293 }
294}
295
296impl<'de> Deserialize<'de> for WebDriverPrefValue {
297 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
298 where
299 D: Deserializer<'de>,
300 {
301 struct Visitor;
302
303 impl ::serde::de::Visitor<'_> for Visitor {
304 type Value = WebDriverPrefValue;
305
306 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
307 formatter.write_str("preference value")
308 }
309
310 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
311 where
312 E: ::serde::de::Error,
313 {
314 Ok(WebDriverPrefValue(PrefValue::Float(value)))
315 }
316
317 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
318 where
319 E: ::serde::de::Error,
320 {
321 Ok(WebDriverPrefValue(PrefValue::Int(value)))
322 }
323
324 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
325 where
326 E: ::serde::de::Error,
327 {
328 Ok(WebDriverPrefValue(PrefValue::Int(value as i64)))
329 }
330
331 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
332 where
333 E: ::serde::de::Error,
334 {
335 Ok(WebDriverPrefValue(PrefValue::Str(String::from(value))))
336 }
337
338 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
339 where
340 E: ::serde::de::Error,
341 {
342 Ok(WebDriverPrefValue(PrefValue::Bool(value)))
343 }
344 }
345
346 deserializer.deserialize_any(Visitor)
347 }
348}
349
350#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
351struct GetPrefsParameters {
352 prefs: Vec<String>,
353}
354
355#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
356struct SetPrefsParameters {
357 #[serde(deserialize_with = "map_to_vec")]
358 prefs: Vec<(String, WebDriverPrefValue)>,
359}
360
361fn map_to_vec<'de, D>(de: D) -> Result<Vec<(String, WebDriverPrefValue)>, D::Error>
362where
363 D: Deserializer<'de>,
364{
365 de.deserialize_map(TupleVecMapVisitor)
366}
367
368struct TupleVecMapVisitor;
369
370impl<'de> Visitor<'de> for TupleVecMapVisitor {
371 type Value = Vec<(String, WebDriverPrefValue)>;
372
373 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
374 formatter.write_str("a map")
375 }
376
377 #[inline]
378 fn visit_unit<E>(self) -> Result<Self::Value, E> {
379 Ok(Vec::new())
380 }
381
382 #[inline]
383 fn visit_map<T>(self, mut access: T) -> Result<Self::Value, T::Error>
384 where
385 T: MapAccess<'de>,
386 {
387 let mut values = Vec::new();
388
389 while let Some((key, value)) = access.next_entry()? {
390 values.push((key, value));
391 }
392
393 Ok(values)
394 }
395}
396
397enum VerifyBrowsingContextIsOpen {
398 Yes,
399 No,
400}
401
402impl Handler {
403 fn new(
404 embedder_sender: Sender<WebDriverCommandMsg>,
405 event_loop_waker: Box<dyn EventLoopWaker>,
406 ) -> Handler {
407 let (load_status_sender, receiver) = generic_channel::channel().unwrap();
413 let load_status_receiver = receiver.route_preserving_errors();
414
415 Handler {
416 load_status_sender,
417 load_status_receiver,
418 session: None,
419 embedder_sender,
420 event_loop_waker,
421 pending_input_event_receivers: Default::default(),
422 num_pending_actions: Cell::new(0),
423 }
424 }
425
426 fn browsing_context_id(&self) -> WebDriverResult<BrowsingContextId> {
427 self.session()?
428 .current_browsing_context_id()
429 .ok_or_else(|| {
430 WebDriverError::new(ErrorStatus::UnknownError, "No browsing context available")
431 })
432 }
433
434 fn webview_id(&self) -> WebDriverResult<WebViewId> {
435 self.session()?
436 .current_webview_id()
437 .ok_or_else(|| WebDriverError::new(ErrorStatus::UnknownError, "No webview available"))
438 }
439
440 fn send_input_event_to_embedder(&self, input_event: InputEvent) {
441 let _ = self.send_message_to_embedder(WebDriverCommandMsg::InputEvent(
442 self.verified_webview_id(),
443 input_event,
444 None,
445 ));
446 }
447
448 fn send_blocking_input_event_to_embedder(&self, input_event: InputEvent) {
449 let (result_sender, result_receiver) = unbounded();
450 if self
451 .send_message_to_embedder(WebDriverCommandMsg::InputEvent(
452 self.verified_webview_id(),
453 input_event,
454 Some(result_sender),
455 ))
456 .is_ok()
457 {
458 self.pending_input_event_receivers
459 .borrow_mut()
460 .push(result_receiver);
461 }
462 }
463
464 fn send_message_to_embedder(&self, msg: WebDriverCommandMsg) -> WebDriverResult<()> {
465 self.embedder_sender.send(msg).map_err(|_| {
466 WebDriverError::new(
467 ErrorStatus::UnknownError,
468 "Failed to send message to embedder",
469 )
470 })?;
471 self.event_loop_waker.wake();
472 Ok(())
473 }
474
475 fn add_load_status_sender(&self) -> WebDriverResult<()> {
476 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
477 self.browsing_context_id()?,
478 WebDriverScriptCommand::AddLoadStatusSender(
479 self.webview_id()?,
480 self.load_status_sender.clone(),
481 ),
482 ))
483 }
484
485 fn clear_load_status_sender(&self) -> WebDriverResult<()> {
486 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
487 self.browsing_context_id()?,
488 WebDriverScriptCommand::RemoveLoadStatusSender(self.webview_id()?),
489 ))
490 }
491
492 fn verified_webview_id(&self) -> WebViewId {
494 self.session().unwrap().current_webview_id().unwrap()
495 }
496
497 fn focused_webview_id(&self) -> WebDriverResult<Option<WebViewId>> {
498 let (sender, receiver) = ipc::channel().unwrap();
499 self.send_message_to_embedder(WebDriverCommandMsg::GetFocusedWebView(sender.clone()))?;
500 wait_for_ipc_response(receiver)
502 }
503
504 fn session(&self) -> WebDriverResult<&WebDriverSession> {
505 match self.session {
506 Some(ref x) => Ok(x),
507 None => Err(WebDriverError::new(
509 ErrorStatus::InvalidSessionId,
510 "Session not created",
511 )),
512 }
513 }
514
515 fn session_mut(&mut self) -> WebDriverResult<&mut WebDriverSession> {
516 match self.session {
517 Some(ref mut x) => Ok(x),
518 None => Err(WebDriverError::new(
520 ErrorStatus::InvalidSessionId,
521 "Session not created",
522 )),
523 }
524 }
525
526 fn handle_new_session(
528 &mut self,
529 parameters: &NewSessionParameters,
530 ) -> WebDriverResult<WebDriverResponse> {
531 if let Ok(value) = env::var("DELAY_AFTER_ACCEPT") {
532 let seconds = value.parse::<u64>().unwrap_or_default();
533 println!("Waiting for {} seconds...", seconds);
534 println!("lldb -p {}", process::id());
535 thread::sleep(Duration::from_secs(seconds));
536 }
537
538 if self.session.is_some() {
541 return Err(WebDriverError::new(
542 ErrorStatus::SessionNotCreated,
543 "Session already created",
544 ));
545 }
546
547 let mut servo_capabilities = ServoCapabilities::new();
552 let processed_capabilities = parameters.match_browser(&mut servo_capabilities)?;
553
554 let mut capabilities = match processed_capabilities {
556 Some(capabilities) => capabilities,
557 None => {
558 return Err(WebDriverError::new(
559 ErrorStatus::SessionNotCreated,
560 "Session not created due to invalid capabilities",
561 ));
562 },
563 };
564
565 let session_id = self.create_session(&mut capabilities, &servo_capabilities)?;
567
568 let response = NewSessionResponse::new(session_id.to_string(), Value::Object(capabilities));
570
571 match self.focused_webview_id()? {
573 Some(webview_id) => {
574 self.session_mut()?.set_webview_id(webview_id);
575 self.session_mut()?
576 .set_browsing_context_id(BrowsingContextId::from(webview_id));
577 },
578 None => {
579 let (sender, receiver) = ipc::channel().unwrap();
582
583 self.send_message_to_embedder(WebDriverCommandMsg::NewWebView(
584 sender,
585 Some(self.load_status_sender.clone()),
586 ))?;
587 let webview_id = receiver
588 .recv()
589 .expect("IPC failure when creating new webview for new session");
590 self.focus_webview(webview_id)?;
591 self.session_mut()?.set_webview_id(webview_id);
592 self.session_mut()?
593 .set_browsing_context_id(BrowsingContextId::from(webview_id));
594 let _ = self.wait_document_ready(Some(3000));
595 },
596 };
597
598 Ok(WebDriverResponse::NewSession(response))
603 }
604
605 fn handle_delete_session(&mut self) -> WebDriverResult<WebDriverResponse> {
607 self.session = None;
609
610 Ok(WebDriverResponse::DeleteSession)
612 }
613
614 fn handle_status(&self) -> WebDriverResult<WebDriverResponse> {
616 Ok(WebDriverResponse::Generic(ValueResponse(
617 if self.session.is_none() {
618 json!({ "ready": true, "message": "Ready for a new session" })
619 } else {
620 json!({ "ready": false, "message": "Not ready for a new session" })
621 },
622 )))
623 }
624
625 fn browsing_context_script_command(
629 &self,
630 cmd_msg: WebDriverScriptCommand,
631 verify: VerifyBrowsingContextIsOpen,
632 ) -> WebDriverResult<()> {
633 let browsing_context_id = self.browsing_context_id()?;
634 if let VerifyBrowsingContextIsOpen::Yes = verify {
635 self.verify_browsing_context_is_open(browsing_context_id)?;
636 }
637 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
638 browsing_context_id,
639 cmd_msg,
640 ))?;
641 Ok(())
642 }
643
644 fn top_level_script_command(
648 &self,
649 cmd_msg: WebDriverScriptCommand,
650 verify: VerifyBrowsingContextIsOpen,
651 ) -> WebDriverResult<()> {
652 let webview_id = self.webview_id()?;
653 if let VerifyBrowsingContextIsOpen::Yes = verify {
654 self.verify_top_level_browsing_context_is_open(webview_id)?;
655 }
656 let browsing_context_id = BrowsingContextId::from(webview_id);
657 self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand(
658 browsing_context_id,
659 cmd_msg,
660 ))?;
661 Ok(())
662 }
663
664 fn handle_get(&mut self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> {
666 let webview_id = self.webview_id()?;
667 self.verify_top_level_browsing_context_is_open(webview_id)?;
670 let url = ServoUrl::parse(¶meters.url)
673 .map(|url| url.into_url())
674 .map_err(|_| WebDriverError::new(ErrorStatus::InvalidArgument, "Invalid URL"))?;
675
676 self.handle_any_user_prompts(webview_id)?;
678
679 let cmd_msg =
680 WebDriverCommandMsg::LoadUrl(webview_id, url, self.load_status_sender.clone());
681 self.send_message_to_embedder(cmd_msg)?;
682
683 self.wait_for_navigation_complete()?;
685
686 self.session_mut()?
688 .set_browsing_context_id(BrowsingContextId::from(webview_id));
689
690 Ok(WebDriverResponse::Void)
691 }
692
693 fn wait_document_ready(&self, timeout: Option<u64>) -> WebDriverResult<WebDriverResponse> {
694 let timeout_channel = match timeout {
695 Some(timeout) => after(Duration::from_millis(timeout)),
696 None => crossbeam_channel::never(),
697 };
698
699 select! {
700 recv(self.load_status_receiver) -> res => {
701 match res {
702 Ok(Ok(WebDriverLoadStatus::Blocked)) => {
704 Ok(WebDriverResponse::Void)
711 },
712 Ok(Ok(WebDriverLoadStatus::Complete)) |
713 Ok(Ok(WebDriverLoadStatus::NavigationStop)) =>
714 Ok(WebDriverResponse::Void)
715 ,
716 _ => Err(WebDriverError::new(
717 ErrorStatus::UnknownError,
718 "Unexpected load status received while waiting for document ready state",
719 )),
720 }
721 },
722 recv(timeout_channel) -> _ => Err(
723 WebDriverError::new(ErrorStatus::Timeout, "Load timed out")
724 ),
725 }
726 }
727
728 fn wait_for_navigation_complete(&self) -> WebDriverResult<WebDriverResponse> {
730 debug!("waiting for load");
731
732 let session = self.session()?;
733
734 if session.page_loading_strategy() == PageLoadStrategy::None {
737 return Ok(WebDriverResponse::Void);
738 }
739
740 if self
743 .verify_browsing_context_is_open(self.browsing_context_id()?)
744 .is_err()
745 {
746 return Ok(WebDriverResponse::Void);
747 }
748
749 let timeout = session.session_timeouts().page_load;
751
752 let result = self.wait_document_ready(timeout);
755 debug!("finished waiting for load with {:?}", result);
756 result
757 }
758
759 fn wait_for_navigation(&self) -> WebDriverResult<WebDriverResponse> {
761 let navigation_status = match self.load_status_receiver.try_recv() {
762 Ok(Ok(status)) => status,
763 Err(crossbeam_channel::TryRecvError::Empty) => {
765 return Ok(WebDriverResponse::Void);
766 },
767 Err(crossbeam_channel::TryRecvError::Disconnected) => {
768 return Err(WebDriverError::new(
769 ErrorStatus::UnknownError,
770 "Load status channel disconnected",
771 ));
772 },
773 Ok(Err(ipc_error)) => {
774 return Err(WebDriverError::new(
775 ErrorStatus::UnknownError,
776 format!("Load status channel ipc error: {ipc_error}"),
777 ));
778 },
779 };
780
781 match navigation_status {
782 WebDriverLoadStatus::NavigationStart => self.wait_for_navigation_complete(),
783 WebDriverLoadStatus::Timeout => Err(WebDriverError::new(
785 ErrorStatus::Timeout,
786 "Navigation timed out",
787 )),
788 WebDriverLoadStatus::Blocked => Ok(WebDriverResponse::Void),
791 WebDriverLoadStatus::NavigationStop | WebDriverLoadStatus::Complete => {
792 unreachable!("Unexpected load status received")
793 },
794 }
795 }
796
797 fn handle_current_url(&self) -> WebDriverResult<WebDriverResponse> {
799 let webview_id = self.webview_id()?;
800
801 self.verify_top_level_browsing_context_is_open(webview_id)?;
804
805 self.handle_any_user_prompts(webview_id)?;
807
808 let (sender, receiver) = ipc::channel().unwrap();
809 self.top_level_script_command(
810 WebDriverScriptCommand::GetUrl(sender),
811 VerifyBrowsingContextIsOpen::No,
812 )?;
813
814 let url = wait_for_ipc_response(receiver)?;
815
816 Ok(WebDriverResponse::Generic(ValueResponse(
817 serde_json::to_value(url)?,
818 )))
819 }
820
821 fn handle_window_rect(
823 &self,
824 verify: VerifyBrowsingContextIsOpen,
825 ) -> WebDriverResult<WebDriverResponse> {
826 let (sender, receiver) = ipc::channel().unwrap();
827 let webview_id = self.webview_id()?;
828 if let VerifyBrowsingContextIsOpen::Yes = verify {
831 self.verify_top_level_browsing_context_is_open(webview_id)?;
832 }
833
834 self.handle_any_user_prompts(webview_id)?;
836
837 self.send_message_to_embedder(WebDriverCommandMsg::GetWindowRect(webview_id, sender))?;
838
839 let window_rect = wait_for_ipc_response(receiver)?;
840 let window_size_response = WindowRectResponse {
841 x: window_rect.min.x,
842 y: window_rect.min.y,
843 width: window_rect.width(),
844 height: window_rect.height(),
845 };
846 Ok(WebDriverResponse::WindowRect(window_size_response))
847 }
848
849 fn handle_set_window_rect(
851 &self,
852 params: &WindowRectParameters,
853 ) -> WebDriverResult<WebDriverResponse> {
854 let webview_id = self.webview_id()?;
862 self.verify_top_level_browsing_context_is_open(webview_id)?;
865
866 self.handle_any_user_prompts(webview_id)?;
868
869 let current = LazyCell::new(|| {
873 let WebDriverResponse::WindowRect(current) = self
874 .handle_window_rect(VerifyBrowsingContextIsOpen::No)
875 .unwrap()
876 else {
877 unreachable!("handle_window_size() must return WindowRect");
878 };
879 current
880 });
881
882 let (x, y, width, height) = (
883 params.x.unwrap_or_else(|| current.x),
884 params.y.unwrap_or_else(|| current.y),
885 params.width.unwrap_or_else(|| current.width),
886 params.height.unwrap_or_else(|| current.height),
887 );
888 let (sender, receiver) = ipc::channel().unwrap();
889 self.send_message_to_embedder(WebDriverCommandMsg::SetWindowRect(
895 webview_id,
896 DeviceIndependentIntRect::from_origin_and_size(
897 Point2D::new(x, y),
898 Size2D::new(width, height),
899 ),
900 sender,
901 ))?;
902
903 let window_rect = wait_for_ipc_response(receiver)?;
904 debug!("Result window_rect: {window_rect:?}");
905 let window_size_response = WindowRectResponse {
906 x: window_rect.min.x,
907 y: window_rect.min.y,
908 width: window_rect.width(),
909 height: window_rect.height(),
910 };
911 Ok(WebDriverResponse::WindowRect(window_size_response))
912 }
913
914 fn handle_maximize_window(&mut self) -> WebDriverResult<WebDriverResponse> {
916 let webview_id = self.webview_id()?;
920 self.verify_top_level_browsing_context_is_open(webview_id)?;
923
924 self.handle_any_user_prompts(self.webview_id()?)?;
926
927 let (sender, receiver) = ipc::channel().unwrap();
933 self.send_message_to_embedder(WebDriverCommandMsg::MaximizeWebView(webview_id, sender))?;
934
935 let window_rect = wait_for_ipc_response(receiver)?;
936 debug!("Result window_rect: {window_rect:?}");
937 let window_size_response = WindowRectResponse {
938 x: window_rect.min.x,
939 y: window_rect.min.y,
940 width: window_rect.width(),
941 height: window_rect.height(),
942 };
943 Ok(WebDriverResponse::WindowRect(window_size_response))
944 }
945
946 fn handle_is_enabled(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
947 let browsing_context = self.browsing_context_id()?;
950 self.verify_browsing_context_is_open(browsing_context)?;
951
952 let webview_id = self.webview_id()?;
954 self.handle_any_user_prompts(webview_id)?;
955
956 let (sender, receiver) = ipc::channel().unwrap();
957 self.browsing_context_script_command(
958 WebDriverScriptCommand::IsEnabled(element.to_string(), sender),
959 VerifyBrowsingContextIsOpen::No,
960 )?;
961
962 Ok(WebDriverResponse::Generic(ValueResponse(
963 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
964 )))
965 }
966
967 fn handle_is_selected(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
968 let browsing_context = self.browsing_context_id()?;
971 self.verify_browsing_context_is_open(browsing_context)?;
972
973 let webview_id = self.webview_id()?;
975 self.handle_any_user_prompts(webview_id)?;
976
977 let (sender, receiver) = ipc::channel().unwrap();
978 self.browsing_context_script_command(
979 WebDriverScriptCommand::IsSelected(element.to_string(), sender),
980 VerifyBrowsingContextIsOpen::No,
981 )?;
982
983 Ok(WebDriverResponse::Generic(ValueResponse(
984 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
985 )))
986 }
987
988 fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> {
990 let webview_id = self.webview_id()?;
991 self.verify_top_level_browsing_context_is_open(webview_id)?;
994
995 self.handle_any_user_prompts(webview_id)?;
997
998 self.send_message_to_embedder(WebDriverCommandMsg::GoBack(
999 webview_id,
1000 self.load_status_sender.clone(),
1001 ))?;
1002 self.wait_for_navigation_complete()
1003 }
1004
1005 fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> {
1007 let webview_id = self.webview_id()?;
1008 self.verify_top_level_browsing_context_is_open(webview_id)?;
1011
1012 self.handle_any_user_prompts(webview_id)?;
1014
1015 self.send_message_to_embedder(WebDriverCommandMsg::GoForward(
1016 webview_id,
1017 self.load_status_sender.clone(),
1018 ))?;
1019 self.wait_for_navigation_complete()
1020 }
1021
1022 fn handle_refresh(&mut self) -> WebDriverResult<WebDriverResponse> {
1024 let webview_id = self.webview_id()?;
1025 self.verify_top_level_browsing_context_is_open(webview_id)?;
1028
1029 self.handle_any_user_prompts(webview_id)?;
1031
1032 let cmd_msg = WebDriverCommandMsg::Refresh(webview_id, self.load_status_sender.clone());
1033 self.send_message_to_embedder(cmd_msg)?;
1034
1035 self.wait_for_navigation_complete()?;
1037
1038 self.session_mut()?
1040 .set_browsing_context_id(BrowsingContextId::from(webview_id));
1041
1042 Ok(WebDriverResponse::Void)
1043 }
1044
1045 fn handle_title(&self) -> WebDriverResult<WebDriverResponse> {
1047 let webview_id = self.webview_id()?;
1048
1049 self.verify_top_level_browsing_context_is_open(webview_id)?;
1052 self.handle_any_user_prompts(webview_id)?;
1054
1055 let (sender, receiver) = ipc::channel().unwrap();
1056
1057 self.top_level_script_command(
1058 WebDriverScriptCommand::GetTitle(sender),
1059 VerifyBrowsingContextIsOpen::No,
1060 )?;
1061
1062 let title = wait_for_ipc_response(receiver)?;
1065 Ok(WebDriverResponse::Generic(ValueResponse(
1066 serde_json::to_value(title)?,
1067 )))
1068 }
1069
1070 fn handle_window_handle(&mut self) -> WebDriverResult<WebDriverResponse> {
1072 let webview_id = self.webview_id()?;
1073
1074 self.verify_top_level_browsing_context_is_open(webview_id)?;
1077
1078 let handle = self
1080 .get_window_handle(webview_id)
1081 .expect("Failed to get window handle of an existing webview");
1082
1083 Ok(WebDriverResponse::Generic(ValueResponse(
1084 serde_json::to_value(handle)?,
1085 )))
1086 }
1087
1088 fn handle_window_handles(&mut self) -> WebDriverResult<WebDriverResponse> {
1090 let mut handles = self.get_window_handles();
1091 handles.sort_unstable();
1092
1093 Ok(WebDriverResponse::Generic(ValueResponse(
1094 serde_json::to_value(handles)?,
1095 )))
1096 }
1097
1098 fn get_window_handle(&mut self, webview_id: WebViewId) -> Option<String> {
1099 self.get_window_handles()
1100 .iter()
1101 .find(|id| id == &&webview_id.to_string())
1102 .cloned()
1103 }
1104
1105 fn get_window_handles(&self) -> Vec<String> {
1106 self.get_all_webview_ids()
1107 .into_iter()
1108 .map(|id| id.to_string())
1109 .collect()
1110 }
1111
1112 fn get_all_webview_ids(&self) -> Vec<WebViewId> {
1113 let (sender, receiver) = ipc::channel().unwrap();
1114 self.send_message_to_embedder(WebDriverCommandMsg::GetAllWebViews(sender))
1115 .unwrap();
1116 wait_for_ipc_response(receiver).unwrap_or_default()
1117 }
1118
1119 fn handle_close_window(&mut self) -> WebDriverResult<WebDriverResponse> {
1121 let webview_id = self.webview_id()?;
1122 self.verify_top_level_browsing_context_is_open(webview_id)?;
1125
1126 self.handle_any_user_prompts(webview_id)?;
1128
1129 let (sender, receiver) = ipc::channel().unwrap();
1131
1132 let cmd_msg = WebDriverCommandMsg::CloseWebView(webview_id, sender);
1133 self.send_message_to_embedder(cmd_msg)?;
1134
1135 wait_for_ipc_response(receiver)?;
1136
1137 let window_handles = self.get_window_handles();
1139
1140 if window_handles.is_empty() {
1141 self.session = None;
1142 }
1143
1144 Ok(WebDriverResponse::CloseWindow(CloseWindowResponse(
1146 window_handles,
1147 )))
1148 }
1149
1150 fn handle_new_window(
1152 &mut self,
1153 _parameters: &NewWindowParameters,
1154 ) -> WebDriverResult<WebDriverResponse> {
1155 let (sender, receiver) = ipc::channel().unwrap();
1156
1157 let webview_id = self.webview_id()?;
1158
1159 self.verify_top_level_browsing_context_is_open(webview_id)?;
1162
1163 self.handle_any_user_prompts(webview_id)?;
1165
1166 let cmd_msg =
1167 WebDriverCommandMsg::NewWebView(sender, Some(self.load_status_sender.clone()));
1168 self.send_message_to_embedder(cmd_msg)?;
1171
1172 if let Ok(webview_id) = receiver.recv() {
1173 let _ = self.wait_for_navigation_complete();
1174 let handle = self
1175 .get_window_handle(webview_id)
1176 .expect("Failed to get window handle of an existing webview");
1177
1178 Ok(WebDriverResponse::NewWindow(NewWindowResponse {
1179 handle,
1180 typ: "tab".to_string(),
1181 }))
1182 } else {
1183 Err(WebDriverError::new(
1184 ErrorStatus::UnknownError,
1185 "No webview ID received",
1186 ))
1187 }
1188 }
1189
1190 fn handle_switch_to_frame(
1192 &mut self,
1193 parameters: &SwitchToFrameParameters,
1194 ) -> WebDriverResult<WebDriverResponse> {
1195 use webdriver::common::FrameId;
1196 let frame_id = match parameters.id {
1197 FrameId::Top => {
1199 let webview_id = self.webview_id()?;
1200 self.verify_top_level_browsing_context_is_open(webview_id)?;
1203 self.handle_any_user_prompts(webview_id)?;
1205 let browsing_context_id = BrowsingContextId::from(webview_id);
1208 self.session_mut()?
1209 .set_browsing_context_id(browsing_context_id);
1210
1211 self.focus_browsing_context(browsing_context_id)?;
1215 return Ok(WebDriverResponse::Void);
1216 },
1217 FrameId::Short(ref x) => {
1219 WebDriverFrameId::Short(*x)
1223 },
1224 FrameId::Element(ref x) => WebDriverFrameId::Element(x.to_string()),
1225 };
1226
1227 self.switch_to_frame(frame_id)
1228 }
1229
1230 fn handle_switch_to_parent_frame(&mut self) -> WebDriverResult<WebDriverResponse> {
1232 let webview_id = self.webview_id()?;
1233 let browsing_context = self.browsing_context_id()?;
1234
1235 if browsing_context == webview_id {
1237 self.verify_browsing_context_is_open(browsing_context)?;
1240 return Ok(WebDriverResponse::Void);
1242 }
1243
1244 let (sender, receiver) = ipc::channel().unwrap();
1247 let cmd = WebDriverScriptCommand::GetParentFrameId(sender);
1248 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1249
1250 self.handle_any_user_prompts(webview_id)?;
1252
1253 let browsing_context_id = wait_for_ipc_response_flatten(receiver)?;
1256 self.session_mut()?
1257 .set_browsing_context_id(browsing_context_id);
1258 self.focus_browsing_context(browsing_context_id)?;
1262 Ok(WebDriverResponse::Void)
1263 }
1264
1265 fn handle_switch_to_window(
1267 &mut self,
1268 parameters: &SwitchToWindowParameters,
1269 ) -> WebDriverResult<WebDriverResponse> {
1270 let Some(webview_id) = self
1271 .get_all_webview_ids()
1272 .into_iter()
1273 .find(|id| id.to_string() == parameters.handle)
1274 else {
1275 return Err(WebDriverError::new(
1276 ErrorStatus::NoSuchWindow,
1277 "No such window while switching to window",
1278 ));
1279 };
1280
1281 let session = self.session_mut()?;
1282 session.set_webview_id(webview_id);
1283 session.set_browsing_context_id(BrowsingContextId::from(webview_id));
1284
1285 self.focus_webview(webview_id)?;
1289
1290 Ok(WebDriverResponse::Void)
1291 }
1292
1293 fn switch_to_frame(
1294 &mut self,
1295 frame_id: WebDriverFrameId,
1296 ) -> WebDriverResult<WebDriverResponse> {
1297 let (sender, receiver) = ipc::channel().unwrap();
1298 let cmd = WebDriverScriptCommand::GetBrowsingContextId(frame_id, sender);
1299 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1300 self.handle_any_user_prompts(self.webview_id()?)?;
1301
1302 let browsing_context_id = wait_for_ipc_response_flatten(receiver)?;
1303 self.session_mut()?
1304 .set_browsing_context_id(browsing_context_id);
1305 self.focus_browsing_context(browsing_context_id)?;
1309 Ok(WebDriverResponse::Void)
1310 }
1311
1312 fn handle_find_element(
1314 &self,
1315 parameters: &LocatorParameters,
1316 ) -> WebDriverResult<WebDriverResponse> {
1317 let res = self.handle_find_elements(parameters)?;
1319 unwrap_first_element_response(res)
1322 }
1323
1324 fn implicit_wait<T>(
1325 &self,
1326 callback: impl Fn() -> Result<Vec<T>, WebDriverError>,
1327 ) -> Result<Vec<T>, WebDriverError>
1328 where
1329 T: for<'de> Deserialize<'de> + Serialize,
1330 {
1331 let now = Instant::now();
1332 let (implicit_wait, sleep_interval) = {
1333 let timeouts = self.session()?.session_timeouts();
1334 (
1335 Duration::from_millis(timeouts.implicit_wait.unwrap_or(0)),
1336 Duration::from_millis(timeouts.sleep_interval),
1337 )
1338 };
1339
1340 loop {
1341 match callback() {
1342 Ok(value) if !value.is_empty() || now.elapsed() > implicit_wait => {
1343 return Ok(value);
1344 },
1345 Ok(_) => {},
1346 Err(error) => return Err(error),
1347 }
1348 sleep(sleep_interval);
1349 }
1350 }
1351
1352 fn handle_find_elements(
1354 &self,
1355 parameters: &LocatorParameters,
1356 ) -> WebDriverResult<WebDriverResponse> {
1357 if parameters.value.is_empty() {
1359 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1360 }
1361 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1364
1365 self.handle_any_user_prompts(self.webview_id()?)?;
1367
1368 self.implicit_wait(|| {
1369 let (sender, receiver) = ipc::channel().unwrap();
1370 match parameters.using {
1371 LocatorStrategy::CSSSelector => {
1372 let cmd = WebDriverScriptCommand::FindElementsCSSSelector(
1373 parameters.value.clone(),
1374 sender,
1375 );
1376 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1377 },
1378 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1379 let cmd = WebDriverScriptCommand::FindElementsLinkText(
1380 parameters.value.clone(),
1381 parameters.using == LocatorStrategy::PartialLinkText,
1382 sender,
1383 );
1384 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1385 },
1386 LocatorStrategy::TagName => {
1387 let cmd = WebDriverScriptCommand::FindElementsTagName(
1388 parameters.value.clone(),
1389 sender,
1390 );
1391 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1392 },
1393 LocatorStrategy::XPath => {
1394 let cmd = WebDriverScriptCommand::FindElementsXpathSelector(
1395 parameters.value.clone(),
1396 sender,
1397 );
1398 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1399 },
1400 }
1401 wait_for_ipc_response_flatten(receiver)
1402 })
1403 .and_then(|response| {
1404 let resp_value: Vec<WebElement> = response.into_iter().map(WebElement).collect();
1405 Ok(WebDriverResponse::Generic(ValueResponse(
1406 serde_json::to_value(resp_value)?,
1407 )))
1408 })
1409 }
1410
1411 fn handle_find_element_from_element(
1413 &self,
1414 element: &WebElement,
1415 parameters: &LocatorParameters,
1416 ) -> WebDriverResult<WebDriverResponse> {
1417 let res = self.handle_find_elements_from_element(element, parameters)?;
1419 unwrap_first_element_response(res)
1422 }
1423
1424 fn handle_find_elements_from_element(
1426 &self,
1427 element: &WebElement,
1428 parameters: &LocatorParameters,
1429 ) -> WebDriverResult<WebDriverResponse> {
1430 if parameters.value.is_empty() {
1432 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1433 }
1434 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1437
1438 self.handle_any_user_prompts(self.webview_id()?)?;
1440
1441 self.implicit_wait(|| {
1442 let (sender, receiver) = ipc::channel().unwrap();
1443
1444 match parameters.using {
1445 LocatorStrategy::CSSSelector => {
1446 let cmd = WebDriverScriptCommand::FindElementElementsCSSSelector(
1447 parameters.value.clone(),
1448 element.to_string(),
1449 sender,
1450 );
1451 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1452 },
1453 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1454 let cmd = WebDriverScriptCommand::FindElementElementsLinkText(
1455 parameters.value.clone(),
1456 element.to_string(),
1457 parameters.using == LocatorStrategy::PartialLinkText,
1458 sender,
1459 );
1460 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1461 },
1462 LocatorStrategy::TagName => {
1463 let cmd = WebDriverScriptCommand::FindElementElementsTagName(
1464 parameters.value.clone(),
1465 element.to_string(),
1466 sender,
1467 );
1468 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1469 },
1470 LocatorStrategy::XPath => {
1471 let cmd = WebDriverScriptCommand::FindElementElementsXPathSelector(
1472 parameters.value.clone(),
1473 element.to_string(),
1474 sender,
1475 );
1476 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1477 },
1478 }
1479
1480 wait_for_ipc_response_flatten(receiver)
1481 })
1482 .and_then(|response| {
1483 let resp_value: Vec<Value> = response
1484 .into_iter()
1485 .map(|x| serde_json::to_value(WebElement(x)).unwrap())
1486 .collect();
1487 Ok(WebDriverResponse::Generic(ValueResponse(
1488 serde_json::to_value(resp_value)?,
1489 )))
1490 })
1491 }
1492
1493 fn handle_find_elements_from_shadow_root(
1495 &self,
1496 shadow_root: &ShadowRoot,
1497 parameters: &LocatorParameters,
1498 ) -> WebDriverResult<WebDriverResponse> {
1499 if parameters.value.is_empty() {
1501 return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1502 }
1503
1504 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1507
1508 self.handle_any_user_prompts(self.webview_id()?)?;
1510
1511 self.implicit_wait(|| {
1512 let (sender, receiver) = ipc::channel().unwrap();
1513
1514 match parameters.using {
1515 LocatorStrategy::CSSSelector => {
1516 let cmd = WebDriverScriptCommand::FindShadowElementsCSSSelector(
1517 parameters.value.clone(),
1518 shadow_root.to_string(),
1519 sender,
1520 );
1521 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1522 },
1523 LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
1524 let cmd = WebDriverScriptCommand::FindShadowElementsLinkText(
1525 parameters.value.clone(),
1526 shadow_root.to_string(),
1527 parameters.using == LocatorStrategy::PartialLinkText,
1528 sender,
1529 );
1530 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1531 },
1532 LocatorStrategy::TagName => {
1533 let cmd = WebDriverScriptCommand::FindShadowElementsTagName(
1534 parameters.value.clone(),
1535 shadow_root.to_string(),
1536 sender,
1537 );
1538 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1539 },
1540 LocatorStrategy::XPath => {
1541 let cmd = WebDriverScriptCommand::FindShadowElementsXPathSelector(
1542 parameters.value.clone(),
1543 shadow_root.to_string(),
1544 sender,
1545 );
1546 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1547 },
1548 }
1549
1550 wait_for_ipc_response_flatten(receiver)
1551 })
1552 .and_then(|response| {
1553 let resp_value: Vec<Value> = response
1554 .into_iter()
1555 .map(|x| serde_json::to_value(WebElement(x)).unwrap())
1556 .collect();
1557 Ok(WebDriverResponse::Generic(ValueResponse(
1558 serde_json::to_value(resp_value)?,
1559 )))
1560 })
1561 }
1562
1563 fn handle_find_element_from_shadow_root(
1565 &self,
1566 shadow_root: &ShadowRoot,
1567 parameters: &LocatorParameters,
1568 ) -> WebDriverResult<WebDriverResponse> {
1569 let res = self.handle_find_elements_from_shadow_root(shadow_root, parameters)?;
1571 unwrap_first_element_response(res)
1574 }
1575
1576 fn handle_get_shadow_root(&self, element: WebElement) -> WebDriverResult<WebDriverResponse> {
1578 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1581 self.handle_any_user_prompts(self.webview_id()?)?;
1583 let (sender, receiver) = ipc::channel().unwrap();
1584 let cmd = WebDriverScriptCommand::GetElementShadowRoot(element.to_string(), sender);
1585 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1586 let Some(value) = wait_for_ipc_response_flatten(receiver)? else {
1588 return Err(WebDriverError::new(ErrorStatus::NoSuchShadowRoot, ""));
1589 };
1590 Ok(WebDriverResponse::Generic(ValueResponse(
1591 serde_json::to_value(ShadowRoot(value))?,
1592 )))
1593 }
1594
1595 fn handle_element_rect(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1597 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1600 self.handle_any_user_prompts(self.webview_id()?)?;
1602 let (sender, receiver) = ipc::channel().unwrap();
1603 let cmd = WebDriverScriptCommand::GetElementRect(element.to_string(), sender);
1604 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1605 let rect = wait_for_ipc_response_flatten(receiver)?;
1606 let response = ElementRectResponse {
1607 x: rect.origin.x,
1608 y: rect.origin.y,
1609 width: rect.size.width,
1610 height: rect.size.height,
1611 };
1612 Ok(WebDriverResponse::ElementRect(response))
1613 }
1614
1615 fn handle_element_text(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1617 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1620 self.handle_any_user_prompts(self.webview_id()?)?;
1622 let (sender, receiver) = ipc::channel().unwrap();
1623 let cmd = WebDriverScriptCommand::GetElementText(element.to_string(), sender);
1624 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1625 Ok(WebDriverResponse::Generic(ValueResponse(
1626 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1627 )))
1628 }
1629
1630 fn handle_active_element(&self) -> WebDriverResult<WebDriverResponse> {
1632 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1635 self.handle_any_user_prompts(self.webview_id()?)?;
1637 let (sender, receiver) = ipc::channel().unwrap();
1638 let cmd = WebDriverScriptCommand::GetActiveElement(sender);
1639 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1640 let value =
1641 wait_for_ipc_response(receiver)?.map(|x| serde_json::to_value(WebElement(x)).unwrap());
1642 if value.is_some() {
1646 Ok(WebDriverResponse::Generic(ValueResponse(
1647 serde_json::to_value(value)?,
1648 )))
1649 } else {
1650 Err(WebDriverError::new(
1651 ErrorStatus::NoSuchElement,
1652 "No active element found",
1653 ))
1654 }
1655 }
1656
1657 fn handle_computed_role(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1659 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1662 self.handle_any_user_prompts(self.webview_id()?)?;
1664 let (sender, receiver) = ipc::channel().unwrap();
1665 let cmd = WebDriverScriptCommand::GetComputedRole(element.to_string(), sender);
1666 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1667 Ok(WebDriverResponse::Generic(ValueResponse(
1668 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1669 )))
1670 }
1671
1672 fn handle_element_tag_name(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1674 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1677 self.handle_any_user_prompts(self.webview_id()?)?;
1679 let (sender, receiver) = ipc::channel().unwrap();
1680 let cmd = WebDriverScriptCommand::GetElementTagName(element.to_string(), sender);
1681 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1682 Ok(WebDriverResponse::Generic(ValueResponse(
1683 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1684 )))
1685 }
1686
1687 fn handle_element_attribute(
1689 &self,
1690 element: &WebElement,
1691 name: &str,
1692 ) -> WebDriverResult<WebDriverResponse> {
1693 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1696 self.handle_any_user_prompts(self.webview_id()?)?;
1698 let (sender, receiver) = ipc::channel().unwrap();
1699 let cmd = WebDriverScriptCommand::GetElementAttribute(
1700 element.to_string(),
1701 name.to_owned(),
1702 sender,
1703 );
1704 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1705 Ok(WebDriverResponse::Generic(ValueResponse(
1706 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1707 )))
1708 }
1709
1710 fn handle_element_property(
1712 &self,
1713 element: &WebElement,
1714 name: &str,
1715 ) -> WebDriverResult<WebDriverResponse> {
1716 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1719 self.handle_any_user_prompts(self.webview_id()?)?;
1721 let (sender, receiver) = ipc::channel().unwrap();
1722
1723 let cmd = WebDriverScriptCommand::GetElementProperty(
1724 element.to_string(),
1725 name.to_owned(),
1726 sender,
1727 );
1728 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1729
1730 Ok(WebDriverResponse::Generic(ValueResponse(
1731 serde_json::to_value(SendableJSValue(wait_for_ipc_response_flatten(receiver)?))?,
1732 )))
1733 }
1734
1735 fn handle_element_css(
1737 &self,
1738 element: &WebElement,
1739 name: &str,
1740 ) -> WebDriverResult<WebDriverResponse> {
1741 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1744 self.handle_any_user_prompts(self.webview_id()?)?;
1746 let (sender, receiver) = ipc::channel().unwrap();
1747 let cmd =
1748 WebDriverScriptCommand::GetElementCSS(element.to_string(), name.to_owned(), sender);
1749 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1750 Ok(WebDriverResponse::Generic(ValueResponse(
1751 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1752 )))
1753 }
1754
1755 fn handle_get_cookies(&self) -> WebDriverResult<WebDriverResponse> {
1757 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1760 self.handle_any_user_prompts(self.webview_id()?)?;
1762 let (sender, receiver) = ipc::channel().unwrap();
1763 let cmd = WebDriverScriptCommand::GetCookies(sender);
1764 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1765 let cookies = wait_for_ipc_response_flatten(receiver)?;
1766 let response = cookies
1767 .into_iter()
1768 .map(|cookie| cookie_msg_to_cookie(cookie.into_inner()))
1769 .collect::<Vec<Cookie>>();
1770 Ok(WebDriverResponse::Cookies(CookiesResponse(response)))
1771 }
1772
1773 fn handle_get_cookie(&self, name: String) -> WebDriverResult<WebDriverResponse> {
1775 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1778 self.handle_any_user_prompts(self.webview_id()?)?;
1780 let (sender, receiver) = ipc::channel().unwrap();
1781 let cmd = WebDriverScriptCommand::GetCookie(name, sender);
1782 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1783 let cookies = wait_for_ipc_response_flatten(receiver)?;
1784 let Some(response) = cookies
1785 .into_iter()
1786 .map(|cookie| cookie_msg_to_cookie(cookie.into_inner()))
1787 .next()
1788 else {
1789 return Err(WebDriverError::new(ErrorStatus::NoSuchCookie, ""));
1790 };
1791 Ok(WebDriverResponse::Cookie(CookieResponse(response)))
1792 }
1793
1794 fn handle_add_cookie(
1796 &self,
1797 params: &AddCookieParameters,
1798 ) -> WebDriverResult<WebDriverResponse> {
1799 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1802 self.handle_any_user_prompts(self.webview_id()?)?;
1804 let (sender, receiver) = ipc::channel().unwrap();
1805
1806 let mut cookie_builder =
1807 CookieBuilder::new(params.name.to_owned(), params.value.to_owned())
1808 .secure(params.secure)
1809 .http_only(params.httpOnly);
1810 if let Some(ref domain) = params.domain {
1811 cookie_builder = cookie_builder.domain(domain.clone());
1812 }
1813 if let Some(ref path) = params.path {
1814 cookie_builder = cookie_builder.path(path.clone());
1815 }
1816 if let Some(ref expiry) = params.expiry {
1817 if let Ok(datetime) = OffsetDateTime::from_unix_timestamp(expiry.0 as i64) {
1818 cookie_builder = cookie_builder.expires(datetime);
1819 }
1820 }
1821 if let Some(ref same_site) = params.sameSite {
1822 cookie_builder = match same_site.as_str() {
1823 "None" => Ok(cookie_builder.same_site(SameSite::None)),
1824 "Lax" => Ok(cookie_builder.same_site(SameSite::Lax)),
1825 "Strict" => Ok(cookie_builder.same_site(SameSite::Strict)),
1826 _ => Err(WebDriverError::new(
1827 ErrorStatus::InvalidArgument,
1828 "invalid argument",
1829 )),
1830 }?;
1831 }
1832
1833 let cmd = WebDriverScriptCommand::AddCookie(cookie_builder.build(), sender);
1834 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1835 wait_for_ipc_response_flatten(receiver)?;
1836 Ok(WebDriverResponse::Void)
1837 }
1838
1839 fn handle_delete_cookie(&self, name: String) -> WebDriverResult<WebDriverResponse> {
1841 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1844 self.handle_any_user_prompts(self.webview_id()?)?;
1846 let (sender, receiver) = ipc::channel().unwrap();
1847 let cmd = WebDriverScriptCommand::DeleteCookie(name, sender);
1848 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1849 wait_for_ipc_response_flatten(receiver)?;
1850 Ok(WebDriverResponse::Void)
1851 }
1852
1853 fn handle_delete_cookies(&self) -> WebDriverResult<WebDriverResponse> {
1855 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1858 self.handle_any_user_prompts(self.webview_id()?)?;
1860 let (sender, receiver) = ipc::channel().unwrap();
1861 let cmd = WebDriverScriptCommand::DeleteCookies(sender);
1862 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1863 wait_for_ipc_response_flatten(receiver)?;
1864 Ok(WebDriverResponse::Void)
1865 }
1866
1867 fn handle_get_timeouts(&mut self) -> WebDriverResult<WebDriverResponse> {
1869 let timeouts = self.session()?.session_timeouts();
1870
1871 let timeouts = TimeoutsResponse {
1875 script: timeouts.script,
1876 page_load: timeouts.page_load.unwrap_or(DEFAULT_PAGE_LOAD_TIMEOUT),
1877 implicit: timeouts.implicit_wait.unwrap_or(0),
1878 };
1879
1880 Ok(WebDriverResponse::Timeouts(timeouts))
1881 }
1882
1883 fn handle_set_timeouts(
1885 &mut self,
1886 parameters: &TimeoutsParameters,
1887 ) -> WebDriverResult<WebDriverResponse> {
1888 let session = self.session_mut()?;
1889
1890 if let Some(timeout) = parameters.script {
1891 session.session_timeouts_mut().script = timeout;
1892 }
1893 if let Some(timeout) = parameters.page_load {
1894 session.session_timeouts_mut().page_load = Some(timeout);
1895 }
1896 if let Some(timeout) = parameters.implicit {
1897 session.session_timeouts_mut().implicit_wait = Some(timeout);
1898 }
1899
1900 Ok(WebDriverResponse::Void)
1901 }
1902
1903 fn handle_get_page_source(&self) -> WebDriverResult<WebDriverResponse> {
1905 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1908 self.handle_any_user_prompts(self.webview_id()?)?;
1910 let (sender, receiver) = ipc::channel().unwrap();
1911
1912 let cmd = WebDriverScriptCommand::GetPageSource(sender);
1913 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
1914
1915 Ok(WebDriverResponse::Generic(ValueResponse(
1916 serde_json::to_value(wait_for_ipc_response_flatten(receiver)?)?,
1917 )))
1918 }
1919
1920 fn handle_perform_actions(
1922 &mut self,
1923 parameters: ActionsParameters,
1924 ) -> WebDriverResult<WebDriverResponse> {
1925 let browsing_context = self.browsing_context_id()?;
1926 self.verify_browsing_context_is_open(browsing_context)?;
1929
1930 self.handle_any_user_prompts(self.webview_id()?)?;
1932
1933 let actions_by_tick = self.extract_an_action_sequence(parameters);
1935
1936 match self.dispatch_actions(actions_by_tick, browsing_context) {
1938 Ok(_) => Ok(WebDriverResponse::Void),
1939 Err(error) => Err(WebDriverError::new(error, "")),
1940 }
1941 }
1942
1943 fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
1945 let browsing_context_id = self.browsing_context_id()?;
1946 self.verify_browsing_context_is_open(browsing_context_id)?;
1949
1950 self.handle_any_user_prompts(self.webview_id()?)?;
1952
1953 let undo_actions = self
1960 .session_mut()?
1961 .input_cancel_list
1962 .drain(..)
1963 .rev()
1964 .map(|(id, action_item)| Vec::from([(id, action_item)]))
1965 .collect();
1966 if let Err(err) = self.dispatch_actions(undo_actions, browsing_context_id) {
1968 return Err(WebDriverError::new(err, "Failed to dispatch undo actions"));
1969 }
1970
1971 self.session_mut()?.input_state_table.clear();
1973
1974 Ok(WebDriverResponse::Void)
1975 }
1976
1977 fn handle_execute_script(
1979 &self,
1980 parameters: JavascriptCommandParameters,
1981 ) -> WebDriverResult<WebDriverResponse> {
1982 let (func_body, args_string) = self.extract_script_arguments(parameters)?;
1985 let script = format!(
1989 "(function() {{ {}\n }})({})",
1990 func_body,
1991 args_string.join(", ")
1992 );
1993 debug!("{}", script);
1994
1995 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1998
1999 self.handle_any_user_prompts(self.webview_id()?)?;
2001
2002 let (sender, receiver) = ipc::channel().unwrap();
2003 let cmd = WebDriverScriptCommand::ExecuteScript(script, sender);
2004 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2005
2006 let timeout_duration = self
2007 .session()?
2008 .session_timeouts()
2009 .script
2010 .map(Duration::from_millis);
2011 let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2012
2013 self.javascript_evaluation_result_to_webdriver_response(result)
2014 }
2015
2016 fn handle_execute_async_script(
2017 &self,
2018 parameters: JavascriptCommandParameters,
2019 ) -> WebDriverResult<WebDriverResponse> {
2020 let (function_body, mut args_string) = self.extract_script_arguments(parameters)?;
2023 args_string.push("resolve".to_string());
2024
2025 let joined_args = args_string.join(", ");
2026 let script = format!(
2027 r#"(function() {{
2028 let webdriverPromise = new Promise(function(resolve, reject) {{
2029 (async function() {{
2030 {function_body}
2031 }})({joined_args})
2032 .then((v) => {{}}, (err) => reject(err))
2033 }})
2034 .then((v) => window.webdriverCallback(v), (r) => window.webdriverException(r))
2035 .catch((r) => window.webdriverException(r));
2036 }})();"#,
2037 );
2038 debug!("{}", script);
2039
2040 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2043
2044 self.handle_any_user_prompts(self.webview_id()?)?;
2046
2047 let (sender, receiver) = ipc::channel().unwrap();
2048 self.browsing_context_script_command(
2049 WebDriverScriptCommand::ExecuteAsyncScript(script, sender),
2050 VerifyBrowsingContextIsOpen::No,
2051 )?;
2052
2053 let timeout_duration = self
2054 .session()?
2055 .session_timeouts()
2056 .script
2057 .map(Duration::from_millis);
2058 let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2059
2060 self.javascript_evaluation_result_to_webdriver_response(result)
2061 }
2062
2063 fn javascript_evaluation_result_to_webdriver_response(
2064 &self,
2065 result: WebDriverJSResult,
2066 ) -> WebDriverResult<WebDriverResponse> {
2067 match result {
2068 Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
2069 serde_json::to_value(SendableJSValue(value))?,
2070 ))),
2071 Err(error) => {
2072 let message = format!("{error:?}");
2073 let status = match error {
2074 JavaScriptEvaluationError::DocumentNotFound => ErrorStatus::NoSuchWindow,
2075 JavaScriptEvaluationError::CompilationFailure => ErrorStatus::JavascriptError,
2076 JavaScriptEvaluationError::EvaluationFailure(Some(error_info)) => {
2077 return Err(WebDriverError::new_with_data(
2078 ErrorStatus::JavascriptError,
2079 error_info.message,
2080 None,
2081 error_info.stack,
2082 ));
2083 },
2084 JavaScriptEvaluationError::EvaluationFailure(None) => {
2085 ErrorStatus::JavascriptError
2086 },
2087 JavaScriptEvaluationError::InternalError => ErrorStatus::JavascriptError,
2088 JavaScriptEvaluationError::SerializationError(serialization_error) => {
2089 match serialization_error {
2090 JavaScriptEvaluationResultSerializationError::DetachedShadowRoot => {
2091 ErrorStatus::DetachedShadowRoot
2092 },
2093 JavaScriptEvaluationResultSerializationError::OtherJavaScriptError => {
2094 ErrorStatus::JavascriptError
2095 },
2096 JavaScriptEvaluationResultSerializationError::StaleElementReference => {
2097 ErrorStatus::StaleElementReference
2098 },
2099 JavaScriptEvaluationResultSerializationError::UnknownType => {
2100 ErrorStatus::UnsupportedOperation
2101 },
2102 }
2103 },
2104 JavaScriptEvaluationError::WebViewNotReady => ErrorStatus::NoSuchWindow,
2105 };
2106 Err(WebDriverError::new(status, message))
2107 },
2108 }
2109 }
2110
2111 fn handle_element_send_keys(
2113 &mut self,
2114 element: &WebElement,
2115 keys: &SendKeysParameters,
2116 ) -> WebDriverResult<WebDriverResponse> {
2117 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2120 self.handle_any_user_prompts(self.webview_id()?)?;
2122
2123 let (sender, receiver) = ipc::channel().unwrap();
2124 let cmd = WebDriverScriptCommand::WillSendKeys(
2125 element.to_string(),
2126 keys.text.to_string(),
2127 self.session()?.strict_file_interactability(),
2128 sender,
2129 );
2130 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2131
2132 if !wait_for_ipc_response_flatten(receiver)? {
2135 return Ok(WebDriverResponse::Void);
2136 }
2137
2138 let id = Uuid::new_v4().to_string();
2140
2141 self.session_mut()?
2143 .input_state_table
2144 .insert(id.clone(), InputSourceState::Key(KeyInputState::new()));
2145
2146 let input_events = send_keys(&keys.text);
2149
2150 for event in input_events {
2151 match event {
2152 DispatchStringEvent::Keyboard(event) => {
2153 let raw_string = convert_keyboard_event_to_string(&event);
2154 let key_action = match event.state {
2155 KeyState::Down => KeyAction::Down(KeyDownAction { value: raw_string }),
2156 KeyState::Up => KeyAction::Up(KeyUpAction { value: raw_string }),
2157 };
2158 let action_sequence = ActionSequence {
2159 id: id.clone(),
2160 actions: ActionsType::Key {
2161 actions: vec![KeyActionItem::Key(key_action)],
2162 },
2163 };
2164
2165 let actions_by_tick = self.actions_by_tick_from_sequence(vec![action_sequence]);
2166 if let Err(e) =
2167 self.dispatch_actions(actions_by_tick, self.browsing_context_id()?)
2168 {
2169 log::error!("handle_element_send_keys: dispatch_actions failed: {:?}", e);
2170 }
2171 },
2172 DispatchStringEvent::Composition(event) => {
2173 self.send_input_event_to_embedder(InputEvent::Ime(ImeEvent::Composition(
2174 event,
2175 )));
2176 },
2177 }
2178 }
2179
2180 if self
2185 .session()?
2186 .input_cancel_list
2187 .iter()
2188 .all(|(cancel_item_id, _)| &id != cancel_item_id)
2189 {
2190 self.session_mut()?.input_state_table.remove(&id);
2191 }
2192
2193 Ok(WebDriverResponse::Void)
2194 }
2195
2196 fn handle_element_clear(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2198 self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2201
2202 self.handle_any_user_prompts(self.webview_id()?)?;
2204
2205 let (sender, receiver) = ipc::channel().unwrap();
2207 let cmd = WebDriverScriptCommand::ElementClear(element.to_string(), sender);
2208 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2209
2210 wait_for_ipc_response_flatten(receiver)?;
2211 Ok(WebDriverResponse::Void)
2212 }
2213
2214 fn handle_element_click(&mut self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2216 let browsing_context_id = self.browsing_context_id()?;
2219 self.verify_browsing_context_is_open(browsing_context_id)?;
2220
2221 self.handle_any_user_prompts(self.webview_id()?)?;
2223
2224 let (sender, receiver) = ipc::channel().unwrap();
2225
2226 let cmd = WebDriverScriptCommand::ElementClick(element.to_string(), sender);
2228 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2229
2230 match wait_for_ipc_response_flatten(receiver)? {
2231 Some(element_id) => {
2232 self.add_load_status_sender()?;
2235
2236 self.perform_element_click(element_id)?;
2237
2238 let res = self.wait_for_navigation()?;
2241
2242 self.clear_load_status_sender()?;
2244
2245 Ok(res)
2246 },
2247 None => Ok(WebDriverResponse::Void),
2249 }
2250 }
2251
2252 fn perform_element_click(&mut self, element: String) -> WebDriverResult<WebDriverResponse> {
2255 let id = Uuid::new_v4().to_string();
2257
2258 let pointer_ids = self.session()?.pointer_ids();
2259 self.session_mut()?.input_state_table.insert(
2260 id.clone(),
2261 InputSourceState::Pointer(PointerInputState::new(PointerType::Mouse, pointer_ids)),
2262 );
2263
2264 let pointer_move_action = PointerMoveAction {
2269 duration: None,
2270 origin: PointerOrigin::Element(WebElement(element)),
2271 x: 0.0,
2272 y: 0.0,
2273 ..Default::default()
2274 };
2275
2276 let pointer_down_action = PointerDownAction {
2279 button: i16::from(MouseButton::Left) as u64,
2280 ..Default::default()
2281 };
2282
2283 let pointer_up_action = PointerUpAction {
2286 button: i16::from(MouseButton::Left) as u64,
2287 ..Default::default()
2288 };
2289
2290 let action_sequence = ActionSequence {
2291 id: id.clone(),
2292 actions: ActionsType::Pointer {
2293 parameters: PointerActionParameters {
2294 pointer_type: PointerType::Mouse,
2295 },
2296 actions: vec![
2297 PointerActionItem::Pointer(PointerAction::Move(pointer_move_action)),
2298 PointerActionItem::Pointer(PointerAction::Down(pointer_down_action)),
2299 PointerActionItem::Pointer(PointerAction::Up(pointer_up_action)),
2300 ],
2301 },
2302 };
2303
2304 let actions_by_tick = self.actions_by_tick_from_sequence(vec![action_sequence]);
2306 if let Err(e) = self.dispatch_actions(actions_by_tick, self.browsing_context_id()?) {
2307 log::error!("handle_element_click: dispatch_actions failed: {:?}", e);
2308 }
2309
2310 self.session_mut()?.input_state_table.remove(&id);
2312
2313 Ok(WebDriverResponse::Void)
2314 }
2315
2316 fn take_screenshot(&self, rect: Option<Rect<f32, CSSPixel>>) -> WebDriverResult<String> {
2317 let _ = self.handle_execute_async_script(JavascriptCommandParameters {
2319 script: "requestAnimationFrame(() => arguments[0]());".to_string(),
2320 args: None,
2321 });
2322 if rect.as_ref().is_some_and(Rect::is_empty) {
2323 return Err(WebDriverError::new(
2324 ErrorStatus::UnknownError,
2325 "The requested `rect` has zero width and/or height",
2326 ));
2327 }
2328
2329 let webview_id = self.webview_id()?;
2330 let (sender, receiver) = crossbeam_channel::unbounded();
2331 self.send_message_to_embedder(WebDriverCommandMsg::TakeScreenshot(
2332 webview_id, rect, sender,
2333 ))?;
2334
2335 let result = match receiver.recv_timeout(SCREENSHOT_TIMEOUT) {
2336 Ok(result) => Ok(result),
2337 Err(RecvTimeoutError::Timeout) => Err(WebDriverError::new(
2338 ErrorStatus::Timeout,
2339 "Timed out waiting to take screenshot. Test likely didn't finish.",
2340 )),
2341 Err(RecvTimeoutError::Disconnected) => Err(WebDriverError::new(
2342 ErrorStatus::UnknownError,
2343 "Could not take screenshot because channel disconnected.",
2344 )),
2345 }?;
2346
2347 let image = result.map_err(|error| {
2348 WebDriverError::new(
2349 ErrorStatus::UnknownError,
2350 format!("Failed to take screenshot: {error:?}"),
2351 )
2352 })?;
2353
2354 let mut png_data = Cursor::new(Vec::new());
2355 DynamicImage::ImageRgba8(image)
2356 .write_to(&mut png_data, ImageFormat::Png)
2357 .unwrap();
2358
2359 Ok(base64::engine::general_purpose::STANDARD.encode(png_data.get_ref()))
2360 }
2361
2362 fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> {
2363 let webview_id = self.webview_id()?;
2366 self.verify_top_level_browsing_context_is_open(webview_id)?;
2367
2368 self.handle_any_user_prompts(webview_id)?;
2369
2370 let encoded = self.take_screenshot(None)?;
2372
2373 Ok(WebDriverResponse::Generic(ValueResponse(
2374 serde_json::to_value(encoded)?,
2375 )))
2376 }
2377
2378 fn handle_take_element_screenshot(
2379 &self,
2380 element: &WebElement,
2381 ) -> WebDriverResult<WebDriverResponse> {
2382 let webview_id = self.webview_id()?;
2385 self.verify_top_level_browsing_context_is_open(webview_id)?;
2386
2387 self.handle_any_user_prompts(webview_id)?;
2389
2390 let (sender, receiver) = ipc::channel().unwrap();
2393 let cmd =
2394 WebDriverScriptCommand::ScrollAndGetBoundingClientRect(element.to_string(), sender);
2395 self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
2396
2397 let rect = wait_for_ipc_response_flatten(receiver)?;
2398
2399 let encoded = self.take_screenshot(Some(Rect::from_untyped(&rect)))?;
2401
2402 Ok(WebDriverResponse::Generic(ValueResponse(
2404 serde_json::to_value(encoded)?,
2405 )))
2406 }
2407
2408 fn handle_get_prefs(
2409 &self,
2410 parameters: &GetPrefsParameters,
2411 ) -> WebDriverResult<WebDriverResponse> {
2412 let prefs = parameters
2413 .prefs
2414 .iter()
2415 .map(|item| {
2416 (
2417 item.clone(),
2418 serde_json::to_value(prefs::get().get_value(item)).unwrap(),
2419 )
2420 })
2421 .collect::<BTreeMap<_, _>>();
2422
2423 Ok(WebDriverResponse::Generic(ValueResponse(
2424 serde_json::to_value(prefs)?,
2425 )))
2426 }
2427
2428 fn handle_set_prefs(
2429 &self,
2430 parameters: &SetPrefsParameters,
2431 ) -> WebDriverResult<WebDriverResponse> {
2432 let mut current_preferences = prefs::get().clone();
2433 for (key, value) in parameters.prefs.iter() {
2434 current_preferences.set_value(key, value.0.clone());
2435 }
2436 prefs::set(current_preferences);
2437
2438 Ok(WebDriverResponse::Void)
2439 }
2440
2441 fn handle_reset_prefs(
2442 &self,
2443 parameters: &GetPrefsParameters,
2444 ) -> WebDriverResult<WebDriverResponse> {
2445 let (new_preferences, map) = if parameters.prefs.is_empty() {
2446 (Preferences::default(), BTreeMap::new())
2447 } else {
2448 let mut new_preferences = prefs::get().clone();
2450 let default_preferences = Preferences::default();
2451 for key in parameters.prefs.iter() {
2452 new_preferences.set_value(key, default_preferences.get_value(key))
2453 }
2454
2455 let map = parameters
2456 .prefs
2457 .iter()
2458 .map(|item| (item.clone(), new_preferences.get_value(item)))
2459 .collect::<BTreeMap<_, _>>();
2460
2461 (new_preferences, map)
2462 };
2463
2464 prefs::set(new_preferences);
2465
2466 Ok(WebDriverResponse::Generic(ValueResponse(
2467 serde_json::to_value(map)?,
2468 )))
2469 }
2470
2471 fn handle_shutdown(&self) -> WebDriverResult<WebDriverResponse> {
2472 self.send_message_to_embedder(WebDriverCommandMsg::Shutdown)?;
2473 Ok(WebDriverResponse::Void)
2474 }
2475
2476 fn verify_top_level_browsing_context_is_open(
2477 &self,
2478 webview_id: WebViewId,
2479 ) -> Result<(), WebDriverError> {
2480 let (sender, receiver) = ipc::channel().unwrap();
2481 self.send_message_to_embedder(WebDriverCommandMsg::IsWebViewOpen(webview_id, sender))?;
2482 if wait_for_ipc_response(receiver)? {
2483 Ok(())
2484 } else {
2485 Err(WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2486 }
2487 }
2488
2489 fn verify_browsing_context_is_open(
2490 &self,
2491 browsing_context_id: BrowsingContextId,
2492 ) -> Result<(), WebDriverError> {
2493 let (sender, receiver) = ipc::channel().unwrap();
2494 self.send_message_to_embedder(WebDriverCommandMsg::IsBrowsingContextOpen(
2495 browsing_context_id,
2496 sender,
2497 ))?;
2498 if !receiver.recv().unwrap_or(false) {
2499 Err(WebDriverError::new(
2500 ErrorStatus::NoSuchWindow,
2501 "No such window",
2502 ))
2503 } else {
2504 Ok(())
2505 }
2506 }
2507
2508 fn focus_webview(&self, webview_id: WebViewId) -> WebDriverResult<()> {
2509 self.send_message_to_embedder(WebDriverCommandMsg::FocusWebView(webview_id))
2510 }
2511
2512 fn focus_browsing_context(&self, browsing_cotext_id: BrowsingContextId) -> WebDriverResult<()> {
2513 self.send_message_to_embedder(WebDriverCommandMsg::FocusBrowsingContext(
2514 browsing_cotext_id,
2515 ))
2516 }
2517}
2518
2519impl WebDriverHandler<ServoExtensionRoute> for Handler {
2520 fn handle_command(
2521 &mut self,
2522 _session: &Option<Session>,
2523 msg: WebDriverMessage<ServoExtensionRoute>,
2524 ) -> WebDriverResult<WebDriverResponse> {
2525 info!("{:?}", msg.command);
2526
2527 while self.load_status_receiver.try_recv().is_ok() {}
2529
2530 match msg.command {
2533 WebDriverCommand::NewSession(_) |
2534 WebDriverCommand::Status |
2535 WebDriverCommand::DeleteSession |
2536 WebDriverCommand::Extension(ServoExtensionCommand::Shutdown) => {},
2537 _ => {
2538 self.session()?;
2539 },
2540 }
2541
2542 match msg.command {
2543 WebDriverCommand::NewSession(ref parameters) => self.handle_new_session(parameters),
2544 WebDriverCommand::DeleteSession => self.handle_delete_session(),
2545 WebDriverCommand::Status => self.handle_status(),
2546 WebDriverCommand::AddCookie(ref parameters) => self.handle_add_cookie(parameters),
2547 WebDriverCommand::Get(ref parameters) => self.handle_get(parameters),
2548 WebDriverCommand::GetCurrentUrl => self.handle_current_url(),
2549 WebDriverCommand::GetWindowRect => {
2550 self.handle_window_rect(VerifyBrowsingContextIsOpen::Yes)
2551 },
2552 WebDriverCommand::SetWindowRect(ref size) => self.handle_set_window_rect(size),
2553 WebDriverCommand::IsEnabled(ref element) => self.handle_is_enabled(element),
2554 WebDriverCommand::IsSelected(ref element) => self.handle_is_selected(element),
2555 WebDriverCommand::GoBack => self.handle_go_back(),
2556 WebDriverCommand::GoForward => self.handle_go_forward(),
2557 WebDriverCommand::Refresh => self.handle_refresh(),
2558 WebDriverCommand::GetTitle => self.handle_title(),
2559 WebDriverCommand::GetWindowHandle => self.handle_window_handle(),
2560 WebDriverCommand::GetWindowHandles => self.handle_window_handles(),
2561 WebDriverCommand::NewWindow(ref parameters) => self.handle_new_window(parameters),
2562 WebDriverCommand::CloseWindow => self.handle_close_window(),
2563 WebDriverCommand::MaximizeWindow => self.handle_maximize_window(),
2564 WebDriverCommand::SwitchToFrame(ref parameters) => {
2565 self.handle_switch_to_frame(parameters)
2566 },
2567 WebDriverCommand::SwitchToParentFrame => self.handle_switch_to_parent_frame(),
2568 WebDriverCommand::SwitchToWindow(ref parameters) => {
2569 self.handle_switch_to_window(parameters)
2570 },
2571 WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
2572 WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
2573 WebDriverCommand::FindElementElement(ref element, ref parameters) => {
2574 self.handle_find_element_from_element(element, parameters)
2575 },
2576 WebDriverCommand::FindElementElements(ref element, ref parameters) => {
2577 self.handle_find_elements_from_element(element, parameters)
2578 },
2579 WebDriverCommand::FindShadowRootElements(ref shadow_root, ref parameters) => {
2580 self.handle_find_elements_from_shadow_root(shadow_root, parameters)
2581 },
2582 WebDriverCommand::FindShadowRootElement(ref shadow_root, ref parameters) => {
2583 self.handle_find_element_from_shadow_root(shadow_root, parameters)
2584 },
2585 WebDriverCommand::GetShadowRoot(element) => self.handle_get_shadow_root(element),
2586 WebDriverCommand::GetNamedCookie(name) => self.handle_get_cookie(name),
2587 WebDriverCommand::GetCookies => self.handle_get_cookies(),
2588 WebDriverCommand::GetActiveElement => self.handle_active_element(),
2589 WebDriverCommand::GetComputedRole(ref element) => self.handle_computed_role(element),
2590 WebDriverCommand::GetElementRect(ref element) => self.handle_element_rect(element),
2591 WebDriverCommand::GetElementText(ref element) => self.handle_element_text(element),
2592 WebDriverCommand::GetElementTagName(ref element) => {
2593 self.handle_element_tag_name(element)
2594 },
2595 WebDriverCommand::GetElementAttribute(ref element, ref name) => {
2596 self.handle_element_attribute(element, name)
2597 },
2598 WebDriverCommand::GetElementProperty(ref element, ref name) => {
2599 self.handle_element_property(element, name)
2600 },
2601 WebDriverCommand::GetCSSValue(ref element, ref name) => {
2602 self.handle_element_css(element, name)
2603 },
2604 WebDriverCommand::GetPageSource => self.handle_get_page_source(),
2605 WebDriverCommand::PerformActions(actions_parameters) => {
2606 self.handle_perform_actions(actions_parameters)
2607 },
2608 WebDriverCommand::ReleaseActions => self.handle_release_actions(),
2609 WebDriverCommand::ExecuteScript(x) => self.handle_execute_script(x),
2610 WebDriverCommand::ExecuteAsyncScript(x) => self.handle_execute_async_script(x),
2611 WebDriverCommand::ElementSendKeys(ref element, ref keys) => {
2612 self.handle_element_send_keys(element, keys)
2613 },
2614 WebDriverCommand::ElementClear(ref element) => self.handle_element_clear(element),
2615 WebDriverCommand::ElementClick(ref element) => self.handle_element_click(element),
2616 WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
2617 WebDriverCommand::AcceptAlert => self.handle_accept_alert(),
2618 WebDriverCommand::GetAlertText => self.handle_get_alert_text(),
2619 WebDriverCommand::SendAlertText(text) => self.handle_send_alert_text(text.text),
2620 WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
2621 WebDriverCommand::DeleteCookie(name) => self.handle_delete_cookie(name),
2622 WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),
2623 WebDriverCommand::SetTimeouts(ref x) => self.handle_set_timeouts(x),
2624 WebDriverCommand::TakeScreenshot => self.handle_take_screenshot(),
2625 WebDriverCommand::TakeElementScreenshot(ref x) => {
2626 self.handle_take_element_screenshot(x)
2627 },
2628 WebDriverCommand::Extension(extension) => match extension {
2629 ServoExtensionCommand::GetPrefs(ref x) => self.handle_get_prefs(x),
2630 ServoExtensionCommand::SetPrefs(ref x) => self.handle_set_prefs(x),
2631 ServoExtensionCommand::ResetPrefs(ref x) => self.handle_reset_prefs(x),
2632 ServoExtensionCommand::Shutdown => self.handle_shutdown(),
2633 },
2634 _ => Err(WebDriverError::new(
2635 ErrorStatus::UnsupportedOperation,
2636 format!("Command not implemented: {:?}", msg.command),
2637 )),
2638 }
2639 }
2640
2641 fn teardown_session(&mut self, _session: SessionTeardownKind) {
2642 self.session = None;
2643 }
2644}
2645
2646fn wait_for_ipc_response<T>(receiver: IpcReceiver<T>) -> Result<T, WebDriverError>
2647where
2648 T: for<'de> Deserialize<'de> + Serialize,
2649{
2650 receiver
2651 .recv()
2652 .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2653}
2654
2655fn wait_for_ipc_response_flatten<T>(
2658 receiver: IpcReceiver<Result<T, ErrorStatus>>,
2659) -> Result<T, WebDriverError>
2660where
2661 T: for<'de> Deserialize<'de> + Serialize,
2662{
2663 match receiver.recv() {
2664 Ok(Ok(value)) => Ok(value),
2665 Ok(Err(error_status)) => Err(WebDriverError::new(error_status, "")),
2666 Err(_) => Err(WebDriverError::new(ErrorStatus::NoSuchWindow, "")),
2667 }
2668}
2669
2670fn wait_for_script_ipc_response_with_timeout<T>(
2671 receiver: IpcReceiver<T>,
2672 timeout: Option<Duration>,
2673) -> Result<T, WebDriverError>
2674where
2675 T: for<'de> Deserialize<'de> + Serialize,
2676{
2677 let Some(timeout) = timeout else {
2678 return wait_for_ipc_response(receiver);
2679 };
2680 receiver
2681 .try_recv_timeout(timeout)
2682 .map_err(|error| match error {
2683 TryRecvError::IpcError(_) => WebDriverError::new(ErrorStatus::NoSuchWindow, ""),
2684 TryRecvError::Empty => WebDriverError::new(ErrorStatus::ScriptTimeout, ""),
2685 })
2686}
2687
2688fn unwrap_first_element_response(res: WebDriverResponse) -> WebDriverResult<WebDriverResponse> {
2689 if let WebDriverResponse::Generic(ValueResponse(values)) = res {
2690 let arr = values.as_array().unwrap();
2691 if let Some(first) = arr.first() {
2692 Ok(WebDriverResponse::Generic(ValueResponse(first.clone())))
2693 } else {
2694 Err(WebDriverError::new(ErrorStatus::NoSuchElement, ""))
2695 }
2696 } else {
2697 unreachable!()
2698 }
2699}
2700
2701fn convert_keyboard_event_to_string(event: &KeyboardEvent) -> String {
2702 let key = &event.key;
2703 let named_key = match key {
2704 Key::Character(s) => return s.to_string(),
2705 Key::Named(named_key) => named_key,
2706 };
2707
2708 match event.location {
2709 Location::Left | Location::Standard => match named_key {
2710 NamedKey::Unidentified => '\u{E000}'.to_string(),
2711 NamedKey::Cancel => '\u{E001}'.to_string(),
2712 NamedKey::Help => '\u{E002}'.to_string(),
2713 NamedKey::Backspace => '\u{E003}'.to_string(),
2714 NamedKey::Tab => '\u{E004}'.to_string(),
2715 NamedKey::Clear => '\u{E005}'.to_string(),
2716 NamedKey::Enter => match event.code {
2717 Code::NumpadEnter => '\u{E007}'.to_string(),
2718 _ => '\u{E006}'.to_string(),
2719 },
2720 NamedKey::Shift => '\u{E008}'.to_string(),
2721 NamedKey::Control => '\u{E009}'.to_string(),
2722 NamedKey::Alt => '\u{E00A}'.to_string(),
2723 NamedKey::Pause => '\u{E00B}'.to_string(),
2724 NamedKey::Escape => '\u{E00C}'.to_string(),
2725 NamedKey::PageUp => '\u{E00E}'.to_string(),
2726 NamedKey::PageDown => '\u{E00F}'.to_string(),
2727 NamedKey::End => '\u{E010}'.to_string(),
2728 NamedKey::Home => '\u{E011}'.to_string(),
2729 NamedKey::ArrowLeft => '\u{E012}'.to_string(),
2730 NamedKey::ArrowUp => '\u{E013}'.to_string(),
2731 NamedKey::ArrowRight => '\u{E014}'.to_string(),
2732 NamedKey::ArrowDown => '\u{E015}'.to_string(),
2733 NamedKey::Insert => '\u{E016}'.to_string(),
2734 NamedKey::Delete => '\u{E017}'.to_string(),
2735 NamedKey::F1 => '\u{E031}'.to_string(),
2736 NamedKey::F2 => '\u{E032}'.to_string(),
2737 NamedKey::F3 => '\u{E033}'.to_string(),
2738 NamedKey::F4 => '\u{E034}'.to_string(),
2739 NamedKey::F5 => '\u{E035}'.to_string(),
2740 NamedKey::F6 => '\u{E036}'.to_string(),
2741 NamedKey::F7 => '\u{E037}'.to_string(),
2742 NamedKey::F8 => '\u{E038}'.to_string(),
2743 NamedKey::F9 => '\u{E039}'.to_string(),
2744 NamedKey::F10 => '\u{E03A}'.to_string(),
2745 NamedKey::F11 => '\u{E03B}'.to_string(),
2746 NamedKey::F12 => '\u{E03C}'.to_string(),
2747 NamedKey::Meta => '\u{E03D}'.to_string(),
2748 NamedKey::ZenkakuHankaku => '\u{E040}'.to_string(),
2749 _ => {
2750 error!("Unexpected NamedKey on send_keys");
2751 '\u{E000}'.to_string()
2752 },
2753 },
2754 Location::Right | Location::Numpad => match named_key {
2755 NamedKey::Shift => '\u{E050}'.to_string(),
2756 NamedKey::Control => '\u{E051}'.to_string(),
2757 NamedKey::Alt => '\u{E052}'.to_string(),
2758 NamedKey::Meta => '\u{E053}'.to_string(),
2759 NamedKey::PageUp => '\u{E054}'.to_string(),
2760 NamedKey::PageDown => '\u{E055}'.to_string(),
2761 NamedKey::End => '\u{E056}'.to_string(),
2762 NamedKey::Home => '\u{E057}'.to_string(),
2763 NamedKey::ArrowLeft => '\u{E058}'.to_string(),
2764 NamedKey::ArrowUp => '\u{E059}'.to_string(),
2765 NamedKey::ArrowRight => '\u{E05A}'.to_string(),
2766 NamedKey::ArrowDown => '\u{E05B}'.to_string(),
2767 NamedKey::Insert => '\u{E05C}'.to_string(),
2768 NamedKey::Delete => '\u{E05D}'.to_string(),
2769 _ => {
2770 error!("Unexpected NamedKey on send_keys");
2771 '\u{E000}'.to_string()
2772 },
2773 },
2774 }
2775}