• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 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 "chrome/test/chromedriver/server/http_handler.h"
6 
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"  // For CHECK macros.
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/message_loop/message_loop_proxy.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/sys_info.h"
19 #include "base/values.h"
20 #include "chrome/test/chromedriver/alert_commands.h"
21 #include "chrome/test/chromedriver/chrome/adb_impl.h"
22 #include "chrome/test/chromedriver/chrome/device_manager.h"
23 #include "chrome/test/chromedriver/chrome/status.h"
24 #include "chrome/test/chromedriver/net/port_server.h"
25 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
26 #include "chrome/test/chromedriver/session.h"
27 #include "chrome/test/chromedriver/session_thread_map.h"
28 #include "chrome/test/chromedriver/util.h"
29 #include "chrome/test/chromedriver/version.h"
30 #include "net/server/http_server_request_info.h"
31 #include "net/server/http_server_response_info.h"
32 
33 #if defined(OS_MACOSX)
34 #include "base/mac/scoped_nsautorelease_pool.h"
35 #endif
36 
37 namespace {
38 
39 const char kLocalStorage[] = "localStorage";
40 const char kSessionStorage[] = "sessionStorage";
41 const char kShutdownPath[] = "shutdown";
42 
UnimplementedCommand(const base::DictionaryValue & params,const std::string & session_id,const CommandCallback & callback)43 void UnimplementedCommand(
44     const base::DictionaryValue& params,
45     const std::string& session_id,
46     const CommandCallback& callback) {
47   callback.Run(Status(kUnknownCommand), scoped_ptr<base::Value>(), session_id);
48 }
49 
50 }  // namespace
51 
CommandMapping(HttpMethod method,const std::string & path_pattern,const Command & command)52 CommandMapping::CommandMapping(HttpMethod method,
53                                const std::string& path_pattern,
54                                const Command& command)
55     : method(method), path_pattern(path_pattern), command(command) {}
56 
~CommandMapping()57 CommandMapping::~CommandMapping() {}
58 
HttpHandler(const std::string & url_base)59 HttpHandler::HttpHandler(const std::string& url_base)
60     : url_base_(url_base),
61       received_shutdown_(false),
62       command_map_(new CommandMap()),
63       weak_ptr_factory_(this) {}
64 
HttpHandler(const base::Closure & quit_func,const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,const std::string & url_base,int adb_port,scoped_ptr<PortServer> port_server)65 HttpHandler::HttpHandler(
66     const base::Closure& quit_func,
67     const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
68     const std::string& url_base,
69     int adb_port,
70     scoped_ptr<PortServer> port_server)
71     : quit_func_(quit_func),
72       url_base_(url_base),
73       received_shutdown_(false),
74       weak_ptr_factory_(this) {
75 #if defined(OS_MACOSX)
76   base::mac::ScopedNSAutoreleasePool autorelease_pool;
77 #endif
78   context_getter_ = new URLRequestContextGetter(io_task_runner);
79   socket_factory_ = CreateSyncWebSocketFactory(context_getter_.get());
80   adb_.reset(new AdbImpl(io_task_runner, adb_port));
81   device_manager_.reset(new DeviceManager(adb_.get()));
82   port_server_ = port_server.Pass();
83   port_manager_.reset(new PortManager(12000, 13000));
84 
85   CommandMapping commands[] = {
86       CommandMapping(
87           kPost,
88           internal::kNewSessionPathPattern,
89           base::Bind(&ExecuteCreateSession,
90                      &session_thread_map_,
91                      WrapToCommand(
92                          "InitSession",
93                          base::Bind(&ExecuteInitSession,
94                                     InitSessionParams(context_getter_,
95                                                       socket_factory_,
96                                                       device_manager_.get(),
97                                                       port_server_.get(),
98                                                       port_manager_.get()))))),
99       CommandMapping(kGet,
100                      "session/:sessionId",
101                      WrapToCommand("GetSessionCapabilities",
102                                    base::Bind(&ExecuteGetSessionCapabilities))),
103       CommandMapping(kDelete,
104                      "session/:sessionId",
105                      base::Bind(&ExecuteSessionCommand,
106                                 &session_thread_map_,
107                                 "Quit",
108                                 base::Bind(&ExecuteQuit, false),
109                                 true)),
110       CommandMapping(kGet,
111                      "session/:sessionId/window_handle",
112                      WrapToCommand("GetWindow",
113                                    base::Bind(&ExecuteGetCurrentWindowHandle))),
114       CommandMapping(
115           kGet,
116           "session/:sessionId/window_handles",
117           WrapToCommand("GetWindows", base::Bind(&ExecuteGetWindowHandles))),
118       CommandMapping(kPost,
119                      "session/:sessionId/url",
120                      WrapToCommand("Navigate", base::Bind(&ExecuteGet))),
121       CommandMapping(kPost,
122                      "session/:sessionId/chromium/launch_app",
123                      WrapToCommand("LaunchApp", base::Bind(&ExecuteLaunchApp))),
124       CommandMapping(kGet,
125                      "session/:sessionId/alert",
126                      WrapToCommand("IsAlertOpen",
127                                    base::Bind(&ExecuteAlertCommand,
128                                               base::Bind(&ExecuteGetAlert)))),
129       CommandMapping(
130           kPost,
131           "session/:sessionId/dismiss_alert",
132           WrapToCommand("DismissAlert",
133                         base::Bind(&ExecuteAlertCommand,
134                                    base::Bind(&ExecuteDismissAlert)))),
135       CommandMapping(
136           kPost,
137           "session/:sessionId/accept_alert",
138           WrapToCommand("AcceptAlert",
139                         base::Bind(&ExecuteAlertCommand,
140                                    base::Bind(&ExecuteAcceptAlert)))),
141       CommandMapping(
142           kGet,
143           "session/:sessionId/alert_text",
144           WrapToCommand("GetAlertMessage",
145                         base::Bind(&ExecuteAlertCommand,
146                                    base::Bind(&ExecuteGetAlertText)))),
147       CommandMapping(
148           kPost,
149           "session/:sessionId/alert_text",
150           WrapToCommand("SetAlertPrompt",
151                         base::Bind(&ExecuteAlertCommand,
152                                    base::Bind(&ExecuteSetAlertValue)))),
153       CommandMapping(kPost,
154                      "session/:sessionId/forward",
155                      WrapToCommand("GoForward", base::Bind(&ExecuteGoForward))),
156       CommandMapping(kPost,
157                      "session/:sessionId/back",
158                      WrapToCommand("GoBack", base::Bind(&ExecuteGoBack))),
159       CommandMapping(kPost,
160                      "session/:sessionId/refresh",
161                      WrapToCommand("Refresh", base::Bind(&ExecuteRefresh))),
162       CommandMapping(
163           kPost,
164           "session/:sessionId/execute",
165           WrapToCommand("ExecuteScript", base::Bind(&ExecuteExecuteScript))),
166       CommandMapping(kPost,
167                      "session/:sessionId/execute_async",
168                      WrapToCommand("ExecuteAsyncScript",
169                                    base::Bind(&ExecuteExecuteAsyncScript))),
170       CommandMapping(
171           kGet,
172           "session/:sessionId/url",
173           WrapToCommand("GetUrl", base::Bind(&ExecuteGetCurrentUrl))),
174       CommandMapping(kGet,
175                      "session/:sessionId/title",
176                      WrapToCommand("GetTitle", base::Bind(&ExecuteGetTitle))),
177       CommandMapping(
178           kGet,
179           "session/:sessionId/source",
180           WrapToCommand("GetSource", base::Bind(&ExecuteGetPageSource))),
181       CommandMapping(
182           kGet,
183           "session/:sessionId/screenshot",
184           WrapToCommand("Screenshot", base::Bind(&ExecuteScreenshot))),
185       CommandMapping(
186           kGet,
187           "session/:sessionId/chromium/heap_snapshot",
188           WrapToCommand("HeapSnapshot", base::Bind(&ExecuteTakeHeapSnapshot))),
189       CommandMapping(kPost,
190                      "session/:sessionId/visible",
191                      base::Bind(&UnimplementedCommand)),
192       CommandMapping(kGet,
193                      "session/:sessionId/visible",
194                      base::Bind(&UnimplementedCommand)),
195       CommandMapping(
196           kPost,
197           "session/:sessionId/element",
198           WrapToCommand("FindElement", base::Bind(&ExecuteFindElement, 50))),
199       CommandMapping(
200           kPost,
201           "session/:sessionId/elements",
202           WrapToCommand("FindElements", base::Bind(&ExecuteFindElements, 50))),
203       CommandMapping(kPost,
204                      "session/:sessionId/element/active",
205                      WrapToCommand("GetActiveElement",
206                                    base::Bind(&ExecuteGetActiveElement))),
207       CommandMapping(kPost,
208                      "session/:sessionId/element/:id/element",
209                      WrapToCommand("FindChildElement",
210                                    base::Bind(&ExecuteFindChildElement, 50))),
211       CommandMapping(kPost,
212                      "session/:sessionId/element/:id/elements",
213                      WrapToCommand("FindChildElements",
214                                    base::Bind(&ExecuteFindChildElements, 50))),
215       CommandMapping(
216           kPost,
217           "session/:sessionId/element/:id/click",
218           WrapToCommand("ClickElement", base::Bind(&ExecuteClickElement))),
219       CommandMapping(
220           kPost,
221           "session/:sessionId/element/:id/clear",
222           WrapToCommand("ClearElement", base::Bind(&ExecuteClearElement))),
223       CommandMapping(
224           kPost,
225           "session/:sessionId/element/:id/submit",
226           WrapToCommand("SubmitElement", base::Bind(&ExecuteSubmitElement))),
227       CommandMapping(
228           kGet,
229           "session/:sessionId/element/:id/text",
230           WrapToCommand("GetElementText", base::Bind(&ExecuteGetElementText))),
231       CommandMapping(
232           kPost,
233           "session/:sessionId/element/:id/value",
234           WrapToCommand("TypeElement", base::Bind(&ExecuteSendKeysToElement))),
235       CommandMapping(
236           kPost,
237           "session/:sessionId/file",
238           WrapToCommand("UploadFile", base::Bind(&ExecuteUploadFile))),
239       CommandMapping(kGet,
240                      "session/:sessionId/element/:id/value",
241                      WrapToCommand("GetElementValue",
242                                    base::Bind(&ExecuteGetElementValue))),
243       CommandMapping(kGet,
244                      "session/:sessionId/element/:id/name",
245                      WrapToCommand("GetElementTagName",
246                                    base::Bind(&ExecuteGetElementTagName))),
247       CommandMapping(kGet,
248                      "session/:sessionId/element/:id/selected",
249                      WrapToCommand("IsElementSelected",
250                                    base::Bind(&ExecuteIsElementSelected))),
251       CommandMapping(kGet,
252                      "session/:sessionId/element/:id/enabled",
253                      WrapToCommand("IsElementEnabled",
254                                    base::Bind(&ExecuteIsElementEnabled))),
255       CommandMapping(kGet,
256                      "session/:sessionId/element/:id/displayed",
257                      WrapToCommand("IsElementDisplayed",
258                                    base::Bind(&ExecuteIsElementDisplayed))),
259       CommandMapping(
260           kPost,
261           "session/:sessionId/element/:id/hover",
262           WrapToCommand("HoverElement", base::Bind(&ExecuteHoverOverElement))),
263       CommandMapping(kGet,
264                      "session/:sessionId/element/:id/location",
265                      WrapToCommand("GetElementLocation",
266                                    base::Bind(&ExecuteGetElementLocation))),
267       CommandMapping(
268           kGet,
269           "session/:sessionId/element/:id/location_in_view",
270           WrapToCommand(
271               "GetElementLocationInView",
272               base::Bind(&ExecuteGetElementLocationOnceScrolledIntoView))),
273       CommandMapping(
274           kGet,
275           "session/:sessionId/element/:id/size",
276           WrapToCommand("GetElementSize", base::Bind(&ExecuteGetElementSize))),
277       CommandMapping(kGet,
278                      "session/:sessionId/element/:id/attribute/:name",
279                      WrapToCommand("GetElementAttribute",
280                                    base::Bind(&ExecuteGetElementAttribute))),
281       CommandMapping(
282           kGet,
283           "session/:sessionId/element/:id/equals/:other",
284           WrapToCommand("IsElementEqual", base::Bind(&ExecuteElementEquals))),
285       CommandMapping(
286           kGet,
287           "session/:sessionId/cookie",
288           WrapToCommand("GetCookies", base::Bind(&ExecuteGetCookies))),
289       CommandMapping(kPost,
290                      "session/:sessionId/cookie",
291                      WrapToCommand("AddCookie", base::Bind(&ExecuteAddCookie))),
292       CommandMapping(kDelete,
293                      "session/:sessionId/cookie",
294                      WrapToCommand("DeleteAllCookies",
295                                    base::Bind(&ExecuteDeleteAllCookies))),
296       CommandMapping(
297           kDelete,
298           "session/:sessionId/cookie/:name",
299           WrapToCommand("DeleteCookie", base::Bind(&ExecuteDeleteCookie))),
300       CommandMapping(
301           kPost,
302           "session/:sessionId/frame",
303           WrapToCommand("SwitchToFrame", base::Bind(&ExecuteSwitchToFrame))),
304       CommandMapping(
305           kPost,
306           "session/:sessionId/frame/parent",
307           WrapToCommand("SwitchToParentFrame",
308                         base::Bind(&ExecuteSwitchToParentFrame))),
309       CommandMapping(
310           kPost,
311           "session/:sessionId/window",
312           WrapToCommand("SwitchToWindow", base::Bind(&ExecuteSwitchToWindow))),
313       CommandMapping(
314           kGet,
315           "session/:sessionId/window/:windowHandle/size",
316           WrapToCommand("GetWindowSize", base::Bind(&ExecuteGetWindowSize))),
317       CommandMapping(kGet,
318                      "session/:sessionId/window/:windowHandle/position",
319                      WrapToCommand("GetWindowPosition",
320                                    base::Bind(&ExecuteGetWindowPosition))),
321       CommandMapping(
322           kPost,
323           "session/:sessionId/window/:windowHandle/size",
324           WrapToCommand("SetWindowSize", base::Bind(&ExecuteSetWindowSize))),
325       CommandMapping(kPost,
326                      "session/:sessionId/window/:windowHandle/position",
327                      WrapToCommand("SetWindowPosition",
328                                    base::Bind(&ExecuteSetWindowPosition))),
329       CommandMapping(
330           kPost,
331           "session/:sessionId/window/:windowHandle/maximize",
332           WrapToCommand("MaximizeWindow", base::Bind(&ExecuteMaximizeWindow))),
333       CommandMapping(kDelete,
334                      "session/:sessionId/window",
335                      WrapToCommand("CloseWindow", base::Bind(&ExecuteClose))),
336       CommandMapping(kPost,
337                      "session/:sessionId/element/:id/drag",
338                      base::Bind(&UnimplementedCommand)),
339       CommandMapping(
340           kGet,
341           "session/:sessionId/element/:id/css/:propertyName",
342           WrapToCommand("GetElementCSSProperty",
343                         base::Bind(&ExecuteGetElementValueOfCSSProperty))),
344       CommandMapping(
345           kPost,
346           "session/:sessionId/timeouts/implicit_wait",
347           WrapToCommand("SetImplicitWait", base::Bind(&ExecuteImplicitlyWait))),
348       CommandMapping(kPost,
349                      "session/:sessionId/timeouts/async_script",
350                      WrapToCommand("SetScriptTimeout",
351                                    base::Bind(&ExecuteSetScriptTimeout))),
352       CommandMapping(
353           kPost,
354           "session/:sessionId/timeouts",
355           WrapToCommand("SetTimeout", base::Bind(&ExecuteSetTimeout))),
356       CommandMapping(kPost,
357                      "session/:sessionId/execute_sql",
358                      base::Bind(&UnimplementedCommand)),
359       CommandMapping(
360           kGet,
361           "session/:sessionId/location",
362           WrapToCommand("GetGeolocation", base::Bind(&ExecuteGetLocation))),
363       CommandMapping(
364           kPost,
365           "session/:sessionId/location",
366           WrapToCommand("SetGeolocation", base::Bind(&ExecuteSetLocation))),
367       CommandMapping(kGet,
368                      "session/:sessionId/application_cache/status",
369                      base::Bind(&ExecuteGetStatus)),
370       CommandMapping(kGet,
371                      "session/:sessionId/browser_connection",
372                      base::Bind(&UnimplementedCommand)),
373       CommandMapping(kPost,
374                      "session/:sessionId/browser_connection",
375                      base::Bind(&UnimplementedCommand)),
376       CommandMapping(
377           kGet,
378           "session/:sessionId/local_storage/key/:key",
379           WrapToCommand("GetLocalStorageItem",
380                         base::Bind(&ExecuteGetStorageItem, kLocalStorage))),
381       CommandMapping(
382           kDelete,
383           "session/:sessionId/local_storage/key/:key",
384           WrapToCommand("RemoveLocalStorageItem",
385                         base::Bind(&ExecuteRemoveStorageItem, kLocalStorage))),
386       CommandMapping(
387           kGet,
388           "session/:sessionId/local_storage",
389           WrapToCommand("GetLocalStorageKeys",
390                         base::Bind(&ExecuteGetStorageKeys, kLocalStorage))),
391       CommandMapping(
392           kPost,
393           "session/:sessionId/local_storage",
394           WrapToCommand("SetLocalStorageKeys",
395                         base::Bind(&ExecuteSetStorageItem, kLocalStorage))),
396       CommandMapping(
397           kDelete,
398           "session/:sessionId/local_storage",
399           WrapToCommand("ClearLocalStorage",
400                         base::Bind(&ExecuteClearStorage, kLocalStorage))),
401       CommandMapping(
402           kGet,
403           "session/:sessionId/local_storage/size",
404           WrapToCommand("GetLocalStorageSize",
405                         base::Bind(&ExecuteGetStorageSize, kLocalStorage))),
406       CommandMapping(
407           kGet,
408           "session/:sessionId/session_storage/key/:key",
409           WrapToCommand("GetSessionStorageItem",
410                         base::Bind(&ExecuteGetStorageItem, kSessionStorage))),
411       CommandMapping(kDelete,
412                      "session/:sessionId/session_storage/key/:key",
413                      WrapToCommand("RemoveSessionStorageItem",
414                                    base::Bind(&ExecuteRemoveStorageItem,
415                                               kSessionStorage))),
416       CommandMapping(
417           kGet,
418           "session/:sessionId/session_storage",
419           WrapToCommand("GetSessionStorageKeys",
420                         base::Bind(&ExecuteGetStorageKeys, kSessionStorage))),
421       CommandMapping(
422           kPost,
423           "session/:sessionId/session_storage",
424           WrapToCommand("SetSessionStorageItem",
425                         base::Bind(&ExecuteSetStorageItem, kSessionStorage))),
426       CommandMapping(
427           kDelete,
428           "session/:sessionId/session_storage",
429           WrapToCommand("ClearSessionStorage",
430                         base::Bind(&ExecuteClearStorage, kSessionStorage))),
431       CommandMapping(
432           kGet,
433           "session/:sessionId/session_storage/size",
434           WrapToCommand("GetSessionStorageSize",
435                         base::Bind(&ExecuteGetStorageSize, kSessionStorage))),
436       CommandMapping(kGet,
437                      "session/:sessionId/orientation",
438                      base::Bind(&UnimplementedCommand)),
439       CommandMapping(kPost,
440                      "session/:sessionId/orientation",
441                      base::Bind(&UnimplementedCommand)),
442       CommandMapping(kPost,
443                      "session/:sessionId/click",
444                      WrapToCommand("Click", base::Bind(&ExecuteMouseClick))),
445       CommandMapping(
446           kPost,
447           "session/:sessionId/doubleclick",
448           WrapToCommand("DoubleClick", base::Bind(&ExecuteMouseDoubleClick))),
449       CommandMapping(
450           kPost,
451           "session/:sessionId/buttondown",
452           WrapToCommand("MouseDown", base::Bind(&ExecuteMouseButtonDown))),
453       CommandMapping(
454           kPost,
455           "session/:sessionId/buttonup",
456           WrapToCommand("MouseUp", base::Bind(&ExecuteMouseButtonUp))),
457       CommandMapping(
458           kPost,
459           "session/:sessionId/moveto",
460           WrapToCommand("MouseMove", base::Bind(&ExecuteMouseMoveTo))),
461       CommandMapping(
462           kPost,
463           "session/:sessionId/keys",
464           WrapToCommand("Type", base::Bind(&ExecuteSendKeysToActiveElement))),
465       CommandMapping(kGet,
466                      "session/:sessionId/ime/available_engines",
467                      base::Bind(&UnimplementedCommand)),
468       CommandMapping(kGet,
469                      "session/:sessionId/ime/active_engine",
470                      base::Bind(&UnimplementedCommand)),
471       CommandMapping(kGet,
472                      "session/:sessionId/ime/activated",
473                      base::Bind(&UnimplementedCommand)),
474       CommandMapping(kPost,
475                      "session/:sessionId/ime/deactivate",
476                      base::Bind(&UnimplementedCommand)),
477       CommandMapping(kPost,
478                      "session/:sessionId/ime/activate",
479                      base::Bind(&UnimplementedCommand)),
480       CommandMapping(kPost,
481                      "session/:sessionId/touch/click",
482                      WrapToCommand("Tap", base::Bind(&ExecuteTouchSingleTap))),
483       CommandMapping(kPost,
484                      "session/:sessionId/touch/down",
485                      WrapToCommand("TouchDown", base::Bind(&ExecuteTouchDown))),
486       CommandMapping(kPost,
487                      "session/:sessionId/touch/up",
488                      WrapToCommand("TouchUp", base::Bind(&ExecuteTouchUp))),
489       CommandMapping(kPost,
490                      "session/:sessionId/touch/move",
491                      WrapToCommand("TouchMove", base::Bind(&ExecuteTouchMove))),
492       CommandMapping(kPost,
493                      "session/:sessionId/touch/scroll",
494                      base::Bind(&UnimplementedCommand)),
495       CommandMapping(kPost,
496                      "session/:sessionId/touch/doubleclick",
497                      base::Bind(&UnimplementedCommand)),
498       CommandMapping(kPost,
499                      "session/:sessionId/touch/longclick",
500                      base::Bind(&UnimplementedCommand)),
501       CommandMapping(kPost,
502                      "session/:sessionId/touch/flick",
503                      WrapToCommand("TouchFlick", base::Bind(&ExecuteFlick))),
504       CommandMapping(kPost,
505                      "session/:sessionId/log",
506                      WrapToCommand("GetLog", base::Bind(&ExecuteGetLog))),
507       CommandMapping(kGet,
508                      "session/:sessionId/log/types",
509                      WrapToCommand("GetLogTypes",
510                                    base::Bind(&ExecuteGetAvailableLogTypes))),
511       CommandMapping(kPost, "logs", base::Bind(&UnimplementedCommand)),
512       CommandMapping(kGet, "status", base::Bind(&ExecuteGetStatus)),
513 
514       // Custom Chrome commands:
515       // Allow quit all to be called with GET or POST.
516       CommandMapping(
517           kGet,
518           kShutdownPath,
519           base::Bind(&ExecuteQuitAll,
520                      WrapToCommand("QuitAll", base::Bind(&ExecuteQuit, true)),
521                      &session_thread_map_)),
522       CommandMapping(
523           kPost,
524           kShutdownPath,
525           base::Bind(&ExecuteQuitAll,
526                      WrapToCommand("QuitAll", base::Bind(&ExecuteQuit, true)),
527                      &session_thread_map_)),
528       CommandMapping(kGet,
529                      "session/:sessionId/is_loading",
530                      WrapToCommand("IsLoading", base::Bind(&ExecuteIsLoading))),
531       CommandMapping(kGet,
532                      "session/:sessionId/autoreport",
533                      WrapToCommand("IsAutoReporting",
534                                    base::Bind(&ExecuteIsAutoReporting))),
535       CommandMapping(kPost,
536                      "session/:sessionId/autoreport",
537                      WrapToCommand(
538                          "SetAutoReporting",
539                          base::Bind(&ExecuteSetAutoReporting))),
540   };
541   command_map_.reset(
542       new CommandMap(commands, commands + arraysize(commands)));
543 }
544 
~HttpHandler()545 HttpHandler::~HttpHandler() {}
546 
Handle(const net::HttpServerRequestInfo & request,const HttpResponseSenderFunc & send_response_func)547 void HttpHandler::Handle(const net::HttpServerRequestInfo& request,
548                          const HttpResponseSenderFunc& send_response_func) {
549   CHECK(thread_checker_.CalledOnValidThread());
550 
551   if (received_shutdown_)
552     return;
553 
554   std::string path = request.path;
555   if (!StartsWithASCII(path, url_base_, true)) {
556     scoped_ptr<net::HttpServerResponseInfo> response(
557         new net::HttpServerResponseInfo(net::HTTP_BAD_REQUEST));
558     response->SetBody("unhandled request", "text/plain");
559     send_response_func.Run(response.Pass());
560     return;
561   }
562 
563   path.erase(0, url_base_.length());
564 
565   HandleCommand(request, path, send_response_func);
566 
567   if (path == kShutdownPath)
568     received_shutdown_ = true;
569 }
570 
WrapToCommand(const char * name,const SessionCommand & session_command)571 Command HttpHandler::WrapToCommand(
572     const char* name,
573     const SessionCommand& session_command) {
574   return base::Bind(&ExecuteSessionCommand,
575                     &session_thread_map_,
576                     name,
577                     session_command,
578                     false);
579 }
580 
WrapToCommand(const char * name,const WindowCommand & window_command)581 Command HttpHandler::WrapToCommand(
582     const char* name,
583     const WindowCommand& window_command) {
584   return WrapToCommand(name, base::Bind(&ExecuteWindowCommand, window_command));
585 }
586 
WrapToCommand(const char * name,const ElementCommand & element_command)587 Command HttpHandler::WrapToCommand(
588     const char* name,
589     const ElementCommand& element_command) {
590   return WrapToCommand(name,
591                        base::Bind(&ExecuteElementCommand, element_command));
592 }
593 
HandleCommand(const net::HttpServerRequestInfo & request,const std::string & trimmed_path,const HttpResponseSenderFunc & send_response_func)594 void HttpHandler::HandleCommand(
595     const net::HttpServerRequestInfo& request,
596     const std::string& trimmed_path,
597     const HttpResponseSenderFunc& send_response_func) {
598   base::DictionaryValue params;
599   std::string session_id;
600   CommandMap::const_iterator iter = command_map_->begin();
601   while (true) {
602     if (iter == command_map_->end()) {
603       scoped_ptr<net::HttpServerResponseInfo> response(
604           new net::HttpServerResponseInfo(net::HTTP_NOT_FOUND));
605       response->SetBody("unknown command: " + trimmed_path, "text/plain");
606       send_response_func.Run(response.Pass());
607       return;
608     }
609     if (internal::MatchesCommand(
610             request.method, trimmed_path, *iter, &session_id, &params)) {
611       break;
612     }
613     ++iter;
614   }
615 
616   if (request.data.length()) {
617     base::DictionaryValue* body_params;
618     scoped_ptr<base::Value> parsed_body(base::JSONReader::Read(request.data));
619     if (!parsed_body || !parsed_body->GetAsDictionary(&body_params)) {
620       scoped_ptr<net::HttpServerResponseInfo> response(
621           new net::HttpServerResponseInfo(net::HTTP_BAD_REQUEST));
622       response->SetBody("missing command parameters", "text/plain");
623       send_response_func.Run(response.Pass());
624       return;
625     }
626     params.MergeDictionary(body_params);
627   }
628 
629   iter->command.Run(params,
630                     session_id,
631                     base::Bind(&HttpHandler::PrepareResponse,
632                                weak_ptr_factory_.GetWeakPtr(),
633                                trimmed_path,
634                                send_response_func));
635 }
636 
PrepareResponse(const std::string & trimmed_path,const HttpResponseSenderFunc & send_response_func,const Status & status,scoped_ptr<base::Value> value,const std::string & session_id)637 void HttpHandler::PrepareResponse(
638     const std::string& trimmed_path,
639     const HttpResponseSenderFunc& send_response_func,
640     const Status& status,
641     scoped_ptr<base::Value> value,
642     const std::string& session_id) {
643   CHECK(thread_checker_.CalledOnValidThread());
644   scoped_ptr<net::HttpServerResponseInfo> response =
645       PrepareResponseHelper(trimmed_path, status, value.Pass(), session_id);
646   send_response_func.Run(response.Pass());
647   if (trimmed_path == kShutdownPath)
648     quit_func_.Run();
649 }
650 
PrepareResponseHelper(const std::string & trimmed_path,const Status & status,scoped_ptr<base::Value> value,const std::string & session_id)651 scoped_ptr<net::HttpServerResponseInfo> HttpHandler::PrepareResponseHelper(
652     const std::string& trimmed_path,
653     const Status& status,
654     scoped_ptr<base::Value> value,
655     const std::string& session_id) {
656   if (status.code() == kUnknownCommand) {
657     scoped_ptr<net::HttpServerResponseInfo> response(
658         new net::HttpServerResponseInfo(net::HTTP_NOT_IMPLEMENTED));
659     response->SetBody("unimplemented command: " + trimmed_path, "text/plain");
660     return response.Pass();
661   }
662 
663   if (status.IsError()) {
664     Status full_status(status);
665     full_status.AddDetails(base::StringPrintf(
666         "Driver info: chromedriver=%s,platform=%s %s %s",
667         kChromeDriverVersion,
668         base::SysInfo::OperatingSystemName().c_str(),
669         base::SysInfo::OperatingSystemVersion().c_str(),
670         base::SysInfo::OperatingSystemArchitecture().c_str()));
671     scoped_ptr<base::DictionaryValue> error(new base::DictionaryValue());
672     error->SetString("message", full_status.message());
673     value.reset(error.release());
674   }
675   if (!value)
676     value.reset(base::Value::CreateNullValue());
677 
678   base::DictionaryValue body_params;
679   body_params.SetInteger("status", status.code());
680   body_params.Set("value", value.release());
681   body_params.SetString("sessionId", session_id);
682   std::string body;
683   base::JSONWriter::WriteWithOptions(
684       &body_params, base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
685       &body);
686   scoped_ptr<net::HttpServerResponseInfo> response(
687       new net::HttpServerResponseInfo(net::HTTP_OK));
688   response->SetBody(body, "application/json; charset=utf-8");
689   return response.Pass();
690 }
691 
692 namespace internal {
693 
694 const char kNewSessionPathPattern[] = "session";
695 
MatchesMethod(HttpMethod command_method,const std::string & method)696 bool MatchesMethod(HttpMethod command_method, const std::string& method) {
697   std::string lower_method = base::StringToLowerASCII(method);
698   switch (command_method) {
699   case kGet:
700     return lower_method == "get";
701   case kPost:
702     return lower_method == "post" || lower_method == "put";
703   case kDelete:
704     return lower_method == "delete";
705   }
706   return false;
707 }
708 
MatchesCommand(const std::string & method,const std::string & path,const CommandMapping & command,std::string * session_id,base::DictionaryValue * out_params)709 bool MatchesCommand(const std::string& method,
710                     const std::string& path,
711                     const CommandMapping& command,
712                     std::string* session_id,
713                     base::DictionaryValue* out_params) {
714   if (!MatchesMethod(command.method, method))
715     return false;
716 
717   std::vector<std::string> path_parts;
718   base::SplitString(path, '/', &path_parts);
719   std::vector<std::string> command_path_parts;
720   base::SplitString(command.path_pattern, '/', &command_path_parts);
721   if (path_parts.size() != command_path_parts.size())
722     return false;
723 
724   base::DictionaryValue params;
725   for (size_t i = 0; i < path_parts.size(); ++i) {
726     CHECK(command_path_parts[i].length());
727     if (command_path_parts[i][0] == ':') {
728       std::string name = command_path_parts[i];
729       name.erase(0, 1);
730       CHECK(name.length());
731       if (name == "sessionId")
732         *session_id = path_parts[i];
733       else
734         params.SetString(name, path_parts[i]);
735     } else if (command_path_parts[i] != path_parts[i]) {
736       return false;
737     }
738   }
739   out_params->MergeDictionary(&params);
740   return true;
741 }
742 
743 }  // namespace internal
744