tor-browser

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

webdriver.rs (17503B)


      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 serde::{Deserialize, Serialize};
      6 use serde_json::Value;
      7 
      8 use crate::common::{from_cookie, from_name, to_cookie, to_name, Cookie, Frame, Timeouts, Window};
      9 
     10 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
     11 pub struct Url {
     12    pub url: String,
     13 }
     14 
     15 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
     16 pub struct Locator {
     17    #[serde(skip_serializing_if = "Option::is_none")]
     18    pub element: Option<String>,
     19    pub using: Selector,
     20    pub value: String,
     21 }
     22 
     23 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
     24 pub enum Selector {
     25    #[serde(rename = "css selector")]
     26    Css,
     27    #[serde(rename = "link text")]
     28    LinkText,
     29    #[serde(rename = "partial link text")]
     30    PartialLinkText,
     31    #[serde(rename = "tag name")]
     32    TagName,
     33    #[serde(rename = "xpath")]
     34    XPath,
     35 }
     36 
     37 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
     38 pub struct NewWindow {
     39    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
     40    pub type_hint: Option<String>,
     41 }
     42 
     43 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
     44 pub struct WindowRect {
     45    #[serde(default, skip_serializing_if = "Option::is_none")]
     46    pub x: Option<i32>,
     47    #[serde(default, skip_serializing_if = "Option::is_none")]
     48    pub y: Option<i32>,
     49    #[serde(default, skip_serializing_if = "Option::is_none")]
     50    pub width: Option<i32>,
     51    #[serde(default, skip_serializing_if = "Option::is_none")]
     52    pub height: Option<i32>,
     53 }
     54 
     55 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
     56 pub struct Keys {
     57    pub text: String,
     58    pub value: Vec<String>,
     59 }
     60 
     61 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
     62 #[serde(untagged)]
     63 pub enum PrintPageRange {
     64    Integer(u64),
     65    Range(String),
     66 }
     67 
     68 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
     69 #[serde(default, rename_all = "camelCase")]
     70 pub struct PrintParameters {
     71    pub orientation: PrintOrientation,
     72    pub scale: f64,
     73    pub background: bool,
     74    pub page: PrintPage,
     75    pub margin: PrintMargins,
     76    pub page_ranges: Vec<PrintPageRange>,
     77    pub shrink_to_fit: bool,
     78 }
     79 
     80 impl Default for PrintParameters {
     81    fn default() -> Self {
     82        PrintParameters {
     83            orientation: PrintOrientation::default(),
     84            scale: 1.0,
     85            background: false,
     86            page: PrintPage::default(),
     87            margin: PrintMargins::default(),
     88            page_ranges: Vec::new(),
     89            shrink_to_fit: true,
     90        }
     91    }
     92 }
     93 
     94 #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
     95 #[serde(rename_all = "lowercase")]
     96 pub enum PrintOrientation {
     97    Landscape,
     98    #[default]
     99    Portrait,
    100 }
    101 
    102 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    103 pub struct PrintPage {
    104    pub width: f64,
    105    pub height: f64,
    106 }
    107 
    108 impl Default for PrintPage {
    109    fn default() -> Self {
    110        PrintPage {
    111            width: 21.59,
    112            height: 27.94,
    113        }
    114    }
    115 }
    116 
    117 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    118 pub struct PrintMargins {
    119    pub top: f64,
    120    pub bottom: f64,
    121    pub left: f64,
    122    pub right: f64,
    123 }
    124 
    125 impl Default for PrintMargins {
    126    fn default() -> Self {
    127        PrintMargins {
    128            top: 1.0,
    129            bottom: 1.0,
    130            left: 1.0,
    131            right: 1.0,
    132        }
    133    }
    134 }
    135 
    136 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    137 pub struct SetPermissionParameters {
    138    pub descriptor: SetPermissionDescriptor,
    139    pub state: SetPermissionState,
    140 }
    141 
    142 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    143 pub struct SetPermissionDescriptor {
    144    pub name: String,
    145 }
    146 
    147 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    148 #[serde(rename_all = "lowercase")]
    149 pub enum SetPermissionState {
    150    Denied,
    151    Granted,
    152    Prompt,
    153 }
    154 
    155 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    156 pub enum WebAuthnProtocol {
    157    #[serde(rename = "ctap1/u2f")]
    158    Ctap1U2f,
    159    #[serde(rename = "ctap2")]
    160    Ctap2,
    161    #[serde(rename = "ctap2_1")]
    162    Ctap2_1,
    163 }
    164 
    165 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    166 #[serde(rename_all = "kebab-case")]
    167 pub enum AuthenticatorTransport {
    168    Usb,
    169    Nfc,
    170    Ble,
    171    SmartCard,
    172    Hybrid,
    173    Internal,
    174 }
    175 
    176 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    177 #[serde(rename_all = "camelCase")]
    178 pub struct AuthenticatorParameters {
    179    pub protocol: WebAuthnProtocol,
    180    pub transport: AuthenticatorTransport,
    181    pub has_resident_key: bool,
    182    pub has_user_verification: bool,
    183    pub is_user_consenting: bool,
    184    pub is_user_verified: bool,
    185 }
    186 
    187 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    188 #[serde(rename_all = "camelCase")]
    189 pub struct CredentialParameters {
    190    pub credential_id: String,
    191    pub is_resident_credential: bool,
    192    pub rp_id: String,
    193    pub private_key: String,
    194    pub user_handle: String,
    195    pub sign_count: u64,
    196 }
    197 
    198 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    199 #[serde(rename_all = "camelCase")]
    200 pub struct UserVerificationParameters {
    201    pub is_user_verified: bool,
    202 }
    203 
    204 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
    205 #[serde(rename_all = "camelCase")]
    206 pub struct GlobalPrivacyControlParameters {
    207    pub gpc: bool,
    208 }
    209 
    210 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    211 pub struct ScreenshotOptions {
    212    pub id: Option<String>,
    213    pub highlights: Vec<Option<String>>,
    214    pub full: bool,
    215 }
    216 
    217 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    218 pub struct Script {
    219    pub script: String,
    220    pub args: Option<Vec<Value>>,
    221 }
    222 
    223 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
    224 pub enum Command {
    225    #[serde(rename = "WebDriver:AcceptAlert")]
    226    AcceptAlert,
    227    #[serde(
    228        rename = "WebDriver:AddCookie",
    229        serialize_with = "to_cookie",
    230        deserialize_with = "from_cookie"
    231    )]
    232    AddCookie(Cookie),
    233    #[serde(rename = "WebDriver:CloseWindow")]
    234    CloseWindow,
    235    #[serde(
    236        rename = "WebDriver:DeleteCookie",
    237        serialize_with = "to_name",
    238        deserialize_with = "from_name"
    239    )]
    240    DeleteCookie(String),
    241    #[serde(rename = "WebDriver:DeleteAllCookies")]
    242    DeleteCookies,
    243    #[serde(rename = "WebDriver:DeleteSession")]
    244    DeleteSession,
    245    #[serde(rename = "WebDriver:DismissAlert")]
    246    DismissAlert,
    247    #[serde(rename = "WebDriver:ElementClear")]
    248    ElementClear { id: String },
    249    #[serde(rename = "WebDriver:ElementClick")]
    250    ElementClick { id: String },
    251    #[serde(rename = "WebDriver:ElementSendKeys")]
    252    ElementSendKeys {
    253        id: String,
    254        text: String,
    255        value: Vec<String>,
    256    },
    257    #[serde(rename = "WebDriver:ExecuteAsyncScript")]
    258    ExecuteAsyncScript(Script),
    259    #[serde(rename = "WebDriver:ExecuteScript")]
    260    ExecuteScript(Script),
    261    #[serde(rename = "WebDriver:FindElement")]
    262    FindElement(Locator),
    263    #[serde(rename = "WebDriver:FindElements")]
    264    FindElements(Locator),
    265    #[serde(rename = "WebDriver:FindElementFromShadowRoot")]
    266    FindShadowRootElement {
    267        #[serde(rename = "shadowRoot")]
    268        shadow_root: String,
    269        using: Selector,
    270        value: String,
    271    },
    272    #[serde(rename = "WebDriver:FindElementsFromShadowRoot")]
    273    FindShadowRootElements {
    274        #[serde(rename = "shadowRoot")]
    275        shadow_root: String,
    276        using: Selector,
    277        value: String,
    278    },
    279    #[serde(rename = "WebDriver:FullscreenWindow")]
    280    FullscreenWindow,
    281    #[serde(rename = "WebDriver:Navigate")]
    282    Get(Url),
    283    #[serde(rename = "WebDriver:GetActiveElement")]
    284    GetActiveElement,
    285    #[serde(rename = "WebDriver:GetAlertText")]
    286    GetAlertText,
    287    #[serde(rename = "WebDriver:GetComputedLabel")]
    288    GetComputedLabel { id: String },
    289    #[serde(rename = "WebDriver:GetComputedRole")]
    290    GetComputedRole { id: String },
    291    #[serde(rename = "WebDriver:GetCookies")]
    292    GetCookies,
    293    #[serde(rename = "WebDriver:GetElementCSSValue")]
    294    GetCSSValue {
    295        id: String,
    296        #[serde(rename = "propertyName")]
    297        property: String,
    298    },
    299    #[serde(rename = "WebDriver:GetCurrentURL")]
    300    GetCurrentUrl,
    301    #[serde(rename = "WebDriver:GetElementAttribute")]
    302    GetElementAttribute { id: String, name: String },
    303    #[serde(rename = "WebDriver:GetElementProperty")]
    304    GetElementProperty { id: String, name: String },
    305    #[serde(rename = "WebDriver:GetElementRect")]
    306    GetElementRect { id: String },
    307    #[serde(rename = "WebDriver:GetElementTagName")]
    308    GetElementTagName { id: String },
    309    #[serde(rename = "WebDriver:GetElementText")]
    310    GetElementText { id: String },
    311    #[serde(rename = "WebDriver:GetPageSource")]
    312    GetPageSource,
    313    #[serde(rename = "WebDriver:GetShadowRoot")]
    314    GetShadowRoot { id: String },
    315    #[serde(rename = "WebDriver:GetTimeouts")]
    316    GetTimeouts,
    317    #[serde(rename = "WebDriver:GetTitle")]
    318    GetTitle,
    319    #[serde(rename = "WebDriver:GetWindowHandle")]
    320    GetWindowHandle,
    321    #[serde(rename = "WebDriver:GetWindowHandles")]
    322    GetWindowHandles,
    323    #[serde(rename = "WebDriver:GetWindowRect")]
    324    GetWindowRect,
    325    #[serde(rename = "WebDriver:Back")]
    326    GoBack,
    327    #[serde(rename = "WebDriver:Forward")]
    328    GoForward,
    329    #[serde(rename = "WebDriver:IsElementDisplayed")]
    330    IsDisplayed { id: String },
    331    #[serde(rename = "WebDriver:IsElementEnabled")]
    332    IsEnabled { id: String },
    333    #[serde(rename = "WebDriver:IsElementSelected")]
    334    IsSelected { id: String },
    335    #[serde(rename = "WebDriver:MaximizeWindow")]
    336    MaximizeWindow,
    337    #[serde(rename = "WebDriver:MinimizeWindow")]
    338    MinimizeWindow,
    339    #[serde(rename = "WebDriver:NewWindow")]
    340    NewWindow(NewWindow),
    341    #[serde(rename = "WebDriver:Print")]
    342    Print(PrintParameters),
    343    #[serde(rename = "WebDriver:Refresh")]
    344    Refresh,
    345    #[serde(rename = "WebDriver:ReleaseActions")]
    346    ReleaseActions,
    347    #[serde(rename = "WebDriver:SendAlertText")]
    348    SendAlertText(Keys),
    349    #[serde(rename = "WebDriver:SetPermission")]
    350    SetPermission(SetPermissionParameters),
    351    #[serde(rename = "WebDriver:SetTimeouts")]
    352    SetTimeouts(Timeouts),
    353    #[serde(rename = "WebDriver:SetWindowRect")]
    354    SetWindowRect(WindowRect),
    355    #[serde(rename = "WebDriver:SwitchToFrame")]
    356    SwitchToFrame(Frame),
    357    #[serde(rename = "WebDriver:SwitchToParentFrame")]
    358    SwitchToParentFrame,
    359    #[serde(rename = "WebDriver:SwitchToWindow")]
    360    SwitchToWindow(Window),
    361    #[serde(rename = "WebDriver:TakeScreenshot")]
    362    TakeScreenshot(ScreenshotOptions),
    363    #[serde(rename = "GPC:GetGlobalPrivacyControl")]
    364    GPCGetGlobalPrivacyControl,
    365    #[serde(rename = "GPC:SetGlobalPrivacyControl")]
    366    GPCSetGlobalPrivacyControl(GlobalPrivacyControlParameters),
    367    #[serde(rename = "WebAuthn:AddVirtualAuthenticator")]
    368    WebAuthnAddVirtualAuthenticator(AuthenticatorParameters),
    369    #[serde(rename = "WebAuthn:RemoveVirtualAuthenticator")]
    370    WebAuthnRemoveVirtualAuthenticator,
    371    #[serde(rename = "WebAuthn:AddCredential")]
    372    WebAuthnAddCredential(CredentialParameters),
    373    #[serde(rename = "WebAuthn:GetCredentials")]
    374    WebAuthnGetCredentials,
    375    #[serde(rename = "WebAuthn:RemoveCredential")]
    376    WebAuthnRemoveCredential,
    377    #[serde(rename = "WebAuthn:RemoveAllCredentials")]
    378    WebAuthnRemoveAllCredentials,
    379    #[serde(rename = "WebAuthn:SetUserVerified")]
    380    WebAuthnSetUserVerified(UserVerificationParameters),
    381 }
    382 
    383 #[cfg(test)]
    384 mod tests {
    385    use super::*;
    386    use crate::common::Date;
    387    use crate::test::{assert_ser, assert_ser_de};
    388    use serde_json::json;
    389 
    390    #[test]
    391    fn test_json_screenshot() {
    392        let data = ScreenshotOptions {
    393            id: None,
    394            highlights: vec![],
    395            full: false,
    396        };
    397        let json = json!({"full":false,"highlights":[],"id":null});
    398        assert_ser_de(&data, json);
    399    }
    400 
    401    #[test]
    402    fn test_json_selector_css() {
    403        assert_ser_de(&Selector::Css, json!("css selector"));
    404    }
    405 
    406    #[test]
    407    fn test_json_selector_link_text() {
    408        assert_ser_de(&Selector::LinkText, json!("link text"));
    409    }
    410 
    411    #[test]
    412    fn test_json_selector_partial_link_text() {
    413        assert_ser_de(&Selector::PartialLinkText, json!("partial link text"));
    414    }
    415 
    416    #[test]
    417    fn test_json_selector_tag_name() {
    418        assert_ser_de(&Selector::TagName, json!("tag name"));
    419    }
    420 
    421    #[test]
    422    fn test_json_selector_xpath() {
    423        assert_ser_de(&Selector::XPath, json!("xpath"));
    424    }
    425 
    426    #[test]
    427    fn test_json_selector_invalid() {
    428        assert!(serde_json::from_value::<Selector>(json!("foo")).is_err());
    429    }
    430 
    431    #[test]
    432    fn test_json_locator() {
    433        let json = json!({
    434            "using": "partial link text",
    435            "value": "link text",
    436        });
    437        let data = Locator {
    438            element: None,
    439            using: Selector::PartialLinkText,
    440            value: "link text".into(),
    441        };
    442 
    443        assert_ser_de(&data, json);
    444    }
    445 
    446    #[test]
    447    fn test_json_keys() {
    448        let data = Keys {
    449            text: "Foo".into(),
    450            value: vec!["F".into(), "o".into(), "o".into()],
    451        };
    452        let json = json!({"text": "Foo", "value": ["F", "o", "o"]});
    453        assert_ser_de(&data, json);
    454    }
    455 
    456    #[test]
    457    fn test_json_new_window() {
    458        let data = NewWindow {
    459            type_hint: Some("foo".into()),
    460        };
    461        assert_ser_de(&data, json!({ "type": "foo" }));
    462    }
    463 
    464    #[test]
    465    fn test_json_window_rect() {
    466        let data = WindowRect {
    467            x: Some(123),
    468            y: None,
    469            width: None,
    470            height: None,
    471        };
    472        assert_ser_de(&data, json!({"x": 123}));
    473    }
    474 
    475    #[test]
    476    fn test_command_with_params() {
    477        let locator = Locator {
    478            element: None,
    479            using: Selector::Css,
    480            value: "value".into(),
    481        };
    482        let json = json!({"WebDriver:FindElement": {"using": "css selector", "value": "value"}});
    483        assert_ser_de(&Command::FindElement(locator), json);
    484    }
    485 
    486    #[test]
    487    fn test_command_with_wrapper_params() {
    488        let cookie = Cookie {
    489            name: "hello".into(),
    490            value: "world".into(),
    491            path: None,
    492            domain: None,
    493            secure: false,
    494            http_only: false,
    495            expiry: Some(Date(1564488092)),
    496            same_site: None,
    497        };
    498        let json = json!({"WebDriver:AddCookie": {"cookie": {"name": "hello", "value": "world", "secure": false, "httpOnly": false, "expiry": 1564488092}}});
    499        assert_ser_de(&Command::AddCookie(cookie), json);
    500    }
    501 
    502    #[test]
    503    fn test_empty_commands() {
    504        assert_ser_de(&Command::GetTimeouts, json!("WebDriver:GetTimeouts"));
    505    }
    506 
    507    #[test]
    508    fn test_json_command_invalid() {
    509        assert!(serde_json::from_value::<Command>(json!("foo")).is_err());
    510    }
    511 
    512    #[test]
    513    fn test_json_delete_cookie_command() {
    514        let json = json!({"WebDriver:DeleteCookie": {"name": "foo"}});
    515        assert_ser_de(&Command::DeleteCookie("foo".into()), json);
    516    }
    517 
    518    #[test]
    519    fn test_json_new_window_command() {
    520        let data = NewWindow {
    521            type_hint: Some("foo".into()),
    522        };
    523        let json = json!({"WebDriver:NewWindow": {"type": "foo"}});
    524        assert_ser_de(&Command::NewWindow(data), json);
    525    }
    526 
    527    #[test]
    528    fn test_json_new_window_command_with_none_value() {
    529        let data = NewWindow { type_hint: None };
    530        let json = json!({"WebDriver:NewWindow": {}});
    531        assert_ser_de(&Command::NewWindow(data), json);
    532    }
    533 
    534    #[test]
    535    fn test_json_command_as_struct() {
    536        assert_ser(
    537            &Command::FindElement(Locator {
    538                element: Some("foo".into()),
    539                using: Selector::XPath,
    540                value: "bar".into(),
    541            }),
    542            json!({"WebDriver:FindElement": {"element": "foo", "using": "xpath", "value": "bar" }}),
    543        );
    544    }
    545 
    546    #[test]
    547    fn test_json_get_computed_label_command() {
    548        assert_ser_de(
    549            &Command::GetComputedLabel { id: "foo".into() },
    550            json!({"WebDriver:GetComputedLabel": {"id": "foo"}}),
    551        );
    552    }
    553 
    554    #[test]
    555    fn test_json_get_computed_role_command() {
    556        assert_ser_de(
    557            &Command::GetComputedRole { id: "foo".into() },
    558            json!({"WebDriver:GetComputedRole": {"id": "foo"}}),
    559        );
    560    }
    561 
    562    #[test]
    563    fn test_json_get_css_value() {
    564        assert_ser_de(
    565            &Command::GetCSSValue {
    566                id: "foo".into(),
    567                property: "bar".into(),
    568            },
    569            json!({"WebDriver:GetElementCSSValue": {"id": "foo", "propertyName": "bar"}}),
    570        );
    571    }
    572 
    573    #[test]
    574    fn test_json_find_shadow_root_element() {
    575        assert_ser_de(
    576            &Command::FindShadowRootElement {
    577                shadow_root: "foo".into(),
    578                using: Selector::Css,
    579                value: "bar".into(),
    580            },
    581            json!({"WebDriver:FindElementFromShadowRoot": {"shadowRoot": "foo", "using": "css selector", "value": "bar"}}),
    582        );
    583    }
    584 
    585    #[test]
    586    fn test_json_find_shadow_root_elements() {
    587        assert_ser_de(
    588            &Command::FindShadowRootElements {
    589                shadow_root: "foo".into(),
    590                using: Selector::Css,
    591                value: "bar".into(),
    592            },
    593            json!({"WebDriver:FindElementsFromShadowRoot": {"shadowRoot": "foo", "using": "css selector", "value": "bar"}}),
    594        );
    595    }
    596 }