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"
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"
35 Status
GetMouseButton(const base::DictionaryValue
& params
,
36 MouseButton
* button
) {
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
);
48 Status
GetUrl(WebView
* web_view
, const std::string
& frame
, std::string
* url
) {
49 scoped_ptr
<base::Value
> value
;
51 Status status
= web_view
->CallFunction(
52 frame
, "function() { return document.URL; }", args
, &value
);
55 if (!value
->GetAsString(url
))
56 return Status(kUnknownError
, "javascript failed to return the url");
61 Cookie(const std::string
& name
,
62 const std::string
& value
,
63 const std::string
& domain
,
64 const std::string
& path
,
69 : name(name
), value(value
), domain(domain
), path(path
), expiry(expiry
),
70 http_only(http_only
), secure(secure
), session(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
);
91 dict
->SetDouble("expiry", cookie
.expiry
);
92 dict
->SetBoolean("httpOnly", cookie
.http_only
);
93 dict
->SetBoolean("secure", cookie
.secure
);
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())
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");
110 cookie_dict
->GetString("name", &name
);
112 cookie_dict
->GetString("value", &value
);
114 cookie_dict
->GetString("domain", &domain
);
116 cookie_dict
->GetString("path", &path
);
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
);
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
);
134 Status
ScrollCoordinateInToView(
135 Session
* session
, WebView
* web_view
, int x
, int y
, int* offset_x
,
137 scoped_ptr
<base::Value
> value
;
138 base::ListValue args
;
139 args
.AppendInteger(x
);
140 args
.AppendInteger(y
);
141 Status status
= web_view
->CallFunction(
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);"
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)};"
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");
175 Status
ExecuteTouchEvent(
176 Session
* session
, WebView
* web_view
, TouchEventType type
,
177 const base::DictionaryValue
& params
) {
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");
185 Status status
= ScrollCoordinateInToView(
186 session
, web_view
, x
, y
, &relative_x
, &relative_y
);
189 std::list
<TouchEvent
> events
;
191 TouchEvent(type
, relative_x
, relative_y
));
192 return web_view
->DispatchTouchEvents(events
);
197 Status
ExecuteWindowCommand(
198 const WindowCommand
& command
,
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())
207 status
= web_view
->ConnectIfNecessary();
208 if (status
.IsError())
211 status
= web_view
->HandleReceivedEvents();
212 if (status
.IsError())
215 if (web_view
->GetJavaScriptDialogManager()->IsDialogOpen())
216 return Status(kUnexpectedAlertOpen
);
218 Status
nav_status(kOk
);
219 for (int attempt
= 0; attempt
< 3; attempt
++) {
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())
230 status
= command
.Run(session
, web_view
, params
, value
);
231 if (status
.code() == kNoSuchExecutionContext
) {
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(),
239 if (nav_status
.IsError())
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
)
253 if (status
.code() == kUnexpectedAlertOpen
)
261 const base::DictionaryValue
& params
,
262 scoped_ptr
<base::Value
>* value
) {
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())
269 session
->SwitchToTopFrame();
273 Status
ExecuteExecuteScript(
276 const base::DictionaryValue
& params
,
277 scoped_ptr
<base::Value
>* value
) {
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
);
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(
300 const base::DictionaryValue
& params
,
301 scoped_ptr
<base::Value
>* value
) {
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(
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();
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());
337 " return document.evaluate(xpath, document, null, "
338 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
340 std::string xpath
= "(/html/body//iframe|/html/frameset/frame)";
341 std::string id_string
;
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);
349 return Status(kUnknownError
, "invalid 'id'");
351 args
.Append(new base::StringValue(xpath
));
354 Status status
= web_view
->GetFrameByFunction(
355 session
->GetCurrentFrameId(), script
, args
, &frame
);
356 if (status
.IsError())
359 scoped_ptr
<base::Value
> result
;
360 status
= web_view
->CallFunction(
361 session
->GetCurrentFrameId(), script
, args
, &result
);
362 if (status
.IsError())
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);"
373 base::ListValue new_args
;
374 new_args
.Append(element
->DeepCopy());
375 new_args
.AppendString(chrome_driver_id
);
377 status
= web_view
->CallFunction(
378 session
->GetCurrentFrameId(), kSetFrameIdentifier
, new_args
, &result
);
379 if (status
.IsError())
381 session
->SwitchToSubFrame(frame
, chrome_driver_id
);
385 Status
ExecuteSwitchToParentFrame(
388 const base::DictionaryValue
& params
,
389 scoped_ptr
<base::Value
>* value
) {
390 session
->SwitchToParentFrame();
394 Status
ExecuteGetTitle(
397 const base::DictionaryValue
& params
,
398 scoped_ptr
<base::Value
>* value
) {
399 const char kGetTitleScript
[] =
401 " if (document.title)"
402 " return document.title;"
404 " return document.URL;"
406 base::ListValue args
;
407 return web_view
->CallFunction(std::string(), kGetTitleScript
, args
, value
);
410 Status
ExecuteGetPageSource(
413 const base::DictionaryValue
& params
,
414 scoped_ptr
<base::Value
>* value
) {
415 const char kGetPageSource
[] =
417 " return new XMLSerializer().serializeToString(document);"
419 base::ListValue args
;
420 return web_view
->CallFunction(
421 session
->GetCurrentFrameId(), kGetPageSource
, args
, value
);
424 Status
ExecuteFindElement(
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(
437 const base::DictionaryValue
& params
,
438 scoped_ptr
<base::Value
>* value
) {
440 interval_ms
, false, NULL
, session
, web_view
, params
, value
);
443 Status
ExecuteGetCurrentUrl(
446 const base::DictionaryValue
& params
,
447 scoped_ptr
<base::Value
>* value
) {
449 Status status
= GetUrl(web_view
, session
->GetCurrentFrameId(), &url
);
450 if (status
.IsError())
452 value
->reset(new base::StringValue(url
));
456 Status
ExecuteGoBack(
459 const base::DictionaryValue
& params
,
460 scoped_ptr
<base::Value
>* value
) {
461 Status status
= web_view
->TraverseHistory(-1);
462 if (status
.IsError())
464 session
->SwitchToTopFrame();
468 Status
ExecuteGoForward(
471 const base::DictionaryValue
& params
,
472 scoped_ptr
<base::Value
>* value
) {
473 Status status
= web_view
->TraverseHistory(1);
474 if (status
.IsError())
476 session
->SwitchToTopFrame();
480 Status
ExecuteRefresh(
483 const base::DictionaryValue
& params
,
484 scoped_ptr
<base::Value
>* value
) {
485 Status status
= web_view
->Reload();
486 if (status
.IsError())
488 session
->SwitchToTopFrame();
492 Status
ExecuteMouseMoveTo(
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
);
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");
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())
514 location
= session
->mouse_position
;
516 location
.Offset(x_offset
, y_offset
);
519 std::list
<MouseEvent
> events
;
521 MouseEvent(kMovedMouseEventType
, kNoneMouseButton
,
522 location
.x
, location
.y
, session
->sticky_modifiers
, 0));
524 web_view
->DispatchMouseEvents(events
, session
->GetCurrentFrameId());
526 session
->mouse_position
= location
;
530 Status
ExecuteMouseClick(
533 const base::DictionaryValue
& params
,
534 scoped_ptr
<base::Value
>* value
) {
536 Status status
= GetMouseButton(params
, &button
);
537 if (status
.IsError())
539 std::list
<MouseEvent
> events
;
541 MouseEvent(kPressedMouseEventType
, button
,
542 session
->mouse_position
.x
, session
->mouse_position
.y
,
543 session
->sticky_modifiers
, 1));
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(
554 const base::DictionaryValue
& params
,
555 scoped_ptr
<base::Value
>* value
) {
557 Status status
= GetMouseButton(params
, &button
);
558 if (status
.IsError())
560 std::list
<MouseEvent
> events
;
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(
571 const base::DictionaryValue
& params
,
572 scoped_ptr
<base::Value
>* value
) {
574 Status status
= GetMouseButton(params
, &button
);
575 if (status
.IsError())
577 std::list
<MouseEvent
> events
;
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(
588 const base::DictionaryValue
& params
,
589 scoped_ptr
<base::Value
>* value
) {
591 Status status
= GetMouseButton(params
, &button
);
592 if (status
.IsError())
594 std::list
<MouseEvent
> events
;
596 MouseEvent(kPressedMouseEventType
, button
,
597 session
->mouse_position
.x
, session
->mouse_position
.y
,
598 session
->sticky_modifiers
, 2));
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(
609 const base::DictionaryValue
& params
,
610 scoped_ptr
<base::Value
>* value
) {
611 return ExecuteTouchEvent(session
, web_view
, kTouchStart
, params
);
614 Status
ExecuteTouchUp(
617 const base::DictionaryValue
& params
,
618 scoped_ptr
<base::Value
>* value
) {
619 return ExecuteTouchEvent(session
, web_view
, kTouchEnd
, params
);
622 Status
ExecuteTouchMove(
625 const base::DictionaryValue
& params
,
626 scoped_ptr
<base::Value
>* value
) {
627 return ExecuteTouchEvent(session
, web_view
, kTouchMove
, params
);
630 Status
ExecuteTouchScroll(
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
;
641 if (params
.GetString("element", &element
)) {
642 Status status
= GetElementClickableLocation(
643 session
, web_view
, element
, &location
);
644 if (status
.IsError())
648 if (!params
.GetInteger("xoffset", &xoffset
))
649 return Status(kUnknownError
, "'xoffset' must be an integer");
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(
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+");
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");
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(
680 const base::DictionaryValue
& params
,
681 scoped_ptr
<base::Value
>* value
) {
682 return GetActiveElement(session
, web_view
, value
);
685 Status
ExecuteSendKeysToActiveElement(
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(
700 const base::DictionaryValue
& params
,
701 scoped_ptr
<base::Value
>* value
) {
702 return web_view
->EvaluateScript(
703 session
->GetCurrentFrameId(),
704 "applicationCache.status",
708 Status
ExecuteIsBrowserOnline(
711 const base::DictionaryValue
& params
,
712 scoped_ptr
<base::Value
>* value
) {
713 return web_view
->EvaluateScript(
714 session
->GetCurrentFrameId(),
719 Status
ExecuteGetStorageItem(
723 const base::DictionaryValue
& params
,
724 scoped_ptr
<base::Value
>* value
) {
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
),
737 Status
ExecuteGetStorageKeys(
741 const base::DictionaryValue
& params
,
742 scoped_ptr
<base::Value
>* value
) {
743 const char script
[] =
745 "for (var key in %s) {"
749 return web_view
->EvaluateScript(
750 session
->GetCurrentFrameId(),
751 base::StringPrintf(script
, storage
),
755 Status
ExecuteSetStorageItem(
759 const base::DictionaryValue
& params
,
760 scoped_ptr
<base::Value
>* value
) {
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
),
777 Status
ExecuteRemoveStorageItem(
781 const base::DictionaryValue
& params
,
782 scoped_ptr
<base::Value
>* value
) {
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
),
795 Status
ExecuteClearStorage(
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
),
807 Status
ExecuteGetStorageSize(
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
),
819 Status
ExecuteScreenshot(
822 const base::DictionaryValue
& params
,
823 scoped_ptr
<base::Value
>* value
) {
824 Status status
= session
->chrome
->ActivateWebView(web_view
->GetId());
825 if (status
.IsError())
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())
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
);
842 status
= web_view
->CaptureScreenshot(&screenshot
);
844 if (status
.IsError())
847 value
->reset(new base::StringValue(screenshot
));
851 Status
ExecuteGetCookies(
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())
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());
869 Status
ExecuteAddCookie(
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(
887 const base::DictionaryValue
& params
,
888 scoped_ptr
<base::Value
>* value
) {
890 if (!params
.GetString("name", &name
))
891 return Status(kUnknownError
, "missing 'name'");
892 base::DictionaryValue params_url
;
893 scoped_ptr
<base::Value
> value_url
;
895 Status status
= GetUrl(web_view
, session
->GetCurrentFrameId(), &url
);
896 if (status
.IsError())
898 return web_view
->DeleteCookie(name
, url
);
901 Status
ExecuteDeleteAllCookies(
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())
911 if (!cookies
.empty()) {
912 base::DictionaryValue params_url
;
913 scoped_ptr
<base::Value
> value_url
;
915 status
= GetUrl(web_view
, session
->GetCurrentFrameId(), &url
);
916 if (status
.IsError())
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())
929 Status
ExecuteSetLocation(
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'");
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
);
951 session
->overridden_geoposition
.reset(new Geoposition(geoposition
));
955 Status
ExecuteSetNetworkConditions(
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())
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'");
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'");
1001 network_conditions
->offline
= false;
1004 return Status(kUnknownError
,
1005 "either 'network_conditions' or 'network_name' must be "
1009 session
->overridden_network_conditions
.reset(
1010 network_conditions
.release());
1011 return web_view
->OverrideNetworkConditions(
1012 *session
->overridden_network_conditions
);
1015 Status
ExecuteDeleteNetworkConditions(
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())
1028 status
= web_view
->OverrideNetworkConditions(network_conditions
);
1029 if (status
.IsError())
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();
1038 Status
ExecuteTakeHeapSnapshot(
1041 const base::DictionaryValue
& params
,
1042 scoped_ptr
<base::Value
>* value
) {
1043 return web_view
->TakeHeapSnapshot(value
);