Extract SIGPIPE ignoring code to a common place.
[chromium-blink-merge.git] / chrome / test / webdriver / webdriver_server.cc
blob5781bac40def47bd9d330280d4504d88da133164
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <signal.h>
6 #include <stdlib.h>
8 #if defined(OS_WIN)
9 #include <windows.h>
10 #endif
12 #include <iostream>
13 #include <fstream>
15 #include "base/at_exit.h"
16 #include "base/base_paths.h"
17 #include "base/command_line.h"
18 #include "base/file_path.h"
19 #include "base/file_util.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/path_service.h"
22 #include "base/process_util.h"
23 #include "base/stringprintf.h"
24 #include "base/string_number_conversions.h"
25 #include "base/string_split.h"
26 #include "base/string_util.h"
27 #include "base/synchronization/waitable_event.h"
28 #include "base/test/test_timeouts.h"
29 #include "base/threading/platform_thread.h"
30 #include "base/time.h"
31 #include "base/utf_string_conversions.h"
32 #include "chrome/common/chrome_constants.h"
33 #include "chrome/common/chrome_paths.h"
34 #include "chrome/common/chrome_switches.h"
35 #include "chrome/test/webdriver/commands/alert_commands.h"
36 #include "chrome/test/webdriver/commands/appcache_status_command.h"
37 #include "chrome/test/webdriver/commands/browser_connection_commands.h"
38 #include "chrome/test/webdriver/commands/chrome_commands.h"
39 #include "chrome/test/webdriver/commands/cookie_commands.h"
40 #include "chrome/test/webdriver/commands/create_session.h"
41 #include "chrome/test/webdriver/commands/execute_async_script_command.h"
42 #include "chrome/test/webdriver/commands/execute_command.h"
43 #include "chrome/test/webdriver/commands/file_upload_command.h"
44 #include "chrome/test/webdriver/commands/find_element_commands.h"
45 #include "chrome/test/webdriver/commands/html5_location_commands.h"
46 #include "chrome/test/webdriver/commands/html5_storage_commands.h"
47 #include "chrome/test/webdriver/commands/keys_command.h"
48 #include "chrome/test/webdriver/commands/log_command.h"
49 #include "chrome/test/webdriver/commands/navigate_commands.h"
50 #include "chrome/test/webdriver/commands/mouse_commands.h"
51 #include "chrome/test/webdriver/commands/screenshot_command.h"
52 #include "chrome/test/webdriver/commands/session_with_id.h"
53 #include "chrome/test/webdriver/commands/set_timeout_commands.h"
54 #include "chrome/test/webdriver/commands/source_command.h"
55 #include "chrome/test/webdriver/commands/target_locator_commands.h"
56 #include "chrome/test/webdriver/commands/title_command.h"
57 #include "chrome/test/webdriver/commands/url_command.h"
58 #include "chrome/test/webdriver/commands/webelement_commands.h"
59 #include "chrome/test/webdriver/commands/window_commands.h"
60 #include "chrome/test/webdriver/webdriver_dispatch.h"
61 #include "chrome/test/webdriver/webdriver_logging.h"
62 #include "chrome/test/webdriver/webdriver_session_manager.h"
63 #include "chrome/test/webdriver/webdriver_switches.h"
64 #include "chrome/test/webdriver/webdriver_util.h"
65 #include "third_party/mongoose/mongoose.h"
67 #if defined(OS_WIN)
68 #include <time.h>
69 #elif defined(OS_POSIX)
70 #include <errno.h>
71 #include <sys/time.h>
72 #include <sys/types.h>
73 #include <sys/wait.h>
74 #endif
76 namespace webdriver {
78 namespace {
80 void InitCallbacks(Dispatcher* dispatcher,
81 base::WaitableEvent* shutdown_event,
82 bool forbid_other_requests) {
83 dispatcher->AddShutdown("/shutdown", shutdown_event);
84 dispatcher->AddStatus("/status");
85 dispatcher->AddLog("/log");
87 dispatcher->Add<CreateSession>("/session");
89 // WebElement commands
90 dispatcher->Add<FindOneElementCommand>( "/session/*/element");
91 dispatcher->Add<FindManyElementsCommand>("/session/*/elements");
92 dispatcher->Add<ActiveElementCommand>( "/session/*/element/active");
93 dispatcher->Add<FindOneElementCommand>( "/session/*/element/*/element");
94 dispatcher->Add<FindManyElementsCommand>("/session/*/elements/*/elements");
95 dispatcher->Add<ElementAttributeCommand>("/session/*/element/*/attribute/*");
96 dispatcher->Add<ElementCssCommand>( "/session/*/element/*/css/*");
97 dispatcher->Add<ElementClearCommand>( "/session/*/element/*/clear");
98 dispatcher->Add<ElementDisplayedCommand>("/session/*/element/*/displayed");
99 dispatcher->Add<ElementEnabledCommand>( "/session/*/element/*/enabled");
100 dispatcher->Add<ElementEqualsCommand>( "/session/*/element/*/equals/*");
101 dispatcher->Add<ElementLocationCommand>( "/session/*/element/*/location");
102 dispatcher->Add<ElementLocationInViewCommand>(
103 "/session/*/element/*/location_in_view");
104 dispatcher->Add<ElementNameCommand>( "/session/*/element/*/name");
105 dispatcher->Add<ElementSelectedCommand>("/session/*/element/*/selected");
106 dispatcher->Add<ElementSizeCommand>( "/session/*/element/*/size");
107 dispatcher->Add<ElementSubmitCommand>( "/session/*/element/*/submit");
108 dispatcher->Add<ElementTextCommand>( "/session/*/element/*/text");
109 dispatcher->Add<ElementToggleCommand>( "/session/*/element/*/toggle");
110 dispatcher->Add<ElementValueCommand>( "/session/*/element/*/value");
112 dispatcher->Add<ScreenshotCommand>("/session/*/screenshot");
114 // Mouse Commands
115 dispatcher->Add<MoveAndClickCommand>("/session/*/element/*/click");
116 dispatcher->Add<DragCommand>( "/session/*/element/*/drag");
117 dispatcher->Add<HoverCommand>( "/session/*/element/*/hover");
119 dispatcher->Add<MoveToCommand>( "/session/*/moveto");
120 dispatcher->Add<ClickCommand>( "/session/*/click");
121 dispatcher->Add<ButtonDownCommand>( "/session/*/buttondown");
122 dispatcher->Add<ButtonUpCommand>( "/session/*/buttonup");
123 dispatcher->Add<DoubleClickCommand>("/session/*/doubleclick");
125 // All session based commands should be listed after the element based
126 // commands to avoid potential mapping conflicts from an overzealous
127 // wildcard match. For example, /session/*/title maps to the handler to
128 // fetch the page title. If mapped first, this would overwrite the handler
129 // for /session/*/element/*/attribute/title, which should fetch the title
130 // attribute of the element.
131 dispatcher->Add<AcceptAlertCommand>( "/session/*/accept_alert");
132 dispatcher->Add<AlertTextCommand>( "/session/*/alert_text");
133 dispatcher->Add<BackCommand>( "/session/*/back");
134 dispatcher->Add<DismissAlertCommand>( "/session/*/dismiss_alert");
135 dispatcher->Add<ExecuteCommand>( "/session/*/execute");
136 dispatcher->Add<ExecuteAsyncScriptCommand>(
137 "/session/*/execute_async");
138 dispatcher->Add<ForwardCommand>( "/session/*/forward");
139 dispatcher->Add<SwitchFrameCommand>( "/session/*/frame");
140 dispatcher->Add<KeysCommand>( "/session/*/keys");
141 dispatcher->Add<RefreshCommand>( "/session/*/refresh");
142 dispatcher->Add<SourceCommand>( "/session/*/source");
143 dispatcher->Add<TitleCommand>( "/session/*/title");
144 dispatcher->Add<URLCommand>( "/session/*/url");
145 dispatcher->Add<WindowCommand>( "/session/*/window");
146 dispatcher->Add<WindowHandleCommand>( "/session/*/window_handle");
147 dispatcher->Add<WindowHandlesCommand>("/session/*/window_handles");
148 dispatcher->Add<WindowSizeCommand>( "/session/*/window/*/size");
149 dispatcher->Add<WindowPositionCommand>(
150 "/session/*/window/*/position");
151 dispatcher->Add<WindowMaximizeCommand>(
152 "/session/*/window/*/maximize");
153 dispatcher->Add<SetAsyncScriptTimeoutCommand>(
154 "/session/*/timeouts/async_script");
155 dispatcher->Add<ImplicitWaitCommand>( "/session/*/timeouts/implicit_wait");
156 dispatcher->Add<LogCommand>( "/session/*/log");
157 dispatcher->Add<FileUploadCommand>( "/session/*/file");
159 // Cookie functions.
160 dispatcher->Add<CookieCommand>( "/session/*/cookie");
161 dispatcher->Add<NamedCookieCommand>("/session/*/cookie/*");
163 dispatcher->Add<BrowserConnectionCommand>("/session/*/browser_connection");
164 dispatcher->Add<AppCacheStatusCommand>("/session/*/application_cache/status");
166 // Chrome-specific commands.
167 dispatcher->Add<ExtensionsCommand>("/session/*/chrome/extensions");
168 dispatcher->Add<ExtensionCommand>("/session/*/chrome/extension/*");
169 dispatcher->Add<ViewsCommand>("/session/*/chrome/views");
170 #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
171 dispatcher->Add<HeapProfilerDumpCommand>(
172 "/session/*/chrome/heapprofilerdump");
173 #endif // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
175 // HTML5 functions.
176 dispatcher->Add<HTML5LocationCommand>("/session/*/location");
177 dispatcher->Add<LocalStorageCommand>("/session/*/local_storage");
178 dispatcher->Add<LocalStorageSizeCommand>("/session/*/local_storage/size");
179 dispatcher->Add<LocalStorageKeyCommand>("/session/*/local_storage/key*");
180 dispatcher->Add<SessionStorageCommand>("/session/*/session_storage");
181 dispatcher->Add<SessionStorageSizeCommand>("/session/*/session_storage/size");
182 dispatcher->Add<SessionStorageKeyCommand>("/session/*/session_storage/key*");
184 // Since the /session/* is a wild card that would match the above URIs, this
185 // line MUST be after all other webdriver command callbacks.
186 dispatcher->Add<SessionWithID>("/session/*");
188 if (forbid_other_requests)
189 dispatcher->ForbidAllOtherRequests();
192 void* ProcessHttpRequest(mg_event event_raised,
193 struct mg_connection* connection,
194 const struct mg_request_info* request_info) {
195 bool handler_result_code = false;
196 if (event_raised == MG_NEW_REQUEST) {
197 handler_result_code =
198 reinterpret_cast<Dispatcher*>(request_info->user_data)->
199 ProcessHttpRequest(connection, request_info);
202 return reinterpret_cast<void*>(handler_result_code);
205 void MakeMongooseOptions(const std::string& port,
206 const std::string& root,
207 int http_threads,
208 bool enable_keep_alive,
209 std::vector<std::string>* out_options) {
210 out_options->push_back("listening_ports");
211 out_options->push_back(port);
212 out_options->push_back("enable_keep_alive");
213 out_options->push_back(enable_keep_alive ? "yes" : "no");
214 out_options->push_back("num_threads");
215 out_options->push_back(base::IntToString(http_threads));
216 if (!root.empty()) {
217 out_options->push_back("document_root");
218 out_options->push_back(root);
222 } // namespace
224 int RunChromeDriver() {
225 base::AtExitManager exit;
226 base::WaitableEvent shutdown_event(false, false);
227 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
229 #if defined(OS_POSIX)
230 if (!IgnoreSigPipe()) {
231 LOG(ERROR) << "Failed to ignore SIGPIPE";
232 return EXIT_FAILURE;
234 #endif // defined(OS_POSIX)
235 srand((unsigned int)time(NULL));
237 // Register Chrome's path provider so that the AutomationProxy will find our
238 // built Chrome.
239 chrome::RegisterPathProvider();
240 TestTimeouts::Initialize();
242 // Parse command line flags.
243 std::string port = "9515";
244 FilePath log_path;
245 std::string root;
246 std::string url_base;
247 int http_threads = 4;
248 bool enable_keep_alive = false;
249 if (cmd_line->HasSwitch("port"))
250 port = cmd_line->GetSwitchValueASCII("port");
251 if (cmd_line->HasSwitch("log-path"))
252 log_path = cmd_line->GetSwitchValuePath("log-path");
253 // The 'root' flag allows the user to specify a location to serve files from.
254 // If it is not given, a callback will be registered to forbid all file
255 // requests.
256 if (cmd_line->HasSwitch("root"))
257 root = cmd_line->GetSwitchValueASCII("root");
258 if (cmd_line->HasSwitch("url-base"))
259 url_base = cmd_line->GetSwitchValueASCII("url-base");
260 if (cmd_line->HasSwitch("http-threads")) {
261 if (!base::StringToInt(cmd_line->GetSwitchValueASCII("http-threads"),
262 &http_threads)) {
263 std::cerr << "'http-threads' option must be an integer";
264 return 1;
267 if (cmd_line->HasSwitch(kEnableKeepAlive))
268 enable_keep_alive = true;
270 bool logging_success = InitWebDriverLogging(log_path, kAllLogLevel);
271 std::string chromedriver_info = base::StringPrintf(
272 "ChromeDriver %s", chrome::kChromeVersion);
273 FilePath chromedriver_exe;
274 if (PathService::Get(base::FILE_EXE, &chromedriver_exe)) {
275 chromedriver_info += base::StringPrintf(
276 " %" PRFilePath, chromedriver_exe.value().c_str());
278 FileLog::Get()->Log(kInfoLogLevel, base::Time::Now(), chromedriver_info);
281 SessionManager* manager = SessionManager::GetInstance();
282 manager->set_port(port);
283 manager->set_url_base(url_base);
285 Dispatcher dispatcher(url_base);
286 InitCallbacks(&dispatcher, &shutdown_event, root.empty());
288 std::vector<std::string> args;
289 MakeMongooseOptions(port, root, http_threads, enable_keep_alive, &args);
290 scoped_array<const char*> options(new const char*[args.size() + 1]);
291 for (size_t i = 0; i < args.size(); ++i) {
292 options[i] = args[i].c_str();
294 options[args.size()] = NULL;
296 // Initialize SHTTPD context.
297 // Listen on port 9515 or port specified on command line.
298 // TODO(jmikhail) Maybe add port 9516 as a secure connection.
299 struct mg_context* ctx = mg_start(&ProcessHttpRequest,
300 &dispatcher,
301 options.get());
302 if (ctx == NULL) {
303 std::cerr << "Port already in use. Exiting..." << std::endl;
304 #if defined(OS_WIN)
305 return WSAEADDRINUSE;
306 #else
307 return EADDRINUSE;
308 #endif
311 // The tests depend on parsing the first line ChromeDriver outputs,
312 // so all other logging should happen after this.
313 if (!cmd_line->HasSwitch("silent")) {
314 std::cout << "Started ChromeDriver" << std::endl
315 << "port=" << port << std::endl
316 << "version=" << chrome::kChromeVersion << std::endl;
317 if (logging_success)
318 std::cout << "log=" << FileLog::Get()->path().value() << std::endl;
319 else
320 std::cout << "Log file could not be created" << std::endl;
323 // Run until we receive command to shutdown.
324 // Don't call mg_stop because mongoose will hang if clients are still
325 // connected when keep-alive is enabled.
326 shutdown_event.Wait();
328 return (EXIT_SUCCESS);
331 } // namespace webdriver
333 int main(int argc, char *argv[]) {
334 CommandLine::Init(argc, argv);
335 return webdriver::RunChromeDriver();