tor-browser

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

error.rs (17686B)


      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 base64::DecodeError;
      6 use http::StatusCode;
      7 use serde::de::{Deserialize, Deserializer};
      8 use serde::ser::{Serialize, Serializer};
      9 use serde_json::Value;
     10 use std::borrow::Cow;
     11 use std::collections::BTreeMap;
     12 use std::error;
     13 use std::io;
     14 use thiserror::Error;
     15 
     16 #[derive(Debug, PartialEq)]
     17 pub enum ErrorStatus {
     18    /// The [element]'s [ShadowRoot] is not attached to the active document,
     19    /// or the reference is stale
     20    /// [element]: ../common/struct.WebElement.html
     21    /// [ShadowRoot]: ../common/struct.ShadowRoot.html
     22    DetachedShadowRoot,
     23 
     24    /// The [`ElementClick`] command could not be completed because the
     25    /// [element] receiving the events is obscuring the element that was
     26    /// requested clicked.
     27    ///
     28    /// [`ElementClick`]:
     29    /// ../command/enum.WebDriverCommand.html#variant.ElementClick
     30    /// [element]: ../common/struct.WebElement.html
     31    ElementClickIntercepted,
     32 
     33    /// A [command] could not be completed because the element is not pointer-
     34    /// or keyboard interactable.
     35    ///
     36    /// [command]: ../command/index.html
     37    ElementNotInteractable,
     38 
     39    /// An attempt was made to select an [element] that cannot be selected.
     40    ///
     41    /// [element]: ../common/struct.WebElement.html
     42    ElementNotSelectable,
     43 
     44    /// Navigation caused the user agent to hit a certificate warning, which is
     45    /// usually the result of an expired or invalid TLS certificate.
     46    InsecureCertificate,
     47 
     48    /// The arguments passed to a [command] are either invalid or malformed.
     49    ///
     50    /// [command]: ../command/index.html
     51    InvalidArgument,
     52 
     53    /// An illegal attempt was made to set a cookie under a different domain
     54    /// than the current page.
     55    InvalidCookieDomain,
     56 
     57    /// The coordinates provided to an interactions operation are invalid.
     58    InvalidCoordinates,
     59 
     60    /// A [command] could not be completed because the element is an invalid
     61    /// state, e.g. attempting to click an element that is no longer attached
     62    /// to the document.
     63    ///
     64    /// [command]: ../command/index.html
     65    InvalidElementState,
     66 
     67    /// Argument was an invalid selector.
     68    InvalidSelector,
     69 
     70    /// Occurs if the given session ID is not in the list of active sessions,
     71    /// meaning the session either does not exist or that it’s not active.
     72    InvalidSessionId,
     73 
     74    /// An error occurred while executing JavaScript supplied by the user.
     75    JavascriptError,
     76 
     77    /// The target for mouse interaction is not in the browser’s viewport and
     78    /// cannot be brought into that viewport.
     79    MoveTargetOutOfBounds,
     80 
     81    /// An attempt was made to operate on a modal dialogue when one was not
     82    /// open.
     83    NoSuchAlert,
     84 
     85    /// No cookie matching the given path name was found amongst the associated
     86    /// cookies of the current browsing context’s active document.
     87    NoSuchCookie,
     88 
     89    /// An [element] could not be located on the page using the given search
     90    /// parameters.
     91    ///
     92    /// [element]: ../common/struct.WebElement.html
     93    NoSuchElement,
     94 
     95    /// A [command] to switch to a frame could not be satisfied because the
     96    /// frame could not be found.
     97    ///
     98    /// [command]: ../command/index.html
     99    NoSuchFrame,
    100 
    101    /// An [element]'s [ShadowRoot] was not found attached to the element.
    102    ///
    103    /// [element]: ../common/struct.WebElement.html
    104    /// [ShadowRoot]: ../common/struct.ShadowRoot.html
    105    NoSuchShadowRoot,
    106 
    107    /// A [command] to switch to a window could not be satisfied because the
    108    /// window could not be found.
    109    ///
    110    /// [command]: ../command/index.html
    111    NoSuchWindow,
    112 
    113    /// A script did not complete before its timeout expired.
    114    ScriptTimeout,
    115 
    116    /// A new session could not be created.
    117    SessionNotCreated,
    118 
    119    /// A [command] failed because the referenced [element] is no longer
    120    /// attached to the DOM.
    121    ///
    122    /// [command]: ../command/index.html
    123    /// [element]: ../common/struct.WebElement.html
    124    StaleElementReference,
    125 
    126    /// An operation did not complete before its timeout expired.
    127    Timeout,
    128 
    129    /// A screen capture was made impossible.
    130    UnableToCaptureScreen,
    131 
    132    /// Setting the cookie’s value could not be done.
    133    UnableToSetCookie,
    134 
    135    /// A modal dialogue was open, blocking this operation.
    136    UnexpectedAlertOpen,
    137 
    138    /// The requested command could not be executed because it does not exist.
    139    UnknownCommand,
    140 
    141    /// An unknown error occurred in the remote end whilst processing the
    142    /// [command].
    143    ///
    144    /// [command]: ../command/index.html
    145    UnknownError,
    146 
    147    /// The requested [command] matched a known endpoint, but did not match a
    148    /// method for that endpoint.
    149    ///
    150    /// [command]: ../command/index.html
    151    UnknownMethod,
    152 
    153    /// Indicates that a [command] that should have executed properly is not
    154    /// currently supported.
    155    UnsupportedOperation,
    156 }
    157 
    158 impl Serialize for ErrorStatus {
    159    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    160    where
    161        S: Serializer,
    162    {
    163        self.error_code().serialize(serializer)
    164    }
    165 }
    166 
    167 impl<'de> Deserialize<'de> for ErrorStatus {
    168    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    169    where
    170        D: Deserializer<'de>,
    171    {
    172        let error_string = String::deserialize(deserializer)?;
    173        Ok(ErrorStatus::from(error_string))
    174    }
    175 }
    176 
    177 impl ErrorStatus {
    178    /// Returns the string serialisation of the error type.
    179    pub fn error_code(&self) -> &'static str {
    180        use self::ErrorStatus::*;
    181        match *self {
    182            DetachedShadowRoot => "detached shadow root",
    183            ElementClickIntercepted => "element click intercepted",
    184            ElementNotInteractable => "element not interactable",
    185            ElementNotSelectable => "element not selectable",
    186            InsecureCertificate => "insecure certificate",
    187            InvalidArgument => "invalid argument",
    188            InvalidCookieDomain => "invalid cookie domain",
    189            InvalidCoordinates => "invalid coordinates",
    190            InvalidElementState => "invalid element state",
    191            InvalidSelector => "invalid selector",
    192            InvalidSessionId => "invalid session id",
    193            JavascriptError => "javascript error",
    194            MoveTargetOutOfBounds => "move target out of bounds",
    195            NoSuchAlert => "no such alert",
    196            NoSuchCookie => "no such cookie",
    197            NoSuchElement => "no such element",
    198            NoSuchFrame => "no such frame",
    199            NoSuchShadowRoot => "no such shadow root",
    200            NoSuchWindow => "no such window",
    201            ScriptTimeout => "script timeout",
    202            SessionNotCreated => "session not created",
    203            StaleElementReference => "stale element reference",
    204            Timeout => "timeout",
    205            UnableToCaptureScreen => "unable to capture screen",
    206            UnableToSetCookie => "unable to set cookie",
    207            UnexpectedAlertOpen => "unexpected alert open",
    208            UnknownError => "unknown error",
    209            UnknownMethod => "unknown method",
    210            UnknownCommand => "unknown command",
    211            UnsupportedOperation => "unsupported operation",
    212        }
    213    }
    214 
    215    /// Returns the correct HTTP status code associated with the error type.
    216    pub fn http_status(&self) -> StatusCode {
    217        use self::ErrorStatus::*;
    218        match *self {
    219            DetachedShadowRoot => StatusCode::NOT_FOUND,
    220            ElementClickIntercepted => StatusCode::BAD_REQUEST,
    221            ElementNotInteractable => StatusCode::BAD_REQUEST,
    222            ElementNotSelectable => StatusCode::BAD_REQUEST,
    223            InsecureCertificate => StatusCode::BAD_REQUEST,
    224            InvalidArgument => StatusCode::BAD_REQUEST,
    225            InvalidCookieDomain => StatusCode::BAD_REQUEST,
    226            InvalidCoordinates => StatusCode::BAD_REQUEST,
    227            InvalidElementState => StatusCode::BAD_REQUEST,
    228            InvalidSelector => StatusCode::BAD_REQUEST,
    229            InvalidSessionId => StatusCode::NOT_FOUND,
    230            JavascriptError => StatusCode::INTERNAL_SERVER_ERROR,
    231            MoveTargetOutOfBounds => StatusCode::INTERNAL_SERVER_ERROR,
    232            NoSuchAlert => StatusCode::NOT_FOUND,
    233            NoSuchCookie => StatusCode::NOT_FOUND,
    234            NoSuchElement => StatusCode::NOT_FOUND,
    235            NoSuchFrame => StatusCode::NOT_FOUND,
    236            NoSuchShadowRoot => StatusCode::NOT_FOUND,
    237            NoSuchWindow => StatusCode::NOT_FOUND,
    238            ScriptTimeout => StatusCode::INTERNAL_SERVER_ERROR,
    239            SessionNotCreated => StatusCode::INTERNAL_SERVER_ERROR,
    240            StaleElementReference => StatusCode::NOT_FOUND,
    241            Timeout => StatusCode::INTERNAL_SERVER_ERROR,
    242            UnableToCaptureScreen => StatusCode::BAD_REQUEST,
    243            UnableToSetCookie => StatusCode::INTERNAL_SERVER_ERROR,
    244            UnexpectedAlertOpen => StatusCode::INTERNAL_SERVER_ERROR,
    245            UnknownCommand => StatusCode::NOT_FOUND,
    246            UnknownError => StatusCode::INTERNAL_SERVER_ERROR,
    247            UnknownMethod => StatusCode::METHOD_NOT_ALLOWED,
    248            UnsupportedOperation => StatusCode::INTERNAL_SERVER_ERROR,
    249        }
    250    }
    251 }
    252 
    253 /// Deserialises error type from string.
    254 impl From<String> for ErrorStatus {
    255    fn from(s: String) -> ErrorStatus {
    256        use self::ErrorStatus::*;
    257        match &*s {
    258            "detached shadow root" => DetachedShadowRoot,
    259            "element click intercepted" => ElementClickIntercepted,
    260            "element not interactable" | "element not visible" => ElementNotInteractable,
    261            "element not selectable" => ElementNotSelectable,
    262            "insecure certificate" => InsecureCertificate,
    263            "invalid argument" => InvalidArgument,
    264            "invalid cookie domain" => InvalidCookieDomain,
    265            "invalid coordinates" | "invalid element coordinates" => InvalidCoordinates,
    266            "invalid element state" => InvalidElementState,
    267            "invalid selector" => InvalidSelector,
    268            "invalid session id" => InvalidSessionId,
    269            "javascript error" => JavascriptError,
    270            "move target out of bounds" => MoveTargetOutOfBounds,
    271            "no such alert" => NoSuchAlert,
    272            "no such element" => NoSuchElement,
    273            "no such frame" => NoSuchFrame,
    274            "no such shadow root" => NoSuchShadowRoot,
    275            "no such window" => NoSuchWindow,
    276            "script timeout" => ScriptTimeout,
    277            "session not created" => SessionNotCreated,
    278            "stale element reference" => StaleElementReference,
    279            "timeout" => Timeout,
    280            "unable to capture screen" => UnableToCaptureScreen,
    281            "unable to set cookie" => UnableToSetCookie,
    282            "unexpected alert open" => UnexpectedAlertOpen,
    283            "unknown command" => UnknownCommand,
    284            "unknown error" => UnknownError,
    285            "unsupported operation" => UnsupportedOperation,
    286            _ => UnknownError,
    287        }
    288    }
    289 }
    290 
    291 pub type WebDriverResult<T> = Result<T, WebDriverError>;
    292 
    293 #[derive(Debug, PartialEq, Serialize, Error)]
    294 #[serde(remote = "Self")]
    295 #[error("{}", .error.error_code())]
    296 pub struct WebDriverError {
    297    pub error: ErrorStatus,
    298    pub message: Cow<'static, str>,
    299    #[serde(rename = "stacktrace")]
    300    pub stack: Cow<'static, str>,
    301    #[serde(skip_serializing_if = "Option::is_none")]
    302    pub data: Option<BTreeMap<Cow<'static, str>, Value>>,
    303    #[serde(skip)]
    304    pub delete_session: bool,
    305 }
    306 
    307 impl Serialize for WebDriverError {
    308    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    309    where
    310        S: Serializer,
    311    {
    312        #[derive(Serialize)]
    313        struct Wrapper<'a> {
    314            #[serde(with = "WebDriverError")]
    315            value: &'a WebDriverError,
    316        }
    317 
    318        Wrapper { value: self }.serialize(serializer)
    319    }
    320 }
    321 
    322 impl WebDriverError {
    323    pub fn new<S>(error: ErrorStatus, message: S) -> WebDriverError
    324    where
    325        S: Into<Cow<'static, str>>,
    326    {
    327        WebDriverError {
    328            error,
    329            message: message.into(),
    330            data: None,
    331            stack: "".into(),
    332            delete_session: false,
    333        }
    334    }
    335 
    336    pub fn new_with_data<S>(
    337        error: ErrorStatus,
    338        message: S,
    339        data: Option<BTreeMap<Cow<'static, str>, Value>>,
    340        stacktrace: Option<S>,
    341    ) -> WebDriverError
    342    where
    343        S: Into<Cow<'static, str>>,
    344    {
    345        WebDriverError {
    346            error,
    347            message: message.into(),
    348            data,
    349            stack: stacktrace.map_or_else(|| "".into(), Into::into),
    350            delete_session: false,
    351        }
    352    }
    353 
    354    pub fn error_code(&self) -> &'static str {
    355        self.error.error_code()
    356    }
    357 
    358    pub fn http_status(&self) -> StatusCode {
    359        self.error.http_status()
    360    }
    361 }
    362 
    363 impl From<serde_json::Error> for WebDriverError {
    364    fn from(err: serde_json::Error) -> WebDriverError {
    365        WebDriverError::new(ErrorStatus::InvalidArgument, err.to_string())
    366    }
    367 }
    368 
    369 impl From<io::Error> for WebDriverError {
    370    fn from(err: io::Error) -> WebDriverError {
    371        WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
    372    }
    373 }
    374 
    375 impl From<DecodeError> for WebDriverError {
    376    fn from(err: DecodeError) -> WebDriverError {
    377        WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
    378    }
    379 }
    380 
    381 impl From<Box<dyn error::Error>> for WebDriverError {
    382    fn from(err: Box<dyn error::Error>) -> WebDriverError {
    383        WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
    384    }
    385 }
    386 
    387 #[cfg(test)]
    388 mod tests {
    389    use serde_json::json;
    390 
    391    use super::*;
    392    use crate::test::assert_ser;
    393 
    394    #[test]
    395    fn test_error_status_serialization() {
    396        assert_ser(&ErrorStatus::UnknownError, json!("unknown error"));
    397    }
    398 
    399    #[test]
    400    fn test_webdriver_error_json() {
    401        let data: Option<BTreeMap<Cow<'static, str>, Value>> = Some(
    402            [
    403                (Cow::Borrowed("foo"), Value::from(42)),
    404                (Cow::Borrowed("bar"), Value::from(vec![true])),
    405            ]
    406            .into_iter()
    407            .collect(),
    408        );
    409 
    410        let json = json!({"value": {
    411            "error": "unknown error",
    412            "message": "serialized error",
    413            "stacktrace": "foo\nbar",
    414            "data": {
    415                "foo": 42,
    416                "bar": [
    417                    true
    418                ]
    419            }
    420        }});
    421 
    422        let error = WebDriverError {
    423            error: ErrorStatus::UnknownError,
    424            message: "serialized error".into(),
    425            stack: "foo\nbar".into(),
    426            data,
    427            delete_session: true,
    428        };
    429 
    430        assert_ser(&error, json);
    431    }
    432 
    433    #[test]
    434    fn test_webdriver_error_new() {
    435        let json = json!({"value": {
    436            "error": "unknown error",
    437            "message": "error with default stack",
    438            "stacktrace": "",
    439        }});
    440 
    441        let error = WebDriverError::new::<Cow<'static, str>>(
    442            ErrorStatus::UnknownError,
    443            "error with default stack".into(),
    444        );
    445        assert_ser(&error, json);
    446    }
    447 
    448    #[test]
    449    fn test_webdriver_error_new_with_data_serialization() {
    450        let mut data_map = BTreeMap::new();
    451        data_map.insert("foo".into(), json!(42));
    452        data_map.insert("bar".into(), json!(vec!(true)));
    453 
    454        let error = WebDriverError::new_with_data(
    455            ErrorStatus::UnknownError,
    456            "serialization test",
    457            Some(data_map.clone()),
    458            Some("foo\nbar"),
    459        );
    460 
    461        let serialized = serde_json::to_string(&error).unwrap();
    462        let expected_json = json!({"value": {
    463            "error": "unknown error",
    464            "message": "serialization test",
    465            "stacktrace": "foo\nbar",
    466            "data": {
    467                "foo": 42,
    468                "bar": [true]
    469            }
    470        }});
    471 
    472        assert_eq!(
    473            serde_json::from_str::<Value>(&serialized).unwrap(),
    474            expected_json
    475        );
    476    }
    477 
    478    #[test]
    479    fn test_webdriver_error_new_with_data_no_data_no_stacktrace() {
    480        let error = WebDriverError::new_with_data(
    481            ErrorStatus::UnknownError,
    482            "error with no data and stack",
    483            None,
    484            None,
    485        );
    486 
    487        assert_eq!(error.error, ErrorStatus::UnknownError);
    488        assert_eq!(error.message, "error with no data and stack");
    489        assert_eq!(error.stack, "");
    490        assert_eq!(error.data, None);
    491    }
    492 
    493    #[test]
    494    fn test_webdriver_error_new_with_data_no_stacktrace() {
    495        let mut data_map = BTreeMap::new();
    496        data_map.insert("foo".into(), json!(42));
    497 
    498        let error = WebDriverError::new_with_data(
    499            ErrorStatus::UnknownError,
    500            "error with no stack",
    501            Some(data_map.clone()),
    502            None,
    503        );
    504 
    505        assert_eq!(error.error, ErrorStatus::UnknownError);
    506        assert_eq!(error.message, "error with no stack");
    507        assert_eq!(error.stack, "");
    508        assert_eq!(error.data, Some(data_map));
    509    }
    510 
    511    #[test]
    512    fn test_webdriver_error_new_with_data_no_data() {
    513        let error = WebDriverError::new_with_data(
    514            ErrorStatus::UnknownError,
    515            "error with no data",
    516            None,
    517            Some("foo\nbar"),
    518        );
    519 
    520        assert_eq!(error.error, ErrorStatus::UnknownError);
    521        assert_eq!(error.message, "error with no data");
    522        assert_eq!(error.stack, "foo\nbar");
    523        assert_eq!(error.data, None);
    524    }
    525 }