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