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
));