Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/test/chromedriver/element_util.h" | 5 #include "chrome/test/chromedriver/element_util.h" |
| 6 | 6 |
| 7 #include "base/string_util.h" | 7 #include "base/string_util.h" |
| 8 #include "base/stringprintf.h" | |
| 9 #include "base/strings/string_number_conversions.cc" | |
| 8 #include "base/threading/platform_thread.h" | 10 #include "base/threading/platform_thread.h" |
| 9 #include "base/time.h" | 11 #include "base/time.h" |
| 10 #include "base/values.h" | 12 #include "base/values.h" |
| 11 #include "chrome/test/chromedriver/basic_types.h" | 13 #include "chrome/test/chromedriver/basic_types.h" |
| 12 #include "chrome/test/chromedriver/js.h" | 14 #include "chrome/test/chromedriver/js.h" |
| 13 #include "chrome/test/chromedriver/session.h" | 15 #include "chrome/test/chromedriver/session.h" |
| 14 #include "chrome/test/chromedriver/status.h" | 16 #include "chrome/test/chromedriver/status.h" |
| 15 #include "chrome/test/chromedriver/web_view.h" | 17 #include "chrome/test/chromedriver/web_view.h" |
| 16 #include "third_party/webdriver/atoms.h" | 18 #include "third_party/webdriver/atoms.h" |
| 17 | 19 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 79 base::Value* CreateValueFrom(const WebRect* rect) { | 81 base::Value* CreateValueFrom(const WebRect* rect) { |
| 80 base::DictionaryValue* dict = new base::DictionaryValue(); | 82 base::DictionaryValue* dict = new base::DictionaryValue(); |
| 81 dict->SetInteger("left", rect->origin.x); | 83 dict->SetInteger("left", rect->origin.x); |
| 82 dict->SetInteger("top", rect->origin.y); | 84 dict->SetInteger("top", rect->origin.y); |
| 83 dict->SetInteger("width", rect->size.width); | 85 dict->SetInteger("width", rect->size.width); |
| 84 dict->SetInteger("height", rect->size.height); | 86 dict->SetInteger("height", rect->size.height); |
| 85 return dict; | 87 return dict; |
| 86 } | 88 } |
| 87 | 89 |
| 88 Status CallAtomsJs( | 90 Status CallAtomsJs( |
| 89 Session* session, | 91 const std::string& frame, |
| 90 WebView* web_view, | 92 WebView* web_view, |
| 91 const char* const* atom_function, | 93 const char* const* atom_function, |
| 92 const base::ListValue& args, | 94 const base::ListValue& args, |
| 93 scoped_ptr<base::Value>* result) { | 95 scoped_ptr<base::Value>* result) { |
| 94 return web_view->CallFunction( | 96 return web_view->CallFunction( |
| 95 session->frame, webdriver::atoms::asString(atom_function), | 97 frame, webdriver::atoms::asString(atom_function), args, result); |
| 96 args, result); | 98 } |
| 99 | |
| 100 Status VerifyElementClickable( | |
| 101 const std::string& frame, | |
| 102 WebView* web_view, | |
| 103 const std::string& element_id, | |
| 104 WebPoint* location) { | |
| 105 base::ListValue args; | |
| 106 args.Append(CreateElement(element_id)); | |
| 107 args.Append(CreateValueFrom(location)); | |
| 108 scoped_ptr<base::Value> result; | |
| 109 Status status = CallAtomsJs( | |
| 110 frame, web_view, webdriver::atoms::IS_ELEMENT_CLICKABLE, | |
| 111 args, &result); | |
| 112 if (status.IsError()) | |
| 113 return status; | |
| 114 base::DictionaryValue* dict; | |
| 115 bool is_clickable; | |
| 116 if (!result->GetAsDictionary(&dict) || | |
| 117 !dict->GetBoolean("clickable", &is_clickable)) | |
| 118 return Status(kUnknownError, "fail to parse value of IS_ELEMENT_CLICKABLE"); | |
| 119 | |
| 120 if (!is_clickable) { | |
| 121 std::string message; | |
| 122 if (!dict->GetString("message", &message)) | |
| 123 message = "element is not clickable"; | |
| 124 return Status(kUnknownError, message); | |
| 125 } | |
| 126 return Status(kOk); | |
| 127 } | |
| 128 | |
| 129 Status ScrollElementRegionIntoViewHelper( | |
| 130 const std::string& frame, | |
| 131 WebView* web_view, | |
| 132 const std::string& element_id, | |
| 133 const WebRect& region, | |
| 134 bool center, | |
| 135 bool verify_clickable, | |
| 136 WebPoint* location) { | |
| 137 base::ListValue args; | |
| 138 args.Append(CreateElement(element_id)); | |
| 139 args.AppendBoolean(center); | |
| 140 args.Append(CreateValueFrom(®ion)); | |
| 141 scoped_ptr<base::Value> result; | |
| 142 Status status = web_view->CallFunction( | |
| 143 frame, webdriver::atoms::asString(webdriver::atoms::GET_LOCATION_IN_VIEW), | |
| 144 args, &result); | |
| 145 if (status.IsError()) | |
| 146 return status; | |
| 147 if (!ParseFromValue(result.get(), location)) | |
| 148 return Status(kUnknownError, "fail to parse value of GET_LOCATION_IN_VIEW"); | |
| 149 if (verify_clickable) { | |
| 150 WebPoint middle = *location; | |
| 151 middle.Offset(region.width() / 2, region.height() / 2); | |
| 152 return VerifyElementClickable(frame, web_view, element_id, &middle); | |
| 153 } | |
| 154 return Status(kOk); | |
| 155 } | |
| 156 | |
| 157 Status GetElementEffectiveStyle( | |
| 158 const std::string& frame, | |
| 159 WebView* web_view, | |
| 160 const std::string& element_id, | |
| 161 const std::string& property, | |
| 162 std::string* value) { | |
| 163 base::ListValue args; | |
| 164 args.Append(CreateElement(element_id)); | |
| 165 args.AppendString(property); | |
| 166 scoped_ptr<base::Value> result; | |
| 167 Status status = web_view->CallFunction( | |
| 168 frame, webdriver::atoms::asString(webdriver::atoms::GET_EFFECTIVE_STYLE), | |
| 169 args, &result); | |
| 170 if (status.IsError()) | |
| 171 return status; | |
| 172 if (!result->GetAsString(value)) | |
| 173 return Status(kUnknownError, "fail to parse value of GET_EFFECTIVE_STYLE"); | |
| 174 return Status(kOk); | |
| 175 } | |
| 176 | |
| 177 Status GetElementBorder( | |
| 178 const std::string& frame, | |
| 179 WebView* web_view, | |
| 180 const std::string& element_id, | |
| 181 int* border_left, | |
| 182 int* border_top) { | |
| 183 std::string border_left_str; | |
| 184 Status status = GetElementEffectiveStyle( | |
| 185 frame, web_view, element_id, "border-left-width", &border_left_str); | |
| 186 if (status.IsError()) | |
| 187 return status; | |
| 188 std::string border_top_str; | |
| 189 status = GetElementEffectiveStyle( | |
| 190 frame, web_view, element_id, "border-top-width", &border_top_str); | |
| 191 if (status.IsError()) | |
| 192 return status; | |
| 193 base::StringToInt(border_left_str, border_left); | |
|
kkania
2013/03/12 04:04:07
check return value?
chrisgao (Use stgao instead)
2013/03/12 17:41:18
Done.
| |
| 194 base::StringToInt(border_top_str, border_top); | |
| 195 return Status(kOk); | |
| 97 } | 196 } |
| 98 | 197 |
| 99 } // namespace | 198 } // namespace |
| 100 | 199 |
| 101 base::DictionaryValue* CreateElement(const std::string& element_id) { | 200 base::DictionaryValue* CreateElement(const std::string& element_id) { |
| 102 base::DictionaryValue* element = new base::DictionaryValue(); | 201 base::DictionaryValue* element = new base::DictionaryValue(); |
| 103 element->SetString(kElementKey, element_id); | 202 element->SetString(kElementKey, element_id); |
| 104 return element; | 203 return element; |
| 105 } | 204 } |
| 106 | 205 |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 173 Status GetElementAttribute( | 272 Status GetElementAttribute( |
| 174 Session* session, | 273 Session* session, |
| 175 WebView* web_view, | 274 WebView* web_view, |
| 176 const std::string& element_id, | 275 const std::string& element_id, |
| 177 const std::string& attribute_name, | 276 const std::string& attribute_name, |
| 178 scoped_ptr<base::Value>* value) { | 277 scoped_ptr<base::Value>* value) { |
| 179 base::ListValue args; | 278 base::ListValue args; |
| 180 args.Append(CreateElement(element_id)); | 279 args.Append(CreateElement(element_id)); |
| 181 args.AppendString(attribute_name); | 280 args.AppendString(attribute_name); |
| 182 return CallAtomsJs( | 281 return CallAtomsJs( |
| 183 session, web_view, webdriver::atoms::GET_ATTRIBUTE, args, value); | 282 session->frame, web_view, webdriver::atoms::GET_ATTRIBUTE, args, value); |
| 184 } | 283 } |
| 185 | 284 |
| 186 Status IsElementAttributeEqualToIgnoreCase( | 285 Status IsElementAttributeEqualToIgnoreCase( |
| 187 Session* session, | 286 Session* session, |
| 188 WebView* web_view, | 287 WebView* web_view, |
| 189 const std::string& element_id, | 288 const std::string& element_id, |
| 190 const std::string& attribute_name, | 289 const std::string& attribute_name, |
| 191 const std::string& attribute_value, | 290 const std::string& attribute_value, |
| 192 bool* is_equal) { | 291 bool* is_equal) { |
| 193 scoped_ptr<base::Value> result; | 292 scoped_ptr<base::Value> result; |
| 194 Status status = GetElementAttribute( | 293 Status status = GetElementAttribute( |
| 195 session, web_view, element_id, attribute_name, &result); | 294 session, web_view, element_id, attribute_name, &result); |
| 196 if (status.IsError()) | 295 if (status.IsError()) |
| 197 return status; | 296 return status; |
| 198 std::string actual_value; | 297 std::string actual_value; |
| 199 if (result->GetAsString(&actual_value)) | 298 if (result->GetAsString(&actual_value)) |
| 200 *is_equal = LowerCaseEqualsASCII(actual_value, attribute_value.c_str()); | 299 *is_equal = LowerCaseEqualsASCII(actual_value, attribute_value.c_str()); |
| 201 else | 300 else |
| 202 *is_equal = false; | 301 *is_equal = false; |
| 203 return status; | 302 return status; |
| 204 } | 303 } |
| 205 | 304 |
| 206 Status GetElementClickableLocation( | 305 Status GetElementClickableLocation( |
| 207 Session* session, | 306 Session* session, |
| 208 WebView* web_view, | 307 WebView* web_view, |
| 209 const std::string& element_id, | 308 const std::string& element_id, |
| 210 WebPoint* location, | 309 WebPoint* location) { |
| 211 bool* is_clickable) { | |
| 212 bool is_displayed = false; | 310 bool is_displayed = false; |
| 213 Status status = IsElementDisplayed( | 311 Status status = IsElementDisplayed( |
| 214 session, web_view, element_id, true, &is_displayed); | 312 session, web_view, element_id, true, &is_displayed); |
| 215 if (status.IsError()) | 313 if (status.IsError()) |
| 216 return status; | 314 return status; |
| 217 if (!is_displayed) | 315 if (!is_displayed) |
| 218 return Status(kElementNotVisible); | 316 return Status(kElementNotVisible); |
| 219 | 317 |
| 220 WebRect rect; | 318 WebRect rect; |
| 221 status = GetElementRegion(session, web_view, element_id, &rect); | 319 status = GetElementRegion(session, web_view, element_id, &rect); |
| 222 if (status.IsError()) | 320 if (status.IsError()) |
| 223 return status; | 321 return status; |
| 224 | 322 |
| 225 status = ScrollElementRegionIntoView( | 323 status = ScrollElementRegionIntoView( |
| 226 session, web_view, element_id, rect, true, location); | 324 session, web_view, element_id, rect, |
| 325 true /* center */, true /* verify_clickable */, location); | |
| 227 if (status.IsError()) | 326 if (status.IsError()) |
| 228 return status; | 327 return status; |
| 229 location->offset(rect.width() / 2, rect.height() / 2); | 328 location->Offset(rect.width() / 2, rect.height() / 2); |
| 230 if (is_clickable) { | |
| 231 return IsElementClickable( | |
| 232 session, web_view, element_id, location, is_clickable); | |
| 233 } | |
| 234 return Status(kOk); | 329 return Status(kOk); |
| 235 } | 330 } |
| 236 | 331 |
| 237 Status GetElementRegion( | 332 Status GetElementRegion( |
| 238 Session* session, | 333 Session* session, |
| 239 WebView* web_view, | 334 WebView* web_view, |
| 240 const std::string& element_id, | 335 const std::string& element_id, |
| 241 WebRect* rect) { | 336 WebRect* rect) { |
| 242 base::ListValue args; | 337 base::ListValue args; |
| 243 args.Append(CreateElement(element_id)); | 338 args.Append(CreateElement(element_id)); |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 273 | 368 |
| 274 Status GetElementSize( | 369 Status GetElementSize( |
| 275 Session* session, | 370 Session* session, |
| 276 WebView* web_view, | 371 WebView* web_view, |
| 277 const std::string& element_id, | 372 const std::string& element_id, |
| 278 WebSize* size) { | 373 WebSize* size) { |
| 279 base::ListValue args; | 374 base::ListValue args; |
| 280 args.Append(CreateElement(element_id)); | 375 args.Append(CreateElement(element_id)); |
| 281 scoped_ptr<base::Value> result; | 376 scoped_ptr<base::Value> result; |
| 282 Status status = CallAtomsJs( | 377 Status status = CallAtomsJs( |
| 283 session, web_view, webdriver::atoms::GET_SIZE, args, &result); | 378 session->frame, web_view, webdriver::atoms::GET_SIZE, args, &result); |
| 284 if (status.IsError()) | 379 if (status.IsError()) |
| 285 return status; | 380 return status; |
| 286 if (!ParseFromValue(result.get(), size)) | 381 if (!ParseFromValue(result.get(), size)) |
| 287 return Status(kUnknownError, "fail to parse value of GET_SIZE"); | 382 return Status(kUnknownError, "fail to parse value of GET_SIZE"); |
| 288 return Status(kOk); | 383 return Status(kOk); |
| 289 } | 384 } |
| 290 | 385 |
| 291 Status IsElementClickable( | |
| 292 Session* session, | |
| 293 WebView* web_view, | |
| 294 const std::string& element_id, | |
| 295 WebPoint* location, | |
| 296 bool* is_clickable) { | |
| 297 base::ListValue args; | |
| 298 args.Append(CreateElement(element_id)); | |
| 299 args.Append(CreateValueFrom(location)); | |
| 300 scoped_ptr<base::Value> result; | |
| 301 Status status = CallAtomsJs( | |
| 302 session, web_view, webdriver::atoms::IS_ELEMENT_CLICKABLE, args, &result); | |
| 303 if (status.IsError()) | |
| 304 return status; | |
| 305 base::DictionaryValue* dict; | |
| 306 if (!result->GetAsDictionary(&dict) || | |
| 307 !dict->GetBoolean("clickable", is_clickable)) | |
| 308 return Status(kUnknownError, "fail to parse value of IS_ELEMENT_CLICKABLE"); | |
| 309 | |
| 310 if (!*is_clickable) { | |
| 311 std::string message; | |
| 312 if (!dict->GetString("message", &message)) | |
| 313 message = "element is not clickable"; | |
| 314 return Status(kOk, message); | |
| 315 } | |
| 316 return Status(kOk); | |
| 317 } | |
| 318 | |
| 319 Status IsElementDisplayed( | 386 Status IsElementDisplayed( |
| 320 Session* session, | 387 Session* session, |
| 321 WebView* web_view, | 388 WebView* web_view, |
| 322 const std::string& element_id, | 389 const std::string& element_id, |
| 323 bool ignore_opacity, | 390 bool ignore_opacity, |
| 324 bool* is_displayed) { | 391 bool* is_displayed) { |
| 325 base::ListValue args; | 392 base::ListValue args; |
| 326 args.Append(CreateElement(element_id)); | 393 args.Append(CreateElement(element_id)); |
| 327 args.AppendBoolean(ignore_opacity); | 394 args.AppendBoolean(ignore_opacity); |
| 328 scoped_ptr<base::Value> result; | 395 scoped_ptr<base::Value> result; |
| 329 Status status = CallAtomsJs( | 396 Status status = CallAtomsJs( |
| 330 session, web_view, webdriver::atoms::IS_DISPLAYED, args, &result); | 397 session->frame, web_view, webdriver::atoms::IS_DISPLAYED, args, &result); |
| 331 if (status.IsError()) | 398 if (status.IsError()) |
| 332 return status; | 399 return status; |
| 333 if (!result->GetAsBoolean(is_displayed)) | 400 if (!result->GetAsBoolean(is_displayed)) |
| 334 return Status(kUnknownError, "IS_DISPLAYED should return a boolean value"); | 401 return Status(kUnknownError, "IS_DISPLAYED should return a boolean value"); |
| 335 return Status(kOk); | 402 return Status(kOk); |
| 336 } | 403 } |
| 337 | 404 |
| 338 Status IsElementEnabled( | 405 Status IsElementEnabled( |
| 339 Session* session, | 406 Session* session, |
| 340 WebView* web_view, | 407 WebView* web_view, |
| 341 const std::string& element_id, | 408 const std::string& element_id, |
| 342 bool* is_enabled) { | 409 bool* is_enabled) { |
| 343 base::ListValue args; | 410 base::ListValue args; |
| 344 args.Append(CreateElement(element_id)); | 411 args.Append(CreateElement(element_id)); |
| 345 scoped_ptr<base::Value> result; | 412 scoped_ptr<base::Value> result; |
| 346 Status status = CallAtomsJs( | 413 Status status = CallAtomsJs( |
| 347 session, web_view, webdriver::atoms::IS_ENABLED, args, &result); | 414 session->frame, web_view, webdriver::atoms::IS_ENABLED, args, &result); |
| 348 if (status.IsError()) | 415 if (status.IsError()) |
| 349 return status; | 416 return status; |
| 350 if (!result->GetAsBoolean(is_enabled)) | 417 if (!result->GetAsBoolean(is_enabled)) |
| 351 return Status(kUnknownError, "IS_ENABLED should return a boolean value"); | 418 return Status(kUnknownError, "IS_ENABLED should return a boolean value"); |
| 352 return Status(kOk); | 419 return Status(kOk); |
| 353 } | 420 } |
| 354 | 421 |
| 355 Status IsOptionElementSelected( | 422 Status IsOptionElementSelected( |
| 356 Session* session, | 423 Session* session, |
| 357 WebView* web_view, | 424 WebView* web_view, |
| 358 const std::string& element_id, | 425 const std::string& element_id, |
| 359 bool* is_selected) { | 426 bool* is_selected) { |
| 360 base::ListValue args; | 427 base::ListValue args; |
| 361 args.Append(CreateElement(element_id)); | 428 args.Append(CreateElement(element_id)); |
| 362 scoped_ptr<base::Value> result; | 429 scoped_ptr<base::Value> result; |
| 363 Status status = CallAtomsJs( | 430 Status status = CallAtomsJs( |
| 364 session, web_view, webdriver::atoms::IS_SELECTED, args, &result); | 431 session->frame, web_view, webdriver::atoms::IS_SELECTED, args, &result); |
| 365 if (status.IsError()) | 432 if (status.IsError()) |
| 366 return status; | 433 return status; |
| 367 if (!result->GetAsBoolean(is_selected)) | 434 if (!result->GetAsBoolean(is_selected)) |
| 368 return Status(kUnknownError, "IS_SELECTED should return a boolean value"); | 435 return Status(kUnknownError, "IS_SELECTED should return a boolean value"); |
| 369 return Status(kOk); | 436 return Status(kOk); |
| 370 } | 437 } |
| 371 | 438 |
| 372 Status IsOptionElementTogglable( | 439 Status IsOptionElementTogglable( |
| 373 Session* session, | 440 Session* session, |
| 374 WebView* web_view, | 441 WebView* web_view, |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 390 Session* session, | 457 Session* session, |
| 391 WebView* web_view, | 458 WebView* web_view, |
| 392 const std::string& element_id, | 459 const std::string& element_id, |
| 393 bool selected) { | 460 bool selected) { |
| 394 // TODO(171034): need to fix throwing error if an alert is triggered. | 461 // TODO(171034): need to fix throwing error if an alert is triggered. |
| 395 base::ListValue args; | 462 base::ListValue args; |
| 396 args.Append(CreateElement(element_id)); | 463 args.Append(CreateElement(element_id)); |
| 397 args.AppendBoolean(selected); | 464 args.AppendBoolean(selected); |
| 398 scoped_ptr<base::Value> result; | 465 scoped_ptr<base::Value> result; |
| 399 return CallAtomsJs( | 466 return CallAtomsJs( |
| 400 session, web_view, webdriver::atoms::CLICK, args, &result); | 467 session->frame, web_view, webdriver::atoms::CLICK, args, &result); |
| 401 } | 468 } |
| 402 | 469 |
| 403 Status ToggleOptionElement( | 470 Status ToggleOptionElement( |
| 404 Session* session, | 471 Session* session, |
| 405 WebView* web_view, | 472 WebView* web_view, |
| 406 const std::string& element_id) { | 473 const std::string& element_id) { |
| 407 bool is_selected; | 474 bool is_selected; |
| 408 Status status = IsOptionElementSelected( | 475 Status status = IsOptionElementSelected( |
| 409 session, web_view, element_id, &is_selected); | 476 session, web_view, element_id, &is_selected); |
| 410 if (status.IsError()) | 477 if (status.IsError()) |
| 411 return status; | 478 return status; |
| 412 return SetOptionElementSelected(session, web_view, element_id, !is_selected); | 479 return SetOptionElementSelected(session, web_view, element_id, !is_selected); |
| 413 } | 480 } |
| 414 | 481 |
| 415 Status ScrollElementIntoView( | 482 Status ScrollElementIntoView( |
| 416 Session* session, | 483 Session* session, |
| 417 WebView* web_view, | 484 WebView* web_view, |
| 418 const std::string& id, | 485 const std::string& id, |
| 419 WebPoint* location) { | 486 WebPoint* location) { |
| 420 WebSize size; | 487 WebSize size; |
| 421 Status status = GetElementSize(session, web_view, id, &size); | 488 Status status = GetElementSize(session, web_view, id, &size); |
| 422 if (status.IsError()) | 489 if (status.IsError()) |
| 423 return status; | 490 return status; |
| 424 return ScrollElementRegionIntoView( | 491 return ScrollElementRegionIntoView( |
| 425 session, web_view, id, WebRect(WebPoint(0, 0), size), false, location); | 492 session, web_view, id, WebRect(WebPoint(0, 0), size), |
| 493 false /* center */, false /* verify_clickable */, location); | |
| 426 } | 494 } |
| 427 | 495 |
| 428 Status ScrollElementRegionIntoView( | 496 Status ScrollElementRegionIntoView( |
| 429 Session* session, | 497 Session* session, |
| 430 WebView* web_view, | 498 WebView* web_view, |
| 431 const std::string& element_id, | 499 const std::string& element_id, |
| 432 const WebRect& region, | 500 const WebRect& region, |
| 433 bool center, | 501 bool center, |
| 502 bool verify_clickable, | |
| 434 WebPoint* location) { | 503 WebPoint* location) { |
| 435 WebPoint region_offset = region.origin; | 504 WebPoint region_offset = region.origin; |
| 436 base::ListValue args; | 505 WebSize region_size = region.size; |
| 437 args.Append(CreateElement(element_id)); | 506 Status status = ScrollElementRegionIntoViewHelper( |
| 438 args.AppendBoolean(center); | 507 session->frame, web_view, element_id, region, |
| 439 args.Append(CreateValueFrom(®ion)); | 508 center, verify_clickable, ®ion_offset); |
| 440 scoped_ptr<base::Value> result; | |
| 441 | |
| 442 // TODO(chrisgao): Nested frame. See http://crbug.com/170998. | |
| 443 Status status = CallAtomsJs( | |
| 444 session, web_view, webdriver::atoms::GET_LOCATION_IN_VIEW, args, &result); | |
| 445 if (status.IsError()) | 509 if (status.IsError()) |
| 446 return status; | 510 return status; |
| 447 if (!ParseFromValue(result.get(), ®ion_offset)) | 511 if (!session->frame.empty()) { |
| 448 return Status(kUnknownError, "fail to parse value of GET_LOCATION_IN_VIEW"); | 512 std::list<Frame> frames; |
| 513 status = web_view->GetFramePath(session->frame, &frames); | |
| 514 if (status.IsError()) | |
| 515 return status; | |
| 516 std::string root_frame_id = frames.back().id; | |
| 517 const char* kFindSubFrameScript = | |
| 518 "function(xpath) {" | |
| 519 " return document.evaluate(xpath, document, null," | |
| 520 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" | |
| 521 "}"; | |
| 522 for (std::list<Frame>::const_iterator it = frames.begin(); | |
| 523 it != frames.end(); ++it) { | |
| 524 if (!it->IsSubFrame()) | |
| 525 break; | |
| 526 | |
| 527 std::string xpath = "(/html/body//iframe|/html/frameset/frame)"; | |
| 528 if (it->HasName()) | |
| 529 xpath += base::StringPrintf("[@name=\"%s\"]", it->name.c_str()); | |
|
kkania
2013/03/12 04:04:07
why do we support both name and index? i don't th
chrisgao (Use stgao instead)
2013/03/12 17:41:18
Because name of frame is optional in the Inspector
| |
| 530 else | |
| 531 xpath += base::StringPrintf("[%d]", it->index + 1); | |
| 532 | |
| 533 std::string parent_frame_id = it->parent_id; | |
| 534 if (parent_frame_id == root_frame_id) | |
| 535 parent_frame_id = ""; | |
| 536 | |
| 537 base::ListValue args; | |
| 538 args.AppendString(xpath); | |
| 539 scoped_ptr<base::Value> result; | |
| 540 status = web_view->CallFunction( | |
| 541 parent_frame_id, kFindSubFrameScript, args, &result); | |
| 542 if (status.IsError()) | |
| 543 return status; | |
| 544 const base::DictionaryValue* element_dict; | |
| 545 if (!result->GetAsDictionary(&element_dict)) | |
| 546 return Status(kUnknownError, "no element reference returned by script"); | |
| 547 std::string frame_element_id; | |
| 548 if (!element_dict->GetString(kElementKey, &frame_element_id)) | |
| 549 return Status(kUnknownError, "fail to locate a sub frame"); | |
| 550 | |
| 551 // Modify |region_offset| by the frame's border. | |
| 552 int border_lef, border_top; | |
| 553 status = GetElementBorder( | |
| 554 parent_frame_id, web_view, frame_element_id, | |
| 555 &border_lef, &border_top); | |
| 556 if (status.IsError()) | |
| 557 return status; | |
| 558 region_offset.Offset(border_lef, border_top); | |
| 559 | |
| 560 status = ScrollElementRegionIntoViewHelper( | |
| 561 parent_frame_id, web_view, frame_element_id, | |
| 562 WebRect(region_offset, region_size), | |
| 563 center, verify_clickable, ®ion_offset); | |
| 564 if (status.IsError()) | |
| 565 return status; | |
| 566 } | |
| 567 } | |
| 449 *location = region_offset; | 568 *location = region_offset; |
| 450 return Status(kOk); | 569 return Status(kOk); |
| 451 } | 570 } |
| OLD | NEW |