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