tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

command.rs (56403B)


      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 http://mozilla.org/MPL/2.0/. */
      4 
      5 use crate::actions::ActionSequence;
      6 use crate::capabilities::{
      7    BrowserCapabilities, Capabilities, CapabilitiesMatching, SpecNewSessionParameters,
      8 };
      9 use crate::common::{
     10    CredentialParameters, Date, FrameId, LocatorStrategy, ShadowRoot, WebElement, MAX_SAFE_INTEGER,
     11 };
     12 use crate::error::{ErrorStatus, WebDriverError, WebDriverResult};
     13 use crate::httpapi::{Route, VoidWebDriverExtensionRoute, WebDriverExtensionRoute};
     14 use crate::Parameters;
     15 use serde::de::{self, Deserialize, Deserializer};
     16 use serde_json::Value;
     17 
     18 #[derive(Debug, PartialEq)]
     19 pub enum WebDriverCommand<T: WebDriverExtensionCommand> {
     20    NewSession(NewSessionParameters),
     21    DeleteSession,
     22    Get(GetParameters),
     23    GetCurrentUrl,
     24    GoBack,
     25    GoForward,
     26    Refresh,
     27    GetTitle,
     28    GetPageSource,
     29    GetWindowHandle,
     30    GetWindowHandles,
     31    NewWindow(NewWindowParameters),
     32    CloseWindow,
     33    GetWindowRect,
     34    SetWindowRect(WindowRectParameters),
     35    MinimizeWindow,
     36    MaximizeWindow,
     37    FullscreenWindow,
     38    SwitchToWindow(SwitchToWindowParameters),
     39    SwitchToFrame(SwitchToFrameParameters),
     40    SwitchToParentFrame,
     41    FindElement(LocatorParameters),
     42    FindElements(LocatorParameters),
     43    FindElementElement(WebElement, LocatorParameters),
     44    FindElementElements(WebElement, LocatorParameters),
     45    FindShadowRootElement(ShadowRoot, LocatorParameters),
     46    FindShadowRootElements(ShadowRoot, LocatorParameters),
     47    GetActiveElement,
     48    GetComputedLabel(WebElement),
     49    GetComputedRole(WebElement),
     50    GetShadowRoot(WebElement),
     51    IsDisplayed(WebElement),
     52    IsSelected(WebElement),
     53    GetElementAttribute(WebElement, String),
     54    GetElementProperty(WebElement, String),
     55    GetCSSValue(WebElement, String),
     56    GetElementText(WebElement),
     57    GetElementTagName(WebElement),
     58    GetElementRect(WebElement),
     59    IsEnabled(WebElement),
     60    ExecuteScript(JavascriptCommandParameters),
     61    ExecuteAsyncScript(JavascriptCommandParameters),
     62    GetCookies,
     63    GetNamedCookie(String),
     64    AddCookie(AddCookieParameters),
     65    DeleteCookies,
     66    DeleteCookie(String),
     67    GetTimeouts,
     68    SetTimeouts(TimeoutsParameters),
     69    ElementClick(WebElement),
     70    ElementClear(WebElement),
     71    ElementSendKeys(WebElement, SendKeysParameters),
     72    PerformActions(ActionsParameters),
     73    ReleaseActions,
     74    DismissAlert,
     75    AcceptAlert,
     76    GetAlertText,
     77    SendAlertText(SendKeysParameters),
     78    TakeScreenshot,
     79    TakeElementScreenshot(WebElement),
     80    Print(PrintParameters),
     81    SetPermission(SetPermissionParameters),
     82    Status,
     83    Extension(T),
     84    GPCSetGlobalPrivacyControl(GlobalPrivacyControlParameters),
     85    GPCGetGlobalPrivacyControl,
     86    WebAuthnAddVirtualAuthenticator(AuthenticatorParameters),
     87    WebAuthnRemoveVirtualAuthenticator,
     88    WebAuthnAddCredential(CredentialParameters),
     89    WebAuthnGetCredentials,
     90    WebAuthnRemoveCredential,
     91    WebAuthnRemoveAllCredentials,
     92    WebAuthnSetUserVerified(UserVerificationParameters),
     93 }
     94 
     95 pub trait WebDriverExtensionCommand: Clone + Send {
     96    fn parameters_json(&self) -> Option<Value>;
     97 }
     98 
     99 #[derive(Clone, Debug)]
    100 pub struct VoidWebDriverExtensionCommand;
    101 
    102 impl WebDriverExtensionCommand for VoidWebDriverExtensionCommand {
    103    fn parameters_json(&self) -> Option<Value> {
    104        panic!("No extensions implemented");
    105    }
    106 }
    107 
    108 #[derive(Debug, PartialEq)]
    109 pub struct WebDriverMessage<U: WebDriverExtensionRoute = VoidWebDriverExtensionRoute> {
    110    pub session_id: Option<String>,
    111    pub command: WebDriverCommand<U::Command>,
    112 }
    113 
    114 impl<U: WebDriverExtensionRoute> WebDriverMessage<U> {
    115    pub fn new(
    116        session_id: Option<String>,
    117        command: WebDriverCommand<U::Command>,
    118    ) -> WebDriverMessage<U> {
    119        WebDriverMessage {
    120            session_id,
    121            command,
    122        }
    123    }
    124 
    125    pub fn from_http(
    126        match_type: Route<U>,
    127        params: &Parameters,
    128        raw_body: &str,
    129        requires_body: bool,
    130    ) -> WebDriverResult<WebDriverMessage<U>> {
    131        let session_id = WebDriverMessage::<U>::get_session_id(params);
    132        let body_data = WebDriverMessage::<U>::decode_body(raw_body, requires_body)?;
    133        let command = match match_type {
    134            Route::NewSession => WebDriverCommand::NewSession(serde_json::from_str(raw_body)?),
    135            Route::DeleteSession => WebDriverCommand::DeleteSession,
    136            Route::Get => WebDriverCommand::Get(serde_json::from_str(raw_body)?),
    137            Route::GetCurrentUrl => WebDriverCommand::GetCurrentUrl,
    138            Route::GoBack => WebDriverCommand::GoBack,
    139            Route::GoForward => WebDriverCommand::GoForward,
    140            Route::Refresh => WebDriverCommand::Refresh,
    141            Route::GetTitle => WebDriverCommand::GetTitle,
    142            Route::GetPageSource => WebDriverCommand::GetPageSource,
    143            Route::GetWindowHandle => WebDriverCommand::GetWindowHandle,
    144            Route::GetWindowHandles => WebDriverCommand::GetWindowHandles,
    145            Route::NewWindow => WebDriverCommand::NewWindow(serde_json::from_str(raw_body)?),
    146            Route::CloseWindow => WebDriverCommand::CloseWindow,
    147            Route::GetTimeouts => WebDriverCommand::GetTimeouts,
    148            Route::SetTimeouts => WebDriverCommand::SetTimeouts(serde_json::from_str(raw_body)?),
    149            Route::GetWindowRect | Route::GetWindowPosition | Route::GetWindowSize => {
    150                WebDriverCommand::GetWindowRect
    151            }
    152            Route::SetWindowRect | Route::SetWindowPosition | Route::SetWindowSize => {
    153                WebDriverCommand::SetWindowRect(serde_json::from_str(raw_body)?)
    154            }
    155            Route::MinimizeWindow => WebDriverCommand::MinimizeWindow,
    156            Route::MaximizeWindow => WebDriverCommand::MaximizeWindow,
    157            Route::FullscreenWindow => WebDriverCommand::FullscreenWindow,
    158            Route::SwitchToWindow => {
    159                WebDriverCommand::SwitchToWindow(serde_json::from_str(raw_body)?)
    160            }
    161            Route::SwitchToFrame => {
    162                WebDriverCommand::SwitchToFrame(serde_json::from_str(raw_body)?)
    163            }
    164            Route::SwitchToParentFrame => WebDriverCommand::SwitchToParentFrame,
    165            Route::FindElement => WebDriverCommand::FindElement(serde_json::from_str(raw_body)?),
    166            Route::FindElements => WebDriverCommand::FindElements(serde_json::from_str(raw_body)?),
    167            Route::FindElementElement => {
    168                let element_id = try_opt!(
    169                    params.get("elementId"),
    170                    ErrorStatus::InvalidArgument,
    171                    "Missing elementId parameter"
    172                );
    173                let element = WebElement(element_id.as_str().into());
    174                WebDriverCommand::FindElementElement(element, serde_json::from_str(raw_body)?)
    175            }
    176            Route::FindElementElements => {
    177                let element_id = try_opt!(
    178                    params.get("elementId"),
    179                    ErrorStatus::InvalidArgument,
    180                    "Missing elementId parameter"
    181                );
    182                let element = WebElement(element_id.as_str().into());
    183                WebDriverCommand::FindElementElements(element, serde_json::from_str(raw_body)?)
    184            }
    185            Route::FindShadowRootElement => {
    186                let shadow_id = try_opt!(
    187                    params.get("shadowId"),
    188                    ErrorStatus::InvalidArgument,
    189                    "Missing shadowId parameter"
    190                );
    191                let shadow_root = ShadowRoot(shadow_id.as_str().into());
    192                WebDriverCommand::FindShadowRootElement(
    193                    shadow_root,
    194                    serde_json::from_str(raw_body)?,
    195                )
    196            }
    197            Route::FindShadowRootElements => {
    198                let shadow_id = try_opt!(
    199                    params.get("shadowId"),
    200                    ErrorStatus::InvalidArgument,
    201                    "Missing shadowId parameter"
    202                );
    203                let shadow_root = ShadowRoot(shadow_id.as_str().into());
    204                WebDriverCommand::FindShadowRootElements(
    205                    shadow_root,
    206                    serde_json::from_str(raw_body)?,
    207                )
    208            }
    209            Route::GetActiveElement => WebDriverCommand::GetActiveElement,
    210            Route::GetShadowRoot => {
    211                let element_id = try_opt!(
    212                    params.get("elementId"),
    213                    ErrorStatus::InvalidArgument,
    214                    "Missing elementId parameter"
    215                );
    216                let element = WebElement(element_id.as_str().into());
    217                WebDriverCommand::GetShadowRoot(element)
    218            }
    219            Route::GetComputedLabel => {
    220                let element_id = try_opt!(
    221                    params.get("elementId"),
    222                    ErrorStatus::InvalidArgument,
    223                    "Missing elementId parameter"
    224                );
    225                let element = WebElement(element_id.as_str().into());
    226                WebDriverCommand::GetComputedLabel(element)
    227            }
    228            Route::GetComputedRole => {
    229                let element_id = try_opt!(
    230                    params.get("elementId"),
    231                    ErrorStatus::InvalidArgument,
    232                    "Missing elementId parameter"
    233                );
    234                let element = WebElement(element_id.as_str().into());
    235                WebDriverCommand::GetComputedRole(element)
    236            }
    237            Route::IsDisplayed => {
    238                let element_id = try_opt!(
    239                    params.get("elementId"),
    240                    ErrorStatus::InvalidArgument,
    241                    "Missing elementId parameter"
    242                );
    243                let element = WebElement(element_id.as_str().into());
    244                WebDriverCommand::IsDisplayed(element)
    245            }
    246            Route::IsSelected => {
    247                let element_id = try_opt!(
    248                    params.get("elementId"),
    249                    ErrorStatus::InvalidArgument,
    250                    "Missing elementId parameter"
    251                );
    252                let element = WebElement(element_id.as_str().into());
    253                WebDriverCommand::IsSelected(element)
    254            }
    255            Route::GetElementAttribute => {
    256                let element_id = try_opt!(
    257                    params.get("elementId"),
    258                    ErrorStatus::InvalidArgument,
    259                    "Missing elementId parameter"
    260                );
    261                let element = WebElement(element_id.as_str().into());
    262                let attr = try_opt!(
    263                    params.get("name"),
    264                    ErrorStatus::InvalidArgument,
    265                    "Missing name parameter"
    266                )
    267                .as_str();
    268                WebDriverCommand::GetElementAttribute(element, attr.into())
    269            }
    270            Route::GetElementProperty => {
    271                let element_id = try_opt!(
    272                    params.get("elementId"),
    273                    ErrorStatus::InvalidArgument,
    274                    "Missing elementId parameter"
    275                );
    276                let element = WebElement(element_id.as_str().into());
    277                let property = try_opt!(
    278                    params.get("name"),
    279                    ErrorStatus::InvalidArgument,
    280                    "Missing name parameter"
    281                )
    282                .as_str();
    283                WebDriverCommand::GetElementProperty(element, property.into())
    284            }
    285            Route::GetCSSValue => {
    286                let element_id = try_opt!(
    287                    params.get("elementId"),
    288                    ErrorStatus::InvalidArgument,
    289                    "Missing elementId parameter"
    290                );
    291                let element = WebElement(element_id.as_str().into());
    292                let property = try_opt!(
    293                    params.get("propertyName"),
    294                    ErrorStatus::InvalidArgument,
    295                    "Missing propertyName parameter"
    296                )
    297                .as_str();
    298                WebDriverCommand::GetCSSValue(element, property.into())
    299            }
    300            Route::GetElementText => {
    301                let element_id = try_opt!(
    302                    params.get("elementId"),
    303                    ErrorStatus::InvalidArgument,
    304                    "Missing elementId parameter"
    305                );
    306                let element = WebElement(element_id.as_str().into());
    307                WebDriverCommand::GetElementText(element)
    308            }
    309            Route::GetElementTagName => {
    310                let element_id = try_opt!(
    311                    params.get("elementId"),
    312                    ErrorStatus::InvalidArgument,
    313                    "Missing elementId parameter"
    314                );
    315                let element = WebElement(element_id.as_str().into());
    316                WebDriverCommand::GetElementTagName(element)
    317            }
    318            Route::GetElementRect => {
    319                let element_id = try_opt!(
    320                    params.get("elementId"),
    321                    ErrorStatus::InvalidArgument,
    322                    "Missing elementId parameter"
    323                );
    324                let element = WebElement(element_id.as_str().into());
    325                WebDriverCommand::GetElementRect(element)
    326            }
    327            Route::IsEnabled => {
    328                let element_id = try_opt!(
    329                    params.get("elementId"),
    330                    ErrorStatus::InvalidArgument,
    331                    "Missing elementId parameter"
    332                );
    333                let element = WebElement(element_id.as_str().into());
    334                WebDriverCommand::IsEnabled(element)
    335            }
    336            Route::ElementClick => {
    337                let element_id = try_opt!(
    338                    params.get("elementId"),
    339                    ErrorStatus::InvalidArgument,
    340                    "Missing elementId parameter"
    341                );
    342                let element = WebElement(element_id.as_str().into());
    343                WebDriverCommand::ElementClick(element)
    344            }
    345            Route::ElementClear => {
    346                let element_id = try_opt!(
    347                    params.get("elementId"),
    348                    ErrorStatus::InvalidArgument,
    349                    "Missing elementId parameter"
    350                );
    351                let element = WebElement(element_id.as_str().into());
    352                WebDriverCommand::ElementClear(element)
    353            }
    354            Route::ElementSendKeys => {
    355                let element_id = try_opt!(
    356                    params.get("elementId"),
    357                    ErrorStatus::InvalidArgument,
    358                    "Missing elementId parameter"
    359                );
    360                let element = WebElement(element_id.as_str().into());
    361                WebDriverCommand::ElementSendKeys(element, serde_json::from_str(raw_body)?)
    362            }
    363            Route::ExecuteScript => {
    364                WebDriverCommand::ExecuteScript(serde_json::from_str(raw_body)?)
    365            }
    366            Route::ExecuteAsyncScript => {
    367                WebDriverCommand::ExecuteAsyncScript(serde_json::from_str(raw_body)?)
    368            }
    369            Route::GetCookies => WebDriverCommand::GetCookies,
    370            Route::GetNamedCookie => {
    371                let name = try_opt!(
    372                    params.get("name"),
    373                    ErrorStatus::InvalidArgument,
    374                    "Missing 'name' parameter"
    375                )
    376                .as_str()
    377                .into();
    378                WebDriverCommand::GetNamedCookie(name)
    379            }
    380            Route::AddCookie => WebDriverCommand::AddCookie(serde_json::from_str(raw_body)?),
    381            Route::DeleteCookies => WebDriverCommand::DeleteCookies,
    382            Route::DeleteCookie => {
    383                let name = try_opt!(
    384                    params.get("name"),
    385                    ErrorStatus::InvalidArgument,
    386                    "Missing name parameter"
    387                )
    388                .as_str()
    389                .into();
    390                WebDriverCommand::DeleteCookie(name)
    391            }
    392            Route::PerformActions => {
    393                WebDriverCommand::PerformActions(serde_json::from_str(raw_body)?)
    394            }
    395            Route::ReleaseActions => WebDriverCommand::ReleaseActions,
    396            Route::DismissAlert => WebDriverCommand::DismissAlert,
    397            Route::AcceptAlert => WebDriverCommand::AcceptAlert,
    398            Route::GetAlertText => WebDriverCommand::GetAlertText,
    399            Route::SendAlertText => {
    400                WebDriverCommand::SendAlertText(serde_json::from_str(raw_body)?)
    401            }
    402            Route::TakeScreenshot => WebDriverCommand::TakeScreenshot,
    403            Route::TakeElementScreenshot => {
    404                let element_id = try_opt!(
    405                    params.get("elementId"),
    406                    ErrorStatus::InvalidArgument,
    407                    "Missing elementId parameter"
    408                );
    409                let element = WebElement(element_id.as_str().into());
    410                WebDriverCommand::TakeElementScreenshot(element)
    411            }
    412            Route::Print => WebDriverCommand::Print(serde_json::from_str(raw_body)?),
    413            Route::SetPermission => {
    414                WebDriverCommand::SetPermission(serde_json::from_str(raw_body)?)
    415            }
    416            Route::Status => WebDriverCommand::Status,
    417            Route::Extension(ref extension) => extension.command(params, &body_data)?,
    418            Route::GPCGetGlobalPrivacyControl => WebDriverCommand::GPCGetGlobalPrivacyControl,
    419            Route::GPCSetGlobalPrivacyControl => {
    420                WebDriverCommand::GPCSetGlobalPrivacyControl(serde_json::from_str(raw_body)?)
    421            }
    422            Route::WebAuthnAddVirtualAuthenticator => {
    423                WebDriverCommand::WebAuthnAddVirtualAuthenticator(serde_json::from_str(raw_body)?)
    424            }
    425            Route::WebAuthnRemoveVirtualAuthenticator => {
    426                WebDriverCommand::WebAuthnRemoveVirtualAuthenticator
    427            }
    428            Route::WebAuthnAddCredential => {
    429                WebDriverCommand::WebAuthnAddCredential(serde_json::from_str(raw_body)?)
    430            }
    431            Route::WebAuthnGetCredentials => WebDriverCommand::WebAuthnGetCredentials,
    432            Route::WebAuthnRemoveCredential => WebDriverCommand::WebAuthnRemoveCredential,
    433            Route::WebAuthnRemoveAllCredentials => WebDriverCommand::WebAuthnRemoveAllCredentials,
    434            Route::WebAuthnSetUserVerified => {
    435                WebDriverCommand::WebAuthnSetUserVerified(serde_json::from_str(raw_body)?)
    436            }
    437        };
    438        Ok(WebDriverMessage::new(session_id, command))
    439    }
    440 
    441    fn get_session_id(params: &Parameters) -> Option<String> {
    442        params.get("sessionId").cloned()
    443    }
    444 
    445    fn decode_body(body: &str, requires_body: bool) -> WebDriverResult<Value> {
    446        if requires_body {
    447            match serde_json::from_str(body) {
    448                Ok(x @ Value::Object(_)) => Ok(x),
    449                Ok(_) => Err(WebDriverError::new(
    450                    ErrorStatus::InvalidArgument,
    451                    "Body was not a JSON Object",
    452                )),
    453                Err(e) => {
    454                    if e.is_io() {
    455                        Err(WebDriverError::new(
    456                            ErrorStatus::InvalidArgument,
    457                            format!("I/O error whilst decoding body: {}", e),
    458                        ))
    459                    } else {
    460                        let msg = format!("Failed to decode request as JSON: {}", body);
    461                        let stack = format!("Syntax error at :{}:{}", e.line(), e.column());
    462                        Err(WebDriverError::new_with_data(
    463                            ErrorStatus::InvalidArgument,
    464                            msg,
    465                            None,
    466                            Some(stack),
    467                        ))
    468                    }
    469                }
    470            }
    471        } else {
    472            Ok(Value::Null)
    473        }
    474    }
    475 }
    476 
    477 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    478 pub struct ActionsParameters {
    479    pub actions: Vec<ActionSequence>,
    480 }
    481 
    482 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    483 #[serde(remote = "Self")]
    484 pub struct AddCookieParameters {
    485    pub name: String,
    486    pub value: String,
    487    pub path: Option<String>,
    488    pub domain: Option<String>,
    489    #[serde(default)]
    490    pub secure: bool,
    491    #[serde(default)]
    492    pub httpOnly: bool,
    493    #[serde(skip_serializing_if = "Option::is_none")]
    494    pub expiry: Option<Date>,
    495    pub sameSite: Option<String>,
    496 }
    497 
    498 impl<'de> Deserialize<'de> for AddCookieParameters {
    499    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    500    where
    501        D: Deserializer<'de>,
    502    {
    503        #[derive(Deserialize)]
    504        struct Wrapper {
    505            #[serde(with = "AddCookieParameters")]
    506            cookie: AddCookieParameters,
    507        }
    508 
    509        Wrapper::deserialize(deserializer).map(|wrapper| wrapper.cookie)
    510    }
    511 }
    512 
    513 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    514 pub struct GetParameters {
    515    pub url: String,
    516 }
    517 
    518 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    519 pub struct GetNamedCookieParameters {
    520    pub name: Option<String>,
    521 }
    522 
    523 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    524 pub struct JavascriptCommandParameters {
    525    pub script: String,
    526    pub args: Option<Vec<Value>>,
    527 }
    528 
    529 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    530 pub struct LocatorParameters {
    531    pub using: LocatorStrategy,
    532    pub value: String,
    533 }
    534 
    535 #[derive(Debug, PartialEq, Serialize)]
    536 pub struct NewSessionParameters {
    537    pub capabilities: SpecNewSessionParameters,
    538 }
    539 
    540 // Manual deserialize implementation to error if capabilities is not an object
    541 // Without this the empty list test fails
    542 impl<'de> Deserialize<'de> for NewSessionParameters {
    543    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    544    where
    545        D: Deserializer<'de>,
    546    {
    547        let value = serde_json::Value::deserialize(deserializer)?;
    548        let caps = value
    549            .get("capabilities")
    550            .ok_or(de::Error::missing_field("capabilities"))?;
    551        if !caps.is_object() {
    552            return Err(de::Error::custom("capabilities must be objects"));
    553        }
    554        let capabilities =
    555            SpecNewSessionParameters::deserialize(caps).map_err(de::Error::custom)?;
    556        Ok(NewSessionParameters { capabilities })
    557    }
    558 }
    559 
    560 impl CapabilitiesMatching for NewSessionParameters {
    561    fn match_browser<T: BrowserCapabilities>(
    562        &self,
    563        browser_capabilities: &mut T,
    564    ) -> WebDriverResult<Option<Capabilities>> {
    565        self.capabilities.match_browser(browser_capabilities)
    566    }
    567 }
    568 
    569 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    570 pub struct NewWindowParameters {
    571    #[serde(rename = "type")]
    572    pub type_hint: Option<String>,
    573 }
    574 
    575 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    576 #[serde(untagged)]
    577 pub enum PrintPageRange {
    578    Integer(u64),
    579    Range(String),
    580 }
    581 
    582 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    583 #[serde(default, rename_all = "camelCase")]
    584 pub struct PrintParameters {
    585    pub orientation: PrintOrientation,
    586    #[serde(deserialize_with = "deserialize_to_print_scale_f64")]
    587    pub scale: f64,
    588    pub background: bool,
    589    pub page: PrintPage,
    590    pub margin: PrintMargins,
    591    pub page_ranges: Vec<PrintPageRange>,
    592    pub shrink_to_fit: bool,
    593 }
    594 
    595 impl Default for PrintParameters {
    596    fn default() -> Self {
    597        PrintParameters {
    598            orientation: PrintOrientation::default(),
    599            scale: 1.0,
    600            background: false,
    601            page: PrintPage::default(),
    602            margin: PrintMargins::default(),
    603            page_ranges: Vec::new(),
    604            shrink_to_fit: true,
    605        }
    606    }
    607 }
    608 
    609 #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
    610 #[serde(rename_all = "lowercase")]
    611 pub enum PrintOrientation {
    612    Landscape,
    613    #[default]
    614    Portrait,
    615 }
    616 
    617 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    618 #[serde(default)]
    619 pub struct PrintPage {
    620    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    621    pub width: f64,
    622    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    623    pub height: f64,
    624 }
    625 
    626 impl Default for PrintPage {
    627    fn default() -> Self {
    628        PrintPage {
    629            width: 21.59,
    630            height: 27.94,
    631        }
    632    }
    633 }
    634 
    635 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    636 #[serde(default)]
    637 pub struct PrintMargins {
    638    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    639    pub top: f64,
    640    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    641    pub bottom: f64,
    642    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    643    pub left: f64,
    644    #[serde(deserialize_with = "deserialize_to_positive_f64")]
    645    pub right: f64,
    646 }
    647 
    648 impl Default for PrintMargins {
    649    fn default() -> Self {
    650        PrintMargins {
    651            top: 1.0,
    652            bottom: 1.0,
    653            left: 1.0,
    654            right: 1.0,
    655        }
    656    }
    657 }
    658 
    659 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    660 pub struct SetPermissionParameters {
    661    pub descriptor: SetPermissionDescriptor,
    662    pub state: SetPermissionState,
    663 }
    664 
    665 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    666 pub struct SetPermissionDescriptor {
    667    pub name: String,
    668 }
    669 
    670 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    671 #[serde(rename_all = "lowercase")]
    672 pub enum SetPermissionState {
    673    Denied,
    674    Granted,
    675    Prompt,
    676 }
    677 
    678 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    679 pub enum WebAuthnProtocol {
    680    #[serde(rename = "ctap1/u2f")]
    681    Ctap1U2f,
    682    #[serde(rename = "ctap2")]
    683    Ctap2,
    684    #[serde(rename = "ctap2_1")]
    685    Ctap2_1,
    686 }
    687 
    688 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    689 #[serde(rename_all = "kebab-case")]
    690 pub enum AuthenticatorTransport {
    691    Usb,
    692    Nfc,
    693    Ble,
    694    SmartCard,
    695    Hybrid,
    696    Internal,
    697 }
    698 
    699 fn default_as_true() -> bool {
    700    true
    701 }
    702 
    703 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    704 #[serde(rename_all = "camelCase")]
    705 pub struct AuthenticatorParameters {
    706    pub protocol: WebAuthnProtocol,
    707    pub transport: AuthenticatorTransport,
    708    #[serde(default)]
    709    pub has_resident_key: bool,
    710    #[serde(default)]
    711    pub has_user_verification: bool,
    712    #[serde(default = "default_as_true")]
    713    pub is_user_consenting: bool,
    714    #[serde(default)]
    715    pub is_user_verified: bool,
    716 }
    717 
    718 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    719 pub struct UserVerificationParameters {
    720    #[serde(rename = "isUserVerified")]
    721    pub is_user_verified: bool,
    722 }
    723 
    724 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    725 pub struct GlobalPrivacyControlParameters {
    726    pub gpc: bool,
    727 }
    728 
    729 fn deserialize_to_positive_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
    730 where
    731    D: Deserializer<'de>,
    732 {
    733    let val = f64::deserialize(deserializer)?;
    734    if val < 0.0 {
    735        return Err(de::Error::custom(format!("{} is negative", val)));
    736    };
    737    Ok(val)
    738 }
    739 
    740 fn deserialize_to_print_scale_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
    741 where
    742    D: Deserializer<'de>,
    743 {
    744    let val = f64::deserialize(deserializer)?;
    745    if !(0.1..=2.0).contains(&val) {
    746        return Err(de::Error::custom(format!("{} is outside range 0.1-2", val)));
    747    };
    748    Ok(val)
    749 }
    750 
    751 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    752 pub struct SendKeysParameters {
    753    pub text: String,
    754 }
    755 
    756 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    757 pub struct SwitchToFrameParameters {
    758    pub id: FrameId,
    759 }
    760 
    761 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    762 pub struct SwitchToWindowParameters {
    763    pub handle: String,
    764 }
    765 
    766 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    767 pub struct TakeScreenshotParameters {
    768    pub element: Option<WebElement>,
    769 }
    770 
    771 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    772 pub struct TimeoutsParameters {
    773    #[serde(
    774        default,
    775        skip_serializing_if = "Option::is_none",
    776        deserialize_with = "deserialize_to_u64"
    777    )]
    778    pub implicit: Option<u64>,
    779    #[serde(
    780        default,
    781        rename = "pageLoad",
    782        skip_serializing_if = "Option::is_none",
    783        deserialize_with = "deserialize_to_u64"
    784    )]
    785    pub page_load: Option<u64>,
    786    #[serde(
    787        default,
    788        skip_serializing_if = "Option::is_none",
    789        deserialize_with = "deserialize_to_nullable_u64"
    790    )]
    791    #[allow(clippy::option_option)]
    792    pub script: Option<Option<u64>>,
    793 }
    794 
    795 #[allow(clippy::option_option)]
    796 fn deserialize_to_nullable_u64<'de, D>(deserializer: D) -> Result<Option<Option<u64>>, D::Error>
    797 where
    798    D: Deserializer<'de>,
    799 {
    800    let opt: Option<f64> = Option::deserialize(deserializer)?;
    801    let value = match opt {
    802        Some(n) => {
    803            if n < 0.0 || n.fract() != 0.0 {
    804                return Err(de::Error::custom(format!(
    805                    "{} is not a positive Integer",
    806                    n
    807                )));
    808            }
    809            if (n as u64) > MAX_SAFE_INTEGER {
    810                return Err(de::Error::custom(format!(
    811                    "{} is greater than maximum safe integer",
    812                    n
    813                )));
    814            }
    815            Some(Some(n as u64))
    816        }
    817        None => Some(None),
    818    };
    819 
    820    Ok(value)
    821 }
    822 
    823 fn deserialize_to_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
    824 where
    825    D: Deserializer<'de>,
    826 {
    827    let opt: Option<f64> = Option::deserialize(deserializer)?;
    828    let value = match opt {
    829        Some(n) => {
    830            if n < 0.0 || n.fract() != 0.0 {
    831                return Err(de::Error::custom(format!(
    832                    "{} is not a positive Integer",
    833                    n
    834                )));
    835            }
    836            if (n as u64) > MAX_SAFE_INTEGER {
    837                return Err(de::Error::custom(format!(
    838                    "{} is greater than maximum safe integer",
    839                    n
    840                )));
    841            }
    842            Some(n as u64)
    843        }
    844        None => return Err(de::Error::custom("null is not a positive integer")),
    845    };
    846 
    847    Ok(value)
    848 }
    849 
    850 /// A top-level browsing context’s window rect is a dictionary of the
    851 /// [`screenX`], [`screenY`], `width`, and `height` attributes of the
    852 /// `WindowProxy`.
    853 ///
    854 /// In some user agents the operating system’s window dimensions, including
    855 /// decorations, are provided by the proprietary `window.outerWidth` and
    856 /// `window.outerHeight` DOM properties.
    857 ///
    858 /// [`screenX`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screenx
    859 /// [`screenY`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screeny
    860 #[derive(Debug, PartialEq, Serialize, Deserialize)]
    861 pub struct WindowRectParameters {
    862    #[serde(
    863        default,
    864        skip_serializing_if = "Option::is_none",
    865        deserialize_with = "deserialize_to_i32"
    866    )]
    867    pub x: Option<i32>,
    868    #[serde(
    869        default,
    870        skip_serializing_if = "Option::is_none",
    871        deserialize_with = "deserialize_to_i32"
    872    )]
    873    pub y: Option<i32>,
    874    #[serde(
    875        default,
    876        skip_serializing_if = "Option::is_none",
    877        deserialize_with = "deserialize_to_positive_i32"
    878    )]
    879    pub width: Option<i32>,
    880    #[serde(
    881        default,
    882        skip_serializing_if = "Option::is_none",
    883        deserialize_with = "deserialize_to_positive_i32"
    884    )]
    885    pub height: Option<i32>,
    886 }
    887 
    888 fn deserialize_to_i32<'de, D>(deserializer: D) -> Result<Option<i32>, D::Error>
    889 where
    890    D: Deserializer<'de>,
    891 {
    892    let opt = Option::deserialize(deserializer)?.map(|value: f64| value as i64);
    893    let value = match opt {
    894        Some(n) => {
    895            if n < i64::from(i32::MIN) || n > i64::from(i32::MAX) {
    896                return Err(de::Error::custom(format!("'{}' is larger than i32", n)));
    897            }
    898            Some(n as i32)
    899        }
    900        None => None,
    901    };
    902 
    903    Ok(value)
    904 }
    905 
    906 fn deserialize_to_positive_i32<'de, D>(deserializer: D) -> Result<Option<i32>, D::Error>
    907 where
    908    D: Deserializer<'de>,
    909 {
    910    let opt = Option::deserialize(deserializer)?.map(|value: f64| value as i64);
    911    let value = match opt {
    912        Some(n) => {
    913            if n < 0 || n > i64::from(i32::MAX) {
    914                return Err(de::Error::custom(format!("'{}' is outside of i32", n)));
    915            }
    916            Some(n as i32)
    917        }
    918        None => None,
    919    };
    920 
    921    Ok(value)
    922 }
    923 
    924 #[cfg(test)]
    925 mod tests {
    926    use super::*;
    927    use crate::capabilities::SpecNewSessionParameters;
    928    use crate::common::ELEMENT_KEY;
    929    use crate::test::assert_de;
    930    use serde_json::{self, json};
    931 
    932    #[test]
    933    fn test_json_actions_parameters_missing_actions_field() {
    934        assert!(serde_json::from_value::<ActionsParameters>(json!({})).is_err());
    935    }
    936 
    937    #[test]
    938    fn test_json_actions_parameters_invalid() {
    939        assert!(serde_json::from_value::<ActionsParameters>(json!({ "actions": null })).is_err());
    940    }
    941 
    942    #[test]
    943    fn test_json_action_parameters_empty_list() {
    944        assert_de(
    945            &ActionsParameters { actions: vec![] },
    946            json!({"actions": []}),
    947        );
    948    }
    949 
    950    #[test]
    951    fn test_json_action_parameters_with_unknown_field() {
    952        assert_de(
    953            &ActionsParameters { actions: vec![] },
    954            json!({"actions": [], "foo": "bar"}),
    955        );
    956    }
    957 
    958    #[test]
    959    fn test_json_add_cookie_parameters_with_values() {
    960        let json = json!({"cookie": {
    961            "name": "foo",
    962            "value": "bar",
    963            "path": "/",
    964            "domain": "foo.bar",
    965            "expiry": 123,
    966            "secure": true,
    967            "httpOnly": false,
    968            "sameSite": "Lax",
    969        }});
    970        let cookie = AddCookieParameters {
    971            name: "foo".into(),
    972            value: "bar".into(),
    973            path: Some("/".into()),
    974            domain: Some("foo.bar".into()),
    975            expiry: Some(Date(123)),
    976            secure: true,
    977            httpOnly: false,
    978            sameSite: Some("Lax".into()),
    979        };
    980 
    981        assert_de(&cookie, json);
    982    }
    983 
    984    #[test]
    985    fn test_json_add_cookie_parameters_with_optional_null_fields() {
    986        let json = json!({"cookie": {
    987            "name": "foo",
    988            "value": "bar",
    989            "path": null,
    990            "domain": null,
    991            "expiry": null,
    992            "secure": true,
    993            "httpOnly": false,
    994            "sameSite": null,
    995        }});
    996        let cookie = AddCookieParameters {
    997            name: "foo".into(),
    998            value: "bar".into(),
    999            path: None,
   1000            domain: None,
   1001            expiry: None,
   1002            secure: true,
   1003            httpOnly: false,
   1004            sameSite: None,
   1005        };
   1006 
   1007        assert_de(&cookie, json);
   1008    }
   1009 
   1010    #[test]
   1011    fn test_json_add_cookie_parameters_without_optional_fields() {
   1012        let json = json!({"cookie": {
   1013            "name": "foo",
   1014            "value": "bar",
   1015            "secure": true,
   1016            "httpOnly": false,
   1017        }});
   1018        let cookie = AddCookieParameters {
   1019            name: "foo".into(),
   1020            value: "bar".into(),
   1021            path: None,
   1022            domain: None,
   1023            expiry: None,
   1024            secure: true,
   1025            httpOnly: false,
   1026            sameSite: None,
   1027        };
   1028 
   1029        assert_de(&cookie, json);
   1030    }
   1031 
   1032    #[test]
   1033    fn test_json_add_cookie_parameters_with_invalid_cookie_field() {
   1034        assert!(serde_json::from_value::<AddCookieParameters>(json!({"name": "foo"})).is_err());
   1035    }
   1036 
   1037    #[test]
   1038    fn test_json_add_cookie_parameters_with_unknown_field() {
   1039        let json = json!({"cookie": {
   1040            "name": "foo",
   1041            "value": "bar",
   1042            "secure": true,
   1043            "httpOnly": false,
   1044            "foo": "bar",
   1045        }, "baz": "bah"});
   1046        let cookie = AddCookieParameters {
   1047            name: "foo".into(),
   1048            value: "bar".into(),
   1049            path: None,
   1050            domain: None,
   1051            expiry: None,
   1052            secure: true,
   1053            httpOnly: false,
   1054            sameSite: None,
   1055        };
   1056 
   1057        assert_de(&cookie, json);
   1058    }
   1059 
   1060    #[test]
   1061    fn test_json_get_parameters_with_url() {
   1062        assert_de(
   1063            &GetParameters {
   1064                url: "foo.bar".into(),
   1065            },
   1066            json!({"url": "foo.bar"}),
   1067        );
   1068    }
   1069 
   1070    #[test]
   1071    fn test_json_get_parameters_with_invalid_url_value() {
   1072        assert!(serde_json::from_value::<GetParameters>(json!({"url": 3})).is_err());
   1073    }
   1074 
   1075    #[test]
   1076    fn test_json_get_parameters_with_invalid_url_field() {
   1077        assert!(serde_json::from_value::<GetParameters>(json!({"foo": "bar"})).is_err());
   1078    }
   1079 
   1080    #[test]
   1081    fn test_json_get_parameters_with_unknown_field() {
   1082        assert_de(
   1083            &GetParameters {
   1084                url: "foo.bar".into(),
   1085            },
   1086            json!({"url": "foo.bar", "foo": "bar"}),
   1087        );
   1088    }
   1089 
   1090    #[test]
   1091    fn test_json_get_named_cookie_parameters_with_value() {
   1092        assert_de(
   1093            &GetNamedCookieParameters {
   1094                name: Some("foo".into()),
   1095            },
   1096            json!({"name": "foo"}),
   1097        );
   1098    }
   1099 
   1100    #[test]
   1101    fn test_json_get_named_cookie_parameters_with_optional_null_field() {
   1102        assert_de(
   1103            &GetNamedCookieParameters { name: None },
   1104            json!({ "name": null }),
   1105        );
   1106    }
   1107 
   1108    #[test]
   1109    fn test_json_get_named_cookie_parameters_without_optional_null_field() {
   1110        assert_de(&GetNamedCookieParameters { name: None }, json!({}));
   1111    }
   1112 
   1113    #[test]
   1114    fn test_json_get_named_cookie_parameters_with_invalid_name_field() {
   1115        assert!(serde_json::from_value::<GetNamedCookieParameters>(json!({"name": 3})).is_err());
   1116    }
   1117 
   1118    #[test]
   1119    fn test_json_get_named_cookie_parameters_with_unknown_field() {
   1120        assert_de(
   1121            &GetNamedCookieParameters {
   1122                name: Some("foo".into()),
   1123            },
   1124            json!({"name": "foo", "foo": "bar"}),
   1125        );
   1126    }
   1127 
   1128    #[test]
   1129    fn test_json_javascript_command_parameters_with_values() {
   1130        let json = json!({
   1131            "script": "foo",
   1132            "args": ["1", 2],
   1133        });
   1134        let execute_script = JavascriptCommandParameters {
   1135            script: "foo".into(),
   1136            args: Some(vec!["1".into(), 2.into()]),
   1137        };
   1138 
   1139        assert_de(&execute_script, json);
   1140    }
   1141 
   1142    #[test]
   1143    fn test_json_javascript_command_parameters_with_optional_null_field() {
   1144        let json = json!({
   1145            "script": "foo",
   1146            "args": null,
   1147        });
   1148        let execute_script = JavascriptCommandParameters {
   1149            script: "foo".into(),
   1150            args: None,
   1151        };
   1152 
   1153        assert_de(&execute_script, json);
   1154    }
   1155 
   1156    #[test]
   1157    fn test_json_javascript_command_parameters_without_optional_null_field() {
   1158        let execute_script = JavascriptCommandParameters {
   1159            script: "foo".into(),
   1160            args: None,
   1161        };
   1162        assert_de(&execute_script, json!({"script": "foo"}));
   1163    }
   1164 
   1165    #[test]
   1166    fn test_json_javascript_command_parameters_invalid_script_field() {
   1167        let json = json!({ "script": null });
   1168        assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
   1169    }
   1170 
   1171    #[test]
   1172    fn test_json_javascript_command_parameters_invalid_args_field() {
   1173        let json = json!({
   1174            "script": null,
   1175            "args": "1",
   1176        });
   1177        assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
   1178    }
   1179 
   1180    #[test]
   1181    fn test_json_javascript_command_parameters_missing_script_field() {
   1182        let json = json!({ "args": null });
   1183        assert!(serde_json::from_value::<JavascriptCommandParameters>(json).is_err());
   1184    }
   1185 
   1186    #[test]
   1187    fn test_json_javascript_command_parameters_with_unknown_field() {
   1188        let json = json!({
   1189            "script": "foo",
   1190            "foo": "bar",
   1191        });
   1192        let execute_script = JavascriptCommandParameters {
   1193            script: "foo".into(),
   1194            args: None,
   1195        };
   1196 
   1197        assert_de(&execute_script, json);
   1198    }
   1199 
   1200    #[test]
   1201    fn test_json_locator_parameters_with_values() {
   1202        let json = json!({
   1203            "using": "xpath",
   1204            "value": "bar",
   1205        });
   1206        let locator = LocatorParameters {
   1207            using: LocatorStrategy::XPath,
   1208            value: "bar".into(),
   1209        };
   1210 
   1211        assert_de(&locator, json);
   1212    }
   1213 
   1214    #[test]
   1215    fn test_json_locator_parameters_invalid_using_field() {
   1216        let json = json!({
   1217            "using": "foo",
   1218            "value": "bar",
   1219        });
   1220        assert!(serde_json::from_value::<LocatorParameters>(json).is_err());
   1221    }
   1222 
   1223    #[test]
   1224    fn test_json_locator_parameters_invalid_value_field() {
   1225        let json = json!({
   1226            "using": "xpath",
   1227            "value": 3,
   1228        });
   1229        assert!(serde_json::from_value::<LocatorParameters>(json).is_err());
   1230    }
   1231 
   1232    #[test]
   1233    fn test_json_locator_parameters_missing_using_field() {
   1234        assert!(serde_json::from_value::<LocatorParameters>(json!({"value": "bar"})).is_err());
   1235    }
   1236 
   1237    #[test]
   1238    fn test_json_locator_parameters_missing_value_field() {
   1239        assert!(serde_json::from_value::<LocatorParameters>(json!({"using": "xpath"})).is_err());
   1240    }
   1241 
   1242    #[test]
   1243    fn test_json_locator_parameters_with_unknown_field() {
   1244        let json = json!({
   1245            "using": "xpath",
   1246            "value": "bar",
   1247            "foo": "bar",
   1248        });
   1249        let locator = LocatorParameters {
   1250            using: LocatorStrategy::XPath,
   1251            value: "bar".into(),
   1252        };
   1253 
   1254        assert_de(&locator, json);
   1255    }
   1256 
   1257    #[test]
   1258    fn test_json_new_session_parameters_spec() {
   1259        let json = json!({"capabilities": {
   1260            "alwaysMatch": {},
   1261            "firstMatch": [{}],
   1262        }});
   1263        let caps = NewSessionParameters {
   1264            capabilities: SpecNewSessionParameters {
   1265                alwaysMatch: Capabilities::new(),
   1266                firstMatch: vec![Capabilities::new()],
   1267            },
   1268        };
   1269 
   1270        assert_de(&caps, json);
   1271    }
   1272 
   1273    #[test]
   1274    fn test_json_new_session_parameters_capabilities_null() {
   1275        let json = json!({ "capabilities": null });
   1276        assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
   1277    }
   1278 
   1279    #[test]
   1280    fn test_json_new_session_parameters_capabilities_empty_list() {
   1281        let json = json!({ "capabilities": []});
   1282        assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
   1283    }
   1284 
   1285    #[test]
   1286    fn test_json_new_session_parameters_legacy() {
   1287        let json = json!({
   1288            "desiredCapabilities": {},
   1289            "requiredCapabilities": {},
   1290        });
   1291        assert!(serde_json::from_value::<NewSessionParameters>(json).is_err());
   1292    }
   1293 
   1294    #[test]
   1295    fn test_json_new_session_parameters_spec_and_legacy() {
   1296        let json = json!({
   1297            "capabilities": {
   1298                "alwaysMatch": {},
   1299                "firstMatch": [{}],
   1300            },
   1301            "desiredCapabilities": {},
   1302            "requiredCapabilities": {},
   1303        });
   1304        let caps = NewSessionParameters {
   1305            capabilities: SpecNewSessionParameters {
   1306                alwaysMatch: Capabilities::new(),
   1307                firstMatch: vec![Capabilities::new()],
   1308            },
   1309        };
   1310 
   1311        assert_de(&caps, json);
   1312    }
   1313 
   1314    #[test]
   1315    fn test_json_new_session_parameters_with_unknown_field() {
   1316        let json = json!({
   1317            "capabilities": {
   1318                "alwaysMatch": {},
   1319                "firstMatch": [{}]
   1320            },
   1321            "foo": "bar",
   1322        });
   1323        let caps = NewSessionParameters {
   1324            capabilities: SpecNewSessionParameters {
   1325                alwaysMatch: Capabilities::new(),
   1326                firstMatch: vec![Capabilities::new()],
   1327            },
   1328        };
   1329 
   1330        assert_de(&caps, json);
   1331    }
   1332 
   1333    #[test]
   1334    fn test_json_new_window_parameters_without_type() {
   1335        assert_de(&NewWindowParameters { type_hint: None }, json!({}));
   1336    }
   1337 
   1338    #[test]
   1339    fn test_json_new_window_parameters_with_optional_null_type() {
   1340        assert_de(
   1341            &NewWindowParameters { type_hint: None },
   1342            json!({ "type": null }),
   1343        );
   1344    }
   1345 
   1346    #[test]
   1347    fn test_json_new_window_parameters_with_supported_type() {
   1348        assert_de(
   1349            &NewWindowParameters {
   1350                type_hint: Some("tab".into()),
   1351            },
   1352            json!({"type": "tab"}),
   1353        );
   1354    }
   1355 
   1356    #[test]
   1357    fn test_json_new_window_parameters_with_unknown_type() {
   1358        assert_de(
   1359            &NewWindowParameters {
   1360                type_hint: Some("foo".into()),
   1361            },
   1362            json!({"type": "foo"}),
   1363        );
   1364    }
   1365 
   1366    #[test]
   1367    fn test_json_new_window_parameters_with_invalid_type() {
   1368        assert!(serde_json::from_value::<NewWindowParameters>(json!({"type": 3})).is_err());
   1369    }
   1370 
   1371    #[test]
   1372    fn test_json_new_window_parameters_with_unknown_field() {
   1373        let json = json!({
   1374            "type": "tab",
   1375            "foo": "bar",
   1376        });
   1377        let new_window = NewWindowParameters {
   1378            type_hint: Some("tab".into()),
   1379        };
   1380 
   1381        assert_de(&new_window, json);
   1382    }
   1383 
   1384    #[test]
   1385    fn test_json_print_defaults() {
   1386        let params = PrintParameters::default();
   1387        assert_de(&params, json!({}));
   1388    }
   1389 
   1390    #[test]
   1391    fn test_json_print() {
   1392        let params = PrintParameters {
   1393            orientation: PrintOrientation::Landscape,
   1394            page: PrintPage {
   1395                width: 10.0,
   1396                ..Default::default()
   1397            },
   1398            margin: PrintMargins {
   1399                top: 10.0,
   1400                ..Default::default()
   1401            },
   1402            scale: 1.5,
   1403            ..Default::default()
   1404        };
   1405        assert_de(
   1406            &params,
   1407            json!({"orientation": "landscape", "page": {"width": 10}, "margin": {"top": 10}, "scale": 1.5}),
   1408        );
   1409    }
   1410 
   1411    #[test]
   1412    fn test_json_scale_invalid() {
   1413        assert!(serde_json::from_value::<PrintParameters>(json!({"scale": 3})).is_err());
   1414    }
   1415 
   1416    #[test]
   1417    fn test_json_permission() {
   1418        let params: SetPermissionParameters = SetPermissionParameters {
   1419            descriptor: SetPermissionDescriptor {
   1420                name: "push".into(),
   1421            },
   1422            state: SetPermissionState::Granted,
   1423        };
   1424        assert_de(
   1425            &params,
   1426            json!({"descriptor": {"name": "push"}, "state": "granted"}),
   1427        );
   1428    }
   1429 
   1430    #[test]
   1431    fn test_json_permission_parameters_invalid() {
   1432        assert!(serde_json::from_value::<SetPermissionParameters>(json!({"test": 3})).is_err());
   1433    }
   1434 
   1435    #[test]
   1436    fn test_json_permission_descriptor_invalid_type() {
   1437        assert!(serde_json::from_value::<SetPermissionParameters>(
   1438            json!({"descriptor": "test", "state": "granted"})
   1439        )
   1440        .is_err());
   1441    }
   1442 
   1443    #[test]
   1444    fn test_json_permission_state_invalid_type() {
   1445        assert!(serde_json::from_value::<SetPermissionParameters>(
   1446            json!({"descriptor": {"name": "push"}, "state": 3})
   1447        )
   1448        .is_err());
   1449    }
   1450 
   1451    #[test]
   1452    fn test_json_permission_state_invalid_value() {
   1453        assert!(serde_json::from_value::<SetPermissionParameters>(
   1454            json!({"descriptor": {"name": "push"}, "state": "invalid"})
   1455        )
   1456        .is_err());
   1457    }
   1458 
   1459    #[test]
   1460    fn test_json_authenticator() {
   1461        let params = AuthenticatorParameters {
   1462            protocol: WebAuthnProtocol::Ctap1U2f,
   1463            transport: AuthenticatorTransport::Usb,
   1464            has_resident_key: false,
   1465            has_user_verification: false,
   1466            is_user_consenting: false,
   1467            is_user_verified: false,
   1468        };
   1469        assert_de(
   1470            &params,
   1471            json!({"protocol": "ctap1/u2f", "transport": "usb", "hasResidentKey": false, "hasUserVerification": false, "isUserConsenting": false, "isUserVerified": false}),
   1472        );
   1473    }
   1474 
   1475    #[test]
   1476    fn test_json_credential() {
   1477        use base64::{engine::general_purpose::URL_SAFE, Engine};
   1478 
   1479        let encoded_string = URL_SAFE.encode(b"hello internet~");
   1480        let params = CredentialParameters {
   1481            credential_id: r"c3VwZXIgcmVhZGVy".to_string(),
   1482            is_resident_credential: true,
   1483            rp_id: "valid.rpid".to_string(),
   1484            private_key: encoded_string.clone(),
   1485            user_handle: encoded_string.clone(),
   1486            sign_count: 0,
   1487        };
   1488        assert_de(
   1489            &params,
   1490            json!({"credentialId": r"c3VwZXIgcmVhZGVy", "isResidentCredential": true, "rpId": "valid.rpid", "privateKey": encoded_string, "userHandle": encoded_string, "signCount": 0}),
   1491        );
   1492    }
   1493 
   1494    #[test]
   1495    fn test_json_user_verification() {
   1496        let params = UserVerificationParameters {
   1497            is_user_verified: false,
   1498        };
   1499        assert_de(&params, json!({"isUserVerified": false}));
   1500    }
   1501 
   1502    #[test]
   1503    fn test_json_send_keys_parameters_with_value() {
   1504        assert_de(
   1505            &SendKeysParameters { text: "foo".into() },
   1506            json!({"text": "foo"}),
   1507        );
   1508    }
   1509 
   1510    #[test]
   1511    fn test_json_send_keys_parameters_invalid_text_field() {
   1512        assert!(serde_json::from_value::<SendKeysParameters>(json!({"text": 3})).is_err());
   1513    }
   1514 
   1515    #[test]
   1516    fn test_json_send_keys_parameters_missing_text_field() {
   1517        assert!(serde_json::from_value::<SendKeysParameters>(json!({})).is_err());
   1518    }
   1519 
   1520    #[test]
   1521    fn test_json_send_keys_parameters_with_unknown_field() {
   1522        let json = json!({
   1523            "text": "foo",
   1524            "foo": "bar",
   1525        });
   1526        let send_keys = SendKeysParameters { text: "foo".into() };
   1527 
   1528        assert_de(&send_keys, json);
   1529    }
   1530 
   1531    #[test]
   1532    fn test_json_switch_to_frame_parameters_with_number() {
   1533        assert_de(
   1534            &SwitchToFrameParameters {
   1535                id: FrameId::Short(3),
   1536            },
   1537            json!({"id": 3}),
   1538        );
   1539    }
   1540 
   1541    #[test]
   1542    fn test_json_switch_to_frame_parameters_with_null() {
   1543        assert_de(
   1544            &SwitchToFrameParameters { id: FrameId::Top },
   1545            json!({"id": null}),
   1546        );
   1547    }
   1548 
   1549    #[test]
   1550    fn test_json_switch_to_frame_parameters_with_web_element() {
   1551        assert_de(
   1552            &SwitchToFrameParameters {
   1553                id: FrameId::Element(WebElement("foo".to_string())),
   1554            },
   1555            json!({"id": {"element-6066-11e4-a52e-4f735466cecf": "foo"}}),
   1556        );
   1557    }
   1558 
   1559    #[test]
   1560    fn test_json_switch_to_frame_parameters_with_missing_id() {
   1561        assert!(serde_json::from_value::<SwitchToFrameParameters>(json!({})).is_err())
   1562    }
   1563 
   1564    #[test]
   1565    fn test_json_switch_to_frame_parameters_with_invalid_id_field() {
   1566        assert!(serde_json::from_value::<SwitchToFrameParameters>(json!({"id": "3"})).is_err());
   1567    }
   1568 
   1569    #[test]
   1570    fn test_json_switch_to_frame_parameters_with_unknown_field() {
   1571        let json = json!({
   1572            "id":3,
   1573            "foo": "bar",
   1574        });
   1575        let switch_to_frame = SwitchToFrameParameters {
   1576            id: FrameId::Short(3),
   1577        };
   1578 
   1579        assert_de(&switch_to_frame, json);
   1580    }
   1581 
   1582    #[test]
   1583    fn test_json_switch_to_window_parameters_with_value() {
   1584        assert_de(
   1585            &SwitchToWindowParameters {
   1586                handle: "foo".into(),
   1587            },
   1588            json!({"handle": "foo"}),
   1589        );
   1590    }
   1591 
   1592    #[test]
   1593    fn test_json_switch_to_window_parameters_invalid_handle_field() {
   1594        assert!(serde_json::from_value::<SwitchToWindowParameters>(json!({"handle": 3})).is_err());
   1595    }
   1596 
   1597    #[test]
   1598    fn test_json_switch_to_window_parameters_missing_handle_field() {
   1599        assert!(serde_json::from_value::<SwitchToWindowParameters>(json!({})).is_err());
   1600    }
   1601 
   1602    #[test]
   1603    fn test_json_switch_to_window_parameters_with_unknown_field() {
   1604        let json = json!({
   1605            "handle": "foo",
   1606            "foo": "bar",
   1607        });
   1608        let switch_to_window = SwitchToWindowParameters {
   1609            handle: "foo".into(),
   1610        };
   1611 
   1612        assert_de(&switch_to_window, json);
   1613    }
   1614 
   1615    #[test]
   1616    fn test_json_take_screenshot_parameters_with_element() {
   1617        assert_de(
   1618            &TakeScreenshotParameters {
   1619                element: Some(WebElement("elem".into())),
   1620            },
   1621            json!({"element": {ELEMENT_KEY: "elem"}}),
   1622        );
   1623    }
   1624 
   1625    #[test]
   1626    fn test_json_take_screenshot_parameters_with_optional_null_field() {
   1627        assert_de(
   1628            &TakeScreenshotParameters { element: None },
   1629            json!({ "element": null }),
   1630        );
   1631    }
   1632 
   1633    #[test]
   1634    fn test_json_take_screenshot_parameters_without_optional_null_field() {
   1635        assert_de(&TakeScreenshotParameters { element: None }, json!({}));
   1636    }
   1637 
   1638    #[test]
   1639    fn test_json_take_screenshot_parameters_with_invalid_element_field() {
   1640        assert!(
   1641            serde_json::from_value::<TakeScreenshotParameters>(json!({"element": "foo"})).is_err()
   1642        );
   1643    }
   1644 
   1645    #[test]
   1646    fn test_json_take_screenshot_parameters_with_unknown_field() {
   1647        let json = json!({
   1648            "element": {ELEMENT_KEY: "elem"},
   1649            "foo": "bar",
   1650        });
   1651        let take_screenshot = TakeScreenshotParameters {
   1652            element: Some(WebElement("elem".into())),
   1653        };
   1654 
   1655        assert_de(&take_screenshot, json);
   1656    }
   1657 
   1658    #[test]
   1659    fn test_json_timeout_parameters_with_only_null_script_timeout() {
   1660        let timeouts = TimeoutsParameters {
   1661            implicit: None,
   1662            page_load: None,
   1663            script: Some(None),
   1664        };
   1665        assert_de(&timeouts, json!({ "script": null }));
   1666    }
   1667 
   1668    #[test]
   1669    fn test_json_timeout_parameters_with_only_null_implicit_timeout() {
   1670        assert!(serde_json::from_value::<TimeoutsParameters>(json!({ "implicit": null })).is_err());
   1671    }
   1672 
   1673    #[test]
   1674    fn test_json_timeout_parameters_with_only_null_pageload_timeout() {
   1675        assert!(serde_json::from_value::<TimeoutsParameters>(json!({ "pageLoad": null })).is_err());
   1676    }
   1677 
   1678    #[test]
   1679    fn test_json_timeout_parameters_without_optional_null_field() {
   1680        let timeouts = TimeoutsParameters {
   1681            implicit: None,
   1682            page_load: None,
   1683            script: None,
   1684        };
   1685        assert_de(&timeouts, json!({}));
   1686    }
   1687 
   1688    #[test]
   1689    fn test_json_timeout_parameters_with_unknown_field() {
   1690        let json = json!({
   1691            "script": 60000,
   1692            "foo": "bar",
   1693        });
   1694        let timeouts = TimeoutsParameters {
   1695            implicit: None,
   1696            page_load: None,
   1697            script: Some(Some(60000)),
   1698        };
   1699 
   1700        assert_de(&timeouts, json);
   1701    }
   1702 
   1703    #[test]
   1704    fn test_json_window_rect_parameters_with_values() {
   1705        let json = json!({
   1706            "x": 0,
   1707            "y": 1,
   1708            "width": 2,
   1709            "height": 3,
   1710        });
   1711        let rect = WindowRectParameters {
   1712            x: Some(0i32),
   1713            y: Some(1i32),
   1714            width: Some(2i32),
   1715            height: Some(3i32),
   1716        };
   1717 
   1718        assert_de(&rect, json);
   1719    }
   1720 
   1721    #[test]
   1722    fn test_json_window_rect_parameters_with_optional_null_fields() {
   1723        let json = json!({
   1724            "x": null,
   1725            "y": null,
   1726            "width": null,
   1727            "height": null,
   1728        });
   1729        let rect = WindowRectParameters {
   1730            x: None,
   1731            y: None,
   1732            width: None,
   1733            height: None,
   1734        };
   1735 
   1736        assert_de(&rect, json);
   1737    }
   1738 
   1739    #[test]
   1740    fn test_json_window_rect_parameters_without_optional_fields() {
   1741        let rect = WindowRectParameters {
   1742            x: None,
   1743            y: None,
   1744            width: None,
   1745            height: None,
   1746        };
   1747        assert_de(&rect, json!({}));
   1748    }
   1749 
   1750    #[test]
   1751    fn test_json_window_rect_parameters_invalid_values_float() {
   1752        let json = json!({
   1753            "x": 1.1,
   1754            "y": 2.2,
   1755            "width": 3.3,
   1756            "height": 4.4,
   1757        });
   1758        let rect = WindowRectParameters {
   1759            x: Some(1),
   1760            y: Some(2),
   1761            width: Some(3),
   1762            height: Some(4),
   1763        };
   1764 
   1765        assert_de(&rect, json);
   1766    }
   1767 
   1768    #[test]
   1769    fn test_json_window_rect_parameters_with_unknown_field() {
   1770        let json = json!({
   1771            "x": 1.1,
   1772            "y": 2.2,
   1773            "foo": "bar",
   1774        });
   1775        let rect = WindowRectParameters {
   1776            x: Some(1),
   1777            y: Some(2),
   1778            width: None,
   1779            height: None,
   1780        };
   1781 
   1782        assert_de(&rect, json);
   1783    }
   1784 }