Chromium Code Reviews| 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(®ion)); |
| + 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(®ion)); |
| - 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, ®ion_offset); |
| if (status.IsError()) |
| return status; |
| - if (!ParseFromValue(result.get(), ®ion_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, ®ion_offset); |
| + if (status.IsError()) |
| + return status; |
| + } |
| *location = region_offset; |
| return Status(kOk); |
| } |