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
,
68 : name(name
), value(value
), domain(domain
), path(path
), expiry(expiry
),
69 secure(secure
), session(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
);
89 dict
->SetDouble("expiry", cookie
.expiry
);
90 dict
->SetBoolean("secure", cookie
.secure
);
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
);
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");
107 cookie_dict
->GetString("name", &name
);
109 cookie_dict
->GetString("value", &value
);
111 cookie_dict
->GetString("domain", &domain
);
113 cookie_dict
->GetString("path", &path
);
115 cookie_dict
->GetDouble("expires", &expiry
);
116 expiry
/= 1000; // Convert from millisecond to second.
117 bool session
= false;
118 cookie_dict
->GetBoolean("session", &session
);
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
);
129 Status
ScrollCoordinateInToView(
130 Session
* session
, WebView
* web_view
, int x
, int y
, int* offset_x
,
132 scoped_ptr
<base::Value
> value
;
133 base::ListValue args
;
134 args
.AppendInteger(x
);
135 args
.AppendInteger(y
);
136 Status status
= web_view
->CallFunction(
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);"
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)};"
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");
170 Status
ExecuteTouchEvent(
171 Session
* session
, WebView
* web_view
, TouchEventType type
,
172 const base::DictionaryValue
& params
) {
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");
180 Status status
= ScrollCoordinateInToView(
181 session
, web_view
, x
, y
, &relative_x
, &relative_y
);
184 std::list
<TouchEvent
> events
;
186 TouchEvent(type
, relative_x
, relative_y
));
187 return web_view
->DispatchTouchEvents(events
);
192 Status
ExecuteWindowCommand(
193 const WindowCommand
& command
,
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())
202 status
= web_view
->ConnectIfNecessary();
203 if (status
.IsError())
206 status
= web_view
->HandleReceivedEvents();
207 if (status
.IsError())
210 if (web_view
->GetJavaScriptDialogManager()->IsDialogOpen())
211 return Status(kUnexpectedAlertOpen
);
213 Status
nav_status(kOk
);
214 for (int attempt
= 0; attempt
< 3; attempt
++) {
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())
225 status
= command
.Run(session
, web_view
, params
, value
);
226 if (status
.code() != kNoSuchExecutionContext
)
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
)
236 if (status
.code() == kUnexpectedAlertOpen
)
244 const base::DictionaryValue
& params
,
245 scoped_ptr
<base::Value
>* value
) {
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())
252 session
->SwitchToTopFrame();
256 Status
ExecuteExecuteScript(
259 const base::DictionaryValue
& params
,
260 scoped_ptr
<base::Value
>* value
) {
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
);
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(
283 const base::DictionaryValue
& params
,
284 scoped_ptr
<base::Value
>* value
) {
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(
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();
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());
320 " return document.evaluate(xpath, document, null, "
321 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
323 std::string xpath
= "(/html/body//iframe|/html/frameset/frame)";
324 std::string id_string
;
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);
332 return Status(kUnknownError
, "invalid 'id'");
334 args
.Append(new base::StringValue(xpath
));
337 Status status
= web_view
->GetFrameByFunction(
338 session
->GetCurrentFrameId(), script
, args
, &frame
);
339 if (status
.IsError())
342 scoped_ptr
<base::Value
> result
;
343 status
= web_view
->CallFunction(
344 session
->GetCurrentFrameId(), script
, args
, &result
);
345 if (status
.IsError())
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);"
356 base::ListValue new_args
;
357 new_args
.Append(element
->DeepCopy());
358 new_args
.AppendString(chrome_driver_id
);
360 status
= web_view
->CallFunction(
361 session
->GetCurrentFrameId(), kSetFrameIdentifier
, new_args
, &result
);
362 if (status
.IsError())
364 session
->SwitchToSubFrame(frame
, chrome_driver_id
);
368 Status
ExecuteSwitchToParentFrame(
371 const base::DictionaryValue
& params
,
372 scoped_ptr
<base::Value
>* value
) {
373 session
->SwitchToParentFrame();
377 Status
ExecuteGetTitle(
380 const base::DictionaryValue
& params
,
381 scoped_ptr
<base::Value
>* value
) {
382 const char kGetTitleScript
[] =
384 " if (document.title)"
385 " return document.title;"
387 " return document.URL;"
389 base::ListValue args
;
390 return web_view
->CallFunction(std::string(), kGetTitleScript
, args
, value
);
393 Status
ExecuteGetPageSource(
396 const base::DictionaryValue
& params
,
397 scoped_ptr
<base::Value
>* value
) {
398 const char kGetPageSource
[] =
400 " return new XMLSerializer().serializeToString(document);"
402 base::ListValue args
;
403 return web_view
->CallFunction(
404 session
->GetCurrentFrameId(), kGetPageSource
, args
, value
);
407 Status
ExecuteFindElement(
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(
420 const base::DictionaryValue
& params
,
421 scoped_ptr
<base::Value
>* value
) {
423 interval_ms
, false, NULL
, session
, web_view
, params
, value
);
426 Status
ExecuteGetCurrentUrl(
429 const base::DictionaryValue
& params
,
430 scoped_ptr
<base::Value
>* value
) {
432 Status status
= GetUrl(web_view
, session
->GetCurrentFrameId(), &url
);
433 if (status
.IsError())
435 value
->reset(new base::StringValue(url
));
439 Status
ExecuteGoBack(
442 const base::DictionaryValue
& params
,
443 scoped_ptr
<base::Value
>* value
) {
444 Status status
= web_view
->TraverseHistory(-1);
445 if (status
.IsError())
447 session
->SwitchToTopFrame();
451 Status
ExecuteGoForward(
454 const base::DictionaryValue
& params
,
455 scoped_ptr
<base::Value
>* value
) {
456 Status status
= web_view
->TraverseHistory(1);
457 if (status
.IsError())
459 session
->SwitchToTopFrame();
463 Status
ExecuteRefresh(
466 const base::DictionaryValue
& params
,
467 scoped_ptr
<base::Value
>* value
) {
468 Status status
= web_view
->Reload();
469 if (status
.IsError())
471 session
->SwitchToTopFrame();
475 Status
ExecuteMouseMoveTo(
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
);
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");
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())
497 location
= session
->mouse_position
;
499 location
.Offset(x_offset
, y_offset
);
502 std::list
<MouseEvent
> events
;
504 MouseEvent(kMovedMouseEventType
, kNoneMouseButton
,
505 location
.x
, location
.y
, session
->sticky_modifiers
, 0));
507 web_view
->DispatchMouseEvents(events
, session
->GetCurrentFrameId());
509 session
->mouse_position
= location
;
513 Status
ExecuteMouseClick(
516 const base::DictionaryValue
& params
,
517 scoped_ptr
<base::Value
>* value
) {
519 Status status
= GetMouseButton(params
, &button
);
520 if (status
.IsError())
522 std::list
<MouseEvent
> events
;
524 MouseEvent(kPressedMouseEventType
, button
,
525 session
->mouse_position
.x
, session
->mouse_position
.y
,
526 session
->sticky_modifiers
, 1));
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(
537 const base::DictionaryValue
& params
,
538 scoped_ptr
<base::Value
>* value
) {
540 Status status
= GetMouseButton(params
, &button
);
541 if (status
.IsError())
543 std::list
<MouseEvent
> events
;
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(
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(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(
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(kPressedMouseEventType
, button
,
580 session
->mouse_position
.x
, session
->mouse_position
.y
,
581 session
->sticky_modifiers
, 2));
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(
592 const base::DictionaryValue
& params
,
593 scoped_ptr
<base::Value
>* value
) {
594 return ExecuteTouchEvent(session
, web_view
, kTouchStart
, params
);
597 Status
ExecuteTouchUp(
600 const base::DictionaryValue
& params
,
601 scoped_ptr
<base::Value
>* value
) {
602 return ExecuteTouchEvent(session
, web_view
, kTouchEnd
, params
);
605 Status
ExecuteTouchMove(
608 const base::DictionaryValue
& params
,
609 scoped_ptr
<base::Value
>* value
) {
610 return ExecuteTouchEvent(session
, web_view
, kTouchMove
, params
);
613 Status
ExecuteTouchScroll(
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
;
624 if (params
.GetString("element", &element
)) {
625 Status status
= GetElementClickableLocation(
626 session
, web_view
, element
, &location
);
627 if (status
.IsError())
631 if (!params
.GetInteger("xoffset", &xoffset
))
632 return Status(kUnknownError
, "'xoffset' must be an integer");
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(
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+");
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");
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(
663 const base::DictionaryValue
& params
,
664 scoped_ptr
<base::Value
>* value
) {
665 return GetActiveElement(session
, web_view
, value
);
668 Status
ExecuteSendKeysToActiveElement(
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(
683 const base::DictionaryValue
& params
,
684 scoped_ptr
<base::Value
>* value
) {
685 return web_view
->EvaluateScript(
686 session
->GetCurrentFrameId(),
687 "applicationCache.status",
691 Status
ExecuteIsBrowserOnline(
694 const base::DictionaryValue
& params
,
695 scoped_ptr
<base::Value
>* value
) {
696 return web_view
->EvaluateScript(
697 session
->GetCurrentFrameId(),
702 Status
ExecuteGetStorageItem(
706 const base::DictionaryValue
& params
,
707 scoped_ptr
<base::Value
>* value
) {
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
),
720 Status
ExecuteGetStorageKeys(
724 const base::DictionaryValue
& params
,
725 scoped_ptr
<base::Value
>* value
) {
726 const char script
[] =
728 "for (var key in %s) {"
732 return web_view
->EvaluateScript(
733 session
->GetCurrentFrameId(),
734 base::StringPrintf(script
, storage
),
738 Status
ExecuteSetStorageItem(
742 const base::DictionaryValue
& params
,
743 scoped_ptr
<base::Value
>* value
) {
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
),
760 Status
ExecuteRemoveStorageItem(
764 const base::DictionaryValue
& params
,
765 scoped_ptr
<base::Value
>* value
) {
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
),
778 Status
ExecuteClearStorage(
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
),
790 Status
ExecuteGetStorageSize(
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
),
802 Status
ExecuteScreenshot(
805 const base::DictionaryValue
& params
,
806 scoped_ptr
<base::Value
>* value
) {
807 Status status
= session
->chrome
->ActivateWebView(web_view
->GetId());
808 if (status
.IsError())
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())
819 status
= extension
->CaptureScreenshot(&screenshot
);
820 // If the screenshot was forbidden, fallback to DevTools.
821 if (status
.code() == kForbidden
)
822 status
= web_view
->CaptureScreenshot(&screenshot
);
824 status
= web_view
->CaptureScreenshot(&screenshot
);
826 if (status
.IsError())
829 value
->reset(new base::StringValue(screenshot
));
833 Status
ExecuteGetCookies(
836 const base::DictionaryValue
& params
,
837 scoped_ptr
<base::Value
>* value
) {
838 std::list
<Cookie
> cookies
;
839 Status status
= GetVisibleCookies(web_view
, &cookies
);
840 if (status
.IsError())
842 scoped_ptr
<base::ListValue
> cookie_list(new base::ListValue());
843 for (std::list
<Cookie
>::const_iterator it
= cookies
.begin();
844 it
!= cookies
.end(); ++it
) {
845 cookie_list
->Append(CreateDictionaryFrom(*it
));
847 value
->reset(cookie_list
.release());
851 Status
ExecuteAddCookie(
854 const base::DictionaryValue
& params
,
855 scoped_ptr
<base::Value
>* value
) {
856 const base::DictionaryValue
* cookie
;
857 if (!params
.GetDictionary("cookie", &cookie
))
858 return Status(kUnknownError
, "missing 'cookie'");
859 base::ListValue args
;
860 args
.Append(cookie
->DeepCopy());
861 scoped_ptr
<base::Value
> result
;
862 return web_view
->CallFunction(
863 session
->GetCurrentFrameId(), kAddCookieScript
, args
, &result
);
866 Status
ExecuteDeleteCookie(
869 const base::DictionaryValue
& params
,
870 scoped_ptr
<base::Value
>* value
) {
872 if (!params
.GetString("name", &name
))
873 return Status(kUnknownError
, "missing 'name'");
874 base::DictionaryValue params_url
;
875 scoped_ptr
<base::Value
> value_url
;
877 Status status
= GetUrl(web_view
, session
->GetCurrentFrameId(), &url
);
878 if (status
.IsError())
880 return web_view
->DeleteCookie(name
, url
);
883 Status
ExecuteDeleteAllCookies(
886 const base::DictionaryValue
& params
,
887 scoped_ptr
<base::Value
>* value
) {
888 std::list
<Cookie
> cookies
;
889 Status status
= GetVisibleCookies(web_view
, &cookies
);
890 if (status
.IsError())
893 if (!cookies
.empty()) {
894 base::DictionaryValue params_url
;
895 scoped_ptr
<base::Value
> value_url
;
897 status
= GetUrl(web_view
, session
->GetCurrentFrameId(), &url
);
898 if (status
.IsError())
900 for (std::list
<Cookie
>::const_iterator it
= cookies
.begin();
901 it
!= cookies
.end(); ++it
) {
902 status
= web_view
->DeleteCookie(it
->name
, url
);
903 if (status
.IsError())
911 Status
ExecuteSetLocation(
914 const base::DictionaryValue
& params
,
915 scoped_ptr
<base::Value
>* value
) {
916 const base::DictionaryValue
* location
= NULL
;
917 Geoposition geoposition
;
918 if (!params
.GetDictionary("location", &location
) ||
919 !location
->GetDouble("latitude", &geoposition
.latitude
) ||
920 !location
->GetDouble("longitude", &geoposition
.longitude
))
921 return Status(kUnknownError
, "missing or invalid 'location'");
922 if (location
->HasKey("accuracy") &&
923 !location
->GetDouble("accuracy", &geoposition
.accuracy
)) {
924 return Status(kUnknownError
, "invalid 'accuracy'");
926 // |accuracy| is not part of the WebDriver spec yet, so if it is not given
927 // default to 100 meters accuracy.
928 geoposition
.accuracy
= 100;
931 Status status
= web_view
->OverrideGeolocation(geoposition
);
933 session
->overridden_geoposition
.reset(new Geoposition(geoposition
));
937 Status
ExecuteSetNetworkConditions(
940 const base::DictionaryValue
& params
,
941 scoped_ptr
<base::Value
>* value
) {
942 std::string network_name
;
943 const base::DictionaryValue
* conditions
= NULL
;
944 scoped_ptr
<NetworkConditions
> network_conditions(new NetworkConditions());
945 if (params
.GetString("network_name", &network_name
)) {
946 // Get conditions from preset list.
947 Status status
= FindPresetNetwork(network_name
, network_conditions
.get());
948 if (status
.IsError())
950 } else if (params
.GetDictionary("network_conditions", &conditions
)) {
951 // |latency| is required.
952 if (!conditions
->GetDouble("latency", &network_conditions
->latency
))
953 return Status(kUnknownError
,
954 "invalid 'network_conditions' is missing 'latency'");
956 // Either |throughput| or the pair |download_throughput| and
957 // |upload_throughput| is required.
958 if (conditions
->HasKey("throughput")) {
959 if (!conditions
->GetDouble("throughput",
960 &network_conditions
->download_throughput
))
961 return Status(kUnknownError
, "invalid 'throughput'");
962 conditions
->GetDouble("throughput",
963 &network_conditions
->upload_throughput
);
964 } else if (conditions
->HasKey("download_throughput") &&
965 conditions
->HasKey("upload_throughput")) {
966 if (!conditions
->GetDouble("download_throughput",
967 &network_conditions
->download_throughput
) ||
968 !conditions
->GetDouble("upload_throughput",
969 &network_conditions
->upload_throughput
))
970 return Status(kUnknownError
,
971 "invalid 'download_throughput' or 'upload_throughput'");
973 return Status(kUnknownError
,
974 "invalid 'network_conditions' is missing 'throughput' or "
975 "'download_throughput'/'upload_throughput' pair");
978 // |offline| is optional.
979 if (conditions
->HasKey("offline")) {
980 if (!conditions
->GetBoolean("offline", &network_conditions
->offline
))
981 return Status(kUnknownError
, "invalid 'offline'");
983 network_conditions
->offline
= false;
986 return Status(kUnknownError
,
987 "either 'network_conditions' or 'network_name' must be "
991 session
->overridden_network_conditions
.reset(
992 network_conditions
.release());
993 return web_view
->OverrideNetworkConditions(
994 *session
->overridden_network_conditions
);
997 Status
ExecuteDeleteNetworkConditions(
1000 const base::DictionaryValue
& params
,
1001 scoped_ptr
<base::Value
>* value
) {
1002 // Chrome does not have any command to stop overriding network conditions, so
1003 // we just override the network conditions with the "No throttling" preset.
1004 NetworkConditions network_conditions
;
1005 // Get conditions from preset list.
1006 Status status
= FindPresetNetwork("No throttling", &network_conditions
);
1007 if (status
.IsError())
1010 status
= web_view
->OverrideNetworkConditions(network_conditions
);
1011 if (status
.IsError())
1014 // After we've successfully overridden the network conditions with
1015 // "No throttling", we can delete them from |session|.
1016 session
->overridden_network_conditions
.reset();
1020 Status
ExecuteTakeHeapSnapshot(
1023 const base::DictionaryValue
& params
,
1024 scoped_ptr
<base::Value
>* value
) {
1025 return web_view
->TakeHeapSnapshot(value
);