Unregister from GCM when the only GCM app is removed
[chromium-blink-merge.git] / chrome / test / chromedriver / element_util.cc
blobe8714d3d33ef980e43ef819db7bf1560e687bc5e
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_util.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/threading/platform_thread.h"
11 #include "base/time/time.h"
12 #include "base/values.h"
13 #include "chrome/test/chromedriver/basic_types.h"
14 #include "chrome/test/chromedriver/chrome/chrome.h"
15 #include "chrome/test/chromedriver/chrome/js.h"
16 #include "chrome/test/chromedriver/chrome/status.h"
17 #include "chrome/test/chromedriver/chrome/web_view.h"
18 #include "chrome/test/chromedriver/session.h"
19 #include "third_party/webdriver/atoms.h"
21 namespace {
23 const char kElementKey[] = "ELEMENT";
25 bool ParseFromValue(base::Value* value, WebPoint* point) {
26 base::DictionaryValue* dict_value;
27 if (!value->GetAsDictionary(&dict_value))
28 return false;
29 double x = 0;
30 double y = 0;
31 if (!dict_value->GetDouble("x", &x) ||
32 !dict_value->GetDouble("y", &y))
33 return false;
34 point->x = static_cast<int>(x);
35 point->y = static_cast<int>(y);
36 return true;
39 bool ParseFromValue(base::Value* value, WebSize* size) {
40 base::DictionaryValue* dict_value;
41 if (!value->GetAsDictionary(&dict_value))
42 return false;
43 double width = 0;
44 double height = 0;
45 if (!dict_value->GetDouble("width", &width) ||
46 !dict_value->GetDouble("height", &height))
47 return false;
48 size->width = static_cast<int>(width);
49 size->height = static_cast<int>(height);
50 return true;
53 bool ParseFromValue(base::Value* value, WebRect* rect) {
54 base::DictionaryValue* dict_value;
55 if (!value->GetAsDictionary(&dict_value))
56 return false;
57 double x = 0;
58 double y = 0;
59 double width = 0;
60 double height = 0;
61 if (!dict_value->GetDouble("left", &x) ||
62 !dict_value->GetDouble("top", &y) ||
63 !dict_value->GetDouble("width", &width) ||
64 !dict_value->GetDouble("height", &height))
65 return false;
66 rect->origin.x = static_cast<int>(x);
67 rect->origin.y = static_cast<int>(y);
68 rect->size.width = static_cast<int>(width);
69 rect->size.height = static_cast<int>(height);
70 return true;
73 base::Value* CreateValueFrom(const WebRect& rect) {
74 base::DictionaryValue* dict = new base::DictionaryValue();
75 dict->SetInteger("left", rect.X());
76 dict->SetInteger("top", rect.Y());
77 dict->SetInteger("width", rect.Width());
78 dict->SetInteger("height", rect.Height());
79 return dict;
82 Status CallAtomsJs(
83 const std::string& frame,
84 WebView* web_view,
85 const char* const* atom_function,
86 const base::ListValue& args,
87 scoped_ptr<base::Value>* result) {
88 return web_view->CallFunction(
89 frame, webdriver::atoms::asString(atom_function), args, result);
92 Status VerifyElementClickable(
93 const std::string& frame,
94 WebView* web_view,
95 const std::string& element_id,
96 const WebPoint& location) {
97 base::ListValue args;
98 args.Append(CreateElement(element_id));
99 args.Append(CreateValueFrom(location));
100 scoped_ptr<base::Value> result;
101 Status status = CallAtomsJs(
102 frame, web_view, webdriver::atoms::IS_ELEMENT_CLICKABLE,
103 args, &result);
104 if (status.IsError())
105 return status;
106 base::DictionaryValue* dict;
107 bool is_clickable = false;
108 if (!result->GetAsDictionary(&dict) ||
109 !dict->GetBoolean("clickable", &is_clickable)) {
110 return Status(kUnknownError,
111 "failed to parse value of IS_ELEMENT_CLICKABLE");
114 if (!is_clickable) {
115 std::string message;
116 if (!dict->GetString("message", &message))
117 message = "element is not clickable";
118 return Status(kUnknownError, message);
120 return Status(kOk);
123 Status ScrollElementRegionIntoViewHelper(
124 const std::string& frame,
125 WebView* web_view,
126 const std::string& element_id,
127 const WebRect& region,
128 bool center,
129 const std::string& clickable_element_id,
130 WebPoint* location) {
131 WebPoint tmp_location = *location;
132 base::ListValue args;
133 args.Append(CreateElement(element_id));
134 args.AppendBoolean(center);
135 args.Append(CreateValueFrom(region));
136 scoped_ptr<base::Value> result;
137 Status status = web_view->CallFunction(
138 frame, webdriver::atoms::asString(webdriver::atoms::GET_LOCATION_IN_VIEW),
139 args, &result);
140 if (status.IsError())
141 return status;
142 if (!ParseFromValue(result.get(), &tmp_location)) {
143 return Status(kUnknownError,
144 "failed to parse value of GET_LOCATION_IN_VIEW");
146 if (!clickable_element_id.empty()) {
147 WebPoint middle = tmp_location;
148 middle.Offset(region.Width() / 2, region.Height() / 2);
149 status = VerifyElementClickable(
150 frame, web_view, clickable_element_id, middle);
151 if (status.IsError())
152 return status;
154 *location = tmp_location;
155 return Status(kOk);
158 Status GetElementEffectiveStyle(
159 const std::string& frame,
160 WebView* web_view,
161 const std::string& element_id,
162 const std::string& property,
163 std::string* value) {
164 base::ListValue args;
165 args.Append(CreateElement(element_id));
166 args.AppendString(property);
167 scoped_ptr<base::Value> result;
168 Status status = web_view->CallFunction(
169 frame, webdriver::atoms::asString(webdriver::atoms::GET_EFFECTIVE_STYLE),
170 args, &result);
171 if (status.IsError())
172 return status;
173 if (!result->GetAsString(value)) {
174 return Status(kUnknownError,
175 "failed to parse value of GET_EFFECTIVE_STYLE");
177 return Status(kOk);
180 Status GetElementBorder(
181 const std::string& frame,
182 WebView* web_view,
183 const std::string& element_id,
184 int* border_left,
185 int* border_top) {
186 std::string border_left_str;
187 Status status = GetElementEffectiveStyle(
188 frame, web_view, element_id, "border-left-width", &border_left_str);
189 if (status.IsError())
190 return status;
191 std::string border_top_str;
192 status = GetElementEffectiveStyle(
193 frame, web_view, element_id, "border-top-width", &border_top_str);
194 if (status.IsError())
195 return status;
196 int border_left_tmp = -1;
197 int border_top_tmp = -1;
198 base::StringToInt(border_left_str, &border_left_tmp);
199 base::StringToInt(border_top_str, &border_top_tmp);
200 if (border_left_tmp == -1 || border_top_tmp == -1)
201 return Status(kUnknownError, "failed to get border width of element");
202 *border_left = border_left_tmp;
203 *border_top = border_top_tmp;
204 return Status(kOk);
207 } // namespace
209 base::DictionaryValue* CreateElement(const std::string& element_id) {
210 base::DictionaryValue* element = new base::DictionaryValue();
211 element->SetString(kElementKey, element_id);
212 return element;
215 base::Value* CreateValueFrom(const WebPoint& point) {
216 base::DictionaryValue* dict = new base::DictionaryValue();
217 dict->SetInteger("x", point.x);
218 dict->SetInteger("y", point.y);
219 return dict;
222 Status FindElement(
223 int interval_ms,
224 bool only_one,
225 const std::string* root_element_id,
226 Session* session,
227 WebView* web_view,
228 const base::DictionaryValue& params,
229 scoped_ptr<base::Value>* value) {
230 std::string strategy;
231 if (!params.GetString("using", &strategy))
232 return Status(kUnknownError, "'using' must be a string");
233 std::string target;
234 if (!params.GetString("value", &target))
235 return Status(kUnknownError, "'value' must be a string");
237 std::string script;
238 if (only_one)
239 script = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENT);
240 else
241 script = webdriver::atoms::asString(webdriver::atoms::FIND_ELEMENTS);
242 scoped_ptr<base::DictionaryValue> locator(new base::DictionaryValue());
243 locator->SetString(strategy, target);
244 base::ListValue arguments;
245 arguments.Append(locator.release());
246 if (root_element_id)
247 arguments.Append(CreateElement(*root_element_id));
249 base::TimeTicks start_time = base::TimeTicks::Now();
250 while (true) {
251 scoped_ptr<base::Value> temp;
252 Status status = web_view->CallFunction(
253 session->GetCurrentFrameId(), script, arguments, &temp);
254 if (status.IsError())
255 return status;
257 if (!temp->IsType(base::Value::TYPE_NULL)) {
258 if (only_one) {
259 value->reset(temp.release());
260 return Status(kOk);
261 } else {
262 base::ListValue* result;
263 if (!temp->GetAsList(&result))
264 return Status(kUnknownError, "script returns unexpected result");
266 if (result->GetSize() > 0U) {
267 value->reset(temp.release());
268 return Status(kOk);
273 if (base::TimeTicks::Now() - start_time >= session->implicit_wait) {
274 if (only_one) {
275 return Status(kNoSuchElement);
276 } else {
277 value->reset(new base::ListValue());
278 return Status(kOk);
281 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(interval_ms));
284 return Status(kUnknownError);
287 Status GetActiveElement(
288 Session* session,
289 WebView* web_view,
290 scoped_ptr<base::Value>* value) {
291 base::ListValue args;
292 return web_view->CallFunction(
293 session->GetCurrentFrameId(),
294 "function() { return document.activeElement || document.body }",
295 args,
296 value);
299 Status IsElementFocused(
300 Session* session,
301 WebView* web_view,
302 const std::string& element_id,
303 bool* is_focused) {
304 scoped_ptr<base::Value> result;
305 Status status = GetActiveElement(session, web_view, &result);
306 if (status.IsError())
307 return status;
308 scoped_ptr<base::Value> element_dict(CreateElement(element_id));
309 *is_focused = result->Equals(element_dict.get());
310 return Status(kOk);
313 Status GetElementAttribute(
314 Session* session,
315 WebView* web_view,
316 const std::string& element_id,
317 const std::string& attribute_name,
318 scoped_ptr<base::Value>* value) {
319 base::ListValue args;
320 args.Append(CreateElement(element_id));
321 args.AppendString(attribute_name);
322 return CallAtomsJs(
323 session->GetCurrentFrameId(), web_view, webdriver::atoms::GET_ATTRIBUTE,
324 args, value);
327 Status IsElementAttributeEqualToIgnoreCase(
328 Session* session,
329 WebView* web_view,
330 const std::string& element_id,
331 const std::string& attribute_name,
332 const std::string& attribute_value,
333 bool* is_equal) {
334 scoped_ptr<base::Value> result;
335 Status status = GetElementAttribute(
336 session, web_view, element_id, attribute_name, &result);
337 if (status.IsError())
338 return status;
339 std::string actual_value;
340 if (result->GetAsString(&actual_value))
341 *is_equal = LowerCaseEqualsASCII(actual_value, attribute_value.c_str());
342 else
343 *is_equal = false;
344 return status;
347 Status GetElementClickableLocation(
348 Session* session,
349 WebView* web_view,
350 const std::string& element_id,
351 WebPoint* location) {
352 std::string tag_name;
353 Status status = GetElementTagName(session, web_view, element_id, &tag_name);
354 if (status.IsError())
355 return status;
356 std::string target_element_id = element_id;
357 if (tag_name == "area") {
358 // Scroll the image into view instead of the area.
359 const char kGetImageElementForArea[] =
360 "function (element) {"
361 " var map = element.parentElement;"
362 " if (map.tagName.toLowerCase() != 'map')"
363 " throw new Error('the area is not within a map');"
364 " var mapName = map.getAttribute('name');"
365 " if (mapName == null)"
366 " throw new Error ('area\\'s parent map must have a name');"
367 " mapName = '#' + mapName.toLowerCase();"
368 " var images = document.getElementsByTagName('img');"
369 " for (var i = 0; i < images.length; i++) {"
370 " if (images[i].useMap.toLowerCase() == mapName)"
371 " return images[i];"
372 " }"
373 " throw new Error('no img is found for the area');"
374 "}";
375 base::ListValue args;
376 args.Append(CreateElement(element_id));
377 scoped_ptr<base::Value> result;
378 status = web_view->CallFunction(
379 session->GetCurrentFrameId(), kGetImageElementForArea, args, &result);
380 if (status.IsError())
381 return status;
382 const base::DictionaryValue* element_dict;
383 if (!result->GetAsDictionary(&element_dict) ||
384 !element_dict->GetString(kElementKey, &target_element_id))
385 return Status(kUnknownError, "no element reference returned by script");
387 bool is_displayed = false;
388 status = IsElementDisplayed(
389 session, web_view, target_element_id, true, &is_displayed);
390 if (status.IsError())
391 return status;
392 if (!is_displayed)
393 return Status(kElementNotVisible);
395 WebRect rect;
396 status = GetElementRegion(session, web_view, element_id, &rect);
397 if (status.IsError())
398 return status;
400 status = ScrollElementRegionIntoView(
401 session, web_view, target_element_id, rect,
402 true /* center */, element_id, location);
403 if (status.IsError())
404 return status;
405 location->Offset(rect.Width() / 2, rect.Height() / 2);
406 return Status(kOk);
409 Status GetElementEffectiveStyle(
410 Session* session,
411 WebView* web_view,
412 const std::string& element_id,
413 const std::string& property_name,
414 std::string* property_value) {
415 return GetElementEffectiveStyle(session->GetCurrentFrameId(), web_view,
416 element_id, property_name, property_value);
419 Status GetElementRegion(
420 Session* session,
421 WebView* web_view,
422 const std::string& element_id,
423 WebRect* rect) {
424 base::ListValue args;
425 args.Append(CreateElement(element_id));
426 scoped_ptr<base::Value> result;
427 Status status = web_view->CallFunction(
428 session->GetCurrentFrameId(), kGetElementRegionScript, args, &result);
429 if (status.IsError())
430 return status;
431 if (!ParseFromValue(result.get(), rect)) {
432 return Status(kUnknownError,
433 "failed to parse value of getElementRegion");
435 return Status(kOk);
438 Status GetElementTagName(
439 Session* session,
440 WebView* web_view,
441 const std::string& element_id,
442 std::string* name) {
443 base::ListValue args;
444 args.Append(CreateElement(element_id));
445 scoped_ptr<base::Value> result;
446 Status status = web_view->CallFunction(
447 session->GetCurrentFrameId(),
448 "function(elem) { return elem.tagName.toLowerCase(); }",
449 args, &result);
450 if (status.IsError())
451 return status;
452 if (!result->GetAsString(name))
453 return Status(kUnknownError, "failed to get element tag name");
454 return Status(kOk);
457 Status GetElementSize(
458 Session* session,
459 WebView* web_view,
460 const std::string& element_id,
461 WebSize* size) {
462 base::ListValue args;
463 args.Append(CreateElement(element_id));
464 scoped_ptr<base::Value> result;
465 Status status = CallAtomsJs(
466 session->GetCurrentFrameId(), web_view, webdriver::atoms::GET_SIZE,
467 args, &result);
468 if (status.IsError())
469 return status;
470 if (!ParseFromValue(result.get(), size))
471 return Status(kUnknownError, "failed to parse value of GET_SIZE");
472 return Status(kOk);
475 Status IsElementDisplayed(
476 Session* session,
477 WebView* web_view,
478 const std::string& element_id,
479 bool ignore_opacity,
480 bool* is_displayed) {
481 base::ListValue args;
482 args.Append(CreateElement(element_id));
483 args.AppendBoolean(ignore_opacity);
484 scoped_ptr<base::Value> result;
485 Status status = CallAtomsJs(
486 session->GetCurrentFrameId(), web_view, webdriver::atoms::IS_DISPLAYED,
487 args, &result);
488 if (status.IsError())
489 return status;
490 if (!result->GetAsBoolean(is_displayed))
491 return Status(kUnknownError, "IS_DISPLAYED should return a boolean value");
492 return Status(kOk);
495 Status IsElementEnabled(
496 Session* session,
497 WebView* web_view,
498 const std::string& element_id,
499 bool* is_enabled) {
500 base::ListValue args;
501 args.Append(CreateElement(element_id));
502 scoped_ptr<base::Value> result;
503 Status status = CallAtomsJs(
504 session->GetCurrentFrameId(), web_view, webdriver::atoms::IS_ENABLED,
505 args, &result);
506 if (status.IsError())
507 return status;
508 if (!result->GetAsBoolean(is_enabled))
509 return Status(kUnknownError, "IS_ENABLED should return a boolean value");
510 return Status(kOk);
513 Status IsOptionElementSelected(
514 Session* session,
515 WebView* web_view,
516 const std::string& element_id,
517 bool* is_selected) {
518 base::ListValue args;
519 args.Append(CreateElement(element_id));
520 scoped_ptr<base::Value> result;
521 Status status = CallAtomsJs(
522 session->GetCurrentFrameId(), web_view, webdriver::atoms::IS_SELECTED,
523 args, &result);
524 if (status.IsError())
525 return status;
526 if (!result->GetAsBoolean(is_selected))
527 return Status(kUnknownError, "IS_SELECTED should return a boolean value");
528 return Status(kOk);
531 Status IsOptionElementTogglable(
532 Session* session,
533 WebView* web_view,
534 const std::string& element_id,
535 bool* is_togglable) {
536 base::ListValue args;
537 args.Append(CreateElement(element_id));
538 scoped_ptr<base::Value> result;
539 Status status = web_view->CallFunction(
540 session->GetCurrentFrameId(), kIsOptionElementToggleableScript,
541 args, &result);
542 if (status.IsError())
543 return status;
544 if (!result->GetAsBoolean(is_togglable))
545 return Status(kUnknownError, "failed check if option togglable or not");
546 return Status(kOk);
549 Status SetOptionElementSelected(
550 Session* session,
551 WebView* web_view,
552 const std::string& element_id,
553 bool selected) {
554 // TODO(171034): need to fix throwing error if an alert is triggered.
555 base::ListValue args;
556 args.Append(CreateElement(element_id));
557 args.AppendBoolean(selected);
558 scoped_ptr<base::Value> result;
559 return CallAtomsJs(
560 session->GetCurrentFrameId(), web_view, webdriver::atoms::CLICK,
561 args, &result);
564 Status ToggleOptionElement(
565 Session* session,
566 WebView* web_view,
567 const std::string& element_id) {
568 bool is_selected;
569 Status status = IsOptionElementSelected(
570 session, web_view, element_id, &is_selected);
571 if (status.IsError())
572 return status;
573 return SetOptionElementSelected(session, web_view, element_id, !is_selected);
576 Status ScrollElementIntoView(
577 Session* session,
578 WebView* web_view,
579 const std::string& id,
580 const WebPoint* offset,
581 WebPoint* location) {
582 WebRect region;
583 Status status = GetElementRegion(session, web_view, id, &region);
584 if (status.IsError())
585 return status;
586 status = ScrollElementRegionIntoView(session, web_view, id, region,
587 false /* center */, std::string(), location);
588 if (status.IsError())
589 return status;
590 if (offset)
591 location->Offset(offset->x, offset->y);
592 else
593 location->Offset(region.size.width / 2, region.size.height / 2);
594 return Status(kOk);
597 Status ScrollElementRegionIntoView(
598 Session* session,
599 WebView* web_view,
600 const std::string& element_id,
601 const WebRect& region,
602 bool center,
603 const std::string& clickable_element_id,
604 WebPoint* location) {
605 WebPoint region_offset = region.origin;
606 WebSize region_size = region.size;
607 Status status = ScrollElementRegionIntoViewHelper(
608 session->GetCurrentFrameId(), web_view, element_id, region,
609 center, clickable_element_id, &region_offset);
610 if (status.IsError())
611 return status;
612 const char kFindSubFrameScript[] =
613 "function(xpath) {"
614 " return document.evaluate(xpath, document, null,"
615 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
616 "}";
617 for (std::list<FrameInfo>::reverse_iterator rit = session->frames.rbegin();
618 rit != session->frames.rend(); ++rit) {
619 base::ListValue args;
620 args.AppendString(
621 base::StringPrintf("//*[@cd_frame_id_ = '%s']",
622 rit->chromedriver_frame_id.c_str()));
623 scoped_ptr<base::Value> result;
624 status = web_view->CallFunction(
625 rit->parent_frame_id, kFindSubFrameScript, args, &result);
626 if (status.IsError())
627 return status;
628 const base::DictionaryValue* element_dict;
629 if (!result->GetAsDictionary(&element_dict))
630 return Status(kUnknownError, "no element reference returned by script");
631 std::string frame_element_id;
632 if (!element_dict->GetString(kElementKey, &frame_element_id))
633 return Status(kUnknownError, "failed to locate a sub frame");
635 // Modify |region_offset| by the frame's border.
636 int border_left = -1;
637 int border_top = -1;
638 status = GetElementBorder(
639 rit->parent_frame_id, web_view, frame_element_id,
640 &border_left, &border_top);
641 if (status.IsError())
642 return status;
643 region_offset.Offset(border_left, border_top);
645 status = ScrollElementRegionIntoViewHelper(
646 rit->parent_frame_id, web_view, frame_element_id,
647 WebRect(region_offset, region_size),
648 center, frame_element_id, &region_offset);
649 if (status.IsError())
650 return status;
652 *location = region_offset;
653 return Status(kOk);