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/element_commands.h"
      6 
      7 #include <list>
      8 #include <vector>
      9 
     10 #include "base/callback.h"
     11 #include "base/files/file_path.h"
     12 #include "base/strings/string_split.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/threading/platform_thread.h"
     15 #include "base/time/time.h"
     16 #include "base/values.h"
     17 #include "chrome/test/chromedriver/basic_types.h"
     18 #include "chrome/test/chromedriver/chrome/chrome.h"
     19 #include "chrome/test/chromedriver/chrome/js.h"
     20 #include "chrome/test/chromedriver/chrome/status.h"
     21 #include "chrome/test/chromedriver/chrome/ui_events.h"
     22 #include "chrome/test/chromedriver/chrome/web_view.h"
     23 #include "chrome/test/chromedriver/element_util.h"
     24 #include "chrome/test/chromedriver/session.h"
     25 #include "chrome/test/chromedriver/util.h"
     26 #include "third_party/webdriver/atoms.h"
     27 
     28 namespace {
     29 
     30 Status SendKeysToElement(
     31     Session* session,
     32     WebView* web_view,
     33     const std::string& element_id,
     34     const ListValue* key_list) {
     35   bool is_displayed = false;
     36   bool is_focused = false;
     37   base::TimeTicks start_time = base::TimeTicks::Now();
     38   while (true) {
     39     Status status = IsElementDisplayed(
     40         session, web_view, element_id, true, &is_displayed);
     41     if (status.IsError())
     42       return status;
     43     if (is_displayed)
     44       break;
     45     status = IsElementFocused(session, web_view, element_id, &is_focused);
     46     if (status.IsError())
     47       return status;
     48     if (is_focused)
     49       break;
     50     if (base::TimeTicks::Now() - start_time >= session->implicit_wait) {
     51       return Status(kElementNotVisible);
     52     }
     53     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
     54   }
     55 
     56   bool is_enabled = false;
     57   Status status = IsElementEnabled(session, web_view, element_id, &is_enabled);
     58   if (status.IsError())
     59     return status;
     60   if (!is_enabled)
     61     return Status(kInvalidElementState);
     62 
     63   if (!is_focused) {
     64     base::ListValue args;
     65     args.Append(CreateElement(element_id));
     66     scoped_ptr<base::Value> result;
     67     status = web_view->CallFunction(
     68         session->GetCurrentFrameId(), kFocusScript, args, &result);
     69     if (status.IsError())
     70       return status;
     71   }
     72 
     73   return SendKeysOnWindow(web_view, key_list, true, &session->sticky_modifiers);
     74 }
     75 
     76 Status ExecuteTouchSingleTapAtom(
     77     Session* session,
     78     WebView* web_view,
     79     const std::string& element_id,
     80     const base::DictionaryValue& params,
     81     scoped_ptr<base::Value>* value) {
     82   base::ListValue args;
     83   args.Append(CreateElement(element_id));
     84   return web_view->CallFunction(
     85       session->GetCurrentFrameId(),
     86       webdriver::atoms::asString(webdriver::atoms::TOUCH_SINGLE_TAP),
     87       args,
     88       value);
     89 }
     90 
     91 }  // namespace
     92 
     93 Status ExecuteElementCommand(
     94     const ElementCommand& command,
     95     Session* session,
     96     WebView* web_view,
     97     const base::DictionaryValue& params,
     98     scoped_ptr<base::Value>* value) {
     99   std::string id;
    100   if (params.GetString("id", &id) || params.GetString("element", &id))
    101     return command.Run(session, web_view, id, params, value);
    102   return Status(kUnknownError, "element identifier must be a string");
    103 }
    104 
    105 Status ExecuteFindChildElement(
    106     int interval_ms,
    107     Session* session,
    108     WebView* web_view,
    109     const std::string& element_id,
    110     const base::DictionaryValue& params,
    111     scoped_ptr<base::Value>* value) {
    112   return FindElement(
    113       interval_ms, true, &element_id, session, web_view, params, value);
    114 }
    115 
    116 Status ExecuteFindChildElements(
    117     int interval_ms,
    118     Session* session,
    119     WebView* web_view,
    120     const std::string& element_id,
    121     const base::DictionaryValue& params,
    122     scoped_ptr<base::Value>* value) {
    123   return FindElement(
    124       interval_ms, false, &element_id, session, web_view, params, value);
    125 }
    126 
    127 Status ExecuteHoverOverElement(
    128     Session* session,
    129     WebView* web_view,
    130     const std::string& element_id,
    131     const base::DictionaryValue& params,
    132     scoped_ptr<base::Value>* value) {
    133   WebPoint location;
    134   Status status = GetElementClickableLocation(
    135       session, web_view, element_id, &location);
    136   if (status.IsError())
    137     return status;
    138 
    139   MouseEvent move_event(
    140       kMovedMouseEventType, kNoneMouseButton, location.x, location.y,
    141       session->sticky_modifiers, 0);
    142   std::list<MouseEvent> events;
    143   events.push_back(move_event);
    144   status = web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    145   if (status.IsOk())
    146     session->mouse_position = location;
    147   return status;
    148 }
    149 
    150 Status ExecuteClickElement(
    151     Session* session,
    152     WebView* web_view,
    153     const std::string& element_id,
    154     const base::DictionaryValue& params,
    155     scoped_ptr<base::Value>* value) {
    156   std::string tag_name;
    157   Status status = GetElementTagName(session, web_view, element_id, &tag_name);
    158   if (status.IsError())
    159     return status;
    160   if (tag_name == "option") {
    161     bool is_toggleable;
    162     status = IsOptionElementTogglable(
    163         session, web_view, element_id, &is_toggleable);
    164     if (status.IsError())
    165       return status;
    166     if (is_toggleable)
    167       return ToggleOptionElement(session, web_view, element_id);
    168     else
    169       return SetOptionElementSelected(session, web_view, element_id, true);
    170   } else {
    171     WebPoint location;
    172     status = GetElementClickableLocation(
    173         session, web_view, element_id, &location);
    174     if (status.IsError())
    175       return status;
    176 
    177     std::list<MouseEvent> events;
    178     events.push_back(
    179         MouseEvent(kMovedMouseEventType, kNoneMouseButton,
    180                    location.x, location.y, session->sticky_modifiers, 0));
    181     events.push_back(
    182         MouseEvent(kPressedMouseEventType, kLeftMouseButton,
    183                    location.x, location.y, session->sticky_modifiers, 1));
    184     events.push_back(
    185         MouseEvent(kReleasedMouseEventType, kLeftMouseButton,
    186                    location.x, location.y, session->sticky_modifiers, 1));
    187     status =
    188         web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
    189     if (status.IsOk())
    190       session->mouse_position = location;
    191     return status;
    192   }
    193 }
    194 
    195 Status ExecuteTouchSingleTap(
    196     Session* session,
    197     WebView* web_view,
    198     const std::string& element_id,
    199     const base::DictionaryValue& params,
    200     scoped_ptr<base::Value>* value) {
    201   // Fall back to javascript atom for pre-m30 Chrome.
    202   if (session->chrome->GetBuildNo() < 1576)
    203     return ExecuteTouchSingleTapAtom(
    204         session, web_view, element_id, params, value);
    205 
    206   WebPoint location;
    207   Status status = GetElementClickableLocation(
    208       session, web_view, element_id, &location);
    209   if (status.IsError())
    210     return status;
    211 
    212   std::list<TouchEvent> events;
    213   events.push_back(
    214       TouchEvent(kTouchStart, location.x, location.y));
    215   events.push_back(
    216       TouchEvent(kTouchEnd, location.x, location.y));
    217   return web_view->DispatchTouchEvents(events);
    218 }
    219 
    220 Status ExecuteClearElement(
    221     Session* session,
    222     WebView* web_view,
    223     const std::string& element_id,
    224     const base::DictionaryValue& params,
    225     scoped_ptr<base::Value>* value) {
    226   base::ListValue args;
    227   args.Append(CreateElement(element_id));
    228   scoped_ptr<base::Value> result;
    229   return web_view->CallFunction(
    230       session->GetCurrentFrameId(),
    231       webdriver::atoms::asString(webdriver::atoms::CLEAR),
    232       args, &result);
    233 }
    234 
    235 Status ExecuteSendKeysToElement(
    236     Session* session,
    237     WebView* web_view,
    238     const std::string& element_id,
    239     const base::DictionaryValue& params,
    240     scoped_ptr<base::Value>* value) {
    241   const base::ListValue* key_list;
    242   if (!params.GetList("value", &key_list))
    243     return Status(kUnknownError, "'value' must be a list");
    244 
    245   bool is_input = false;
    246   Status status = IsElementAttributeEqualToIgnoreCase(
    247       session, web_view, element_id, "tagName", "input", &is_input);
    248   if (status.IsError())
    249     return status;
    250   bool is_file = false;
    251   status = IsElementAttributeEqualToIgnoreCase(
    252       session, web_view, element_id, "type", "file", &is_file);
    253   if (status.IsError())
    254     return status;
    255   if (is_input && is_file) {
    256     // Compress array into a single string.
    257     base::FilePath::StringType paths_string;
    258     for (size_t i = 0; i < key_list->GetSize(); ++i) {
    259       base::FilePath::StringType path_part;
    260       if (!key_list->GetString(i, &path_part))
    261         return Status(kUnknownError, "'value' is invalid");
    262       paths_string.append(path_part);
    263     }
    264 
    265     // Separate the string into separate paths, delimited by '\n'.
    266     std::vector<base::FilePath::StringType> path_strings;
    267     base::SplitString(paths_string, '\n', &path_strings);
    268     std::vector<base::FilePath> paths;
    269     for (size_t i = 0; i < path_strings.size(); ++i)
    270       paths.push_back(base::FilePath(path_strings[i]));
    271 
    272     bool multiple = false;
    273     status = IsElementAttributeEqualToIgnoreCase(
    274         session, web_view, element_id, "multiple", "true", &multiple);
    275     if (status.IsError())
    276       return status;
    277     if (!multiple && paths.size() > 1)
    278       return Status(kUnknownError, "the element can not hold multiple files");
    279 
    280     scoped_ptr<base::DictionaryValue> element(CreateElement(element_id));
    281     return web_view->SetFileInputFiles(
    282         session->GetCurrentFrameId(), *element, paths);
    283   } else {
    284     return SendKeysToElement(session, web_view, element_id, key_list);
    285   }
    286 }
    287 
    288 Status ExecuteSubmitElement(
    289     Session* session,
    290     WebView* web_view,
    291     const std::string& element_id,
    292     const base::DictionaryValue& params,
    293     scoped_ptr<base::Value>* value) {
    294   base::ListValue args;
    295   args.Append(CreateElement(element_id));
    296   return web_view->CallFunction(
    297       session->GetCurrentFrameId(),
    298       webdriver::atoms::asString(webdriver::atoms::SUBMIT),
    299       args,
    300       value);
    301 }
    302 
    303 Status ExecuteGetElementText(
    304     Session* session,
    305     WebView* web_view,
    306     const std::string& element_id,
    307     const base::DictionaryValue& params,
    308     scoped_ptr<base::Value>* value) {
    309   base::ListValue args;
    310   args.Append(CreateElement(element_id));
    311   return web_view->CallFunction(
    312       session->GetCurrentFrameId(),
    313       webdriver::atoms::asString(webdriver::atoms::GET_TEXT),
    314       args,
    315       value);
    316 }
    317 
    318 Status ExecuteGetElementValue(
    319     Session* session,
    320     WebView* web_view,
    321     const std::string& element_id,
    322     const base::DictionaryValue& params,
    323     scoped_ptr<base::Value>* value) {
    324   base::ListValue args;
    325   args.Append(CreateElement(element_id));
    326   return web_view->CallFunction(
    327       session->GetCurrentFrameId(),
    328       "function(elem) { return elem['value'] }",
    329       args,
    330       value);
    331 }
    332 
    333 Status ExecuteGetElementTagName(
    334     Session* session,
    335     WebView* web_view,
    336     const std::string& element_id,
    337     const base::DictionaryValue& params,
    338     scoped_ptr<base::Value>* value) {
    339   base::ListValue args;
    340   args.Append(CreateElement(element_id));
    341   return web_view->CallFunction(
    342       session->GetCurrentFrameId(),
    343       "function(elem) { return elem.tagName.toLowerCase() }",
    344       args,
    345       value);
    346 }
    347 
    348 Status ExecuteIsElementSelected(
    349     Session* session,
    350     WebView* web_view,
    351     const std::string& element_id,
    352     const base::DictionaryValue& params,
    353     scoped_ptr<base::Value>* value) {
    354   base::ListValue args;
    355   args.Append(CreateElement(element_id));
    356   return web_view->CallFunction(
    357       session->GetCurrentFrameId(),
    358       webdriver::atoms::asString(webdriver::atoms::IS_SELECTED),
    359       args,
    360       value);
    361 }
    362 
    363 Status ExecuteIsElementEnabled(
    364     Session* session,
    365     WebView* web_view,
    366     const std::string& element_id,
    367     const base::DictionaryValue& params,
    368     scoped_ptr<base::Value>* value) {
    369   base::ListValue args;
    370   args.Append(CreateElement(element_id));
    371   return web_view->CallFunction(
    372       session->GetCurrentFrameId(),
    373       webdriver::atoms::asString(webdriver::atoms::IS_ENABLED),
    374       args,
    375       value);
    376 }
    377 
    378 Status ExecuteIsElementDisplayed(
    379     Session* session,
    380     WebView* web_view,
    381     const std::string& element_id,
    382     const base::DictionaryValue& params,
    383     scoped_ptr<base::Value>* value) {
    384   base::ListValue args;
    385   args.Append(CreateElement(element_id));
    386   return web_view->CallFunction(
    387       session->GetCurrentFrameId(),
    388       webdriver::atoms::asString(webdriver::atoms::IS_DISPLAYED),
    389       args,
    390       value);
    391 }
    392 
    393 Status ExecuteGetElementLocation(
    394     Session* session,
    395     WebView* web_view,
    396     const std::string& element_id,
    397     const base::DictionaryValue& params,
    398     scoped_ptr<base::Value>* value) {
    399   base::ListValue args;
    400   args.Append(CreateElement(element_id));
    401   return web_view->CallFunction(
    402       session->GetCurrentFrameId(),
    403       webdriver::atoms::asString(webdriver::atoms::GET_LOCATION),
    404       args,
    405       value);
    406 }
    407 
    408 Status ExecuteGetElementLocationOnceScrolledIntoView(
    409     Session* session,
    410     WebView* web_view,
    411     const std::string& element_id,
    412     const base::DictionaryValue& params,
    413     scoped_ptr<base::Value>* value) {
    414   WebPoint location;
    415   Status status = ScrollElementIntoView(
    416       session, web_view, element_id, &location);
    417   if (status.IsError())
    418     return status;
    419   value->reset(CreateValueFrom(location));
    420   return Status(kOk);
    421 }
    422 
    423 Status ExecuteGetElementSize(
    424     Session* session,
    425     WebView* web_view,
    426     const std::string& element_id,
    427     const base::DictionaryValue& params,
    428     scoped_ptr<base::Value>* value) {
    429   base::ListValue args;
    430   args.Append(CreateElement(element_id));
    431   return web_view->CallFunction(
    432       session->GetCurrentFrameId(),
    433       webdriver::atoms::asString(webdriver::atoms::GET_SIZE),
    434       args,
    435       value);
    436 }
    437 
    438 Status ExecuteGetElementAttribute(
    439     Session* session,
    440     WebView* web_view,
    441     const std::string& element_id,
    442     const base::DictionaryValue& params,
    443     scoped_ptr<base::Value>* value) {
    444   std::string name;
    445   if (!params.GetString("name", &name))
    446     return Status(kUnknownError, "missing 'name'");
    447   return GetElementAttribute(session, web_view, element_id, name, value);
    448 }
    449 
    450 Status ExecuteGetElementValueOfCSSProperty(
    451     Session* session,
    452     WebView* web_view,
    453     const std::string& element_id,
    454     const base::DictionaryValue& params,
    455     scoped_ptr<base::Value>* value) {
    456   std::string property_name;
    457   if (!params.GetString("propertyName", &property_name))
    458     return Status(kUnknownError, "missing 'propertyName'");
    459   std::string property_value;
    460   Status status = GetElementEffectiveStyle(
    461       session, web_view, element_id, property_name, &property_value);
    462   if (status.IsError())
    463     return status;
    464   value->reset(new base::StringValue(property_value));
    465   return Status(kOk);
    466 }
    467 
    468 Status ExecuteElementEquals(
    469     Session* session,
    470     WebView* web_view,
    471     const std::string& element_id,
    472     const base::DictionaryValue& params,
    473     scoped_ptr<base::Value>* value) {
    474   std::string other_element_id;
    475   if (!params.GetString("other", &other_element_id))
    476     return Status(kUnknownError, "'other' must be a string");
    477   value->reset(new base::FundamentalValue(element_id == other_element_id));
    478   return Status(kOk);
    479 }
    480