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