blob: 0785fafddf0515d1449f7cfea3c566dd43d7a355 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <signal.h>
#include <stdlib.h>
#if defined(OS_WIN)
#include <windows.h>
#endif
#include <fstream>
#include <iostream>
#include "base/at_exit.h"
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/webdriver/commands/alert_commands.h"
#include "chrome/test/webdriver/commands/appcache_status_command.h"
#include "chrome/test/webdriver/commands/browser_connection_commands.h"
#include "chrome/test/webdriver/commands/chrome_commands.h"
#include "chrome/test/webdriver/commands/cookie_commands.h"
#include "chrome/test/webdriver/commands/create_session.h"
#include "chrome/test/webdriver/commands/execute_async_script_command.h"
#include "chrome/test/webdriver/commands/execute_command.h"
#include "chrome/test/webdriver/commands/file_upload_command.h"
#include "chrome/test/webdriver/commands/find_element_commands.h"
#include "chrome/test/webdriver/commands/html5_location_commands.h"
#include "chrome/test/webdriver/commands/html5_storage_commands.h"
#include "chrome/test/webdriver/commands/keys_command.h"
#include "chrome/test/webdriver/commands/log_command.h"
#include "chrome/test/webdriver/commands/mouse_commands.h"
#include "chrome/test/webdriver/commands/navigate_commands.h"
#include "chrome/test/webdriver/commands/screenshot_command.h"
#include "chrome/test/webdriver/commands/session_with_id.h"
#include "chrome/test/webdriver/commands/set_timeout_commands.h"
#include "chrome/test/webdriver/commands/source_command.h"
#include "chrome/test/webdriver/commands/target_locator_commands.h"
#include "chrome/test/webdriver/commands/title_command.h"
#include "chrome/test/webdriver/commands/url_command.h"
#include "chrome/test/webdriver/commands/webelement_commands.h"
#include "chrome/test/webdriver/commands/window_commands.h"
#include "chrome/test/webdriver/webdriver_dispatch.h"
#include "chrome/test/webdriver/webdriver_logging.h"
#include "chrome/test/webdriver/webdriver_session_manager.h"
#include "chrome/test/webdriver/webdriver_switches.h"
#include "chrome/test/webdriver/webdriver_util.h"
#include "third_party/mongoose/mongoose.h"
#if defined(OS_WIN)
#include <time.h>
#elif defined(OS_POSIX)
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#endif
namespace webdriver {
namespace {
void InitCallbacks(Dispatcher* dispatcher,
base::WaitableEvent* shutdown_event,
bool forbid_other_requests) {
dispatcher->AddShutdown("/shutdown", shutdown_event);
dispatcher->AddStatus("/status");
dispatcher->AddLog("/log");
dispatcher->Add<CreateSession>("/session");
// WebElement commands
dispatcher->Add<FindOneElementCommand>( "/session/*/element");
dispatcher->Add<FindManyElementsCommand>("/session/*/elements");
dispatcher->Add<ActiveElementCommand>( "/session/*/element/active");
dispatcher->Add<FindOneElementCommand>( "/session/*/element/*/element");
dispatcher->Add<FindManyElementsCommand>("/session/*/elements/*/elements");
dispatcher->Add<ElementAttributeCommand>("/session/*/element/*/attribute/*");
dispatcher->Add<ElementCssCommand>( "/session/*/element/*/css/*");
dispatcher->Add<ElementClearCommand>( "/session/*/element/*/clear");
dispatcher->Add<ElementDisplayedCommand>("/session/*/element/*/displayed");
dispatcher->Add<ElementEnabledCommand>( "/session/*/element/*/enabled");
dispatcher->Add<ElementEqualsCommand>( "/session/*/element/*/equals/*");
dispatcher->Add<ElementLocationCommand>( "/session/*/element/*/location");
dispatcher->Add<ElementLocationInViewCommand>(
"/session/*/element/*/location_in_view");
dispatcher->Add<ElementNameCommand>( "/session/*/element/*/name");
dispatcher->Add<ElementSelectedCommand>("/session/*/element/*/selected");
dispatcher->Add<ElementSizeCommand>( "/session/*/element/*/size");
dispatcher->Add<ElementSubmitCommand>( "/session/*/element/*/submit");
dispatcher->Add<ElementTextCommand>( "/session/*/element/*/text");
dispatcher->Add<ElementToggleCommand>( "/session/*/element/*/toggle");
dispatcher->Add<ElementValueCommand>( "/session/*/element/*/value");
dispatcher->Add<ScreenshotCommand>("/session/*/screenshot");
// Mouse Commands
dispatcher->Add<MoveAndClickCommand>("/session/*/element/*/click");
dispatcher->Add<DragCommand>( "/session/*/element/*/drag");
dispatcher->Add<HoverCommand>( "/session/*/element/*/hover");
dispatcher->Add<MoveToCommand>( "/session/*/moveto");
dispatcher->Add<ClickCommand>( "/session/*/click");
dispatcher->Add<ButtonDownCommand>( "/session/*/buttondown");
dispatcher->Add<ButtonUpCommand>( "/session/*/buttonup");
dispatcher->Add<DoubleClickCommand>("/session/*/doubleclick");
// All session based commands should be listed after the element based
// commands to avoid potential mapping conflicts from an overzealous
// wildcard match. For example, /session/*/title maps to the handler to
// fetch the page title. If mapped first, this would overwrite the handler
// for /session/*/element/*/attribute/title, which should fetch the title
// attribute of the element.
dispatcher->Add<AcceptAlertCommand>( "/session/*/accept_alert");
dispatcher->Add<AlertTextCommand>( "/session/*/alert_text");
dispatcher->Add<BackCommand>( "/session/*/back");
dispatcher->Add<DismissAlertCommand>( "/session/*/dismiss_alert");
dispatcher->Add<ExecuteCommand>( "/session/*/execute");
dispatcher->Add<ExecuteAsyncScriptCommand>(
"/session/*/execute_async");
dispatcher->Add<ForwardCommand>( "/session/*/forward");
dispatcher->Add<SwitchFrameCommand>( "/session/*/frame");
dispatcher->Add<KeysCommand>( "/session/*/keys");
dispatcher->Add<RefreshCommand>( "/session/*/refresh");
dispatcher->Add<SourceCommand>( "/session/*/source");
dispatcher->Add<TitleCommand>( "/session/*/title");
dispatcher->Add<URLCommand>( "/session/*/url");
dispatcher->Add<WindowCommand>( "/session/*/window");
dispatcher->Add<WindowHandleCommand>( "/session/*/window_handle");
dispatcher->Add<WindowHandlesCommand>("/session/*/window_handles");
dispatcher->Add<WindowSizeCommand>( "/session/*/window/*/size");
dispatcher->Add<WindowPositionCommand>(
"/session/*/window/*/position");
dispatcher->Add<WindowMaximizeCommand>(
"/session/*/window/*/maximize");
dispatcher->Add<SetAsyncScriptTimeoutCommand>(
"/session/*/timeouts/async_script");
dispatcher->Add<ImplicitWaitCommand>( "/session/*/timeouts/implicit_wait");
dispatcher->Add<LogCommand>( "/session/*/log");
dispatcher->Add<FileUploadCommand>( "/session/*/file");
// Cookie functions.
dispatcher->Add<CookieCommand>( "/session/*/cookie");
dispatcher->Add<NamedCookieCommand>("/session/*/cookie/*");
dispatcher->Add<BrowserConnectionCommand>("/session/*/browser_connection");
dispatcher->Add<AppCacheStatusCommand>("/session/*/application_cache/status");
// Chrome-specific commands.
dispatcher->Add<ExtensionsCommand>("/session/*/chrome/extensions");
dispatcher->Add<ExtensionCommand>("/session/*/chrome/extension/*");
dispatcher->Add<ViewsCommand>("/session/*/chrome/views");
#if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
dispatcher->Add<HeapProfilerDumpCommand>(
"/session/*/chrome/heapprofilerdump");
#endif // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
// HTML5 functions.
dispatcher->Add<HTML5LocationCommand>("/session/*/location");
dispatcher->Add<LocalStorageCommand>("/session/*/local_storage");
dispatcher->Add<LocalStorageSizeCommand>("/session/*/local_storage/size");
dispatcher->Add<LocalStorageKeyCommand>("/session/*/local_storage/key*");
dispatcher->Add<SessionStorageCommand>("/session/*/session_storage");
dispatcher->Add<SessionStorageSizeCommand>("/session/*/session_storage/size");
dispatcher->Add<SessionStorageKeyCommand>("/session/*/session_storage/key*");
// Since the /session/* is a wild card that would match the above URIs, this
// line MUST be after all other webdriver command callbacks.
dispatcher->Add<SessionWithID>("/session/*");
if (forbid_other_requests)
dispatcher->ForbidAllOtherRequests();
}
void* ProcessHttpRequest(mg_event event_raised,
struct mg_connection* connection,
const struct mg_request_info* request_info) {
bool handler_result_code = false;
if (event_raised == MG_NEW_REQUEST) {
handler_result_code =
reinterpret_cast<Dispatcher*>(request_info->user_data)->
ProcessHttpRequest(connection, request_info);
}
return reinterpret_cast<void*>(handler_result_code);
}
void MakeMongooseOptions(const std::string& port,
const std::string& root,
int http_threads,
bool enable_keep_alive,
std::vector<std::string>* out_options) {
out_options->push_back("listening_ports");
out_options->push_back(port);
out_options->push_back("enable_keep_alive");
out_options->push_back(enable_keep_alive ? "yes" : "no");
out_options->push_back("num_threads");
out_options->push_back(base::IntToString(http_threads));
if (!root.empty()) {
out_options->push_back("document_root");
out_options->push_back(root);
}
}
} // namespace
int RunChromeDriver() {
base::AtExitManager exit;
base::WaitableEvent shutdown_event(false, false);
CommandLine* cmd_line = CommandLine::ForCurrentProcess();
#if defined(OS_POSIX)
signal(SIGPIPE, SIG_IGN);
#endif
srand((unsigned int)time(NULL));
// Register Chrome's path provider so that the AutomationProxy will find our
// built Chrome.
chrome::RegisterPathProvider();
TestTimeouts::Initialize();
// Parse command line flags.
std::string port = "9515";
base::FilePath log_path;
std::string root;
std::string url_base;
int http_threads = 4;
bool enable_keep_alive = false;
if (cmd_line->HasSwitch("port"))
port = cmd_line->GetSwitchValueASCII("port");
if (cmd_line->HasSwitch("log-path"))
log_path = cmd_line->GetSwitchValuePath("log-path");
// The 'root' flag allows the user to specify a location to serve files from.
// If it is not given, a callback will be registered to forbid all file
// requests.
if (cmd_line->HasSwitch("root"))
root = cmd_line->GetSwitchValueASCII("root");
if (cmd_line->HasSwitch("url-base"))
url_base = cmd_line->GetSwitchValueASCII("url-base");
if (cmd_line->HasSwitch("http-threads")) {
if (!base::StringToInt(cmd_line->GetSwitchValueASCII("http-threads"),
&http_threads)) {
std::cerr << "'http-threads' option must be an integer";
return 1;
}
}
if (cmd_line->HasSwitch(kEnableKeepAlive))
enable_keep_alive = true;
bool logging_success = InitWebDriverLogging(log_path, kAllLogLevel);
std::string chromedriver_info = base::StringPrintf(
"ChromeDriver %s", chrome::kChromeVersion);
base::FilePath chromedriver_exe;
if (PathService::Get(base::FILE_EXE, &chromedriver_exe)) {
chromedriver_info += base::StringPrintf(
" %" PRFilePath, chromedriver_exe.value().c_str());
}
FileLog::Get()->Log(kInfoLogLevel, base::Time::Now(), chromedriver_info);
SessionManager* manager = SessionManager::GetInstance();
manager->set_port(port);
manager->set_url_base(url_base);
Dispatcher dispatcher(url_base);
InitCallbacks(&dispatcher, &shutdown_event, root.empty());
std::vector<std::string> args;
MakeMongooseOptions(port, root, http_threads, enable_keep_alive, &args);
scoped_ptr<const char*[]> options(new const char*[args.size() + 1]);
for (size_t i = 0; i < args.size(); ++i) {
options[i] = args[i].c_str();
}
options[args.size()] = NULL;
// Initialize SHTTPD context.
// Listen on port 9515 or port specified on command line.
// TODO(jmikhail) Maybe add port 9516 as a secure connection.
struct mg_context* ctx = mg_start(&ProcessHttpRequest,
&dispatcher,
options.get());
if (ctx == NULL) {
std::cerr << "Port already in use. Exiting..." << std::endl;
#if defined(OS_WIN)
return WSAEADDRINUSE;
#else
return EADDRINUSE;
#endif
}
// The tests depend on parsing the first line ChromeDriver outputs,
// so all other logging should happen after this.
if (!cmd_line->HasSwitch("silent")) {
std::cout << "Started ChromeDriver" << std::endl
<< "port=" << port << std::endl
<< "version=" << chrome::kChromeVersion << std::endl;
if (logging_success)
std::cout << "log=" << FileLog::Get()->path().value() << std::endl;
else
std::cout << "Log file could not be created" << std::endl;
}
// Run until we receive command to shutdown.
// Don't call mg_stop because mongoose will hang if clients are still
// connected when keep-alive is enabled.
shutdown_event.Wait();
return (EXIT_SUCCESS);
}
} // namespace webdriver
int main(int argc, char *argv[]) {
CommandLine::Init(argc, argv);
return webdriver::RunChromeDriver();
}