Roll src/third_party/WebKit bf18a82:a9cee16 (svn 185297:185304)
[chromium-blink-merge.git] / chrome / test / chromedriver / element_commands.cc
blob88bacc53dc1a323d2a564f9368751ed3ac765225
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.
5 #include "chrome/test/chromedriver/element_commands.h"
7 #include <cmath>
8 #include <list>
9 #include <vector>
11 #include "base/callback.h"
12 #include "base/files/file_path.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/time/time.h"
17 #include "base/values.h"
18 #include "chrome/test/chromedriver/basic_types.h"
19 #include "chrome/test/chromedriver/chrome/browser_info.h"
20 #include "chrome/test/chromedriver/chrome/chrome.h"
21 #include "chrome/test/chromedriver/chrome/js.h"
22 #include "chrome/test/chromedriver/chrome/status.h"
23 #include "chrome/test/chromedriver/chrome/ui_events.h"
24 #include "chrome/test/chromedriver/chrome/web_view.h"
25 #include "chrome/test/chromedriver/element_util.h"
26 #include "chrome/test/chromedriver/session.h"
27 #include "chrome/test/chromedriver/util.h"
28 #include "third_party/webdriver/atoms.h"
30 const int kFlickTouchEventsPerSecond = 30;
32 namespace {
34 Status SendKeysToElement(
35 Session* session,
36 WebView* web_view,
37 const std::string& element_id,
38 const base::ListValue* key_list) {
39 bool is_displayed = false;
40 bool is_focused = false;
41 base::TimeTicks start_time = base::TimeTicks::Now();
42 while (true) {
43 Status status = IsElementDisplayed(
44 session, web_view, element_id, true, &is_displayed);
45 if (status.IsError())
46 return status;
47 if (is_displayed)
48 break;
49 status = IsElementFocused(session, web_view, element_id, &is_focused);
50 if (status.IsError())
51 return status;
52 if (is_focused)
53 break;
54 if (base::TimeTicks::Now() - start_time >= session->implicit_wait) {
55 return Status(kElementNotVisible);
57 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
60 bool is_enabled = false;
61 Status status = IsElementEnabled(session, web_view, element_id, &is_enabled);
62 if (status.IsError())
63 return status;
64 if (!is_enabled)
65 return Status(kInvalidElementState);
67 if (!is_focused) {
68 base::ListValue args;
69 args.Append(CreateElement(element_id));
70 scoped_ptr<base::Value> result;
71 status = web_view->CallFunction(
72 session->GetCurrentFrameId(), kFocusScript, args, &result);
73 if (status.IsError())
74 return status;
77 return SendKeysOnWindow(web_view, key_list, true, &session->sticky_modifiers);
80 Status ExecuteTouchSingleTapAtom(
81 Session* session,
82 WebView* web_view,
83 const std::string& element_id,
84 const base::DictionaryValue& params,
85 scoped_ptr<base::Value>* value) {
86 base::ListValue args;
87 args.Append(CreateElement(element_id));
88 return web_view->CallFunction(
89 session->GetCurrentFrameId(),
90 webdriver::atoms::asString(webdriver::atoms::TOUCH_SINGLE_TAP),
91 args,
92 value);
95 } // namespace
97 Status ExecuteElementCommand(
98 const ElementCommand& command,
99 Session* session,
100 WebView* web_view,
101 const base::DictionaryValue& params,
102 scoped_ptr<base::Value>* value) {
103 std::string id;
104 if (params.GetString("id", &id) || params.GetString("element", &id))
105 return command.Run(session, web_view, id, params, value);
106 return Status(kUnknownError, "element identifier must be a string");
109 Status ExecuteFindChildElement(
110 int interval_ms,
111 Session* session,
112 WebView* web_view,
113 const std::string& element_id,
114 const base::DictionaryValue& params,
115 scoped_ptr<base::Value>* value) {
116 return FindElement(
117 interval_ms, true, &element_id, session, web_view, params, value);
120 Status ExecuteFindChildElements(
121 int interval_ms,
122 Session* session,
123 WebView* web_view,
124 const std::string& element_id,
125 const base::DictionaryValue& params,
126 scoped_ptr<base::Value>* value) {
127 return FindElement(
128 interval_ms, false, &element_id, session, web_view, params, value);
131 Status ExecuteHoverOverElement(
132 Session* session,
133 WebView* web_view,
134 const std::string& element_id,
135 const base::DictionaryValue& params,
136 scoped_ptr<base::Value>* value) {
137 WebPoint location;
138 Status status = GetElementClickableLocation(
139 session, web_view, element_id, &location);
140 if (status.IsError())
141 return status;
143 MouseEvent move_event(
144 kMovedMouseEventType, kNoneMouseButton, location.x, location.y,
145 session->sticky_modifiers, 0);
146 std::list<MouseEvent> events;
147 events.push_back(move_event);
148 status = web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
149 if (status.IsOk())
150 session->mouse_position = location;
151 return status;
154 Status ExecuteClickElement(
155 Session* session,
156 WebView* web_view,
157 const std::string& element_id,
158 const base::DictionaryValue& params,
159 scoped_ptr<base::Value>* value) {
160 std::string tag_name;
161 Status status = GetElementTagName(session, web_view, element_id, &tag_name);
162 if (status.IsError())
163 return status;
164 if (tag_name == "option") {
165 bool is_toggleable;
166 status = IsOptionElementTogglable(
167 session, web_view, element_id, &is_toggleable);
168 if (status.IsError())
169 return status;
170 if (is_toggleable)
171 return ToggleOptionElement(session, web_view, element_id);
172 else
173 return SetOptionElementSelected(session, web_view, element_id, true);
174 } else {
175 WebPoint location;
176 status = GetElementClickableLocation(
177 session, web_view, element_id, &location);
178 if (status.IsError())
179 return status;
181 std::list<MouseEvent> events;
182 events.push_back(
183 MouseEvent(kMovedMouseEventType, kNoneMouseButton,
184 location.x, location.y, session->sticky_modifiers, 0));
185 events.push_back(
186 MouseEvent(kPressedMouseEventType, kLeftMouseButton,
187 location.x, location.y, session->sticky_modifiers, 1));
188 events.push_back(
189 MouseEvent(kReleasedMouseEventType, kLeftMouseButton,
190 location.x, location.y, session->sticky_modifiers, 1));
191 status =
192 web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
193 if (status.IsOk())
194 session->mouse_position = location;
195 return status;
199 Status ExecuteTouchSingleTap(
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 // Fall back to javascript atom for pre-m30 Chrome.
206 if (session->chrome->GetBrowserInfo()->build_no < 1576)
207 return ExecuteTouchSingleTapAtom(
208 session, web_view, element_id, params, value);
210 WebPoint location;
211 Status status = GetElementClickableLocation(
212 session, web_view, element_id, &location);
213 if (status.IsError())
214 return status;
216 std::list<TouchEvent> events;
217 events.push_back(
218 TouchEvent(kTouchStart, location.x, location.y));
219 events.push_back(
220 TouchEvent(kTouchEnd, location.x, location.y));
221 return web_view->DispatchTouchEvents(events);
224 Status ExecuteFlick(
225 Session* session,
226 WebView* web_view,
227 const std::string& element_id,
228 const base::DictionaryValue& params,
229 scoped_ptr<base::Value>* value) {
230 WebPoint location;
231 Status status = GetElementClickableLocation(
232 session, web_view, element_id, &location);
233 if (status.IsError())
234 return status;
236 int xoffset, yoffset, speed;
237 if (!params.GetInteger("xoffset", &xoffset))
238 return Status(kUnknownError, "'xoffset' must be an integer");
239 if (!params.GetInteger("yoffset", &yoffset))
240 return Status(kUnknownError, "'yoffset' must be an integer");
241 if (!params.GetInteger("speed", &speed))
242 return Status(kUnknownError, "'speed' must be an integer");
243 if (speed < 1)
244 return Status(kUnknownError, "'speed' must be a positive integer");
246 status = web_view->DispatchTouchEvent(
247 TouchEvent(kTouchStart, location.x, location.y));
248 if (status.IsError())
249 return status;
251 const double offset =
252 std::sqrt(static_cast<double>(xoffset * xoffset + yoffset * yoffset));
253 const double xoffset_per_event =
254 (speed * xoffset) / (kFlickTouchEventsPerSecond * offset);
255 const double yoffset_per_event =
256 (speed * yoffset) / (kFlickTouchEventsPerSecond * offset);
257 const int total_events =
258 (offset * kFlickTouchEventsPerSecond) / speed;
259 for (int i = 0; i < total_events; i++) {
260 status = web_view->DispatchTouchEvent(
261 TouchEvent(kTouchMove,
262 location.x + xoffset_per_event * i,
263 location.y + yoffset_per_event * i));
264 if (status.IsError())
265 return status;
266 base::PlatformThread::Sleep(
267 base::TimeDelta::FromMilliseconds(1000 / kFlickTouchEventsPerSecond));
269 return web_view->DispatchTouchEvent(
270 TouchEvent(kTouchEnd, location.x + xoffset, location.y + yoffset));
273 Status ExecuteClearElement(
274 Session* session,
275 WebView* web_view,
276 const std::string& element_id,
277 const base::DictionaryValue& params,
278 scoped_ptr<base::Value>* value) {
279 base::ListValue args;
280 args.Append(CreateElement(element_id));
281 scoped_ptr<base::Value> result;
282 return web_view->CallFunction(
283 session->GetCurrentFrameId(),
284 webdriver::atoms::asString(webdriver::atoms::CLEAR),
285 args, &result);
288 Status ExecuteSendKeysToElement(
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 const base::ListValue* key_list;
295 if (!params.GetList("value", &key_list))
296 return Status(kUnknownError, "'value' must be a list");
298 bool is_input = false;
299 Status status = IsElementAttributeEqualToIgnoreCase(
300 session, web_view, element_id, "tagName", "input", &is_input);
301 if (status.IsError())
302 return status;
303 bool is_file = false;
304 status = IsElementAttributeEqualToIgnoreCase(
305 session, web_view, element_id, "type", "file", &is_file);
306 if (status.IsError())
307 return status;
308 if (is_input && is_file) {
309 // Compress array into a single string.
310 base::FilePath::StringType paths_string;
311 for (size_t i = 0; i < key_list->GetSize(); ++i) {
312 base::FilePath::StringType path_part;
313 if (!key_list->GetString(i, &path_part))
314 return Status(kUnknownError, "'value' is invalid");
315 paths_string.append(path_part);
318 // Separate the string into separate paths, delimited by '\n'.
319 std::vector<base::FilePath::StringType> path_strings;
320 base::SplitString(paths_string, '\n', &path_strings);
321 std::vector<base::FilePath> paths;
322 for (size_t i = 0; i < path_strings.size(); ++i)
323 paths.push_back(base::FilePath(path_strings[i]));
325 bool multiple = false;
326 status = IsElementAttributeEqualToIgnoreCase(
327 session, web_view, element_id, "multiple", "true", &multiple);
328 if (status.IsError())
329 return status;
330 if (!multiple && paths.size() > 1)
331 return Status(kUnknownError, "the element can not hold multiple files");
333 scoped_ptr<base::DictionaryValue> element(CreateElement(element_id));
334 return web_view->SetFileInputFiles(
335 session->GetCurrentFrameId(), *element, paths);
336 } else {
337 return SendKeysToElement(session, web_view, element_id, key_list);
341 Status ExecuteSubmitElement(
342 Session* session,
343 WebView* web_view,
344 const std::string& element_id,
345 const base::DictionaryValue& params,
346 scoped_ptr<base::Value>* value) {
347 base::ListValue args;
348 args.Append(CreateElement(element_id));
349 return web_view->CallFunction(
350 session->GetCurrentFrameId(),
351 webdriver::atoms::asString(webdriver::atoms::SUBMIT),
352 args,
353 value);
356 Status ExecuteGetElementText(
357 Session* session,
358 WebView* web_view,
359 const std::string& element_id,
360 const base::DictionaryValue& params,
361 scoped_ptr<base::Value>* value) {
362 base::ListValue args;
363 args.Append(CreateElement(element_id));
364 return web_view->CallFunction(
365 session->GetCurrentFrameId(),
366 webdriver::atoms::asString(webdriver::atoms::GET_TEXT),
367 args,
368 value);
371 Status ExecuteGetElementValue(
372 Session* session,
373 WebView* web_view,
374 const std::string& element_id,
375 const base::DictionaryValue& params,
376 scoped_ptr<base::Value>* value) {
377 base::ListValue args;
378 args.Append(CreateElement(element_id));
379 return web_view->CallFunction(
380 session->GetCurrentFrameId(),
381 "function(elem) { return elem['value'] }",
382 args,
383 value);
386 Status ExecuteGetElementTagName(
387 Session* session,
388 WebView* web_view,
389 const std::string& element_id,
390 const base::DictionaryValue& params,
391 scoped_ptr<base::Value>* value) {
392 base::ListValue args;
393 args.Append(CreateElement(element_id));
394 return web_view->CallFunction(
395 session->GetCurrentFrameId(),
396 "function(elem) { return elem.tagName.toLowerCase() }",
397 args,
398 value);
401 Status ExecuteIsElementSelected(
402 Session* session,
403 WebView* web_view,
404 const std::string& element_id,
405 const base::DictionaryValue& params,
406 scoped_ptr<base::Value>* value) {
407 base::ListValue args;
408 args.Append(CreateElement(element_id));
409 return web_view->CallFunction(
410 session->GetCurrentFrameId(),
411 webdriver::atoms::asString(webdriver::atoms::IS_SELECTED),
412 args,
413 value);
416 Status ExecuteIsElementEnabled(
417 Session* session,
418 WebView* web_view,
419 const std::string& element_id,
420 const base::DictionaryValue& params,
421 scoped_ptr<base::Value>* value) {
422 base::ListValue args;
423 args.Append(CreateElement(element_id));
424 return web_view->CallFunction(
425 session->GetCurrentFrameId(),
426 webdriver::atoms::asString(webdriver::atoms::IS_ENABLED),
427 args,
428 value);
431 Status ExecuteIsElementDisplayed(
432 Session* session,
433 WebView* web_view,
434 const std::string& element_id,
435 const base::DictionaryValue& params,
436 scoped_ptr<base::Value>* value) {
437 base::ListValue args;
438 args.Append(CreateElement(element_id));
439 return web_view->CallFunction(
440 session->GetCurrentFrameId(),
441 webdriver::atoms::asString(webdriver::atoms::IS_DISPLAYED),
442 args,
443 value);
446 Status ExecuteGetElementLocation(
447 Session* session,
448 WebView* web_view,
449 const std::string& element_id,
450 const base::DictionaryValue& params,
451 scoped_ptr<base::Value>* value) {
452 base::ListValue args;
453 args.Append(CreateElement(element_id));
454 return web_view->CallFunction(
455 session->GetCurrentFrameId(),
456 webdriver::atoms::asString(webdriver::atoms::GET_LOCATION),
457 args,
458 value);
461 Status ExecuteGetElementLocationOnceScrolledIntoView(
462 Session* session,
463 WebView* web_view,
464 const std::string& element_id,
465 const base::DictionaryValue& params,
466 scoped_ptr<base::Value>* value) {
467 WebPoint location;
468 Status status = ScrollElementIntoView(
469 session, web_view, element_id, &location);
470 if (status.IsError())
471 return status;
472 value->reset(CreateValueFrom(location));
473 return Status(kOk);
476 Status ExecuteGetElementSize(
477 Session* session,
478 WebView* web_view,
479 const std::string& element_id,
480 const base::DictionaryValue& params,
481 scoped_ptr<base::Value>* value) {
482 base::ListValue args;
483 args.Append(CreateElement(element_id));
484 return web_view->CallFunction(
485 session->GetCurrentFrameId(),
486 webdriver::atoms::asString(webdriver::atoms::GET_SIZE),
487 args,
488 value);
491 Status ExecuteGetElementAttribute(
492 Session* session,
493 WebView* web_view,
494 const std::string& element_id,
495 const base::DictionaryValue& params,
496 scoped_ptr<base::Value>* value) {
497 std::string name;
498 if (!params.GetString("name", &name))
499 return Status(kUnknownError, "missing 'name'");
500 return GetElementAttribute(session, web_view, element_id, name, value);
503 Status ExecuteGetElementValueOfCSSProperty(
504 Session* session,
505 WebView* web_view,
506 const std::string& element_id,
507 const base::DictionaryValue& params,
508 scoped_ptr<base::Value>* value) {
509 std::string property_name;
510 if (!params.GetString("propertyName", &property_name))
511 return Status(kUnknownError, "missing 'propertyName'");
512 std::string property_value;
513 Status status = GetElementEffectiveStyle(
514 session, web_view, element_id, property_name, &property_value);
515 if (status.IsError())
516 return status;
517 value->reset(new base::StringValue(property_value));
518 return Status(kOk);
521 Status ExecuteElementEquals(
522 Session* session,
523 WebView* web_view,
524 const std::string& element_id,
525 const base::DictionaryValue& params,
526 scoped_ptr<base::Value>* value) {
527 std::string other_element_id;
528 if (!params.GetString("other", &other_element_id))
529 return Status(kUnknownError, "'other' must be a string");
530 value->reset(new base::FundamentalValue(element_id == other_element_id));
531 return Status(kOk);