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. 4 5 #include "chrome/test/chromedriver/window_commands.h" 6 7 #include <list> 8 #include <string> 9 10 #include "base/callback.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/threading/platform_thread.h" 14 #include "base/time/time.h" 15 #include "base/values.h" 16 #include "chrome/test/chromedriver/basic_types.h" 17 #include "chrome/test/chromedriver/chrome/automation_extension.h" 18 #include "chrome/test/chromedriver/chrome/chrome.h" 19 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h" 20 #include "chrome/test/chromedriver/chrome/devtools_client.h" 21 #include "chrome/test/chromedriver/chrome/geoposition.h" 22 #include "chrome/test/chromedriver/chrome/javascript_dialog_manager.h" 23 #include "chrome/test/chromedriver/chrome/js.h" 24 #include "chrome/test/chromedriver/chrome/status.h" 25 #include "chrome/test/chromedriver/chrome/ui_events.h" 26 #include "chrome/test/chromedriver/chrome/web_view.h" 27 #include "chrome/test/chromedriver/element_util.h" 28 #include "chrome/test/chromedriver/session.h" 29 #include "chrome/test/chromedriver/util.h" 30 31 namespace { 32 33 Status GetMouseButton(const base::DictionaryValue& params, 34 MouseButton* button) { 35 int button_num; 36 if (!params.GetInteger("button", &button_num)) { 37 button_num = 0; // Default to left mouse button. 38 } else if (button_num < 0 || button_num > 2) { 39 return Status(kUnknownError, 40 base::StringPrintf("invalid button: %d", button_num)); 41 } 42 *button = static_cast<MouseButton>(button_num); 43 return Status(kOk); 44 } 45 46 Status GetUrl(WebView* web_view, const std::string& frame, std::string* url) { 47 scoped_ptr<base::Value> value; 48 base::ListValue args; 49 Status status = web_view->CallFunction( 50 frame, "function() { return document.URL; }", args, &value); 51 if (status.IsError()) 52 return status; 53 if (!value->GetAsString(url)) 54 return Status(kUnknownError, "javascript failed to return the url"); 55 return Status(kOk); 56 } 57 58 struct Cookie { 59 Cookie(const std::string& name, 60 const std::string& value, 61 const std::string& domain, 62 const std::string& path, 63 double expiry, 64 bool secure, 65 bool session) 66 : name(name), value(value), domain(domain), path(path), expiry(expiry), 67 secure(secure), session(session) {} 68 69 std::string name; 70 std::string value; 71 std::string domain; 72 std::string path; 73 double expiry; 74 bool secure; 75 bool session; 76 }; 77 78 base::DictionaryValue* CreateDictionaryFrom(const Cookie& cookie) { 79 base::DictionaryValue* dict = new base::DictionaryValue(); 80 dict->SetString("name", cookie.name); 81 dict->SetString("value", cookie.value); 82 if (!cookie.domain.empty()) 83 dict->SetString("domain", cookie.domain); 84 if (!cookie.path.empty()) 85 dict->SetString("path", cookie.path); 86 if (!cookie.session) 87 dict->SetDouble("expiry", cookie.expiry); 88 dict->SetBoolean("secure", cookie.secure); 89 return dict; 90 } 91 92 Status GetVisibleCookies(WebView* web_view, 93 std::list<Cookie>* cookies) { 94 scoped_ptr<base::ListValue> internal_cookies; 95 Status status = web_view->GetCookies(&internal_cookies); 96 if (status.IsError()) 97 return status; 98 std::list<Cookie> cookies_tmp; 99 for (size_t i = 0; i < internal_cookies->GetSize(); ++i) { 100 base::DictionaryValue* cookie_dict; 101 if (!internal_cookies->GetDictionary(i, &cookie_dict)) 102 return Status(kUnknownError, "DevTools returns a non-dictionary cookie"); 103 104 std::string name; 105 cookie_dict->GetString("name", &name); 106 std::string value; 107 cookie_dict->GetString("value", &value); 108 std::string domain; 109 cookie_dict->GetString("domain", &domain); 110 std::string path; 111 cookie_dict->GetString("path", &path); 112 double expiry = 0; 113 cookie_dict->GetDouble("expires", &expiry); 114 expiry /= 1000; // Convert from millisecond to second. 115 bool session = false; 116 cookie_dict->GetBoolean("session", &session); 117 bool secure = false; 118 cookie_dict->GetBoolean("secure", &secure); 119 120 cookies_tmp.push_back( 121 Cookie(name, value, domain, path, expiry, secure, session)); 122 } 123 cookies->swap(cookies_tmp); 124 return Status(kOk); 125 } 126 127 Status ScrollCoordinateInToView( 128 Session* session, WebView* web_view, int x, int y, int* offset_x, 129 int* offset_y) { 130 scoped_ptr<base::Value> value; 131 base::ListValue args; 132 args.AppendInteger(x); 133 args.AppendInteger(y); 134 Status status = web_view->CallFunction( 135 std::string(), 136 "function(x, y) {" 137 " if (x < window.pageXOffset ||" 138 " x >= window.pageXOffset + window.innerWidth ||" 139 " y < window.pageYOffset ||" 140 " y >= window.pageYOffset + window.innerHeight) {" 141 " window.scrollTo(x - window.innerWidth/2, y - window.innerHeight/2);" 142 " }" 143 " return {" 144 " view_x: Math.floor(window.pageXOffset)," 145 " view_y: Math.floor(window.pageYOffset)," 146 " view_width: Math.floor(window.innerWidth)," 147 " view_height: Math.floor(window.innerHeight)};" 148 "}", 149 args, 150 &value); 151 if (!status.IsOk()) 152 return status; 153 base::DictionaryValue* view_attrib; 154 value->GetAsDictionary(&view_attrib); 155 int view_x, view_y, view_width, view_height; 156 view_attrib->GetInteger("view_x", &view_x); 157 view_attrib->GetInteger("view_y", &view_y); 158 view_attrib->GetInteger("view_width", &view_width); 159 view_attrib->GetInteger("view_height", &view_height); 160 *offset_x = x - view_x; 161 *offset_y = y - view_y; 162 if (*offset_x < 0 || *offset_x >= view_width || *offset_y < 0 || 163 *offset_y >= view_height) 164 return Status(kUnknownError, "Failed to scroll coordinate into view"); 165 return Status(kOk); 166 } 167 168 Status ExecuteTouchEvent( 169 Session* session, WebView* web_view, TouchEventType type, 170 const base::DictionaryValue& params) { 171 int x, y; 172 if (!params.GetInteger("x", &x)) 173 return Status(kUnknownError, "'x' must be an integer"); 174 if (!params.GetInteger("y", &y)) 175 return Status(kUnknownError, "'y' must be an integer"); 176 int relative_x = x; 177 int relative_y = y; 178 Status status = ScrollCoordinateInToView( 179 session, web_view, x, y, &relative_x, &relative_y); 180 if (!status.IsOk()) 181 return status; 182 std::list<TouchEvent> events; 183 events.push_back( 184 TouchEvent(type, relative_x, relative_y)); 185 return web_view->DispatchTouchEvents(events); 186 } 187 188 } // namespace 189 190 Status ExecuteWindowCommand( 191 const WindowCommand& command, 192 Session* session, 193 const base::DictionaryValue& params, 194 scoped_ptr<base::Value>* value) { 195 WebView* web_view = NULL; 196 Status status = session->GetTargetWindow(&web_view); 197 if (status.IsError()) 198 return status; 199 200 status = web_view->ConnectIfNecessary(); 201 if (status.IsError()) 202 return status; 203 204 status = web_view->HandleReceivedEvents(); 205 if (status.IsError()) 206 return status; 207 208 if (web_view->GetJavaScriptDialogManager()->IsDialogOpen()) 209 return Status(kUnexpectedAlertOpen); 210 211 Status nav_status(kOk); 212 for (int attempt = 0; attempt < 2; attempt++) { 213 if (attempt == 1) { 214 if (status.code() == kNoSuchExecutionContext) 215 // Switch to main frame and retry command if subframe no longer exists. 216 session->SwitchToTopFrame(); 217 else 218 break; 219 } 220 nav_status = web_view->WaitForPendingNavigations( 221 session->GetCurrentFrameId(), session->page_load_timeout, true); 222 if (nav_status.IsError()) 223 return nav_status; 224 225 status = command.Run(session, web_view, params, value); 226 } 227 228 nav_status = web_view->WaitForPendingNavigations( 229 session->GetCurrentFrameId(), session->page_load_timeout, true); 230 231 if (status.IsOk() && nav_status.IsError() && 232 nav_status.code() != kUnexpectedAlertOpen) 233 return nav_status; 234 if (status.code() == kUnexpectedAlertOpen) 235 return Status(kOk); 236 return status; 237 } 238 239 Status ExecuteGet( 240 Session* session, 241 WebView* web_view, 242 const base::DictionaryValue& params, 243 scoped_ptr<base::Value>* value) { 244 std::string url; 245 if (!params.GetString("url", &url)) 246 return Status(kUnknownError, "'url' must be a string"); 247 return web_view->Load(url); 248 } 249 250 Status ExecuteExecuteScript( 251 Session* session, 252 WebView* web_view, 253 const base::DictionaryValue& params, 254 scoped_ptr<base::Value>* value) { 255 std::string script; 256 if (!params.GetString("script", &script)) 257 return Status(kUnknownError, "'script' must be a string"); 258 if (script == ":takeHeapSnapshot") { 259 return web_view->TakeHeapSnapshot(value); 260 } else if (script == ":startProfile") { 261 return web_view->StartProfile(); 262 } else if (script == ":endProfile") { 263 return web_view->EndProfile(value); 264 } else { 265 const base::ListValue* args; 266 if (!params.GetList("args", &args)) 267 return Status(kUnknownError, "'args' must be a list"); 268 269 return web_view->CallFunction(session->GetCurrentFrameId(), 270 "function(){" + script + "}", *args, value); 271 } 272 } 273 274 Status ExecuteExecuteAsyncScript( 275 Session* session, 276 WebView* web_view, 277 const base::DictionaryValue& params, 278 scoped_ptr<base::Value>* value) { 279 std::string script; 280 if (!params.GetString("script", &script)) 281 return Status(kUnknownError, "'script' must be a string"); 282 const base::ListValue* args; 283 if (!params.GetList("args", &args)) 284 return Status(kUnknownError, "'args' must be a list"); 285 286 return web_view->CallUserAsyncFunction( 287 session->GetCurrentFrameId(), "function(){" + script + "}", *args, 288 session->script_timeout, value); 289 } 290 291 Status ExecuteSwitchToFrame( 292 Session* session, 293 WebView* web_view, 294 const base::DictionaryValue& params, 295 scoped_ptr<base::Value>* value) { 296 const base::Value* id; 297 if (!params.Get("id", &id)) 298 return Status(kUnknownError, "missing 'id'"); 299 300 if (id->IsType(base::Value::TYPE_NULL)) { 301 session->SwitchToTopFrame(); 302 return Status(kOk); 303 } 304 305 std::string script; 306 base::ListValue args; 307 const base::DictionaryValue* id_dict; 308 if (id->GetAsDictionary(&id_dict)) { 309 script = "function(elem) { return elem; }"; 310 args.Append(id_dict->DeepCopy()); 311 } else { 312 script = 313 "function(xpath) {" 314 " return document.evaluate(xpath, document, null, " 315 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;" 316 "}"; 317 std::string xpath = "(/html/body//iframe|/html/frameset/frame)"; 318 std::string id_string; 319 int id_int; 320 if (id->GetAsString(&id_string)) { 321 xpath += base::StringPrintf( 322 "[@name=\"%s\" or @id=\"%s\"]", id_string.c_str(), id_string.c_str()); 323 } else if (id->GetAsInteger(&id_int)) { 324 xpath += base::StringPrintf("[%d]", id_int + 1); 325 } else { 326 return Status(kUnknownError, "invalid 'id'"); 327 } 328 args.Append(new base::StringValue(xpath)); 329 } 330 std::string frame; 331 Status status = web_view->GetFrameByFunction( 332 session->GetCurrentFrameId(), script, args, &frame); 333 if (status.IsError()) 334 return status; 335 336 scoped_ptr<base::Value> result; 337 status = web_view->CallFunction( 338 session->GetCurrentFrameId(), script, args, &result); 339 if (status.IsError()) 340 return status; 341 const base::DictionaryValue* element; 342 if (!result->GetAsDictionary(&element)) 343 return Status(kUnknownError, "fail to locate the sub frame element"); 344 345 std::string chrome_driver_id = GenerateId(); 346 const char* kSetFrameIdentifier = 347 "function(frame, id) {" 348 " frame.setAttribute('cd_frame_id_', id);" 349 "}"; 350 base::ListValue new_args; 351 new_args.Append(element->DeepCopy()); 352 new_args.AppendString(chrome_driver_id); 353 result.reset(NULL); 354 status = web_view->CallFunction( 355 session->GetCurrentFrameId(), kSetFrameIdentifier, new_args, &result); 356 if (status.IsError()) 357 return status; 358 session->SwitchToSubFrame(frame, chrome_driver_id); 359 return Status(kOk); 360 } 361 362 Status ExecuteSwitchToParentFrame( 363 Session* session, 364 WebView* web_view, 365 const base::DictionaryValue& params, 366 scoped_ptr<base::Value>* value) { 367 session->SwitchToParentFrame(); 368 return Status(kOk); 369 } 370 371 Status ExecuteGetTitle( 372 Session* session, 373 WebView* web_view, 374 const base::DictionaryValue& params, 375 scoped_ptr<base::Value>* value) { 376 const char* kGetTitleScript = 377 "function() {" 378 " if (document.title)" 379 " return document.title;" 380 " else" 381 " return document.URL;" 382 "}"; 383 base::ListValue args; 384 return web_view->CallFunction(std::string(), kGetTitleScript, args, value); 385 } 386 387 Status ExecuteGetPageSource( 388 Session* session, 389 WebView* web_view, 390 const base::DictionaryValue& params, 391 scoped_ptr<base::Value>* value) { 392 const char* kGetPageSource = 393 "function() {" 394 " return new XMLSerializer().serializeToString(document);" 395 "}"; 396 base::ListValue args; 397 return web_view->CallFunction( 398 session->GetCurrentFrameId(), kGetPageSource, args, value); 399 } 400 401 Status ExecuteFindElement( 402 int interval_ms, 403 Session* session, 404 WebView* web_view, 405 const base::DictionaryValue& params, 406 scoped_ptr<base::Value>* value) { 407 return FindElement(interval_ms, true, NULL, session, web_view, params, value); 408 } 409 410 Status ExecuteFindElements( 411 int interval_ms, 412 Session* session, 413 WebView* web_view, 414 const base::DictionaryValue& params, 415 scoped_ptr<base::Value>* value) { 416 return FindElement( 417 interval_ms, false, NULL, session, web_view, params, value); 418 } 419 420 Status ExecuteGetCurrentUrl( 421 Session* session, 422 WebView* web_view, 423 const base::DictionaryValue& params, 424 scoped_ptr<base::Value>* value) { 425 std::string url; 426 Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url); 427 if (status.IsError()) 428 return status; 429 value->reset(new base::StringValue(url)); 430 return Status(kOk); 431 } 432 433 Status ExecuteGoBack( 434 Session* session, 435 WebView* web_view, 436 const base::DictionaryValue& params, 437 scoped_ptr<base::Value>* value) { 438 return web_view->EvaluateScript( 439 std::string(), "window.history.back();", value); 440 } 441 442 Status ExecuteGoForward( 443 Session* session, 444 WebView* web_view, 445 const base::DictionaryValue& params, 446 scoped_ptr<base::Value>* value) { 447 return web_view->EvaluateScript( 448 std::string(), "window.history.forward();", value); 449 } 450 451 Status ExecuteRefresh( 452 Session* session, 453 WebView* web_view, 454 const base::DictionaryValue& params, 455 scoped_ptr<base::Value>* value) { 456 return web_view->Reload(); 457 } 458 459 Status ExecuteMouseMoveTo( 460 Session* session, 461 WebView* web_view, 462 const base::DictionaryValue& params, 463 scoped_ptr<base::Value>* value) { 464 std::string element_id; 465 bool has_element = params.GetString("element", &element_id); 466 int x_offset = 0; 467 int y_offset = 0; 468 bool has_offset = params.GetInteger("xoffset", &x_offset) && 469 params.GetInteger("yoffset", &y_offset); 470 if (!has_element && !has_offset) 471 return Status(kUnknownError, "at least an element or offset should be set"); 472 473 WebPoint location; 474 if (has_element) { 475 Status status = ScrollElementIntoView( 476 session, web_view, element_id, &location); 477 if (status.IsError()) 478 return status; 479 } else { 480 location = session->mouse_position; 481 } 482 483 if (has_offset) { 484 location.Offset(x_offset, y_offset); 485 } else { 486 WebSize size; 487 Status status = GetElementSize(session, web_view, element_id, &size); 488 if (status.IsError()) 489 return status; 490 location.Offset(size.width / 2, size.height / 2); 491 } 492 493 std::list<MouseEvent> events; 494 events.push_back( 495 MouseEvent(kMovedMouseEventType, kNoneMouseButton, 496 location.x, location.y, session->sticky_modifiers, 0)); 497 Status status = 498 web_view->DispatchMouseEvents(events, session->GetCurrentFrameId()); 499 if (status.IsOk()) 500 session->mouse_position = location; 501 return status; 502 } 503 504 Status ExecuteMouseClick( 505 Session* session, 506 WebView* web_view, 507 const base::DictionaryValue& params, 508 scoped_ptr<base::Value>* value) { 509 MouseButton button; 510 Status status = GetMouseButton(params, &button); 511 if (status.IsError()) 512 return status; 513 std::list<MouseEvent> events; 514 events.push_back( 515 MouseEvent(kPressedMouseEventType, button, 516 session->mouse_position.x, session->mouse_position.y, 517 session->sticky_modifiers, 1)); 518 events.push_back( 519 MouseEvent(kReleasedMouseEventType, button, 520 session->mouse_position.x, session->mouse_position.y, 521 session->sticky_modifiers, 1)); 522 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId()); 523 } 524 525 Status ExecuteMouseButtonDown( 526 Session* session, 527 WebView* web_view, 528 const base::DictionaryValue& params, 529 scoped_ptr<base::Value>* value) { 530 MouseButton button; 531 Status status = GetMouseButton(params, &button); 532 if (status.IsError()) 533 return status; 534 std::list<MouseEvent> events; 535 events.push_back( 536 MouseEvent(kPressedMouseEventType, button, 537 session->mouse_position.x, session->mouse_position.y, 538 session->sticky_modifiers, 1)); 539 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId()); 540 } 541 542 Status ExecuteMouseButtonUp( 543 Session* session, 544 WebView* web_view, 545 const base::DictionaryValue& params, 546 scoped_ptr<base::Value>* value) { 547 MouseButton button; 548 Status status = GetMouseButton(params, &button); 549 if (status.IsError()) 550 return status; 551 std::list<MouseEvent> events; 552 events.push_back( 553 MouseEvent(kReleasedMouseEventType, button, 554 session->mouse_position.x, session->mouse_position.y, 555 session->sticky_modifiers, 1)); 556 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId()); 557 } 558 559 Status ExecuteMouseDoubleClick( 560 Session* session, 561 WebView* web_view, 562 const base::DictionaryValue& params, 563 scoped_ptr<base::Value>* value) { 564 MouseButton button; 565 Status status = GetMouseButton(params, &button); 566 if (status.IsError()) 567 return status; 568 std::list<MouseEvent> events; 569 events.push_back( 570 MouseEvent(kPressedMouseEventType, button, 571 session->mouse_position.x, session->mouse_position.y, 572 session->sticky_modifiers, 2)); 573 events.push_back( 574 MouseEvent(kReleasedMouseEventType, button, 575 session->mouse_position.x, session->mouse_position.y, 576 session->sticky_modifiers, 2)); 577 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId()); 578 } 579 580 Status ExecuteTouchDown( 581 Session* session, 582 WebView* web_view, 583 const base::DictionaryValue& params, 584 scoped_ptr<base::Value>* value) { 585 return ExecuteTouchEvent(session, web_view, kTouchStart, params); 586 } 587 588 Status ExecuteTouchUp( 589 Session* session, 590 WebView* web_view, 591 const base::DictionaryValue& params, 592 scoped_ptr<base::Value>* value) { 593 return ExecuteTouchEvent(session, web_view, kTouchEnd, params); 594 } 595 596 Status ExecuteTouchMove( 597 Session* session, 598 WebView* web_view, 599 const base::DictionaryValue& params, 600 scoped_ptr<base::Value>* value) { 601 return ExecuteTouchEvent(session, web_view, kTouchMove, params); 602 } 603 604 Status ExecuteGetActiveElement( 605 Session* session, 606 WebView* web_view, 607 const base::DictionaryValue& params, 608 scoped_ptr<base::Value>* value) { 609 return GetActiveElement(session, web_view, value); 610 } 611 612 Status ExecuteSendKeysToActiveElement( 613 Session* session, 614 WebView* web_view, 615 const base::DictionaryValue& params, 616 scoped_ptr<base::Value>* value) { 617 const base::ListValue* key_list; 618 if (!params.GetList("value", &key_list)) 619 return Status(kUnknownError, "'value' must be a list"); 620 return SendKeysOnWindow( 621 web_view, key_list, false, &session->sticky_modifiers); 622 } 623 624 Status ExecuteGetAppCacheStatus( 625 Session* session, 626 WebView* web_view, 627 const base::DictionaryValue& params, 628 scoped_ptr<base::Value>* value) { 629 return web_view->EvaluateScript( 630 session->GetCurrentFrameId(), 631 "applicationCache.status", 632 value); 633 } 634 635 Status ExecuteIsBrowserOnline( 636 Session* session, 637 WebView* web_view, 638 const base::DictionaryValue& params, 639 scoped_ptr<base::Value>* value) { 640 return web_view->EvaluateScript( 641 session->GetCurrentFrameId(), 642 "navigator.onLine", 643 value); 644 } 645 646 Status ExecuteGetStorageItem( 647 const char* storage, 648 Session* session, 649 WebView* web_view, 650 const base::DictionaryValue& params, 651 scoped_ptr<base::Value>* value) { 652 std::string key; 653 if (!params.GetString("key", &key)) 654 return Status(kUnknownError, "'key' must be a string"); 655 base::ListValue args; 656 args.Append(new base::StringValue(key)); 657 return web_view->CallFunction( 658 session->GetCurrentFrameId(), 659 base::StringPrintf("function(key) { return %s[key]; }", storage), 660 args, 661 value); 662 } 663 664 Status ExecuteGetStorageKeys( 665 const char* storage, 666 Session* session, 667 WebView* web_view, 668 const base::DictionaryValue& params, 669 scoped_ptr<base::Value>* value) { 670 const char script[] = 671 "var keys = [];" 672 "for (var key in %s) {" 673 " keys.push(key);" 674 "}" 675 "keys"; 676 return web_view->EvaluateScript( 677 session->GetCurrentFrameId(), 678 base::StringPrintf(script, storage), 679 value); 680 } 681 682 Status ExecuteSetStorageItem( 683 const char* storage, 684 Session* session, 685 WebView* web_view, 686 const base::DictionaryValue& params, 687 scoped_ptr<base::Value>* value) { 688 std::string key; 689 if (!params.GetString("key", &key)) 690 return Status(kUnknownError, "'key' must be a string"); 691 std::string storage_value; 692 if (!params.GetString("value", &storage_value)) 693 return Status(kUnknownError, "'value' must be a string"); 694 base::ListValue args; 695 args.Append(new base::StringValue(key)); 696 args.Append(new base::StringValue(storage_value)); 697 return web_view->CallFunction( 698 session->GetCurrentFrameId(), 699 base::StringPrintf("function(key, value) { %s[key] = value; }", storage), 700 args, 701 value); 702 } 703 704 Status ExecuteRemoveStorageItem( 705 const char* storage, 706 Session* session, 707 WebView* web_view, 708 const base::DictionaryValue& params, 709 scoped_ptr<base::Value>* value) { 710 std::string key; 711 if (!params.GetString("key", &key)) 712 return Status(kUnknownError, "'key' must be a string"); 713 base::ListValue args; 714 args.Append(new base::StringValue(key)); 715 return web_view->CallFunction( 716 session->GetCurrentFrameId(), 717 base::StringPrintf("function(key) { %s.removeItem(key) }", storage), 718 args, 719 value); 720 } 721 722 Status ExecuteClearStorage( 723 const char* storage, 724 Session* session, 725 WebView* web_view, 726 const base::DictionaryValue& params, 727 scoped_ptr<base::Value>* value) { 728 return web_view->EvaluateScript( 729 session->GetCurrentFrameId(), 730 base::StringPrintf("%s.clear()", storage), 731 value); 732 } 733 734 Status ExecuteGetStorageSize( 735 const char* storage, 736 Session* session, 737 WebView* web_view, 738 const base::DictionaryValue& params, 739 scoped_ptr<base::Value>* value) { 740 return web_view->EvaluateScript( 741 session->GetCurrentFrameId(), 742 base::StringPrintf("%s.length", storage), 743 value); 744 } 745 746 Status ExecuteScreenshot( 747 Session* session, 748 WebView* web_view, 749 const base::DictionaryValue& params, 750 scoped_ptr<base::Value>* value) { 751 Status status = session->chrome->ActivateWebView(web_view->GetId()); 752 if (status.IsError()) 753 return status; 754 755 std::string screenshot; 756 if (session->chrome->GetAsDesktop() && !session->force_devtools_screenshot) { 757 AutomationExtension* extension = NULL; 758 status = 759 session->chrome->GetAsDesktop()->GetAutomationExtension(&extension); 760 if (status.IsError()) 761 return status; 762 status = extension->CaptureScreenshot(&screenshot); 763 // If the screenshot was forbidden, fallback to DevTools. 764 if (status.code() == kForbidden) 765 status = web_view->CaptureScreenshot(&screenshot); 766 } else { 767 status = web_view->CaptureScreenshot(&screenshot); 768 } 769 if (status.IsError()) 770 return status; 771 772 value->reset(new base::StringValue(screenshot)); 773 return Status(kOk); 774 } 775 776 Status ExecuteGetCookies( 777 Session* session, 778 WebView* web_view, 779 const base::DictionaryValue& params, 780 scoped_ptr<base::Value>* value) { 781 std::list<Cookie> cookies; 782 Status status = GetVisibleCookies(web_view, &cookies); 783 if (status.IsError()) 784 return status; 785 scoped_ptr<base::ListValue> cookie_list(new base::ListValue()); 786 for (std::list<Cookie>::const_iterator it = cookies.begin(); 787 it != cookies.end(); ++it) { 788 cookie_list->Append(CreateDictionaryFrom(*it)); 789 } 790 value->reset(cookie_list.release()); 791 return Status(kOk); 792 } 793 794 Status ExecuteAddCookie( 795 Session* session, 796 WebView* web_view, 797 const base::DictionaryValue& params, 798 scoped_ptr<base::Value>* value) { 799 const base::DictionaryValue* cookie; 800 if (!params.GetDictionary("cookie", &cookie)) 801 return Status(kUnknownError, "missing 'cookie'"); 802 base::ListValue args; 803 args.Append(cookie->DeepCopy()); 804 scoped_ptr<base::Value> result; 805 return web_view->CallFunction( 806 session->GetCurrentFrameId(), kAddCookieScript, args, &result); 807 } 808 809 Status ExecuteDeleteCookie( 810 Session* session, 811 WebView* web_view, 812 const base::DictionaryValue& params, 813 scoped_ptr<base::Value>* value) { 814 std::string name; 815 if (!params.GetString("name", &name)) 816 return Status(kUnknownError, "missing 'name'"); 817 base::DictionaryValue params_url; 818 scoped_ptr<base::Value> value_url; 819 std::string url; 820 Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url); 821 if (status.IsError()) 822 return status; 823 return web_view->DeleteCookie(name, url); 824 } 825 826 Status ExecuteDeleteAllCookies( 827 Session* session, 828 WebView* web_view, 829 const base::DictionaryValue& params, 830 scoped_ptr<base::Value>* value) { 831 std::list<Cookie> cookies; 832 Status status = GetVisibleCookies(web_view, &cookies); 833 if (status.IsError()) 834 return status; 835 836 if (!cookies.empty()) { 837 base::DictionaryValue params_url; 838 scoped_ptr<base::Value> value_url; 839 std::string url; 840 status = GetUrl(web_view, session->GetCurrentFrameId(), &url); 841 if (status.IsError()) 842 return status; 843 for (std::list<Cookie>::const_iterator it = cookies.begin(); 844 it != cookies.end(); ++it) { 845 status = web_view->DeleteCookie(it->name, url); 846 if (status.IsError()) 847 return status; 848 } 849 } 850 851 return Status(kOk); 852 } 853 854 Status ExecuteSetLocation( 855 Session* session, 856 WebView* web_view, 857 const base::DictionaryValue& params, 858 scoped_ptr<base::Value>* value) { 859 const base::DictionaryValue* location = NULL; 860 Geoposition geoposition; 861 if (!params.GetDictionary("location", &location) || 862 !location->GetDouble("latitude", &geoposition.latitude) || 863 !location->GetDouble("longitude", &geoposition.longitude)) 864 return Status(kUnknownError, "missing or invalid 'location'"); 865 if (location->HasKey("accuracy") && 866 !location->GetDouble("accuracy", &geoposition.accuracy)) { 867 return Status(kUnknownError, "invalid 'accuracy'"); 868 } else { 869 // |accuracy| is not part of the WebDriver spec yet, so if it is not given 870 // default to 100 meters accuracy. 871 geoposition.accuracy = 100; 872 } 873 874 Status status = web_view->OverrideGeolocation(geoposition); 875 if (status.IsOk()) 876 session->overridden_geoposition.reset(new Geoposition(geoposition)); 877 return status; 878 } 879 880 Status ExecuteTakeHeapSnapshot( 881 Session* session, 882 WebView* web_view, 883 const base::DictionaryValue& params, 884 scoped_ptr<base::Value>* value) { 885 return web_view->TakeHeapSnapshot(value); 886 } 887