Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / chrome / test / chromedriver / element_commands.cc
blob0dcf7b3f114068acd81fad326c6c21de204565ab
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/chrome.h"
20 #include "chrome/test/chromedriver/chrome/js.h"
21 #include "chrome/test/chromedriver/chrome/status.h"
22 #include "chrome/test/chromedriver/chrome/ui_events.h"
23 #include "chrome/test/chromedriver/chrome/web_view.h"
24 #include "chrome/test/chromedriver/element_util.h"
25 #include "chrome/test/chromedriver/session.h"
26 #include "chrome/test/chromedriver/util.h"
27 #include "third_party/webdriver/atoms.h"
29 const int kFlickTouchEventsPerSecond = 30;
31 namespace {
33 Status SendKeysToElement(
34 Session* session,
35 WebView* web_view,
36 const std::string& element_id,
37 const base::ListValue* key_list) {
38 bool is_displayed = false;
39 bool is_focused = false;
40 base::TimeTicks start_time = base::TimeTicks::Now();
41 while (true) {
42 Status status = IsElementDisplayed(
43 session, web_view, element_id, true, &is_displayed);
44 if (status.IsError())
45 return status;
46 if (is_displayed)
47 break;
48 status = IsElementFocused(session, web_view, element_id, &is_focused);
49 if (status.IsError())
50 return status;
51 if (is_focused)
52 break;
53 if (base::TimeTicks::Now() - start_time >= session->implicit_wait) {
54 return Status(kElementNotVisible);
56 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
59 bool is_enabled = false;
60 Status status = IsElementEnabled(session, web_view, element_id, &is_enabled);
61 if (status.IsError())
62 return status;
63 if (!is_enabled)
64 return Status(kInvalidElementState);
66 if (!is_focused) {
67 base::ListValue args;
68 args.Append(CreateElement(element_id));
69 scoped_ptr<base::Value> result;
70 status = web_view->CallFunction(
71 session->GetCurrentFrameId(), kFocusScript, args, &result);
72 if (status.IsError())
73 return status;
76 return SendKeysOnWindow(web_view, key_list, true, &session->sticky_modifiers);
79 } // namespace
81 Status ExecuteElementCommand(
82 const ElementCommand& command,
83 Session* session,
84 WebView* web_view,
85 const base::DictionaryValue& params,
86 scoped_ptr<base::Value>* value) {
87 std::string id;
88 if (params.GetString("id", &id) || params.GetString("element", &id))
89 return command.Run(session, web_view, id, params, value);
90 return Status(kUnknownError, "element identifier must be a string");
93 Status ExecuteFindChildElement(
94 int interval_ms,
95 Session* session,
96 WebView* web_view,
97 const std::string& element_id,
98 const base::DictionaryValue& params,
99 scoped_ptr<base::Value>* value) {
100 return FindElement(
101 interval_ms, true, &element_id, session, web_view, params, value);
104 Status ExecuteFindChildElements(
105 int interval_ms,
106 Session* session,
107 WebView* web_view,
108 const std::string& element_id,
109 const base::DictionaryValue& params,
110 scoped_ptr<base::Value>* value) {
111 return FindElement(
112 interval_ms, false, &element_id, session, web_view, params, value);
115 Status ExecuteHoverOverElement(
116 Session* session,
117 WebView* web_view,
118 const std::string& element_id,
119 const base::DictionaryValue& params,
120 scoped_ptr<base::Value>* value) {
121 WebPoint location;
122 Status status = GetElementClickableLocation(
123 session, web_view, element_id, &location);
124 if (status.IsError())
125 return status;
127 MouseEvent move_event(
128 kMovedMouseEventType, kNoneMouseButton, location.x, location.y,
129 session->sticky_modifiers, 0);
130 std::list<MouseEvent> events;
131 events.push_back(move_event);
132 status = web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
133 if (status.IsOk())
134 session->mouse_position = location;
135 return status;
138 Status ExecuteClickElement(
139 Session* session,
140 WebView* web_view,
141 const std::string& element_id,
142 const base::DictionaryValue& params,
143 scoped_ptr<base::Value>* value) {
144 std::string tag_name;
145 Status status = GetElementTagName(session, web_view, element_id, &tag_name);
146 if (status.IsError())
147 return status;
148 if (tag_name == "option") {
149 bool is_toggleable;
150 status = IsOptionElementTogglable(
151 session, web_view, element_id, &is_toggleable);
152 if (status.IsError())
153 return status;
154 if (is_toggleable)
155 return ToggleOptionElement(session, web_view, element_id);
156 else
157 return SetOptionElementSelected(session, web_view, element_id, true);
158 } else {
159 WebPoint location;
160 status = GetElementClickableLocation(
161 session, web_view, element_id, &location);
162 if (status.IsError())
163 return status;
165 std::list<MouseEvent> events;
166 events.push_back(
167 MouseEvent(kMovedMouseEventType, kNoneMouseButton,
168 location.x, location.y, session->sticky_modifiers, 0));
169 events.push_back(
170 MouseEvent(kPressedMouseEventType, kLeftMouseButton,
171 location.x, location.y, session->sticky_modifiers, 1));
172 events.push_back(
173 MouseEvent(kReleasedMouseEventType, kLeftMouseButton,
174 location.x, location.y, session->sticky_modifiers, 1));
175 status =
176 web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
177 if (status.IsOk())
178 session->mouse_position = location;
179 return status;
183 Status ExecuteTouchSingleTap(
184 Session* session,
185 WebView* web_view,
186 const std::string& element_id,
187 const base::DictionaryValue& params,
188 scoped_ptr<base::Value>* value) {
189 WebPoint location;
190 Status status = GetElementClickableLocation(
191 session, web_view, element_id, &location);
192 if (status.IsError())
193 return status;
195 std::list<TouchEvent> events;
196 events.push_back(
197 TouchEvent(kTouchStart, location.x, location.y));
198 events.push_back(
199 TouchEvent(kTouchEnd, location.x, location.y));
200 return web_view->DispatchTouchEvents(events);
203 Status ExecuteFlick(
204 Session* session,
205 WebView* web_view,
206 const std::string& element_id,
207 const base::DictionaryValue& params,
208 scoped_ptr<base::Value>* value) {
209 WebPoint location;
210 Status status = GetElementClickableLocation(
211 session, web_view, element_id, &location);
212 if (status.IsError())
213 return status;
215 int xoffset, yoffset, speed;
216 if (!params.GetInteger("xoffset", &xoffset))
217 return Status(kUnknownError, "'xoffset' must be an integer");
218 if (!params.GetInteger("yoffset", &yoffset))
219 return Status(kUnknownError, "'yoffset' must be an integer");
220 if (!params.GetInteger("speed", &speed))
221 return Status(kUnknownError, "'speed' must be an integer");
222 if (speed < 1)
223 return Status(kUnknownError, "'speed' must be a positive integer");
225 status = web_view->DispatchTouchEvent(
226 TouchEvent(kTouchStart, location.x, location.y));
227 if (status.IsError())
228 return status;
230 const double offset =
231 std::sqrt(static_cast<double>(xoffset * xoffset + yoffset * yoffset));
232 const double xoffset_per_event =
233 (speed * xoffset) / (kFlickTouchEventsPerSecond * offset);
234 const double yoffset_per_event =
235 (speed * yoffset) / (kFlickTouchEventsPerSecond * offset);
236 const int total_events =
237 (offset * kFlickTouchEventsPerSecond) / speed;
238 for (int i = 0; i < total_events; i++) {
239 status = web_view->DispatchTouchEvent(
240 TouchEvent(kTouchMove,
241 location.x + xoffset_per_event * i,
242 location.y + yoffset_per_event * i));
243 if (status.IsError())
244 return status;
245 base::PlatformThread::Sleep(
246 base::TimeDelta::FromMilliseconds(1000 / kFlickTouchEventsPerSecond));
248 return web_view->DispatchTouchEvent(
249 TouchEvent(kTouchEnd, location.x + xoffset, location.y + yoffset));
252 Status ExecuteClearElement(
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 scoped_ptr<base::Value> result;
261 return web_view->CallFunction(
262 session->GetCurrentFrameId(),
263 webdriver::atoms::asString(webdriver::atoms::CLEAR),
264 args, &result);
267 Status ExecuteSendKeysToElement(
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 const base::ListValue* key_list;
274 if (!params.GetList("value", &key_list))
275 return Status(kUnknownError, "'value' must be a list");
277 bool is_input = false;
278 Status status = IsElementAttributeEqualToIgnoreCase(
279 session, web_view, element_id, "tagName", "input", &is_input);
280 if (status.IsError())
281 return status;
282 bool is_file = false;
283 status = IsElementAttributeEqualToIgnoreCase(
284 session, web_view, element_id, "type", "file", &is_file);
285 if (status.IsError())
286 return status;
287 if (is_input && is_file) {
288 // Compress array into a single string.
289 base::FilePath::StringType paths_string;
290 for (size_t i = 0; i < key_list->GetSize(); ++i) {
291 base::FilePath::StringType path_part;
292 if (!key_list->GetString(i, &path_part))
293 return Status(kUnknownError, "'value' is invalid");
294 paths_string.append(path_part);
297 // Separate the string into separate paths, delimited by '\n'.
298 std::vector<base::FilePath::StringType> path_strings;
299 base::SplitString(paths_string, '\n', &path_strings);
300 std::vector<base::FilePath> paths;
301 for (size_t i = 0; i < path_strings.size(); ++i)
302 paths.push_back(base::FilePath(path_strings[i]));
304 bool multiple = false;
305 status = IsElementAttributeEqualToIgnoreCase(
306 session, web_view, element_id, "multiple", "true", &multiple);
307 if (status.IsError())
308 return status;
309 if (!multiple && paths.size() > 1)
310 return Status(kUnknownError, "the element can not hold multiple files");
312 scoped_ptr<base::DictionaryValue> element(CreateElement(element_id));
313 return web_view->SetFileInputFiles(
314 session->GetCurrentFrameId(), *element, paths);
315 } else {
316 return SendKeysToElement(session, web_view, element_id, key_list);
320 Status ExecuteSubmitElement(
321 Session* session,
322 WebView* web_view,
323 const std::string& element_id,
324 const base::DictionaryValue& params,
325 scoped_ptr<base::Value>* value) {
326 base::ListValue args;
327 args.Append(CreateElement(element_id));
328 return web_view->CallFunction(
329 session->GetCurrentFrameId(),
330 webdriver::atoms::asString(webdriver::atoms::SUBMIT),
331 args,
332 value);
335 Status ExecuteGetElementText(
336 Session* session,
337 WebView* web_view,
338 const std::string& element_id,
339 const base::DictionaryValue& params,
340 scoped_ptr<base::Value>* value) {
341 base::ListValue args;
342 args.Append(CreateElement(element_id));
343 return web_view->CallFunction(
344 session->GetCurrentFrameId(),
345 webdriver::atoms::asString(webdriver::atoms::GET_TEXT),
346 args,
347 value);
350 Status ExecuteGetElementValue(
351 Session* session,
352 WebView* web_view,
353 const std::string& element_id,
354 const base::DictionaryValue& params,
355 scoped_ptr<base::Value>* value) {
356 base::ListValue args;
357 args.Append(CreateElement(element_id));
358 return web_view->CallFunction(
359 session->GetCurrentFrameId(),
360 "function(elem) { return elem['value'] }",
361 args,
362 value);
365 Status ExecuteGetElementTagName(
366 Session* session,
367 WebView* web_view,
368 const std::string& element_id,
369 const base::DictionaryValue& params,
370 scoped_ptr<base::Value>* value) {
371 base::ListValue args;
372 args.Append(CreateElement(element_id));
373 return web_view->CallFunction(
374 session->GetCurrentFrameId(),
375 "function(elem) { return elem.tagName.toLowerCase() }",
376 args,
377 value);
380 Status ExecuteIsElementSelected(
381 Session* session,
382 WebView* web_view,
383 const std::string& element_id,
384 const base::DictionaryValue& params,
385 scoped_ptr<base::Value>* value) {
386 base::ListValue args;
387 args.Append(CreateElement(element_id));
388 return web_view->CallFunction(
389 session->GetCurrentFrameId(),
390 webdriver::atoms::asString(webdriver::atoms::IS_SELECTED),
391 args,
392 value);
395 Status ExecuteIsElementEnabled(
396 Session* session,
397 WebView* web_view,
398 const std::string& element_id,
399 const base::DictionaryValue& params,
400 scoped_ptr<base::Value>* value) {
401 base::ListValue args;
402 args.Append(CreateElement(element_id));
403 return web_view->CallFunction(
404 session->GetCurrentFrameId(),
405 webdriver::atoms::asString(webdriver::atoms::IS_ENABLED),
406 args,
407 value);
410 Status ExecuteIsElementDisplayed(
411 Session* session,
412 WebView* web_view,
413 const std::string& element_id,
414 const base::DictionaryValue& params,
415 scoped_ptr<base::Value>* value) {
416 base::ListValue args;
417 args.Append(CreateElement(element_id));
418 return web_view->CallFunction(
419 session->GetCurrentFrameId(),
420 webdriver::atoms::asString(webdriver::atoms::IS_DISPLAYED),
421 args,
422 value);
425 Status ExecuteGetElementLocation(
426 Session* session,
427 WebView* web_view,
428 const std::string& element_id,
429 const base::DictionaryValue& params,
430 scoped_ptr<base::Value>* value) {
431 base::ListValue args;
432 args.Append(CreateElement(element_id));
433 return web_view->CallFunction(
434 session->GetCurrentFrameId(),
435 webdriver::atoms::asString(webdriver::atoms::GET_LOCATION),
436 args,
437 value);
440 Status ExecuteGetElementLocationOnceScrolledIntoView(
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 WebPoint offset(0, 0);
447 WebPoint location;
448 Status status = ScrollElementIntoView(
449 session, web_view, element_id, &offset, &location);
450 if (status.IsError())
451 return status;
452 value->reset(CreateValueFrom(location));
453 return Status(kOk);
456 Status ExecuteGetElementSize(
457 Session* session,
458 WebView* web_view,
459 const std::string& element_id,
460 const base::DictionaryValue& params,
461 scoped_ptr<base::Value>* value) {
462 base::ListValue args;
463 args.Append(CreateElement(element_id));
464 return web_view->CallFunction(
465 session->GetCurrentFrameId(),
466 webdriver::atoms::asString(webdriver::atoms::GET_SIZE),
467 args,
468 value);
471 Status ExecuteGetElementAttribute(
472 Session* session,
473 WebView* web_view,
474 const std::string& element_id,
475 const base::DictionaryValue& params,
476 scoped_ptr<base::Value>* value) {
477 std::string name;
478 if (!params.GetString("name", &name))
479 return Status(kUnknownError, "missing 'name'");
480 return GetElementAttribute(session, web_view, element_id, name, value);
483 Status ExecuteGetElementValueOfCSSProperty(
484 Session* session,
485 WebView* web_view,
486 const std::string& element_id,
487 const base::DictionaryValue& params,
488 scoped_ptr<base::Value>* value) {
489 std::string property_name;
490 if (!params.GetString("propertyName", &property_name))
491 return Status(kUnknownError, "missing 'propertyName'");
492 std::string property_value;
493 Status status = GetElementEffectiveStyle(
494 session, web_view, element_id, property_name, &property_value);
495 if (status.IsError())
496 return status;
497 value->reset(new base::StringValue(property_value));
498 return Status(kOk);
501 Status ExecuteElementEquals(
502 Session* session,
503 WebView* web_view,
504 const std::string& element_id,
505 const base::DictionaryValue& params,
506 scoped_ptr<base::Value>* value) {
507 std::string other_element_id;
508 if (!params.GetString("other", &other_element_id))
509 return Status(kUnknownError, "'other' must be a string");
510 value->reset(new base::FundamentalValue(element_id == other_element_id));
511 return Status(kOk);