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