// 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 #include #if defined(OS_WIN) #include #endif #include #include #include "base/at_exit.h" #include "base/base_paths.h" #include "base/command_line.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" #include "base/stringprintf.h" #include "base/string_number_conversions.h" #include "base/string_split.h" #include "base/string_util.h" #include "base/synchronization/waitable_event.h" #include "base/test/test_timeouts.h" #include "base/threading/platform_thread.h" #include "base/time.h" #include "base/utf_string_conversions.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/navigate_commands.h" #include "chrome/test/webdriver/commands/mouse_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_util.h" #include "third_party/mongoose/mongoose.h" #if defined(OS_WIN) #include #elif defined(OS_POSIX) #include #include #include #include #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("/session"); // WebElement commands dispatcher->Add( "/session/*/element"); dispatcher->Add("/session/*/elements"); dispatcher->Add( "/session/*/element/active"); dispatcher->Add( "/session/*/element/*/element"); dispatcher->Add("/session/*/elements/*/elements"); dispatcher->Add("/session/*/element/*/attribute/*"); dispatcher->Add( "/session/*/element/*/css/*"); dispatcher->Add( "/session/*/element/*/clear"); dispatcher->Add("/session/*/element/*/displayed"); dispatcher->Add( "/session/*/element/*/enabled"); dispatcher->Add( "/session/*/element/*/equals/*"); dispatcher->Add( "/session/*/element/*/location"); dispatcher->Add( "/session/*/element/*/location_in_view"); dispatcher->Add( "/session/*/element/*/name"); dispatcher->Add("/session/*/element/*/selected"); dispatcher->Add( "/session/*/element/*/size"); dispatcher->Add( "/session/*/element/*/submit"); dispatcher->Add( "/session/*/element/*/text"); dispatcher->Add( "/session/*/element/*/toggle"); dispatcher->Add( "/session/*/element/*/value"); dispatcher->Add("/session/*/screenshot"); // Mouse Commands dispatcher->Add("/session/*/element/*/click"); dispatcher->Add( "/session/*/element/*/drag"); dispatcher->Add( "/session/*/element/*/hover"); dispatcher->Add( "/session/*/moveto"); dispatcher->Add( "/session/*/click"); dispatcher->Add( "/session/*/buttondown"); dispatcher->Add( "/session/*/buttonup"); dispatcher->Add("/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( "/session/*/accept_alert"); dispatcher->Add( "/session/*/alert_text"); dispatcher->Add( "/session/*/back"); dispatcher->Add( "/session/*/dismiss_alert"); dispatcher->Add( "/session/*/execute"); dispatcher->Add( "/session/*/execute_async"); dispatcher->Add( "/session/*/forward"); dispatcher->Add( "/session/*/frame"); dispatcher->Add( "/session/*/keys"); dispatcher->Add( "/session/*/refresh"); dispatcher->Add( "/session/*/source"); dispatcher->Add( "/session/*/title"); dispatcher->Add( "/session/*/url"); dispatcher->Add( "/session/*/window"); dispatcher->Add( "/session/*/window_handle"); dispatcher->Add("/session/*/window_handles"); dispatcher->Add( "/session/*/window/*/size"); dispatcher->Add( "/session/*/window/*/position"); dispatcher->Add( "/session/*/window/*/maximize"); dispatcher->Add( "/session/*/timeouts/async_script"); dispatcher->Add( "/session/*/timeouts/implicit_wait"); dispatcher->Add( "/session/*/log"); dispatcher->Add( "/session/*/file"); // Cookie functions. dispatcher->Add( "/session/*/cookie"); dispatcher->Add("/session/*/cookie/*"); dispatcher->Add("/session/*/browser_connection"); dispatcher->Add("/session/*/application_cache/status"); // Chrome-specific commands. dispatcher->Add("/session/*/chrome/extensions"); dispatcher->Add("/session/*/chrome/extension/*"); dispatcher->Add("/session/*/chrome/views"); #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) dispatcher->Add( "/session/*/chrome/heapprofilerdump"); #endif // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS)) // HTML5 functions. dispatcher->Add("/session/*/location"); dispatcher->Add("/session/*/local_storage"); dispatcher->Add("/session/*/local_storage/size"); dispatcher->Add("/session/*/local_storage/key*"); dispatcher->Add("/session/*/session_storage"); dispatcher->Add("/session/*/session_storage/size"); dispatcher->Add("/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("/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(request_info->user_data)-> ProcessHttpRequest(connection, request_info); } return reinterpret_cast(handler_result_code); } void MakeMongooseOptions(const std::string& port, const std::string& root, int http_threads, bool enable_keep_alive, std::vector* 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"; 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("enable-keep-alive")) enable_keep_alive = true; bool logging_success = InitWebDriverLogging(log_path, kAllLogLevel); std::string chromedriver_info = base::StringPrintf( "ChromeDriver %s", chrome::kChromeVersion); 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 args; MakeMongooseOptions(port, root, http_threads, enable_keep_alive, &args); scoped_array 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. 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(); }