Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4866)

Unified Diff: chrome/test/chromedriver/element_util.cc

Issue 12764021: [chromedriver] Support clicking an element in sub frames. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/test/chromedriver/element_util.cc
diff --git a/chrome/test/chromedriver/element_util.cc b/chrome/test/chromedriver/element_util.cc
index 4b1a361af6d5815ae4834659bd6d08c47e20d408..7ff4d77718fdbc8f384d7c596994b8b9f291d1f5 100644
--- a/chrome/test/chromedriver/element_util.cc
+++ b/chrome/test/chromedriver/element_util.cc
@@ -5,6 +5,8 @@
#include "chrome/test/chromedriver/element_util.h"
#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/strings/string_number_conversions.cc"
#include "base/threading/platform_thread.h"
#include "base/time.h"
#include "base/values.h"
@@ -86,14 +88,117 @@ base::Value* CreateValueFrom(const WebRect* rect) {
}
Status CallAtomsJs(
- Session* session,
+ const std::string& frame,
WebView* web_view,
const char* const* atom_function,
const base::ListValue& args,
scoped_ptr<base::Value>* result) {
return web_view->CallFunction(
- session->frame, webdriver::atoms::asString(atom_function),
- args, result);
+ frame, webdriver::atoms::asString(atom_function), args, result);
+}
+
+Status VerifyElementClickable(
+ const std::string& frame,
+ WebView* web_view,
+ const std::string& element_id,
+ WebPoint* location) {
kkania 2013/03/12 18:34:25 make this const WebPoint&; also, change all the Cr
chrisgao (Use stgao instead) 2013/03/12 23:17:41 Done.
+ base::ListValue args;
+ args.Append(CreateElement(element_id));
+ args.Append(CreateValueFrom(location));
+ scoped_ptr<base::Value> result;
+ Status status = CallAtomsJs(
+ frame, web_view, webdriver::atoms::IS_ELEMENT_CLICKABLE,
+ args, &result);
+ if (status.IsError())
+ return status;
+ base::DictionaryValue* dict;
+ bool is_clickable;
+ if (!result->GetAsDictionary(&dict) ||
+ !dict->GetBoolean("clickable", &is_clickable))
+ return Status(kUnknownError, "fail to parse value of IS_ELEMENT_CLICKABLE");
+
+ if (!is_clickable) {
+ std::string message;
+ if (!dict->GetString("message", &message))
+ message = "element is not clickable";
+ return Status(kUnknownError, message);
+ }
+ return Status(kOk);
+}
+
+Status ScrollElementRegionIntoViewHelper(
+ const std::string& frame,
+ WebView* web_view,
+ const std::string& element_id,
+ const WebRect& region,
+ bool center,
+ bool verify_clickable,
+ WebPoint* location) {
+ base::ListValue args;
+ args.Append(CreateElement(element_id));
+ args.AppendBoolean(center);
+ args.Append(CreateValueFrom(&region));
+ scoped_ptr<base::Value> result;
+ Status status = web_view->CallFunction(
+ frame, webdriver::atoms::asString(webdriver::atoms::GET_LOCATION_IN_VIEW),
+ args, &result);
+ if (status.IsError())
+ return status;
+ if (!ParseFromValue(result.get(), location))
kkania 2013/03/12 18:34:25 usually we've been trying to keep to the conventio
chrisgao (Use stgao instead) 2013/03/12 23:17:41 Done.
+ return Status(kUnknownError, "fail to parse value of GET_LOCATION_IN_VIEW");
+ if (verify_clickable) {
+ WebPoint middle = *location;
+ middle.Offset(region.width() / 2, region.height() / 2);
+ return VerifyElementClickable(frame, web_view, element_id, &middle);
+ }
+ return Status(kOk);
+}
+
+Status GetElementEffectiveStyle(
+ const std::string& frame,
+ WebView* web_view,
+ const std::string& element_id,
+ const std::string& property,
+ std::string* value) {
+ base::ListValue args;
+ args.Append(CreateElement(element_id));
+ args.AppendString(property);
+ scoped_ptr<base::Value> result;
+ Status status = web_view->CallFunction(
+ frame, webdriver::atoms::asString(webdriver::atoms::GET_EFFECTIVE_STYLE),
+ args, &result);
+ if (status.IsError())
+ return status;
+ if (!result->GetAsString(value))
+ return Status(kUnknownError, "fail to parse value of GET_EFFECTIVE_STYLE");
+ return Status(kOk);
+}
+
+Status GetElementBorder(
+ const std::string& frame,
+ WebView* web_view,
+ const std::string& element_id,
+ int* border_left,
+ int* border_top) {
+ std::string border_left_str;
+ Status status = GetElementEffectiveStyle(
+ frame, web_view, element_id, "border-left-width", &border_left_str);
+ if (status.IsError())
+ return status;
+ std::string border_top_str;
+ status = GetElementEffectiveStyle(
+ frame, web_view, element_id, "border-top-width", &border_top_str);
+ if (status.IsError())
+ return status;
+ int border_left_tmp = -1;
+ int border_top_tmp = -1;
+ base::StringToInt(border_left_str, &border_left_tmp);
+ base::StringToInt(border_top_str, &border_top_tmp);
+ if (border_left_tmp == -1 || border_top_tmp == -1)
+ return Status(kUnknownError, "fail to get border width of element");
+ *border_left = border_left_tmp;
+ *border_top = border_top_tmp;
+ return Status(kOk);
}
} // namespace
@@ -180,7 +285,7 @@ Status GetElementAttribute(
args.Append(CreateElement(element_id));
args.AppendString(attribute_name);
return CallAtomsJs(
- session, web_view, webdriver::atoms::GET_ATTRIBUTE, args, value);
+ session->frame, web_view, webdriver::atoms::GET_ATTRIBUTE, args, value);
}
Status IsElementAttributeEqualToIgnoreCase(
@@ -207,8 +312,7 @@ Status GetElementClickableLocation(
Session* session,
WebView* web_view,
const std::string& element_id,
- WebPoint* location,
- bool* is_clickable) {
+ WebPoint* location) {
bool is_displayed = false;
Status status = IsElementDisplayed(
session, web_view, element_id, true, &is_displayed);
@@ -223,14 +327,11 @@ Status GetElementClickableLocation(
return status;
status = ScrollElementRegionIntoView(
- session, web_view, element_id, rect, true, location);
+ session, web_view, element_id, rect,
+ true /* center */, true /* verify_clickable */, location);
if (status.IsError())
return status;
- location->offset(rect.width() / 2, rect.height() / 2);
- if (is_clickable) {
- return IsElementClickable(
- session, web_view, element_id, location, is_clickable);
- }
+ location->Offset(rect.width() / 2, rect.height() / 2);
return Status(kOk);
}
@@ -280,7 +381,7 @@ Status GetElementSize(
args.Append(CreateElement(element_id));
scoped_ptr<base::Value> result;
Status status = CallAtomsJs(
- session, web_view, webdriver::atoms::GET_SIZE, args, &result);
+ session->frame, web_view, webdriver::atoms::GET_SIZE, args, &result);
if (status.IsError())
return status;
if (!ParseFromValue(result.get(), size))
@@ -288,34 +389,6 @@ Status GetElementSize(
return Status(kOk);
}
-Status IsElementClickable(
- Session* session,
- WebView* web_view,
- const std::string& element_id,
- WebPoint* location,
- bool* is_clickable) {
- base::ListValue args;
- args.Append(CreateElement(element_id));
- args.Append(CreateValueFrom(location));
- scoped_ptr<base::Value> result;
- Status status = CallAtomsJs(
- session, web_view, webdriver::atoms::IS_ELEMENT_CLICKABLE, args, &result);
- if (status.IsError())
- return status;
- base::DictionaryValue* dict;
- if (!result->GetAsDictionary(&dict) ||
- !dict->GetBoolean("clickable", is_clickable))
- return Status(kUnknownError, "fail to parse value of IS_ELEMENT_CLICKABLE");
-
- if (!*is_clickable) {
- std::string message;
- if (!dict->GetString("message", &message))
- message = "element is not clickable";
- return Status(kOk, message);
- }
- return Status(kOk);
-}
-
Status IsElementDisplayed(
Session* session,
WebView* web_view,
@@ -327,7 +400,7 @@ Status IsElementDisplayed(
args.AppendBoolean(ignore_opacity);
scoped_ptr<base::Value> result;
Status status = CallAtomsJs(
- session, web_view, webdriver::atoms::IS_DISPLAYED, args, &result);
+ session->frame, web_view, webdriver::atoms::IS_DISPLAYED, args, &result);
if (status.IsError())
return status;
if (!result->GetAsBoolean(is_displayed))
@@ -344,7 +417,7 @@ Status IsElementEnabled(
args.Append(CreateElement(element_id));
scoped_ptr<base::Value> result;
Status status = CallAtomsJs(
- session, web_view, webdriver::atoms::IS_ENABLED, args, &result);
+ session->frame, web_view, webdriver::atoms::IS_ENABLED, args, &result);
if (status.IsError())
return status;
if (!result->GetAsBoolean(is_enabled))
@@ -361,7 +434,7 @@ Status IsOptionElementSelected(
args.Append(CreateElement(element_id));
scoped_ptr<base::Value> result;
Status status = CallAtomsJs(
- session, web_view, webdriver::atoms::IS_SELECTED, args, &result);
+ session->frame, web_view, webdriver::atoms::IS_SELECTED, args, &result);
if (status.IsError())
return status;
if (!result->GetAsBoolean(is_selected))
@@ -397,7 +470,7 @@ Status SetOptionElementSelected(
args.AppendBoolean(selected);
scoped_ptr<base::Value> result;
return CallAtomsJs(
- session, web_view, webdriver::atoms::CLICK, args, &result);
+ session->frame, web_view, webdriver::atoms::CLICK, args, &result);
}
Status ToggleOptionElement(
@@ -422,7 +495,8 @@ Status ScrollElementIntoView(
if (status.IsError())
return status;
return ScrollElementRegionIntoView(
- session, web_view, id, WebRect(WebPoint(0, 0), size), false, location);
+ session, web_view, id, WebRect(WebPoint(0, 0), size),
+ false /* center */, false /* verify_clickable */, location);
}
Status ScrollElementRegionIntoView(
@@ -431,21 +505,53 @@ Status ScrollElementRegionIntoView(
const std::string& element_id,
const WebRect& region,
bool center,
+ bool verify_clickable,
WebPoint* location) {
WebPoint region_offset = region.origin;
- base::ListValue args;
- args.Append(CreateElement(element_id));
- args.AppendBoolean(center);
- args.Append(CreateValueFrom(&region));
- scoped_ptr<base::Value> result;
-
- // TODO(chrisgao): Nested frame. See http://crbug.com/170998.
- Status status = CallAtomsJs(
- session, web_view, webdriver::atoms::GET_LOCATION_IN_VIEW, args, &result);
+ WebSize region_size = region.size;
+ Status status = ScrollElementRegionIntoViewHelper(
+ session->frame, web_view, element_id, region,
+ center, verify_clickable, &region_offset);
if (status.IsError())
return status;
- if (!ParseFromValue(result.get(), &region_offset))
- return Status(kUnknownError, "fail to parse value of GET_LOCATION_IN_VIEW");
+ const char* kFindSubFrameScript =
+ "function(xpath) {"
+ " return document.evaluate(xpath, document, null,"
+ " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
+ "}";
+ for (std::list<FrameInfo>::reverse_iterator rit =
+ session->frame_elements.rbegin();
+ rit != session->frame_elements.rend(); ++rit) {
+ base::ListValue args;
+ args.AppendString(rit->xpath);
+ scoped_ptr<base::Value> result;
+ status = web_view->CallFunction(
+ rit->parent_id, kFindSubFrameScript, args, &result);
+ if (status.IsError())
+ return status;
+ const base::DictionaryValue* element_dict;
+ if (!result->GetAsDictionary(&element_dict))
+ return Status(kUnknownError, "no element reference returned by script");
+ std::string frame_element_id;
+ if (!element_dict->GetString(kElementKey, &frame_element_id))
+ return Status(kUnknownError, "fail to locate a sub frame");
+
+ // Modify |region_offset| by the frame's border.
+ int border_lef, border_top;
+ status = GetElementBorder(
+ rit->parent_id, web_view, frame_element_id,
+ &border_lef, &border_top);
+ if (status.IsError())
+ return status;
+ region_offset.Offset(border_lef, border_top);
+
+ status = ScrollElementRegionIntoViewHelper(
+ rit->parent_id, web_view, frame_element_id,
+ WebRect(region_offset, region_size),
+ center, verify_clickable, &region_offset);
+ if (status.IsError())
+ return status;
+ }
*location = region_offset;
return Status(kOk);
}

Powered by Google App Engine
This is Rietveld 408576698