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