| // Copyright 2011 Software Freedom Conservancy | |
| // Licensed 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 <regex> | |
| #include "server.h" | |
| #include "logging.h" | |
| #define SERVER_DEFAULT_PAGE "<html><head><title>WebDriver</title></head><body><p id='main'>This is the initial start page for the WebDriver server.</p></body></html>" | |
| #define HTML_CONTENT_TYPE "text/html" | |
| #define JSON_CONTENT_TYPE "application/json" | |
| namespace webdriver { | |
| Server::Server(const int port) { | |
| this->Initialize(port, "", "", ""); | |
| } | |
| Server::Server(const int port, const std::string& host) { | |
| this->Initialize(port, host, "", ""); | |
| } | |
| Server::Server(const int port, | |
| const std::string& host, | |
| const std::string& log_level, | |
| const std::string& log_file) { | |
| this->Initialize(port, host, log_level, log_file); | |
| } | |
| Server::~Server(void) { | |
| SessionMap::iterator it = this->sessions_.begin(); | |
| for (; it != this->sessions_.end(); ++it) { | |
| std::string session_id = it->first; | |
| this->ShutDownSession(session_id); | |
| } | |
| } | |
| void Server::Initialize(const int port, | |
| const std::string& host, | |
| const std::string& log_level, | |
| const std::string& log_file) { | |
| LOG::Level(log_level); | |
| LOG::File(log_file); | |
| LOG(INFO) << "Starting WebDriver server on port: '" << port << "' on host: '" << host << "'"; | |
| this->port_ = port; | |
| this->host_ = host; | |
| this->PopulateCommandRepository(); | |
| } | |
| void* Server::OnHttpEvent(enum mg_event event_raised, | |
| struct mg_connection* conn, | |
| const struct mg_request_info* request_info) { | |
| LOG(TRACE) << "Entering Server::OnHttpEvent"; | |
| // Mongoose calls method with the following events: | |
| // - MG_EVENT_LOG - on crying to log | |
| // - MG_NEW_REQUEST - on processing new HTTP request | |
| // - MG_HTTP_ERROR - on sending HTTP error | |
| // - MG_REQUEST_COMPLETE - on request processing is completed (in last version of code) | |
| int handler_result_code = 0; | |
| if (event_raised == MG_NEW_REQUEST) { | |
| handler_result_code = reinterpret_cast<Server*>(request_info->user_data)-> | |
| ProcessRequest(conn, request_info); | |
| } else if (event_raised == MG_EVENT_LOG) { | |
| LOG(WARN) << "Mongoose log event: " << request_info->log_message; | |
| } else if (event_raised == MG_HTTP_ERROR) { | |
| // do nothing due it will be reported as MG_EVENT_LOG with more info | |
| } | |
| return reinterpret_cast<void*>(handler_result_code); | |
| } | |
| bool Server::Start() { | |
| LOG(TRACE) << "Entering Server::Start"; | |
| std::string port_format_string = "%s:%d"; | |
| if (this->host_.size() == 0) { | |
| // If the host name is an empty string, then we don't want the colon | |
| // in the listening ports string. Remove it from the format string, | |
| // and when we use printf to format, the %s will be replaced by an | |
| // empty string. | |
| port_format_string = "%s%d"; | |
| } | |
| int formatted_string_size = _scprintf(port_format_string.c_str(), | |
| this->host_.c_str(), | |
| this->port_); | |
| char* listening_ports_buffer = new char[formatted_string_size + 1]; | |
| _snprintf_s(listening_ports_buffer, | |
| formatted_string_size + 1, | |
| formatted_string_size, | |
| port_format_string.c_str(), | |
| this->host_.c_str(), | |
| this->port_); | |
| std::string acl = "-0.0.0.0/0,+127.0.0.1"; | |
| LOG(DEBUG) << "Mongoose ACL is " << acl; | |
| const char* options[] = { "listening_ports", listening_ports_buffer, | |
| "access_control_list", acl.c_str(), | |
| // "enable_keep_alive", "yes", | |
| NULL }; | |
| context_ = mg_start(&OnHttpEvent, this, options); | |
| if (context_ == NULL) { | |
| LOG(WARN) << "Failed to start Mongoose"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| void Server::Stop() { | |
| LOG(TRACE) << "Entering Server::Stop"; | |
| if (context_) { | |
| mg_stop(context_); | |
| context_ = NULL; | |
| } | |
| } | |
| int Server::ProcessRequest(struct mg_connection* conn, | |
| const struct mg_request_info* request_info) { | |
| LOG(TRACE) << "Entering Server::ProcessRequest"; | |
| int http_response_code = NULL; | |
| std::string http_verb = request_info->request_method; | |
| std::string request_body = "{}"; | |
| if (http_verb == "POST") { | |
| request_body = this->ReadRequestBody(conn, request_info); | |
| } | |
| LOG(TRACE) << "Process request with:" | |
| << " URI: " << request_info->uri | |
| << " HTTP verb: " << http_verb << std::endl | |
| << "body: " << request_body; | |
| if (strcmp(request_info->uri, "/") == 0) { | |
| this->SendHttpOk(conn, | |
| request_info, | |
| SERVER_DEFAULT_PAGE, | |
| HTML_CONTENT_TYPE); | |
| http_response_code = 200; | |
| } else if (strcmp(request_info->uri, "/shutdown") == 0) { | |
| this->SendHttpOk(conn, | |
| request_info, | |
| SERVER_DEFAULT_PAGE, | |
| HTML_CONTENT_TYPE); | |
| http_response_code = 200; | |
| this->ShutDown(); | |
| } else { | |
| std::string serialized_response = this->DispatchCommand(request_info->uri, | |
| http_verb, | |
| request_body); | |
| http_response_code = this->SendResponseToClient(conn, | |
| request_info, | |
| serialized_response); | |
| } | |
| return http_response_code; | |
| } | |
| std::string Server::CreateSession() { | |
| LOG(TRACE) << "Entering Server::CreateSession"; | |
| SessionHandle session_handle= this->InitializeSession(); | |
| std::string session_id = session_handle->session_id(); | |
| this->sessions_[session_id] = session_handle; | |
| return session_id; | |
| } | |
| void Server::ShutDownSession(const std::string& session_id) { | |
| LOG(TRACE) << "Entering Server::ShutDownSession"; | |
| SessionMap::iterator it = this->sessions_.find(session_id); | |
| if (it != this->sessions_.end()) { | |
| it->second->ShutDown(); | |
| this->sessions_.erase(session_id); | |
| } else { | |
| LOG(DEBUG) << "Shutdown session is not found"; | |
| } | |
| } | |
| std::string Server::ReadRequestBody(struct mg_connection* conn, | |
| const struct mg_request_info* request_info) { | |
| LOG(TRACE) << "Entering Server::ReadRequestBody"; | |
| std::string request_body = ""; | |
| int content_length = 0; | |
| for (int header_index = 0; header_index < 64; ++header_index) { | |
| if (request_info->http_headers[header_index].name == NULL) { | |
| break; | |
| } | |
| if (strcmp(request_info->http_headers[header_index].name, | |
| "Content-Length") == 0) { | |
| content_length = atoi(request_info->http_headers[header_index].value); | |
| break; | |
| } | |
| } | |
| if (content_length == 0) { | |
| request_body = "{}"; | |
| } else { | |
| std::vector<char> buffer(content_length + 1); | |
| int bytes_read = 0; | |
| while (bytes_read < content_length) { | |
| bytes_read += mg_read(conn, | |
| &buffer[bytes_read], | |
| content_length - bytes_read); | |
| } | |
| buffer[content_length] = '\0'; | |
| request_body.append(&buffer[0]); | |
| } | |
| return request_body; | |
| } | |
| std::string Server::DispatchCommand(const std::string& uri, | |
| const std::string& http_verb, | |
| const std::string& command_body) { | |
| LOG(TRACE) << "Entering Server::DispatchCommand"; | |
| std::string session_id = ""; | |
| std::string locator_parameters = ""; | |
| std::string serialized_response = ""; | |
| std::string command = this->LookupCommand(uri, | |
| http_verb, | |
| &session_id, | |
| &locator_parameters); | |
| LOG(DEBUG) << "Command: " << http_verb << " " << uri << " " << command_body; | |
| if (command == webdriver::CommandType::NoCommand) { | |
| if (locator_parameters.size() != 0) { | |
| // Hand-code the response for an invalid HTTP verb for URL | |
| serialized_response.append("{ \"status\" : 405, "); | |
| serialized_response.append("\"sessionId\" : \"<no session>\", "); | |
| serialized_response.append("\"value\" : \""); | |
| serialized_response.append(locator_parameters); | |
| serialized_response.append("\" }"); | |
| } else { | |
| // Hand-code the response for an unknown URL | |
| serialized_response.append("{ \"status\" : 404, "); | |
| serialized_response.append("\"sessionId\" : \"<no session>\", "); | |
| serialized_response.append("\"value\" : \"Command not found: "); | |
| serialized_response.append(http_verb); | |
| serialized_response.append(" "); | |
| serialized_response.append(uri); | |
| serialized_response.append("\" }"); | |
| } | |
| } else if (command == webdriver::CommandType::Status) { | |
| // Status command must be handled by the server, not by the session. | |
| serialized_response = this->GetStatus(); | |
| } else if (command == webdriver::CommandType::GetSessionList) { | |
| // GetSessionList command must be handled by the server, | |
| // not by the session. | |
| serialized_response = this->ListSessions(); | |
| } else { | |
| if (command == webdriver::CommandType::NewSession) { | |
| session_id = this->CreateSession(); | |
| } | |
| SessionHandle session_handle = NULL; | |
| if (!this->LookupSession(session_id, &session_handle)) { | |
| if (command == webdriver::CommandType::Quit) { | |
| // Calling quit on an invalid session should be a no-op. | |
| // Hand-code the response for quit on an invalid (already | |
| // quit) session. | |
| serialized_response.append("{ \"status\" : 0, "); | |
| serialized_response.append("\"sessionId\" : \""); | |
| serialized_response.append(session_id); | |
| serialized_response.append("\", "); | |
| serialized_response.append("\"value\" : null }"); | |
| } else { | |
| // Hand-code the response for an invalid session id | |
| serialized_response.append("{ \"status\" : 6, "); | |
| serialized_response.append("\"sessionId\" : \""); | |
| serialized_response.append(session_id); | |
| serialized_response.append("\", "); | |
| serialized_response.append("\"value\" : \"session "); | |
| serialized_response.append(session_id); | |
| serialized_response.append(" does not exist\" }"); | |
| } | |
| } else { | |
| // Compile the serialized JSON representation of the command by hand. | |
| std::string serialized_command = "{ \"command\" : \"" + command + "\""; | |
| serialized_command.append(", \"locator\" : "); | |
| serialized_command.append(locator_parameters); | |
| serialized_command.append(", \"parameters\" : "); | |
| serialized_command.append(command_body); | |
| serialized_command.append(" }"); | |
| bool session_is_valid = session_handle->ExecuteCommand( | |
| serialized_command, | |
| &serialized_response); | |
| if (!session_is_valid) { | |
| this->ShutDownSession(session_id); | |
| } | |
| } | |
| } | |
| LOG(DEBUG) << "Response: " << serialized_response; | |
| return serialized_response; | |
| } | |
| std::string Server::ListSessions() { | |
| LOG(TRACE) << "Entering Server::ListSessions"; | |
| // Manually construct the serialized command for getting | |
| // session capabilities. | |
| std::string get_caps_command = "{ \"command\" : \"" + webdriver::CommandType::GetSessionCapabilities + "\"" + | |
| ", \"locator\" : {}, \"parameters\" : {} }"; | |
| Json::Value sessions(Json::arrayValue); | |
| SessionMap::iterator it = this->sessions_.begin(); | |
| for (; it != this->sessions_.end(); ++it) { | |
| // Each element of the GetSessionList command is an object with two | |
| // named properties, "id" and "capabilities". We already know the | |
| // ID, so we execute the GetSessionCapabilities command on each session | |
| // to be able to return the capabilities. | |
| Json::Value session_descriptor; | |
| session_descriptor["id"] = it->first; | |
| SessionHandle session = it->second; | |
| std::string serialized_session_response; | |
| session->ExecuteCommand(get_caps_command, &serialized_session_response); | |
| Response session_response; | |
| session_response.Deserialize(serialized_session_response); | |
| session_descriptor["capabilities"] = session_response.value(); | |
| sessions.append(session_descriptor); | |
| } | |
| Response response; | |
| response.SetSuccessResponse(sessions); | |
| return response.Serialize(); | |
| } | |
| bool Server::LookupSession(const std::string& session_id, | |
| SessionHandle* session_handle) { | |
| LOG(TRACE) << "Entering Server::LookupSession"; | |
| SessionMap::iterator it = this->sessions_.find(session_id); | |
| if (it == this->sessions_.end()) { | |
| return false; | |
| } | |
| *session_handle = it->second; | |
| return true; | |
| } | |
| int Server::SendResponseToClient(struct mg_connection* conn, | |
| const struct mg_request_info* request_info, | |
| const std::string& serialized_response) { | |
| LOG(TRACE) << "Entering Server::SendResponseToClient"; | |
| int return_code = 0; | |
| if (serialized_response.size() > 0) { | |
| Response response; | |
| response.Deserialize(serialized_response); | |
| return_code = response.status_code(); | |
| if (return_code == 0) { | |
| this->SendHttpOk(conn, | |
| request_info, | |
| serialized_response, | |
| JSON_CONTENT_TYPE); | |
| return_code = 200; | |
| } else if (return_code == 200) { | |
| this->SendHttpOk(conn, | |
| request_info, | |
| serialized_response, | |
| HTML_CONTENT_TYPE); | |
| } else if (return_code == 303) { | |
| std::string location = response.value().asString(); | |
| response.SetSuccessResponse(response.value()); | |
| this->SendHttpSeeOther(conn, request_info, location); | |
| return_code = 303; | |
| } else if (return_code == 400) { | |
| this->SendHttpBadRequest(conn, request_info, serialized_response); | |
| return_code = 400; | |
| } else if (return_code == 404) { | |
| this->SendHttpNotFound(conn, request_info, serialized_response); | |
| return_code = 404; | |
| } else if (return_code == 405) { | |
| std::string parameters = response.value().asString(); | |
| this->SendHttpMethodNotAllowed(conn, request_info, parameters); | |
| return_code = 405; | |
| } else if (return_code == 501) { | |
| this->SendHttpNotImplemented(conn, | |
| request_info, | |
| ""); | |
| return_code = 501; | |
| } else { | |
| this->SendHttpInternalError(conn, request_info, serialized_response); | |
| return_code = 500; | |
| } | |
| } | |
| return return_code; | |
| } | |
| // The standard HTTP Status codes are implemented below. Chrome uses | |
| // OK, See Other, Not Found, Method Not Allowed, and Internal Error. | |
| // Internal Error, HTTP 500, is used as a catch all for any issue | |
| // not covered in the JSON protocol. | |
| void Server::SendHttpOk(struct mg_connection* connection, | |
| const struct mg_request_info* request_info, | |
| const std::string& body, | |
| const std::string& content_type) { | |
| LOG(TRACE) << "Entering Server::SendHttpOk"; | |
| std::ostringstream out; | |
| out << "HTTP/1.1 200 OK\r\n" | |
| << "Content-Length: " << strlen(body.c_str()) << "\r\n" | |
| << "Content-Type: " << content_type << "; charset=UTF-8\r\n" | |
| << "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n" | |
| << "Accept-Ranges: bytes\r\n" | |
| << "Connection: close\r\n\r\n"; | |
| if (strcmp(request_info->request_method, "HEAD") != 0) { | |
| out << body << "\r\n"; | |
| } | |
| mg_write(connection, out.str().c_str(), out.str().size()); | |
| } | |
| void Server::SendHttpBadRequest(struct mg_connection* const connection, | |
| const struct mg_request_info* request_info, | |
| const std::string& body) { | |
| LOG(TRACE) << "Entering Server::SendHttpBadRequest"; | |
| std::ostringstream out; | |
| out << "HTTP/1.1 400 Bad Request\r\n" | |
| << "Content-Length: " << strlen(body.c_str()) << "\r\n" | |
| << "Content-Type: application/json; charset=UTF-8\r\n" | |
| << "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n" | |
| << "Accept-Ranges: bytes\r\n" | |
| << "Connection: close\r\n\r\n"; | |
| if (strcmp(request_info->request_method, "HEAD") != 0) { | |
| out << body << "\r\n"; | |
| } | |
| mg_printf(connection, "%s", out.str().c_str()); | |
| } | |
| void Server::SendHttpInternalError(struct mg_connection* connection, | |
| const struct mg_request_info* request_info, | |
| const std::string& body) { | |
| LOG(TRACE) << "Entering Server::SendHttpInternalError"; | |
| std::ostringstream out; | |
| out << "HTTP/1.1 500 Internal Server Error\r\n" | |
| << "Content-Length: " << strlen(body.c_str()) << "\r\n" | |
| << "Content-Type: application/json; charset=UTF-8\r\n" | |
| << "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n" | |
| << "Accept-Ranges: bytes\r\n" | |
| << "Connection: close\r\n\r\n"; | |
| if (strcmp(request_info->request_method, "HEAD") != 0) { | |
| out << body << "\r\n"; | |
| } | |
| mg_write(connection, out.str().c_str(), out.str().size()); | |
| } | |
| void Server::SendHttpNotFound(struct mg_connection* const connection, | |
| const struct mg_request_info* request_info, | |
| const std::string& body) { | |
| LOG(TRACE) << "Entering Server::SendHttpNotFound"; | |
| std::ostringstream out; | |
| out << "HTTP/1.1 404 Not Found\r\n" | |
| << "Content-Length: " << strlen(body.c_str()) << "\r\n" | |
| << "Content-Type: application/json; charset=UTF-8\r\n" | |
| << "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n" | |
| << "Accept-Ranges: bytes\r\n" | |
| << "Connection: close\r\n\r\n"; | |
| if (strcmp(request_info->request_method, "HEAD") != 0) { | |
| out << body << "\r\n"; | |
| } | |
| mg_printf(connection, "%s", out.str().c_str()); | |
| } | |
| void Server::SendHttpMethodNotAllowed( | |
| struct mg_connection* connection, | |
| const struct mg_request_info* request_info, | |
| const std::string& allowed_methods) { | |
| LOG(TRACE) << "Entering Server::SendHttpMethodNotAllowed"; | |
| std::ostringstream out; | |
| out << "HTTP/1.1 405 Method Not Allowed\r\n" | |
| << "Content-Type: text/html\r\n" | |
| << "Content-Length: 0\r\n" | |
| << "Allow: " << allowed_methods << "\r\n\r\n"; | |
| mg_write(connection, out.str().c_str(), out.str().size()); | |
| } | |
| void Server::SendHttpNotImplemented(struct mg_connection* connection, | |
| const struct mg_request_info* request_info, | |
| const std::string& body) { | |
| LOG(TRACE) << "Entering Server::SendHttpNotImplemented"; | |
| std::ostringstream out; | |
| out << "HTTP/1.1 501 Not Implemented\r\n\r\n"; | |
| mg_write(connection, out.str().c_str(), out.str().size()); | |
| } | |
| void Server::SendHttpSeeOther(struct mg_connection* connection, | |
| const struct mg_request_info* request_info, | |
| const std::string& location) { | |
| LOG(TRACE) << "Entering Server::SendHttpSeeOther"; | |
| std::ostringstream out; | |
| out << "HTTP/1.1 303 See Other\r\n" | |
| << "Location: " << location << "\r\n" | |
| << "Content-Type: text/html\r\n" | |
| << "Content-Length: 0\r\n\r\n"; | |
| mg_write(connection, out.str().c_str(), out.str().size()); | |
| } | |
| std::string Server::LookupCommand(const std::string& uri, | |
| const std::string& http_verb, | |
| std::string* session_id, | |
| std::string* locator) { | |
| LOG(TRACE) << "Entering Server::LookupCommand"; | |
| std::string value = webdriver::CommandType::NoCommand; | |
| UrlMap::const_iterator it = this->commands_.begin(); | |
| for (; it != this->commands_.end(); ++it) { | |
| std::vector<std::string> locator_param_names; | |
| std::string url_candidate = it->first; | |
| size_t param_start_pos = url_candidate.find_first_of(":"); | |
| while (param_start_pos != std::string::npos) { | |
| size_t param_len = std::string::npos; | |
| size_t param_end_pos = url_candidate.find_first_of("/", param_start_pos); | |
| if (param_end_pos != std::string::npos) { | |
| param_len = param_end_pos - param_start_pos; | |
| } | |
| // Skip the colon | |
| std::string param_name = url_candidate.substr(param_start_pos + 1, | |
| param_len - 1); | |
| locator_param_names.push_back(param_name); | |
| if (param_name == "sessionid" || param_name == "id") { | |
| url_candidate.replace(param_start_pos, param_len, "([0-9a-fA-F-]+)"); | |
| } else { | |
| url_candidate.replace(param_start_pos, param_len, "([^/]+)"); | |
| } | |
| param_start_pos = url_candidate.find_first_of(":"); | |
| } | |
| std::tr1::regex matcher("^" + url_candidate + "$"); | |
| std::tr1::match_results<std::string::const_iterator> matches; | |
| if (std::tr1::regex_search(uri, matches, matcher)) { | |
| VerbMap::const_iterator verb_iterator = it->second.find(http_verb); | |
| if (verb_iterator != it->second.end()) { | |
| value = verb_iterator->second; | |
| std::string param = "{"; | |
| size_t param_count = locator_param_names.size(); | |
| for (unsigned int i = 0; i < param_count; i++) { | |
| if (i != 0) { | |
| param.append(","); | |
| } | |
| std::string locator_param_value = matches[i + 1].str(); | |
| param.append(" \""); | |
| param.append(locator_param_names[i]); | |
| param.append("\" : \""); | |
| param.append(locator_param_value); | |
| param.append("\""); | |
| if (locator_param_names[i] == "sessionid") { | |
| session_id->append(locator_param_value); | |
| } | |
| } | |
| param.append(" }"); | |
| locator->append(param); | |
| break; | |
| } else { | |
| verb_iterator = it->second.begin(); | |
| for (; verb_iterator != it->second.end(); ++verb_iterator) { | |
| if (locator->size() != 0) { | |
| locator->append(","); | |
| } | |
| locator->append(verb_iterator->first); | |
| } | |
| } | |
| } | |
| } | |
| return value; | |
| } | |
| void Server::PopulateCommandRepository() { | |
| LOG(TRACE) << "Entering Server::PopulateCommandRepository"; | |
| this->commands_["/status"]["GET"] = webdriver::CommandType::Status; | |
| this->commands_["/session"]["POST"] = webdriver::CommandType::NewSession; | |
| this->commands_["/sessions"]["GET"] = webdriver::CommandType::GetSessionList; | |
| this->commands_["/session/:sessionid"]["GET"] = webdriver::CommandType::GetSessionCapabilities; | |
| this->commands_["/session/:sessionid"]["DELETE"] = webdriver::CommandType::Quit; | |
| this->commands_["/session/:sessionid/window_handle"]["GET"] = webdriver::CommandType::GetCurrentWindowHandle; | |
| this->commands_["/session/:sessionid/window_handles"]["GET"] = webdriver::CommandType::GetWindowHandles; | |
| this->commands_["/session/:sessionid/url"]["GET"] = webdriver::CommandType::GetCurrentUrl; | |
| this->commands_["/session/:sessionid/url"]["POST"] = webdriver::CommandType::Get; | |
| this->commands_["/session/:sessionid/forward"]["POST"] = webdriver::CommandType::GoForward; | |
| this->commands_["/session/:sessionid/back"]["POST"] = webdriver::CommandType::GoBack; | |
| this->commands_["/session/:sessionid/refresh"]["POST"] = webdriver::CommandType::Refresh; | |
| this->commands_["/session/:sessionid/execute"]["POST"] = webdriver::CommandType::ExecuteScript; | |
| this->commands_["/session/:sessionid/execute_async"]["POST"] = webdriver::CommandType::ExecuteAsyncScript; | |
| this->commands_["/session/:sessionid/screenshot"]["GET"] = webdriver::CommandType::Screenshot; | |
| this->commands_["/session/:sessionid/frame"]["POST"] = webdriver::CommandType::SwitchToFrame; | |
| this->commands_["/session/:sessionid/window"]["POST"] = webdriver::CommandType::SwitchToWindow; | |
| this->commands_["/session/:sessionid/window"]["DELETE"] = webdriver::CommandType::Close; | |
| this->commands_["/session/:sessionid/cookie"]["GET"] = webdriver::CommandType::GetAllCookies; | |
| this->commands_["/session/:sessionid/cookie"]["POST"] = webdriver::CommandType::AddCookie; | |
| this->commands_["/session/:sessionid/cookie"]["DELETE"] = webdriver::CommandType::DeleteAllCookies; | |
| this->commands_["/session/:sessionid/cookie/:name"]["DELETE"] = webdriver::CommandType::DeleteCookie; | |
| this->commands_["/session/:sessionid/source"]["GET"] = webdriver::CommandType::GetPageSource; | |
| this->commands_["/session/:sessionid/title"]["GET"] = webdriver::CommandType::GetTitle; | |
| this->commands_["/session/:sessionid/element"]["POST"] = webdriver::CommandType::FindElement; | |
| this->commands_["/session/:sessionid/elements"]["POST"] = webdriver::CommandType::FindElements; | |
| this->commands_["/session/:sessionid/timeouts"]["POST"] = webdriver::CommandType::SetTimeout; | |
| this->commands_["/session/:sessionid/timeouts/implicit_wait"]["POST"] = webdriver::CommandType::ImplicitlyWait; | |
| this->commands_["/session/:sessionid/timeouts/async_script"]["POST"] = webdriver::CommandType::SetAsyncScriptTimeout; | |
| this->commands_["/session/:sessionid/element/active"]["POST"] = webdriver::CommandType::GetActiveElement; | |
| this->commands_["/session/:sessionid/element/:id/element"]["POST"] = webdriver::CommandType::FindChildElement; | |
| this->commands_["/session/:sessionid/element/:id/elements"]["POST"] = webdriver::CommandType::FindChildElements; | |
| this->commands_["/session/:sessionid/element/:id"]["GET"] = webdriver::CommandType::DescribeElement; | |
| this->commands_["/session/:sessionid/element/:id/click"]["POST"] = webdriver::CommandType::ClickElement; | |
| this->commands_["/session/:sessionid/element/:id/text"]["GET"] = webdriver::CommandType::GetElementText; | |
| this->commands_["/session/:sessionid/element/:id/submit"]["POST"] = webdriver::CommandType::SubmitElement; | |
| this->commands_["/session/:sessionid/element/:id/value"]["GET"] = webdriver::CommandType::GetElementValue; | |
| this->commands_["/session/:sessionid/element/:id/value"]["POST"] = webdriver::CommandType::SendKeysToElement; | |
| this->commands_["/session/:sessionid/element/:id/name"]["GET"] = webdriver::CommandType::GetElementTagName; | |
| this->commands_["/session/:sessionid/element/:id/clear"]["POST"] = webdriver::CommandType::ClearElement; | |
| this->commands_["/session/:sessionid/element/:id/selected"]["GET"] = webdriver::CommandType::IsElementSelected; | |
| this->commands_["/session/:sessionid/element/:id/enabled"]["GET"] = webdriver::CommandType::IsElementEnabled; | |
| this->commands_["/session/:sessionid/element/:id/displayed"]["GET"] = webdriver::CommandType::IsElementDisplayed; | |
| this->commands_["/session/:sessionid/element/:id/location"]["GET"] = webdriver::CommandType::GetElementLocation; | |
| this->commands_["/session/:sessionid/element/:id/location_in_view"]["GET"] = webdriver::CommandType::GetElementLocationOnceScrolledIntoView; | |
| this->commands_["/session/:sessionid/element/:id/size"]["GET"] = webdriver::CommandType::GetElementSize; | |
| this->commands_["/session/:sessionid/element/:id/css/:propertyName"]["GET"] = webdriver::CommandType::GetElementValueOfCssProperty; | |
| this->commands_["/session/:sessionid/element/:id/attribute/:name"]["GET"] = webdriver::CommandType::GetElementAttribute; | |
| this->commands_["/session/:sessionid/element/:id/equals/:other"]["GET"] = webdriver::CommandType::ElementEquals; | |
| this->commands_["/session/:sessionid/screenshot"]["GET"] = webdriver::CommandType::Screenshot; | |
| this->commands_["/session/:sessionid/orientation"]["GET"] = webdriver::CommandType::GetOrientation; | |
| this->commands_["/session/:sessionid/orientation"]["POST"] = webdriver::CommandType::SetOrientation; | |
| this->commands_["/session/:sessionid/window/:windowHandle/size"]["GET"] = webdriver::CommandType::GetWindowSize; | |
| this->commands_["/session/:sessionid/window/:windowHandle/size"]["POST"] = webdriver::CommandType::SetWindowSize; | |
| this->commands_["/session/:sessionid/window/:windowHandle/position"]["GET"] = webdriver::CommandType::GetWindowPosition; | |
| this->commands_["/session/:sessionid/window/:windowHandle/position"]["POST"] = webdriver::CommandType::SetWindowPosition; | |
| this->commands_["/session/:sessionid/window/:windowHandle/maximize"]["POST"] = webdriver::CommandType::MaximizeWindow; | |
| this->commands_["/session/:sessionid/accept_alert"]["POST"] = webdriver::CommandType::AcceptAlert; | |
| this->commands_["/session/:sessionid/dismiss_alert"]["POST"] = webdriver::CommandType::DismissAlert; | |
| this->commands_["/session/:sessionid/alert_text"]["GET"] = webdriver::CommandType::GetAlertText; | |
| this->commands_["/session/:sessionid/alert_text"]["POST"] = webdriver::CommandType::SendKeysToAlert; | |
| this->commands_["/session/:sessionid/keys"]["POST"] = webdriver::CommandType::SendKeysToActiveElement; | |
| this->commands_["/session/:sessionid/moveto"]["POST"] = webdriver::CommandType::MouseMoveTo; | |
| this->commands_["/session/:sessionid/click"]["POST"] = webdriver::CommandType::MouseClick; | |
| this->commands_["/session/:sessionid/doubleclick"]["POST"] = webdriver::CommandType::MouseDoubleClick; | |
| this->commands_["/session/:sessionid/buttondown"]["POST"] = webdriver::CommandType::MouseButtonDown; | |
| this->commands_["/session/:sessionid/buttonup"]["POST"] = webdriver::CommandType::MouseButtonUp; | |
| this->commands_["/session/:sessionid/ime/available_engines"]["GET"] = webdriver::CommandType::ListAvailableImeEngines; | |
| this->commands_["/session/:sessionid/ime/active_engines"]["GET"] = webdriver::CommandType::GetActiveImeEngine; | |
| this->commands_["/session/:sessionid/ime/activated"]["GET"] = webdriver::CommandType::IsImeActivated; | |
| this->commands_["/session/:sessionid/ime/activate"]["POST"] = webdriver::CommandType::ActivateImeEngine; | |
| this->commands_["/session/:sessionid/ime/deactivate"]["POST"] = webdriver::CommandType::DeactivateImeEngine; | |
| this->commands_["/session/:sessionId/touch/click"]["POST"] = webdriver::CommandType::TouchClick; | |
| this->commands_["/session/:sessionId/touch/down"]["POST"] = webdriver::CommandType::TouchDown; | |
| this->commands_["/session/:sessionId/touch/up"]["POST"] = webdriver::CommandType::TouchUp; | |
| this->commands_["/session/:sessionId/touch/move"]["POST"] = webdriver::CommandType::TouchMove; | |
| this->commands_["/session/:sessionId/touch/scroll"]["POST"] = webdriver::CommandType::TouchScroll; | |
| this->commands_["/session/:sessionId/touch/doubleclick"]["POST"] = webdriver::CommandType::TouchDoubleClick; | |
| this->commands_["/session/:sessionId/touch/longclick"]["POST"] = webdriver::CommandType::TouchLongClick; | |
| this->commands_["/session/:sessionId/touch/flick"]["POST"] = webdriver::CommandType::TouchFlick; | |
| } | |
| } // namespace webdriver |