Home | History | Annotate | Download | only in chromedriver
      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/window_commands.h"
      6 
      7 #include <list>
      8 #include <string>
      9 
     10 #include "base/callback.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/threading/platform_thread.h"
     14 #include "base/time/time.h"
     15 #include "base/values.h"
     16 #include "chrome/test/chromedriver/basic_types.h"
     17 #include "chrome/test/chromedriver/chrome/automation_extension.h"
     18 #include "chrome/test/chromedriver/chrome/chrome.h"
     19 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
     20 #include "chrome/test/chromedriver/chrome/devtools_client.h"
     21 #include "chrome/test/chromedriver/chrome/geoposition.h"
     22 #include "chrome/test/chromedriver/chrome/javascript_dialog_manager.h"
     23 #include "chrome/test/chromedriver/chrome/js.h"
     24 #include "chrome/test/chromedriver/chrome/status.h"
     25 #include "chrome/test/chromedriver/chrome/ui_events.h"
     26 #include "chrome/test/chromedriver/chrome/web_view.h"
     27 #include "chrome/test/chromedriver/element_util.h"
     28 #include "chrome/test/chromedriver/session.h"
     29 #include "chrome/test/chromedriver/util.h"
     30 
     31 namespace {
     32 
     33 Status GetMouseButton(const base::DictionaryValue& params,
     34                       MouseButton* button) {
     35   int button_num;
     36   if (!params.GetInteger("button", &button_num)) {
     37     button_num = 0;  // Default to left mouse button.
     38   } else if (button_num < 0 || button_num > 2) {
     39     return Status(kUnknownError,
     40                   base::StringPrintf("invalid button: %d", button_num));
     41   }
     42   *button = static_cast<MouseButton>(button_num);
     43   return Status(kOk);
     44 }
     45 
     46 Status GetUrl(WebView* web_view, const std::string& frame, std::string* url) {
     47   scoped_ptr<base::Value> value;
     48   base::ListValue args;
     49   Status status = web_view->CallFunction(
     50       frame, "function() { return document.URL; }", args, &value);
     51   if (status.IsError())
     52     return status;
     53   if (!value->GetAsString(url))
     54     return Status(kUnknownError, "javascript failed to return the url");
     55   return Status(kOk);
     56 }
     57 
     58 struct Cookie {
     59   Cookie(const std::string& name,
     60          const std::string& value,
     61          const std::string& domain,
     62          const std::string& path,
     63          double expiry,
     64          bool secure,
     65          bool session)
     66       : name(name), value(value), domain(domain), path(path), expiry(expiry),
     67         secure(secure), session(session) {}
     68 
     69   std::string name;
     70   std::string value;
     71   std::string domain;
     72   std::string path;
     73   double expiry;
     74   bool secure;
     75   bool session;
     76 };
     77 
     78 base::DictionaryValue* CreateDictionaryFrom(const Cookie& cookie) {
     79   base::DictionaryValue* dict = new base::DictionaryValue();
     80   dict->SetString("name", cookie.name);
     81   dict->SetString("value", cookie.value);
     82   if (!cookie.domain.empty())
     83     dict->SetString("domain", cookie.domain);
     84   if (!cookie.path.empty())
     85     dict->SetString("path", cookie.path);
     86   if (!cookie.session)
     87     dict->SetDouble("expiry", cookie.expiry);
     88   dict->SetBoolean("secure", cookie.secure);
     89   return dict;
     90 }
     91 
     92 Status GetVisibleCookies(WebView* web_view,
     93                          std::list<Cookie>* cookies) {
     94   scoped_ptr<base::ListValue> internal_cookies;
     95   Status status = web_view->GetCookies(&internal_cookies);
     96   if (status.IsError())
     97     return status;
     98   std::list<Cookie> cookies_tmp;
     99   for (size_t i = 0; i < internal_cookies->GetSize(); ++i) {
    100     base::DictionaryValue* cookie_dict;
    101     if (!internal_cookies->GetDictionary(i, &cookie_dict))
    102       return Status(kUnknownError, "DevTools returns a non-dictionary cookie");
    103 
    104     std::string name;
    105     cookie_dict->GetString("name", &name);
    106     std::string value;
    107     cookie_dict->GetString("value", &value);
    108     std::string domain;
    109     cookie_dict->GetString("domain", &domain);
    110     std::string path;
    111     cookie_dict->GetString("path", &path);
    112     double expiry = 0;
    113     cookie_dict->GetDouble("expires", &expiry);
    114     expiry /= 1000;  // Convert from millisecond to second.
    115     bool session = false;
    116     cookie_dict->GetBoolean("session", &session);
    117     bool secure = false;
    118     cookie_dict->GetBoolean("secure", &secure);
    119 
    120     cookies_tmp.push_back(
    121         Cookie(name, value, domain, path, expiry, secure, session));
    122   }
    123   cookies->swap(cookies_tmp);
    124   return Status(kOk);
    125 }
    126 
    127 Status ScrollCoordinateInToView(
    128     Session* session, WebView* web_view, int x, int y, int* offset_x,
    129     int* offset_y) {
    130   scoped_ptr<base::Value> value;
    131   base::ListValue args;
    132   args.AppendInteger(x);
    133   args.AppendInteger(y);
    134   Status status = web_view->CallFunction(
    135       std::string(),
    136       "function(x, y) {"
    137       "  if (x < window.pageXOffset ||"
    138       "      x >= window.pageXOffset + window.innerWidth ||"
    139       "      y < window.pageYOffset ||"
    140       "      y >= window.pageYOffset + window.innerHeight) {"
    141       "    window.scrollTo(x - window.innerWidth/2, y - window.innerHeight/2);"
    142       "  }"
    143       "  return {"
    144       "    view_x: Math.floor(window.pageXOffset),"
    145       "    view_y: Math.floor(window.pageYOffset),"
    146       "    view_width: Math.floor(window.innerWidth),"
    147       "    view_height: Math.floor(window.innerHeight)};"
    148       "}",
    149       args,
    150       &value);
    151   if (!status.IsOk())
    152     return status;
    153   base::DictionaryValue* view_attrib;
    154   value->GetAsDictionary(&view_attrib);
    155   int view_x, view_y, view_width, view_height;
    156   view_attrib->GetInteger("view_x", &view_x);
    157   view_attrib->GetInteger("view_y", &view_y);
    158   view_attrib->GetInteger("view_width", &view_width);
    159   view_attrib->GetInteger("view_height", &view_height);
    160   *offset_x = x - view_x;
    161   *offset_y = y - view_y;
    162   if (*offset_x < 0 || *offset_x >= view_width || *offset_y < 0 ||
    163       *offset_y >= view_height)
    164     return Status(kUnknownError, "Failed to scroll coordinate into view");
    165   return Status(kOk);
    166 }
    167 
    168 Status ExecuteTouchEvent(
    169     Session* session, WebView* web_view, TouchEventType type,
    170     const base::DictionaryValue& params) {
    171   int x, y;
    172   if (!params.GetInteger("x", &x))
    173     return Status(kUnknownError, "'x' must be an integer");
    174   if (!params.GetInteger("y", &y))
    175     return Status(kUnknownError, "'y' must be an integer");
    176   int relative_x = x;
    177   int relative_y = y;
    178   Status status = ScrollCoordinateInToView(
    179       session, web_view, x, y, &relative_x, &relative_y);
    180   if (!status.IsOk())
    181     return status;
    182   std::list<TouchEvent> events;
    183   events.push_back(
    184       TouchEvent(type, relative_x, relative_y));
    185   return web_view->DispatchTouchEvents(events);
    186 }
    187 
    188 }  // namespace
    189 
    190 Status ExecuteWindowCommand(
    191     const WindowCommand& command,
    192     Session* session,
    193     const base::DictionaryValue& params,
    194     scoped_ptr<base::Value>* value) {
    195   WebView* web_view = NULL;
    196   Status status = session->GetTargetWindow(&web_view);
    197   if (status.IsError())
    198     return status;
    199 
    200   status = web_view->ConnectIfNecessary();
    201   if (status.IsError())
    202     return status;
    203 
    204   status = web_view->HandleReceivedEvents();
    205   if (status.IsError())
    206     return status;
    207 
    208   if (web_view->GetJavaScriptDialogManager()->IsDialogOpen())
    209     return Status(kUnexpectedAlertOpen);
    210 
    211   Status nav_status(kOk);
    212   for (int attempt = 0; attempt < 2; attempt++) {
    213     if (attempt == 1) {
    214       if (status.code() == kNoSuchExecutionContext)
    215         // Switch to main frame and retry command if subframe no longer exists.
    216         session->SwitchToTopFrame();
    217       else
    218         break;
    219     }
    220     nav_status = web_view->WaitForPendingNavigations(
    221         session->GetCurrentFrameId(), session->page_load_timeout, true);
    222     if (nav_status.IsError())
    223       return nav_status;
    224 
    225     status = command.Run(session, web_view, params, value);
    226   }
    227 
    228   nav_status = web_view->WaitForPendingNavigations(
    229       session->GetCurrentFrameId(), session->page_load_timeout, true);
    230 
    231   if (status.IsOk() && nav_status.IsError() &&
    232       nav_status.code() != kUnexpectedAlertOpen)
    233     return nav_status;
    234   if (status.code() == kUnexpectedAlertOpen)
    235     return Status(kOk);
    236   return status;
    237 }
    238 
    239 Status ExecuteGet(
    240     Session* session,
    241     WebView* web_view,
    242     const base::DictionaryValue& params,
    243     scoped_ptr<base::Value>* value) {
    244   std::string url;
    245   if (!params.GetString("url", &url))
    246     return Status(kUnknownError, "'url' must be a string");
    247   return web_view->Load(url);
    248 }
    249 
    250 Status ExecuteExecuteScript(
    251     Session* session,
    252     WebView* web_view,
    253     const base::DictionaryValue& params,
    254     scoped_ptr<base::Value>* value) {
    255   std::string script;
    256   if (!params.GetString("script", &script))
    257     return Status(kUnknownError, "'script' must be a string");
    258   if (script == ":takeHeapSnapshot") {
    259     return web_view->TakeHeapSnapshot(value);
    260   } else if (script == ":startProfile") {
    261     return web_view->StartProfile();
    262   } else if (script == ":endProfile") {
    263     return web_view->EndProfile(value);
    264   } else {
    265     const base::ListValue* args;
    266     if (!params.GetList("args", &args))
    267       return Status(kUnknownError, "'args' must be a list");
    268 
    269     return web_view->CallFunction(session->GetCurrentFrameId(),
    270                                   "function(){" + script + "}", *args, value);
    271   }
    272 }
    273 
    274 Status ExecuteExecuteAsyncScript(
    275     Session* session,
    276     WebView* web_view,
    277     const base::DictionaryValue& params,
    278     scoped_ptr<base::Value>* value) {
    279   std::string script;
    280   if (!params.GetString("script", &script))
    281     return Status(kUnknownError, "'script' must be a string");
    282   const base::ListValue* args;
    283   if (!params.GetList("args", &args))
    284     return Status(kUnknownError, "'args' must be a list");
    285 
    286   return web_view->CallUserAsyncFunction(
    287       session->GetCurrentFrameId(), "function(){" + script + "}", *args,
    288       session->script_timeout, value);
    289 }
    290 
    291 Status ExecuteSwitchToFrame(
    292     Session* session,
    293     WebView* web_view,
    294     const base::DictionaryValue& params,
    295     scoped_ptr<base::Value>* value) {
    296   const base::Value* id;
    297   if (!params.Get("id", &id))
    298     return Status(kUnknownError, "missing 'id'");
    299 
    300   if (id->IsType(base::Value::TYPE_NULL)) {
    301     session->SwitchToTopFrame();
    302     return Status(kOk);
    303   }
    304 
    305   std::string script;
    306   base::ListValue args;
    307   const base::DictionaryValue* id_dict;
    308   if (id->GetAsDictionary(&id_dict)) {
    309     script = "function(elem) { return elem; }";
    310     args.Append(id_dict->DeepCopy());
    311   } else {
    312     script =
    313         "function(xpath) {"
    314         "  return document.evaluate(xpath, document, null, "
    315         "      XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
    316         "}";
    317     std::string xpath = "(/html/body//iframe|/html/frameset/frame)";
    318     std::string id_string;
    319     int id_int;
    320     if (id->GetAsString(&id_string)) {
    321       xpath += base::StringPrintf(
    322           "[@name=\"%s\" or @id=\"%s\"]", id_string.c_str(), id_string.c_str());
    323     } else if (id->GetAsInteger(&id_int)) {
    324       xpath += base::StringPrintf("[%d]", id_int + 1);
    325     } else {
    326       return Status(kUnknownError, "invalid 'id'");
    327     }
    328     args.Append(new base::StringValue(xpath));
    329   }
    330   std::string frame;
    331   Status status = web_view->GetFrameByFunction(
    332       session->GetCurrentFrameId(), script, args, &frame);
    333   if (status.IsError())
    334     return status;
    335 
    336   scoped_ptr<base::Value> result;
    337   status = web_view->CallFunction(
    338       session->GetCurrentFrameId(), script, args, &result);
    339   if (status.IsError())
    340     return status;
    341   const base::DictionaryValue* element;
    342   if (!result->GetAsDictionary(&element))
    343     return Status(kUnknownError, "fail to locate the sub frame element");
    344 
    345   std::string chrome_driver_id = GenerateId();
    346   const char* kSetFrameIdentifier =
    347       "function(frame, id) {"
    348       "  frame.setAttribute('cd_frame_id_', id);"
    349       "}";
    350   base::ListValue new_args;
    351   new_args.Append(element->DeepCopy());
    352   new_args.AppendString(chrome_driver_id);
    353   result.reset(NULL);
    354   status = web_view->CallFunction(
    355       session->GetCurrentFrameId(), kSetFrameIdentifier, new_args, &result);
    356   if (status.IsError())
    357     return status;
    358   session->SwitchToSubFrame(frame, chrome_driver_id);
    359   return Status(kOk);
    360 }
    361 
    362 Status ExecuteSwitchToParentFrame(
    363     Session* session,
    364     WebView* web_view,
    365     const base::DictionaryValue& params,
    366     scoped_ptr<base::Value>* value) {
    367   session->SwitchToParentFrame();
    368   return Status(kOk);
    369 }
    370 
    371 Status ExecuteGetTitle(
    372     Session* session,
    373     WebView* web_view,
    374     const base::DictionaryValue& params,
    375     scoped_ptr<base::Value>* value) {
    376   const char* kGetTitleScript =
    377       "function() {"
    378       "  if (document.title)"
    379       "    return document.title;"
    380       "  else"
    381       "    return document.URL;"
    382       "}";
    383   base::ListValue args;
    384   return web_view->CallFunction(std::string(), kGetTitleScript, args, value);
    385 }
    386 
    387 Status ExecuteGetPageSource(
    388     Session* session,
    389     WebView* web_view,
    390     const base::DictionaryValue& params,
    391     scoped_ptr<base::Value>* value) {
    392   const char* kGetPageSource =
    393       "function() {"
    394       "  return new XMLSerializer().serializeToString(document);"
    395       "}";
    396   base::ListValue args;
    397   return web_view->CallFunction(
    398       session->GetCurrentFrameId(), kGetPageSource, args, value);
    399 }
    400 
    401 Status ExecuteFindElement(
    402     int interval_ms,
    403     Session* session,
    404     WebView* web_view,
    405     const base::DictionaryValue& params,
    406     scoped_ptr<base::Value>* value) {
    407   return FindElement(interval_ms, true, NULL, session, web_view, params, value);
    408 }
    409 
    410 Status ExecuteFindElements(
    411     int interval_ms,
    412     Session* session,
    413     WebView* web_view,
    414     const base::DictionaryValue& params,
    415     scoped_ptr<base::Value>* value) {
    416   return FindElement(
    417       interval_ms, false, NULL, session, web_view, params, value);
    418 }
    419 
    420 Status ExecuteGetCurrentUrl(
    421     Session* session,
    422     WebView* web_view,
    423     const base::DictionaryValue& params,
    424     scoped_ptr<base::Value>* value) {
    425   std::string url;
    426   Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
    427   if (status.IsError())
    428     return status;
    429   value->reset(new base::StringValue(url));
    430   return Status(kOk);
    431 }
    432 
    433 Status ExecuteGoBack(
    434     Session* session,
    435     WebView* web_view,
    436     const base::DictionaryValue& params,
    437     scoped_ptr<base::Value>* value) {
    438   return web_view->EvaluateScript(
    439       std::string(), "window.history.back();", value);
    440 }
    441 
    442 Status ExecuteGoForward(
    443     Session* session,
    444     WebView* web_view,
    445     const base::DictionaryValue& params,
    446     scoped_ptr<base::Value>* value) {
    447   return web_view->EvaluateScript(
    448       std::string(), "window.history.forward();", value);
    449 }
    450 
    451 Status ExecuteRefresh(
    452     Session* session,
    453     WebView* web_view,
    454     const base::DictionaryValue& params,
    455     scoped_ptr<base::Value>* value) {
    456   return web_view->Reload();
    457 }
    458 
    459 Status ExecuteMouseMoveTo(
    460     Session* session,
    461     WebView* web_view,
    462     const base::DictionaryValue& params,
    463     scoped_ptr<base::Value>* value) {
    464   std::string element_id;
    465   bool has_element = params.GetString("element", &element_id);
    466   int x_offset = 0;
    467   int y_offset = 0;
    468   bool has_offset = params.GetInteger("xoffset", &x_offset) &&
    469       params.GetInteger("yoffset", &y_offset);
    470   if (!has_element && !has_offset)
    471     return Status(kUnknownError, "at least an element or offset should be set");
    472 
    473   WebPoint location;
    474   if (has_element) {
    475     Status status = ScrollElementIntoView(
    476         session, web_view, element_id, &location);
    477     if (status.IsError())
    478       return status;
    479   } else {
    480     location = session->mouse_position;
    481   }
    482 
    483   if (has_offset) {
    484     location.Offset(x_offset, y_offset);
    485   } else {
    486     WebSize size;
    487     Status status = GetElementSize(session, web_view, element_id, &size);
    488     if (status.IsError())
    489       return status;
    490     location.Offset(size.width / 2, size.height / 2);
    491   }
    492 
    493   std::list<MouseEvent> events;
    494   events.push_back(
    495       MouseEvent(kMovedMouseEventType, kNoneMouseButton,
    496                  location.x, location.y, session->sticky_modifiers, 0));
    497   Status status =
    498       web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    499   if (status.IsOk())
    500     session->mouse_position = location;
    501   return status;
    502 }
    503 
    504 Status ExecuteMouseClick(
    505     Session* session,
    506     WebView* web_view,
    507     const base::DictionaryValue& params,
    508     scoped_ptr<base::Value>* value) {
    509   MouseButton button;
    510   Status status = GetMouseButton(params, &button);
    511   if (status.IsError())
    512     return status;
    513   std::list<MouseEvent> events;
    514   events.push_back(
    515       MouseEvent(kPressedMouseEventType, button,
    516                  session->mouse_position.x, session->mouse_position.y,
    517                  session->sticky_modifiers, 1));
    518   events.push_back(
    519       MouseEvent(kReleasedMouseEventType, button,
    520                  session->mouse_position.x, session->mouse_position.y,
    521                  session->sticky_modifiers, 1));
    522   return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    523 }
    524 
    525 Status ExecuteMouseButtonDown(
    526     Session* session,
    527     WebView* web_view,
    528     const base::DictionaryValue& params,
    529     scoped_ptr<base::Value>* value) {
    530   MouseButton button;
    531   Status status = GetMouseButton(params, &button);
    532   if (status.IsError())
    533     return status;
    534   std::list<MouseEvent> events;
    535   events.push_back(
    536       MouseEvent(kPressedMouseEventType, button,
    537                  session->mouse_position.x, session->mouse_position.y,
    538                  session->sticky_modifiers, 1));
    539   return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    540 }
    541 
    542 Status ExecuteMouseButtonUp(
    543     Session* session,
    544     WebView* web_view,
    545     const base::DictionaryValue& params,
    546     scoped_ptr<base::Value>* value) {
    547   MouseButton button;
    548   Status status = GetMouseButton(params, &button);
    549   if (status.IsError())
    550     return status;
    551   std::list<MouseEvent> events;
    552   events.push_back(
    553       MouseEvent(kReleasedMouseEventType, button,
    554                  session->mouse_position.x, session->mouse_position.y,
    555                  session->sticky_modifiers, 1));
    556   return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    557 }
    558 
    559 Status ExecuteMouseDoubleClick(
    560     Session* session,
    561     WebView* web_view,
    562     const base::DictionaryValue& params,
    563     scoped_ptr<base::Value>* value) {
    564   MouseButton button;
    565   Status status = GetMouseButton(params, &button);
    566   if (status.IsError())
    567     return status;
    568   std::list<MouseEvent> events;
    569   events.push_back(
    570       MouseEvent(kPressedMouseEventType, button,
    571                  session->mouse_position.x, session->mouse_position.y,
    572                  session->sticky_modifiers, 2));
    573   events.push_back(
    574       MouseEvent(kReleasedMouseEventType, button,
    575                  session->mouse_position.x, session->mouse_position.y,
    576                  session->sticky_modifiers, 2));
    577   return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    578 }
    579 
    580 Status ExecuteTouchDown(
    581     Session* session,
    582     WebView* web_view,
    583     const base::DictionaryValue& params,
    584     scoped_ptr<base::Value>* value) {
    585   return ExecuteTouchEvent(session, web_view, kTouchStart, params);
    586 }
    587 
    588 Status ExecuteTouchUp(
    589     Session* session,
    590     WebView* web_view,
    591     const base::DictionaryValue& params,
    592     scoped_ptr<base::Value>* value) {
    593   return ExecuteTouchEvent(session, web_view, kTouchEnd, params);
    594 }
    595 
    596 Status ExecuteTouchMove(
    597     Session* session,
    598     WebView* web_view,
    599     const base::DictionaryValue& params,
    600     scoped_ptr<base::Value>* value) {
    601   return ExecuteTouchEvent(session, web_view, kTouchMove, params);
    602 }
    603 
    604 Status ExecuteGetActiveElement(
    605     Session* session,
    606     WebView* web_view,
    607     const base::DictionaryValue& params,
    608     scoped_ptr<base::Value>* value) {
    609   return GetActiveElement(session, web_view, value);
    610 }
    611 
    612 Status ExecuteSendKeysToActiveElement(
    613     Session* session,
    614     WebView* web_view,
    615     const base::DictionaryValue& params,
    616     scoped_ptr<base::Value>* value) {
    617   const base::ListValue* key_list;
    618   if (!params.GetList("value", &key_list))
    619     return Status(kUnknownError, "'value' must be a list");
    620   return SendKeysOnWindow(
    621       web_view, key_list, false, &session->sticky_modifiers);
    622 }
    623 
    624 Status ExecuteGetAppCacheStatus(
    625     Session* session,
    626     WebView* web_view,
    627     const base::DictionaryValue& params,
    628     scoped_ptr<base::Value>* value) {
    629   return web_view->EvaluateScript(
    630       session->GetCurrentFrameId(),
    631       "applicationCache.status",
    632       value);
    633 }
    634 
    635 Status ExecuteIsBrowserOnline(
    636     Session* session,
    637     WebView* web_view,
    638     const base::DictionaryValue& params,
    639     scoped_ptr<base::Value>* value) {
    640   return web_view->EvaluateScript(
    641       session->GetCurrentFrameId(),
    642       "navigator.onLine",
    643       value);
    644 }
    645 
    646 Status ExecuteGetStorageItem(
    647     const char* storage,
    648     Session* session,
    649     WebView* web_view,
    650     const base::DictionaryValue& params,
    651     scoped_ptr<base::Value>* value) {
    652   std::string key;
    653   if (!params.GetString("key", &key))
    654     return Status(kUnknownError, "'key' must be a string");
    655   base::ListValue args;
    656   args.Append(new base::StringValue(key));
    657   return web_view->CallFunction(
    658       session->GetCurrentFrameId(),
    659       base::StringPrintf("function(key) { return %s[key]; }", storage),
    660       args,
    661       value);
    662 }
    663 
    664 Status ExecuteGetStorageKeys(
    665     const char* storage,
    666     Session* session,
    667     WebView* web_view,
    668     const base::DictionaryValue& params,
    669     scoped_ptr<base::Value>* value) {
    670   const char script[] =
    671       "var keys = [];"
    672       "for (var key in %s) {"
    673       "  keys.push(key);"
    674       "}"
    675       "keys";
    676   return web_view->EvaluateScript(
    677       session->GetCurrentFrameId(),
    678       base::StringPrintf(script, storage),
    679       value);
    680 }
    681 
    682 Status ExecuteSetStorageItem(
    683     const char* storage,
    684     Session* session,
    685     WebView* web_view,
    686     const base::DictionaryValue& params,
    687     scoped_ptr<base::Value>* value) {
    688   std::string key;
    689   if (!params.GetString("key", &key))
    690     return Status(kUnknownError, "'key' must be a string");
    691   std::string storage_value;
    692   if (!params.GetString("value", &storage_value))
    693     return Status(kUnknownError, "'value' must be a string");
    694   base::ListValue args;
    695   args.Append(new base::StringValue(key));
    696   args.Append(new base::StringValue(storage_value));
    697   return web_view->CallFunction(
    698       session->GetCurrentFrameId(),
    699       base::StringPrintf("function(key, value) { %s[key] = value; }", storage),
    700       args,
    701       value);
    702 }
    703 
    704 Status ExecuteRemoveStorageItem(
    705     const char* storage,
    706     Session* session,
    707     WebView* web_view,
    708     const base::DictionaryValue& params,
    709     scoped_ptr<base::Value>* value) {
    710   std::string key;
    711   if (!params.GetString("key", &key))
    712     return Status(kUnknownError, "'key' must be a string");
    713   base::ListValue args;
    714   args.Append(new base::StringValue(key));
    715   return web_view->CallFunction(
    716       session->GetCurrentFrameId(),
    717       base::StringPrintf("function(key) { %s.removeItem(key) }", storage),
    718       args,
    719       value);
    720 }
    721 
    722 Status ExecuteClearStorage(
    723     const char* storage,
    724     Session* session,
    725     WebView* web_view,
    726     const base::DictionaryValue& params,
    727     scoped_ptr<base::Value>* value) {
    728   return web_view->EvaluateScript(
    729       session->GetCurrentFrameId(),
    730       base::StringPrintf("%s.clear()", storage),
    731       value);
    732 }
    733 
    734 Status ExecuteGetStorageSize(
    735     const char* storage,
    736     Session* session,
    737     WebView* web_view,
    738     const base::DictionaryValue& params,
    739     scoped_ptr<base::Value>* value) {
    740   return web_view->EvaluateScript(
    741       session->GetCurrentFrameId(),
    742       base::StringPrintf("%s.length", storage),
    743       value);
    744 }
    745 
    746 Status ExecuteScreenshot(
    747     Session* session,
    748     WebView* web_view,
    749     const base::DictionaryValue& params,
    750     scoped_ptr<base::Value>* value) {
    751   Status status = session->chrome->ActivateWebView(web_view->GetId());
    752   if (status.IsError())
    753     return status;
    754 
    755   std::string screenshot;
    756   if (session->chrome->GetAsDesktop() && !session->force_devtools_screenshot) {
    757     AutomationExtension* extension = NULL;
    758     status =
    759         session->chrome->GetAsDesktop()->GetAutomationExtension(&extension);
    760     if (status.IsError())
    761       return status;
    762     status = extension->CaptureScreenshot(&screenshot);
    763     // If the screenshot was forbidden, fallback to DevTools.
    764     if (status.code() == kForbidden)
    765       status = web_view->CaptureScreenshot(&screenshot);
    766   } else {
    767     status = web_view->CaptureScreenshot(&screenshot);
    768   }
    769   if (status.IsError())
    770     return status;
    771 
    772   value->reset(new base::StringValue(screenshot));
    773   return Status(kOk);
    774 }
    775 
    776 Status ExecuteGetCookies(
    777     Session* session,
    778     WebView* web_view,
    779     const base::DictionaryValue& params,
    780     scoped_ptr<base::Value>* value) {
    781   std::list<Cookie> cookies;
    782   Status status = GetVisibleCookies(web_view, &cookies);
    783   if (status.IsError())
    784     return status;
    785   scoped_ptr<base::ListValue> cookie_list(new base::ListValue());
    786   for (std::list<Cookie>::const_iterator it = cookies.begin();
    787        it != cookies.end(); ++it) {
    788     cookie_list->Append(CreateDictionaryFrom(*it));
    789   }
    790   value->reset(cookie_list.release());
    791   return Status(kOk);
    792 }
    793 
    794 Status ExecuteAddCookie(
    795     Session* session,
    796     WebView* web_view,
    797     const base::DictionaryValue& params,
    798     scoped_ptr<base::Value>* value) {
    799   const base::DictionaryValue* cookie;
    800   if (!params.GetDictionary("cookie", &cookie))
    801     return Status(kUnknownError, "missing 'cookie'");
    802   base::ListValue args;
    803   args.Append(cookie->DeepCopy());
    804   scoped_ptr<base::Value> result;
    805   return web_view->CallFunction(
    806       session->GetCurrentFrameId(), kAddCookieScript, args, &result);
    807 }
    808 
    809 Status ExecuteDeleteCookie(
    810     Session* session,
    811     WebView* web_view,
    812     const base::DictionaryValue& params,
    813     scoped_ptr<base::Value>* value) {
    814   std::string name;
    815   if (!params.GetString("name", &name))
    816     return Status(kUnknownError, "missing 'name'");
    817   base::DictionaryValue params_url;
    818   scoped_ptr<base::Value> value_url;
    819   std::string url;
    820   Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
    821   if (status.IsError())
    822     return status;
    823   return web_view->DeleteCookie(name, url);
    824 }
    825 
    826 Status ExecuteDeleteAllCookies(
    827     Session* session,
    828     WebView* web_view,
    829     const base::DictionaryValue& params,
    830     scoped_ptr<base::Value>* value) {
    831   std::list<Cookie> cookies;
    832   Status status = GetVisibleCookies(web_view, &cookies);
    833   if (status.IsError())
    834     return status;
    835 
    836   if (!cookies.empty()) {
    837     base::DictionaryValue params_url;
    838     scoped_ptr<base::Value> value_url;
    839     std::string url;
    840     status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
    841     if (status.IsError())
    842       return status;
    843     for (std::list<Cookie>::const_iterator it = cookies.begin();
    844          it != cookies.end(); ++it) {
    845       status = web_view->DeleteCookie(it->name, url);
    846       if (status.IsError())
    847         return status;
    848     }
    849   }
    850 
    851   return Status(kOk);
    852 }
    853 
    854 Status ExecuteSetLocation(
    855     Session* session,
    856     WebView* web_view,
    857     const base::DictionaryValue& params,
    858     scoped_ptr<base::Value>* value) {
    859   const base::DictionaryValue* location = NULL;
    860   Geoposition geoposition;
    861   if (!params.GetDictionary("location", &location) ||
    862       !location->GetDouble("latitude", &geoposition.latitude) ||
    863       !location->GetDouble("longitude", &geoposition.longitude))
    864     return Status(kUnknownError, "missing or invalid 'location'");
    865   if (location->HasKey("accuracy") &&
    866       !location->GetDouble("accuracy", &geoposition.accuracy)) {
    867     return Status(kUnknownError, "invalid 'accuracy'");
    868   } else {
    869     // |accuracy| is not part of the WebDriver spec yet, so if it is not given
    870     // default to 100 meters accuracy.
    871     geoposition.accuracy = 100;
    872   }
    873 
    874   Status status = web_view->OverrideGeolocation(geoposition);
    875   if (status.IsOk())
    876     session->overridden_geoposition.reset(new Geoposition(geoposition));
    877   return status;
    878 }
    879 
    880 Status ExecuteTakeHeapSnapshot(
    881     Session* session,
    882     WebView* web_view,
    883     const base::DictionaryValue& params,
    884     scoped_ptr<base::Value>* value) {
    885   return web_view->TakeHeapSnapshot(value);
    886 }
    887