// Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The SFC licenses this file // to you under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "response.h" #include "errorcodes.h" #include "logging.h" namespace webdriver { Response::Response(void) : error_(""), value_(Json::Value::null), additional_data_(Json::Value::null) { } Response::~Response(void) { } void Response::Deserialize(const std::string& json) { LOG(TRACE) << "Entering Response::Deserialize"; Json::Value response_object; std::string parse_errors; std::stringstream json_stream; json_stream.str(json); Json::parseFromStream(Json::CharReaderBuilder(), json_stream, &response_object, &parse_errors); Json::Value value_object; if (response_object.isMember("value")) { value_object = response_object["value"]; if (value_object.isObject() && value_object.isMember("error")) { this->error_ = value_object["error"].asString(); this->value_ = value_object["message"].asString(); if (value_object.isMember("data")) { this->additional_data_ = value_object["data"]; } } else { this->error_ = ""; this->value_ = value_object; } } else { this->value_ = Json::Value::null; } } std::string Response::Serialize(void) { LOG(TRACE) << "Entering Response::Serialize"; Json::Value json_object; if (this->error_.size() > 0) { Json::Value error_object; error_object["error"] = this->error_; error_object["message"] = this->value_.asString(); error_object["stacktrace"] = ""; if (!this->value_.isNull() && !this->additional_data_.isNull()) { error_object["data"] = this->additional_data_; } json_object["value"] = error_object; } else { json_object["value"] = this->value_; } Json::StreamWriterBuilder writer; std::string output(Json::writeString(writer, json_object)); return output; } void Response::SetSuccessResponse(const Json::Value& response_value) { LOG(TRACE) << "Entering Response::SetSuccessResponse"; this->SetResponse("", response_value); } void Response::SetResponse(const std::string& error, const Json::Value& response_value) { LOG(TRACE) << "Entering Response::SetResponse"; this->error_ = error; this->value_ = response_value; } void Response::SetErrorResponse(const std::string& error, const std::string& message) { LOG(TRACE) << "Entering Response::SetErrorResponse"; this->SetResponse(error, message); } void Response::SetErrorResponse(const int status_code, const std::string& message) { LOG(TRACE) << "Entering Response::SetErrorResponse"; LOG(WARN) << "Error response has status code " << status_code << " and message '" << message << "' message"; this->SetErrorResponse(ConvertErrorCode(status_code), message); } void Response::AddAdditionalData(const std::string& data_name, const std::string& data_value) { LOG(TRACE) << "Entering Response::AddAdditionalData"; if (this->additional_data_.isNull()) { Json::Value new_data; this->additional_data_ = new_data; } this->additional_data_[data_name] = data_value; } std::string Response::GetSessionId(void) { if (this->error_.size() == 0) { return this->value_.get("sessionId", "").asString(); } return ""; } int Response::GetHttpResponseCode(void) { int response_code = 200; if (this->error_ == ERROR_ELEMENT_CLICK_INTERCEPTED || this->error_ == ERROR_ELEMENT_NOT_SELECTABLE || this->error_ == ERROR_ELEMENT_NOT_INTERACTABLE || this->error_ == ERROR_INSECURE_CERTIFICATE || this->error_ == ERROR_INVALID_ARGUMENT || this->error_ == ERROR_INVALID_COOKIE_DOMAIN || this->error_ == ERROR_INVALID_COORDINATES || this->error_ == ERROR_INVALID_ELEMENT_STATE || this->error_ == ERROR_INVALID_SELECTOR) { response_code = 400; } else if (this->error_ == ERROR_INVALID_SESSION_ID || this->error_ == ERROR_NO_SUCH_COOKIE || this->error_ == ERROR_NO_SUCH_ALERT || this->error_ == ERROR_NO_SUCH_ELEMENT || this->error_ == ERROR_NO_SUCH_FRAME || this->error_ == ERROR_NO_SUCH_WINDOW || this->error_ == ERROR_STALE_ELEMENT_REFERENCE || this->error_ == ERROR_UNKNOWN_COMMAND) { response_code = 404; } else if (this->error_ == ERROR_UNKNOWN_METHOD) { response_code = 405; } else if (this->error_ == ERROR_JAVASCRIPT_ERROR || this->error_ == ERROR_MOVE_TARGET_OUT_OF_BOUNDS || this->error_ == ERROR_SCRIPT_TIMEOUT || this->error_ == ERROR_SESSION_NOT_CREATED || this->error_ == ERROR_UNABLE_TO_SET_COOKIE || this->error_ == ERROR_UNABLE_TO_CAPTURE_SCREEN || this->error_ == ERROR_UNEXPECTED_ALERT_OPEN || this->error_ == ERROR_UNKNOWN_ERROR || this->error_ == ERROR_UNSUPPORTED_OPERATION || this->error_ == ERROR_WEBDRIVER_TIMEOUT) { response_code = 500; } else { response_code = 200; } return response_code; } std::string Response::ConvertErrorCode(const int error_code) { if (error_code == WD_SUCCESS) { return ""; } else if (error_code == ENOSUCHFRAME) { return ERROR_NO_SUCH_FRAME; } else if (error_code == ENOSUCHWINDOW) { return ERROR_NO_SUCH_WINDOW; } else if (error_code == EOBSOLETEELEMENT) { return ERROR_STALE_ELEMENT_REFERENCE; } else if (error_code == EINVALIDSELECTOR) { return ERROR_INVALID_SELECTOR; } else if (error_code == ENOSUCHALERT) { return ERROR_NO_SUCH_ALERT; } else if (error_code == EUNEXPECTEDALERTOPEN) { return ERROR_UNEXPECTED_ALERT_OPEN; } else if (error_code == ENOSUCHCOOKIE) { return ERROR_NO_SUCH_COOKIE; } else if (error_code == EELEMENTNOTENABLED) { return ERROR_INVALID_ELEMENT_STATE; } else if (error_code == EELEMENTNOTDISPLAYED) { return ERROR_ELEMENT_NOT_INTERACTABLE; } else if (error_code == EUNEXPECTEDJSERROR) { return ERROR_JAVASCRIPT_ERROR; } else if (error_code == EINVALIDCOOKIEDOMAIN) { return ERROR_INVALID_COOKIE_DOMAIN; } else if (error_code == ESCRIPTTIMEOUT) { return ERROR_SCRIPT_TIMEOUT; } else if (error_code == EMOVETARGETOUTOFBOUNDS) { return ERROR_MOVE_TARGET_OUT_OF_BOUNDS; } else if (error_code == EINVALIDARGUMENT) { return ERROR_INVALID_ARGUMENT; } else if (error_code == ENOSUCHELEMENT) { return ERROR_NO_SUCH_ELEMENT; } else if (error_code == EUNSUPPORTEDOPERATION) { return ERROR_UNSUPPORTED_OPERATION; } return ""; } // TODO: This method will be rendered unnecessary once all implementations // move to string status codes instead of integer status codes. This mapping // is not entirely correct; it's merely intended as a stopgap. int Response::ConvertStatusToCode(const std::string& status_string) { if (status_string == "success") { // Special case success to return early. return WD_SUCCESS; } if (status_string == "element not selectable") { return EELEMENTNOTSELECTED; } if (status_string == "element not visible") { return EELEMENTNOTDISPLAYED; } if (status_string == "invalid cookie domain") { return EINVALIDCOOKIEDOMAIN; } if (status_string == "invalid element coordinates") { return EINVALIDCOORDINATES; } if (status_string == "invalid element state") { return EELEMENTNOTENABLED; } if (status_string == "invalid selector") { return EINVALIDSELECTOR; } if (status_string == "javascript error") { return EUNEXPECTEDJSERROR; } if (status_string == "no such alert") { return ENOSUCHALERT; } if (status_string == "no such element") { return ENOSUCHELEMENT; } if (status_string == "no such frame") { return ENOSUCHFRAME; } if (status_string == "no such window") { return ENOSUCHWINDOW; } if (status_string == "script timeout") { return ESCRIPTTIMEOUT; } if (status_string == "stale element reference") { return EOBSOLETEELEMENT; } if (status_string == "timeout") { return ETIMEOUT; } if (status_string == "unable to set cookie") { return EUNABLETOSETCOOKIE; } if (status_string == "unexpected alert open") { return EUNEXPECTEDALERTOPEN; } if (status_string == "unknown command") { return ENOTIMPLEMENTED; } if (status_string == "unknown error") { return EUNHANDLEDERROR; } if (status_string == "unsupported operation") { return EUNSUPPORTEDOPERATION; } return EUNHANDLEDERROR; } } // namespace webdriver