Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / test / chromedriver / element_commands.cc
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 <cmath>
8 #include <list>
9 #include <vector>
10
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/version.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"
29
30 const int kFlickTouchEventsPerSecond = 30;
31
32 namespace {
33
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);
56     }
57     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
58   }
59
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);
66
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;
75   }
76
77   return SendKeysOnWindow(web_view, key_list, true, &session->sticky_modifiers);
78 }
79
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);
93 }
94
95 }  // namespace
96
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");
107 }
108
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);
118 }
119
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);
129 }
130
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;
142
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;
152 }
153
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;
180
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;
196   }
197 }
198
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);
209
210   WebPoint location;
211   Status status = GetElementClickableLocation(
212       session, web_view, element_id, &location);
213   if (status.IsError())
214     return status;
215
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);
222 }
223
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;
235
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");
245
246   status = web_view->DispatchTouchEvent(
247       TouchEvent(kTouchStart, location.x, location.y));
248   if (status.IsError())
249     return status;
250
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));
268   }
269   return web_view->DispatchTouchEvent(
270       TouchEvent(kTouchEnd, location.x + xoffset, location.y + yoffset));
271 }
272
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);
286 }
287
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");
297
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);
316     }
317
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]));
324
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");
332
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);
338   }
339 }
340
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);
354 }
355
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);
369 }
370
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);
384 }
385
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);
399 }
400
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);
414 }
415
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);
429 }
430
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);
444 }
445
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);
459 }
460
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);
474 }
475
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);
489 }
490
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);
501 }
502
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);
519 }
520
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);
532 }