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 {
    261     const base::ListValue* args;
    262     if (!params.GetList("args", &args))
    263       return Status(kUnknownError, "'args' must be a list");
    264 
    265     return web_view->CallFunction(session->GetCurrentFrameId(),
    266                                   "function(){" + script + "}", *args, value);
    267   }
    268 }
    269 
    270 Status ExecuteExecuteAsyncScript(
    271     Session* session,
    272     WebView* web_view,
    273     const base::DictionaryValue& params,
    274     scoped_ptr<base::Value>* value) {
    275   std::string script;
    276   if (!params.GetString("script", &script))
    277     return Status(kUnknownError, "'script' must be a string");
    278   const base::ListValue* args;
    279   if (!params.GetList("args", &args))
    280     return Status(kUnknownError, "'args' must be a list");
    281 
    282   return web_view->CallUserAsyncFunction(
    283       session->GetCurrentFrameId(), "function(){" + script + "}", *args,
    284       session->script_timeout, value);
    285 }
    286 
    287 Status ExecuteSwitchToFrame(
    288     Session* session,
    289     WebView* web_view,
    290     const base::DictionaryValue& params,
    291     scoped_ptr<base::Value>* value) {
    292   const base::Value* id;
    293   if (!params.Get("id", &id))
    294     return Status(kUnknownError, "missing 'id'");
    295 
    296   if (id->IsType(base::Value::TYPE_NULL)) {
    297     session->SwitchToTopFrame();
    298     return Status(kOk);
    299   }
    300 
    301   std::string script;
    302   base::ListValue args;
    303   const base::DictionaryValue* id_dict;
    304   if (id->GetAsDictionary(&id_dict)) {
    305     script = "function(elem) { return elem; }";
    306     args.Append(id_dict->DeepCopy());
    307   } else {
    308     script =
    309         "function(xpath) {"
    310         "  return document.evaluate(xpath, document, null, "
    311         "      XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
    312         "}";
    313     std::string xpath = "(/html/body//iframe|/html/frameset/frame)";
    314     std::string id_string;
    315     int id_int;
    316     if (id->GetAsString(&id_string)) {
    317       xpath += base::StringPrintf(
    318           "[@name=\"%s\" or @id=\"%s\"]", id_string.c_str(), id_string.c_str());
    319     } else if (id->GetAsInteger(&id_int)) {
    320       xpath += base::StringPrintf("[%d]", id_int + 1);
    321     } else {
    322       return Status(kUnknownError, "invalid 'id'");
    323     }
    324     args.Append(new base::StringValue(xpath));
    325   }
    326   std::string frame;
    327   Status status = web_view->GetFrameByFunction(
    328       session->GetCurrentFrameId(), script, args, &frame);
    329   if (status.IsError())
    330     return status;
    331 
    332   scoped_ptr<base::Value> result;
    333   status = web_view->CallFunction(
    334       session->GetCurrentFrameId(), script, args, &result);
    335   if (status.IsError())
    336     return status;
    337   const base::DictionaryValue* element;
    338   if (!result->GetAsDictionary(&element))
    339     return Status(kUnknownError, "fail to locate the sub frame element");
    340 
    341   std::string chrome_driver_id = GenerateId();
    342   const char* kSetFrameIdentifier =
    343       "function(frame, id) {"
    344       "  frame.setAttribute('cd_frame_id_', id);"
    345       "}";
    346   base::ListValue new_args;
    347   new_args.Append(element->DeepCopy());
    348   new_args.AppendString(chrome_driver_id);
    349   result.reset(NULL);
    350   status = web_view->CallFunction(
    351       session->GetCurrentFrameId(), kSetFrameIdentifier, new_args, &result);
    352   if (status.IsError())
    353     return status;
    354   session->SwitchToSubFrame(frame, chrome_driver_id);
    355   return Status(kOk);
    356 }
    357 
    358 Status ExecuteGetTitle(
    359     Session* session,
    360     WebView* web_view,
    361     const base::DictionaryValue& params,
    362     scoped_ptr<base::Value>* value) {
    363   const char* kGetTitleScript =
    364       "function() {"
    365       "  if (document.title)"
    366       "    return document.title;"
    367       "  else"
    368       "    return document.URL;"
    369       "}";
    370   base::ListValue args;
    371   return web_view->CallFunction(std::string(), kGetTitleScript, args, value);
    372 }
    373 
    374 Status ExecuteGetPageSource(
    375     Session* session,
    376     WebView* web_view,
    377     const base::DictionaryValue& params,
    378     scoped_ptr<base::Value>* value) {
    379   const char* kGetPageSource =
    380       "function() {"
    381       "  return new XMLSerializer().serializeToString(document);"
    382       "}";
    383   base::ListValue args;
    384   return web_view->CallFunction(
    385       session->GetCurrentFrameId(), kGetPageSource, args, value);
    386 }
    387 
    388 Status ExecuteFindElement(
    389     int interval_ms,
    390     Session* session,
    391     WebView* web_view,
    392     const base::DictionaryValue& params,
    393     scoped_ptr<base::Value>* value) {
    394   return FindElement(interval_ms, true, NULL, session, web_view, params, value);
    395 }
    396 
    397 Status ExecuteFindElements(
    398     int interval_ms,
    399     Session* session,
    400     WebView* web_view,
    401     const base::DictionaryValue& params,
    402     scoped_ptr<base::Value>* value) {
    403   return FindElement(
    404       interval_ms, false, NULL, session, web_view, params, value);
    405 }
    406 
    407 Status ExecuteGetCurrentUrl(
    408     Session* session,
    409     WebView* web_view,
    410     const base::DictionaryValue& params,
    411     scoped_ptr<base::Value>* value) {
    412   std::string url;
    413   Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
    414   if (status.IsError())
    415     return status;
    416   value->reset(new base::StringValue(url));
    417   return Status(kOk);
    418 }
    419 
    420 Status ExecuteGoBack(
    421     Session* session,
    422     WebView* web_view,
    423     const base::DictionaryValue& params,
    424     scoped_ptr<base::Value>* value) {
    425   return web_view->EvaluateScript(
    426       std::string(), "window.history.back();", value);
    427 }
    428 
    429 Status ExecuteGoForward(
    430     Session* session,
    431     WebView* web_view,
    432     const base::DictionaryValue& params,
    433     scoped_ptr<base::Value>* value) {
    434   return web_view->EvaluateScript(
    435       std::string(), "window.history.forward();", value);
    436 }
    437 
    438 Status ExecuteRefresh(
    439     Session* session,
    440     WebView* web_view,
    441     const base::DictionaryValue& params,
    442     scoped_ptr<base::Value>* value) {
    443   return web_view->Reload();
    444 }
    445 
    446 Status ExecuteMouseMoveTo(
    447     Session* session,
    448     WebView* web_view,
    449     const base::DictionaryValue& params,
    450     scoped_ptr<base::Value>* value) {
    451   std::string element_id;
    452   bool has_element = params.GetString("element", &element_id);
    453   int x_offset = 0;
    454   int y_offset = 0;
    455   bool has_offset = params.GetInteger("xoffset", &x_offset) &&
    456       params.GetInteger("yoffset", &y_offset);
    457   if (!has_element && !has_offset)
    458     return Status(kUnknownError, "at least an element or offset should be set");
    459 
    460   WebPoint location;
    461   if (has_element) {
    462     Status status = ScrollElementIntoView(
    463         session, web_view, element_id, &location);
    464     if (status.IsError())
    465       return status;
    466   } else {
    467     location = session->mouse_position;
    468   }
    469 
    470   if (has_offset) {
    471     location.Offset(x_offset, y_offset);
    472   } else {
    473     WebSize size;
    474     Status status = GetElementSize(session, web_view, element_id, &size);
    475     if (status.IsError())
    476       return status;
    477     location.Offset(size.width / 2, size.height / 2);
    478   }
    479 
    480   std::list<MouseEvent> events;
    481   events.push_back(
    482       MouseEvent(kMovedMouseEventType, kNoneMouseButton,
    483                  location.x, location.y, session->sticky_modifiers, 0));
    484   Status status =
    485       web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    486   if (status.IsOk())
    487     session->mouse_position = location;
    488   return status;
    489 }
    490 
    491 Status ExecuteMouseClick(
    492     Session* session,
    493     WebView* web_view,
    494     const base::DictionaryValue& params,
    495     scoped_ptr<base::Value>* value) {
    496   MouseButton button;
    497   Status status = GetMouseButton(params, &button);
    498   if (status.IsError())
    499     return status;
    500   std::list<MouseEvent> events;
    501   events.push_back(
    502       MouseEvent(kPressedMouseEventType, button,
    503                  session->mouse_position.x, session->mouse_position.y,
    504                  session->sticky_modifiers, 1));
    505   events.push_back(
    506       MouseEvent(kReleasedMouseEventType, button,
    507                  session->mouse_position.x, session->mouse_position.y,
    508                  session->sticky_modifiers, 1));
    509   return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    510 }
    511 
    512 Status ExecuteMouseButtonDown(
    513     Session* session,
    514     WebView* web_view,
    515     const base::DictionaryValue& params,
    516     scoped_ptr<base::Value>* value) {
    517   MouseButton button;
    518   Status status = GetMouseButton(params, &button);
    519   if (status.IsError())
    520     return status;
    521   std::list<MouseEvent> events;
    522   events.push_back(
    523       MouseEvent(kPressedMouseEventType, button,
    524                  session->mouse_position.x, session->mouse_position.y,
    525                  session->sticky_modifiers, 1));
    526   return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    527 }
    528 
    529 Status ExecuteMouseButtonUp(
    530     Session* session,
    531     WebView* web_view,
    532     const base::DictionaryValue& params,
    533     scoped_ptr<base::Value>* value) {
    534   MouseButton button;
    535   Status status = GetMouseButton(params, &button);
    536   if (status.IsError())
    537     return status;
    538   std::list<MouseEvent> events;
    539   events.push_back(
    540       MouseEvent(kReleasedMouseEventType, button,
    541                  session->mouse_position.x, session->mouse_position.y,
    542                  session->sticky_modifiers, 1));
    543   return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    544 }
    545 
    546 Status ExecuteMouseDoubleClick(
    547     Session* session,
    548     WebView* web_view,
    549     const base::DictionaryValue& params,
    550     scoped_ptr<base::Value>* value) {
    551   MouseButton button;
    552   Status status = GetMouseButton(params, &button);
    553   if (status.IsError())
    554     return status;
    555   std::list<MouseEvent> events;
    556   events.push_back(
    557       MouseEvent(kPressedMouseEventType, button,
    558                  session->mouse_position.x, session->mouse_position.y,
    559                  session->sticky_modifiers, 2));
    560   events.push_back(
    561       MouseEvent(kReleasedMouseEventType, button,
    562                  session->mouse_position.x, session->mouse_position.y,
    563                  session->sticky_modifiers, 2));
    564   return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    565 }
    566 
    567 Status ExecuteTouchDown(
    568     Session* session,
    569     WebView* web_view,
    570     const base::DictionaryValue& params,
    571     scoped_ptr<base::Value>* value) {
    572   return ExecuteTouchEvent(session, web_view, kTouchStart, params);
    573 }
    574 
    575 Status ExecuteTouchUp(
    576     Session* session,
    577     WebView* web_view,
    578     const base::DictionaryValue& params,
    579     scoped_ptr<base::Value>* value) {
    580   return ExecuteTouchEvent(session, web_view, kTouchEnd, params);
    581 }
    582 
    583 Status ExecuteTouchMove(
    584     Session* session,
    585     WebView* web_view,
    586     const base::DictionaryValue& params,
    587     scoped_ptr<base::Value>* value) {
    588   return ExecuteTouchEvent(session, web_view, kTouchMove, params);
    589 }
    590 
    591 Status ExecuteGetActiveElement(
    592     Session* session,
    593     WebView* web_view,
    594     const base::DictionaryValue& params,
    595     scoped_ptr<base::Value>* value) {
    596   return GetActiveElement(session, web_view, value);
    597 }
    598 
    599 Status ExecuteSendKeysToActiveElement(
    600     Session* session,
    601     WebView* web_view,
    602     const base::DictionaryValue& params,
    603     scoped_ptr<base::Value>* value) {
    604   const base::ListValue* key_list;
    605   if (!params.GetList("value", &key_list))
    606     return Status(kUnknownError, "'value' must be a list");
    607   return SendKeysOnWindow(
    608       web_view, key_list, false, &session->sticky_modifiers);
    609 }
    610 
    611 Status ExecuteGetAppCacheStatus(
    612     Session* session,
    613     WebView* web_view,
    614     const base::DictionaryValue& params,
    615     scoped_ptr<base::Value>* value) {
    616   return web_view->EvaluateScript(
    617       session->GetCurrentFrameId(),
    618       "applicationCache.status",
    619       value);
    620 }
    621 
    622 Status ExecuteIsBrowserOnline(
    623     Session* session,
    624     WebView* web_view,
    625     const base::DictionaryValue& params,
    626     scoped_ptr<base::Value>* value) {
    627   return web_view->EvaluateScript(
    628       session->GetCurrentFrameId(),
    629       "navigator.onLine",
    630       value);
    631 }
    632 
    633 Status ExecuteGetStorageItem(
    634     const char* storage,
    635     Session* session,
    636     WebView* web_view,
    637     const base::DictionaryValue& params,
    638     scoped_ptr<base::Value>* value) {
    639   std::string key;
    640   if (!params.GetString("key", &key))
    641     return Status(kUnknownError, "'key' must be a string");
    642   base::ListValue args;
    643   args.Append(new base::StringValue(key));
    644   return web_view->CallFunction(
    645       session->GetCurrentFrameId(),
    646       base::StringPrintf("function(key) { return %s[key]; }", storage),
    647       args,
    648       value);
    649 }
    650 
    651 Status ExecuteGetStorageKeys(
    652     const char* storage,
    653     Session* session,
    654     WebView* web_view,
    655     const base::DictionaryValue& params,
    656     scoped_ptr<base::Value>* value) {
    657   const char script[] =
    658       "var keys = [];"
    659       "for (var key in %s) {"
    660       "  keys.push(key);"
    661       "}"
    662       "keys";
    663   return web_view->EvaluateScript(
    664       session->GetCurrentFrameId(),
    665       base::StringPrintf(script, storage),
    666       value);
    667 }
    668 
    669 Status ExecuteSetStorageItem(
    670     const char* storage,
    671     Session* session,
    672     WebView* web_view,
    673     const base::DictionaryValue& params,
    674     scoped_ptr<base::Value>* value) {
    675   std::string key;
    676   if (!params.GetString("key", &key))
    677     return Status(kUnknownError, "'key' must be a string");
    678   std::string storage_value;
    679   if (!params.GetString("value", &storage_value))
    680     return Status(kUnknownError, "'value' must be a string");
    681   base::ListValue args;
    682   args.Append(new base::StringValue(key));
    683   args.Append(new base::StringValue(storage_value));
    684   return web_view->CallFunction(
    685       session->GetCurrentFrameId(),
    686       base::StringPrintf("function(key, value) { %s[key] = value; }", storage),
    687       args,
    688       value);
    689 }
    690 
    691 Status ExecuteRemoveStorageItem(
    692     const char* storage,
    693     Session* session,
    694     WebView* web_view,
    695     const base::DictionaryValue& params,
    696     scoped_ptr<base::Value>* value) {
    697   std::string key;
    698   if (!params.GetString("key", &key))
    699     return Status(kUnknownError, "'key' must be a string");
    700   base::ListValue args;
    701   args.Append(new base::StringValue(key));
    702   return web_view->CallFunction(
    703       session->GetCurrentFrameId(),
    704       base::StringPrintf("function(key) { %s.removeItem(key) }", storage),
    705       args,
    706       value);
    707 }
    708 
    709 Status ExecuteClearStorage(
    710     const char* storage,
    711     Session* session,
    712     WebView* web_view,
    713     const base::DictionaryValue& params,
    714     scoped_ptr<base::Value>* value) {
    715   return web_view->EvaluateScript(
    716       session->GetCurrentFrameId(),
    717       base::StringPrintf("%s.clear()", storage),
    718       value);
    719 }
    720 
    721 Status ExecuteGetStorageSize(
    722     const char* storage,
    723     Session* session,
    724     WebView* web_view,
    725     const base::DictionaryValue& params,
    726     scoped_ptr<base::Value>* value) {
    727   return web_view->EvaluateScript(
    728       session->GetCurrentFrameId(),
    729       base::StringPrintf("%s.length", storage),
    730       value);
    731 }
    732 
    733 Status ExecuteScreenshot(
    734     Session* session,
    735     WebView* web_view,
    736     const base::DictionaryValue& params,
    737     scoped_ptr<base::Value>* value) {
    738   Status status = session->chrome->ActivateWebView(web_view->GetId());
    739   if (status.IsError())
    740     return status;
    741 
    742   std::string screenshot;
    743   if (session->chrome->GetAsDesktop() && !session->force_devtools_screenshot) {
    744     AutomationExtension* extension = NULL;
    745     status =
    746         session->chrome->GetAsDesktop()->GetAutomationExtension(&extension);
    747     if (status.IsError())
    748       return status;
    749     status = extension->CaptureScreenshot(&screenshot);
    750     // If the screenshot was forbidden, fallback to DevTools.
    751     if (status.code() == kForbidden)
    752       status = web_view->CaptureScreenshot(&screenshot);
    753   } else {
    754     status = web_view->CaptureScreenshot(&screenshot);
    755   }
    756   if (status.IsError())
    757     return status;
    758 
    759   value->reset(new base::StringValue(screenshot));
    760   return Status(kOk);
    761 }
    762 
    763 Status ExecuteGetCookies(
    764     Session* session,
    765     WebView* web_view,
    766     const base::DictionaryValue& params,
    767     scoped_ptr<base::Value>* value) {
    768   std::list<Cookie> cookies;
    769   Status status = GetVisibleCookies(web_view, &cookies);
    770   if (status.IsError())
    771     return status;
    772   scoped_ptr<base::ListValue> cookie_list(new base::ListValue());
    773   for (std::list<Cookie>::const_iterator it = cookies.begin();
    774        it != cookies.end(); ++it) {
    775     cookie_list->Append(CreateDictionaryFrom(*it));
    776   }
    777   value->reset(cookie_list.release());
    778   return Status(kOk);
    779 }
    780 
    781 Status ExecuteAddCookie(
    782     Session* session,
    783     WebView* web_view,
    784     const base::DictionaryValue& params,
    785     scoped_ptr<base::Value>* value) {
    786   const base::DictionaryValue* cookie;
    787   if (!params.GetDictionary("cookie", &cookie))
    788     return Status(kUnknownError, "missing 'cookie'");
    789   base::ListValue args;
    790   args.Append(cookie->DeepCopy());
    791   scoped_ptr<base::Value> result;
    792   return web_view->CallFunction(
    793       session->GetCurrentFrameId(), kAddCookieScript, args, &result);
    794 }
    795 
    796 Status ExecuteDeleteCookie(
    797     Session* session,
    798     WebView* web_view,
    799     const base::DictionaryValue& params,
    800     scoped_ptr<base::Value>* value) {
    801   std::string name;
    802   if (!params.GetString("name", &name))
    803     return Status(kUnknownError, "missing 'name'");
    804   base::DictionaryValue params_url;
    805   scoped_ptr<base::Value> value_url;
    806   std::string url;
    807   Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
    808   if (status.IsError())
    809     return status;
    810   return web_view->DeleteCookie(name, url);
    811 }
    812 
    813 Status ExecuteDeleteAllCookies(
    814     Session* session,
    815     WebView* web_view,
    816     const base::DictionaryValue& params,
    817     scoped_ptr<base::Value>* value) {
    818   std::list<Cookie> cookies;
    819   Status status = GetVisibleCookies(web_view, &cookies);
    820   if (status.IsError())
    821     return status;
    822 
    823   if (!cookies.empty()) {
    824     base::DictionaryValue params_url;
    825     scoped_ptr<base::Value> value_url;
    826     std::string url;
    827     status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
    828     if (status.IsError())
    829       return status;
    830     for (std::list<Cookie>::const_iterator it = cookies.begin();
    831          it != cookies.end(); ++it) {
    832       status = web_view->DeleteCookie(it->name, url);
    833       if (status.IsError())
    834         return status;
    835     }
    836   }
    837 
    838   return Status(kOk);
    839 }
    840 
    841 Status ExecuteSetLocation(
    842     Session* session,
    843     WebView* web_view,
    844     const base::DictionaryValue& params,
    845     scoped_ptr<base::Value>* value) {
    846   const base::DictionaryValue* location = NULL;
    847   Geoposition geoposition;
    848   if (!params.GetDictionary("location", &location) ||
    849       !location->GetDouble("latitude", &geoposition.latitude) ||
    850       !location->GetDouble("longitude", &geoposition.longitude))
    851     return Status(kUnknownError, "missing or invalid 'location'");
    852   if (location->HasKey("accuracy") &&
    853       !location->GetDouble("accuracy", &geoposition.accuracy)) {
    854     return Status(kUnknownError, "invalid 'accuracy'");
    855   } else {
    856     // |accuracy| is not part of the WebDriver spec yet, so if it is not given
    857     // default to 100 meters accuracy.
    858     geoposition.accuracy = 100;
    859   }
    860 
    861   Status status = web_view->OverrideGeolocation(geoposition);
    862   if (status.IsOk())
    863     session->overridden_geoposition.reset(new Geoposition(geoposition));
    864   return status;
    865 }
    866 
    867 Status ExecuteTakeHeapSnapshot(
    868     Session* session,
    869     WebView* web_view,
    870     const base::DictionaryValue& params,
    871     scoped_ptr<base::Value>* value) {
    872   return web_view->TakeHeapSnapshot(value);
    873 }
    874