Upstream version 5.34.104.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/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"
28
29 const int kFlickTouchEventsPerSecond = 30;
30
31 namespace {
32
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);
55     }
56     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
57   }
58
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);
65
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;
74   }
75
76   return SendKeysOnWindow(web_view, key_list, true, &session->sticky_modifiers);
77 }
78
79 Status ExecuteTouchSingleTapAtom(
80     Session* session,
81     WebView* web_view,
82     const std::string& element_id,
83     const base::DictionaryValue& params,
84     scoped_ptr<base::Value>* value) {
85   base::ListValue args;
86   args.Append(CreateElement(element_id));
87   return web_view->CallFunction(
88       session->GetCurrentFrameId(),
89       webdriver::atoms::asString(webdriver::atoms::TOUCH_SINGLE_TAP),
90       args,
91       value);
92 }
93
94 }  // namespace
95
96 Status ExecuteElementCommand(
97     const ElementCommand& command,
98     Session* session,
99     WebView* web_view,
100     const base::DictionaryValue& params,
101     scoped_ptr<base::Value>* value) {
102   std::string id;
103   if (params.GetString("id", &id) || params.GetString("element", &id))
104     return command.Run(session, web_view, id, params, value);
105   return Status(kUnknownError, "element identifier must be a string");
106 }
107
108 Status ExecuteFindChildElement(
109     int interval_ms,
110     Session* session,
111     WebView* web_view,
112     const std::string& element_id,
113     const base::DictionaryValue& params,
114     scoped_ptr<base::Value>* value) {
115   return FindElement(
116       interval_ms, true, &element_id, session, web_view, params, value);
117 }
118
119 Status ExecuteFindChildElements(
120     int interval_ms,
121     Session* session,
122     WebView* web_view,
123     const std::string& element_id,
124     const base::DictionaryValue& params,
125     scoped_ptr<base::Value>* value) {
126   return FindElement(
127       interval_ms, false, &element_id, session, web_view, params, value);
128 }
129
130 Status ExecuteHoverOverElement(
131     Session* session,
132     WebView* web_view,
133     const std::string& element_id,
134     const base::DictionaryValue& params,
135     scoped_ptr<base::Value>* value) {
136   WebPoint location;
137   Status status = GetElementClickableLocation(
138       session, web_view, element_id, &location);
139   if (status.IsError())
140     return status;
141
142   MouseEvent move_event(
143       kMovedMouseEventType, kNoneMouseButton, location.x, location.y,
144       session->sticky_modifiers, 0);
145   std::list<MouseEvent> events;
146   events.push_back(move_event);
147   status = web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
148   if (status.IsOk())
149     session->mouse_position = location;
150   return status;
151 }
152
153 Status ExecuteClickElement(
154     Session* session,
155     WebView* web_view,
156     const std::string& element_id,
157     const base::DictionaryValue& params,
158     scoped_ptr<base::Value>* value) {
159   std::string tag_name;
160   Status status = GetElementTagName(session, web_view, element_id, &tag_name);
161   if (status.IsError())
162     return status;
163   if (tag_name == "option") {
164     bool is_toggleable;
165     status = IsOptionElementTogglable(
166         session, web_view, element_id, &is_toggleable);
167     if (status.IsError())
168       return status;
169     if (is_toggleable)
170       return ToggleOptionElement(session, web_view, element_id);
171     else
172       return SetOptionElementSelected(session, web_view, element_id, true);
173   } else {
174     WebPoint location;
175     status = GetElementClickableLocation(
176         session, web_view, element_id, &location);
177     if (status.IsError())
178       return status;
179
180     std::list<MouseEvent> events;
181     events.push_back(
182         MouseEvent(kMovedMouseEventType, kNoneMouseButton,
183                    location.x, location.y, session->sticky_modifiers, 0));
184     events.push_back(
185         MouseEvent(kPressedMouseEventType, kLeftMouseButton,
186                    location.x, location.y, session->sticky_modifiers, 1));
187     events.push_back(
188         MouseEvent(kReleasedMouseEventType, kLeftMouseButton,
189                    location.x, location.y, session->sticky_modifiers, 1));
190     status =
191         web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
192     if (status.IsOk())
193       session->mouse_position = location;
194     return status;
195   }
196 }
197
198 Status ExecuteTouchSingleTap(
199     Session* session,
200     WebView* web_view,
201     const std::string& element_id,
202     const base::DictionaryValue& params,
203     scoped_ptr<base::Value>* value) {
204   // Fall back to javascript atom for pre-m30 Chrome.
205   if (session->chrome->GetBuildNo() < 1576)
206     return ExecuteTouchSingleTapAtom(
207         session, web_view, element_id, params, value);
208
209   WebPoint location;
210   Status status = GetElementClickableLocation(
211       session, web_view, element_id, &location);
212   if (status.IsError())
213     return status;
214
215   std::list<TouchEvent> events;
216   events.push_back(
217       TouchEvent(kTouchStart, location.x, location.y));
218   events.push_back(
219       TouchEvent(kTouchEnd, location.x, location.y));
220   return web_view->DispatchTouchEvents(events);
221 }
222
223 Status ExecuteFlick(
224     Session* session,
225     WebView* web_view,
226     const std::string& element_id,
227     const base::DictionaryValue& params,
228     scoped_ptr<base::Value>* value) {
229   WebPoint location;
230   Status status = GetElementClickableLocation(
231       session, web_view, element_id, &location);
232   if (status.IsError())
233     return status;
234
235   int xoffset, yoffset, speed;
236   if (!params.GetInteger("xoffset", &xoffset))
237     return Status(kUnknownError, "'xoffset' must be an integer");
238   if (!params.GetInteger("yoffset", &yoffset))
239     return Status(kUnknownError, "'yoffset' must be an integer");
240   if (!params.GetInteger("speed", &speed))
241     return Status(kUnknownError, "'speed' must be an integer");
242   if (speed < 1)
243     return Status(kUnknownError, "'speed' must be a positive integer");
244
245   status = web_view->DispatchTouchEvent(
246       TouchEvent(kTouchStart, location.x, location.y));
247   if (status.IsError())
248     return status;
249
250   const double offset =
251       std::sqrt(static_cast<double>(xoffset * xoffset + yoffset * yoffset));
252   const double xoffset_per_event =
253       (speed * xoffset) / (kFlickTouchEventsPerSecond * offset);
254   const double yoffset_per_event =
255       (speed * yoffset) / (kFlickTouchEventsPerSecond * offset);
256   const int total_events =
257       (offset * kFlickTouchEventsPerSecond) / speed;
258   for (int i = 0; i < total_events; i++) {
259     status = web_view->DispatchTouchEvent(
260         TouchEvent(kTouchMove,
261                    location.x + xoffset_per_event * i,
262                    location.y + yoffset_per_event * i));
263     if (status.IsError())
264       return status;
265     base::PlatformThread::Sleep(
266         base::TimeDelta::FromMilliseconds(1000 / kFlickTouchEventsPerSecond));
267   }
268   return web_view->DispatchTouchEvent(
269       TouchEvent(kTouchEnd, location.x + xoffset, location.y + yoffset));
270 }
271
272 Status ExecuteClearElement(
273     Session* session,
274     WebView* web_view,
275     const std::string& element_id,
276     const base::DictionaryValue& params,
277     scoped_ptr<base::Value>* value) {
278   base::ListValue args;
279   args.Append(CreateElement(element_id));
280   scoped_ptr<base::Value> result;
281   return web_view->CallFunction(
282       session->GetCurrentFrameId(),
283       webdriver::atoms::asString(webdriver::atoms::CLEAR),
284       args, &result);
285 }
286
287 Status ExecuteSendKeysToElement(
288     Session* session,
289     WebView* web_view,
290     const std::string& element_id,
291     const base::DictionaryValue& params,
292     scoped_ptr<base::Value>* value) {
293   const base::ListValue* key_list;
294   if (!params.GetList("value", &key_list))
295     return Status(kUnknownError, "'value' must be a list");
296
297   bool is_input = false;
298   Status status = IsElementAttributeEqualToIgnoreCase(
299       session, web_view, element_id, "tagName", "input", &is_input);
300   if (status.IsError())
301     return status;
302   bool is_file = false;
303   status = IsElementAttributeEqualToIgnoreCase(
304       session, web_view, element_id, "type", "file", &is_file);
305   if (status.IsError())
306     return status;
307   if (is_input && is_file) {
308     // Compress array into a single string.
309     base::FilePath::StringType paths_string;
310     for (size_t i = 0; i < key_list->GetSize(); ++i) {
311       base::FilePath::StringType path_part;
312       if (!key_list->GetString(i, &path_part))
313         return Status(kUnknownError, "'value' is invalid");
314       paths_string.append(path_part);
315     }
316
317     // Separate the string into separate paths, delimited by '\n'.
318     std::vector<base::FilePath::StringType> path_strings;
319     base::SplitString(paths_string, '\n', &path_strings);
320     std::vector<base::FilePath> paths;
321     for (size_t i = 0; i < path_strings.size(); ++i)
322       paths.push_back(base::FilePath(path_strings[i]));
323
324     bool multiple = false;
325     status = IsElementAttributeEqualToIgnoreCase(
326         session, web_view, element_id, "multiple", "true", &multiple);
327     if (status.IsError())
328       return status;
329     if (!multiple && paths.size() > 1)
330       return Status(kUnknownError, "the element can not hold multiple files");
331
332     scoped_ptr<base::DictionaryValue> element(CreateElement(element_id));
333     return web_view->SetFileInputFiles(
334         session->GetCurrentFrameId(), *element, paths);
335   } else {
336     return SendKeysToElement(session, web_view, element_id, key_list);
337   }
338 }
339
340 Status ExecuteSubmitElement(
341     Session* session,
342     WebView* web_view,
343     const std::string& element_id,
344     const base::DictionaryValue& params,
345     scoped_ptr<base::Value>* value) {
346   base::ListValue args;
347   args.Append(CreateElement(element_id));
348   return web_view->CallFunction(
349       session->GetCurrentFrameId(),
350       webdriver::atoms::asString(webdriver::atoms::SUBMIT),
351       args,
352       value);
353 }
354
355 Status ExecuteGetElementText(
356     Session* session,
357     WebView* web_view,
358     const std::string& element_id,
359     const base::DictionaryValue& params,
360     scoped_ptr<base::Value>* value) {
361   base::ListValue args;
362   args.Append(CreateElement(element_id));
363   return web_view->CallFunction(
364       session->GetCurrentFrameId(),
365       webdriver::atoms::asString(webdriver::atoms::GET_TEXT),
366       args,
367       value);
368 }
369
370 Status ExecuteGetElementValue(
371     Session* session,
372     WebView* web_view,
373     const std::string& element_id,
374     const base::DictionaryValue& params,
375     scoped_ptr<base::Value>* value) {
376   base::ListValue args;
377   args.Append(CreateElement(element_id));
378   return web_view->CallFunction(
379       session->GetCurrentFrameId(),
380       "function(elem) { return elem['value'] }",
381       args,
382       value);
383 }
384
385 Status ExecuteGetElementTagName(
386     Session* session,
387     WebView* web_view,
388     const std::string& element_id,
389     const base::DictionaryValue& params,
390     scoped_ptr<base::Value>* value) {
391   base::ListValue args;
392   args.Append(CreateElement(element_id));
393   return web_view->CallFunction(
394       session->GetCurrentFrameId(),
395       "function(elem) { return elem.tagName.toLowerCase() }",
396       args,
397       value);
398 }
399
400 Status ExecuteIsElementSelected(
401     Session* session,
402     WebView* web_view,
403     const std::string& element_id,
404     const base::DictionaryValue& params,
405     scoped_ptr<base::Value>* value) {
406   base::ListValue args;
407   args.Append(CreateElement(element_id));
408   return web_view->CallFunction(
409       session->GetCurrentFrameId(),
410       webdriver::atoms::asString(webdriver::atoms::IS_SELECTED),
411       args,
412       value);
413 }
414
415 Status ExecuteIsElementEnabled(
416     Session* session,
417     WebView* web_view,
418     const std::string& element_id,
419     const base::DictionaryValue& params,
420     scoped_ptr<base::Value>* value) {
421   base::ListValue args;
422   args.Append(CreateElement(element_id));
423   return web_view->CallFunction(
424       session->GetCurrentFrameId(),
425       webdriver::atoms::asString(webdriver::atoms::IS_ENABLED),
426       args,
427       value);
428 }
429
430 Status ExecuteIsElementDisplayed(
431     Session* session,
432     WebView* web_view,
433     const std::string& element_id,
434     const base::DictionaryValue& params,
435     scoped_ptr<base::Value>* value) {
436   base::ListValue args;
437   args.Append(CreateElement(element_id));
438   return web_view->CallFunction(
439       session->GetCurrentFrameId(),
440       webdriver::atoms::asString(webdriver::atoms::IS_DISPLAYED),
441       args,
442       value);
443 }
444
445 Status ExecuteGetElementLocation(
446     Session* session,
447     WebView* web_view,
448     const std::string& element_id,
449     const base::DictionaryValue& params,
450     scoped_ptr<base::Value>* value) {
451   base::ListValue args;
452   args.Append(CreateElement(element_id));
453   return web_view->CallFunction(
454       session->GetCurrentFrameId(),
455       webdriver::atoms::asString(webdriver::atoms::GET_LOCATION),
456       args,
457       value);
458 }
459
460 Status ExecuteGetElementLocationOnceScrolledIntoView(
461     Session* session,
462     WebView* web_view,
463     const std::string& element_id,
464     const base::DictionaryValue& params,
465     scoped_ptr<base::Value>* value) {
466   WebPoint location;
467   Status status = ScrollElementIntoView(
468       session, web_view, element_id, &location);
469   if (status.IsError())
470     return status;
471   value->reset(CreateValueFrom(location));
472   return Status(kOk);
473 }
474
475 Status ExecuteGetElementSize(
476     Session* session,
477     WebView* web_view,
478     const std::string& element_id,
479     const base::DictionaryValue& params,
480     scoped_ptr<base::Value>* value) {
481   base::ListValue args;
482   args.Append(CreateElement(element_id));
483   return web_view->CallFunction(
484       session->GetCurrentFrameId(),
485       webdriver::atoms::asString(webdriver::atoms::GET_SIZE),
486       args,
487       value);
488 }
489
490 Status ExecuteGetElementAttribute(
491     Session* session,
492     WebView* web_view,
493     const std::string& element_id,
494     const base::DictionaryValue& params,
495     scoped_ptr<base::Value>* value) {
496   std::string name;
497   if (!params.GetString("name", &name))
498     return Status(kUnknownError, "missing 'name'");
499   return GetElementAttribute(session, web_view, element_id, name, value);
500 }
501
502 Status ExecuteGetElementValueOfCSSProperty(
503     Session* session,
504     WebView* web_view,
505     const std::string& element_id,
506     const base::DictionaryValue& params,
507     scoped_ptr<base::Value>* value) {
508   std::string property_name;
509   if (!params.GetString("propertyName", &property_name))
510     return Status(kUnknownError, "missing 'propertyName'");
511   std::string property_value;
512   Status status = GetElementEffectiveStyle(
513       session, web_view, element_id, property_name, &property_value);
514   if (status.IsError())
515     return status;
516   value->reset(new base::StringValue(property_value));
517   return Status(kOk);
518 }
519
520 Status ExecuteElementEquals(
521     Session* session,
522     WebView* web_view,
523     const std::string& element_id,
524     const base::DictionaryValue& params,
525     scoped_ptr<base::Value>* value) {
526   std::string other_element_id;
527   if (!params.GetString("other", &other_element_id))
528     return Status(kUnknownError, "'other' must be a string");
529   value->reset(new base::FundamentalValue(element_id == other_element_id));
530   return Status(kOk);
531 }