Home | History | Annotate | Download | only in commands
      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/commands/webelement_commands.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/format_macros.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/strings/string_split.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/values.h"
     15 #include "chrome/test/webdriver/commands/response.h"
     16 #include "chrome/test/webdriver/webdriver_basic_types.h"
     17 #include "chrome/test/webdriver/webdriver_error.h"
     18 #include "chrome/test/webdriver/webdriver_session.h"
     19 #include "chrome/test/webdriver/webdriver_util.h"
     20 #include "third_party/webdriver/atoms.h"
     21 
     22 namespace webdriver {
     23 
     24 ///////////////////// WebElementCommand ////////////////////
     25 
     26 WebElementCommand::WebElementCommand(
     27     const std::vector<std::string>& path_segments,
     28     const base::DictionaryValue* const parameters)
     29     : WebDriverCommand(path_segments, parameters),
     30       path_segments_(path_segments) {}
     31 
     32 WebElementCommand::~WebElementCommand() {}
     33 
     34 bool WebElementCommand::Init(Response* const response) {
     35   if (!WebDriverCommand::Init(response))
     36     return false;
     37 
     38   // There should be at least 5 segments to match
     39   // "/session/$session/element/$id"
     40   if (path_segments_.size() < 5) {
     41     response->SetError(new Error(kBadRequest, "Path segments is less than 5"));
     42     return false;
     43   }
     44 
     45   // We cannot verify the ID is valid until we execute the command and
     46   // inject the ID into the in-page cache.
     47   element = ElementId(path_segments_.at(4));
     48   return true;
     49 }
     50 
     51 ///////////////////// ElementAttributeCommand ////////////////////
     52 
     53 ElementAttributeCommand::ElementAttributeCommand(
     54     const std::vector<std::string>& path_segments,
     55     const base::DictionaryValue* parameters)
     56     : WebElementCommand(path_segments, parameters) {}
     57 
     58 ElementAttributeCommand::~ElementAttributeCommand() {}
     59 
     60 bool ElementAttributeCommand::DoesGet() {
     61   return true;
     62 }
     63 
     64 void ElementAttributeCommand::ExecuteGet(Response* const response) {
     65   // There should be at least 7 segments to match
     66   // "/session/$session/element/$id/attribute/$name"
     67   if (path_segments_.size() < 7) {
     68     response->SetError(new Error(kBadRequest, "Path segments is less than 7"));
     69     return;
     70   }
     71 
     72   const std::string key = path_segments_.at(6);
     73   base::Value* value;
     74   Error* error = session_->GetAttribute(element, key, &value);
     75   if (error) {
     76     response->SetError(error);
     77     return;
     78   }
     79 
     80   response->SetValue(value);
     81 }
     82 
     83 ///////////////////// ElementClearCommand ////////////////////
     84 
     85 ElementClearCommand::ElementClearCommand(
     86     const std::vector<std::string>& path_segments,
     87     const base::DictionaryValue* parameters)
     88     : WebElementCommand(path_segments, parameters) {}
     89 
     90 ElementClearCommand::~ElementClearCommand() {}
     91 
     92 bool ElementClearCommand::DoesPost() {
     93   return true;
     94 }
     95 
     96 void ElementClearCommand::ExecutePost(Response* const response) {
     97   base::ListValue args;
     98   args.Append(element.ToValue());
     99 
    100   std::string script = base::StringPrintf(
    101       "(%s).apply(null, arguments);", atoms::asString(atoms::CLEAR).c_str());
    102 
    103   base::Value* result = NULL;
    104   Error* error = session_->ExecuteScript(script, &args, &result);
    105   if (error) {
    106     response->SetError(error);
    107     return;
    108   }
    109   response->SetValue(result);
    110 }
    111 
    112 ///////////////////// ElementCssCommand ////////////////////
    113 
    114 ElementCssCommand::ElementCssCommand(
    115     const std::vector<std::string>& path_segments,
    116     const base::DictionaryValue* parameters)
    117     : WebElementCommand(path_segments, parameters) {}
    118 
    119 ElementCssCommand::~ElementCssCommand() {}
    120 
    121 bool ElementCssCommand::DoesGet() {
    122   return true;
    123 }
    124 
    125 void ElementCssCommand::ExecuteGet(Response* const response) {
    126   // There should be at least 7 segments to match
    127   // "/session/$session/element/$id/css/$propertyName"
    128   if (path_segments_.size() < 7) {
    129     response->SetError(new Error(kBadRequest, "Path segments is less than 7"));
    130     return;
    131   }
    132 
    133   std::string script = base::StringPrintf(
    134       "return (%s).apply(null, arguments);",
    135       atoms::asString(atoms::GET_EFFECTIVE_STYLE).c_str());
    136 
    137   base::ListValue args;
    138   args.Append(element.ToValue());
    139   args.Append(new base::StringValue(path_segments_.at(6)));
    140 
    141   base::Value* result = NULL;
    142   Error* error = session_->ExecuteScript(script, &args, &result);
    143   if (error) {
    144     response->SetError(error);
    145     return;
    146   }
    147   response->SetValue(result);
    148 }
    149 
    150 ///////////////////// ElementDisplayedCommand ////////////////////
    151 
    152 ElementDisplayedCommand::ElementDisplayedCommand(
    153     const std::vector<std::string>& path_segments,
    154     const base::DictionaryValue* parameters)
    155     : WebElementCommand(path_segments, parameters) {}
    156 
    157 ElementDisplayedCommand::~ElementDisplayedCommand() {}
    158 
    159 bool ElementDisplayedCommand::DoesGet() {
    160   return true;
    161 }
    162 
    163 void ElementDisplayedCommand::ExecuteGet(Response* const response) {
    164   bool is_displayed;
    165   Error* error = session_->IsElementDisplayed(
    166       session_->current_target(), element, false /* ignore_opacity */,
    167       &is_displayed);
    168   if (error) {
    169     response->SetError(error);
    170     return;
    171   }
    172   response->SetValue(new base::FundamentalValue(is_displayed));
    173 }
    174 
    175 ///////////////////// ElementEnabledCommand ////////////////////
    176 
    177 ElementEnabledCommand::ElementEnabledCommand(
    178     const std::vector<std::string>& path_segments,
    179     const base::DictionaryValue* parameters)
    180     : WebElementCommand(path_segments, parameters) {}
    181 
    182 ElementEnabledCommand::~ElementEnabledCommand() {}
    183 
    184 bool ElementEnabledCommand::DoesGet() {
    185   return true;
    186 }
    187 
    188 void ElementEnabledCommand::ExecuteGet(Response* const response) {
    189   base::ListValue args;
    190   args.Append(element.ToValue());
    191 
    192   std::string script = base::StringPrintf(
    193       "return (%s).apply(null, arguments);",
    194       atoms::asString(atoms::IS_ENABLED).c_str());
    195 
    196   base::Value* result = NULL;
    197   Error* error = session_->ExecuteScript(script, &args, &result);
    198   if (error) {
    199     response->SetError(error);
    200     return;
    201   }
    202   response->SetValue(result);
    203 }
    204 
    205 ///////////////////// ElementEqualsCommand ////////////////////
    206 
    207 ElementEqualsCommand::ElementEqualsCommand(
    208     const std::vector<std::string>& path_segments,
    209     const base::DictionaryValue* parameters)
    210     : WebElementCommand(path_segments, parameters) {}
    211 
    212 ElementEqualsCommand::~ElementEqualsCommand() {}
    213 
    214 bool ElementEqualsCommand::DoesGet() {
    215   return true;
    216 }
    217 
    218 void ElementEqualsCommand::ExecuteGet(Response* const response) {
    219   // There should be at least 7 segments to match
    220   // "/session/$session/element/$id/equals/$other"
    221   if (path_segments_.size() < 7) {
    222     response->SetError(new Error(kBadRequest, "Path segments is less than 7"));
    223     return;
    224   }
    225 
    226   std::string script = "return arguments[0] == arguments[1];";
    227 
    228   base::ListValue args;
    229   args.Append(element.ToValue());
    230 
    231   ElementId other_element(path_segments_.at(6));
    232   args.Append(other_element.ToValue());
    233 
    234   base::Value* result = NULL;
    235   Error* error = session_->ExecuteScript(script, &args, &result);
    236   if (error) {
    237     response->SetError(error);
    238     return;
    239   }
    240   response->SetValue(result);
    241 }
    242 
    243 ///////////////////// ElementLocationCommand ////////////////////
    244 
    245 ElementLocationCommand::ElementLocationCommand(
    246     const std::vector<std::string>& path_segments,
    247     const base::DictionaryValue* parameters)
    248     : WebElementCommand(path_segments, parameters) {}
    249 
    250 ElementLocationCommand::~ElementLocationCommand() {}
    251 
    252 bool ElementLocationCommand::DoesGet() {
    253   return true;
    254 }
    255 
    256 void ElementLocationCommand::ExecuteGet(Response* const response) {
    257   std::string script = base::StringPrintf(
    258       "return (%s).apply(null, arguments);",
    259       atoms::asString(atoms::GET_LOCATION).c_str());
    260 
    261   base::ListValue args;
    262   args.Append(element.ToValue());
    263 
    264   base::Value* result = NULL;
    265   Error* error = session_->ExecuteScript(script, &args, &result);
    266   if (error) {
    267     response->SetError(error);
    268     return;
    269   }
    270   response->SetValue(result);
    271 }
    272 
    273 ///////////////////// ElementLocationInViewCommand ////////////////////
    274 
    275 ElementLocationInViewCommand::ElementLocationInViewCommand(
    276     const std::vector<std::string>& path_segments,
    277     const base::DictionaryValue* parameters)
    278     : WebElementCommand(path_segments, parameters) {}
    279 
    280 ElementLocationInViewCommand::~ElementLocationInViewCommand() {}
    281 
    282 bool ElementLocationInViewCommand::DoesGet() {
    283   return true;
    284 }
    285 
    286 void ElementLocationInViewCommand::ExecuteGet(Response* const response) {
    287   Point location;
    288   Error* error = session_->GetElementLocationInView(element, &location);
    289   if (error) {
    290     response->SetError(error);
    291     return;
    292   }
    293   base::DictionaryValue* coord_dict = new base::DictionaryValue();
    294   coord_dict->SetInteger("x", location.x());
    295   coord_dict->SetInteger("y", location.y());
    296   response->SetValue(coord_dict);
    297 }
    298 
    299 ///////////////////// ElementNameCommand ////////////////////
    300 
    301 ElementNameCommand::ElementNameCommand(
    302     const std::vector<std::string>& path_segments,
    303     const base::DictionaryValue* parameters)
    304     : WebElementCommand(path_segments, parameters) {}
    305 
    306 ElementNameCommand::~ElementNameCommand() {}
    307 
    308 bool ElementNameCommand::DoesGet() {
    309   return true;
    310 }
    311 
    312 void ElementNameCommand::ExecuteGet(Response* const response) {
    313   std::string tag_name;
    314   Error* error = session_->GetElementTagName(
    315       session_->current_target(), element, &tag_name);
    316   if (error) {
    317     response->SetError(error);
    318     return;
    319   }
    320   response->SetValue(new base::StringValue(tag_name));
    321 }
    322 
    323 ///////////////////// ElementSelectedCommand ////////////////////
    324 
    325 ElementSelectedCommand::ElementSelectedCommand(
    326     const std::vector<std::string>& path_segments,
    327     const base::DictionaryValue* parameters)
    328     : WebElementCommand(path_segments, parameters) {}
    329 
    330 ElementSelectedCommand::~ElementSelectedCommand() {}
    331 
    332 bool ElementSelectedCommand::DoesGet() {
    333   return true;
    334 }
    335 
    336 bool ElementSelectedCommand::DoesPost() {
    337   return true;
    338 }
    339 
    340 void ElementSelectedCommand::ExecuteGet(Response* const response) {
    341   bool is_selected;
    342   Error* error = session_->IsOptionElementSelected(
    343       session_->current_target(), element, &is_selected);
    344   if (error) {
    345     response->SetError(error);
    346     return;
    347   }
    348   response->SetValue(new base::FundamentalValue(is_selected));
    349 }
    350 
    351 void ElementSelectedCommand::ExecutePost(Response* const response) {
    352   Error* error = session_->SetOptionElementSelected(
    353       session_->current_target(), element, true);
    354   if (error) {
    355     response->SetError(error);
    356     return;
    357   }
    358 }
    359 
    360 ///////////////////// ElementSizeCommand ////////////////////
    361 
    362 ElementSizeCommand::ElementSizeCommand(
    363     const std::vector<std::string>& path_segments,
    364     const base::DictionaryValue* parameters)
    365     : WebElementCommand(path_segments, parameters) {}
    366 
    367 ElementSizeCommand::~ElementSizeCommand() {}
    368 
    369 bool ElementSizeCommand::DoesGet() {
    370   return true;
    371 }
    372 
    373 void ElementSizeCommand::ExecuteGet(Response* const response) {
    374   Size size;
    375   Error* error = session_->GetElementSize(
    376       session_->current_target(), element, &size);
    377   if (error) {
    378     response->SetError(error);
    379     return;
    380   }
    381   base::DictionaryValue* dict = new base::DictionaryValue();
    382   dict->SetInteger("width", size.width());
    383   dict->SetInteger("height", size.height());
    384   response->SetValue(dict);
    385 }
    386 
    387 ///////////////////// ElementSubmitCommand ////////////////////
    388 
    389 ElementSubmitCommand::ElementSubmitCommand(
    390     const std::vector<std::string>& path_segments,
    391     const base::DictionaryValue* parameters)
    392     : WebElementCommand(path_segments, parameters) {}
    393 
    394 ElementSubmitCommand::~ElementSubmitCommand() {}
    395 
    396 bool ElementSubmitCommand::DoesPost() {
    397   return true;
    398 }
    399 
    400 void ElementSubmitCommand::ExecutePost(Response* const response) {
    401   std::string script = base::StringPrintf(
    402       "(%s).apply(null, arguments);", atoms::asString(atoms::SUBMIT).c_str());
    403 
    404   base::ListValue args;
    405   args.Append(element.ToValue());
    406 
    407   base::Value* result = NULL;
    408   Error* error = session_->ExecuteScript(script, &args, &result);
    409   if (error) {
    410     response->SetError(error);
    411     return;
    412   }
    413   response->SetValue(result);
    414 }
    415 
    416 ///////////////////// ElementToggleCommand ////////////////////
    417 
    418 ElementToggleCommand::ElementToggleCommand(
    419     const std::vector<std::string>& path_segments,
    420     const base::DictionaryValue* parameters)
    421     : WebElementCommand(path_segments, parameters) {}
    422 
    423 ElementToggleCommand::~ElementToggleCommand() {}
    424 
    425 bool ElementToggleCommand::DoesPost() {
    426   return true;
    427 }
    428 
    429 void ElementToggleCommand::ExecutePost(Response* const response) {
    430   std::string script = base::StringPrintf(
    431       "return (%s).apply(null, arguments);",
    432       atoms::asString(atoms::CLICK).c_str());
    433 
    434   base::ListValue args;
    435   args.Append(element.ToValue());
    436 
    437   base::Value* result = NULL;
    438   Error* error = session_->ExecuteScript(script, &args, &result);
    439   if (error) {
    440     response->SetError(error);
    441     return;
    442   }
    443   response->SetValue(result);
    444 }
    445 
    446 ///////////////////// ElementValueCommand ////////////////////
    447 
    448 ElementValueCommand::ElementValueCommand(
    449     const std::vector<std::string>& path_segments,
    450     const base::DictionaryValue* parameters)
    451     : WebElementCommand(path_segments, parameters) {}
    452 
    453 ElementValueCommand::~ElementValueCommand() {}
    454 
    455 bool ElementValueCommand::DoesGet() {
    456   return true;
    457 }
    458 
    459 bool ElementValueCommand::DoesPost() {
    460   return true;
    461 }
    462 
    463 void ElementValueCommand::ExecuteGet(Response* const response) {
    464   base::Value* unscoped_result = NULL;
    465   base::ListValue args;
    466   std::string script = "return arguments[0]['value']";
    467   args.Append(element.ToValue());
    468 
    469   Error* error = session_->ExecuteScript(script, &args, &unscoped_result);
    470   scoped_ptr<base::Value> result(unscoped_result);
    471   if (error) {
    472     response->SetError(error);
    473     return;
    474   }
    475   if (!result->IsType(base::Value::TYPE_STRING) &&
    476       !result->IsType(base::Value::TYPE_NULL)) {
    477     response->SetError(new Error(
    478         kUnknownError, "Result is not string or null type"));
    479     return;
    480   }
    481   response->SetValue(result.release());
    482 }
    483 
    484 void ElementValueCommand::ExecutePost(Response* const response) {
    485   bool is_input = false;
    486   Error* error = HasAttributeWithLowerCaseValueASCII("tagName", "input",
    487                                                      &is_input);
    488   if (error) {
    489     response->SetError(error);
    490     return;
    491   }
    492 
    493   bool is_file = false;
    494   error = HasAttributeWithLowerCaseValueASCII("type", "file", &is_file);
    495   if (error) {
    496     response->SetError(error);
    497     return;
    498   }
    499 
    500   // If the element is a file upload control, set the file paths to the element.
    501   // Otherwise send the value to the element as key input.
    502   if (is_input && is_file) {
    503     error = DragAndDropFilePaths();
    504   } else {
    505     error = SendKeys();
    506   }
    507 
    508   if (error) {
    509     response->SetError(error);
    510     return;
    511   }
    512 }
    513 
    514 Error* ElementValueCommand::HasAttributeWithLowerCaseValueASCII(
    515     const std::string& key, const std::string& value, bool* result) const {
    516   base::Value* unscoped_value = NULL;
    517   Error* error = session_->GetAttribute(element, key, &unscoped_value);
    518   scoped_ptr<base::Value> scoped_value(unscoped_value);
    519   if (error)
    520     return error;
    521 
    522   std::string actual_value;
    523   if (scoped_value->GetAsString(&actual_value)) {
    524     *result = LowerCaseEqualsASCII(actual_value, value.c_str());
    525   } else {
    526     // Note we do not handle converting a number to a string.
    527     *result = false;
    528   }
    529   return NULL;
    530 }
    531 
    532 Error* ElementValueCommand::DragAndDropFilePaths() const {
    533   const base::ListValue* path_list;
    534   if (!GetListParameter("value", &path_list))
    535     return new Error(kBadRequest, "Missing or invalid 'value' parameter");
    536 
    537   // Compress array into single string.
    538   base::FilePath::StringType paths_string;
    539   for (size_t i = 0; i < path_list->GetSize(); ++i) {
    540     base::FilePath::StringType path_part;
    541     if (!path_list->GetString(i, &path_part)) {
    542       return new Error(
    543           kBadRequest,
    544           "'value' is invalid: " + JsonStringify(path_list));
    545     }
    546     paths_string.append(path_part);
    547   }
    548 
    549   // Separate the string into separate paths, delimited by \n.
    550   std::vector<base::FilePath::StringType> paths;
    551   base::SplitString(paths_string, '\n', &paths);
    552 
    553   // Return an error if trying to drop multiple paths on a single file input.
    554   bool multiple = false;
    555   Error* error = HasAttributeWithLowerCaseValueASCII("multiple", "true",
    556                                                      &multiple);
    557   if (error)
    558     return error;
    559   if (!multiple && paths.size() > 1)
    560     return new Error(kBadRequest, "The element can not hold multiple files");
    561 
    562   // Check the files exist.
    563   for (size_t i = 0; i < paths.size(); ++i) {
    564     if (!base::PathExists(base::FilePath(paths[i]))) {
    565       return new Error(
    566           kBadRequest,
    567           base::StringPrintf("'%s' does not exist on the file system",
    568               UTF16ToUTF8(
    569                   base::FilePath(paths[i]).LossyDisplayName()).c_str()));
    570     }
    571   }
    572 
    573   Point location;
    574   error = session_->GetClickableLocation(element, &location);
    575   if (error)
    576     return error;
    577 
    578   return session_->DragAndDropFilePaths(location, paths);
    579 }
    580 
    581 Error* ElementValueCommand::SendKeys() const {
    582   const base::ListValue* key_list;
    583   if (!GetListParameter("value", &key_list)) {
    584     return new Error(kBadRequest, "Missing or invalid 'value' parameter");
    585   }
    586 
    587   // Flatten the given array of strings into one.
    588   string16 keys;
    589   Error* error = FlattenStringArray(key_list, &keys);
    590   if (error)
    591     return error;
    592 
    593   return session_->SendKeys(element, keys);
    594 }
    595 
    596 ///////////////////// ElementTextCommand ////////////////////
    597 
    598 ElementTextCommand::ElementTextCommand(
    599     const std::vector<std::string>& path_segments,
    600     const base::DictionaryValue* parameters)
    601     : WebElementCommand(path_segments, parameters) {}
    602 
    603 ElementTextCommand::~ElementTextCommand() {}
    604 
    605 bool ElementTextCommand::DoesGet() {
    606   return true;
    607 }
    608 
    609 void ElementTextCommand::ExecuteGet(Response* const response) {
    610   base::Value* unscoped_result = NULL;
    611   base::ListValue args;
    612   args.Append(element.ToValue());
    613 
    614   std::string script = base::StringPrintf(
    615       "return (%s).apply(null, arguments);",
    616       atoms::asString(atoms::GET_TEXT).c_str());
    617 
    618   Error* error = session_->ExecuteScript(script, &args,
    619                                          &unscoped_result);
    620   scoped_ptr<base::Value> result(unscoped_result);
    621   if (error) {
    622     response->SetError(error);
    623     return;
    624   }
    625   if (!result->IsType(base::Value::TYPE_STRING)) {
    626     response->SetError(new Error(kUnknownError, "Result is not string type"));
    627     return;
    628   }
    629   response->SetValue(result.release());
    630 }
    631 
    632 }  // namespace webdriver
    633