Skip to main content

webdriver_server/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![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
81/// <https://262.ecma-international.org/6.0/#sec-number.max_safe_integer>
82/// 2^53 - 1
83pub(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        // <https://html.spec.whatwg.org/multipage/#set-rph-registration-mode>
108        (
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    /// The threaded receiver on which we can block for a load-status.
165    /// It will receive messages sent on the load_status_sender,
166    /// and forwarded by the IPC router.
167    load_status_receiver: RoutedReceiver<WebDriverLoadStatus>,
168    /// The IPC sender which we can clone and pass along to the constellation,
169    /// for it to send us a load-status. Messages sent on it
170    /// will be forwarded to the load_status_receiver.
171    load_status_sender: GenericSender<WebDriverLoadStatus>,
172
173    session: Option<WebDriverSession>,
174
175    /// A [`Sender`] that sends messages to the embedder that this `WebDriver instance controls.
176    /// In addition to sending a message, we must always wake up the embedder's event loop so it
177    /// knows that more messages are available for processing.
178    embedder_sender: Sender<WebDriverCommandMsg>,
179
180    /// An [`EventLoopWaker`] which is used to wake up the embedder event loop.
181    event_loop_waker: Box<dyn EventLoopWaker>,
182
183    /// A list of [`Receiver`]s that are used to track when input events are handled in the DOM.
184    /// Once these receivers receive a response, we know that the event has been handled.
185    ///
186    /// TODO: Once we upgrade crossbeam-channel this can be replaced with a `WaitGroup`.
187    pending_input_event_receivers: Vec<Receiver<()>>,
188
189    /// Ongoing [`PointerMoveAction`] or [`WheelScrollAction`] that are being incrementally
190    /// processed across multiple execution cycles within the current tick.
191    pending_actions: Vec<PendingActions>,
192
193    /// The base set of preferences to treat as default when resetting.
194    default_preferences: Preferences,
195}
196
197#[derive(Clone, Copy, Debug, PartialEq)]
198enum ServoExtensionRoute {
199    GetPrefs,
200    SetPrefs,
201    ResetPrefs,
202    /// TODO: Shutdown does not actually use sessionId.
203    /// But the webdriver crate always checks existence of sessionID
204    /// except for WebDriverCommand::Status.
205    /// We have to either use our own fork, or relies on the current workaround:
206    /// passing any dummy sessionID.
207    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        // Create a pair of both an IPC and a threaded channel,
460        // keep the IPC sender to clone and pass to the constellation for each load,
461        // and keep a threaded receiver to block on an incoming load-status.
462        // Pass the others to the IPC router so that IPC messages are forwarded to the threaded receiver.
463        // We need to use the router because IPC does not come with a timeout on receive/select.
464        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    // FIXME: This should be completely removed after we revamp the touch chain.
494    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    // This function is called only if session and webview are verified.
545    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 until the document is ready before returning the top-level browsing context id.
553        wait_for_oneshot_response(receiver)
554    }
555
556    fn session(&self) -> WebDriverResult<&WebDriverSession> {
557        match self.session {
558            Some(ref x) => Ok(x),
559            // https://w3c.github.io/webdriver/#ref-for-dfn-invalid-session-id-1
560            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            // https://w3c.github.io/webdriver/#ref-for-dfn-invalid-session-id-1
571            None => Err(WebDriverError::new(
572                ErrorStatus::InvalidSessionId,
573                "Session not created",
574            )),
575        }
576    }
577
578    /// <https://w3c.github.io/webdriver/#new-session>
579    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        // Step 1. If the list of active HTTP sessions is not empty
591        // return error with error code session not created.
592        if self.session.is_some() {
593            return Err(WebDriverError::new(
594                ErrorStatus::SessionNotCreated,
595                "Session already created",
596            ));
597        }
598
599        // Step 2. Skip because the step is only applied to an intermediary node.
600        // Step 3. Skip since all sessions are http for now.
601
602        // Step 4. Let capabilities be the result of trying to process capabilities
603        let mut servo_capabilities = ServoCapabilities::new();
604        let processed_capabilities = parameters.match_browser(&mut servo_capabilities)?;
605
606        // Step 5. If capabilities's is null, return error with error code session not created.
607        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        // Step 6. Create a session
618        let session_id = self.create_session(&mut capabilities, &servo_capabilities)?;
619
620        // Step 7. Let response be a JSON Object initialized with session's session ID and capabilities
621        let response = NewSessionResponse::new(session_id.to_string(), Value::Object(capabilities));
622
623        // Step 8. Set session' current top-level browsing context
624        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                // This happens when there is no open webview.
633                // We need to create a new one. See https://github.com/servo/servo/issues/37408
634                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        // Step 9. Set the request queue to a new queue.
654        // Skip here because the requests are handled in the external crate.
655
656        // Step 10. Return success with data body
657        Ok(WebDriverResponse::NewSession(response))
658    }
659
660    /// <https://w3c.github.io/webdriver/#dfn-delete-session>
661    fn handle_delete_session(&mut self) -> WebDriverResult<WebDriverResponse> {
662        // Step 1. If session is http, close the session
663        self.session = None;
664
665        // Step 2. Return success with data null
666        Ok(WebDriverResponse::DeleteSession)
667    }
668
669    /// <https://w3c.github.io/webdriver/#status>
670    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    /// Send command to Script Thread with session's current browsing context.
681    /// If `verify` is [`VerifyBrowsingContextIsOpen::Yes`],
682    /// it would verify the existence of browsing context before sending.
683    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    /// Send command to Script Thread with session's current top-level browsing context.
700    /// If `verify` is [`VerifyBrowsingContextIsOpen::Yes`],
701    /// it would verify the existence of top-level browsing context before sending.
702    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    /// <https://w3c.github.io/webdriver/#navigate-to>
720    fn handle_get(&mut self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> {
721        let webview_id = self.webview_id()?;
722        // Step 2. If session's current top-level browsing context is no longer open,
723        // return error with error code no such window.
724        self.verify_top_level_browsing_context_is_open(webview_id)?;
725        // Step 3. If URL is not an absolute URL or is not an absolute URL with fragment
726        // or not a local scheme, return error with error code invalid argument.
727        let url = ServoUrl::parse(&parameters.url)
728            .map(|url| url.into_url())
729            .map_err(|_| WebDriverError::new(ErrorStatus::InvalidArgument, "Invalid URL"))?;
730
731        // Step 4. Handle any user prompt.
732        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        // Step 8.2.1: try to wait for navigation to complete.
739        self.wait_for_navigation_complete()?;
740
741        // Step 8.3. Set current browsing context with session and current top browsing context
742        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                    // If the navigation is navigation to IFrame, no document state event is fired.
758                    Ok(Ok(WebDriverLoadStatus::Blocked)) => {
759                        // TODO: evaluate the correctness later
760                        // Load status is block means an user prompt is shown.
761                        // Alot of tests expect this to return success
762                        // then the user prompt is handled in the next command.
763                        // If user prompt can't be handler, next command returns
764                        // an error anyway.
765                        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    /// <https://w3c.github.io/webdriver/#dfn-wait-for-navigation-to-complete>
784    fn wait_for_navigation_complete(&self) -> WebDriverResult<WebDriverResponse> {
785        debug!("waiting for load");
786
787        let session = self.session()?;
788
789        // Step 1. If session's page loading strategy is "none",
790        // return success with data null.
791        if session.page_loading_strategy() == PageLoadStrategy::None {
792            return Ok(WebDriverResponse::Void);
793        }
794
795        // Step 2. If session's current browsing context is no longer open,
796        // return success with data null.
797        if self
798            .verify_browsing_context_is_open(self.browsing_context_id()?)
799            .is_err()
800        {
801            return Ok(WebDriverResponse::Void);
802        }
803
804        // Step 3. let timeout be the session's page load timeout.
805        let timeout = session.session_timeouts().page_load;
806
807        // TODO: Step 4. Implement timer parameter
808
809        let result = self.wait_document_ready(timeout);
810        debug!("finished waiting for load with {:?}", result);
811        result
812    }
813
814    /// <https://w3c.github.io/webdriver/#dfn-wait-for-navigation-to-complete>
815    fn wait_for_navigation(&self) -> WebDriverResult<WebDriverResponse> {
816        let navigation_status = match self.load_status_receiver.try_recv() {
817            Ok(Ok(status)) => status,
818            // Empty channel means no navigation started. Nothing to wait for.
819            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            // If the load status is timeout, return an error
839            WebDriverLoadStatus::Timeout => Err(WebDriverError::new(
840                ErrorStatus::Timeout,
841                "Navigation timed out",
842            )),
843            // If the load status is blocked, it means a user prompt is shown.
844            // We should handle the user prompt in the next command.
845            WebDriverLoadStatus::Blocked => Ok(WebDriverResponse::Void),
846            WebDriverLoadStatus::NavigationStop | WebDriverLoadStatus::Complete => {
847                unreachable!("Unexpected load status received")
848            },
849        }
850    }
851
852    /// <https://w3c.github.io/webdriver/#dfn-get-current-url>
853    fn handle_current_url(&self) -> WebDriverResult<WebDriverResponse> {
854        let webview_id = self.webview_id()?;
855
856        // Step 1. If session's current top-level browsing context is no longer open,
857        // return error with error code no such window.
858        self.verify_top_level_browsing_context_is_open(webview_id)?;
859
860        // Step 2. Handle any user prompt.
861        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    /// <https://w3c.github.io/webdriver/#get-window-rect>
877    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        // Step 1. If session's current top-level browsing context is no longer open,
884        // return error with error code no such window.
885        if let VerifyBrowsingContextIsOpen::Yes = verify {
886            self.verify_top_level_browsing_context_is_open(webview_id)?;
887        }
888
889        // Step 2. Handle any user prompt.
890        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    /// <https://w3c.github.io/webdriver/#set-window-rect>
905    fn handle_set_window_rect(
906        &self,
907        params: &WindowRectParameters,
908    ) -> WebDriverResult<WebDriverResponse> {
909        // Step 9 - 10. Input Validation. Already done when deserialize.
910
911        // Step 11. In case the Set Window Rect command is partially supported
912        // (i.e. some combinations of arguments are supported but not others),
913        // the implmentation is expected to continue with the remaining steps.
914        // DO NOT return "unsupported operation".
915
916        let webview_id = self.webview_id()?;
917        // Step 12. If session's current top-level browsing context is no longer open,
918        // return error with error code no such window.
919        self.verify_top_level_browsing_context_is_open(webview_id)?;
920
921        // Step 13. Handle any user prompt.
922        self.handle_any_user_prompts(webview_id)?;
923
924        // (TODO) Step 14. Fully exit fullscreen.
925        // (TODO) Step 15. Restore the window.
926
927        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        // Step 16 - 17. Set the width/height in CSS pixels.
945        // This should be done as long as one of width/height is not null.
946
947        // Step 18 - 19. Set the screen x/y in CSS pixels.
948        // This should be done as long as one of width/height is not null.
949        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    /// <https://w3c.github.io/webdriver/#maximize-window>
970    fn handle_maximize_window(&mut self) -> WebDriverResult<WebDriverResponse> {
971        // Step 1. If the remote end does not support the Maximize Window command for session's
972        // current top-level browsing context for any reason,
973        // return error with error code unsupported operation.
974        let webview_id = self.webview_id()?;
975        // Step 2. If session's current top-level browsing context is no longer open,
976        // return error with error code no such window.
977        self.verify_top_level_browsing_context_is_open(webview_id)?;
978
979        // Step 3. Try to handle any user prompts with session.
980        self.handle_any_user_prompts(self.webview_id()?)?;
981
982        // Step 4. (TODO) Fully exit fullscreen.
983
984        // Step 5. (TODO) Restore the window.
985
986        // Step 6. Maximize the window of session's current top-level browsing context.
987        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        // Step 1. If session's current browsing context is no longer open,
1003        // return error with error code no such window.
1004        let browsing_context = self.browsing_context_id()?;
1005        self.verify_browsing_context_is_open(browsing_context)?;
1006
1007        // Step 2. Try to handle any user prompts with session.
1008        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        // Step 1. If session's current browsing context is no longer open,
1024        // return error with error code no such window.
1025        let browsing_context = self.browsing_context_id()?;
1026        self.verify_browsing_context_is_open(browsing_context)?;
1027
1028        // Step 2. Try to handle any user prompts with session.
1029        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    /// <https://w3c.github.io/webdriver/#back>
1044    fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> {
1045        let webview_id = self.webview_id()?;
1046        // Step 1. If session's current top-level browsing context is no longer open,
1047        // return error with error code no such window.
1048        self.verify_top_level_browsing_context_is_open(webview_id)?;
1049
1050        // Step 2. Handle any user prompt.
1051        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    /// <https://w3c.github.io/webdriver/#forward>
1061    fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> {
1062        let webview_id = self.webview_id()?;
1063        // Step 1. If session's current top-level browsing context is no longer open,
1064        // return error with error code no such window.
1065        self.verify_top_level_browsing_context_is_open(webview_id)?;
1066
1067        // Step 2. Handle any user prompt.
1068        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    /// <https://w3c.github.io/webdriver/#refresh>
1078    fn handle_refresh(&mut self) -> WebDriverResult<WebDriverResponse> {
1079        let webview_id = self.webview_id()?;
1080        // Step 1. If session's current top-level browsing context is no longer open,
1081        // return error with error code no such window.
1082        self.verify_top_level_browsing_context_is_open(webview_id)?;
1083
1084        // Step 2. Handle any user prompt.
1085        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        // Step 4.1: Try to wait for navigation to complete.
1091        self.wait_for_navigation_complete()?;
1092
1093        // Step 5. Set current browsing context with session and current top browsing context.
1094        self.session_mut()?
1095            .set_browsing_context_id(BrowsingContextId::from(webview_id));
1096
1097        Ok(WebDriverResponse::Void)
1098    }
1099
1100    /// <https://w3c.github.io/webdriver/#get-title>
1101    fn handle_title(&self) -> WebDriverResult<WebDriverResponse> {
1102        let webview_id = self.webview_id()?;
1103
1104        // Step 1. If session's current top-level browsing context is no longer open,
1105        // return error with error code no such window.
1106        self.verify_top_level_browsing_context_is_open(webview_id)?;
1107        // Step 2. Handle any user prompt.
1108        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        // Step 3. Let title be the session's current top-level
1118        // browsing context's active document's title.
1119        let title = wait_for_ipc_response(receiver)?;
1120        Ok(WebDriverResponse::Generic(ValueResponse(
1121            serde_json::to_value(title)?,
1122        )))
1123    }
1124
1125    /// <https://w3c.github.io/webdriver/#get-window-handle>
1126    fn handle_window_handle(&mut self) -> WebDriverResult<WebDriverResponse> {
1127        let webview_id = self.webview_id()?;
1128
1129        // Step 1. If session's current top-level browsing context is no longer open,
1130        // return error with error code no such window.
1131        self.verify_top_level_browsing_context_is_open(webview_id)?;
1132
1133        // Step 2. Return success with the window handle.
1134        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    /// <https://w3c.github.io/webdriver/#get-window-handles>
1144    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    /// <https://w3c.github.io/webdriver/#close-window>
1175    fn handle_close_window(&mut self) -> WebDriverResult<WebDriverResponse> {
1176        let webview_id = self.webview_id()?;
1177        // Step 1. If session's current top-level browsing context is no longer open,
1178        // return error with error code no such window.
1179        self.verify_top_level_browsing_context_is_open(webview_id)?;
1180
1181        // Step 2. Handle any user prompt.
1182        self.handle_any_user_prompts(webview_id)?;
1183
1184        // Step 3. Close session's current top-level browsing context.
1185        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        // Step 4. If there are no more open top-level browsing contexts, try to close the session.
1193        let window_handles = self.get_window_handles();
1194
1195        if window_handles.is_empty() {
1196            self.session = None;
1197        }
1198
1199        // Step 5. Return the result of running the remote end steps for the Get Window Handles command
1200        Ok(WebDriverResponse::CloseWindow(CloseWindowResponse(
1201            window_handles,
1202        )))
1203    }
1204
1205    /// <https://w3c.github.io/webdriver/#new-window>
1206    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        // Step 2. If session's current top-level browsing context is no longer open,
1215        // return error with error code no such window.
1216        self.verify_top_level_browsing_context_is_open(webview_id)?;
1217
1218        // Step 3. Handle any user prompt.
1219        self.handle_any_user_prompts(webview_id)?;
1220
1221        // Step 4. Let type hint be the result of getting the property "type" from
1222        // parameters.
1223        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        // Step 5. Create a new top-level browsing context by running the window open
1230        // steps with URL set to "about:blank", target set to the empty string, and
1231        // features set to "noopener" and the user agent configured to create a new
1232        // browsing context. This must be done without invoking the focusing steps for the
1233        // created browsing context. If type hint has the value "tab", and the
1234        // implementation supports multiple browsing context in the same OS window, the
1235        // new browsing context should share an OS window with session's current browsing
1236        // context. If type hint is "window", and the implementation supports multiple
1237        // browsing contexts in separate OS windows, the created browsing context should
1238        // be in a new OS window. In all other cases the details of how the browsing
1239        // context is presented to the user are implementation defined.
1240        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    /// <https://w3c.github.io/webdriver/#dfn-switch-to-frame>
1265    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            // id is null
1272            FrameId::Top => {
1273                let webview_id = self.webview_id()?;
1274                // Step 1. If session's current top-level browsing context is no longer open,
1275                // return error with error code no such window.
1276                self.verify_top_level_browsing_context_is_open(webview_id)?;
1277                // Step 2. Try to handle any user prompts with session.
1278                self.handle_any_user_prompts(webview_id)?;
1279                // Step 3. Set the current browsing context with session and
1280                // session's current top-level browsing context.
1281                let browsing_context_id = BrowsingContextId::from(webview_id);
1282                self.session_mut()?
1283                    .set_browsing_context_id(browsing_context_id);
1284
1285                // Step 4. Update any implementation-specific state that would result from
1286                // the user selecting session's current browsing context for interaction,
1287                // without altering OS-level focus.
1288                self.focus_browsing_context(browsing_context_id)?;
1289                return Ok(WebDriverResponse::Void);
1290            },
1291            // id is a Number object
1292            FrameId::Short(ref x) => {
1293                // (Already handled when deserializing in webdriver-crate)
1294                // Step 1. If id is less than 0 or greater than 2^16 – 1,
1295                // return error with error code invalid argument.
1296                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    /// <https://w3c.github.io/webdriver/#switch-to-parent-frame>
1305    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        // Step 1. If session's current browsing context is already the top-level browsing context:
1310        if browsing_context == webview_id {
1311            // Step 1.1. If session's current browsing context is no longer open,
1312            // return error with error code no such window.
1313            self.verify_browsing_context_is_open(browsing_context)?;
1314            // Step 1.2. Return success with data null.
1315            return Ok(WebDriverResponse::Void);
1316        }
1317
1318        // Step 2. If session's current parent browsing context is no longer open,
1319        // return error with error code no such window.
1320        let (sender, receiver) = generic_channel::channel().unwrap();
1321        let cmd = WebDriverScriptCommand::GetParentFrameId(sender);
1322        self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
1323
1324        // Step 3. Handle any user prompt.
1325        self.handle_any_user_prompts(webview_id)?;
1326
1327        // Step 4. If session's current parent browsing context is not null,
1328        // set the current browsing context with session and current parent browsing context.
1329        let browsing_context_id = wait_for_ipc_response_flatten(receiver)?;
1330        self.session_mut()?
1331            .set_browsing_context_id(browsing_context_id);
1332        // Step 5. Update any implementation-specific state that would result from
1333        // the user selecting session's current browsing context for interaction,
1334        // without altering OS-level focus.
1335        self.focus_browsing_context(browsing_context_id)?;
1336        Ok(WebDriverResponse::Void)
1337    }
1338
1339    /// <https://w3c.github.io/webdriver/#switch-to-window>
1340    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        // Step 5. Update any implementation-specific state that would result
1360        // from the user selecting session's current browsing context for interaction,
1361        // without altering OS-level focus.
1362        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        // Step 4. Update any implementation-specific state that would result from
1380        // the user selecting session's current browsing context for interaction,
1381        // without altering OS-level focus.
1382        self.focus_browsing_context(browsing_context_id)?;
1383        Ok(WebDriverResponse::Void)
1384    }
1385
1386    /// <https://w3c.github.io/webdriver/#find-element>
1387    fn handle_find_element(
1388        &self,
1389        parameters: &LocatorParameters,
1390    ) -> WebDriverResult<WebDriverResponse> {
1391        // Step 1 - 9.
1392        let res = self.handle_find_elements(parameters)?;
1393        // Step 10. If result is empty, return error with error code no such element.
1394        // Otherwise, return the first element of result.
1395        unwrap_first_element_response(res)
1396    }
1397
1398    /// The boolean in callback result indicates whether implicit_wait can early return
1399    /// before timeout with current result.
1400    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    /// <https://w3c.github.io/webdriver/#find-elements>
1433    fn handle_find_elements(
1434        &self,
1435        parameters: &LocatorParameters,
1436    ) -> WebDriverResult<WebDriverResponse> {
1437        // Step 4. If selector is undefined, return error with error code invalid argument.
1438        if parameters.value.is_empty() {
1439            return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1440        }
1441        // Step 5. If session's current browsing context is no longer open,
1442        // return error with error code no such window.
1443        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1444
1445        // Step 6. Handle any user prompt.
1446        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    /// <https://w3c.github.io/webdriver/#find-element-from-element>
1485    fn handle_find_element_from_element(
1486        &self,
1487        element: &WebElement,
1488        parameters: &LocatorParameters,
1489    ) -> WebDriverResult<WebDriverResponse> {
1490        // Step 1 - 8.
1491        let res = self.handle_find_elements_from_element(element, parameters)?;
1492        // Step 9. If result is empty, return error with error code no such element.
1493        // Otherwise, return the first element of result.
1494        unwrap_first_element_response(res)
1495    }
1496
1497    /// <https://w3c.github.io/webdriver/#find-elements-from-element>
1498    fn handle_find_elements_from_element(
1499        &self,
1500        element: &WebElement,
1501        parameters: &LocatorParameters,
1502    ) -> WebDriverResult<WebDriverResponse> {
1503        // Step 4. If selector is undefined, return error with error code invalid argument.
1504        if parameters.value.is_empty() {
1505            return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1506        }
1507        // Step 5. If session's current browsing context is no longer open,
1508        // return error with error code no such window.
1509        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1510
1511        // Step 6. Handle any user prompt.
1512        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    /// <https://w3c.github.io/webdriver/#find-elements-from-shadow-root>
1562    fn handle_find_elements_from_shadow_root(
1563        &self,
1564        shadow_root: &ShadowRoot,
1565        parameters: &LocatorParameters,
1566    ) -> WebDriverResult<WebDriverResponse> {
1567        // Step 4. If selector is undefined, return error with error code invalid argument.
1568        if parameters.value.is_empty() {
1569            return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
1570        }
1571
1572        // Step 5. If session's current browsing context is no longer open,
1573        // return error with error code no such window.
1574        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1575
1576        // Step 6. Handle any user prompt.
1577        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    /// <https://w3c.github.io/webdriver/#find-element-from-shadow-root>
1627    fn handle_find_element_from_shadow_root(
1628        &self,
1629        shadow_root: &ShadowRoot,
1630        parameters: &LocatorParameters,
1631    ) -> WebDriverResult<WebDriverResponse> {
1632        // Step 1 - 8.
1633        let res = self.handle_find_elements_from_shadow_root(shadow_root, parameters)?;
1634        // Step 9. If result is empty, return error with error code no such element.
1635        // Otherwise, return the first element of result.
1636        unwrap_first_element_response(res)
1637    }
1638
1639    /// <https://w3c.github.io/webdriver/#get-element-shadow-root>
1640    fn handle_get_shadow_root(&self, element: WebElement) -> WebDriverResult<WebDriverResponse> {
1641        // Step 1. If session's current browsing context is no longer open,
1642        // return error with error code no such window.
1643        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1644        // Step 2. Handle any user prompt.
1645        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        // Step 5. If shadow root is null, return error with error code no such shadow root.
1650        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    /// <https://w3c.github.io/webdriver/#get-element-rect>
1659    fn handle_element_rect(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1660        // Step 1. If session's current browsing context is no longer open,
1661        // return error with error code no such window.
1662        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1663        // Step 2. Handle any user prompt.
1664        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    /// <https://w3c.github.io/webdriver/#dfn-get-element-text>
1679    fn handle_element_text(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1680        // Step 1. If session's current browsing context is no longer open,
1681        // return error with error code no such window.
1682        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1683        // Step 2. Handle any user prompt.
1684        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    ///<https://w3c.github.io/webdriver/#get-active-element>
1694    fn handle_active_element(&self) -> WebDriverResult<WebDriverResponse> {
1695        // Step 1. If session's current browsing context is no longer open,
1696        // return error with error code no such window.
1697        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1698        // Step 2. Handle any user prompt.
1699        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        // Step 4. If active element is a non-null element, return success
1706        // with data set to web element reference object for session and active element.
1707        // Otherwise, return error with error code no such element.
1708        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    /// <https://w3c.github.io/webdriver/#get-computed-role>
1721    fn handle_computed_role(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1722        // Step 1. If session's current browsing context is no longer open,
1723        // return error with error code no such window.
1724        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1725        // Step 2. Handle any user prompt.
1726        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    /// <https://w3c.github.io/webdriver/#get-element-tag-name>
1736    fn handle_element_tag_name(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
1737        // Step 1. If session's current browsing context is no longer open,
1738        // return error with error code no such window.
1739        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1740        // Step 2. Handle any user prompt.
1741        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    /// <https://w3c.github.io/webdriver/#get-element-attribute>
1751    fn handle_element_attribute(
1752        &self,
1753        element: &WebElement,
1754        name: &str,
1755    ) -> WebDriverResult<WebDriverResponse> {
1756        // Step 1. If session's current browsing context is no longer open,
1757        // return error with error code no such window.
1758        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1759        // Step 2. Handle any user prompt.
1760        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    /// <https://w3c.github.io/webdriver/#get-element-property>
1774    fn handle_element_property(
1775        &self,
1776        element: &WebElement,
1777        name: &str,
1778    ) -> WebDriverResult<WebDriverResponse> {
1779        // Step 1. If session's current browsing context is no longer open,
1780        // return error with error code no such window.
1781        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1782        // Step 2. Handle any user prompt.
1783        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    /// <https://w3c.github.io/webdriver/#get-element-css-value>
1799    fn handle_element_css(
1800        &self,
1801        element: &WebElement,
1802        name: &str,
1803    ) -> WebDriverResult<WebDriverResponse> {
1804        // Step 1. If session's current browsing context is no longer open,
1805        // return error with error code no such window.
1806        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1807        // Step 2. Handle any user prompt.
1808        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    /// <https://w3c.github.io/webdriver/#get-all-cookies>
1819    fn handle_get_cookies(&self) -> WebDriverResult<WebDriverResponse> {
1820        // Step 1. If session's current browsing context is no longer open,
1821        // return error with error code no such window.
1822        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1823        // Step 2. Handle any user prompt.
1824        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    /// <https://w3c.github.io/webdriver/#get-named-cookie>
1837    fn handle_get_cookie(&self, name: String) -> WebDriverResult<WebDriverResponse> {
1838        // Step 1. If session's current browsing context is no longer open,
1839        // return error with error code no such window.
1840        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1841        // Step 2. Handle any user prompt.
1842        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    /// <https://w3c.github.io/webdriver/#add-cookie>
1858    fn handle_add_cookie(
1859        &self,
1860        params: &AddCookieParameters,
1861    ) -> WebDriverResult<WebDriverResponse> {
1862        // Step 1. If session's current browsing context is no longer open,
1863        // return error with error code no such window.
1864        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1865        // Step 2. Handle any user prompt.
1866        self.handle_any_user_prompts(self.webview_id()?)?;
1867        let (sender, receiver) = generic_channel::channel().unwrap();
1868
1869        // Step 6. cookie expiry time is not an integer type,
1870        // or it less than 0 or greater than the maximum safe integer,
1871        // return error with error code invalid argument.
1872        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    /// <https://w3c.github.io/webdriver/#delete-cookie>
1916    fn handle_delete_cookie(&self, name: String) -> WebDriverResult<WebDriverResponse> {
1917        // Step 1. If session's current browsing context is no longer open,
1918        // return error with error code no such window.
1919        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1920        // Step 2. Handle any user prompt.
1921        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    /// <https://w3c.github.io/webdriver/#delete-all-cookies>
1930    fn handle_delete_cookies(&self) -> WebDriverResult<WebDriverResponse> {
1931        // Step 1. If session's current browsing context is no longer open,
1932        // return error with error code no such window.
1933        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1934        // Step 2. Handle any user prompt.
1935        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    /// <https://w3c.github.io/webdriver/#get-timeouts>
1944    fn handle_get_timeouts(&mut self) -> WebDriverResult<WebDriverResponse> {
1945        let timeouts = self.session()?.session_timeouts();
1946
1947        // FIXME: The specification says that all of these values can be `null`, but the `webdriver` crate
1948        // only supports setting `script` as null. When set to null, report these values as being the
1949        // default ones for now.
1950        // Waiting for version bump together with geckodriver.
1951        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    /// <https://w3c.github.io/webdriver/#set-timeouts>
1961    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    /// <https://w3c.github.io/webdriver/#dfn-get-page-source>
1981    fn handle_get_page_source(&self) -> WebDriverResult<WebDriverResponse> {
1982        // Step 1. If session's current browsing context is no longer open,
1983        // return error with error code no such window.
1984        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
1985        // Step 2. Handle any user prompt.
1986        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    /// <https://w3c.github.io/webdriver/#perform-actions>
1998    fn handle_perform_actions(
1999        &mut self,
2000        parameters: ActionsParameters,
2001    ) -> WebDriverResult<WebDriverResponse> {
2002        let browsing_context = self.browsing_context_id()?;
2003        // Step 1. If session's current browsing context is no longer open,
2004        // return error with error code no such window.
2005        self.verify_browsing_context_is_open(browsing_context)?;
2006
2007        // Step 2. Handle any user prompt.
2008        self.handle_any_user_prompts(self.webview_id()?)?;
2009
2010        // Step 5. Let actions by tick be the result of trying to extract an action sequence
2011        let actions_by_tick = self.extract_an_action_sequence(parameters.actions);
2012
2013        // Step 6. Dispatch actions with current browsing context
2014        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    /// <https://w3c.github.io/webdriver/#dfn-release-actions>
2021    fn handle_release_actions(&mut self) -> WebDriverResult<WebDriverResponse> {
2022        let browsing_context_id = self.browsing_context_id()?;
2023        // Step 1. If session's current browsing context is no longer open,
2024        // return error with error code no such window.
2025        self.verify_browsing_context_is_open(browsing_context_id)?;
2026
2027        // Step 2. User prompts.
2028        self.handle_any_user_prompts(self.webview_id()?)?;
2029
2030        // TODO: Step 4. Actions options are not used yet.
2031
2032        // Step 5. Not needed because "In a session that is only a HTTP session
2033        // only one command can run at a time, so this will never block."
2034
2035        // Step 6. Let undo actions be input cancel list in reverse order.
2036        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        // Step 7. Dispatch undo actions with current browsing context.
2044        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        // Step 8. Reset the input state of session's current top-level browsing context.
2049        self.session_mut()?.input_state_table.clear();
2050
2051        Ok(WebDriverResponse::Void)
2052    }
2053
2054    /// <https://w3c.github.io/webdriver/#dfn-execute-script>
2055    fn handle_execute_script(
2056        &self,
2057        parameters: JavascriptCommandParameters,
2058    ) -> WebDriverResult<WebDriverResponse> {
2059        // Step 1. Let body and arguments be the result of trying to extract the script arguments
2060        // from a request with argument parameters.
2061        let (func_body, args_string) = self.extract_script_arguments(parameters)?;
2062
2063        // This is pretty ugly; we really want something that acts like
2064        // new Function() and then takes the resulting function and executes
2065        // it with a vec of arguments.
2066        let script = format!(
2067            r#"(async function() {{
2068                try {{
2069                    let result = (async function() {{
2070                        {func_body}
2071                    }})({});
2072                    let value = await result;
2073                    window.webdriverCallback(value);
2074                }} catch (err) {{
2075                    window.webdriverException(err);
2076                }}
2077            }})();"#,
2078            args_string.join(", ")
2079        );
2080
2081        debug!("{}", script);
2082
2083        // Step 2. If session's current browsing context is no longer open,
2084        // return error with error code no such window.
2085        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2086
2087        // Step 3. Handle any user prompt.
2088        self.handle_any_user_prompts(self.webview_id()?)?;
2089
2090        let (sender, receiver) = generic_channel::channel().unwrap();
2091        let cmd = WebDriverScriptCommand::ExecuteScriptWithCallback(script, sender);
2092
2093        self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2094
2095        let timeout_duration = self
2096            .session()?
2097            .session_timeouts()
2098            .script
2099            .map_or(Duration::MAX, Duration::from_millis);
2100
2101        let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2102
2103        self.javascript_evaluation_result_to_webdriver_response(result)
2104    }
2105
2106    fn handle_execute_async_script(
2107        &self,
2108        parameters: JavascriptCommandParameters,
2109    ) -> WebDriverResult<WebDriverResponse> {
2110        // Step 1. Let body and arguments be the result of trying to extract the script arguments
2111        // from a request with argument parameters.
2112        let (function_body, mut args_string) = self.extract_script_arguments(parameters)?;
2113        args_string.push("resolve".to_string());
2114
2115        let joined_args = args_string.join(", ");
2116        let script = format!(
2117            r#"(function() {{
2118                new Promise(function(resolve, reject) {{
2119                  (async function() {{
2120                    {function_body}
2121                  }})({joined_args})
2122                    .catch(reject)
2123              }})
2124              .then((v) => window.webdriverCallback(v), (r) => window.webdriverException(r))
2125              .catch((r) => window.webdriverException(r));
2126            }})();"#,
2127        );
2128        debug!("{}", script);
2129
2130        // Step 2. If session's current browsing context is no longer open,
2131        // return error with error code no such window.
2132        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2133
2134        // Step 3. Handle any user prompt.
2135        self.handle_any_user_prompts(self.webview_id()?)?;
2136
2137        let (sender, receiver) = generic_channel::channel().unwrap();
2138        self.browsing_context_script_command(
2139            WebDriverScriptCommand::ExecuteScriptWithCallback(script, sender),
2140            VerifyBrowsingContextIsOpen::No,
2141        )?;
2142
2143        let timeout_duration = self
2144            .session()?
2145            .session_timeouts()
2146            .script
2147            .map_or(Duration::MAX, Duration::from_millis);
2148        let result = wait_for_script_ipc_response_with_timeout(receiver, timeout_duration)?;
2149
2150        self.javascript_evaluation_result_to_webdriver_response(result)
2151    }
2152
2153    fn javascript_evaluation_result_to_webdriver_response(
2154        &self,
2155        result: WebDriverJSResult,
2156    ) -> WebDriverResult<WebDriverResponse> {
2157        match result {
2158            Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
2159                serde_json::to_value(SendableJSValue(value))?,
2160            ))),
2161            Err(error) => {
2162                let message = format!("{error:?}");
2163                let status = match error {
2164                    JavaScriptEvaluationError::DocumentNotFound => ErrorStatus::NoSuchWindow,
2165                    JavaScriptEvaluationError::CompilationFailure => ErrorStatus::JavascriptError,
2166                    JavaScriptEvaluationError::EvaluationFailure(Some(error_info)) => {
2167                        return Err(WebDriverError::new_with_data(
2168                            ErrorStatus::JavascriptError,
2169                            error_info.message,
2170                            None,
2171                            error_info.stack,
2172                        ));
2173                    },
2174                    JavaScriptEvaluationError::EvaluationFailure(None) => {
2175                        ErrorStatus::JavascriptError
2176                    },
2177                    JavaScriptEvaluationError::InternalError => ErrorStatus::JavascriptError,
2178                    JavaScriptEvaluationError::SerializationError(serialization_error) => {
2179                        match serialization_error {
2180                            JavaScriptEvaluationResultSerializationError::DetachedShadowRoot => {
2181                                ErrorStatus::DetachedShadowRoot
2182                            },
2183                            JavaScriptEvaluationResultSerializationError::OtherJavaScriptError => {
2184                                ErrorStatus::JavascriptError
2185                            },
2186                            JavaScriptEvaluationResultSerializationError::StaleElementReference => {
2187                                ErrorStatus::StaleElementReference
2188                            },
2189                            JavaScriptEvaluationResultSerializationError::UnknownType => {
2190                                ErrorStatus::UnsupportedOperation
2191                            },
2192                        }
2193                    },
2194                    JavaScriptEvaluationError::WebViewNotReady => ErrorStatus::NoSuchWindow,
2195                };
2196                Err(WebDriverError::new(status, message))
2197            },
2198        }
2199    }
2200
2201    /// <https://w3c.github.io/webdriver/#dfn-element-send-keys>
2202    fn handle_element_send_keys(
2203        &mut self,
2204        element: &WebElement,
2205        keys: &SendKeysParameters,
2206    ) -> WebDriverResult<WebDriverResponse> {
2207        // Step 3. If session's current browsing context is no longer open,
2208        // return error with error code no such window.
2209        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2210        // Step 4. Handle any user prompt.
2211        self.handle_any_user_prompts(self.webview_id()?)?;
2212
2213        let (sender, receiver) = generic_channel::channel().unwrap();
2214        let cmd = WebDriverScriptCommand::WillSendKeys(
2215            element.to_string(),
2216            keys.text.to_string(),
2217            self.session()?.strict_file_interactability(),
2218            sender,
2219        );
2220        self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2221
2222        // File input and non-typeable form control should have
2223        // been handled in `webdriver_handler.rs`.
2224        if !wait_for_ipc_response_flatten(receiver)? {
2225            return Ok(WebDriverResponse::Void);
2226        }
2227
2228        // Step 10. Let input id be a the result of generating a UUID.
2229        let id = Uuid::new_v4().to_string();
2230
2231        // Step 12. Add an input source
2232        self.session_mut()?
2233            .input_state_table
2234            .insert(id.clone(), InputSourceState::Key(KeyInputState::new()));
2235
2236        // Step 13. dispatch actions for a string
2237        // https://w3c.github.io/webdriver/#dfn-dispatch-actions-for-a-string
2238        let input_events = send_keys(&keys.text);
2239
2240        for event in input_events {
2241            match event {
2242                DispatchStringEvent::Keyboard(event) => {
2243                    let raw_string = convert_keyboard_event_to_string(&event);
2244                    let key_action = match event.state {
2245                        KeyState::Down => KeyAction::Down(KeyDownAction { value: raw_string }),
2246                        KeyState::Up => KeyAction::Up(KeyUpAction { value: raw_string }),
2247                    };
2248                    let action_sequence = ActionSequence {
2249                        id: id.clone(),
2250                        actions: ActionsType::Key {
2251                            actions: vec![KeyActionItem::Key(key_action)],
2252                        },
2253                    };
2254
2255                    let actions_by_tick = self.extract_an_action_sequence(vec![action_sequence]);
2256                    if let Err(e) =
2257                        self.dispatch_actions(actions_by_tick, self.browsing_context_id()?)
2258                    {
2259                        error!("handle_element_send_keys: dispatch_actions failed: {:?}", e);
2260                    }
2261                },
2262                DispatchStringEvent::Composition(event) => {
2263                    self.send_blocking_input_event_to_embedder(InputEvent::Ime(
2264                        ImeEvent::Composition(event),
2265                    ));
2266                },
2267            }
2268        }
2269
2270        // Step 14. Remove an input source with input state and input id.
2271        // It is possible that we only dispatched keydown.
2272        // In that case, we cannot remove the id from input state table.
2273        // This is a bug in spec: https://github.com/servo/servo/issues/37579#issuecomment-2990762713
2274        if self
2275            .session()?
2276            .input_cancel_list
2277            .iter()
2278            .all(|(cancel_item_id, _)| &id != cancel_item_id)
2279        {
2280            self.session_mut()?.input_state_table.remove(&id);
2281        }
2282
2283        Ok(WebDriverResponse::Void)
2284    }
2285
2286    /// <https://w3c.github.io/webdriver/#element-clear>
2287    fn handle_element_clear(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2288        // Step 1. If session's current browsing context is no longer open,
2289        // return ErrorStatus::NoSuchWindow.
2290        self.verify_browsing_context_is_open(self.browsing_context_id()?)?;
2291
2292        // Step 2. Try to handle any user prompt.
2293        self.handle_any_user_prompts(self.webview_id()?)?;
2294
2295        // Step 3-11 handled in script thread.
2296        let (sender, receiver) = generic_channel::channel().unwrap();
2297        let cmd = WebDriverScriptCommand::ElementClear(element.to_string(), sender);
2298        self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2299
2300        wait_for_ipc_response_flatten(receiver)?;
2301        Ok(WebDriverResponse::Void)
2302    }
2303
2304    /// <https://w3c.github.io/webdriver/#element-click>
2305    fn handle_element_click(&mut self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
2306        // Step 1. If session's current browsing context is no longer open,
2307        // return error with error code no such window.
2308        let browsing_context_id = self.browsing_context_id()?;
2309        self.verify_browsing_context_is_open(browsing_context_id)?;
2310
2311        // Step 2. Handle any user prompts.
2312        self.handle_any_user_prompts(self.webview_id()?)?;
2313
2314        let (sender, receiver) = generic_channel::channel().unwrap();
2315
2316        // Steps 3-7 + Step 8 for <option> are handled in script thread.
2317        let cmd = WebDriverScriptCommand::ElementClick(element.to_string(), sender);
2318        self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?;
2319
2320        match wait_for_ipc_response_flatten(receiver)? {
2321            Some(element_id) => {
2322                // Load status sender should be set up before we dispatch actions
2323                // to ensure webdriver can capture any navigation events.
2324                self.add_load_status_sender()?;
2325
2326                self.perform_element_click(element_id)?;
2327
2328                // Step 11. Try to wait for navigation to complete with session.
2329                // Check if there is a navigation with script
2330                let res = self.wait_for_navigation()?;
2331
2332                // Clear the load status sender
2333                self.clear_load_status_sender()?;
2334
2335                Ok(res)
2336            },
2337            // Step 13
2338            None => Ok(WebDriverResponse::Void),
2339        }
2340    }
2341
2342    /// <https://w3c.github.io/webdriver/#element-click>
2343    /// Step 8 for elements other than <option>,
2344    fn perform_element_click(&mut self, element: String) -> WebDriverResult<WebDriverResponse> {
2345        // Step 8.1 - 8.4: Create UUID, create input source "pointer".
2346        let id = Uuid::new_v4().to_string();
2347
2348        let pointer_ids = self.session()?.pointer_ids();
2349        #[cfg(not(any(target_env = "ohos", target_os = "android")))]
2350        let pointer_type = PointerType::Mouse;
2351
2352        // There is currently no spec for touchscreen element click support,
2353        // which assumes mouse event.
2354        // There is an ongoing discussion in W3C:
2355        // <https://github.com/w3c/webdriver/issues/1925>
2356        #[cfg(any(target_env = "ohos", target_os = "android"))]
2357        let pointer_type = PointerType::Touch;
2358
2359        self.session_mut()?.input_state_table.insert(
2360            id.clone(),
2361            InputSourceState::Pointer(PointerInputState::new(pointer_type, pointer_ids, 0.0, 0.0)),
2362        );
2363
2364        // Step 8.7. Construct a pointer move action.
2365        // Step 8.8. Set a property x to 0 on pointer move action.
2366        // Step 8.9. Set a property y to 0 on pointer move action.
2367        // Step 8.10. Set a property origin to element on pointer move action.
2368        let pointer_move_action = PointerMoveAction {
2369            duration: None,
2370            origin: PointerOrigin::Element(WebElement(element)),
2371            x: 0.0,
2372            y: 0.0,
2373            ..Default::default()
2374        };
2375
2376        // Step 8.11. Construct pointer down action.
2377        // Step 8.12. Set a property button to 0 on pointer down action.
2378        let pointer_down_action = PointerDownAction {
2379            button: ELEMENT_CLICK_BUTTON,
2380            ..Default::default()
2381        };
2382
2383        // Step 8.13. Construct pointer up action.
2384        // Step 8.14. Set a property button to 0 on pointer up action.
2385        let pointer_up_action = PointerUpAction {
2386            button: ELEMENT_CLICK_BUTTON,
2387            ..Default::default()
2388        };
2389
2390        let action_sequence = ActionSequence {
2391            id: id.clone(),
2392            actions: ActionsType::Pointer {
2393                parameters: PointerActionParameters {
2394                    pointer_type: PointerType::Mouse,
2395                },
2396                actions: vec![
2397                    PointerActionItem::Pointer(PointerAction::Move(pointer_move_action)),
2398                    PointerActionItem::Pointer(PointerAction::Down(pointer_down_action)),
2399                    PointerActionItem::Pointer(PointerAction::Up(pointer_up_action)),
2400                ],
2401            },
2402        };
2403
2404        // Step 8.16. Dispatch a list of actions with session's current browsing context
2405        let actions_by_tick = self.extract_an_action_sequence(vec![action_sequence]);
2406        if let Err(e) = self.dispatch_actions(actions_by_tick, self.browsing_context_id()?) {
2407            error!("handle_element_click: dispatch_actions failed: {:?}", e);
2408        }
2409
2410        // Step 8.17 Remove an input source with input state and input id.
2411        self.session_mut()?.input_state_table.remove(&id);
2412
2413        Ok(WebDriverResponse::Void)
2414    }
2415
2416    fn take_screenshot(&self, rect: Option<Rect<f32, CSSPixel>>) -> WebDriverResult<String> {
2417        // Spec: Take screenshot after running the animation frame callbacks.
2418        let _ = self.handle_execute_async_script(JavascriptCommandParameters {
2419            script: "requestAnimationFrame(() => arguments[0]());".to_string(),
2420            args: None,
2421        });
2422        if rect.as_ref().is_some_and(Rect::is_empty) {
2423            return Err(WebDriverError::new(
2424                ErrorStatus::UnknownError,
2425                "The requested `rect` has zero width and/or height",
2426            ));
2427        }
2428
2429        let webview_id = self.webview_id()?;
2430        let (sender, receiver) = crossbeam_channel::unbounded();
2431        self.send_message_to_embedder(WebDriverCommandMsg::TakeScreenshot(
2432            webview_id, rect, sender,
2433        ))?;
2434
2435        let result = match receiver.recv_timeout(SCREENSHOT_TIMEOUT) {
2436            Ok(result) => Ok(result),
2437            Err(RecvTimeoutError::Timeout) => Err(WebDriverError::new(
2438                ErrorStatus::Timeout,
2439                "Timed out waiting to take screenshot. Test likely didn't finish.",
2440            )),
2441            Err(RecvTimeoutError::Disconnected) => Err(WebDriverError::new(
2442                ErrorStatus::UnknownError,
2443                "Could not take screenshot because channel disconnected.",
2444            )),
2445        }?;
2446
2447        let image = result.map_err(|error| {
2448            WebDriverError::new(
2449                ErrorStatus::UnknownError,
2450                format!("Failed to take screenshot: {error:?}"),
2451            )
2452        })?;
2453
2454        let mut png_data = Cursor::new(Vec::new());
2455        DynamicImage::ImageRgba8(image)
2456            .write_to(&mut png_data, ImageFormat::Png)
2457            .unwrap();
2458
2459        Ok(base64::engine::general_purpose::STANDARD.encode(png_data.get_ref()))
2460    }
2461
2462    fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> {
2463        // Step 1. If session's current top-level browsing context is no longer open,
2464        // return error with error code no such window.
2465        let webview_id = self.webview_id()?;
2466        self.verify_top_level_browsing_context_is_open(webview_id)?;
2467
2468        self.handle_any_user_prompts(webview_id)?;
2469
2470        // Step 2
2471        let encoded = self.take_screenshot(None)?;
2472
2473        Ok(WebDriverResponse::Generic(ValueResponse(
2474            serde_json::to_value(encoded)?,
2475        )))
2476    }
2477
2478    fn handle_take_element_screenshot(
2479        &self,
2480        element: &WebElement,
2481    ) -> WebDriverResult<WebDriverResponse> {
2482        // Step 1. If session's current top-level browsing context is no longer open,
2483        // return error with error code no such window.
2484        let webview_id = self.webview_id()?;
2485        self.verify_top_level_browsing_context_is_open(webview_id)?;
2486
2487        // Step 2. Try to handle any user prompts with session.
2488        self.handle_any_user_prompts(webview_id)?;
2489
2490        // Step 3. Trying to get element.
2491        // Step 4. Scroll into view into element.
2492        let (sender, receiver) = generic_channel::channel().unwrap();
2493        let cmd =
2494            WebDriverScriptCommand::ScrollAndGetBoundingClientRect(element.to_string(), sender);
2495        self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?;
2496
2497        let rect = wait_for_ipc_response_flatten(receiver)?;
2498
2499        // Step 5
2500        let encoded = self.take_screenshot(Some(Rect::from_untyped(&rect)))?;
2501
2502        // Step 6. return success with data encoded string.
2503        Ok(WebDriverResponse::Generic(ValueResponse(
2504            serde_json::to_value(encoded)?,
2505        )))
2506    }
2507
2508    /// <https://html.spec.whatwg.org/multipage/#set-rph-registration-mode>
2509    fn handle_custom_handlers_set_mode(
2510        &self,
2511        parameters: &CustomHandlersSetModeParameters,
2512    ) -> WebDriverResult<WebDriverResponse> {
2513        // Step 2. Let mode be the result of getting a property named "mode" from parameters.
2514        // Step 3. If mode is not "autoAccept", "autoReject", or "none", return a WebDriver error with WebDriver error code invalid argument.
2515        let mode = match parameters.mode.as_str() {
2516            "autoAccept" => CustomHandlersAutomationMode::AutoAccept,
2517            "autoReject" => CustomHandlersAutomationMode::AutoReject,
2518            "none" => CustomHandlersAutomationMode::None,
2519            _ => {
2520                return Err(WebDriverError::new(
2521                    ErrorStatus::InvalidArgument,
2522                    "invalid argument",
2523                ));
2524            },
2525        };
2526        // Step 4. Let document be the current browsing context's active document.
2527        // Step 5. Set document's registerProtocolHandler() automation mode to mode.
2528        self.top_level_script_command(
2529            WebDriverScriptCommand::SetProtocolHandlerAutomationMode(mode),
2530            VerifyBrowsingContextIsOpen::Yes,
2531        )?;
2532        // Step 6. Return success with data null.
2533        Ok(WebDriverResponse::Void)
2534    }
2535
2536    fn handle_get_prefs(
2537        &self,
2538        parameters: &GetPrefsParameters,
2539    ) -> WebDriverResult<WebDriverResponse> {
2540        let prefs = parameters
2541            .prefs
2542            .iter()
2543            .map(|item| {
2544                (
2545                    item.clone(),
2546                    serde_json::to_value(prefs::get().get_value(item)).unwrap(),
2547                )
2548            })
2549            .collect::<BTreeMap<_, _>>();
2550
2551        Ok(WebDriverResponse::Generic(ValueResponse(
2552            serde_json::to_value(prefs)?,
2553        )))
2554    }
2555
2556    fn handle_set_prefs(
2557        &self,
2558        parameters: &SetPrefsParameters,
2559    ) -> WebDriverResult<WebDriverResponse> {
2560        let mut current_preferences = prefs::get().clone();
2561        for (key, value) in parameters.prefs.iter() {
2562            current_preferences.set_value(key, value.0.clone());
2563        }
2564        prefs::set(current_preferences);
2565
2566        Ok(WebDriverResponse::Void)
2567    }
2568
2569    fn handle_reset_prefs(
2570        &self,
2571        parameters: &GetPrefsParameters,
2572    ) -> WebDriverResult<WebDriverResponse> {
2573        let (new_preferences, map) = if parameters.prefs.is_empty() {
2574            (self.default_preferences.clone(), BTreeMap::new())
2575        } else {
2576            // If we only want to reset some of the preferences.
2577            let mut new_preferences = prefs::get().clone();
2578            for key in parameters.prefs.iter() {
2579                new_preferences.set_value(key, self.default_preferences.get_value(key))
2580            }
2581
2582            let map = parameters
2583                .prefs
2584                .iter()
2585                .map(|item| (item.clone(), new_preferences.get_value(item)))
2586                .collect::<BTreeMap<_, _>>();
2587
2588            (new_preferences, map)
2589        };
2590
2591        prefs::set(new_preferences);
2592
2593        Ok(WebDriverResponse::Generic(ValueResponse(
2594            serde_json::to_value(map)?,
2595        )))
2596    }
2597
2598    fn handle_shutdown(&self) -> WebDriverResult<WebDriverResponse> {
2599        self.send_message_to_embedder(WebDriverCommandMsg::Shutdown)?;
2600        Ok(WebDriverResponse::Void)
2601    }
2602
2603    fn handle_reset_all_cookies(&self) -> WebDriverResult<WebDriverResponse> {
2604        let (sender, receiver) = unbounded();
2605        self.send_message_to_embedder(WebDriverCommandMsg::ResetAllCookies(sender))?;
2606        if receiver.recv().is_err() {
2607            log::warn!("Communication failure while clearing cookies; status unknown");
2608        }
2609        Ok(WebDriverResponse::Void)
2610    }
2611
2612    fn verify_top_level_browsing_context_is_open(
2613        &self,
2614        webview_id: WebViewId,
2615    ) -> Result<(), WebDriverError> {
2616        let (sender, receiver) = generic_channel::oneshot().unwrap();
2617        self.send_message_to_embedder(WebDriverCommandMsg::IsWebViewOpen(webview_id, sender))?;
2618        if wait_for_oneshot_response(receiver)? {
2619            Ok(())
2620        } else {
2621            Err(WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2622        }
2623    }
2624
2625    fn verify_browsing_context_is_open(
2626        &self,
2627        browsing_context_id: BrowsingContextId,
2628    ) -> Result<(), WebDriverError> {
2629        let (sender, receiver) = generic_channel::oneshot().unwrap();
2630        self.send_message_to_embedder(WebDriverCommandMsg::IsBrowsingContextOpen(
2631            browsing_context_id,
2632            sender,
2633        ))?;
2634        if !receiver.recv().unwrap_or(false) {
2635            Err(WebDriverError::new(
2636                ErrorStatus::NoSuchWindow,
2637                "No such window",
2638            ))
2639        } else {
2640            Ok(())
2641        }
2642    }
2643
2644    fn wait_until_browsing_context_is_open(
2645        &self,
2646        browsing_context_id: BrowsingContextId,
2647    ) -> WebDriverResult<()> {
2648        // We cannot use provided timeout from configuration, as `fn wait_until_browsing_context_is_open` is not a standard step in spec.
2649        // Some tests use `page_load = 0` deliberately, which would always fail with the function.
2650        const OPEN_BROWSING_CONTEXT_TIMEOUT: Duration =
2651            Duration::from_millis(DEFAULT_PAGE_LOAD_TIMEOUT);
2652        let now = Instant::now();
2653        let timeouts = self.session()?.session_timeouts();
2654
2655        let sleep_interval = Duration::from_millis(timeouts.sleep_interval);
2656
2657        while now.elapsed() < OPEN_BROWSING_CONTEXT_TIMEOUT {
2658            if self
2659                .verify_browsing_context_is_open(browsing_context_id)
2660                .is_ok()
2661            {
2662                return Ok(());
2663            }
2664
2665            sleep(sleep_interval);
2666        }
2667        Err(WebDriverError::new(
2668            ErrorStatus::Timeout,
2669            format!(
2670                "Timed out waiting for the top-level browsing context {browsing_context_id} to be ready"
2671            ),
2672        ))
2673    }
2674
2675    fn focus_webview(&self, webview_id: WebViewId) -> WebDriverResult<()> {
2676        self.send_message_to_embedder(WebDriverCommandMsg::FocusWebView(webview_id))
2677    }
2678
2679    fn focus_browsing_context(&self, browsing_cotext_id: BrowsingContextId) -> WebDriverResult<()> {
2680        self.send_message_to_embedder(WebDriverCommandMsg::FocusBrowsingContext(
2681            browsing_cotext_id,
2682        ))
2683    }
2684}
2685
2686impl WebDriverHandler<ServoExtensionRoute> for Handler {
2687    fn handle_command(
2688        &mut self,
2689        _session: &Option<Session>,
2690        msg: WebDriverMessage<ServoExtensionRoute>,
2691    ) -> WebDriverResult<WebDriverResponse> {
2692        info!("{:?}", msg.command);
2693
2694        // Drain the load status receiver to avoid incorrect status handling
2695        while self.load_status_receiver.try_recv().is_ok() {}
2696
2697        // Unless we are trying to create/delete a new session, check status, or shutdown Servo,
2698        // we need to ensure that a session has previously been created.
2699        match msg.command {
2700            WebDriverCommand::NewSession(_) |
2701            WebDriverCommand::Status |
2702            WebDriverCommand::DeleteSession |
2703            WebDriverCommand::Extension(ServoExtensionCommand::Shutdown) |
2704            WebDriverCommand::Extension(ServoExtensionCommand::ResetAllCookies) => {},
2705            _ => {
2706                self.session()?;
2707            },
2708        }
2709
2710        match msg.command {
2711            WebDriverCommand::NewSession(ref parameters) => self.handle_new_session(parameters),
2712            WebDriverCommand::DeleteSession => self.handle_delete_session(),
2713            WebDriverCommand::Status => self.handle_status(),
2714            WebDriverCommand::AddCookie(ref parameters) => self.handle_add_cookie(parameters),
2715            WebDriverCommand::Get(ref parameters) => self.handle_get(parameters),
2716            WebDriverCommand::GetCurrentUrl => self.handle_current_url(),
2717            WebDriverCommand::GetWindowRect => {
2718                self.handle_window_rect(VerifyBrowsingContextIsOpen::Yes)
2719            },
2720            WebDriverCommand::SetWindowRect(ref size) => self.handle_set_window_rect(size),
2721            WebDriverCommand::IsEnabled(ref element) => self.handle_is_enabled(element),
2722            WebDriverCommand::IsSelected(ref element) => self.handle_is_selected(element),
2723            WebDriverCommand::GoBack => self.handle_go_back(),
2724            WebDriverCommand::GoForward => self.handle_go_forward(),
2725            WebDriverCommand::Refresh => self.handle_refresh(),
2726            WebDriverCommand::GetTitle => self.handle_title(),
2727            WebDriverCommand::GetWindowHandle => self.handle_window_handle(),
2728            WebDriverCommand::GetWindowHandles => self.handle_window_handles(),
2729            WebDriverCommand::NewWindow(ref parameters) => self.handle_new_window(parameters),
2730            WebDriverCommand::CloseWindow => self.handle_close_window(),
2731            WebDriverCommand::MaximizeWindow => self.handle_maximize_window(),
2732            WebDriverCommand::SwitchToFrame(ref parameters) => {
2733                self.handle_switch_to_frame(parameters)
2734            },
2735            WebDriverCommand::SwitchToParentFrame => self.handle_switch_to_parent_frame(),
2736            WebDriverCommand::SwitchToWindow(ref parameters) => {
2737                self.handle_switch_to_window(parameters)
2738            },
2739            WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
2740            WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
2741            WebDriverCommand::FindElementElement(ref element, ref parameters) => {
2742                self.handle_find_element_from_element(element, parameters)
2743            },
2744            WebDriverCommand::FindElementElements(ref element, ref parameters) => {
2745                self.handle_find_elements_from_element(element, parameters)
2746            },
2747            WebDriverCommand::FindShadowRootElements(ref shadow_root, ref parameters) => {
2748                self.handle_find_elements_from_shadow_root(shadow_root, parameters)
2749            },
2750            WebDriverCommand::FindShadowRootElement(ref shadow_root, ref parameters) => {
2751                self.handle_find_element_from_shadow_root(shadow_root, parameters)
2752            },
2753            WebDriverCommand::GetShadowRoot(element) => self.handle_get_shadow_root(element),
2754            WebDriverCommand::GetNamedCookie(name) => self.handle_get_cookie(name),
2755            WebDriverCommand::GetCookies => self.handle_get_cookies(),
2756            WebDriverCommand::GetActiveElement => self.handle_active_element(),
2757            WebDriverCommand::GetComputedRole(ref element) => self.handle_computed_role(element),
2758            WebDriverCommand::GetElementRect(ref element) => self.handle_element_rect(element),
2759            WebDriverCommand::GetElementText(ref element) => self.handle_element_text(element),
2760            WebDriverCommand::GetElementTagName(ref element) => {
2761                self.handle_element_tag_name(element)
2762            },
2763            WebDriverCommand::GetElementAttribute(ref element, ref name) => {
2764                self.handle_element_attribute(element, name)
2765            },
2766            WebDriverCommand::GetElementProperty(ref element, ref name) => {
2767                self.handle_element_property(element, name)
2768            },
2769            WebDriverCommand::GetCSSValue(ref element, ref name) => {
2770                self.handle_element_css(element, name)
2771            },
2772            WebDriverCommand::GetPageSource => self.handle_get_page_source(),
2773            WebDriverCommand::PerformActions(actions_parameters) => {
2774                self.handle_perform_actions(actions_parameters)
2775            },
2776            WebDriverCommand::ReleaseActions => self.handle_release_actions(),
2777            WebDriverCommand::ExecuteScript(x) => self.handle_execute_script(x),
2778            WebDriverCommand::ExecuteAsyncScript(x) => self.handle_execute_async_script(x),
2779            WebDriverCommand::ElementSendKeys(ref element, ref keys) => {
2780                self.handle_element_send_keys(element, keys)
2781            },
2782            WebDriverCommand::ElementClear(ref element) => self.handle_element_clear(element),
2783            WebDriverCommand::ElementClick(ref element) => self.handle_element_click(element),
2784            WebDriverCommand::DismissAlert => self.handle_dismiss_alert(),
2785            WebDriverCommand::AcceptAlert => self.handle_accept_alert(),
2786            WebDriverCommand::GetAlertText => self.handle_get_alert_text(),
2787            WebDriverCommand::SendAlertText(text) => self.handle_send_alert_text(text.text),
2788            WebDriverCommand::DeleteCookies => self.handle_delete_cookies(),
2789            WebDriverCommand::DeleteCookie(name) => self.handle_delete_cookie(name),
2790            WebDriverCommand::GetTimeouts => self.handle_get_timeouts(),
2791            WebDriverCommand::SetTimeouts(ref x) => self.handle_set_timeouts(x),
2792            WebDriverCommand::TakeScreenshot => self.handle_take_screenshot(),
2793            WebDriverCommand::TakeElementScreenshot(ref x) => {
2794                self.handle_take_element_screenshot(x)
2795            },
2796            WebDriverCommand::Extension(extension) => match extension {
2797                ServoExtensionCommand::GetPrefs(ref x) => self.handle_get_prefs(x),
2798                ServoExtensionCommand::SetPrefs(ref x) => self.handle_set_prefs(x),
2799                ServoExtensionCommand::ResetPrefs(ref x) => self.handle_reset_prefs(x),
2800                ServoExtensionCommand::CustomHandlersSetMode(ref x) => {
2801                    self.handle_custom_handlers_set_mode(x)
2802                },
2803                ServoExtensionCommand::Shutdown => self.handle_shutdown(),
2804                ServoExtensionCommand::ResetAllCookies => self.handle_reset_all_cookies(),
2805            },
2806            _ => Err(WebDriverError::new(
2807                ErrorStatus::UnsupportedOperation,
2808                format!("Command not implemented: {:?}", msg.command),
2809            )),
2810        }
2811    }
2812
2813    fn teardown_session(&mut self, _session: SessionTeardownKind) {
2814        self.session = None;
2815    }
2816}
2817
2818fn wait_for_oneshot_response<T>(
2819    receiver: generic_channel::GenericOneshotReceiver<T>,
2820) -> Result<T, WebDriverError>
2821where
2822    T: for<'de> Deserialize<'de> + Serialize,
2823{
2824    receiver
2825        .recv()
2826        .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2827}
2828
2829fn wait_for_ipc_response<T>(receiver: GenericReceiver<T>) -> Result<T, WebDriverError>
2830where
2831    T: for<'de> Deserialize<'de> + Serialize,
2832{
2833    receiver
2834        .recv()
2835        .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
2836}
2837
2838/// This function is like `wait_for_ipc_response`, but works on a channel that
2839/// returns a `Result<T, ErrorStatus>`, mapping all errors into `WebDriverError`.
2840fn wait_for_ipc_response_flatten<T>(
2841    receiver: GenericReceiver<Result<T, ErrorStatus>>,
2842) -> Result<T, WebDriverError>
2843where
2844    T: for<'de> Deserialize<'de> + Serialize,
2845{
2846    match receiver.recv() {
2847        Ok(Ok(value)) => Ok(value),
2848        Ok(Err(error_status)) => Err(WebDriverError::new(error_status, "")),
2849        Err(_) => Err(WebDriverError::new(ErrorStatus::NoSuchWindow, "")),
2850    }
2851}
2852
2853fn wait_for_script_ipc_response_with_timeout<T>(
2854    receiver: GenericReceiver<T>,
2855    timeout: Duration,
2856) -> Result<T, WebDriverError>
2857where
2858    T: for<'de> Deserialize<'de> + Serialize,
2859{
2860    receiver
2861        .try_recv_timeout(timeout)
2862        .map_err(|error| match error {
2863            generic_channel::TryReceiveError::ReceiveError(_) => {
2864                WebDriverError::new(ErrorStatus::NoSuchWindow, "")
2865            },
2866            generic_channel::TryReceiveError::Empty => {
2867                WebDriverError::new(ErrorStatus::ScriptTimeout, "")
2868            },
2869        })
2870}
2871
2872fn unwrap_first_element_response(res: WebDriverResponse) -> WebDriverResult<WebDriverResponse> {
2873    if let WebDriverResponse::Generic(ValueResponse(values)) = res {
2874        let arr = values.as_array().unwrap();
2875        if let Some(first) = arr.first() {
2876            Ok(WebDriverResponse::Generic(ValueResponse(first.clone())))
2877        } else {
2878            Err(WebDriverError::new(ErrorStatus::NoSuchElement, ""))
2879        }
2880    } else {
2881        unreachable!()
2882    }
2883}
2884
2885fn convert_keyboard_event_to_string(event: &KeyboardEvent) -> String {
2886    let key = &event.key;
2887    let named_key = match key {
2888        Key::Character(s) => return s.to_string(),
2889        Key::Named(named_key) => named_key,
2890    };
2891
2892    match event.location {
2893        Location::Left | Location::Standard => match named_key {
2894            NamedKey::Unidentified => '\u{E000}'.to_string(),
2895            NamedKey::Cancel => '\u{E001}'.to_string(),
2896            NamedKey::Help => '\u{E002}'.to_string(),
2897            NamedKey::Backspace => '\u{E003}'.to_string(),
2898            NamedKey::Tab => '\u{E004}'.to_string(),
2899            NamedKey::Clear => '\u{E005}'.to_string(),
2900            NamedKey::Enter => match event.code {
2901                Code::NumpadEnter => '\u{E007}'.to_string(),
2902                _ => '\u{E006}'.to_string(),
2903            },
2904            NamedKey::Shift => '\u{E008}'.to_string(),
2905            NamedKey::Control => '\u{E009}'.to_string(),
2906            NamedKey::Alt => '\u{E00A}'.to_string(),
2907            NamedKey::Pause => '\u{E00B}'.to_string(),
2908            NamedKey::Escape => '\u{E00C}'.to_string(),
2909            NamedKey::PageUp => '\u{E00E}'.to_string(),
2910            NamedKey::PageDown => '\u{E00F}'.to_string(),
2911            NamedKey::End => '\u{E010}'.to_string(),
2912            NamedKey::Home => '\u{E011}'.to_string(),
2913            NamedKey::ArrowLeft => '\u{E012}'.to_string(),
2914            NamedKey::ArrowUp => '\u{E013}'.to_string(),
2915            NamedKey::ArrowRight => '\u{E014}'.to_string(),
2916            NamedKey::ArrowDown => '\u{E015}'.to_string(),
2917            NamedKey::Insert => '\u{E016}'.to_string(),
2918            NamedKey::Delete => '\u{E017}'.to_string(),
2919            NamedKey::F1 => '\u{E031}'.to_string(),
2920            NamedKey::F2 => '\u{E032}'.to_string(),
2921            NamedKey::F3 => '\u{E033}'.to_string(),
2922            NamedKey::F4 => '\u{E034}'.to_string(),
2923            NamedKey::F5 => '\u{E035}'.to_string(),
2924            NamedKey::F6 => '\u{E036}'.to_string(),
2925            NamedKey::F7 => '\u{E037}'.to_string(),
2926            NamedKey::F8 => '\u{E038}'.to_string(),
2927            NamedKey::F9 => '\u{E039}'.to_string(),
2928            NamedKey::F10 => '\u{E03A}'.to_string(),
2929            NamedKey::F11 => '\u{E03B}'.to_string(),
2930            NamedKey::F12 => '\u{E03C}'.to_string(),
2931            NamedKey::Meta => '\u{E03D}'.to_string(),
2932            NamedKey::ZenkakuHankaku => '\u{E040}'.to_string(),
2933            _ => {
2934                error!("Unexpected NamedKey on send_keys");
2935                '\u{E000}'.to_string()
2936            },
2937        },
2938        Location::Right | Location::Numpad => match named_key {
2939            NamedKey::Shift => '\u{E050}'.to_string(),
2940            NamedKey::Control => '\u{E051}'.to_string(),
2941            NamedKey::Alt => '\u{E052}'.to_string(),
2942            NamedKey::Meta => '\u{E053}'.to_string(),
2943            NamedKey::PageUp => '\u{E054}'.to_string(),
2944            NamedKey::PageDown => '\u{E055}'.to_string(),
2945            NamedKey::End => '\u{E056}'.to_string(),
2946            NamedKey::Home => '\u{E057}'.to_string(),
2947            NamedKey::ArrowLeft => '\u{E058}'.to_string(),
2948            NamedKey::ArrowUp => '\u{E059}'.to_string(),
2949            NamedKey::ArrowRight => '\u{E05A}'.to_string(),
2950            NamedKey::ArrowDown => '\u{E05B}'.to_string(),
2951            NamedKey::Insert => '\u{E05C}'.to_string(),
2952            NamedKey::Delete => '\u{E05D}'.to_string(),
2953            _ => {
2954                error!("Unexpected NamedKey on send_keys");
2955                '\u{E000}'.to_string()
2956            },
2957        },
2958    }
2959}