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