Update CrOS OOBE throbber to MD throbber; delete old asset
[chromium-blink-merge.git] / chrome / test / chromedriver / window_commands.cc
blob91305fb0937b7a9a92cd61860b347554375d207a
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/window_commands.h"
7 #include <list>
8 #include <string>
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/browser_info.h"
19 #include "chrome/test/chromedriver/chrome/chrome.h"
20 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
21 #include "chrome/test/chromedriver/chrome/devtools_client.h"
22 #include "chrome/test/chromedriver/chrome/geoposition.h"
23 #include "chrome/test/chromedriver/chrome/javascript_dialog_manager.h"
24 #include "chrome/test/chromedriver/chrome/js.h"
25 #include "chrome/test/chromedriver/chrome/network_conditions.h"
26 #include "chrome/test/chromedriver/chrome/status.h"
27 #include "chrome/test/chromedriver/chrome/ui_events.h"
28 #include "chrome/test/chromedriver/chrome/web_view.h"
29 #include "chrome/test/chromedriver/element_util.h"
30 #include "chrome/test/chromedriver/session.h"
31 #include "chrome/test/chromedriver/util.h"
33 namespace {
35 Status GetMouseButton(const base::DictionaryValue& params,
36 MouseButton* button) {
37 int button_num;
38 if (!params.GetInteger("button", &button_num)) {
39 button_num = 0; // Default to left mouse button.
40 } else if (button_num < 0 || button_num > 2) {
41 return Status(kUnknownError,
42 base::StringPrintf("invalid button: %d", button_num));
44 *button = static_cast<MouseButton>(button_num);
45 return Status(kOk);
48 Status GetUrl(WebView* web_view, const std::string& frame, std::string* url) {
49 scoped_ptr<base::Value> value;
50 base::ListValue args;
51 Status status = web_view->CallFunction(
52 frame, "function() { return document.URL; }", args, &value);
53 if (status.IsError())
54 return status;
55 if (!value->GetAsString(url))
56 return Status(kUnknownError, "javascript failed to return the url");
57 return Status(kOk);
60 struct Cookie {
61 Cookie(const std::string& name,
62 const std::string& value,
63 const std::string& domain,
64 const std::string& path,
65 double expiry,
66 bool http_only,
67 bool secure,
68 bool session)
69 : name(name), value(value), domain(domain), path(path), expiry(expiry),
70 http_only(http_only), secure(secure), session(session) {}
72 std::string name;
73 std::string value;
74 std::string domain;
75 std::string path;
76 double expiry;
77 bool http_only;
78 bool secure;
79 bool session;
82 base::DictionaryValue* CreateDictionaryFrom(const Cookie& cookie) {
83 base::DictionaryValue* dict = new base::DictionaryValue();
84 dict->SetString("name", cookie.name);
85 dict->SetString("value", cookie.value);
86 if (!cookie.domain.empty())
87 dict->SetString("domain", cookie.domain);
88 if (!cookie.path.empty())
89 dict->SetString("path", cookie.path);
90 if (!cookie.session)
91 dict->SetDouble("expiry", cookie.expiry);
92 dict->SetBoolean("httpOnly", cookie.http_only);
93 dict->SetBoolean("secure", cookie.secure);
94 return dict;
97 Status GetVisibleCookies(WebView* web_view,
98 std::list<Cookie>* cookies) {
99 scoped_ptr<base::ListValue> internal_cookies;
100 Status status = web_view->GetCookies(&internal_cookies);
101 if (status.IsError())
102 return status;
103 std::list<Cookie> cookies_tmp;
104 for (size_t i = 0; i < internal_cookies->GetSize(); ++i) {
105 base::DictionaryValue* cookie_dict;
106 if (!internal_cookies->GetDictionary(i, &cookie_dict))
107 return Status(kUnknownError, "DevTools returns a non-dictionary cookie");
109 std::string name;
110 cookie_dict->GetString("name", &name);
111 std::string value;
112 cookie_dict->GetString("value", &value);
113 std::string domain;
114 cookie_dict->GetString("domain", &domain);
115 std::string path;
116 cookie_dict->GetString("path", &path);
117 double expiry = 0;
118 cookie_dict->GetDouble("expires", &expiry);
119 expiry /= 1000; // Convert from millisecond to second.
120 bool http_only = false;
121 cookie_dict->GetBoolean("httpOnly", &http_only);
122 bool session = false;
123 cookie_dict->GetBoolean("session", &session);
124 bool secure = false;
125 cookie_dict->GetBoolean("secure", &secure);
127 cookies_tmp.push_back(
128 Cookie(name, value, domain, path, expiry, http_only, secure, session));
130 cookies->swap(cookies_tmp);
131 return Status(kOk);
134 Status ScrollCoordinateInToView(
135 Session* session, WebView* web_view, int x, int y, int* offset_x,
136 int* offset_y) {
137 scoped_ptr<base::Value> value;
138 base::ListValue args;
139 args.AppendInteger(x);
140 args.AppendInteger(y);
141 Status status = web_view->CallFunction(
142 std::string(),
143 "function(x, y) {"
144 " if (x < window.pageXOffset ||"
145 " x >= window.pageXOffset + window.innerWidth ||"
146 " y < window.pageYOffset ||"
147 " y >= window.pageYOffset + window.innerHeight) {"
148 " window.scrollTo(x - window.innerWidth/2, y - window.innerHeight/2);"
149 " }"
150 " return {"
151 " view_x: Math.floor(window.pageXOffset),"
152 " view_y: Math.floor(window.pageYOffset),"
153 " view_width: Math.floor(window.innerWidth),"
154 " view_height: Math.floor(window.innerHeight)};"
155 "}",
156 args,
157 &value);
158 if (!status.IsOk())
159 return status;
160 base::DictionaryValue* view_attrib;
161 value->GetAsDictionary(&view_attrib);
162 int view_x, view_y, view_width, view_height;
163 view_attrib->GetInteger("view_x", &view_x);
164 view_attrib->GetInteger("view_y", &view_y);
165 view_attrib->GetInteger("view_width", &view_width);
166 view_attrib->GetInteger("view_height", &view_height);
167 *offset_x = x - view_x;
168 *offset_y = y - view_y;
169 if (*offset_x < 0 || *offset_x >= view_width || *offset_y < 0 ||
170 *offset_y >= view_height)
171 return Status(kUnknownError, "Failed to scroll coordinate into view");
172 return Status(kOk);
175 Status ExecuteTouchEvent(
176 Session* session, WebView* web_view, TouchEventType type,
177 const base::DictionaryValue& params) {
178 int x, y;
179 if (!params.GetInteger("x", &x))
180 return Status(kUnknownError, "'x' must be an integer");
181 if (!params.GetInteger("y", &y))
182 return Status(kUnknownError, "'y' must be an integer");
183 int relative_x = x;
184 int relative_y = y;
185 Status status = ScrollCoordinateInToView(
186 session, web_view, x, y, &relative_x, &relative_y);
187 if (!status.IsOk())
188 return status;
189 std::list<TouchEvent> events;
190 events.push_back(
191 TouchEvent(type, relative_x, relative_y));
192 return web_view->DispatchTouchEvents(events);
195 } // namespace
197 Status ExecuteWindowCommand(
198 const WindowCommand& command,
199 Session* session,
200 const base::DictionaryValue& params,
201 scoped_ptr<base::Value>* value) {
202 WebView* web_view = NULL;
203 Status status = session->GetTargetWindow(&web_view);
204 if (status.IsError())
205 return status;
207 status = web_view->ConnectIfNecessary();
208 if (status.IsError())
209 return status;
211 status = web_view->HandleReceivedEvents();
212 if (status.IsError())
213 return status;
215 if (web_view->GetJavaScriptDialogManager()->IsDialogOpen())
216 return Status(kUnexpectedAlertOpen);
218 Status nav_status(kOk);
219 for (int attempt = 0; attempt < 3; attempt++) {
220 if (attempt == 2) {
221 // Switch to main frame and retry command if subframe no longer exists.
222 session->SwitchToTopFrame();
225 nav_status = web_view->WaitForPendingNavigations(
226 session->GetCurrentFrameId(), session->page_load_timeout, true);
227 if (nav_status.IsError())
228 return nav_status;
230 status = command.Run(session, web_view, params, value);
231 if (status.code() == kNoSuchExecutionContext) {
232 continue;
233 } else if (status.IsError()) {
234 // If the command failed while a new page or frame started loading, retry
235 // the command after the pending navigation has completed.
236 bool is_pending = false;
237 nav_status = web_view->IsPendingNavigation(session->GetCurrentFrameId(),
238 &is_pending);
239 if (nav_status.IsError())
240 return nav_status;
241 else if (is_pending)
242 continue;
244 break;
247 nav_status = web_view->WaitForPendingNavigations(
248 session->GetCurrentFrameId(), session->page_load_timeout, true);
250 if (status.IsOk() && nav_status.IsError() &&
251 nav_status.code() != kUnexpectedAlertOpen)
252 return nav_status;
253 if (status.code() == kUnexpectedAlertOpen)
254 return Status(kOk);
255 return status;
258 Status ExecuteGet(
259 Session* session,
260 WebView* web_view,
261 const base::DictionaryValue& params,
262 scoped_ptr<base::Value>* value) {
263 std::string url;
264 if (!params.GetString("url", &url))
265 return Status(kUnknownError, "'url' must be a string");
266 Status status = web_view->Load(url);
267 if (status.IsError())
268 return status;
269 session->SwitchToTopFrame();
270 return Status(kOk);
273 Status ExecuteExecuteScript(
274 Session* session,
275 WebView* web_view,
276 const base::DictionaryValue& params,
277 scoped_ptr<base::Value>* value) {
278 std::string script;
279 if (!params.GetString("script", &script))
280 return Status(kUnknownError, "'script' must be a string");
281 if (script == ":takeHeapSnapshot") {
282 return web_view->TakeHeapSnapshot(value);
283 } else if (script == ":startProfile") {
284 return web_view->StartProfile();
285 } else if (script == ":endProfile") {
286 return web_view->EndProfile(value);
287 } else {
288 const base::ListValue* args;
289 if (!params.GetList("args", &args))
290 return Status(kUnknownError, "'args' must be a list");
292 return web_view->CallFunction(session->GetCurrentFrameId(),
293 "function(){" + script + "}", *args, value);
297 Status ExecuteExecuteAsyncScript(
298 Session* session,
299 WebView* web_view,
300 const base::DictionaryValue& params,
301 scoped_ptr<base::Value>* value) {
302 std::string script;
303 if (!params.GetString("script", &script))
304 return Status(kUnknownError, "'script' must be a string");
305 const base::ListValue* args;
306 if (!params.GetList("args", &args))
307 return Status(kUnknownError, "'args' must be a list");
309 return web_view->CallUserAsyncFunction(
310 session->GetCurrentFrameId(), "function(){" + script + "}", *args,
311 session->script_timeout, value);
314 Status ExecuteSwitchToFrame(
315 Session* session,
316 WebView* web_view,
317 const base::DictionaryValue& params,
318 scoped_ptr<base::Value>* value) {
319 const base::Value* id;
320 if (!params.Get("id", &id))
321 return Status(kUnknownError, "missing 'id'");
323 if (id->IsType(base::Value::TYPE_NULL)) {
324 session->SwitchToTopFrame();
325 return Status(kOk);
328 std::string script;
329 base::ListValue args;
330 const base::DictionaryValue* id_dict;
331 if (id->GetAsDictionary(&id_dict)) {
332 script = "function(elem) { return elem; }";
333 args.Append(id_dict->DeepCopy());
334 } else {
335 script =
336 "function(xpath) {"
337 " return document.evaluate(xpath, document, null, "
338 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
339 "}";
340 std::string xpath = "(/html/body//iframe|/html/frameset/frame)";
341 std::string id_string;
342 int id_int;
343 if (id->GetAsString(&id_string)) {
344 xpath += base::StringPrintf(
345 "[@name=\"%s\" or @id=\"%s\"]", id_string.c_str(), id_string.c_str());
346 } else if (id->GetAsInteger(&id_int)) {
347 xpath += base::StringPrintf("[%d]", id_int + 1);
348 } else {
349 return Status(kUnknownError, "invalid 'id'");
351 args.Append(new base::StringValue(xpath));
353 std::string frame;
354 Status status = web_view->GetFrameByFunction(
355 session->GetCurrentFrameId(), script, args, &frame);
356 if (status.IsError())
357 return status;
359 scoped_ptr<base::Value> result;
360 status = web_view->CallFunction(
361 session->GetCurrentFrameId(), script, args, &result);
362 if (status.IsError())
363 return status;
364 const base::DictionaryValue* element;
365 if (!result->GetAsDictionary(&element))
366 return Status(kUnknownError, "fail to locate the sub frame element");
368 std::string chrome_driver_id = GenerateId();
369 const char kSetFrameIdentifier[] =
370 "function(frame, id) {"
371 " frame.setAttribute('cd_frame_id_', id);"
372 "}";
373 base::ListValue new_args;
374 new_args.Append(element->DeepCopy());
375 new_args.AppendString(chrome_driver_id);
376 result.reset(NULL);
377 status = web_view->CallFunction(
378 session->GetCurrentFrameId(), kSetFrameIdentifier, new_args, &result);
379 if (status.IsError())
380 return status;
381 session->SwitchToSubFrame(frame, chrome_driver_id);
382 return Status(kOk);
385 Status ExecuteSwitchToParentFrame(
386 Session* session,
387 WebView* web_view,
388 const base::DictionaryValue& params,
389 scoped_ptr<base::Value>* value) {
390 session->SwitchToParentFrame();
391 return Status(kOk);
394 Status ExecuteGetTitle(
395 Session* session,
396 WebView* web_view,
397 const base::DictionaryValue& params,
398 scoped_ptr<base::Value>* value) {
399 const char kGetTitleScript[] =
400 "function() {"
401 " if (document.title)"
402 " return document.title;"
403 " else"
404 " return document.URL;"
405 "}";
406 base::ListValue args;
407 return web_view->CallFunction(std::string(), kGetTitleScript, args, value);
410 Status ExecuteGetPageSource(
411 Session* session,
412 WebView* web_view,
413 const base::DictionaryValue& params,
414 scoped_ptr<base::Value>* value) {
415 const char kGetPageSource[] =
416 "function() {"
417 " return new XMLSerializer().serializeToString(document);"
418 "}";
419 base::ListValue args;
420 return web_view->CallFunction(
421 session->GetCurrentFrameId(), kGetPageSource, args, value);
424 Status ExecuteFindElement(
425 int interval_ms,
426 Session* session,
427 WebView* web_view,
428 const base::DictionaryValue& params,
429 scoped_ptr<base::Value>* value) {
430 return FindElement(interval_ms, true, NULL, session, web_view, params, value);
433 Status ExecuteFindElements(
434 int interval_ms,
435 Session* session,
436 WebView* web_view,
437 const base::DictionaryValue& params,
438 scoped_ptr<base::Value>* value) {
439 return FindElement(
440 interval_ms, false, NULL, session, web_view, params, value);
443 Status ExecuteGetCurrentUrl(
444 Session* session,
445 WebView* web_view,
446 const base::DictionaryValue& params,
447 scoped_ptr<base::Value>* value) {
448 std::string url;
449 Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
450 if (status.IsError())
451 return status;
452 value->reset(new base::StringValue(url));
453 return Status(kOk);
456 Status ExecuteGoBack(
457 Session* session,
458 WebView* web_view,
459 const base::DictionaryValue& params,
460 scoped_ptr<base::Value>* value) {
461 Status status = web_view->TraverseHistory(-1);
462 if (status.IsError())
463 return status;
464 session->SwitchToTopFrame();
465 return Status(kOk);
468 Status ExecuteGoForward(
469 Session* session,
470 WebView* web_view,
471 const base::DictionaryValue& params,
472 scoped_ptr<base::Value>* value) {
473 Status status = web_view->TraverseHistory(1);
474 if (status.IsError())
475 return status;
476 session->SwitchToTopFrame();
477 return Status(kOk);
480 Status ExecuteRefresh(
481 Session* session,
482 WebView* web_view,
483 const base::DictionaryValue& params,
484 scoped_ptr<base::Value>* value) {
485 Status status = web_view->Reload();
486 if (status.IsError())
487 return status;
488 session->SwitchToTopFrame();
489 return Status(kOk);
492 Status ExecuteMouseMoveTo(
493 Session* session,
494 WebView* web_view,
495 const base::DictionaryValue& params,
496 scoped_ptr<base::Value>* value) {
497 std::string element_id;
498 bool has_element = params.GetString("element", &element_id);
499 int x_offset = 0;
500 int y_offset = 0;
501 bool has_offset = params.GetInteger("xoffset", &x_offset) &&
502 params.GetInteger("yoffset", &y_offset);
503 if (!has_element && !has_offset)
504 return Status(kUnknownError, "at least an element or offset should be set");
506 WebPoint location;
507 if (has_element) {
508 WebPoint offset(x_offset, y_offset);
509 Status status = ScrollElementIntoView(session, web_view, element_id,
510 has_offset ? &offset : nullptr, &location);
511 if (status.IsError())
512 return status;
513 } else {
514 location = session->mouse_position;
515 if (has_offset)
516 location.Offset(x_offset, y_offset);
519 std::list<MouseEvent> events;
520 events.push_back(
521 MouseEvent(kMovedMouseEventType, kNoneMouseButton,
522 location.x, location.y, session->sticky_modifiers, 0));
523 Status status =
524 web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
525 if (status.IsOk())
526 session->mouse_position = location;
527 return status;
530 Status ExecuteMouseClick(
531 Session* session,
532 WebView* web_view,
533 const base::DictionaryValue& params,
534 scoped_ptr<base::Value>* value) {
535 MouseButton button;
536 Status status = GetMouseButton(params, &button);
537 if (status.IsError())
538 return status;
539 std::list<MouseEvent> events;
540 events.push_back(
541 MouseEvent(kPressedMouseEventType, button,
542 session->mouse_position.x, session->mouse_position.y,
543 session->sticky_modifiers, 1));
544 events.push_back(
545 MouseEvent(kReleasedMouseEventType, button,
546 session->mouse_position.x, session->mouse_position.y,
547 session->sticky_modifiers, 1));
548 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
551 Status ExecuteMouseButtonDown(
552 Session* session,
553 WebView* web_view,
554 const base::DictionaryValue& params,
555 scoped_ptr<base::Value>* value) {
556 MouseButton button;
557 Status status = GetMouseButton(params, &button);
558 if (status.IsError())
559 return status;
560 std::list<MouseEvent> events;
561 events.push_back(
562 MouseEvent(kPressedMouseEventType, button,
563 session->mouse_position.x, session->mouse_position.y,
564 session->sticky_modifiers, 1));
565 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
568 Status ExecuteMouseButtonUp(
569 Session* session,
570 WebView* web_view,
571 const base::DictionaryValue& params,
572 scoped_ptr<base::Value>* value) {
573 MouseButton button;
574 Status status = GetMouseButton(params, &button);
575 if (status.IsError())
576 return status;
577 std::list<MouseEvent> events;
578 events.push_back(
579 MouseEvent(kReleasedMouseEventType, button,
580 session->mouse_position.x, session->mouse_position.y,
581 session->sticky_modifiers, 1));
582 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
585 Status ExecuteMouseDoubleClick(
586 Session* session,
587 WebView* web_view,
588 const base::DictionaryValue& params,
589 scoped_ptr<base::Value>* value) {
590 MouseButton button;
591 Status status = GetMouseButton(params, &button);
592 if (status.IsError())
593 return status;
594 std::list<MouseEvent> events;
595 events.push_back(
596 MouseEvent(kPressedMouseEventType, button,
597 session->mouse_position.x, session->mouse_position.y,
598 session->sticky_modifiers, 2));
599 events.push_back(
600 MouseEvent(kReleasedMouseEventType, button,
601 session->mouse_position.x, session->mouse_position.y,
602 session->sticky_modifiers, 2));
603 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
606 Status ExecuteTouchDown(
607 Session* session,
608 WebView* web_view,
609 const base::DictionaryValue& params,
610 scoped_ptr<base::Value>* value) {
611 return ExecuteTouchEvent(session, web_view, kTouchStart, params);
614 Status ExecuteTouchUp(
615 Session* session,
616 WebView* web_view,
617 const base::DictionaryValue& params,
618 scoped_ptr<base::Value>* value) {
619 return ExecuteTouchEvent(session, web_view, kTouchEnd, params);
622 Status ExecuteTouchMove(
623 Session* session,
624 WebView* web_view,
625 const base::DictionaryValue& params,
626 scoped_ptr<base::Value>* value) {
627 return ExecuteTouchEvent(session, web_view, kTouchMove, params);
630 Status ExecuteTouchScroll(
631 Session *session,
632 WebView* web_view,
633 const base::DictionaryValue& params,
634 scoped_ptr<base::Value>* value) {
635 if (session->chrome->GetBrowserInfo()->build_no < 2286) {
636 // TODO(samuong): remove this once we stop supporting M41.
637 return Status(kUnknownCommand, "Touch scroll action requires Chrome 42+");
639 WebPoint location = session->mouse_position;
640 std::string element;
641 if (params.GetString("element", &element)) {
642 Status status = GetElementClickableLocation(
643 session, web_view, element, &location);
644 if (status.IsError())
645 return status;
647 int xoffset;
648 if (!params.GetInteger("xoffset", &xoffset))
649 return Status(kUnknownError, "'xoffset' must be an integer");
650 int yoffset;
651 if (!params.GetInteger("yoffset", &yoffset))
652 return Status(kUnknownError, "'yoffset' must be an integer");
653 return web_view->SynthesizeScrollGesture(
654 location.x, location.y, xoffset, yoffset);
657 Status ExecuteTouchPinch(
658 Session* session,
659 WebView* web_view,
660 const base::DictionaryValue& params,
661 scoped_ptr<base::Value>* value) {
662 if (session->chrome->GetBrowserInfo()->build_no < 2286) {
663 // TODO(samuong): remove this once we stop supporting M41.
664 return Status(kUnknownCommand, "Pinch action requires Chrome 42+");
666 WebPoint location;
667 if (!params.GetInteger("x", &location.x))
668 return Status(kUnknownError, "'x' must be an integer");
669 if (!params.GetInteger("y", &location.y))
670 return Status(kUnknownError, "'y' must be an integer");
671 double scale_factor;
672 if (!params.GetDouble("scale", &scale_factor))
673 return Status(kUnknownError, "'scale' must be an integer");
674 return web_view->SynthesizePinchGesture(location.x, location.y, scale_factor);
677 Status ExecuteGetActiveElement(
678 Session* session,
679 WebView* web_view,
680 const base::DictionaryValue& params,
681 scoped_ptr<base::Value>* value) {
682 return GetActiveElement(session, web_view, value);
685 Status ExecuteSendKeysToActiveElement(
686 Session* session,
687 WebView* web_view,
688 const base::DictionaryValue& params,
689 scoped_ptr<base::Value>* value) {
690 const base::ListValue* key_list;
691 if (!params.GetList("value", &key_list))
692 return Status(kUnknownError, "'value' must be a list");
693 return SendKeysOnWindow(
694 web_view, key_list, false, &session->sticky_modifiers);
697 Status ExecuteGetAppCacheStatus(
698 Session* session,
699 WebView* web_view,
700 const base::DictionaryValue& params,
701 scoped_ptr<base::Value>* value) {
702 return web_view->EvaluateScript(
703 session->GetCurrentFrameId(),
704 "applicationCache.status",
705 value);
708 Status ExecuteIsBrowserOnline(
709 Session* session,
710 WebView* web_view,
711 const base::DictionaryValue& params,
712 scoped_ptr<base::Value>* value) {
713 return web_view->EvaluateScript(
714 session->GetCurrentFrameId(),
715 "navigator.onLine",
716 value);
719 Status ExecuteGetStorageItem(
720 const char* storage,
721 Session* session,
722 WebView* web_view,
723 const base::DictionaryValue& params,
724 scoped_ptr<base::Value>* value) {
725 std::string key;
726 if (!params.GetString("key", &key))
727 return Status(kUnknownError, "'key' must be a string");
728 base::ListValue args;
729 args.Append(new base::StringValue(key));
730 return web_view->CallFunction(
731 session->GetCurrentFrameId(),
732 base::StringPrintf("function(key) { return %s[key]; }", storage),
733 args,
734 value);
737 Status ExecuteGetStorageKeys(
738 const char* storage,
739 Session* session,
740 WebView* web_view,
741 const base::DictionaryValue& params,
742 scoped_ptr<base::Value>* value) {
743 const char script[] =
744 "var keys = [];"
745 "for (var key in %s) {"
746 " keys.push(key);"
748 "keys";
749 return web_view->EvaluateScript(
750 session->GetCurrentFrameId(),
751 base::StringPrintf(script, storage),
752 value);
755 Status ExecuteSetStorageItem(
756 const char* storage,
757 Session* session,
758 WebView* web_view,
759 const base::DictionaryValue& params,
760 scoped_ptr<base::Value>* value) {
761 std::string key;
762 if (!params.GetString("key", &key))
763 return Status(kUnknownError, "'key' must be a string");
764 std::string storage_value;
765 if (!params.GetString("value", &storage_value))
766 return Status(kUnknownError, "'value' must be a string");
767 base::ListValue args;
768 args.Append(new base::StringValue(key));
769 args.Append(new base::StringValue(storage_value));
770 return web_view->CallFunction(
771 session->GetCurrentFrameId(),
772 base::StringPrintf("function(key, value) { %s[key] = value; }", storage),
773 args,
774 value);
777 Status ExecuteRemoveStorageItem(
778 const char* storage,
779 Session* session,
780 WebView* web_view,
781 const base::DictionaryValue& params,
782 scoped_ptr<base::Value>* value) {
783 std::string key;
784 if (!params.GetString("key", &key))
785 return Status(kUnknownError, "'key' must be a string");
786 base::ListValue args;
787 args.Append(new base::StringValue(key));
788 return web_view->CallFunction(
789 session->GetCurrentFrameId(),
790 base::StringPrintf("function(key) { %s.removeItem(key) }", storage),
791 args,
792 value);
795 Status ExecuteClearStorage(
796 const char* storage,
797 Session* session,
798 WebView* web_view,
799 const base::DictionaryValue& params,
800 scoped_ptr<base::Value>* value) {
801 return web_view->EvaluateScript(
802 session->GetCurrentFrameId(),
803 base::StringPrintf("%s.clear()", storage),
804 value);
807 Status ExecuteGetStorageSize(
808 const char* storage,
809 Session* session,
810 WebView* web_view,
811 const base::DictionaryValue& params,
812 scoped_ptr<base::Value>* value) {
813 return web_view->EvaluateScript(
814 session->GetCurrentFrameId(),
815 base::StringPrintf("%s.length", storage),
816 value);
819 Status ExecuteScreenshot(
820 Session* session,
821 WebView* web_view,
822 const base::DictionaryValue& params,
823 scoped_ptr<base::Value>* value) {
824 Status status = session->chrome->ActivateWebView(web_view->GetId());
825 if (status.IsError())
826 return status;
828 std::string screenshot;
829 ChromeDesktopImpl* desktop = NULL;
830 status = session->chrome->GetAsDesktop(&desktop);
831 if (status.IsOk() && !session->force_devtools_screenshot) {
832 AutomationExtension* extension = NULL;
833 status = desktop->GetAutomationExtension(&extension);
834 if (status.IsError())
835 return status;
836 status = extension->CaptureScreenshot(&screenshot);
837 if (status.IsError()) {
838 LOG(WARNING) << "screenshot failed with extension, fallback to DevTools";
839 status = web_view->CaptureScreenshot(&screenshot);
841 } else {
842 status = web_view->CaptureScreenshot(&screenshot);
844 if (status.IsError())
845 return status;
847 value->reset(new base::StringValue(screenshot));
848 return Status(kOk);
851 Status ExecuteGetCookies(
852 Session* session,
853 WebView* web_view,
854 const base::DictionaryValue& params,
855 scoped_ptr<base::Value>* value) {
856 std::list<Cookie> cookies;
857 Status status = GetVisibleCookies(web_view, &cookies);
858 if (status.IsError())
859 return status;
860 scoped_ptr<base::ListValue> cookie_list(new base::ListValue());
861 for (std::list<Cookie>::const_iterator it = cookies.begin();
862 it != cookies.end(); ++it) {
863 cookie_list->Append(CreateDictionaryFrom(*it));
865 value->reset(cookie_list.release());
866 return Status(kOk);
869 Status ExecuteAddCookie(
870 Session* session,
871 WebView* web_view,
872 const base::DictionaryValue& params,
873 scoped_ptr<base::Value>* value) {
874 const base::DictionaryValue* cookie;
875 if (!params.GetDictionary("cookie", &cookie))
876 return Status(kUnknownError, "missing 'cookie'");
877 base::ListValue args;
878 args.Append(cookie->DeepCopy());
879 scoped_ptr<base::Value> result;
880 return web_view->CallFunction(
881 session->GetCurrentFrameId(), kAddCookieScript, args, &result);
884 Status ExecuteDeleteCookie(
885 Session* session,
886 WebView* web_view,
887 const base::DictionaryValue& params,
888 scoped_ptr<base::Value>* value) {
889 std::string name;
890 if (!params.GetString("name", &name))
891 return Status(kUnknownError, "missing 'name'");
892 base::DictionaryValue params_url;
893 scoped_ptr<base::Value> value_url;
894 std::string url;
895 Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
896 if (status.IsError())
897 return status;
898 return web_view->DeleteCookie(name, url);
901 Status ExecuteDeleteAllCookies(
902 Session* session,
903 WebView* web_view,
904 const base::DictionaryValue& params,
905 scoped_ptr<base::Value>* value) {
906 std::list<Cookie> cookies;
907 Status status = GetVisibleCookies(web_view, &cookies);
908 if (status.IsError())
909 return status;
911 if (!cookies.empty()) {
912 base::DictionaryValue params_url;
913 scoped_ptr<base::Value> value_url;
914 std::string url;
915 status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
916 if (status.IsError())
917 return status;
918 for (std::list<Cookie>::const_iterator it = cookies.begin();
919 it != cookies.end(); ++it) {
920 status = web_view->DeleteCookie(it->name, url);
921 if (status.IsError())
922 return status;
926 return Status(kOk);
929 Status ExecuteSetLocation(
930 Session* session,
931 WebView* web_view,
932 const base::DictionaryValue& params,
933 scoped_ptr<base::Value>* value) {
934 const base::DictionaryValue* location = NULL;
935 Geoposition geoposition;
936 if (!params.GetDictionary("location", &location) ||
937 !location->GetDouble("latitude", &geoposition.latitude) ||
938 !location->GetDouble("longitude", &geoposition.longitude))
939 return Status(kUnknownError, "missing or invalid 'location'");
940 if (location->HasKey("accuracy") &&
941 !location->GetDouble("accuracy", &geoposition.accuracy)) {
942 return Status(kUnknownError, "invalid 'accuracy'");
943 } else {
944 // |accuracy| is not part of the WebDriver spec yet, so if it is not given
945 // default to 100 meters accuracy.
946 geoposition.accuracy = 100;
949 Status status = web_view->OverrideGeolocation(geoposition);
950 if (status.IsOk())
951 session->overridden_geoposition.reset(new Geoposition(geoposition));
952 return status;
955 Status ExecuteSetNetworkConditions(
956 Session* session,
957 WebView* web_view,
958 const base::DictionaryValue& params,
959 scoped_ptr<base::Value>* value) {
960 std::string network_name;
961 const base::DictionaryValue* conditions = NULL;
962 scoped_ptr<NetworkConditions> network_conditions(new NetworkConditions());
963 if (params.GetString("network_name", &network_name)) {
964 // Get conditions from preset list.
965 Status status = FindPresetNetwork(network_name, network_conditions.get());
966 if (status.IsError())
967 return status;
968 } else if (params.GetDictionary("network_conditions", &conditions)) {
969 // |latency| is required.
970 if (!conditions->GetDouble("latency", &network_conditions->latency))
971 return Status(kUnknownError,
972 "invalid 'network_conditions' is missing 'latency'");
974 // Either |throughput| or the pair |download_throughput| and
975 // |upload_throughput| is required.
976 if (conditions->HasKey("throughput")) {
977 if (!conditions->GetDouble("throughput",
978 &network_conditions->download_throughput))
979 return Status(kUnknownError, "invalid 'throughput'");
980 conditions->GetDouble("throughput",
981 &network_conditions->upload_throughput);
982 } else if (conditions->HasKey("download_throughput") &&
983 conditions->HasKey("upload_throughput")) {
984 if (!conditions->GetDouble("download_throughput",
985 &network_conditions->download_throughput) ||
986 !conditions->GetDouble("upload_throughput",
987 &network_conditions->upload_throughput))
988 return Status(kUnknownError,
989 "invalid 'download_throughput' or 'upload_throughput'");
990 } else {
991 return Status(kUnknownError,
992 "invalid 'network_conditions' is missing 'throughput' or "
993 "'download_throughput'/'upload_throughput' pair");
996 // |offline| is optional.
997 if (conditions->HasKey("offline")) {
998 if (!conditions->GetBoolean("offline", &network_conditions->offline))
999 return Status(kUnknownError, "invalid 'offline'");
1000 } else {
1001 network_conditions->offline = false;
1003 } else {
1004 return Status(kUnknownError,
1005 "either 'network_conditions' or 'network_name' must be "
1006 "supplied");
1009 session->overridden_network_conditions.reset(
1010 network_conditions.release());
1011 return web_view->OverrideNetworkConditions(
1012 *session->overridden_network_conditions);
1015 Status ExecuteDeleteNetworkConditions(
1016 Session* session,
1017 WebView* web_view,
1018 const base::DictionaryValue& params,
1019 scoped_ptr<base::Value>* value) {
1020 // Chrome does not have any command to stop overriding network conditions, so
1021 // we just override the network conditions with the "No throttling" preset.
1022 NetworkConditions network_conditions;
1023 // Get conditions from preset list.
1024 Status status = FindPresetNetwork("No throttling", &network_conditions);
1025 if (status.IsError())
1026 return status;
1028 status = web_view->OverrideNetworkConditions(network_conditions);
1029 if (status.IsError())
1030 return status;
1032 // After we've successfully overridden the network conditions with
1033 // "No throttling", we can delete them from |session|.
1034 session->overridden_network_conditions.reset();
1035 return status;
1038 Status ExecuteTakeHeapSnapshot(
1039 Session* session,
1040 WebView* web_view,
1041 const base::DictionaryValue& params,
1042 scoped_ptr<base::Value>* value) {
1043 return web_view->TakeHeapSnapshot(value);