Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / chrome / test / chromedriver / window_commands.cc
blob259da82cd800b0af5c34322516d73c19749b7b08
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 secure,
67 bool session)
68 : name(name), value(value), domain(domain), path(path), expiry(expiry),
69 secure(secure), session(session) {}
71 std::string name;
72 std::string value;
73 std::string domain;
74 std::string path;
75 double expiry;
76 bool secure;
77 bool session;
80 base::DictionaryValue* CreateDictionaryFrom(const Cookie& cookie) {
81 base::DictionaryValue* dict = new base::DictionaryValue();
82 dict->SetString("name", cookie.name);
83 dict->SetString("value", cookie.value);
84 if (!cookie.domain.empty())
85 dict->SetString("domain", cookie.domain);
86 if (!cookie.path.empty())
87 dict->SetString("path", cookie.path);
88 if (!cookie.session)
89 dict->SetDouble("expiry", cookie.expiry);
90 dict->SetBoolean("secure", cookie.secure);
91 return dict;
94 Status GetVisibleCookies(WebView* web_view,
95 std::list<Cookie>* cookies) {
96 scoped_ptr<base::ListValue> internal_cookies;
97 Status status = web_view->GetCookies(&internal_cookies);
98 if (status.IsError())
99 return status;
100 std::list<Cookie> cookies_tmp;
101 for (size_t i = 0; i < internal_cookies->GetSize(); ++i) {
102 base::DictionaryValue* cookie_dict;
103 if (!internal_cookies->GetDictionary(i, &cookie_dict))
104 return Status(kUnknownError, "DevTools returns a non-dictionary cookie");
106 std::string name;
107 cookie_dict->GetString("name", &name);
108 std::string value;
109 cookie_dict->GetString("value", &value);
110 std::string domain;
111 cookie_dict->GetString("domain", &domain);
112 std::string path;
113 cookie_dict->GetString("path", &path);
114 double expiry = 0;
115 cookie_dict->GetDouble("expires", &expiry);
116 expiry /= 1000; // Convert from millisecond to second.
117 bool session = false;
118 cookie_dict->GetBoolean("session", &session);
119 bool secure = false;
120 cookie_dict->GetBoolean("secure", &secure);
122 cookies_tmp.push_back(
123 Cookie(name, value, domain, path, expiry, secure, session));
125 cookies->swap(cookies_tmp);
126 return Status(kOk);
129 Status ScrollCoordinateInToView(
130 Session* session, WebView* web_view, int x, int y, int* offset_x,
131 int* offset_y) {
132 scoped_ptr<base::Value> value;
133 base::ListValue args;
134 args.AppendInteger(x);
135 args.AppendInteger(y);
136 Status status = web_view->CallFunction(
137 std::string(),
138 "function(x, y) {"
139 " if (x < window.pageXOffset ||"
140 " x >= window.pageXOffset + window.innerWidth ||"
141 " y < window.pageYOffset ||"
142 " y >= window.pageYOffset + window.innerHeight) {"
143 " window.scrollTo(x - window.innerWidth/2, y - window.innerHeight/2);"
144 " }"
145 " return {"
146 " view_x: Math.floor(window.pageXOffset),"
147 " view_y: Math.floor(window.pageYOffset),"
148 " view_width: Math.floor(window.innerWidth),"
149 " view_height: Math.floor(window.innerHeight)};"
150 "}",
151 args,
152 &value);
153 if (!status.IsOk())
154 return status;
155 base::DictionaryValue* view_attrib;
156 value->GetAsDictionary(&view_attrib);
157 int view_x, view_y, view_width, view_height;
158 view_attrib->GetInteger("view_x", &view_x);
159 view_attrib->GetInteger("view_y", &view_y);
160 view_attrib->GetInteger("view_width", &view_width);
161 view_attrib->GetInteger("view_height", &view_height);
162 *offset_x = x - view_x;
163 *offset_y = y - view_y;
164 if (*offset_x < 0 || *offset_x >= view_width || *offset_y < 0 ||
165 *offset_y >= view_height)
166 return Status(kUnknownError, "Failed to scroll coordinate into view");
167 return Status(kOk);
170 Status ExecuteTouchEvent(
171 Session* session, WebView* web_view, TouchEventType type,
172 const base::DictionaryValue& params) {
173 int x, y;
174 if (!params.GetInteger("x", &x))
175 return Status(kUnknownError, "'x' must be an integer");
176 if (!params.GetInteger("y", &y))
177 return Status(kUnknownError, "'y' must be an integer");
178 int relative_x = x;
179 int relative_y = y;
180 Status status = ScrollCoordinateInToView(
181 session, web_view, x, y, &relative_x, &relative_y);
182 if (!status.IsOk())
183 return status;
184 std::list<TouchEvent> events;
185 events.push_back(
186 TouchEvent(type, relative_x, relative_y));
187 return web_view->DispatchTouchEvents(events);
190 } // namespace
192 Status ExecuteWindowCommand(
193 const WindowCommand& command,
194 Session* session,
195 const base::DictionaryValue& params,
196 scoped_ptr<base::Value>* value) {
197 WebView* web_view = NULL;
198 Status status = session->GetTargetWindow(&web_view);
199 if (status.IsError())
200 return status;
202 status = web_view->ConnectIfNecessary();
203 if (status.IsError())
204 return status;
206 status = web_view->HandleReceivedEvents();
207 if (status.IsError())
208 return status;
210 if (web_view->GetJavaScriptDialogManager()->IsDialogOpen())
211 return Status(kUnexpectedAlertOpen);
213 Status nav_status(kOk);
214 for (int attempt = 0; attempt < 3; attempt++) {
215 if (attempt == 2) {
216 // Switch to main frame and retry command if subframe no longer exists.
217 session->SwitchToTopFrame();
220 nav_status = web_view->WaitForPendingNavigations(
221 session->GetCurrentFrameId(), session->page_load_timeout, true);
222 if (nav_status.IsError())
223 return nav_status;
225 status = command.Run(session, web_view, params, value);
226 if (status.code() != kNoSuchExecutionContext)
227 break;
230 nav_status = web_view->WaitForPendingNavigations(
231 session->GetCurrentFrameId(), session->page_load_timeout, true);
233 if (status.IsOk() && nav_status.IsError() &&
234 nav_status.code() != kUnexpectedAlertOpen)
235 return nav_status;
236 if (status.code() == kUnexpectedAlertOpen)
237 return Status(kOk);
238 return status;
241 Status ExecuteGet(
242 Session* session,
243 WebView* web_view,
244 const base::DictionaryValue& params,
245 scoped_ptr<base::Value>* value) {
246 std::string url;
247 if (!params.GetString("url", &url))
248 return Status(kUnknownError, "'url' must be a string");
249 Status status = web_view->Load(url);
250 if (status.IsError())
251 return status;
252 session->SwitchToTopFrame();
253 return Status(kOk);
256 Status ExecuteExecuteScript(
257 Session* session,
258 WebView* web_view,
259 const base::DictionaryValue& params,
260 scoped_ptr<base::Value>* value) {
261 std::string script;
262 if (!params.GetString("script", &script))
263 return Status(kUnknownError, "'script' must be a string");
264 if (script == ":takeHeapSnapshot") {
265 return web_view->TakeHeapSnapshot(value);
266 } else if (script == ":startProfile") {
267 return web_view->StartProfile();
268 } else if (script == ":endProfile") {
269 return web_view->EndProfile(value);
270 } else {
271 const base::ListValue* args;
272 if (!params.GetList("args", &args))
273 return Status(kUnknownError, "'args' must be a list");
275 return web_view->CallFunction(session->GetCurrentFrameId(),
276 "function(){" + script + "}", *args, value);
280 Status ExecuteExecuteAsyncScript(
281 Session* session,
282 WebView* web_view,
283 const base::DictionaryValue& params,
284 scoped_ptr<base::Value>* value) {
285 std::string script;
286 if (!params.GetString("script", &script))
287 return Status(kUnknownError, "'script' must be a string");
288 const base::ListValue* args;
289 if (!params.GetList("args", &args))
290 return Status(kUnknownError, "'args' must be a list");
292 return web_view->CallUserAsyncFunction(
293 session->GetCurrentFrameId(), "function(){" + script + "}", *args,
294 session->script_timeout, value);
297 Status ExecuteSwitchToFrame(
298 Session* session,
299 WebView* web_view,
300 const base::DictionaryValue& params,
301 scoped_ptr<base::Value>* value) {
302 const base::Value* id;
303 if (!params.Get("id", &id))
304 return Status(kUnknownError, "missing 'id'");
306 if (id->IsType(base::Value::TYPE_NULL)) {
307 session->SwitchToTopFrame();
308 return Status(kOk);
311 std::string script;
312 base::ListValue args;
313 const base::DictionaryValue* id_dict;
314 if (id->GetAsDictionary(&id_dict)) {
315 script = "function(elem) { return elem; }";
316 args.Append(id_dict->DeepCopy());
317 } else {
318 script =
319 "function(xpath) {"
320 " return document.evaluate(xpath, document, null, "
321 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
322 "}";
323 std::string xpath = "(/html/body//iframe|/html/frameset/frame)";
324 std::string id_string;
325 int id_int;
326 if (id->GetAsString(&id_string)) {
327 xpath += base::StringPrintf(
328 "[@name=\"%s\" or @id=\"%s\"]", id_string.c_str(), id_string.c_str());
329 } else if (id->GetAsInteger(&id_int)) {
330 xpath += base::StringPrintf("[%d]", id_int + 1);
331 } else {
332 return Status(kUnknownError, "invalid 'id'");
334 args.Append(new base::StringValue(xpath));
336 std::string frame;
337 Status status = web_view->GetFrameByFunction(
338 session->GetCurrentFrameId(), script, args, &frame);
339 if (status.IsError())
340 return status;
342 scoped_ptr<base::Value> result;
343 status = web_view->CallFunction(
344 session->GetCurrentFrameId(), script, args, &result);
345 if (status.IsError())
346 return status;
347 const base::DictionaryValue* element;
348 if (!result->GetAsDictionary(&element))
349 return Status(kUnknownError, "fail to locate the sub frame element");
351 std::string chrome_driver_id = GenerateId();
352 const char kSetFrameIdentifier[] =
353 "function(frame, id) {"
354 " frame.setAttribute('cd_frame_id_', id);"
355 "}";
356 base::ListValue new_args;
357 new_args.Append(element->DeepCopy());
358 new_args.AppendString(chrome_driver_id);
359 result.reset(NULL);
360 status = web_view->CallFunction(
361 session->GetCurrentFrameId(), kSetFrameIdentifier, new_args, &result);
362 if (status.IsError())
363 return status;
364 session->SwitchToSubFrame(frame, chrome_driver_id);
365 return Status(kOk);
368 Status ExecuteSwitchToParentFrame(
369 Session* session,
370 WebView* web_view,
371 const base::DictionaryValue& params,
372 scoped_ptr<base::Value>* value) {
373 session->SwitchToParentFrame();
374 return Status(kOk);
377 Status ExecuteGetTitle(
378 Session* session,
379 WebView* web_view,
380 const base::DictionaryValue& params,
381 scoped_ptr<base::Value>* value) {
382 const char kGetTitleScript[] =
383 "function() {"
384 " if (document.title)"
385 " return document.title;"
386 " else"
387 " return document.URL;"
388 "}";
389 base::ListValue args;
390 return web_view->CallFunction(std::string(), kGetTitleScript, args, value);
393 Status ExecuteGetPageSource(
394 Session* session,
395 WebView* web_view,
396 const base::DictionaryValue& params,
397 scoped_ptr<base::Value>* value) {
398 const char kGetPageSource[] =
399 "function() {"
400 " return new XMLSerializer().serializeToString(document);"
401 "}";
402 base::ListValue args;
403 return web_view->CallFunction(
404 session->GetCurrentFrameId(), kGetPageSource, args, value);
407 Status ExecuteFindElement(
408 int interval_ms,
409 Session* session,
410 WebView* web_view,
411 const base::DictionaryValue& params,
412 scoped_ptr<base::Value>* value) {
413 return FindElement(interval_ms, true, NULL, session, web_view, params, value);
416 Status ExecuteFindElements(
417 int interval_ms,
418 Session* session,
419 WebView* web_view,
420 const base::DictionaryValue& params,
421 scoped_ptr<base::Value>* value) {
422 return FindElement(
423 interval_ms, false, NULL, session, web_view, params, value);
426 Status ExecuteGetCurrentUrl(
427 Session* session,
428 WebView* web_view,
429 const base::DictionaryValue& params,
430 scoped_ptr<base::Value>* value) {
431 std::string url;
432 Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
433 if (status.IsError())
434 return status;
435 value->reset(new base::StringValue(url));
436 return Status(kOk);
439 Status ExecuteGoBack(
440 Session* session,
441 WebView* web_view,
442 const base::DictionaryValue& params,
443 scoped_ptr<base::Value>* value) {
444 Status status = web_view->TraverseHistory(-1);
445 if (status.IsError())
446 return status;
447 session->SwitchToTopFrame();
448 return Status(kOk);
451 Status ExecuteGoForward(
452 Session* session,
453 WebView* web_view,
454 const base::DictionaryValue& params,
455 scoped_ptr<base::Value>* value) {
456 Status status = web_view->TraverseHistory(1);
457 if (status.IsError())
458 return status;
459 session->SwitchToTopFrame();
460 return Status(kOk);
463 Status ExecuteRefresh(
464 Session* session,
465 WebView* web_view,
466 const base::DictionaryValue& params,
467 scoped_ptr<base::Value>* value) {
468 Status status = web_view->Reload();
469 if (status.IsError())
470 return status;
471 session->SwitchToTopFrame();
472 return Status(kOk);
475 Status ExecuteMouseMoveTo(
476 Session* session,
477 WebView* web_view,
478 const base::DictionaryValue& params,
479 scoped_ptr<base::Value>* value) {
480 std::string element_id;
481 bool has_element = params.GetString("element", &element_id);
482 int x_offset = 0;
483 int y_offset = 0;
484 bool has_offset = params.GetInteger("xoffset", &x_offset) &&
485 params.GetInteger("yoffset", &y_offset);
486 if (!has_element && !has_offset)
487 return Status(kUnknownError, "at least an element or offset should be set");
489 WebPoint location;
490 if (has_element) {
491 WebPoint offset(x_offset, y_offset);
492 Status status = ScrollElementIntoView(session, web_view, element_id,
493 has_offset ? &offset : nullptr, &location);
494 if (status.IsError())
495 return status;
496 } else {
497 location = session->mouse_position;
498 if (has_offset)
499 location.Offset(x_offset, y_offset);
502 std::list<MouseEvent> events;
503 events.push_back(
504 MouseEvent(kMovedMouseEventType, kNoneMouseButton,
505 location.x, location.y, session->sticky_modifiers, 0));
506 Status status =
507 web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
508 if (status.IsOk())
509 session->mouse_position = location;
510 return status;
513 Status ExecuteMouseClick(
514 Session* session,
515 WebView* web_view,
516 const base::DictionaryValue& params,
517 scoped_ptr<base::Value>* value) {
518 MouseButton button;
519 Status status = GetMouseButton(params, &button);
520 if (status.IsError())
521 return status;
522 std::list<MouseEvent> events;
523 events.push_back(
524 MouseEvent(kPressedMouseEventType, button,
525 session->mouse_position.x, session->mouse_position.y,
526 session->sticky_modifiers, 1));
527 events.push_back(
528 MouseEvent(kReleasedMouseEventType, button,
529 session->mouse_position.x, session->mouse_position.y,
530 session->sticky_modifiers, 1));
531 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
534 Status ExecuteMouseButtonDown(
535 Session* session,
536 WebView* web_view,
537 const base::DictionaryValue& params,
538 scoped_ptr<base::Value>* value) {
539 MouseButton button;
540 Status status = GetMouseButton(params, &button);
541 if (status.IsError())
542 return status;
543 std::list<MouseEvent> events;
544 events.push_back(
545 MouseEvent(kPressedMouseEventType, 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 ExecuteMouseButtonUp(
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(kReleasedMouseEventType, 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 ExecuteMouseDoubleClick(
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(kPressedMouseEventType, button,
580 session->mouse_position.x, session->mouse_position.y,
581 session->sticky_modifiers, 2));
582 events.push_back(
583 MouseEvent(kReleasedMouseEventType, button,
584 session->mouse_position.x, session->mouse_position.y,
585 session->sticky_modifiers, 2));
586 return web_view->DispatchMouseEvents(events, session->GetCurrentFrameId());
589 Status ExecuteTouchDown(
590 Session* session,
591 WebView* web_view,
592 const base::DictionaryValue& params,
593 scoped_ptr<base::Value>* value) {
594 return ExecuteTouchEvent(session, web_view, kTouchStart, params);
597 Status ExecuteTouchUp(
598 Session* session,
599 WebView* web_view,
600 const base::DictionaryValue& params,
601 scoped_ptr<base::Value>* value) {
602 return ExecuteTouchEvent(session, web_view, kTouchEnd, params);
605 Status ExecuteTouchMove(
606 Session* session,
607 WebView* web_view,
608 const base::DictionaryValue& params,
609 scoped_ptr<base::Value>* value) {
610 return ExecuteTouchEvent(session, web_view, kTouchMove, params);
613 Status ExecuteTouchScroll(
614 Session *session,
615 WebView* web_view,
616 const base::DictionaryValue& params,
617 scoped_ptr<base::Value>* value) {
618 if (session->chrome->GetBrowserInfo()->build_no < 2286) {
619 // TODO(samuong): remove this once we stop supporting M41.
620 return Status(kUnknownCommand, "Touch scroll action requires Chrome 42+");
622 WebPoint location = session->mouse_position;
623 std::string element;
624 if (params.GetString("element", &element)) {
625 Status status = GetElementClickableLocation(
626 session, web_view, element, &location);
627 if (status.IsError())
628 return status;
630 int xoffset;
631 if (!params.GetInteger("xoffset", &xoffset))
632 return Status(kUnknownError, "'xoffset' must be an integer");
633 int yoffset;
634 if (!params.GetInteger("yoffset", &yoffset))
635 return Status(kUnknownError, "'yoffset' must be an integer");
636 return web_view->SynthesizeScrollGesture(
637 location.x, location.y, xoffset, yoffset);
640 Status ExecuteTouchPinch(
641 Session* session,
642 WebView* web_view,
643 const base::DictionaryValue& params,
644 scoped_ptr<base::Value>* value) {
645 if (session->chrome->GetBrowserInfo()->build_no < 2286) {
646 // TODO(samuong): remove this once we stop supporting M41.
647 return Status(kUnknownCommand, "Pinch action requires Chrome 42+");
649 WebPoint location;
650 if (!params.GetInteger("x", &location.x))
651 return Status(kUnknownError, "'x' must be an integer");
652 if (!params.GetInteger("y", &location.y))
653 return Status(kUnknownError, "'y' must be an integer");
654 double scale_factor;
655 if (!params.GetDouble("scale", &scale_factor))
656 return Status(kUnknownError, "'scale' must be an integer");
657 return web_view->SynthesizePinchGesture(location.x, location.y, scale_factor);
660 Status ExecuteGetActiveElement(
661 Session* session,
662 WebView* web_view,
663 const base::DictionaryValue& params,
664 scoped_ptr<base::Value>* value) {
665 return GetActiveElement(session, web_view, value);
668 Status ExecuteSendKeysToActiveElement(
669 Session* session,
670 WebView* web_view,
671 const base::DictionaryValue& params,
672 scoped_ptr<base::Value>* value) {
673 const base::ListValue* key_list;
674 if (!params.GetList("value", &key_list))
675 return Status(kUnknownError, "'value' must be a list");
676 return SendKeysOnWindow(
677 web_view, key_list, false, &session->sticky_modifiers);
680 Status ExecuteGetAppCacheStatus(
681 Session* session,
682 WebView* web_view,
683 const base::DictionaryValue& params,
684 scoped_ptr<base::Value>* value) {
685 return web_view->EvaluateScript(
686 session->GetCurrentFrameId(),
687 "applicationCache.status",
688 value);
691 Status ExecuteIsBrowserOnline(
692 Session* session,
693 WebView* web_view,
694 const base::DictionaryValue& params,
695 scoped_ptr<base::Value>* value) {
696 return web_view->EvaluateScript(
697 session->GetCurrentFrameId(),
698 "navigator.onLine",
699 value);
702 Status ExecuteGetStorageItem(
703 const char* storage,
704 Session* session,
705 WebView* web_view,
706 const base::DictionaryValue& params,
707 scoped_ptr<base::Value>* value) {
708 std::string key;
709 if (!params.GetString("key", &key))
710 return Status(kUnknownError, "'key' must be a string");
711 base::ListValue args;
712 args.Append(new base::StringValue(key));
713 return web_view->CallFunction(
714 session->GetCurrentFrameId(),
715 base::StringPrintf("function(key) { return %s[key]; }", storage),
716 args,
717 value);
720 Status ExecuteGetStorageKeys(
721 const char* storage,
722 Session* session,
723 WebView* web_view,
724 const base::DictionaryValue& params,
725 scoped_ptr<base::Value>* value) {
726 const char script[] =
727 "var keys = [];"
728 "for (var key in %s) {"
729 " keys.push(key);"
731 "keys";
732 return web_view->EvaluateScript(
733 session->GetCurrentFrameId(),
734 base::StringPrintf(script, storage),
735 value);
738 Status ExecuteSetStorageItem(
739 const char* storage,
740 Session* session,
741 WebView* web_view,
742 const base::DictionaryValue& params,
743 scoped_ptr<base::Value>* value) {
744 std::string key;
745 if (!params.GetString("key", &key))
746 return Status(kUnknownError, "'key' must be a string");
747 std::string storage_value;
748 if (!params.GetString("value", &storage_value))
749 return Status(kUnknownError, "'value' must be a string");
750 base::ListValue args;
751 args.Append(new base::StringValue(key));
752 args.Append(new base::StringValue(storage_value));
753 return web_view->CallFunction(
754 session->GetCurrentFrameId(),
755 base::StringPrintf("function(key, value) { %s[key] = value; }", storage),
756 args,
757 value);
760 Status ExecuteRemoveStorageItem(
761 const char* storage,
762 Session* session,
763 WebView* web_view,
764 const base::DictionaryValue& params,
765 scoped_ptr<base::Value>* value) {
766 std::string key;
767 if (!params.GetString("key", &key))
768 return Status(kUnknownError, "'key' must be a string");
769 base::ListValue args;
770 args.Append(new base::StringValue(key));
771 return web_view->CallFunction(
772 session->GetCurrentFrameId(),
773 base::StringPrintf("function(key) { %s.removeItem(key) }", storage),
774 args,
775 value);
778 Status ExecuteClearStorage(
779 const char* storage,
780 Session* session,
781 WebView* web_view,
782 const base::DictionaryValue& params,
783 scoped_ptr<base::Value>* value) {
784 return web_view->EvaluateScript(
785 session->GetCurrentFrameId(),
786 base::StringPrintf("%s.clear()", storage),
787 value);
790 Status ExecuteGetStorageSize(
791 const char* storage,
792 Session* session,
793 WebView* web_view,
794 const base::DictionaryValue& params,
795 scoped_ptr<base::Value>* value) {
796 return web_view->EvaluateScript(
797 session->GetCurrentFrameId(),
798 base::StringPrintf("%s.length", storage),
799 value);
802 Status ExecuteScreenshot(
803 Session* session,
804 WebView* web_view,
805 const base::DictionaryValue& params,
806 scoped_ptr<base::Value>* value) {
807 Status status = session->chrome->ActivateWebView(web_view->GetId());
808 if (status.IsError())
809 return status;
811 std::string screenshot;
812 ChromeDesktopImpl* desktop = NULL;
813 status = session->chrome->GetAsDesktop(&desktop);
814 if (status.IsOk() && !session->force_devtools_screenshot) {
815 AutomationExtension* extension = NULL;
816 status = desktop->GetAutomationExtension(&extension);
817 if (status.IsError())
818 return status;
819 status = extension->CaptureScreenshot(&screenshot);
820 if (status.IsError()) {
821 LOG(WARNING) << "screenshot failed with extension, fallback to DevTools";
822 status = web_view->CaptureScreenshot(&screenshot);
824 } else {
825 status = web_view->CaptureScreenshot(&screenshot);
827 if (status.IsError())
828 return status;
830 value->reset(new base::StringValue(screenshot));
831 return Status(kOk);
834 Status ExecuteGetCookies(
835 Session* session,
836 WebView* web_view,
837 const base::DictionaryValue& params,
838 scoped_ptr<base::Value>* value) {
839 std::list<Cookie> cookies;
840 Status status = GetVisibleCookies(web_view, &cookies);
841 if (status.IsError())
842 return status;
843 scoped_ptr<base::ListValue> cookie_list(new base::ListValue());
844 for (std::list<Cookie>::const_iterator it = cookies.begin();
845 it != cookies.end(); ++it) {
846 cookie_list->Append(CreateDictionaryFrom(*it));
848 value->reset(cookie_list.release());
849 return Status(kOk);
852 Status ExecuteAddCookie(
853 Session* session,
854 WebView* web_view,
855 const base::DictionaryValue& params,
856 scoped_ptr<base::Value>* value) {
857 const base::DictionaryValue* cookie;
858 if (!params.GetDictionary("cookie", &cookie))
859 return Status(kUnknownError, "missing 'cookie'");
860 base::ListValue args;
861 args.Append(cookie->DeepCopy());
862 scoped_ptr<base::Value> result;
863 return web_view->CallFunction(
864 session->GetCurrentFrameId(), kAddCookieScript, args, &result);
867 Status ExecuteDeleteCookie(
868 Session* session,
869 WebView* web_view,
870 const base::DictionaryValue& params,
871 scoped_ptr<base::Value>* value) {
872 std::string name;
873 if (!params.GetString("name", &name))
874 return Status(kUnknownError, "missing 'name'");
875 base::DictionaryValue params_url;
876 scoped_ptr<base::Value> value_url;
877 std::string url;
878 Status status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
879 if (status.IsError())
880 return status;
881 return web_view->DeleteCookie(name, url);
884 Status ExecuteDeleteAllCookies(
885 Session* session,
886 WebView* web_view,
887 const base::DictionaryValue& params,
888 scoped_ptr<base::Value>* value) {
889 std::list<Cookie> cookies;
890 Status status = GetVisibleCookies(web_view, &cookies);
891 if (status.IsError())
892 return status;
894 if (!cookies.empty()) {
895 base::DictionaryValue params_url;
896 scoped_ptr<base::Value> value_url;
897 std::string url;
898 status = GetUrl(web_view, session->GetCurrentFrameId(), &url);
899 if (status.IsError())
900 return status;
901 for (std::list<Cookie>::const_iterator it = cookies.begin();
902 it != cookies.end(); ++it) {
903 status = web_view->DeleteCookie(it->name, url);
904 if (status.IsError())
905 return status;
909 return Status(kOk);
912 Status ExecuteSetLocation(
913 Session* session,
914 WebView* web_view,
915 const base::DictionaryValue& params,
916 scoped_ptr<base::Value>* value) {
917 const base::DictionaryValue* location = NULL;
918 Geoposition geoposition;
919 if (!params.GetDictionary("location", &location) ||
920 !location->GetDouble("latitude", &geoposition.latitude) ||
921 !location->GetDouble("longitude", &geoposition.longitude))
922 return Status(kUnknownError, "missing or invalid 'location'");
923 if (location->HasKey("accuracy") &&
924 !location->GetDouble("accuracy", &geoposition.accuracy)) {
925 return Status(kUnknownError, "invalid 'accuracy'");
926 } else {
927 // |accuracy| is not part of the WebDriver spec yet, so if it is not given
928 // default to 100 meters accuracy.
929 geoposition.accuracy = 100;
932 Status status = web_view->OverrideGeolocation(geoposition);
933 if (status.IsOk())
934 session->overridden_geoposition.reset(new Geoposition(geoposition));
935 return status;
938 Status ExecuteSetNetworkConditions(
939 Session* session,
940 WebView* web_view,
941 const base::DictionaryValue& params,
942 scoped_ptr<base::Value>* value) {
943 std::string network_name;
944 const base::DictionaryValue* conditions = NULL;
945 scoped_ptr<NetworkConditions> network_conditions(new NetworkConditions());
946 if (params.GetString("network_name", &network_name)) {
947 // Get conditions from preset list.
948 Status status = FindPresetNetwork(network_name, network_conditions.get());
949 if (status.IsError())
950 return status;
951 } else if (params.GetDictionary("network_conditions", &conditions)) {
952 // |latency| is required.
953 if (!conditions->GetDouble("latency", &network_conditions->latency))
954 return Status(kUnknownError,
955 "invalid 'network_conditions' is missing 'latency'");
957 // Either |throughput| or the pair |download_throughput| and
958 // |upload_throughput| is required.
959 if (conditions->HasKey("throughput")) {
960 if (!conditions->GetDouble("throughput",
961 &network_conditions->download_throughput))
962 return Status(kUnknownError, "invalid 'throughput'");
963 conditions->GetDouble("throughput",
964 &network_conditions->upload_throughput);
965 } else if (conditions->HasKey("download_throughput") &&
966 conditions->HasKey("upload_throughput")) {
967 if (!conditions->GetDouble("download_throughput",
968 &network_conditions->download_throughput) ||
969 !conditions->GetDouble("upload_throughput",
970 &network_conditions->upload_throughput))
971 return Status(kUnknownError,
972 "invalid 'download_throughput' or 'upload_throughput'");
973 } else {
974 return Status(kUnknownError,
975 "invalid 'network_conditions' is missing 'throughput' or "
976 "'download_throughput'/'upload_throughput' pair");
979 // |offline| is optional.
980 if (conditions->HasKey("offline")) {
981 if (!conditions->GetBoolean("offline", &network_conditions->offline))
982 return Status(kUnknownError, "invalid 'offline'");
983 } else {
984 network_conditions->offline = false;
986 } else {
987 return Status(kUnknownError,
988 "either 'network_conditions' or 'network_name' must be "
989 "supplied");
992 session->overridden_network_conditions.reset(
993 network_conditions.release());
994 return web_view->OverrideNetworkConditions(
995 *session->overridden_network_conditions);
998 Status ExecuteDeleteNetworkConditions(
999 Session* session,
1000 WebView* web_view,
1001 const base::DictionaryValue& params,
1002 scoped_ptr<base::Value>* value) {
1003 // Chrome does not have any command to stop overriding network conditions, so
1004 // we just override the network conditions with the "No throttling" preset.
1005 NetworkConditions network_conditions;
1006 // Get conditions from preset list.
1007 Status status = FindPresetNetwork("No throttling", &network_conditions);
1008 if (status.IsError())
1009 return status;
1011 status = web_view->OverrideNetworkConditions(network_conditions);
1012 if (status.IsError())
1013 return status;
1015 // After we've successfully overridden the network conditions with
1016 // "No throttling", we can delete them from |session|.
1017 session->overridden_network_conditions.reset();
1018 return status;
1021 Status ExecuteTakeHeapSnapshot(
1022 Session* session,
1023 WebView* web_view,
1024 const base::DictionaryValue& params,
1025 scoped_ptr<base::Value>* value) {
1026 return web_view->TakeHeapSnapshot(value);