Home | History | Annotate | Download | only in webdriver
      1 // Copyright (c) 2012 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/webdriver/webdriver_session.h"
      6 
      7 #include <sstream>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/callback.h"
     12 #include "base/command_line.h"
     13 #include "base/file_util.h"
     14 #include "base/files/file_path.h"
     15 #include "base/json/json_reader.h"
     16 #include "base/json/json_writer.h"
     17 #include "base/memory/scoped_ptr.h"
     18 #include "base/message_loop/message_loop_proxy.h"
     19 #include "base/process/process.h"
     20 #include "base/strings/string_number_conversions.h"
     21 #include "base/strings/string_split.h"
     22 #include "base/strings/string_util.h"
     23 #include "base/strings/stringprintf.h"
     24 #include "base/strings/utf_string_conversions.h"
     25 #include "base/synchronization/waitable_event.h"
     26 #include "base/test/test_timeouts.h"
     27 #include "base/threading/platform_thread.h"
     28 #include "base/time/time.h"
     29 #include "base/values.h"
     30 #include "chrome/app/chrome_command_ids.h"
     31 #include "chrome/common/chrome_constants.h"
     32 #include "chrome/common/chrome_switches.h"
     33 #include "chrome/test/automation/automation_json_requests.h"
     34 #include "chrome/test/automation/value_conversion_util.h"
     35 #include "chrome/test/webdriver/webdriver_capabilities_parser.h"
     36 #include "chrome/test/webdriver/webdriver_error.h"
     37 #include "chrome/test/webdriver/webdriver_key_converter.h"
     38 #include "chrome/test/webdriver/webdriver_logging.h"
     39 #include "chrome/test/webdriver/webdriver_session_manager.h"
     40 #include "chrome/test/webdriver/webdriver_util.h"
     41 #include "third_party/webdriver/atoms.h"
     42 
     43 using automation::kLeftButton;
     44 using automation::kMouseDown;
     45 using automation::kMouseMove;
     46 using automation::kMouseUp;
     47 using automation::kNoButton;
     48 
     49 namespace webdriver {
     50 
     51 namespace {
     52 // This is the minimum version of chrome that supports the new mouse API.
     53 const int kNewMouseAPIMinVersion = 1002;
     54 }
     55 
     56 FrameId::FrameId() {}
     57 
     58 FrameId::FrameId(const WebViewId& view_id, const FramePath& frame_path)
     59     : view_id(view_id),
     60       frame_path(frame_path) {
     61 }
     62 
     63 Session::Session()
     64     : session_log_(new InMemoryLog()),
     65       logger_(kAllLogLevel),
     66       id_(GenerateRandomID()),
     67       current_target_(FrameId(WebViewId(), FramePath())),
     68       thread_(id_.c_str()),
     69       async_script_timeout_(0),
     70       implicit_wait_(0),
     71       has_alert_prompt_text_(false),
     72       sticky_modifiers_(0),
     73       build_no_(0) {
     74   SessionManager::GetInstance()->Add(this);
     75   logger_.AddHandler(session_log_.get());
     76   if (FileLog::Get())
     77     logger_.AddHandler(FileLog::Get());
     78 }
     79 
     80 Session::~Session() {
     81   SessionManager::GetInstance()->Remove(id_);
     82 }
     83 
     84 Error* Session::Init(const base::DictionaryValue* capabilities_dict) {
     85   if (!thread_.Start()) {
     86     delete this;
     87     return new Error(kUnknownError, "Cannot start session thread");
     88   }
     89   if (!temp_dir_.CreateUniqueTempDir()) {
     90     delete this;
     91     return new Error(
     92         kUnknownError, "Unable to create temp directory for unpacking");
     93   }
     94   logger_.Log(kFineLogLevel,
     95               "Initializing session with capabilities " +
     96                   JsonStringifyForDisplay(capabilities_dict));
     97   CapabilitiesParser parser(
     98       capabilities_dict, temp_dir_.path(), logger_, &capabilities_);
     99   Error* error = parser.Parse();
    100   if (error) {
    101     delete this;
    102     return error;
    103   }
    104   logger_.set_min_log_level(capabilities_.log_levels[LogType::kDriver]);
    105 
    106   Automation::BrowserOptions browser_options;
    107   browser_options.command = capabilities_.command;
    108   browser_options.channel_id = capabilities_.channel;
    109   browser_options.detach_process = capabilities_.detach;
    110   browser_options.user_data_dir = capabilities_.profile;
    111   browser_options.exclude_switches = capabilities_.exclude_switches;
    112   if (!capabilities_.no_website_testing_defaults) {
    113     browser_options.ignore_certificate_errors = true;
    114   }
    115   RunSessionTask(base::Bind(
    116       &Session::InitOnSessionThread,
    117       base::Unretained(this),
    118       browser_options,
    119       &build_no_,
    120       &error));
    121   if (!error)
    122     error = PostBrowserStartInit();
    123 
    124   if (error)
    125     Terminate();
    126   return error;
    127 }
    128 
    129 Error* Session::BeforeExecuteCommand() {
    130   Error* error = AfterExecuteCommand();
    131   if (!error) {
    132     scoped_ptr<Error> switch_error(SwitchToTopFrameIfCurrentFrameInvalid());
    133     if (switch_error.get()) {
    134       std::string text;
    135       scoped_ptr<Error> alert_error(GetAlertMessage(&text));
    136       if (alert_error.get()) {
    137         // Only return a frame checking error if a modal dialog is not present.
    138         // TODO(kkania): This is ugly. Fix.
    139         return switch_error.release();
    140       }
    141     }
    142   }
    143   return error;
    144 }
    145 
    146 Error* Session::AfterExecuteCommand() {
    147   Error* error = NULL;
    148   if (!capabilities_.load_async) {
    149     error = WaitForAllViewsToStopLoading();
    150   }
    151   return error;
    152 }
    153 
    154 void Session::Terminate() {
    155   RunSessionTask(base::Bind(
    156       &Session::TerminateOnSessionThread,
    157       base::Unretained(this)));
    158   delete this;
    159 }
    160 
    161 Error* Session::ExecuteScript(const FrameId& frame_id,
    162                               const std::string& script,
    163                               const base::ListValue* const args,
    164                               base::Value** value) {
    165   std::string args_as_json;
    166   base::JSONWriter::Write(static_cast<const base::Value* const>(args),
    167                           &args_as_json);
    168 
    169   // Every injected script is fed through the executeScript atom. This atom
    170   // will catch any errors that are thrown and convert them to the
    171   // appropriate JSON structure.
    172   std::string jscript = base::StringPrintf(
    173       "window.domAutomationController.send((%s).apply(null,"
    174       "[function(){%s\n},%s,true]));",
    175       atoms::asString(atoms::EXECUTE_SCRIPT).c_str(), script.c_str(),
    176       args_as_json.c_str());
    177 
    178   return ExecuteScriptAndParseValue(frame_id, jscript, value);
    179 }
    180 
    181 Error* Session::ExecuteScript(const std::string& script,
    182                               const base::ListValue* const args,
    183                               base::Value** value) {
    184   return ExecuteScript(current_target_, script, args, value);
    185 }
    186 
    187 Error* Session::ExecuteScriptAndParse(const FrameId& frame_id,
    188                                       const std::string& anonymous_func_script,
    189                                       const std::string& script_name,
    190                                       const base::ListValue* args,
    191                                       const ValueParser* parser) {
    192   scoped_ptr<const base::ListValue> scoped_args(args);
    193   scoped_ptr<const ValueParser> scoped_parser(parser);
    194   std::string called_script = base::StringPrintf(
    195       "return (%s).apply(null, arguments);", anonymous_func_script.c_str());
    196   base::Value* unscoped_value = NULL;
    197   Error* error = ExecuteScript(frame_id, called_script, args, &unscoped_value);
    198   if (error) {
    199     error->AddDetails(script_name + " execution failed");
    200     return error;
    201   }
    202 
    203   scoped_ptr<base::Value> value(unscoped_value);
    204   std::string error_msg;
    205   if (!parser->Parse(value.get())) {
    206     error_msg = base::StringPrintf("%s returned invalid value: %s",
    207         script_name.c_str(), JsonStringify(value.get()).c_str());
    208     return new Error(kUnknownError, error_msg);
    209   }
    210   return NULL;
    211 }
    212 
    213 Error* Session::ExecuteAsyncScript(const FrameId& frame_id,
    214                                    const std::string& script,
    215                                    const base::ListValue* const args,
    216                                    base::Value** value) {
    217   std::string args_as_json;
    218   base::JSONWriter::Write(static_cast<const base::Value* const>(args),
    219                           &args_as_json);
    220 
    221   int timeout_ms = async_script_timeout();
    222 
    223   // Every injected script is fed through the executeScript atom. This atom
    224   // will catch any errors that are thrown and convert them to the
    225   // appropriate JSON structure.
    226   std::string jscript = base::StringPrintf(
    227       "(%s).apply(null, [function(){%s},%s,%d,%s,true]);",
    228       atoms::asString(atoms::EXECUTE_ASYNC_SCRIPT).c_str(),
    229       script.c_str(),
    230       args_as_json.c_str(),
    231       timeout_ms,
    232       "function(result) {window.domAutomationController.send(result);}");
    233 
    234   return ExecuteScriptAndParseValue(frame_id, jscript, value);
    235 }
    236 
    237 Error* Session::SendKeys(const ElementId& element, const string16& keys) {
    238   bool is_displayed = false;
    239   Error* error = IsElementDisplayed(
    240       current_target_, element, true /* ignore_opacity */, &is_displayed);
    241   if (error)
    242     return error;
    243   if (!is_displayed)
    244     return new Error(kElementNotVisible);
    245 
    246   bool is_enabled = false;
    247   error = IsElementEnabled(current_target_, element, &is_enabled);
    248   if (error)
    249     return error;
    250   if (!is_enabled)
    251     return new Error(kInvalidElementState);
    252 
    253   // Focus the target element in order to send keys to it.
    254   // First, the currently active element is blurred, if it is different from
    255   // the target element. We do not want to blur an element unnecessarily,
    256   // because this may cause us to lose the current cursor position in the
    257   // element.
    258   // Secondly, we focus the target element.
    259   // Thirdly, if the target element is newly focused and is a text input, we
    260   // set the cursor position at the end.
    261   // Fourthly, we check if the new active element is the target element. If not,
    262   // we throw an error.
    263   // Additional notes:
    264   //   - |document.activeElement| is the currently focused element, or body if
    265   //     no element is focused
    266   //   - Even if |document.hasFocus()| returns true and the active element is
    267   //     the body, sometimes we still need to focus the body element for send
    268   //     keys to work. Not sure why
    269   //   - You cannot focus a descendant of a content editable node
    270   //   - V8 throws a TypeError when calling setSelectionRange for a non-text
    271   //     input, which still have setSelectionRange defined.
    272   // TODO(jleyba): Update this to use the correct atom.
    273   const char* kFocusScript =
    274       "function(elem) {"
    275       "  var doc = elem.ownerDocument || elem;"
    276       "  var prevActiveElem = doc.activeElement;"
    277       "  if (elem != prevActiveElem && prevActiveElem)"
    278       "    prevActiveElem.blur();"
    279       "  elem.focus();"
    280       "  if (elem != prevActiveElem && elem.value && elem.value.length &&"
    281       "      elem.setSelectionRange) {"
    282       "    try {"
    283       "      elem.setSelectionRange(elem.value.length, elem.value.length);"
    284       "    } catch (error) {"
    285       "      if (!(error instanceof TypeError)) {"
    286       "        throw error;"
    287       "      }"
    288       "    }"
    289       "  }"
    290       "  if (elem != doc.activeElement)"
    291       "    throw new Error('Failed to send keys because cannot focus element');"
    292       "}";
    293   error = ExecuteScriptAndParse(current_target_,
    294                                 kFocusScript,
    295                                 "focusElement",
    296                                 CreateListValueFrom(element),
    297                                 CreateDirectValueParser(kSkipParsing));
    298   if (error)
    299     return error;
    300 
    301   RunSessionTask(base::Bind(
    302       &Session::SendKeysOnSessionThread,
    303       base::Unretained(this),
    304       keys,
    305       true /* release_modifiers */,
    306       &error));
    307   return error;
    308 }
    309 
    310 Error* Session::SendKeys(const string16& keys) {
    311   Error* error = NULL;
    312   RunSessionTask(base::Bind(
    313       &Session::SendKeysOnSessionThread,
    314       base::Unretained(this),
    315       keys,
    316       false /* release_modifiers */,
    317       &error));
    318   return error;
    319 }
    320 
    321 Error* Session::DragAndDropFilePaths(
    322     const Point& location,
    323     const std::vector<base::FilePath::StringType>& paths) {
    324   Error* error = NULL;
    325   RunSessionTask(base::Bind(
    326       &Automation::DragAndDropFilePaths,
    327       base::Unretained(automation_.get()),
    328       current_target_.view_id,
    329       location,
    330       paths,
    331       &error));
    332   return error;
    333 }
    334 
    335 Error* Session::NavigateToURL(const std::string& url) {
    336   if (!current_target_.view_id.IsTab()) {
    337     return new Error(kUnknownError,
    338                      "The current target does not support navigation");
    339   }
    340   Error* error = NULL;
    341   if (capabilities_.load_async) {
    342     RunSessionTask(base::Bind(
    343         &Automation::NavigateToURLAsync,
    344         base::Unretained(automation_.get()),
    345         current_target_.view_id,
    346         url,
    347         &error));
    348   } else {
    349     RunSessionTask(base::Bind(
    350         &Automation::NavigateToURL,
    351         base::Unretained(automation_.get()),
    352         current_target_.view_id,
    353         url,
    354         &error));
    355   }
    356   return error;
    357 }
    358 
    359 Error* Session::GoForward() {
    360   if (!current_target_.view_id.IsTab()) {
    361     return new Error(kUnknownError,
    362                      "The current target does not support navigation");
    363   }
    364   Error* error = NULL;
    365   RunSessionTask(base::Bind(
    366       &Automation::GoForward,
    367       base::Unretained(automation_.get()),
    368       current_target_.view_id,
    369       &error));
    370   return error;
    371 }
    372 
    373 Error* Session::GoBack() {
    374   if (!current_target_.view_id.IsTab()) {
    375     return new Error(kUnknownError,
    376                      "The current target does not support navigation");
    377   }
    378   Error* error = NULL;
    379   RunSessionTask(base::Bind(
    380       &Automation::GoBack,
    381       base::Unretained(automation_.get()),
    382       current_target_.view_id,
    383       &error));
    384   return error;
    385 }
    386 
    387 Error* Session::Reload() {
    388   if (!current_target_.view_id.IsTab()) {
    389     return new Error(kUnknownError,
    390                      "The current target does not support navigation");
    391   }
    392   Error* error = NULL;
    393   RunSessionTask(base::Bind(
    394       &Automation::Reload,
    395       base::Unretained(automation_.get()),
    396       current_target_.view_id,
    397       &error));
    398   return error;
    399 }
    400 
    401 Error* Session::GetURL(std::string* url) {
    402   return ExecuteScriptAndParse(current_target_,
    403                                "function() { return document.URL }",
    404                                "getUrl",
    405                                new base::ListValue(),
    406                                CreateDirectValueParser(url));
    407 }
    408 
    409 Error* Session::GetTitle(std::string* tab_title) {
    410   const char* kGetTitleScript =
    411       "function() {"
    412       "  if (document.title)"
    413       "    return document.title;"
    414       "  else"
    415       "    return document.URL;"
    416       "}";
    417   return ExecuteScriptAndParse(FrameId(current_target_.view_id, FramePath()),
    418                                kGetTitleScript,
    419                                "getTitle",
    420                                new base::ListValue(),
    421                                CreateDirectValueParser(tab_title));
    422 }
    423 
    424 Error* Session::MouseMoveAndClick(const Point& location,
    425                                   automation::MouseButton button) {
    426   Error* error = NULL;
    427   if (build_no_ >= kNewMouseAPIMinVersion) {
    428     std::vector<WebMouseEvent> events;
    429     events.push_back(CreateWebMouseEvent(kMouseMove, kNoButton, location, 0));
    430     events.push_back(CreateWebMouseEvent(kMouseDown, button, location, 1));
    431     events.push_back(CreateWebMouseEvent(kMouseUp, button, location, 1));
    432     error = ProcessWebMouseEvents(events);
    433   } else {
    434     RunSessionTask(base::Bind(
    435         &Automation::MouseClickDeprecated,
    436         base::Unretained(automation_.get()),
    437         current_target_.view_id,
    438         location,
    439         button,
    440         &error));
    441   }
    442   if (!error)
    443     mouse_position_ = location;
    444   return error;
    445 }
    446 
    447 Error* Session::MouseMove(const Point& location) {
    448   Error* error = NULL;
    449   if (build_no_ >= kNewMouseAPIMinVersion) {
    450     std::vector<WebMouseEvent> events;
    451     events.push_back(CreateWebMouseEvent(kMouseMove, kNoButton, location, 0));
    452     error = ProcessWebMouseEvents(events);
    453   } else {
    454     RunSessionTask(base::Bind(
    455         &Automation::MouseMoveDeprecated,
    456         base::Unretained(automation_.get()),
    457         current_target_.view_id,
    458         location,
    459         &error));
    460   }
    461   if (!error)
    462     mouse_position_ = location;
    463   return error;
    464 }
    465 
    466 Error* Session::MouseDrag(const Point& start,
    467                           const Point& end) {
    468   Error* error = NULL;
    469   if (build_no_ >= kNewMouseAPIMinVersion) {
    470     std::vector<WebMouseEvent> events;
    471     events.push_back(CreateWebMouseEvent(kMouseMove, kNoButton, start, 0));
    472     events.push_back(CreateWebMouseEvent(kMouseDown, kLeftButton, start, 1));
    473     events.push_back(CreateWebMouseEvent(kMouseMove, kLeftButton, end, 0));
    474     events.push_back(CreateWebMouseEvent(kMouseUp, kLeftButton, end, 1));
    475     error = ProcessWebMouseEvents(events);
    476   } else {
    477     RunSessionTask(base::Bind(
    478         &Automation::MouseDragDeprecated,
    479         base::Unretained(automation_.get()),
    480         current_target_.view_id,
    481         start,
    482         end,
    483         &error));
    484   }
    485   if (!error)
    486     mouse_position_ = end;
    487   return error;
    488 }
    489 
    490 Error* Session::MouseClick(automation::MouseButton button) {
    491   if (build_no_ >= kNewMouseAPIMinVersion) {
    492     std::vector<WebMouseEvent> events;
    493     events.push_back(CreateWebMouseEvent(
    494         kMouseDown, button, mouse_position_, 1));
    495     events.push_back(CreateWebMouseEvent(
    496         kMouseUp, button, mouse_position_, 1));
    497     return ProcessWebMouseEvents(events);
    498   } else {
    499     return MouseMoveAndClick(mouse_position_, button);
    500   }
    501 }
    502 
    503 Error* Session::MouseButtonDown() {
    504   Error* error = NULL;
    505   if (build_no_ >= kNewMouseAPIMinVersion) {
    506     std::vector<WebMouseEvent> events;
    507     events.push_back(CreateWebMouseEvent(
    508         kMouseDown, kLeftButton, mouse_position_, 1));
    509     error = ProcessWebMouseEvents(events);
    510   } else {
    511     RunSessionTask(base::Bind(
    512         &Automation::MouseButtonDownDeprecated,
    513         base::Unretained(automation_.get()),
    514         current_target_.view_id,
    515         mouse_position_,
    516         &error));
    517   }
    518   return error;
    519 }
    520 
    521 Error* Session::MouseButtonUp() {
    522   Error* error = NULL;
    523   if (build_no_ >= kNewMouseAPIMinVersion) {
    524     std::vector<WebMouseEvent> events;
    525     events.push_back(CreateWebMouseEvent(
    526         kMouseUp, kLeftButton, mouse_position_, 1));
    527     error = ProcessWebMouseEvents(events);
    528   } else {
    529     RunSessionTask(base::Bind(
    530         &Automation::MouseButtonUpDeprecated,
    531         base::Unretained(automation_.get()),
    532         current_target_.view_id,
    533         mouse_position_,
    534         &error));
    535   }
    536   return error;
    537 }
    538 
    539 Error* Session::MouseDoubleClick() {
    540   Error* error = NULL;
    541   if (build_no_ >= kNewMouseAPIMinVersion) {
    542     std::vector<WebMouseEvent> events;
    543     events.push_back(CreateWebMouseEvent(
    544         kMouseDown, kLeftButton, mouse_position_, 1));
    545     events.push_back(CreateWebMouseEvent(
    546         kMouseUp, kLeftButton, mouse_position_, 1));
    547     events.push_back(CreateWebMouseEvent(
    548         kMouseDown, kLeftButton, mouse_position_, 2));
    549     events.push_back(CreateWebMouseEvent(
    550         kMouseUp, kLeftButton, mouse_position_, 2));
    551     error = ProcessWebMouseEvents(events);
    552   } else {
    553     RunSessionTask(base::Bind(
    554         &Automation::MouseDoubleClickDeprecated,
    555         base::Unretained(automation_.get()),
    556         current_target_.view_id,
    557         mouse_position_,
    558         &error));
    559   }
    560   return error;
    561 }
    562 
    563 Error* Session::GetCookies(const std::string& url,
    564                            scoped_ptr<base::ListValue>* cookies) {
    565   Error* error = NULL;
    566   RunSessionTask(base::Bind(
    567       &Automation::GetCookies,
    568       base::Unretained(automation_.get()),
    569       url,
    570       cookies,
    571       &error));
    572   return error;
    573 }
    574 
    575 Error* Session::DeleteCookie(const std::string& url,
    576                            const std::string& cookie_name) {
    577   Error* error = NULL;
    578   RunSessionTask(base::Bind(
    579       &Automation::DeleteCookie,
    580       base::Unretained(automation_.get()),
    581       url,
    582       cookie_name,
    583       &error));
    584   return error;
    585 }
    586 
    587 // Note that when this is called from CookieCommand::ExecutePost then
    588 // |cookie_dict| is destroyed as soon as the caller finishes. Therefore
    589 // it is essential that RunSessionTask executes synchronously.
    590 Error* Session::SetCookie(const std::string& url,
    591                           base::DictionaryValue* cookie_dict) {
    592   Error* error = NULL;
    593   RunSessionTask(base::Bind(
    594       &Automation::SetCookie,
    595       base::Unretained(automation_.get()),
    596       url,
    597       cookie_dict,
    598       &error));
    599   return error;
    600 }
    601 
    602 Error* Session::GetViews(std::vector<WebViewInfo>* views) {
    603   Error* error = NULL;
    604   RunSessionTask(base::Bind(
    605       &Automation::GetViews,
    606       base::Unretained(automation_.get()),
    607       views,
    608       &error));
    609   return error;
    610 }
    611 
    612 Error* Session::SwitchToView(const std::string& id_or_name) {
    613   Error* error = NULL;
    614   bool does_exist = false;
    615 
    616   WebViewId new_view;
    617   StringToWebViewId(id_or_name, &new_view);
    618   if (new_view.IsValid()) {
    619     RunSessionTask(base::Bind(
    620         &Automation::DoesViewExist,
    621         base::Unretained(automation_.get()),
    622         new_view,
    623         &does_exist,
    624         &error));
    625     if (error)
    626       return error;
    627   }
    628 
    629   if (!does_exist) {
    630     // See if any of the tab window names match |name|.
    631     std::vector<WebViewInfo> views;
    632     Error* error = GetViews(&views);
    633     if (error)
    634       return error;
    635     for (size_t i = 0; i < views.size(); ++i) {
    636       if (!views[i].view_id.IsTab())
    637         continue;
    638       std::string window_name;
    639       Error* error = ExecuteScriptAndParse(
    640           FrameId(views[i].view_id, FramePath()),
    641           "function() { return window.name; }",
    642           "getWindowName",
    643           new base::ListValue(),
    644           CreateDirectValueParser(&window_name));
    645       if (error)
    646         return error;
    647       if (id_or_name == window_name) {
    648         new_view = views[i].view_id;
    649         does_exist = true;
    650         break;
    651       }
    652     }
    653   }
    654 
    655   if (!does_exist)
    656     return new Error(kNoSuchWindow);
    657   frame_elements_.clear();
    658   current_target_ = FrameId(new_view, FramePath());
    659   return NULL;
    660 }
    661 
    662 Error* Session::SwitchToFrameWithNameOrId(const std::string& name_or_id) {
    663   std::string script =
    664       "function(arg) {"
    665       "  var xpath = '(/html/body//iframe|/html/frameset/frame)';"
    666       "  var sub = function(s) { return s.replace(/\\$/g, arg); };"
    667       "  xpath += sub('[@name=\"$\" or @id=\"$\"]');"
    668       "  return document.evaluate(xpath, document, null, "
    669       "      XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
    670       "}";
    671   return SwitchToFrameWithJavaScriptLocatedFrame(
    672       script, CreateListValueFrom(name_or_id));
    673 }
    674 
    675 Error* Session::SwitchToFrameWithIndex(int index) {
    676   // We cannot simply index into window.frames because we need to know the
    677   // tagName of the frameElement. If child frame N is from another domain, then
    678   // the following will run afoul of the same origin policy:
    679   //   window.frames[N].frameElement;
    680   // Instead of indexing window.frames, we use an XPath expression to index
    681   // into the list of all IFRAME and FRAME elements on the page - if we find
    682   // something, then that XPath expression can be used as the new frame's XPath.
    683   std::string script =
    684       "function(index) {"
    685       "  var xpathIndex = '[' + (index + 1) + ']';"
    686       "  var xpath = '(/html/body//iframe|/html/frameset/frame)' + "
    687       "              xpathIndex;"
    688       "  return document.evaluate(xpath, document, null, "
    689       "      XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
    690       "}";
    691   return SwitchToFrameWithJavaScriptLocatedFrame(
    692       script, CreateListValueFrom(index));
    693 }
    694 
    695 Error* Session::SwitchToFrameWithElement(const ElementId& element) {
    696   // TODO(jleyba): Extract this, and the other frame switch methods to an atom.
    697   std::string script =
    698       "function(elem) {"
    699       "  if (elem.nodeType != 1 || !/^i?frame$/i.test(elem.tagName)) {"
    700       "    console.error('Element is not a frame');"
    701       "    return null;"
    702       "  }"
    703       "  for (var i = 0; i < window.frames.length; i++) {"
    704       "    if (elem.contentWindow == window.frames[i]) {"
    705       "      return elem;"
    706       "    }"
    707       "  }"
    708       "  console.info('Frame is not connected to this DOM tree');"
    709       "  return null;"
    710       "}";
    711   return SwitchToFrameWithJavaScriptLocatedFrame(
    712       script, CreateListValueFrom(element));
    713 }
    714 
    715 void Session::SwitchToTopFrame() {
    716   frame_elements_.clear();
    717   current_target_.frame_path = FramePath();
    718 }
    719 
    720 Error* Session::SwitchToTopFrameIfCurrentFrameInvalid() {
    721   std::vector<std::string> components;
    722   current_target_.frame_path.GetComponents(&components);
    723   if (frame_elements_.size() != components.size()) {
    724     return new Error(kUnknownError,
    725                      "Frame element vector out of sync with frame path");
    726   }
    727   FramePath frame_path;
    728   // Start from the root path and check that each frame element that makes
    729   // up the current frame target is valid by executing an empty script.
    730   // This code should not execute script in any frame before making sure the
    731   // frame element is valid, otherwise the automation hangs until a timeout.
    732   for (size_t i = 0; i < frame_elements_.size(); ++i) {
    733     FrameId frame_id(current_target_.view_id, frame_path);
    734     scoped_ptr<Error> error(ExecuteScriptAndParse(
    735         frame_id,
    736         "function(){ }",
    737         "emptyScript",
    738         CreateListValueFrom(frame_elements_[i]),
    739         CreateDirectValueParser(kSkipParsing)));
    740     if (error.get() && error->code() == kStaleElementReference) {
    741       SwitchToTopFrame();
    742     } else if (error.get()) {
    743       return error.release();
    744     }
    745     frame_path = frame_path.Append(components[i]);
    746   }
    747   return NULL;
    748 }
    749 
    750 Error* Session::CloseWindow() {
    751   Error* error = NULL;
    752   RunSessionTask(base::Bind(
    753       &Automation::CloseView,
    754       base::Unretained(automation_.get()),
    755       current_target_.view_id,
    756       &error));
    757 
    758   if (!error) {
    759     std::vector<WebViewInfo> views;
    760     scoped_ptr<Error> error(GetViews(&views));
    761     if (error.get() || views.empty()) {
    762       // The automation connection will soon be closed, if not already,
    763       // because we supposedly just closed the last window. Terminate the
    764       // session.
    765       // TODO(kkania): This will cause us problems if GetWindowIds fails for a
    766       // reason other than the channel is disconnected. Look into having
    767       // |GetWindowIds| tell us if it just closed the last window.
    768       Terminate();
    769     }
    770   }
    771   return error;
    772 }
    773 
    774 Error* Session::GetWindowBounds(const WebViewId& window, Rect* bounds) {
    775   const char* kGetWindowBoundsScript =
    776       "function() {"
    777       "  return {"
    778       "    'left': window.screenX,"
    779       "    'top': window.screenY,"
    780       "    'width': window.outerWidth,"
    781       "    'height': window.outerHeight"
    782       "  }"
    783       "}";
    784   return ExecuteScriptAndParse(
    785       FrameId(window, FramePath()),
    786       kGetWindowBoundsScript,
    787       "getWindowBoundsScript",
    788       new base::ListValue(),
    789       CreateDirectValueParser(bounds));
    790 }
    791 
    792 Error* Session::SetWindowBounds(
    793     const WebViewId& window,
    794     const Rect& bounds) {
    795   Error* error = NULL;
    796   RunSessionTask(base::Bind(
    797       &Automation::SetViewBounds,
    798       base::Unretained(automation_.get()),
    799       window,
    800       bounds,
    801       &error));
    802   return error;
    803 }
    804 
    805 Error* Session::MaximizeWindow(const WebViewId& window) {
    806   Error* error = NULL;
    807   RunSessionTask(base::Bind(
    808         &Automation::MaximizeView,
    809         base::Unretained(automation_.get()),
    810         window,
    811         &error));
    812   return error;
    813 }
    814 
    815 Error* Session::GetAlertMessage(std::string* text) {
    816   Error* error = NULL;
    817   RunSessionTask(base::Bind(
    818       &Automation::GetAppModalDialogMessage,
    819       base::Unretained(automation_.get()),
    820       text,
    821       &error));
    822   return error;
    823 }
    824 
    825 Error* Session::SetAlertPromptText(const std::string& alert_prompt_text) {
    826   std::string message_text;
    827   // Only set the alert prompt text if an alert is actually active.
    828   Error* error = GetAlertMessage(&message_text);
    829   if (!error) {
    830     has_alert_prompt_text_ = true;
    831     alert_prompt_text_ = alert_prompt_text;
    832   }
    833   return error;
    834 }
    835 
    836 Error* Session::AcceptOrDismissAlert(bool accept) {
    837   Error* error = NULL;
    838   if (accept && has_alert_prompt_text_) {
    839     RunSessionTask(base::Bind(
    840         &Automation::AcceptPromptAppModalDialog,
    841         base::Unretained(automation_.get()),
    842         alert_prompt_text_,
    843         &error));
    844   } else {
    845     RunSessionTask(base::Bind(
    846         &Automation::AcceptOrDismissAppModalDialog,
    847         base::Unretained(automation_.get()),
    848         accept,
    849         &error));
    850   }
    851   has_alert_prompt_text_ = false;
    852   return error;
    853 }
    854 
    855 std::string Session::GetBrowserVersion() {
    856   std::string version;
    857   RunSessionTask(base::Bind(
    858       &Automation::GetBrowserVersion,
    859       base::Unretained(automation_.get()),
    860       &version));
    861   return version;
    862 }
    863 
    864 Error* Session::CompareBrowserVersion(int client_build_no,
    865                                       int client_patch_no,
    866                                       bool* is_newer_or_equal) {
    867   std::string version = GetBrowserVersion();
    868   std::vector<std::string> split_version;
    869   base::SplitString(version, '.', &split_version);
    870   if (split_version.size() != 4) {
    871     return new Error(
    872         kUnknownError, "Browser version has unrecognized format: " + version);
    873   }
    874   int build_no, patch_no;
    875   if (!base::StringToInt(split_version[2], &build_no) ||
    876       !base::StringToInt(split_version[3], &patch_no)) {
    877     return new Error(
    878         kUnknownError, "Browser version has unrecognized format: " + version);
    879   }
    880   if (build_no < client_build_no)
    881     *is_newer_or_equal = false;
    882   else if (build_no > client_build_no)
    883     *is_newer_or_equal = true;
    884   else
    885     *is_newer_or_equal = patch_no >= client_patch_no;
    886   return NULL;
    887 }
    888 
    889 Error* Session::FindElement(const FrameId& frame_id,
    890                             const ElementId& root_element,
    891                             const std::string& locator,
    892                             const std::string& query,
    893                             ElementId* element) {
    894   std::vector<ElementId> elements;
    895   Error* error = FindElementsHelper(
    896       frame_id, root_element, locator, query, true, &elements);
    897   if (!error)
    898     *element = elements[0];
    899   return error;
    900 }
    901 
    902 Error* Session::FindElements(const FrameId& frame_id,
    903                              const ElementId& root_element,
    904                              const std::string& locator,
    905                              const std::string& query,
    906                              std::vector<ElementId>* elements) {
    907   return FindElementsHelper(
    908       frame_id, root_element, locator, query, false, elements);
    909 }
    910 
    911 Error* Session::GetElementLocationInView(
    912     const ElementId& element,
    913     Point* location) {
    914   Size size;
    915   Error* error = GetElementSize(current_target_, element, &size);
    916   if (error)
    917     return error;
    918   return GetElementRegionInView(
    919       element, Rect(Point(0, 0), size),
    920       false /* center */, false /* verify_clickable_at_middle */, location);
    921 }
    922 
    923 Error* Session::GetElementRegionInView(
    924     const ElementId& element,
    925     const Rect& region,
    926     bool center,
    927     bool verify_clickable_at_middle,
    928     Point* location) {
    929   CHECK(element.is_valid());
    930 
    931   Point region_offset = region.origin();
    932   Size region_size = region.size();
    933   Error* error = GetElementRegionInViewHelper(
    934       current_target_, element, region, center, verify_clickable_at_middle,
    935       &region_offset);
    936   if (error)
    937     return error;
    938 
    939   for (FramePath frame_path = current_target_.frame_path;
    940        frame_path.IsSubframe();
    941        frame_path = frame_path.Parent()) {
    942     // Find the frame element for the current frame path.
    943     FrameId frame_id(current_target_.view_id, frame_path.Parent());
    944     ElementId frame_element;
    945     error = FindElement(frame_id,
    946                         ElementId(std::string()),
    947                         LocatorType::kXpath,
    948                         frame_path.BaseName().value(),
    949                         &frame_element);
    950     if (error) {
    951       std::string context = base::StringPrintf(
    952           "Could not find frame element (%s) in frame (%s)",
    953           frame_path.BaseName().value().c_str(),
    954           frame_path.Parent().value().c_str());
    955       error->AddDetails(context);
    956       return error;
    957     }
    958     // Modify |region_offset| by the frame's border.
    959     int border_left, border_top;
    960     error = GetElementBorder(
    961         frame_id, frame_element, &border_left, &border_top);
    962     if (error)
    963       return error;
    964     region_offset.Offset(border_left, border_top);
    965 
    966     error = GetElementRegionInViewHelper(
    967         frame_id, frame_element, Rect(region_offset, region_size),
    968         center, verify_clickable_at_middle, &region_offset);
    969     if (error)
    970       return error;
    971   }
    972   *location = region_offset;
    973   return NULL;
    974 }
    975 
    976 Error* Session::GetElementSize(const FrameId& frame_id,
    977                                const ElementId& element,
    978                                Size* size) {
    979   return ExecuteScriptAndParse(
    980       frame_id,
    981       atoms::asString(atoms::GET_SIZE),
    982       "getSize",
    983       CreateListValueFrom(element),
    984       CreateDirectValueParser(size));
    985 }
    986 
    987 Error* Session::GetElementFirstClientRect(const FrameId& frame_id,
    988                                           const ElementId& element,
    989                                           Rect* rect) {
    990   return ExecuteScriptAndParse(
    991       frame_id,
    992       atoms::asString(atoms::GET_FIRST_CLIENT_RECT),
    993       "getFirstClientRect",
    994       CreateListValueFrom(element),
    995       CreateDirectValueParser(rect));
    996 }
    997 
    998 Error* Session::GetElementEffectiveStyle(
    999     const FrameId& frame_id,
   1000     const ElementId& element,
   1001     const std::string& prop,
   1002     std::string* value) {
   1003   return ExecuteScriptAndParse(
   1004       frame_id,
   1005       atoms::asString(atoms::GET_EFFECTIVE_STYLE),
   1006       "getEffectiveStyle",
   1007       CreateListValueFrom(element, prop),
   1008       CreateDirectValueParser(value));
   1009 }
   1010 
   1011 Error* Session::GetElementBorder(const FrameId& frame_id,
   1012                                  const ElementId& element,
   1013                                  int* border_left,
   1014                                  int* border_top) {
   1015   std::string border_left_str, border_top_str;
   1016   Error* error = GetElementEffectiveStyle(
   1017       frame_id, element, "border-left-width", &border_left_str);
   1018   if (error)
   1019     return error;
   1020   error = GetElementEffectiveStyle(
   1021       frame_id, element, "border-top-width", &border_top_str);
   1022   if (error)
   1023     return error;
   1024 
   1025   base::StringToInt(border_left_str, border_left);
   1026   base::StringToInt(border_top_str, border_top);
   1027   return NULL;
   1028 }
   1029 
   1030 Error* Session::IsElementDisplayed(const FrameId& frame_id,
   1031                                    const ElementId& element,
   1032                                    bool ignore_opacity,
   1033                                    bool* is_displayed) {
   1034   return ExecuteScriptAndParse(
   1035       frame_id,
   1036       atoms::asString(atoms::IS_DISPLAYED),
   1037       "isDisplayed",
   1038       CreateListValueFrom(element, ignore_opacity),
   1039       CreateDirectValueParser(is_displayed));
   1040 }
   1041 
   1042 Error* Session::IsElementEnabled(const FrameId& frame_id,
   1043                                  const ElementId& element,
   1044                                  bool* is_enabled) {
   1045   return ExecuteScriptAndParse(
   1046       frame_id,
   1047       atoms::asString(atoms::IS_ENABLED),
   1048       "isEnabled",
   1049       CreateListValueFrom(element),
   1050       CreateDirectValueParser(is_enabled));
   1051 }
   1052 
   1053 Error* Session::IsOptionElementSelected(const FrameId& frame_id,
   1054                                         const ElementId& element,
   1055                                         bool* is_selected) {
   1056   return ExecuteScriptAndParse(
   1057       frame_id,
   1058       atoms::asString(atoms::IS_SELECTED),
   1059       "isSelected",
   1060       CreateListValueFrom(element),
   1061       CreateDirectValueParser(is_selected));
   1062 }
   1063 
   1064 Error* Session::SetOptionElementSelected(const FrameId& frame_id,
   1065                                          const ElementId& element,
   1066                                          bool selected) {
   1067   // This wrapper ensures the script is started successfully and
   1068   // allows for an alert to happen when the option selection occurs.
   1069   // See selenium bug 2671.
   1070   const char kSetSelectedWrapper[] =
   1071       "var args = [].slice.apply(arguments);"
   1072       "args[args.length - 1]();"
   1073       "return (%s).apply(null, args.slice(0, args.length - 1));";
   1074   base::Value* value = NULL;
   1075   Error* error = ExecuteAsyncScript(
   1076       frame_id,
   1077       base::StringPrintf(kSetSelectedWrapper,
   1078                          atoms::asString(atoms::CLICK).c_str()),
   1079       CreateListValueFrom(element, selected),
   1080       &value);
   1081   scoped_ptr<base::Value> scoped_value(value);
   1082   return error;
   1083 }
   1084 
   1085 Error* Session::ToggleOptionElement(const FrameId& frame_id,
   1086                                     const ElementId& element) {
   1087   bool is_selected;
   1088   Error* error = IsOptionElementSelected(frame_id, element, &is_selected);
   1089   if (error)
   1090     return error;
   1091 
   1092   return SetOptionElementSelected(frame_id, element, !is_selected);
   1093 }
   1094 
   1095 Error* Session::GetElementTagName(const FrameId& frame_id,
   1096                                   const ElementId& element,
   1097                                   std::string* tag_name) {
   1098   return ExecuteScriptAndParse(
   1099       frame_id,
   1100       "function(elem) { return elem.tagName.toLowerCase() }",
   1101       "getElementTagName",
   1102       CreateListValueFrom(element),
   1103       CreateDirectValueParser(tag_name));
   1104 }
   1105 
   1106 Error* Session::GetClickableLocation(const ElementId& element,
   1107                                      Point* location) {
   1108   bool is_displayed = false;
   1109   Error* error = IsElementDisplayed(
   1110       current_target_, element, true /* ignore_opacity */, &is_displayed);
   1111   if (error)
   1112     return error;
   1113   if (!is_displayed)
   1114     return new Error(kElementNotVisible, "Element must be displayed to click");
   1115 
   1116   // We try 3 methods to determine clickable location. This mostly follows
   1117   // what FirefoxDriver does. Try the first client rect, then the bounding
   1118   // client rect, and lastly the size of the element (via closure).
   1119   // SVG is one case that doesn't have a first client rect.
   1120   Rect rect;
   1121   scoped_ptr<Error> ignore_error(
   1122       GetElementFirstClientRect(current_target_, element, &rect));
   1123   if (ignore_error.get()) {
   1124     Rect client_rect;
   1125     ignore_error.reset(ExecuteScriptAndParse(
   1126         current_target_,
   1127         "function(elem) { return elem.getBoundingClientRect() }",
   1128         "getBoundingClientRect",
   1129         CreateListValueFrom(element),
   1130         CreateDirectValueParser(&client_rect)));
   1131     rect = Rect(0, 0, client_rect.width(), client_rect.height());
   1132   }
   1133   if (ignore_error.get()) {
   1134     Size size;
   1135     ignore_error.reset(GetElementSize(current_target_, element, &size));
   1136     rect = Rect(0, 0, size.width(), size.height());
   1137   }
   1138   if (ignore_error.get()) {
   1139     return new Error(kUnknownError,
   1140                      "Unable to determine clickable location of element");
   1141   }
   1142 
   1143   error = GetElementRegionInView(
   1144       element, rect, true /* center */, true /* verify_clickable_at_middle */,
   1145       location);
   1146   if (error)
   1147     return error;
   1148   location->Offset(rect.width() / 2, rect.height() / 2);
   1149   return NULL;
   1150 }
   1151 
   1152 Error* Session::GetAttribute(const ElementId& element,
   1153                              const std::string& key,
   1154                              base::Value** value) {
   1155   return ExecuteScriptAndParse(
   1156       current_target_,
   1157       atoms::asString(atoms::GET_ATTRIBUTE),
   1158       "getAttribute",
   1159       CreateListValueFrom(element, key),
   1160       CreateDirectValueParser(value));
   1161 }
   1162 
   1163 Error* Session::WaitForAllViewsToStopLoading() {
   1164   if (!automation_.get())
   1165     return NULL;
   1166 
   1167   logger_.Log(kFinerLogLevel, "Waiting for all views to stop loading...");
   1168   Error* error = NULL;
   1169   RunSessionTask(base::Bind(
   1170       &Automation::WaitForAllViewsToStopLoading,
   1171       base::Unretained(automation_.get()),
   1172       &error));
   1173   logger_.Log(kFinerLogLevel, "Done waiting for all views to stop loading");
   1174   return error;
   1175 }
   1176 
   1177 Error* Session::InstallExtension(
   1178     const base::FilePath& path, std::string* extension_id) {
   1179   Error* error = NULL;
   1180   RunSessionTask(base::Bind(
   1181       &Automation::InstallExtension,
   1182       base::Unretained(automation_.get()),
   1183       path,
   1184       extension_id,
   1185       &error));
   1186   return error;
   1187 }
   1188 
   1189 Error* Session::GetExtensionsInfo(base::ListValue* extensions_list) {
   1190   Error* error = NULL;
   1191   RunSessionTask(base::Bind(
   1192       &Automation::GetExtensionsInfo,
   1193       base::Unretained(automation_.get()),
   1194       extensions_list,
   1195       &error));
   1196   return error;
   1197 }
   1198 
   1199 Error* Session::IsPageActionVisible(
   1200     const WebViewId& tab_id,
   1201     const std::string& extension_id,
   1202     bool* is_visible) {
   1203   if (!tab_id.IsTab()) {
   1204     return new Error(
   1205         kUnknownError,
   1206         "The current target does not support page actions. Switch to a tab.");
   1207   }
   1208   Error* error = NULL;
   1209   RunSessionTask(base::Bind(
   1210       &Automation::IsPageActionVisible,
   1211       base::Unretained(automation_.get()),
   1212       tab_id,
   1213       extension_id,
   1214       is_visible,
   1215       &error));
   1216   return error;
   1217 }
   1218 
   1219 Error* Session::SetExtensionState(
   1220     const std::string& extension_id, bool enable) {
   1221   Error* error = NULL;
   1222   RunSessionTask(base::Bind(
   1223       &Automation::SetExtensionState,
   1224       base::Unretained(automation_.get()),
   1225       extension_id,
   1226       enable,
   1227       &error));
   1228   return error;
   1229 }
   1230 
   1231 Error* Session::ClickExtensionButton(
   1232     const std::string& extension_id, bool browser_action) {
   1233   Error* error = NULL;
   1234   RunSessionTask(base::Bind(
   1235       &Automation::ClickExtensionButton,
   1236       base::Unretained(automation_.get()),
   1237       extension_id,
   1238       browser_action,
   1239       &error));
   1240   return error;
   1241 }
   1242 
   1243 Error* Session::UninstallExtension(const std::string& extension_id) {
   1244   Error* error = NULL;
   1245   RunSessionTask(base::Bind(
   1246       &Automation::UninstallExtension,
   1247       base::Unretained(automation_.get()),
   1248       extension_id,
   1249       &error));
   1250   return error;
   1251 }
   1252 
   1253 Error* Session::SetPreference(
   1254     const std::string& pref,
   1255     bool is_user_pref,
   1256     base::Value* value) {
   1257   Error* error = NULL;
   1258   if (is_user_pref) {
   1259     RunSessionTask(base::Bind(
   1260         &Automation::SetPreference,
   1261         base::Unretained(automation_.get()),
   1262         pref,
   1263         value,
   1264         &error));
   1265     if (error)
   1266       error->AddDetails("Failed to set user pref '" + pref + "'");
   1267   } else {
   1268     RunSessionTask(base::Bind(
   1269         &Automation::SetLocalStatePreference,
   1270         base::Unretained(automation_.get()),
   1271         pref,
   1272         value,
   1273         &error));
   1274     if (error)
   1275       error->AddDetails("Failed to set local state pref '" + pref + "'");
   1276   }
   1277   return error;
   1278 }
   1279 
   1280 base::ListValue* Session::GetLog() const {
   1281   return session_log_->entries_list()->DeepCopy();
   1282 }
   1283 
   1284 Error* Session::GetBrowserConnectionState(bool* online) {
   1285   return ExecuteScriptAndParse(
   1286       current_target_,
   1287       atoms::asString(atoms::IS_ONLINE),
   1288       "isOnline",
   1289       new base::ListValue(),
   1290       CreateDirectValueParser(online));
   1291 }
   1292 
   1293 Error* Session::GetAppCacheStatus(int* status) {
   1294   return ExecuteScriptAndParse(
   1295       current_target_,
   1296       atoms::asString(atoms::GET_APPCACHE_STATUS),
   1297       "getAppcacheStatus",
   1298       new base::ListValue(),
   1299       CreateDirectValueParser(status));
   1300 }
   1301 
   1302 Error* Session::GetStorageSize(StorageType type, int* size) {
   1303   std::string js = atoms::asString(
   1304       type == kLocalStorageType ? atoms::GET_LOCAL_STORAGE_SIZE
   1305                                 : atoms::GET_SESSION_STORAGE_SIZE);
   1306   return ExecuteScriptAndParse(
   1307       current_target_,
   1308       js,
   1309       "getStorageSize",
   1310       new base::ListValue(),
   1311       CreateDirectValueParser(size));
   1312 }
   1313 
   1314 Error* Session::SetStorageItem(StorageType type,
   1315                                const std::string& key,
   1316                                const std::string& value) {
   1317   std::string js = atoms::asString(
   1318       type == kLocalStorageType ? atoms::SET_LOCAL_STORAGE_ITEM
   1319                                 : atoms::SET_SESSION_STORAGE_ITEM);
   1320   return ExecuteScriptAndParse(
   1321       current_target_,
   1322       js,
   1323       "setStorageItem",
   1324       CreateListValueFrom(key, value),
   1325       CreateDirectValueParser(kSkipParsing));
   1326 }
   1327 
   1328 Error* Session::ClearStorage(StorageType type) {
   1329   std::string js = atoms::asString(
   1330       type == kLocalStorageType ? atoms::CLEAR_LOCAL_STORAGE
   1331                                 : atoms::CLEAR_SESSION_STORAGE);
   1332   return ExecuteScriptAndParse(
   1333       current_target_,
   1334       js,
   1335       "clearStorage",
   1336       new base::ListValue(),
   1337       CreateDirectValueParser(kSkipParsing));
   1338 }
   1339 
   1340 Error* Session::GetStorageKeys(StorageType type, base::ListValue** keys) {
   1341   std::string js = atoms::asString(
   1342       type == kLocalStorageType ? atoms::GET_LOCAL_STORAGE_KEYS
   1343                                 : atoms::GET_SESSION_STORAGE_KEYS);
   1344   return ExecuteScriptAndParse(
   1345       current_target_,
   1346       js,
   1347       "getStorageKeys",
   1348       new base::ListValue(),
   1349       CreateDirectValueParser(keys));
   1350 }
   1351 
   1352 Error* Session::GetStorageItem(StorageType type,
   1353                                const std::string& key,
   1354                                std::string* value) {
   1355   std::string js = atoms::asString(
   1356       type == kLocalStorageType ? atoms::GET_LOCAL_STORAGE_ITEM
   1357                                 : atoms::GET_SESSION_STORAGE_ITEM);
   1358   return ExecuteScriptAndParse(
   1359       current_target_,
   1360       js,
   1361       "getStorageItem",
   1362       CreateListValueFrom(key),
   1363       CreateDirectValueParser(value));
   1364 }
   1365 
   1366 Error* Session::RemoveStorageItem(StorageType type,
   1367                                   const std::string& key,
   1368                                   std::string* value) {
   1369   std::string js = atoms::asString(
   1370       type == kLocalStorageType ? atoms::REMOVE_LOCAL_STORAGE_ITEM
   1371                                 : atoms::REMOVE_SESSION_STORAGE_ITEM);
   1372   return ExecuteScriptAndParse(
   1373       current_target_,
   1374       js,
   1375       "removeStorageItem",
   1376       CreateListValueFrom(key),
   1377       CreateDirectValueParser(value));
   1378 }
   1379 
   1380 Error* Session::GetGeolocation(
   1381     scoped_ptr<base::DictionaryValue>* geolocation) {
   1382   Error* error = NULL;
   1383   RunSessionTask(base::Bind(
   1384       &Automation::GetGeolocation,
   1385       base::Unretained(automation_.get()),
   1386       geolocation,
   1387       &error));
   1388   return error;
   1389 }
   1390 
   1391 Error* Session::OverrideGeolocation(const base::DictionaryValue* geolocation) {
   1392   Error* error = NULL;
   1393   RunSessionTask(base::Bind(
   1394       &Automation::OverrideGeolocation,
   1395       base::Unretained(automation_.get()),
   1396       geolocation,
   1397       &error));
   1398   return error;
   1399 }
   1400 
   1401 const std::string& Session::id() const {
   1402   return id_;
   1403 }
   1404 
   1405 const FrameId& Session::current_target() const {
   1406   return current_target_;
   1407 }
   1408 
   1409 void Session::set_async_script_timeout(int timeout_ms) {
   1410   async_script_timeout_ = timeout_ms;
   1411 }
   1412 
   1413 int Session::async_script_timeout() const {
   1414   return async_script_timeout_;
   1415 }
   1416 
   1417 void Session::set_implicit_wait(int timeout_ms) {
   1418   implicit_wait_ = timeout_ms;
   1419 }
   1420 
   1421 int Session::implicit_wait() const {
   1422   return implicit_wait_;
   1423 }
   1424 
   1425 const Point& Session::get_mouse_position() const {
   1426   return mouse_position_;
   1427 }
   1428 
   1429 const Logger& Session::logger() const {
   1430   return logger_;
   1431 }
   1432 
   1433 const base::FilePath& Session::temp_dir() const {
   1434   return temp_dir_.path();
   1435 }
   1436 
   1437 const Capabilities& Session::capabilities() const {
   1438   return capabilities_;
   1439 }
   1440 
   1441 void Session::RunSessionTask(const base::Closure& task) {
   1442   base::WaitableEvent done_event(false, false);
   1443   thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
   1444       &Session::RunClosureOnSessionThread,
   1445       base::Unretained(this),
   1446       task,
   1447       &done_event));
   1448   // See SetCookie for why it is essential that we wait here.
   1449   done_event.Wait();
   1450 }
   1451 
   1452 void Session::RunClosureOnSessionThread(const base::Closure& task,
   1453                                         base::WaitableEvent* done_event) {
   1454   task.Run();
   1455   done_event->Signal();
   1456 }
   1457 
   1458 void Session::InitOnSessionThread(const Automation::BrowserOptions& options,
   1459                                   int* build_no,
   1460                                   Error** error) {
   1461   automation_.reset(new Automation(logger_));
   1462   automation_->Init(options, build_no, error);
   1463   if (*error)
   1464     return;
   1465 
   1466   std::vector<WebViewInfo> views;
   1467   automation_->GetViews(&views, error);
   1468   if (*error)
   1469     return;
   1470   if (views.empty()) {
   1471     *error = new Error(kUnknownError, "No view ids after initialization");
   1472     return;
   1473   }
   1474   current_target_ = FrameId(views[0].view_id, FramePath());
   1475 }
   1476 
   1477 void Session::TerminateOnSessionThread() {
   1478   if (automation_.get())
   1479     automation_->Terminate();
   1480   automation_.reset();
   1481 }
   1482 
   1483 Error* Session::ExecuteScriptAndParseValue(const FrameId& frame_id,
   1484                                            const std::string& script,
   1485                                            base::Value** script_result) {
   1486   std::string response_json;
   1487   Error* error = NULL;
   1488   RunSessionTask(base::Bind(
   1489       &Automation::ExecuteScript,
   1490       base::Unretained(automation_.get()),
   1491       frame_id.view_id,
   1492       frame_id.frame_path,
   1493       script,
   1494       &response_json,
   1495       &error));
   1496   if (error)
   1497     return error;
   1498 
   1499   scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
   1500       response_json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, NULL));
   1501   if (!value.get())
   1502     return new Error(kUnknownError, "Failed to parse script result");
   1503   if (value->GetType() != base::Value::TYPE_DICTIONARY)
   1504     return new Error(kUnknownError, "Execute script returned non-dict: " +
   1505                          JsonStringify(value.get()));
   1506   base::DictionaryValue* result_dict =
   1507       static_cast<base::DictionaryValue*>(value.get());
   1508 
   1509   int status;
   1510   if (!result_dict->GetInteger("status", &status))
   1511     return new Error(kUnknownError, "Execute script did not return status: " +
   1512                          JsonStringify(result_dict));
   1513   ErrorCode code = static_cast<ErrorCode>(status);
   1514   if (code != kSuccess) {
   1515     base::DictionaryValue* error_dict;
   1516     std::string error_msg;
   1517     if (result_dict->GetDictionary("value", &error_dict))
   1518       error_dict->GetString("message", &error_msg);
   1519     if (error_msg.empty())
   1520       error_msg = "Script failed with error code: " + base::IntToString(code);
   1521     return new Error(code, error_msg);
   1522   }
   1523 
   1524   base::Value* tmp;
   1525   if (result_dict->Get("value", &tmp)) {
   1526     *script_result= tmp->DeepCopy();
   1527   } else {
   1528     // "value" was not defined in the returned dictionary; set to null.
   1529     *script_result= base::Value::CreateNullValue();
   1530   }
   1531   return NULL;
   1532 }
   1533 
   1534 void Session::SendKeysOnSessionThread(const string16& keys,
   1535                                       bool release_modifiers, Error** error) {
   1536   std::vector<WebKeyEvent> key_events;
   1537   std::string error_msg;
   1538   if (!ConvertKeysToWebKeyEvents(keys, logger_, release_modifiers,
   1539                                  &sticky_modifiers_, &key_events, &error_msg)) {
   1540     *error = new Error(kUnknownError, error_msg);
   1541     return;
   1542   }
   1543   for (size_t i = 0; i < key_events.size(); ++i) {
   1544     automation_->SendWebKeyEvent(
   1545         current_target_.view_id,
   1546         key_events[i], error);
   1547     if (*error) {
   1548       std::string details = base::StringPrintf(
   1549           "Failed to send key event. Event details:\n"
   1550               "Type: %d, KeyCode: %d, UnmodifiedText: %s, ModifiedText: %s, "
   1551               "Modifiers: %d",
   1552           key_events[i].type,
   1553           key_events[i].key_code,
   1554           key_events[i].unmodified_text.c_str(),
   1555           key_events[i].modified_text.c_str(),
   1556           key_events[i].modifiers);
   1557       (*error)->AddDetails(details);
   1558       return;
   1559     }
   1560   }
   1561 }
   1562 
   1563 Error* Session::ProcessWebMouseEvents(
   1564     const std::vector<WebMouseEvent>& events) {
   1565   for (size_t i = 0; i < events.size(); ++i) {
   1566     Error* error = NULL;
   1567     RunSessionTask(base::Bind(
   1568         &Automation::SendWebMouseEvent,
   1569         base::Unretained(automation_.get()),
   1570         current_target_.view_id,
   1571         events[i],
   1572         &error));
   1573     if (error)
   1574       return error;
   1575     mouse_position_ = Point(events[i].x, events[i].y);
   1576   }
   1577   return NULL;
   1578 }
   1579 
   1580 WebMouseEvent Session::CreateWebMouseEvent(
   1581     automation::MouseEventType type,
   1582     automation::MouseButton button,
   1583     const Point& point,
   1584     int click_count) {
   1585   return WebMouseEvent(type, button, point.rounded_x(), point.rounded_y(),
   1586                        click_count, sticky_modifiers_);
   1587 }
   1588 
   1589 Error* Session::SwitchToFrameWithJavaScriptLocatedFrame(
   1590     const std::string& script, base::ListValue* args) {
   1591   class SwitchFrameValueParser : public ValueParser {
   1592    public:
   1593     SwitchFrameValueParser(
   1594         bool* found_frame, ElementId* frame)
   1595         : found_frame_(found_frame), frame_(frame) { }
   1596 
   1597     virtual ~SwitchFrameValueParser() { }
   1598 
   1599     virtual bool Parse(base::Value* value) const OVERRIDE {
   1600       if (value->IsType(base::Value::TYPE_NULL)) {
   1601         *found_frame_ = false;
   1602         return true;
   1603       }
   1604       ElementId id(value);
   1605       if (!id.is_valid()) {
   1606         return false;
   1607       }
   1608       *frame_ = id;
   1609       *found_frame_ = true;
   1610       return true;
   1611     }
   1612 
   1613    private:
   1614     bool* found_frame_;
   1615     ElementId* frame_;
   1616   };
   1617 
   1618   bool found_frame;
   1619   ElementId new_frame_element;
   1620   Error* error = ExecuteScriptAndParse(
   1621       current_target_, script, "switchFrame", args,
   1622       new SwitchFrameValueParser(&found_frame, &new_frame_element));
   1623   if (error)
   1624     return error;
   1625 
   1626   if (!found_frame)
   1627     return new Error(kNoSuchFrame);
   1628 
   1629   std::string frame_id = GenerateRandomID();
   1630   error = ExecuteScriptAndParse(
   1631       current_target_,
   1632       "function(elem, id) { elem.setAttribute('wd_frame_id_', id); }",
   1633       "setFrameId",
   1634       CreateListValueFrom(new_frame_element, frame_id),
   1635       CreateDirectValueParser(kSkipParsing));
   1636   if (error)
   1637     return error;
   1638 
   1639   frame_elements_.push_back(new_frame_element);
   1640   current_target_.frame_path = current_target_.frame_path.Append(
   1641       base::StringPrintf("//*[@wd_frame_id_ = '%s']", frame_id.c_str()));
   1642   return NULL;
   1643 }
   1644 
   1645 Error* Session::FindElementsHelper(const FrameId& frame_id,
   1646                                    const ElementId& root_element,
   1647                                    const std::string& locator,
   1648                                    const std::string& query,
   1649                                    bool find_one,
   1650                                    std::vector<ElementId>* elements) {
   1651   CHECK(root_element.is_valid());
   1652   base::Time start_time = base::Time::Now();
   1653   while (true) {
   1654     std::vector<ElementId> temp_elements;
   1655     Error* error = ExecuteFindElementScriptAndParse(
   1656         frame_id, root_element, locator, query, find_one, &temp_elements);
   1657     if (error)
   1658       return error;
   1659 
   1660     if (temp_elements.size() > 0u) {
   1661       elements->swap(temp_elements);
   1662       break;
   1663     }
   1664 
   1665     if ((base::Time::Now() - start_time).InMilliseconds() > implicit_wait_) {
   1666       if (find_one)
   1667         return new Error(kNoSuchElement);
   1668       break;
   1669     }
   1670     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
   1671   }
   1672   return NULL;
   1673 }
   1674 
   1675 Error* Session::ExecuteFindElementScriptAndParse(
   1676     const FrameId& frame_id,
   1677     const ElementId& root_element,
   1678     const std::string& locator,
   1679     const std::string& query,
   1680     bool find_one,
   1681     std::vector<ElementId>* elements) {
   1682   CHECK(root_element.is_valid());
   1683 
   1684   class FindElementsParser : public ValueParser {
   1685    public:
   1686     explicit FindElementsParser(std::vector<ElementId>* elements)
   1687         : elements_(elements) { }
   1688 
   1689     virtual ~FindElementsParser() { }
   1690 
   1691     virtual bool Parse(base::Value* value) const OVERRIDE {
   1692       if (!value->IsType(base::Value::TYPE_LIST))
   1693         return false;
   1694       base::ListValue* list = static_cast<base::ListValue*>(value);
   1695       for (size_t i = 0; i < list->GetSize(); ++i) {
   1696         ElementId element;
   1697         base::Value* element_value = NULL;
   1698         if (!list->Get(i, &element_value))
   1699           return false;
   1700         if (!SetFromValue(element_value, &element))
   1701           return false;
   1702         elements_->push_back(element);
   1703       }
   1704       return true;
   1705     }
   1706    private:
   1707     std::vector<ElementId>* elements_;
   1708   };
   1709 
   1710   class FindElementParser : public ValueParser {
   1711    public:
   1712     explicit FindElementParser(std::vector<ElementId>* elements)
   1713         : elements_(elements) { }
   1714 
   1715     virtual ~FindElementParser() { }
   1716 
   1717     virtual bool Parse(base::Value* value) const OVERRIDE {
   1718       if (value->IsType(base::Value::TYPE_NULL))
   1719         return true;
   1720       ElementId element;
   1721       bool set = SetFromValue(value, &element);
   1722       if (set)
   1723         elements_->push_back(element);
   1724       return set;
   1725     }
   1726    private:
   1727     std::vector<ElementId>* elements_;
   1728   };
   1729 
   1730   base::DictionaryValue locator_dict;
   1731   locator_dict.SetString(locator, query);
   1732   std::vector<ElementId> temp_elements;
   1733   Error* error = NULL;
   1734   if (find_one) {
   1735     error = ExecuteScriptAndParse(
   1736           frame_id,
   1737           atoms::asString(atoms::FIND_ELEMENT),
   1738           "findElement",
   1739           CreateListValueFrom(&locator_dict, root_element),
   1740           new FindElementParser(&temp_elements));
   1741   } else {
   1742     error = ExecuteScriptAndParse(
   1743           frame_id,
   1744           atoms::asString(atoms::FIND_ELEMENTS),
   1745           "findElements",
   1746           CreateListValueFrom(&locator_dict, root_element),
   1747           new FindElementsParser(&temp_elements));
   1748   }
   1749   if (!error)
   1750     elements->swap(temp_elements);
   1751   return error;
   1752 }
   1753 
   1754 Error* Session::VerifyElementIsClickable(
   1755     const FrameId& frame_id,
   1756     const ElementId& element,
   1757     const Point& location) {
   1758   class IsElementClickableParser : public ValueParser {
   1759    public:
   1760     IsElementClickableParser(bool* clickable, std::string* message)
   1761         : clickable_(clickable), message_(message) { }
   1762 
   1763     virtual ~IsElementClickableParser() { }
   1764 
   1765     virtual bool Parse(base::Value* value) const OVERRIDE {
   1766       if (!value->IsType(base::Value::TYPE_DICTIONARY))
   1767         return false;
   1768       base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(value);
   1769       dict->GetString("message", message_);
   1770       return dict->GetBoolean("clickable", clickable_);
   1771     }
   1772 
   1773    private:
   1774     bool* clickable_;
   1775     std::string* message_;
   1776   };
   1777 
   1778   bool clickable;
   1779   std::string message;
   1780   Error* error = ExecuteScriptAndParse(
   1781       frame_id,
   1782       atoms::asString(atoms::IS_ELEMENT_CLICKABLE),
   1783       "isElementClickable",
   1784       CreateListValueFrom(element, location),
   1785       new IsElementClickableParser(&clickable, &message));
   1786   if (error)
   1787     return error;
   1788 
   1789   if (!clickable) {
   1790     if (message.empty())
   1791       message = "element is not clickable";
   1792     return new Error(kUnknownError, message);
   1793   }
   1794   if (message.length()) {
   1795     logger_.Log(kWarningLogLevel, message);
   1796   }
   1797   return NULL;
   1798 }
   1799 
   1800 Error* Session::GetElementRegionInViewHelper(
   1801     const FrameId& frame_id,
   1802     const ElementId& element,
   1803     const Rect& region,
   1804     bool center,
   1805     bool verify_clickable_at_middle,
   1806     Point* location) {
   1807   Point temp_location;
   1808   Error* error = ExecuteScriptAndParse(
   1809       frame_id,
   1810       atoms::asString(atoms::GET_LOCATION_IN_VIEW),
   1811       "getLocationInView",
   1812       CreateListValueFrom(element, center, region),
   1813       CreateDirectValueParser(&temp_location));
   1814 
   1815   if (verify_clickable_at_middle) {
   1816     Point middle_point = temp_location;
   1817     middle_point.Offset(region.width() / 2, region.height() / 2);
   1818     error = VerifyElementIsClickable(frame_id, element, middle_point);
   1819     if (error)
   1820       return error;
   1821   }
   1822   *location = temp_location;
   1823   return NULL;
   1824 }
   1825 
   1826 Error* Session::GetScreenShot(std::string* png) {
   1827   if (!current_target_.view_id.IsTab()) {
   1828     return new Error(kUnknownError,
   1829                      "The current target does not support screenshot");
   1830   }
   1831   Error* error = NULL;
   1832   base::ScopedTempDir screenshots_dir;
   1833   if (!screenshots_dir.CreateUniqueTempDir()) {
   1834     return new Error(kUnknownError,
   1835                      "Could not create temp directory for screenshot");
   1836   }
   1837 
   1838   base::FilePath path = screenshots_dir.path().AppendASCII("screen");
   1839   RunSessionTask(base::Bind(
   1840       &Automation::CaptureEntirePageAsPNG,
   1841       base::Unretained(automation_.get()),
   1842       current_target_.view_id,
   1843       path,
   1844       &error));
   1845   if (error)
   1846     return error;
   1847   if (!file_util::ReadFileToString(path, png))
   1848     return new Error(kUnknownError, "Could not read screenshot file");
   1849   return NULL;
   1850 }
   1851 
   1852 #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
   1853 Error* Session::HeapProfilerDump(const std::string& reason) {
   1854   // TODO(dmikurube): Support browser processes.
   1855   Error* error = NULL;
   1856   RunSessionTask(base::Bind(
   1857       &Automation::HeapProfilerDump,
   1858       base::Unretained(automation_.get()),
   1859       current_target_.view_id,
   1860       reason,
   1861       &error));
   1862   return error;
   1863 }
   1864 #endif  // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
   1865 
   1866 Error* Session::PostBrowserStartInit() {
   1867   Error* error = NULL;
   1868   if (!capabilities_.no_website_testing_defaults)
   1869     error = InitForWebsiteTesting();
   1870   if (!error)
   1871     error = SetPrefs();
   1872   if (error)
   1873     return error;
   1874 
   1875   // Install extensions.
   1876   for (size_t i = 0; i < capabilities_.extensions.size(); ++i) {
   1877     std::string extension_id;
   1878     error = InstallExtension(capabilities_.extensions[i], &extension_id);
   1879     if (error)
   1880       return error;
   1881   }
   1882   return NULL;
   1883 }
   1884 
   1885 Error* Session::InitForWebsiteTesting() {
   1886   bool has_prefs_api = false;
   1887   // Don't set these prefs for Chrome 14 and below.
   1888   // TODO(kkania): Remove this when Chrome 14 is unsupported.
   1889   Error* error = CompareBrowserVersion(874, 0, &has_prefs_api);
   1890   if (error || !has_prefs_api)
   1891     return error;
   1892 
   1893   // Disable checking for SSL certificate revocation.
   1894   error = SetPreference(
   1895       "ssl.rev_checking.enabled",
   1896       false /* is_user_pref */,
   1897       new base::FundamentalValue(false));
   1898   if (error)
   1899     return error;
   1900 
   1901   // Allow content by default.
   1902   // Media-stream cannot be enabled by default; we must specify
   1903   // particular host patterns and devices.
   1904   base::DictionaryValue* devices = new base::DictionaryValue();
   1905   devices->SetString("audio", "Default");
   1906   devices->SetString("video", "Default");
   1907   base::DictionaryValue* content_settings = new base::DictionaryValue();
   1908   content_settings->Set("media-stream", devices);
   1909   base::DictionaryValue* pattern_pairs = new base::DictionaryValue();
   1910   pattern_pairs->Set("https://*,*", content_settings);
   1911   error = SetPreference(
   1912       "profile.content_settings.pattern_pairs",
   1913       true /* is_user_pref */,
   1914       pattern_pairs);
   1915   if (error)
   1916     return error;
   1917   const int kAllowContent = 1;
   1918   base::DictionaryValue* default_content_settings = new base::DictionaryValue();
   1919   default_content_settings->SetInteger("geolocation", kAllowContent);
   1920   default_content_settings->SetInteger("mouselock", kAllowContent);
   1921   default_content_settings->SetInteger("notifications", kAllowContent);
   1922   default_content_settings->SetInteger("popups", kAllowContent);
   1923   return SetPreference(
   1924       "profile.default_content_settings",
   1925       true /* is_user_pref */,
   1926       default_content_settings);
   1927 }
   1928 
   1929 Error* Session::SetPrefs() {
   1930   for (base::DictionaryValue::Iterator iter(*capabilities_.prefs);
   1931        !iter.IsAtEnd(); iter.Advance()) {
   1932     Error* error = SetPreference(iter.key(), true /* is_user_pref */,
   1933                                  iter.value().DeepCopy());
   1934     if (error)
   1935       return error;
   1936   }
   1937   for (base::DictionaryValue::Iterator iter(*capabilities_.local_state);
   1938        !iter.IsAtEnd(); iter.Advance()) {
   1939     Error* error = SetPreference(iter.key(), false /* is_user_pref */,
   1940                                  iter.value().DeepCopy());
   1941     if (error)
   1942       return error;
   1943   }
   1944   return NULL;
   1945 }
   1946 
   1947 }  // namespace webdriver
   1948