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"
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/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"
30 Status SendKeysToElement(
33 const std::string& element_id,
34 const base::ListValue* key_list) {
35 bool is_displayed = false;
36 bool is_focused = false;
37 base::TimeTicks start_time = base::TimeTicks::Now();
39 Status status = IsElementDisplayed(
40 session, web_view, element_id, true, &is_displayed);
45 status = IsElementFocused(session, web_view, element_id, &is_focused);
50 if (base::TimeTicks::Now() - start_time >= session->implicit_wait) {
51 return Status(kElementNotVisible);
53 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
56 bool is_enabled = false;
57 Status status = IsElementEnabled(session, web_view, element_id, &is_enabled);
61 return Status(kInvalidElementState);
65 args.Append(CreateElement(element_id));
66 scoped_ptr<base::Value> result;
67 status = web_view->CallFunction(
68 session->GetCurrentFrameId(), kFocusScript, args, &result);
73 return SendKeysOnWindow(web_view, key_list, true, &session->sticky_modifiers);
76 Status ExecuteTouchSingleTapAtom(
79 const std::string& element_id,
80 const base::DictionaryValue& params,
81 scoped_ptr<base::Value>* value) {
83 args.Append(CreateElement(element_id));
84 return web_view->CallFunction(
85 session->GetCurrentFrameId(),
86 webdriver::atoms::asString(webdriver::atoms::TOUCH_SINGLE_TAP),
93 Status ExecuteElementCommand(
94 const ElementCommand& command,
97 const base::DictionaryValue& params,
98 scoped_ptr<base::Value>* value) {
100 if (params.GetString("id", &id) || params.GetString("element", &id))
101 return command.Run(session, web_view, id, params, value);
102 return Status(kUnknownError, "element identifier must be a string");
105 Status ExecuteFindChildElement(
109 const std::string& element_id,
110 const base::DictionaryValue& params,
111 scoped_ptr<base::Value>* value) {
113 interval_ms, true, &element_id, session, web_view, params, value);
116 Status ExecuteFindChildElements(
120 const std::string& element_id,
121 const base::DictionaryValue& params,
122 scoped_ptr<base::Value>* value) {
124 interval_ms, false, &element_id, session, web_view, params, value);
127 Status ExecuteHoverOverElement(
130 const std::string& element_id,
131 const base::DictionaryValue& params,
132 scoped_ptr<base::Value>* value) {
134 Status status = GetElementClickableLocation(
135 session, web_view, element_id, &location);
136 if (status.IsError())
139 MouseEvent move_event(
140 kMovedMouseEventType, kNoneMouseButton, location.x, location.y,
141 session->sticky_modifiers, 0);
142 std::list<MouseEvent> events;
143 events.push_back(move_event);
144 status = web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
146 session->mouse_position = location;
150 Status ExecuteClickElement(
153 const std::string& element_id,
154 const base::DictionaryValue& params,
155 scoped_ptr<base::Value>* value) {
156 std::string tag_name;
157 Status status = GetElementTagName(session, web_view, element_id, &tag_name);
158 if (status.IsError())
160 if (tag_name == "option") {
162 status = IsOptionElementTogglable(
163 session, web_view, element_id, &is_toggleable);
164 if (status.IsError())
167 return ToggleOptionElement(session, web_view, element_id);
169 return SetOptionElementSelected(session, web_view, element_id, true);
172 status = GetElementClickableLocation(
173 session, web_view, element_id, &location);
174 if (status.IsError())
177 std::list<MouseEvent> events;
179 MouseEvent(kMovedMouseEventType, kNoneMouseButton,
180 location.x, location.y, session->sticky_modifiers, 0));
182 MouseEvent(kPressedMouseEventType, kLeftMouseButton,
183 location.x, location.y, session->sticky_modifiers, 1));
185 MouseEvent(kReleasedMouseEventType, kLeftMouseButton,
186 location.x, location.y, session->sticky_modifiers, 1));
188 web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
190 session->mouse_position = location;
195 Status ExecuteTouchSingleTap(
198 const std::string& element_id,
199 const base::DictionaryValue& params,
200 scoped_ptr<base::Value>* value) {
201 // Fall back to javascript atom for pre-m30 Chrome.
202 if (session->chrome->GetBuildNo() < 1576)
203 return ExecuteTouchSingleTapAtom(
204 session, web_view, element_id, params, value);
207 Status status = GetElementClickableLocation(
208 session, web_view, element_id, &location);
209 if (status.IsError())
212 std::list<TouchEvent> events;
214 TouchEvent(kTouchStart, location.x, location.y));
216 TouchEvent(kTouchEnd, location.x, location.y));
217 return web_view->DispatchTouchEvents(events);
220 Status ExecuteClearElement(
223 const std::string& element_id,
224 const base::DictionaryValue& params,
225 scoped_ptr<base::Value>* value) {
226 base::ListValue args;
227 args.Append(CreateElement(element_id));
228 scoped_ptr<base::Value> result;
229 return web_view->CallFunction(
230 session->GetCurrentFrameId(),
231 webdriver::atoms::asString(webdriver::atoms::CLEAR),
235 Status ExecuteSendKeysToElement(
238 const std::string& element_id,
239 const base::DictionaryValue& params,
240 scoped_ptr<base::Value>* value) {
241 const base::ListValue* key_list;
242 if (!params.GetList("value", &key_list))
243 return Status(kUnknownError, "'value' must be a list");
245 bool is_input = false;
246 Status status = IsElementAttributeEqualToIgnoreCase(
247 session, web_view, element_id, "tagName", "input", &is_input);
248 if (status.IsError())
250 bool is_file = false;
251 status = IsElementAttributeEqualToIgnoreCase(
252 session, web_view, element_id, "type", "file", &is_file);
253 if (status.IsError())
255 if (is_input && is_file) {
256 // Compress array into a single string.
257 base::FilePath::StringType paths_string;
258 for (size_t i = 0; i < key_list->GetSize(); ++i) {
259 base::FilePath::StringType path_part;
260 if (!key_list->GetString(i, &path_part))
261 return Status(kUnknownError, "'value' is invalid");
262 paths_string.append(path_part);
265 // Separate the string into separate paths, delimited by '\n'.
266 std::vector<base::FilePath::StringType> path_strings;
267 base::SplitString(paths_string, '\n', &path_strings);
268 std::vector<base::FilePath> paths;
269 for (size_t i = 0; i < path_strings.size(); ++i)
270 paths.push_back(base::FilePath(path_strings[i]));
272 bool multiple = false;
273 status = IsElementAttributeEqualToIgnoreCase(
274 session, web_view, element_id, "multiple", "true", &multiple);
275 if (status.IsError())
277 if (!multiple && paths.size() > 1)
278 return Status(kUnknownError, "the element can not hold multiple files");
280 scoped_ptr<base::DictionaryValue> element(CreateElement(element_id));
281 return web_view->SetFileInputFiles(
282 session->GetCurrentFrameId(), *element, paths);
284 return SendKeysToElement(session, web_view, element_id, key_list);
288 Status ExecuteSubmitElement(
291 const std::string& element_id,
292 const base::DictionaryValue& params,
293 scoped_ptr<base::Value>* value) {
294 base::ListValue args;
295 args.Append(CreateElement(element_id));
296 return web_view->CallFunction(
297 session->GetCurrentFrameId(),
298 webdriver::atoms::asString(webdriver::atoms::SUBMIT),
303 Status ExecuteGetElementText(
306 const std::string& element_id,
307 const base::DictionaryValue& params,
308 scoped_ptr<base::Value>* value) {
309 base::ListValue args;
310 args.Append(CreateElement(element_id));
311 return web_view->CallFunction(
312 session->GetCurrentFrameId(),
313 webdriver::atoms::asString(webdriver::atoms::GET_TEXT),
318 Status ExecuteGetElementValue(
321 const std::string& element_id,
322 const base::DictionaryValue& params,
323 scoped_ptr<base::Value>* value) {
324 base::ListValue args;
325 args.Append(CreateElement(element_id));
326 return web_view->CallFunction(
327 session->GetCurrentFrameId(),
328 "function(elem) { return elem['value'] }",
333 Status ExecuteGetElementTagName(
336 const std::string& element_id,
337 const base::DictionaryValue& params,
338 scoped_ptr<base::Value>* value) {
339 base::ListValue args;
340 args.Append(CreateElement(element_id));
341 return web_view->CallFunction(
342 session->GetCurrentFrameId(),
343 "function(elem) { return elem.tagName.toLowerCase() }",
348 Status ExecuteIsElementSelected(
351 const std::string& element_id,
352 const base::DictionaryValue& params,
353 scoped_ptr<base::Value>* value) {
354 base::ListValue args;
355 args.Append(CreateElement(element_id));
356 return web_view->CallFunction(
357 session->GetCurrentFrameId(),
358 webdriver::atoms::asString(webdriver::atoms::IS_SELECTED),
363 Status ExecuteIsElementEnabled(
366 const std::string& element_id,
367 const base::DictionaryValue& params,
368 scoped_ptr<base::Value>* value) {
369 base::ListValue args;
370 args.Append(CreateElement(element_id));
371 return web_view->CallFunction(
372 session->GetCurrentFrameId(),
373 webdriver::atoms::asString(webdriver::atoms::IS_ENABLED),
378 Status ExecuteIsElementDisplayed(
381 const std::string& element_id,
382 const base::DictionaryValue& params,
383 scoped_ptr<base::Value>* value) {
384 base::ListValue args;
385 args.Append(CreateElement(element_id));
386 return web_view->CallFunction(
387 session->GetCurrentFrameId(),
388 webdriver::atoms::asString(webdriver::atoms::IS_DISPLAYED),
393 Status ExecuteGetElementLocation(
396 const std::string& element_id,
397 const base::DictionaryValue& params,
398 scoped_ptr<base::Value>* value) {
399 base::ListValue args;
400 args.Append(CreateElement(element_id));
401 return web_view->CallFunction(
402 session->GetCurrentFrameId(),
403 webdriver::atoms::asString(webdriver::atoms::GET_LOCATION),
408 Status ExecuteGetElementLocationOnceScrolledIntoView(
411 const std::string& element_id,
412 const base::DictionaryValue& params,
413 scoped_ptr<base::Value>* value) {
415 Status status = ScrollElementIntoView(
416 session, web_view, element_id, &location);
417 if (status.IsError())
419 value->reset(CreateValueFrom(location));
423 Status ExecuteGetElementSize(
426 const std::string& element_id,
427 const base::DictionaryValue& params,
428 scoped_ptr<base::Value>* value) {
429 base::ListValue args;
430 args.Append(CreateElement(element_id));
431 return web_view->CallFunction(
432 session->GetCurrentFrameId(),
433 webdriver::atoms::asString(webdriver::atoms::GET_SIZE),
438 Status ExecuteGetElementAttribute(
441 const std::string& element_id,
442 const base::DictionaryValue& params,
443 scoped_ptr<base::Value>* value) {
445 if (!params.GetString("name", &name))
446 return Status(kUnknownError, "missing 'name'");
447 return GetElementAttribute(session, web_view, element_id, name, value);
450 Status ExecuteGetElementValueOfCSSProperty(
453 const std::string& element_id,
454 const base::DictionaryValue& params,
455 scoped_ptr<base::Value>* value) {
456 std::string property_name;
457 if (!params.GetString("propertyName", &property_name))
458 return Status(kUnknownError, "missing 'propertyName'");
459 std::string property_value;
460 Status status = GetElementEffectiveStyle(
461 session, web_view, element_id, property_name, &property_value);
462 if (status.IsError())
464 value->reset(new base::StringValue(property_value));
468 Status ExecuteElementEquals(
471 const std::string& element_id,
472 const base::DictionaryValue& params,
473 scoped_ptr<base::Value>* value) {
474 std::string other_element_id;
475 if (!params.GetString("other", &other_element_id))
476 return Status(kUnknownError, "'other' must be a string");
477 value->reset(new base::FundamentalValue(element_id == other_element_id));