fantoccini/
cookies.rs

1//! Cookie-related functionality for WebDriver.
2
3use cookie::SameSite;
4use serde::{Deserialize, Serialize};
5use std::convert::{TryFrom, TryInto};
6use time::OffsetDateTime;
7use webdriver::command::{AddCookieParameters, WebDriverCommand};
8use webdriver::common::Date;
9
10use crate::client::Client;
11use crate::error;
12
13/// Type alias for a [cookie::Cookie]
14pub type Cookie<'a> = cookie::Cookie<'a>;
15
16/// Wrapper for serializing AddCookieParameters.
17#[derive(Debug, Serialize)]
18pub(crate) struct AddCookieParametersWrapper<'a> {
19    /// The cookie to serialize.
20    #[serde(with = "AddCookieParameters")]
21    pub(crate) cookie: &'a AddCookieParameters,
22}
23
24/// Representation of a cookie as [defined by WebDriver](https://www.w3.org/TR/webdriver1/#cookies).
25#[derive(Debug, Deserialize, Serialize)]
26pub(crate) struct WebDriverCookie {
27    name: String,
28    value: String,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    path: Option<String>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    domain: Option<String>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    secure: Option<bool>,
35    #[serde(skip_serializing_if = "Option::is_none", rename = "httpOnly")]
36    http_only: Option<bool>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    expiry: Option<u64>,
39    #[serde(skip_serializing_if = "Option::is_none", rename = "sameSite")]
40    same_site: Option<String>,
41}
42
43impl WebDriverCookie {
44    fn into_params(self) -> AddCookieParameters {
45        AddCookieParameters {
46            name: self.name,
47            value: self.value,
48            path: self.path,
49            domain: self.domain,
50            secure: self.secure.unwrap_or_default(),
51            httpOnly: self.http_only.unwrap_or_default(),
52            expiry: self.expiry.map(Date),
53            sameSite: self.same_site,
54        }
55    }
56}
57
58impl TryFrom<WebDriverCookie> for Cookie<'static> {
59    type Error = error::CmdError;
60
61    fn try_from(webdriver_cookie: WebDriverCookie) -> Result<Self, Self::Error> {
62        let mut cookie = cookie::Cookie::new(webdriver_cookie.name, webdriver_cookie.value);
63
64        if let Some(path) = webdriver_cookie.path {
65            cookie.set_path(path);
66        }
67
68        if let Some(domain) = webdriver_cookie.domain {
69            cookie.set_domain(domain);
70        }
71
72        if let Some(secure) = webdriver_cookie.secure {
73            cookie.set_secure(secure);
74        }
75
76        if let Some(http_only) = webdriver_cookie.http_only {
77            cookie.set_http_only(http_only);
78        }
79
80        if let Some(expiry) = webdriver_cookie.expiry {
81            let dt = OffsetDateTime::from_unix_timestamp(expiry as i64).ok();
82            cookie.set_expires(dt);
83        }
84
85        if let Some(same_site) = webdriver_cookie.same_site {
86            cookie.set_same_site(match &same_site {
87                x if x.eq_ignore_ascii_case("strict") => SameSite::Strict,
88                x if x.eq_ignore_ascii_case("lax") => SameSite::Lax,
89                x if x.eq_ignore_ascii_case("none") => SameSite::None,
90                _ => {
91                    return Err(error::CmdError::InvalidArgument(
92                        "same_site".to_string(),
93                        same_site,
94                    ))
95                }
96            });
97        }
98
99        Ok(cookie)
100    }
101}
102
103impl<'a> From<Cookie<'a>> for WebDriverCookie {
104    fn from(cookie: Cookie<'a>) -> Self {
105        let name = cookie.name().to_string();
106        let value = cookie.value().to_string();
107        let path = cookie.path().map(String::from);
108        let domain = cookie.domain().map(String::from);
109        let secure = cookie.secure();
110        let http_only = cookie.http_only();
111        let expiry = cookie
112            .expires()
113            .and_then(|e| e.datetime().map(|dt| dt.unix_timestamp() as u64));
114        let same_site = Some(match cookie.same_site() {
115            Some(x) => match x {
116                SameSite::Strict => "Strict".to_string(),
117                SameSite::Lax => "Lax".to_string(),
118                SameSite::None => "None".to_string(),
119            },
120            None => "None".to_string(),
121        });
122
123        Self {
124            name,
125            value,
126            path,
127            domain,
128            secure,
129            http_only,
130            expiry,
131            same_site,
132        }
133    }
134}
135
136/// [Cookies](https://www.w3.org/TR/webdriver1/#cookies)
137impl Client {
138    /// Get all cookies associated with the current document.
139    ///
140    /// See [16.1 Get All Cookies](https://www.w3.org/TR/webdriver1/#get-all-cookies) of the
141    /// WebDriver standard.
142    pub async fn get_all_cookies(&self) -> Result<Vec<Cookie<'static>>, error::CmdError> {
143        let resp = self.issue(WebDriverCommand::GetCookies).await?;
144
145        let webdriver_cookies: Vec<WebDriverCookie> = serde_json::from_value(resp)?;
146        webdriver_cookies
147            .into_iter()
148            .map(|raw_cookie| raw_cookie.try_into())
149            .collect()
150    }
151
152    /// Get a single named cookie associated with the current document.
153    ///
154    /// See [16.2 Get Named Cookie](https://www.w3.org/TR/webdriver1/#get-named-cookie) of the
155    /// WebDriver standard.
156    pub async fn get_named_cookie(&self, name: &str) -> Result<Cookie<'static>, error::CmdError> {
157        let resp = self
158            .issue(WebDriverCommand::GetNamedCookie(name.to_string()))
159            .await?;
160        let webdriver_cookie: WebDriverCookie = serde_json::from_value(resp)?;
161        webdriver_cookie.try_into()
162    }
163
164    /// Add the specified cookie.
165    ///
166    /// See [16.3 Add Cookie](https://www.w3.org/TR/webdriver1/#add-cookie) of the
167    /// WebDriver standard.
168    pub async fn add_cookie(&self, cookie: Cookie<'static>) -> Result<(), error::CmdError> {
169        let webdriver_cookie: WebDriverCookie = cookie.into();
170        self.issue(WebDriverCommand::AddCookie(webdriver_cookie.into_params()))
171            .await?;
172        Ok(())
173    }
174
175    /// Delete a single cookie from the current document.
176    ///
177    /// See [16.4 Delete Cookie](https://www.w3.org/TR/webdriver1/#delete-cookie) of the
178    /// WebDriver standard.
179    pub async fn delete_cookie(&self, name: &str) -> Result<(), error::CmdError> {
180        self.issue(WebDriverCommand::DeleteCookie(name.to_string()))
181            .await
182            .map(|_| ())
183    }
184
185    /// Delete all cookies from the current document.
186    ///
187    /// See [16.5 Delete All Cookies](https://www.w3.org/TR/webdriver1/#delete-all-cookies) of the
188    /// WebDriver standard.
189    pub async fn delete_all_cookies(&self) -> Result<(), error::CmdError> {
190        self.issue(WebDriverCommand::DeleteCookies)
191            .await
192            .map(|_| ())
193    }
194}