Home | History | Annotate | Download | only in server
      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 
     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 
     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 
     57 CommandMapping::~CommandMapping() {}
     58 
     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 
     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 
    545 HttpHandler::~HttpHandler() {}
    546 
    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 
    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 
    581 Command HttpHandler::WrapToCommand(
    582     const char* name,
    583     const WindowCommand& window_command) {
    584   return WrapToCommand(name, base::Bind(&ExecuteWindowCommand, window_command));
    585 }
    586 
    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 
    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 
    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 
    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 
    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 
    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