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.
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"
69 #elif defined(OS_POSIX)
72 #include <sys/types.h>
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");
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");
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))
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
,
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
));
217 out_options
->push_back("document_root");
218 out_options
->push_back(root
);
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";
234 #endif // defined(OS_POSIX)
235 srand((unsigned int)time(NULL
));
237 // Register Chrome's path provider so that the AutomationProxy will find our
239 chrome::RegisterPathProvider();
240 TestTimeouts::Initialize();
242 // Parse command line flags.
243 std::string port
= "9515";
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
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"),
263 std::cerr
<< "'http-threads' option must be an integer";
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
,
303 std::cerr
<< "Port already in use. Exiting..." << std::endl
;
305 return WSAEADDRINUSE
;
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
;
318 std::cout
<< "log=" << FileLog::Get()->path().value() << std::endl
;
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();