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