| 1 | /* |
| 2 | * Copyright (C) 2017 Igalia S.L. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| 17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| 23 | * THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "CommandResult.h" |
| 28 | |
| 29 | namespace WebDriver { |
| 30 | |
| 31 | // These error codes are specified in JSON-RPC 2.0, Section 5.1. |
| 32 | enum ProtocolErrorCode { |
| 33 | ParseError = -32700, |
| 34 | InvalidRequest = -32600, |
| 35 | MethodNotFound = -32601, |
| 36 | InvalidParams = -32602, |
| 37 | InternalError = -32603, |
| 38 | ServerError = -32000 |
| 39 | }; |
| 40 | |
| 41 | CommandResult::CommandResult(RefPtr<JSON::Value>&& result, Optional<ErrorCode> errorCode) |
| 42 | : m_errorCode(errorCode) |
| 43 | { |
| 44 | if (!m_errorCode) { |
| 45 | m_result = WTFMove(result); |
| 46 | return; |
| 47 | } |
| 48 | |
| 49 | if (!result) |
| 50 | return; |
| 51 | |
| 52 | RefPtr<JSON::Object> errorObject; |
| 53 | if (!result->asObject(errorObject)) |
| 54 | return; |
| 55 | |
| 56 | int error; |
| 57 | if (!errorObject->getInteger("code" , error)) |
| 58 | return; |
| 59 | String errorMessage; |
| 60 | if (!errorObject->getString("message" , errorMessage)) |
| 61 | return; |
| 62 | |
| 63 | switch (error) { |
| 64 | case ProtocolErrorCode::ParseError: |
| 65 | case ProtocolErrorCode::InvalidRequest: |
| 66 | case ProtocolErrorCode::MethodNotFound: |
| 67 | case ProtocolErrorCode::InvalidParams: |
| 68 | case ProtocolErrorCode::InternalError: |
| 69 | m_errorCode = ErrorCode::UnknownError; |
| 70 | m_errorMessage = errorMessage; |
| 71 | break; |
| 72 | case ProtocolErrorCode::ServerError: { |
| 73 | String errorName; |
| 74 | auto position = errorMessage.find(';'); |
| 75 | if (position != notFound) { |
| 76 | errorName = errorMessage.substring(0, position); |
| 77 | m_errorMessage = errorMessage.substring(position + 1); |
| 78 | } else |
| 79 | errorName = errorMessage; |
| 80 | |
| 81 | if (errorName == "WindowNotFound" ) |
| 82 | m_errorCode = ErrorCode::NoSuchWindow; |
| 83 | else if (errorName == "FrameNotFound" ) |
| 84 | m_errorCode = ErrorCode::NoSuchFrame; |
| 85 | else if (errorName == "NotImplemented" ) |
| 86 | m_errorCode = ErrorCode::UnsupportedOperation; |
| 87 | else if (errorName == "ElementNotInteractable" ) |
| 88 | m_errorCode = ErrorCode::ElementNotInteractable; |
| 89 | else if (errorName == "JavaScriptError" ) |
| 90 | m_errorCode = ErrorCode::JavascriptError; |
| 91 | else if (errorName == "JavaScriptTimeout" ) |
| 92 | m_errorCode = ErrorCode::ScriptTimeout; |
| 93 | else if (errorName == "NodeNotFound" ) |
| 94 | m_errorCode = ErrorCode::StaleElementReference; |
| 95 | else if (errorName == "MissingParameter" || errorName == "InvalidParameter" ) |
| 96 | m_errorCode = ErrorCode::InvalidArgument; |
| 97 | else if (errorName == "InvalidElementState" ) |
| 98 | m_errorCode = ErrorCode::InvalidElementState; |
| 99 | else if (errorName == "InvalidSelector" ) |
| 100 | m_errorCode = ErrorCode::InvalidSelector; |
| 101 | else if (errorName == "Timeout" ) |
| 102 | m_errorCode = ErrorCode::Timeout; |
| 103 | else if (errorName == "NoJavaScriptDialog" ) |
| 104 | m_errorCode = ErrorCode::NoSuchAlert; |
| 105 | else if (errorName == "ElementNotSelectable" ) |
| 106 | m_errorCode = ErrorCode::ElementNotSelectable; |
| 107 | else if (errorName == "ScreenshotError" ) |
| 108 | m_errorCode = ErrorCode::UnableToCaptureScreen; |
| 109 | else if (errorName == "UnexpectedAlertOpen" ) |
| 110 | m_errorCode = ErrorCode::UnexpectedAlertOpen; |
| 111 | else if (errorName == "TargetOutOfBounds" ) |
| 112 | m_errorCode = ErrorCode::MoveTargetOutOfBounds; |
| 113 | |
| 114 | break; |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | CommandResult::CommandResult(ErrorCode errorCode, Optional<String> errorMessage) |
| 120 | : m_errorCode(errorCode) |
| 121 | , m_errorMessage(errorMessage) |
| 122 | { |
| 123 | } |
| 124 | |
| 125 | unsigned CommandResult::httpStatusCode() const |
| 126 | { |
| 127 | if (!m_errorCode) |
| 128 | return 200; |
| 129 | |
| 130 | // ยง6.6 Handling Errors. |
| 131 | // https://www.w3.org/TR/webdriver/#handling-errors |
| 132 | switch (m_errorCode.value()) { |
| 133 | case ErrorCode::ElementClickIntercepted: |
| 134 | case ErrorCode::ElementNotSelectable: |
| 135 | case ErrorCode::ElementNotInteractable: |
| 136 | case ErrorCode::InvalidArgument: |
| 137 | case ErrorCode::InvalidElementState: |
| 138 | case ErrorCode::InvalidSelector: |
| 139 | return 400; |
| 140 | case ErrorCode::NoSuchAlert: |
| 141 | case ErrorCode::NoSuchCookie: |
| 142 | case ErrorCode::NoSuchElement: |
| 143 | case ErrorCode::NoSuchFrame: |
| 144 | case ErrorCode::NoSuchWindow: |
| 145 | case ErrorCode::StaleElementReference: |
| 146 | case ErrorCode::InvalidSessionID: |
| 147 | case ErrorCode::UnknownCommand: |
| 148 | return 404; |
| 149 | case ErrorCode::ScriptTimeout: |
| 150 | case ErrorCode::Timeout: |
| 151 | return 408; |
| 152 | case ErrorCode::JavascriptError: |
| 153 | case ErrorCode::MoveTargetOutOfBounds: |
| 154 | case ErrorCode::SessionNotCreated: |
| 155 | case ErrorCode::UnableToCaptureScreen: |
| 156 | case ErrorCode::UnexpectedAlertOpen: |
| 157 | case ErrorCode::UnknownError: |
| 158 | case ErrorCode::UnsupportedOperation: |
| 159 | return 500; |
| 160 | } |
| 161 | |
| 162 | ASSERT_NOT_REACHED(); |
| 163 | return 200; |
| 164 | } |
| 165 | |
| 166 | String CommandResult::errorString() const |
| 167 | { |
| 168 | ASSERT(isError()); |
| 169 | |
| 170 | switch (m_errorCode.value()) { |
| 171 | case ErrorCode::ElementClickIntercepted: |
| 172 | return "element click intercepted"_s ; |
| 173 | case ErrorCode::ElementNotSelectable: |
| 174 | return "element not selectable"_s ; |
| 175 | case ErrorCode::ElementNotInteractable: |
| 176 | return "element not interactable"_s ; |
| 177 | case ErrorCode::InvalidArgument: |
| 178 | return "invalid argument"_s ; |
| 179 | case ErrorCode::InvalidElementState: |
| 180 | return "invalid element state"_s ; |
| 181 | case ErrorCode::InvalidSelector: |
| 182 | return "invalid selector"_s ; |
| 183 | case ErrorCode::InvalidSessionID: |
| 184 | return "invalid session id"_s ; |
| 185 | case ErrorCode::JavascriptError: |
| 186 | return "javascript error"_s ; |
| 187 | case ErrorCode::NoSuchAlert: |
| 188 | return "no such alert"_s ; |
| 189 | case ErrorCode::NoSuchCookie: |
| 190 | return "no such cookie"_s ; |
| 191 | case ErrorCode::NoSuchElement: |
| 192 | return "no such element"_s ; |
| 193 | case ErrorCode::NoSuchFrame: |
| 194 | return "no such frame"_s ; |
| 195 | case ErrorCode::NoSuchWindow: |
| 196 | return "no such window"_s ; |
| 197 | case ErrorCode::ScriptTimeout: |
| 198 | return "script timeout"_s ; |
| 199 | case ErrorCode::SessionNotCreated: |
| 200 | return "session not created"_s ; |
| 201 | case ErrorCode::StaleElementReference: |
| 202 | return "stale element reference"_s ; |
| 203 | case ErrorCode::Timeout: |
| 204 | return "timeout"_s ; |
| 205 | case ErrorCode::UnableToCaptureScreen: |
| 206 | return "unable to capture screen"_s ; |
| 207 | case ErrorCode::MoveTargetOutOfBounds: |
| 208 | return "move target out of bounds"_s ; |
| 209 | case ErrorCode::UnexpectedAlertOpen: |
| 210 | return "unexpected alert open"_s ; |
| 211 | case ErrorCode::UnknownCommand: |
| 212 | return "unknown command"_s ; |
| 213 | case ErrorCode::UnknownError: |
| 214 | return "unknown error"_s ; |
| 215 | case ErrorCode::UnsupportedOperation: |
| 216 | return "unsupported operation"_s ; |
| 217 | } |
| 218 | |
| 219 | ASSERT_NOT_REACHED(); |
| 220 | return emptyString(); |
| 221 | } |
| 222 | |
| 223 | } // namespace WebDriver |
| 224 | |