Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: chrome/test/webdriver/webdriver_session.cc

Issue 23526047: Delete old chromedriver code, and remove mongoose webserver. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/test/webdriver/webdriver_session.h"
6
7 #include <sstream>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/files/file_path.h"
15 #include "base/json/json_reader.h"
16 #include "base/json/json_writer.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/message_loop/message_loop_proxy.h"
19 #include "base/process/process.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/synchronization/waitable_event.h"
26 #include "base/test/test_timeouts.h"
27 #include "base/threading/platform_thread.h"
28 #include "base/time/time.h"
29 #include "base/values.h"
30 #include "chrome/app/chrome_command_ids.h"
31 #include "chrome/common/chrome_constants.h"
32 #include "chrome/common/chrome_switches.h"
33 #include "chrome/test/automation/automation_json_requests.h"
34 #include "chrome/test/automation/value_conversion_util.h"
35 #include "chrome/test/webdriver/webdriver_capabilities_parser.h"
36 #include "chrome/test/webdriver/webdriver_error.h"
37 #include "chrome/test/webdriver/webdriver_key_converter.h"
38 #include "chrome/test/webdriver/webdriver_logging.h"
39 #include "chrome/test/webdriver/webdriver_session_manager.h"
40 #include "chrome/test/webdriver/webdriver_util.h"
41 #include "third_party/webdriver/atoms.h"
42
43 using automation::kLeftButton;
44 using automation::kMouseDown;
45 using automation::kMouseMove;
46 using automation::kMouseUp;
47 using automation::kNoButton;
48
49 namespace webdriver {
50
51 namespace {
52 // This is the minimum version of chrome that supports the new mouse API.
53 const int kNewMouseAPIMinVersion = 1002;
54 }
55
56 FrameId::FrameId() {}
57
58 FrameId::FrameId(const WebViewId& view_id, const FramePath& frame_path)
59 : view_id(view_id),
60 frame_path(frame_path) {
61 }
62
63 Session::Session()
64 : session_log_(new InMemoryLog()),
65 logger_(kAllLogLevel),
66 id_(GenerateRandomID()),
67 current_target_(FrameId(WebViewId(), FramePath())),
68 thread_(id_.c_str()),
69 async_script_timeout_(0),
70 implicit_wait_(0),
71 has_alert_prompt_text_(false),
72 sticky_modifiers_(0),
73 build_no_(0) {
74 SessionManager::GetInstance()->Add(this);
75 logger_.AddHandler(session_log_.get());
76 if (FileLog::Get())
77 logger_.AddHandler(FileLog::Get());
78 }
79
80 Session::~Session() {
81 SessionManager::GetInstance()->Remove(id_);
82 }
83
84 Error* Session::Init(const base::DictionaryValue* capabilities_dict) {
85 if (!thread_.Start()) {
86 delete this;
87 return new Error(kUnknownError, "Cannot start session thread");
88 }
89 if (!temp_dir_.CreateUniqueTempDir()) {
90 delete this;
91 return new Error(
92 kUnknownError, "Unable to create temp directory for unpacking");
93 }
94 logger_.Log(kFineLogLevel,
95 "Initializing session with capabilities " +
96 JsonStringifyForDisplay(capabilities_dict));
97 CapabilitiesParser parser(
98 capabilities_dict, temp_dir_.path(), logger_, &capabilities_);
99 Error* error = parser.Parse();
100 if (error) {
101 delete this;
102 return error;
103 }
104 logger_.set_min_log_level(capabilities_.log_levels[LogType::kDriver]);
105
106 Automation::BrowserOptions browser_options;
107 browser_options.command = capabilities_.command;
108 browser_options.channel_id = capabilities_.channel;
109 browser_options.detach_process = capabilities_.detach;
110 browser_options.user_data_dir = capabilities_.profile;
111 browser_options.exclude_switches = capabilities_.exclude_switches;
112 if (!capabilities_.no_website_testing_defaults) {
113 browser_options.ignore_certificate_errors = true;
114 }
115 RunSessionTask(base::Bind(
116 &Session::InitOnSessionThread,
117 base::Unretained(this),
118 browser_options,
119 &build_no_,
120 &error));
121 if (!error)
122 error = PostBrowserStartInit();
123
124 if (error)
125 Terminate();
126 return error;
127 }
128
129 Error* Session::BeforeExecuteCommand() {
130 Error* error = AfterExecuteCommand();
131 if (!error) {
132 scoped_ptr<Error> switch_error(SwitchToTopFrameIfCurrentFrameInvalid());
133 if (switch_error.get()) {
134 std::string text;
135 scoped_ptr<Error> alert_error(GetAlertMessage(&text));
136 if (alert_error.get()) {
137 // Only return a frame checking error if a modal dialog is not present.
138 // TODO(kkania): This is ugly. Fix.
139 return switch_error.release();
140 }
141 }
142 }
143 return error;
144 }
145
146 Error* Session::AfterExecuteCommand() {
147 Error* error = NULL;
148 if (!capabilities_.load_async) {
149 error = WaitForAllViewsToStopLoading();
150 }
151 return error;
152 }
153
154 void Session::Terminate() {
155 RunSessionTask(base::Bind(
156 &Session::TerminateOnSessionThread,
157 base::Unretained(this)));
158 delete this;
159 }
160
161 Error* Session::ExecuteScript(const FrameId& frame_id,
162 const std::string& script,
163 const base::ListValue* const args,
164 base::Value** value) {
165 std::string args_as_json;
166 base::JSONWriter::Write(static_cast<const base::Value* const>(args),
167 &args_as_json);
168
169 // Every injected script is fed through the executeScript atom. This atom
170 // will catch any errors that are thrown and convert them to the
171 // appropriate JSON structure.
172 std::string jscript = base::StringPrintf(
173 "window.domAutomationController.send((%s).apply(null,"
174 "[function(){%s\n},%s,true]));",
175 atoms::asString(atoms::EXECUTE_SCRIPT).c_str(), script.c_str(),
176 args_as_json.c_str());
177
178 return ExecuteScriptAndParseValue(frame_id, jscript, value);
179 }
180
181 Error* Session::ExecuteScript(const std::string& script,
182 const base::ListValue* const args,
183 base::Value** value) {
184 return ExecuteScript(current_target_, script, args, value);
185 }
186
187 Error* Session::ExecuteScriptAndParse(const FrameId& frame_id,
188 const std::string& anonymous_func_script,
189 const std::string& script_name,
190 const base::ListValue* args,
191 const ValueParser* parser) {
192 scoped_ptr<const base::ListValue> scoped_args(args);
193 scoped_ptr<const ValueParser> scoped_parser(parser);
194 std::string called_script = base::StringPrintf(
195 "return (%s).apply(null, arguments);", anonymous_func_script.c_str());
196 base::Value* unscoped_value = NULL;
197 Error* error = ExecuteScript(frame_id, called_script, args, &unscoped_value);
198 if (error) {
199 error->AddDetails(script_name + " execution failed");
200 return error;
201 }
202
203 scoped_ptr<base::Value> value(unscoped_value);
204 std::string error_msg;
205 if (!parser->Parse(value.get())) {
206 error_msg = base::StringPrintf("%s returned invalid value: %s",
207 script_name.c_str(), JsonStringify(value.get()).c_str());
208 return new Error(kUnknownError, error_msg);
209 }
210 return NULL;
211 }
212
213 Error* Session::ExecuteAsyncScript(const FrameId& frame_id,
214 const std::string& script,
215 const base::ListValue* const args,
216 base::Value** value) {
217 std::string args_as_json;
218 base::JSONWriter::Write(static_cast<const base::Value* const>(args),
219 &args_as_json);
220
221 int timeout_ms = async_script_timeout();
222
223 // Every injected script is fed through the executeScript atom. This atom
224 // will catch any errors that are thrown and convert them to the
225 // appropriate JSON structure.
226 std::string jscript = base::StringPrintf(
227 "(%s).apply(null, [function(){%s},%s,%d,%s,true]);",
228 atoms::asString(atoms::EXECUTE_ASYNC_SCRIPT).c_str(),
229 script.c_str(),
230 args_as_json.c_str(),
231 timeout_ms,
232 "function(result) {window.domAutomationController.send(result);}");
233
234 return ExecuteScriptAndParseValue(frame_id, jscript, value);
235 }
236
237 Error* Session::SendKeys(const ElementId& element, const string16& keys) {
238 bool is_displayed = false;
239 Error* error = IsElementDisplayed(
240 current_target_, element, true /* ignore_opacity */, &is_displayed);
241 if (error)
242 return error;
243 if (!is_displayed)
244 return new Error(kElementNotVisible);
245
246 bool is_enabled = false;
247 error = IsElementEnabled(current_target_, element, &is_enabled);
248 if (error)
249 return error;
250 if (!is_enabled)
251 return new Error(kInvalidElementState);
252
253 // Focus the target element in order to send keys to it.
254 // First, the currently active element is blurred, if it is different from
255 // the target element. We do not want to blur an element unnecessarily,
256 // because this may cause us to lose the current cursor position in the
257 // element.
258 // Secondly, we focus the target element.
259 // Thirdly, if the target element is newly focused and is a text input, we
260 // set the cursor position at the end.
261 // Fourthly, we check if the new active element is the target element. If not,
262 // we throw an error.
263 // Additional notes:
264 // - |document.activeElement| is the currently focused element, or body if
265 // no element is focused
266 // - Even if |document.hasFocus()| returns true and the active element is
267 // the body, sometimes we still need to focus the body element for send
268 // keys to work. Not sure why
269 // - You cannot focus a descendant of a content editable node
270 // - V8 throws a TypeError when calling setSelectionRange for a non-text
271 // input, which still have setSelectionRange defined.
272 // TODO(jleyba): Update this to use the correct atom.
273 const char* kFocusScript =
274 "function(elem) {"
275 " var doc = elem.ownerDocument || elem;"
276 " var prevActiveElem = doc.activeElement;"
277 " if (elem != prevActiveElem && prevActiveElem)"
278 " prevActiveElem.blur();"
279 " elem.focus();"
280 " if (elem != prevActiveElem && elem.value && elem.value.length &&"
281 " elem.setSelectionRange) {"
282 " try {"
283 " elem.setSelectionRange(elem.value.length, elem.value.length);"
284 " } catch (error) {"
285 " if (!(error instanceof TypeError)) {"
286 " throw error;"
287 " }"
288 " }"
289 " }"
290 " if (elem != doc.activeElement)"
291 " throw new Error('Failed to send keys because cannot focus element');"
292 "}";
293 error = ExecuteScriptAndParse(current_target_,
294 kFocusScript,
295 "focusElement",
296 CreateListValueFrom(element),
297 CreateDirectValueParser(kSkipParsing));
298 if (error)
299 return error;
300
301 RunSessionTask(base::Bind(
302 &Session::SendKeysOnSessionThread,
303 base::Unretained(this),
304 keys,
305 true /* release_modifiers */,
306 &error));
307 return error;
308 }
309
310 Error* Session::SendKeys(const string16& keys) {
311 Error* error = NULL;
312 RunSessionTask(base::Bind(
313 &Session::SendKeysOnSessionThread,
314 base::Unretained(this),
315 keys,
316 false /* release_modifiers */,
317 &error));
318 return error;
319 }
320
321 Error* Session::DragAndDropFilePaths(
322 const Point& location,
323 const std::vector<base::FilePath::StringType>& paths) {
324 Error* error = NULL;
325 RunSessionTask(base::Bind(
326 &Automation::DragAndDropFilePaths,
327 base::Unretained(automation_.get()),
328 current_target_.view_id,
329 location,
330 paths,
331 &error));
332 return error;
333 }
334
335 Error* Session::NavigateToURL(const std::string& url) {
336 if (!current_target_.view_id.IsTab()) {
337 return new Error(kUnknownError,
338 "The current target does not support navigation");
339 }
340 Error* error = NULL;
341 if (capabilities_.load_async) {
342 RunSessionTask(base::Bind(
343 &Automation::NavigateToURLAsync,
344 base::Unretained(automation_.get()),
345 current_target_.view_id,
346 url,
347 &error));
348 } else {
349 RunSessionTask(base::Bind(
350 &Automation::NavigateToURL,
351 base::Unretained(automation_.get()),
352 current_target_.view_id,
353 url,
354 &error));
355 }
356 return error;
357 }
358
359 Error* Session::GoForward() {
360 if (!current_target_.view_id.IsTab()) {
361 return new Error(kUnknownError,
362 "The current target does not support navigation");
363 }
364 Error* error = NULL;
365 RunSessionTask(base::Bind(
366 &Automation::GoForward,
367 base::Unretained(automation_.get()),
368 current_target_.view_id,
369 &error));
370 return error;
371 }
372
373 Error* Session::GoBack() {
374 if (!current_target_.view_id.IsTab()) {
375 return new Error(kUnknownError,
376 "The current target does not support navigation");
377 }
378 Error* error = NULL;
379 RunSessionTask(base::Bind(
380 &Automation::GoBack,
381 base::Unretained(automation_.get()),
382 current_target_.view_id,
383 &error));
384 return error;
385 }
386
387 Error* Session::Reload() {
388 if (!current_target_.view_id.IsTab()) {
389 return new Error(kUnknownError,
390 "The current target does not support navigation");
391 }
392 Error* error = NULL;
393 RunSessionTask(base::Bind(
394 &Automation::Reload,
395 base::Unretained(automation_.get()),
396 current_target_.view_id,
397 &error));
398 return error;
399 }
400
401 Error* Session::GetURL(std::string* url) {
402 return ExecuteScriptAndParse(current_target_,
403 "function() { return document.URL }",
404 "getUrl",
405 new base::ListValue(),
406 CreateDirectValueParser(url));
407 }
408
409 Error* Session::GetTitle(std::string* tab_title) {
410 const char* kGetTitleScript =
411 "function() {"
412 " if (document.title)"
413 " return document.title;"
414 " else"
415 " return document.URL;"
416 "}";
417 return ExecuteScriptAndParse(FrameId(current_target_.view_id, FramePath()),
418 kGetTitleScript,
419 "getTitle",
420 new base::ListValue(),
421 CreateDirectValueParser(tab_title));
422 }
423
424 Error* Session::MouseMoveAndClick(const Point& location,
425 automation::MouseButton button) {
426 Error* error = NULL;
427 if (build_no_ >= kNewMouseAPIMinVersion) {
428 std::vector<WebMouseEvent> events;
429 events.push_back(CreateWebMouseEvent(kMouseMove, kNoButton, location, 0));
430 events.push_back(CreateWebMouseEvent(kMouseDown, button, location, 1));
431 events.push_back(CreateWebMouseEvent(kMouseUp, button, location, 1));
432 error = ProcessWebMouseEvents(events);
433 } else {
434 RunSessionTask(base::Bind(
435 &Automation::MouseClickDeprecated,
436 base::Unretained(automation_.get()),
437 current_target_.view_id,
438 location,
439 button,
440 &error));
441 }
442 if (!error)
443 mouse_position_ = location;
444 return error;
445 }
446
447 Error* Session::MouseMove(const Point& location) {
448 Error* error = NULL;
449 if (build_no_ >= kNewMouseAPIMinVersion) {
450 std::vector<WebMouseEvent> events;
451 events.push_back(CreateWebMouseEvent(kMouseMove, kNoButton, location, 0));
452 error = ProcessWebMouseEvents(events);
453 } else {
454 RunSessionTask(base::Bind(
455 &Automation::MouseMoveDeprecated,
456 base::Unretained(automation_.get()),
457 current_target_.view_id,
458 location,
459 &error));
460 }
461 if (!error)
462 mouse_position_ = location;
463 return error;
464 }
465
466 Error* Session::MouseDrag(const Point& start,
467 const Point& end) {
468 Error* error = NULL;
469 if (build_no_ >= kNewMouseAPIMinVersion) {
470 std::vector<WebMouseEvent> events;
471 events.push_back(CreateWebMouseEvent(kMouseMove, kNoButton, start, 0));
472 events.push_back(CreateWebMouseEvent(kMouseDown, kLeftButton, start, 1));
473 events.push_back(CreateWebMouseEvent(kMouseMove, kLeftButton, end, 0));
474 events.push_back(CreateWebMouseEvent(kMouseUp, kLeftButton, end, 1));
475 error = ProcessWebMouseEvents(events);
476 } else {
477 RunSessionTask(base::Bind(
478 &Automation::MouseDragDeprecated,
479 base::Unretained(automation_.get()),
480 current_target_.view_id,
481 start,
482 end,
483 &error));
484 }
485 if (!error)
486 mouse_position_ = end;
487 return error;
488 }
489
490 Error* Session::MouseClick(automation::MouseButton button) {
491 if (build_no_ >= kNewMouseAPIMinVersion) {
492 std::vector<WebMouseEvent> events;
493 events.push_back(CreateWebMouseEvent(
494 kMouseDown, button, mouse_position_, 1));
495 events.push_back(CreateWebMouseEvent(
496 kMouseUp, button, mouse_position_, 1));
497 return ProcessWebMouseEvents(events);
498 } else {
499 return MouseMoveAndClick(mouse_position_, button);
500 }
501 }
502
503 Error* Session::MouseButtonDown() {
504 Error* error = NULL;
505 if (build_no_ >= kNewMouseAPIMinVersion) {
506 std::vector<WebMouseEvent> events;
507 events.push_back(CreateWebMouseEvent(
508 kMouseDown, kLeftButton, mouse_position_, 1));
509 error = ProcessWebMouseEvents(events);
510 } else {
511 RunSessionTask(base::Bind(
512 &Automation::MouseButtonDownDeprecated,
513 base::Unretained(automation_.get()),
514 current_target_.view_id,
515 mouse_position_,
516 &error));
517 }
518 return error;
519 }
520
521 Error* Session::MouseButtonUp() {
522 Error* error = NULL;
523 if (build_no_ >= kNewMouseAPIMinVersion) {
524 std::vector<WebMouseEvent> events;
525 events.push_back(CreateWebMouseEvent(
526 kMouseUp, kLeftButton, mouse_position_, 1));
527 error = ProcessWebMouseEvents(events);
528 } else {
529 RunSessionTask(base::Bind(
530 &Automation::MouseButtonUpDeprecated,
531 base::Unretained(automation_.get()),
532 current_target_.view_id,
533 mouse_position_,
534 &error));
535 }
536 return error;
537 }
538
539 Error* Session::MouseDoubleClick() {
540 Error* error = NULL;
541 if (build_no_ >= kNewMouseAPIMinVersion) {
542 std::vector<WebMouseEvent> events;
543 events.push_back(CreateWebMouseEvent(
544 kMouseDown, kLeftButton, mouse_position_, 1));
545 events.push_back(CreateWebMouseEvent(
546 kMouseUp, kLeftButton, mouse_position_, 1));
547 events.push_back(CreateWebMouseEvent(
548 kMouseDown, kLeftButton, mouse_position_, 2));
549 events.push_back(CreateWebMouseEvent(
550 kMouseUp, kLeftButton, mouse_position_, 2));
551 error = ProcessWebMouseEvents(events);
552 } else {
553 RunSessionTask(base::Bind(
554 &Automation::MouseDoubleClickDeprecated,
555 base::Unretained(automation_.get()),
556 current_target_.view_id,
557 mouse_position_,
558 &error));
559 }
560 return error;
561 }
562
563 Error* Session::GetCookies(const std::string& url,
564 scoped_ptr<base::ListValue>* cookies) {
565 Error* error = NULL;
566 RunSessionTask(base::Bind(
567 &Automation::GetCookies,
568 base::Unretained(automation_.get()),
569 url,
570 cookies,
571 &error));
572 return error;
573 }
574
575 Error* Session::DeleteCookie(const std::string& url,
576 const std::string& cookie_name) {
577 Error* error = NULL;
578 RunSessionTask(base::Bind(
579 &Automation::DeleteCookie,
580 base::Unretained(automation_.get()),
581 url,
582 cookie_name,
583 &error));
584 return error;
585 }
586
587 // Note that when this is called from CookieCommand::ExecutePost then
588 // |cookie_dict| is destroyed as soon as the caller finishes. Therefore
589 // it is essential that RunSessionTask executes synchronously.
590 Error* Session::SetCookie(const std::string& url,
591 base::DictionaryValue* cookie_dict) {
592 Error* error = NULL;
593 RunSessionTask(base::Bind(
594 &Automation::SetCookie,
595 base::Unretained(automation_.get()),
596 url,
597 cookie_dict,
598 &error));
599 return error;
600 }
601
602 Error* Session::GetViews(std::vector<WebViewInfo>* views) {
603 Error* error = NULL;
604 RunSessionTask(base::Bind(
605 &Automation::GetViews,
606 base::Unretained(automation_.get()),
607 views,
608 &error));
609 return error;
610 }
611
612 Error* Session::SwitchToView(const std::string& id_or_name) {
613 Error* error = NULL;
614 bool does_exist = false;
615
616 WebViewId new_view;
617 StringToWebViewId(id_or_name, &new_view);
618 if (new_view.IsValid()) {
619 RunSessionTask(base::Bind(
620 &Automation::DoesViewExist,
621 base::Unretained(automation_.get()),
622 new_view,
623 &does_exist,
624 &error));
625 if (error)
626 return error;
627 }
628
629 if (!does_exist) {
630 // See if any of the tab window names match |name|.
631 std::vector<WebViewInfo> views;
632 Error* error = GetViews(&views);
633 if (error)
634 return error;
635 for (size_t i = 0; i < views.size(); ++i) {
636 if (!views[i].view_id.IsTab())
637 continue;
638 std::string window_name;
639 Error* error = ExecuteScriptAndParse(
640 FrameId(views[i].view_id, FramePath()),
641 "function() { return window.name; }",
642 "getWindowName",
643 new base::ListValue(),
644 CreateDirectValueParser(&window_name));
645 if (error)
646 return error;
647 if (id_or_name == window_name) {
648 new_view = views[i].view_id;
649 does_exist = true;
650 break;
651 }
652 }
653 }
654
655 if (!does_exist)
656 return new Error(kNoSuchWindow);
657 frame_elements_.clear();
658 current_target_ = FrameId(new_view, FramePath());
659 return NULL;
660 }
661
662 Error* Session::SwitchToFrameWithNameOrId(const std::string& name_or_id) {
663 std::string script =
664 "function(arg) {"
665 " var xpath = '(/html/body//iframe|/html/frameset/frame)';"
666 " var sub = function(s) { return s.replace(/\\$/g, arg); };"
667 " xpath += sub('[@name=\"$\" or @id=\"$\"]');"
668 " return document.evaluate(xpath, document, null, "
669 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
670 "}";
671 return SwitchToFrameWithJavaScriptLocatedFrame(
672 script, CreateListValueFrom(name_or_id));
673 }
674
675 Error* Session::SwitchToFrameWithIndex(int index) {
676 // We cannot simply index into window.frames because we need to know the
677 // tagName of the frameElement. If child frame N is from another domain, then
678 // the following will run afoul of the same origin policy:
679 // window.frames[N].frameElement;
680 // Instead of indexing window.frames, we use an XPath expression to index
681 // into the list of all IFRAME and FRAME elements on the page - if we find
682 // something, then that XPath expression can be used as the new frame's XPath.
683 std::string script =
684 "function(index) {"
685 " var xpathIndex = '[' + (index + 1) + ']';"
686 " var xpath = '(/html/body//iframe|/html/frameset/frame)' + "
687 " xpathIndex;"
688 " return document.evaluate(xpath, document, null, "
689 " XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
690 "}";
691 return SwitchToFrameWithJavaScriptLocatedFrame(
692 script, CreateListValueFrom(index));
693 }
694
695 Error* Session::SwitchToFrameWithElement(const ElementId& element) {
696 // TODO(jleyba): Extract this, and the other frame switch methods to an atom.
697 std::string script =
698 "function(elem) {"
699 " if (elem.nodeType != 1 || !/^i?frame$/i.test(elem.tagName)) {"
700 " console.error('Element is not a frame');"
701 " return null;"
702 " }"
703 " for (var i = 0; i < window.frames.length; i++) {"
704 " if (elem.contentWindow == window.frames[i]) {"
705 " return elem;"
706 " }"
707 " }"
708 " console.info('Frame is not connected to this DOM tree');"
709 " return null;"
710 "}";
711 return SwitchToFrameWithJavaScriptLocatedFrame(
712 script, CreateListValueFrom(element));
713 }
714
715 void Session::SwitchToTopFrame() {
716 frame_elements_.clear();
717 current_target_.frame_path = FramePath();
718 }
719
720 Error* Session::SwitchToTopFrameIfCurrentFrameInvalid() {
721 std::vector<std::string> components;
722 current_target_.frame_path.GetComponents(&components);
723 if (frame_elements_.size() != components.size()) {
724 return new Error(kUnknownError,
725 "Frame element vector out of sync with frame path");
726 }
727 FramePath frame_path;
728 // Start from the root path and check that each frame element that makes
729 // up the current frame target is valid by executing an empty script.
730 // This code should not execute script in any frame before making sure the
731 // frame element is valid, otherwise the automation hangs until a timeout.
732 for (size_t i = 0; i < frame_elements_.size(); ++i) {
733 FrameId frame_id(current_target_.view_id, frame_path);
734 scoped_ptr<Error> error(ExecuteScriptAndParse(
735 frame_id,
736 "function(){ }",
737 "emptyScript",
738 CreateListValueFrom(frame_elements_[i]),
739 CreateDirectValueParser(kSkipParsing)));
740 if (error.get() && error->code() == kStaleElementReference) {
741 SwitchToTopFrame();
742 } else if (error.get()) {
743 return error.release();
744 }
745 frame_path = frame_path.Append(components[i]);
746 }
747 return NULL;
748 }
749
750 Error* Session::CloseWindow() {
751 Error* error = NULL;
752 RunSessionTask(base::Bind(
753 &Automation::CloseView,
754 base::Unretained(automation_.get()),
755 current_target_.view_id,
756 &error));
757
758 if (!error) {
759 std::vector<WebViewInfo> views;
760 scoped_ptr<Error> error(GetViews(&views));
761 if (error.get() || views.empty()) {
762 // The automation connection will soon be closed, if not already,
763 // because we supposedly just closed the last window. Terminate the
764 // session.
765 // TODO(kkania): This will cause us problems if GetWindowIds fails for a
766 // reason other than the channel is disconnected. Look into having
767 // |GetWindowIds| tell us if it just closed the last window.
768 Terminate();
769 }
770 }
771 return error;
772 }
773
774 Error* Session::GetWindowBounds(const WebViewId& window, Rect* bounds) {
775 const char* kGetWindowBoundsScript =
776 "function() {"
777 " return {"
778 " 'left': window.screenX,"
779 " 'top': window.screenY,"
780 " 'width': window.outerWidth,"
781 " 'height': window.outerHeight"
782 " }"
783 "}";
784 return ExecuteScriptAndParse(
785 FrameId(window, FramePath()),
786 kGetWindowBoundsScript,
787 "getWindowBoundsScript",
788 new base::ListValue(),
789 CreateDirectValueParser(bounds));
790 }
791
792 Error* Session::SetWindowBounds(
793 const WebViewId& window,
794 const Rect& bounds) {
795 Error* error = NULL;
796 RunSessionTask(base::Bind(
797 &Automation::SetViewBounds,
798 base::Unretained(automation_.get()),
799 window,
800 bounds,
801 &error));
802 return error;
803 }
804
805 Error* Session::MaximizeWindow(const WebViewId& window) {
806 Error* error = NULL;
807 RunSessionTask(base::Bind(
808 &Automation::MaximizeView,
809 base::Unretained(automation_.get()),
810 window,
811 &error));
812 return error;
813 }
814
815 Error* Session::GetAlertMessage(std::string* text) {
816 Error* error = NULL;
817 RunSessionTask(base::Bind(
818 &Automation::GetAppModalDialogMessage,
819 base::Unretained(automation_.get()),
820 text,
821 &error));
822 return error;
823 }
824
825 Error* Session::SetAlertPromptText(const std::string& alert_prompt_text) {
826 std::string message_text;
827 // Only set the alert prompt text if an alert is actually active.
828 Error* error = GetAlertMessage(&message_text);
829 if (!error) {
830 has_alert_prompt_text_ = true;
831 alert_prompt_text_ = alert_prompt_text;
832 }
833 return error;
834 }
835
836 Error* Session::AcceptOrDismissAlert(bool accept) {
837 Error* error = NULL;
838 if (accept && has_alert_prompt_text_) {
839 RunSessionTask(base::Bind(
840 &Automation::AcceptPromptAppModalDialog,
841 base::Unretained(automation_.get()),
842 alert_prompt_text_,
843 &error));
844 } else {
845 RunSessionTask(base::Bind(
846 &Automation::AcceptOrDismissAppModalDialog,
847 base::Unretained(automation_.get()),
848 accept,
849 &error));
850 }
851 has_alert_prompt_text_ = false;
852 return error;
853 }
854
855 std::string Session::GetBrowserVersion() {
856 std::string version;
857 RunSessionTask(base::Bind(
858 &Automation::GetBrowserVersion,
859 base::Unretained(automation_.get()),
860 &version));
861 return version;
862 }
863
864 Error* Session::CompareBrowserVersion(int client_build_no,
865 int client_patch_no,
866 bool* is_newer_or_equal) {
867 std::string version = GetBrowserVersion();
868 std::vector<std::string> split_version;
869 base::SplitString(version, '.', &split_version);
870 if (split_version.size() != 4) {
871 return new Error(
872 kUnknownError, "Browser version has unrecognized format: " + version);
873 }
874 int build_no, patch_no;
875 if (!base::StringToInt(split_version[2], &build_no) ||
876 !base::StringToInt(split_version[3], &patch_no)) {
877 return new Error(
878 kUnknownError, "Browser version has unrecognized format: " + version);
879 }
880 if (build_no < client_build_no)
881 *is_newer_or_equal = false;
882 else if (build_no > client_build_no)
883 *is_newer_or_equal = true;
884 else
885 *is_newer_or_equal = patch_no >= client_patch_no;
886 return NULL;
887 }
888
889 Error* Session::FindElement(const FrameId& frame_id,
890 const ElementId& root_element,
891 const std::string& locator,
892 const std::string& query,
893 ElementId* element) {
894 std::vector<ElementId> elements;
895 Error* error = FindElementsHelper(
896 frame_id, root_element, locator, query, true, &elements);
897 if (!error)
898 *element = elements[0];
899 return error;
900 }
901
902 Error* Session::FindElements(const FrameId& frame_id,
903 const ElementId& root_element,
904 const std::string& locator,
905 const std::string& query,
906 std::vector<ElementId>* elements) {
907 return FindElementsHelper(
908 frame_id, root_element, locator, query, false, elements);
909 }
910
911 Error* Session::GetElementLocationInView(
912 const ElementId& element,
913 Point* location) {
914 Size size;
915 Error* error = GetElementSize(current_target_, element, &size);
916 if (error)
917 return error;
918 return GetElementRegionInView(
919 element, Rect(Point(0, 0), size),
920 false /* center */, false /* verify_clickable_at_middle */, location);
921 }
922
923 Error* Session::GetElementRegionInView(
924 const ElementId& element,
925 const Rect& region,
926 bool center,
927 bool verify_clickable_at_middle,
928 Point* location) {
929 CHECK(element.is_valid());
930
931 Point region_offset = region.origin();
932 Size region_size = region.size();
933 Error* error = GetElementRegionInViewHelper(
934 current_target_, element, region, center, verify_clickable_at_middle,
935 &region_offset);
936 if (error)
937 return error;
938
939 for (FramePath frame_path = current_target_.frame_path;
940 frame_path.IsSubframe();
941 frame_path = frame_path.Parent()) {
942 // Find the frame element for the current frame path.
943 FrameId frame_id(current_target_.view_id, frame_path.Parent());
944 ElementId frame_element;
945 error = FindElement(frame_id,
946 ElementId(std::string()),
947 LocatorType::kXpath,
948 frame_path.BaseName().value(),
949 &frame_element);
950 if (error) {
951 std::string context = base::StringPrintf(
952 "Could not find frame element (%s) in frame (%s)",
953 frame_path.BaseName().value().c_str(),
954 frame_path.Parent().value().c_str());
955 error->AddDetails(context);
956 return error;
957 }
958 // Modify |region_offset| by the frame's border.
959 int border_left, border_top;
960 error = GetElementBorder(
961 frame_id, frame_element, &border_left, &border_top);
962 if (error)
963 return error;
964 region_offset.Offset(border_left, border_top);
965
966 error = GetElementRegionInViewHelper(
967 frame_id, frame_element, Rect(region_offset, region_size),
968 center, verify_clickable_at_middle, &region_offset);
969 if (error)
970 return error;
971 }
972 *location = region_offset;
973 return NULL;
974 }
975
976 Error* Session::GetElementSize(const FrameId& frame_id,
977 const ElementId& element,
978 Size* size) {
979 return ExecuteScriptAndParse(
980 frame_id,
981 atoms::asString(atoms::GET_SIZE),
982 "getSize",
983 CreateListValueFrom(element),
984 CreateDirectValueParser(size));
985 }
986
987 Error* Session::GetElementFirstClientRect(const FrameId& frame_id,
988 const ElementId& element,
989 Rect* rect) {
990 return ExecuteScriptAndParse(
991 frame_id,
992 atoms::asString(atoms::GET_FIRST_CLIENT_RECT),
993 "getFirstClientRect",
994 CreateListValueFrom(element),
995 CreateDirectValueParser(rect));
996 }
997
998 Error* Session::GetElementEffectiveStyle(
999 const FrameId& frame_id,
1000 const ElementId& element,
1001 const std::string& prop,
1002 std::string* value) {
1003 return ExecuteScriptAndParse(
1004 frame_id,
1005 atoms::asString(atoms::GET_EFFECTIVE_STYLE),
1006 "getEffectiveStyle",
1007 CreateListValueFrom(element, prop),
1008 CreateDirectValueParser(value));
1009 }
1010
1011 Error* Session::GetElementBorder(const FrameId& frame_id,
1012 const ElementId& element,
1013 int* border_left,
1014 int* border_top) {
1015 std::string border_left_str, border_top_str;
1016 Error* error = GetElementEffectiveStyle(
1017 frame_id, element, "border-left-width", &border_left_str);
1018 if (error)
1019 return error;
1020 error = GetElementEffectiveStyle(
1021 frame_id, element, "border-top-width", &border_top_str);
1022 if (error)
1023 return error;
1024
1025 base::StringToInt(border_left_str, border_left);
1026 base::StringToInt(border_top_str, border_top);
1027 return NULL;
1028 }
1029
1030 Error* Session::IsElementDisplayed(const FrameId& frame_id,
1031 const ElementId& element,
1032 bool ignore_opacity,
1033 bool* is_displayed) {
1034 return ExecuteScriptAndParse(
1035 frame_id,
1036 atoms::asString(atoms::IS_DISPLAYED),
1037 "isDisplayed",
1038 CreateListValueFrom(element, ignore_opacity),
1039 CreateDirectValueParser(is_displayed));
1040 }
1041
1042 Error* Session::IsElementEnabled(const FrameId& frame_id,
1043 const ElementId& element,
1044 bool* is_enabled) {
1045 return ExecuteScriptAndParse(
1046 frame_id,
1047 atoms::asString(atoms::IS_ENABLED),
1048 "isEnabled",
1049 CreateListValueFrom(element),
1050 CreateDirectValueParser(is_enabled));
1051 }
1052
1053 Error* Session::IsOptionElementSelected(const FrameId& frame_id,
1054 const ElementId& element,
1055 bool* is_selected) {
1056 return ExecuteScriptAndParse(
1057 frame_id,
1058 atoms::asString(atoms::IS_SELECTED),
1059 "isSelected",
1060 CreateListValueFrom(element),
1061 CreateDirectValueParser(is_selected));
1062 }
1063
1064 Error* Session::SetOptionElementSelected(const FrameId& frame_id,
1065 const ElementId& element,
1066 bool selected) {
1067 // This wrapper ensures the script is started successfully and
1068 // allows for an alert to happen when the option selection occurs.
1069 // See selenium bug 2671.
1070 const char kSetSelectedWrapper[] =
1071 "var args = [].slice.apply(arguments);"
1072 "args[args.length - 1]();"
1073 "return (%s).apply(null, args.slice(0, args.length - 1));";
1074 base::Value* value = NULL;
1075 Error* error = ExecuteAsyncScript(
1076 frame_id,
1077 base::StringPrintf(kSetSelectedWrapper,
1078 atoms::asString(atoms::CLICK).c_str()),
1079 CreateListValueFrom(element, selected),
1080 &value);
1081 scoped_ptr<base::Value> scoped_value(value);
1082 return error;
1083 }
1084
1085 Error* Session::ToggleOptionElement(const FrameId& frame_id,
1086 const ElementId& element) {
1087 bool is_selected;
1088 Error* error = IsOptionElementSelected(frame_id, element, &is_selected);
1089 if (error)
1090 return error;
1091
1092 return SetOptionElementSelected(frame_id, element, !is_selected);
1093 }
1094
1095 Error* Session::GetElementTagName(const FrameId& frame_id,
1096 const ElementId& element,
1097 std::string* tag_name) {
1098 return ExecuteScriptAndParse(
1099 frame_id,
1100 "function(elem) { return elem.tagName.toLowerCase() }",
1101 "getElementTagName",
1102 CreateListValueFrom(element),
1103 CreateDirectValueParser(tag_name));
1104 }
1105
1106 Error* Session::GetClickableLocation(const ElementId& element,
1107 Point* location) {
1108 bool is_displayed = false;
1109 Error* error = IsElementDisplayed(
1110 current_target_, element, true /* ignore_opacity */, &is_displayed);
1111 if (error)
1112 return error;
1113 if (!is_displayed)
1114 return new Error(kElementNotVisible, "Element must be displayed to click");
1115
1116 // We try 3 methods to determine clickable location. This mostly follows
1117 // what FirefoxDriver does. Try the first client rect, then the bounding
1118 // client rect, and lastly the size of the element (via closure).
1119 // SVG is one case that doesn't have a first client rect.
1120 Rect rect;
1121 scoped_ptr<Error> ignore_error(
1122 GetElementFirstClientRect(current_target_, element, &rect));
1123 if (ignore_error.get()) {
1124 Rect client_rect;
1125 ignore_error.reset(ExecuteScriptAndParse(
1126 current_target_,
1127 "function(elem) { return elem.getBoundingClientRect() }",
1128 "getBoundingClientRect",
1129 CreateListValueFrom(element),
1130 CreateDirectValueParser(&client_rect)));
1131 rect = Rect(0, 0, client_rect.width(), client_rect.height());
1132 }
1133 if (ignore_error.get()) {
1134 Size size;
1135 ignore_error.reset(GetElementSize(current_target_, element, &size));
1136 rect = Rect(0, 0, size.width(), size.height());
1137 }
1138 if (ignore_error.get()) {
1139 return new Error(kUnknownError,
1140 "Unable to determine clickable location of element");
1141 }
1142
1143 error = GetElementRegionInView(
1144 element, rect, true /* center */, true /* verify_clickable_at_middle */,
1145 location);
1146 if (error)
1147 return error;
1148 location->Offset(rect.width() / 2, rect.height() / 2);
1149 return NULL;
1150 }
1151
1152 Error* Session::GetAttribute(const ElementId& element,
1153 const std::string& key,
1154 base::Value** value) {
1155 return ExecuteScriptAndParse(
1156 current_target_,
1157 atoms::asString(atoms::GET_ATTRIBUTE),
1158 "getAttribute",
1159 CreateListValueFrom(element, key),
1160 CreateDirectValueParser(value));
1161 }
1162
1163 Error* Session::WaitForAllViewsToStopLoading() {
1164 if (!automation_.get())
1165 return NULL;
1166
1167 logger_.Log(kFinerLogLevel, "Waiting for all views to stop loading...");
1168 Error* error = NULL;
1169 RunSessionTask(base::Bind(
1170 &Automation::WaitForAllViewsToStopLoading,
1171 base::Unretained(automation_.get()),
1172 &error));
1173 logger_.Log(kFinerLogLevel, "Done waiting for all views to stop loading");
1174 return error;
1175 }
1176
1177 Error* Session::InstallExtension(
1178 const base::FilePath& path, std::string* extension_id) {
1179 Error* error = NULL;
1180 RunSessionTask(base::Bind(
1181 &Automation::InstallExtension,
1182 base::Unretained(automation_.get()),
1183 path,
1184 extension_id,
1185 &error));
1186 return error;
1187 }
1188
1189 Error* Session::GetExtensionsInfo(base::ListValue* extensions_list) {
1190 Error* error = NULL;
1191 RunSessionTask(base::Bind(
1192 &Automation::GetExtensionsInfo,
1193 base::Unretained(automation_.get()),
1194 extensions_list,
1195 &error));
1196 return error;
1197 }
1198
1199 Error* Session::IsPageActionVisible(
1200 const WebViewId& tab_id,
1201 const std::string& extension_id,
1202 bool* is_visible) {
1203 if (!tab_id.IsTab()) {
1204 return new Error(
1205 kUnknownError,
1206 "The current target does not support page actions. Switch to a tab.");
1207 }
1208 Error* error = NULL;
1209 RunSessionTask(base::Bind(
1210 &Automation::IsPageActionVisible,
1211 base::Unretained(automation_.get()),
1212 tab_id,
1213 extension_id,
1214 is_visible,
1215 &error));
1216 return error;
1217 }
1218
1219 Error* Session::SetExtensionState(
1220 const std::string& extension_id, bool enable) {
1221 Error* error = NULL;
1222 RunSessionTask(base::Bind(
1223 &Automation::SetExtensionState,
1224 base::Unretained(automation_.get()),
1225 extension_id,
1226 enable,
1227 &error));
1228 return error;
1229 }
1230
1231 Error* Session::ClickExtensionButton(
1232 const std::string& extension_id, bool browser_action) {
1233 Error* error = NULL;
1234 RunSessionTask(base::Bind(
1235 &Automation::ClickExtensionButton,
1236 base::Unretained(automation_.get()),
1237 extension_id,
1238 browser_action,
1239 &error));
1240 return error;
1241 }
1242
1243 Error* Session::UninstallExtension(const std::string& extension_id) {
1244 Error* error = NULL;
1245 RunSessionTask(base::Bind(
1246 &Automation::UninstallExtension,
1247 base::Unretained(automation_.get()),
1248 extension_id,
1249 &error));
1250 return error;
1251 }
1252
1253 Error* Session::SetPreference(
1254 const std::string& pref,
1255 bool is_user_pref,
1256 base::Value* value) {
1257 Error* error = NULL;
1258 if (is_user_pref) {
1259 RunSessionTask(base::Bind(
1260 &Automation::SetPreference,
1261 base::Unretained(automation_.get()),
1262 pref,
1263 value,
1264 &error));
1265 if (error)
1266 error->AddDetails("Failed to set user pref '" + pref + "'");
1267 } else {
1268 RunSessionTask(base::Bind(
1269 &Automation::SetLocalStatePreference,
1270 base::Unretained(automation_.get()),
1271 pref,
1272 value,
1273 &error));
1274 if (error)
1275 error->AddDetails("Failed to set local state pref '" + pref + "'");
1276 }
1277 return error;
1278 }
1279
1280 base::ListValue* Session::GetLog() const {
1281 return session_log_->entries_list()->DeepCopy();
1282 }
1283
1284 Error* Session::GetBrowserConnectionState(bool* online) {
1285 return ExecuteScriptAndParse(
1286 current_target_,
1287 atoms::asString(atoms::IS_ONLINE),
1288 "isOnline",
1289 new base::ListValue(),
1290 CreateDirectValueParser(online));
1291 }
1292
1293 Error* Session::GetAppCacheStatus(int* status) {
1294 return ExecuteScriptAndParse(
1295 current_target_,
1296 atoms::asString(atoms::GET_APPCACHE_STATUS),
1297 "getAppcacheStatus",
1298 new base::ListValue(),
1299 CreateDirectValueParser(status));
1300 }
1301
1302 Error* Session::GetStorageSize(StorageType type, int* size) {
1303 std::string js = atoms::asString(
1304 type == kLocalStorageType ? atoms::GET_LOCAL_STORAGE_SIZE
1305 : atoms::GET_SESSION_STORAGE_SIZE);
1306 return ExecuteScriptAndParse(
1307 current_target_,
1308 js,
1309 "getStorageSize",
1310 new base::ListValue(),
1311 CreateDirectValueParser(size));
1312 }
1313
1314 Error* Session::SetStorageItem(StorageType type,
1315 const std::string& key,
1316 const std::string& value) {
1317 std::string js = atoms::asString(
1318 type == kLocalStorageType ? atoms::SET_LOCAL_STORAGE_ITEM
1319 : atoms::SET_SESSION_STORAGE_ITEM);
1320 return ExecuteScriptAndParse(
1321 current_target_,
1322 js,
1323 "setStorageItem",
1324 CreateListValueFrom(key, value),
1325 CreateDirectValueParser(kSkipParsing));
1326 }
1327
1328 Error* Session::ClearStorage(StorageType type) {
1329 std::string js = atoms::asString(
1330 type == kLocalStorageType ? atoms::CLEAR_LOCAL_STORAGE
1331 : atoms::CLEAR_SESSION_STORAGE);
1332 return ExecuteScriptAndParse(
1333 current_target_,
1334 js,
1335 "clearStorage",
1336 new base::ListValue(),
1337 CreateDirectValueParser(kSkipParsing));
1338 }
1339
1340 Error* Session::GetStorageKeys(StorageType type, base::ListValue** keys) {
1341 std::string js = atoms::asString(
1342 type == kLocalStorageType ? atoms::GET_LOCAL_STORAGE_KEYS
1343 : atoms::GET_SESSION_STORAGE_KEYS);
1344 return ExecuteScriptAndParse(
1345 current_target_,
1346 js,
1347 "getStorageKeys",
1348 new base::ListValue(),
1349 CreateDirectValueParser(keys));
1350 }
1351
1352 Error* Session::GetStorageItem(StorageType type,
1353 const std::string& key,
1354 std::string* value) {
1355 std::string js = atoms::asString(
1356 type == kLocalStorageType ? atoms::GET_LOCAL_STORAGE_ITEM
1357 : atoms::GET_SESSION_STORAGE_ITEM);
1358 return ExecuteScriptAndParse(
1359 current_target_,
1360 js,
1361 "getStorageItem",
1362 CreateListValueFrom(key),
1363 CreateDirectValueParser(value));
1364 }
1365
1366 Error* Session::RemoveStorageItem(StorageType type,
1367 const std::string& key,
1368 std::string* value) {
1369 std::string js = atoms::asString(
1370 type == kLocalStorageType ? atoms::REMOVE_LOCAL_STORAGE_ITEM
1371 : atoms::REMOVE_SESSION_STORAGE_ITEM);
1372 return ExecuteScriptAndParse(
1373 current_target_,
1374 js,
1375 "removeStorageItem",
1376 CreateListValueFrom(key),
1377 CreateDirectValueParser(value));
1378 }
1379
1380 Error* Session::GetGeolocation(
1381 scoped_ptr<base::DictionaryValue>* geolocation) {
1382 Error* error = NULL;
1383 RunSessionTask(base::Bind(
1384 &Automation::GetGeolocation,
1385 base::Unretained(automation_.get()),
1386 geolocation,
1387 &error));
1388 return error;
1389 }
1390
1391 Error* Session::OverrideGeolocation(const base::DictionaryValue* geolocation) {
1392 Error* error = NULL;
1393 RunSessionTask(base::Bind(
1394 &Automation::OverrideGeolocation,
1395 base::Unretained(automation_.get()),
1396 geolocation,
1397 &error));
1398 return error;
1399 }
1400
1401 const std::string& Session::id() const {
1402 return id_;
1403 }
1404
1405 const FrameId& Session::current_target() const {
1406 return current_target_;
1407 }
1408
1409 void Session::set_async_script_timeout(int timeout_ms) {
1410 async_script_timeout_ = timeout_ms;
1411 }
1412
1413 int Session::async_script_timeout() const {
1414 return async_script_timeout_;
1415 }
1416
1417 void Session::set_implicit_wait(int timeout_ms) {
1418 implicit_wait_ = timeout_ms;
1419 }
1420
1421 int Session::implicit_wait() const {
1422 return implicit_wait_;
1423 }
1424
1425 const Point& Session::get_mouse_position() const {
1426 return mouse_position_;
1427 }
1428
1429 const Logger& Session::logger() const {
1430 return logger_;
1431 }
1432
1433 const base::FilePath& Session::temp_dir() const {
1434 return temp_dir_.path();
1435 }
1436
1437 const Capabilities& Session::capabilities() const {
1438 return capabilities_;
1439 }
1440
1441 void Session::RunSessionTask(const base::Closure& task) {
1442 base::WaitableEvent done_event(false, false);
1443 thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
1444 &Session::RunClosureOnSessionThread,
1445 base::Unretained(this),
1446 task,
1447 &done_event));
1448 // See SetCookie for why it is essential that we wait here.
1449 done_event.Wait();
1450 }
1451
1452 void Session::RunClosureOnSessionThread(const base::Closure& task,
1453 base::WaitableEvent* done_event) {
1454 task.Run();
1455 done_event->Signal();
1456 }
1457
1458 void Session::InitOnSessionThread(const Automation::BrowserOptions& options,
1459 int* build_no,
1460 Error** error) {
1461 automation_.reset(new Automation(logger_));
1462 automation_->Init(options, build_no, error);
1463 if (*error)
1464 return;
1465
1466 std::vector<WebViewInfo> views;
1467 automation_->GetViews(&views, error);
1468 if (*error)
1469 return;
1470 if (views.empty()) {
1471 *error = new Error(kUnknownError, "No view ids after initialization");
1472 return;
1473 }
1474 current_target_ = FrameId(views[0].view_id, FramePath());
1475 }
1476
1477 void Session::TerminateOnSessionThread() {
1478 if (automation_.get())
1479 automation_->Terminate();
1480 automation_.reset();
1481 }
1482
1483 Error* Session::ExecuteScriptAndParseValue(const FrameId& frame_id,
1484 const std::string& script,
1485 base::Value** script_result) {
1486 std::string response_json;
1487 Error* error = NULL;
1488 RunSessionTask(base::Bind(
1489 &Automation::ExecuteScript,
1490 base::Unretained(automation_.get()),
1491 frame_id.view_id,
1492 frame_id.frame_path,
1493 script,
1494 &response_json,
1495 &error));
1496 if (error)
1497 return error;
1498
1499 scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
1500 response_json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, NULL));
1501 if (!value.get())
1502 return new Error(kUnknownError, "Failed to parse script result");
1503 if (value->GetType() != base::Value::TYPE_DICTIONARY)
1504 return new Error(kUnknownError, "Execute script returned non-dict: " +
1505 JsonStringify(value.get()));
1506 base::DictionaryValue* result_dict =
1507 static_cast<base::DictionaryValue*>(value.get());
1508
1509 int status;
1510 if (!result_dict->GetInteger("status", &status))
1511 return new Error(kUnknownError, "Execute script did not return status: " +
1512 JsonStringify(result_dict));
1513 ErrorCode code = static_cast<ErrorCode>(status);
1514 if (code != kSuccess) {
1515 base::DictionaryValue* error_dict;
1516 std::string error_msg;
1517 if (result_dict->GetDictionary("value", &error_dict))
1518 error_dict->GetString("message", &error_msg);
1519 if (error_msg.empty())
1520 error_msg = "Script failed with error code: " + base::IntToString(code);
1521 return new Error(code, error_msg);
1522 }
1523
1524 base::Value* tmp;
1525 if (result_dict->Get("value", &tmp)) {
1526 *script_result= tmp->DeepCopy();
1527 } else {
1528 // "value" was not defined in the returned dictionary; set to null.
1529 *script_result= base::Value::CreateNullValue();
1530 }
1531 return NULL;
1532 }
1533
1534 void Session::SendKeysOnSessionThread(const string16& keys,
1535 bool release_modifiers, Error** error) {
1536 std::vector<WebKeyEvent> key_events;
1537 std::string error_msg;
1538 if (!ConvertKeysToWebKeyEvents(keys, logger_, release_modifiers,
1539 &sticky_modifiers_, &key_events, &error_msg)) {
1540 *error = new Error(kUnknownError, error_msg);
1541 return;
1542 }
1543 for (size_t i = 0; i < key_events.size(); ++i) {
1544 automation_->SendWebKeyEvent(
1545 current_target_.view_id,
1546 key_events[i], error);
1547 if (*error) {
1548 std::string details = base::StringPrintf(
1549 "Failed to send key event. Event details:\n"
1550 "Type: %d, KeyCode: %d, UnmodifiedText: %s, ModifiedText: %s, "
1551 "Modifiers: %d",
1552 key_events[i].type,
1553 key_events[i].key_code,
1554 key_events[i].unmodified_text.c_str(),
1555 key_events[i].modified_text.c_str(),
1556 key_events[i].modifiers);
1557 (*error)->AddDetails(details);
1558 return;
1559 }
1560 }
1561 }
1562
1563 Error* Session::ProcessWebMouseEvents(
1564 const std::vector<WebMouseEvent>& events) {
1565 for (size_t i = 0; i < events.size(); ++i) {
1566 Error* error = NULL;
1567 RunSessionTask(base::Bind(
1568 &Automation::SendWebMouseEvent,
1569 base::Unretained(automation_.get()),
1570 current_target_.view_id,
1571 events[i],
1572 &error));
1573 if (error)
1574 return error;
1575 mouse_position_ = Point(events[i].x, events[i].y);
1576 }
1577 return NULL;
1578 }
1579
1580 WebMouseEvent Session::CreateWebMouseEvent(
1581 automation::MouseEventType type,
1582 automation::MouseButton button,
1583 const Point& point,
1584 int click_count) {
1585 return WebMouseEvent(type, button, point.rounded_x(), point.rounded_y(),
1586 click_count, sticky_modifiers_);
1587 }
1588
1589 Error* Session::SwitchToFrameWithJavaScriptLocatedFrame(
1590 const std::string& script, base::ListValue* args) {
1591 class SwitchFrameValueParser : public ValueParser {
1592 public:
1593 SwitchFrameValueParser(
1594 bool* found_frame, ElementId* frame)
1595 : found_frame_(found_frame), frame_(frame) { }
1596
1597 virtual ~SwitchFrameValueParser() { }
1598
1599 virtual bool Parse(base::Value* value) const OVERRIDE {
1600 if (value->IsType(base::Value::TYPE_NULL)) {
1601 *found_frame_ = false;
1602 return true;
1603 }
1604 ElementId id(value);
1605 if (!id.is_valid()) {
1606 return false;
1607 }
1608 *frame_ = id;
1609 *found_frame_ = true;
1610 return true;
1611 }
1612
1613 private:
1614 bool* found_frame_;
1615 ElementId* frame_;
1616 };
1617
1618 bool found_frame;
1619 ElementId new_frame_element;
1620 Error* error = ExecuteScriptAndParse(
1621 current_target_, script, "switchFrame", args,
1622 new SwitchFrameValueParser(&found_frame, &new_frame_element));
1623 if (error)
1624 return error;
1625
1626 if (!found_frame)
1627 return new Error(kNoSuchFrame);
1628
1629 std::string frame_id = GenerateRandomID();
1630 error = ExecuteScriptAndParse(
1631 current_target_,
1632 "function(elem, id) { elem.setAttribute('wd_frame_id_', id); }",
1633 "setFrameId",
1634 CreateListValueFrom(new_frame_element, frame_id),
1635 CreateDirectValueParser(kSkipParsing));
1636 if (error)
1637 return error;
1638
1639 frame_elements_.push_back(new_frame_element);
1640 current_target_.frame_path = current_target_.frame_path.Append(
1641 base::StringPrintf("//*[@wd_frame_id_ = '%s']", frame_id.c_str()));
1642 return NULL;
1643 }
1644
1645 Error* Session::FindElementsHelper(const FrameId& frame_id,
1646 const ElementId& root_element,
1647 const std::string& locator,
1648 const std::string& query,
1649 bool find_one,
1650 std::vector<ElementId>* elements) {
1651 CHECK(root_element.is_valid());
1652 base::Time start_time = base::Time::Now();
1653 while (true) {
1654 std::vector<ElementId> temp_elements;
1655 Error* error = ExecuteFindElementScriptAndParse(
1656 frame_id, root_element, locator, query, find_one, &temp_elements);
1657 if (error)
1658 return error;
1659
1660 if (temp_elements.size() > 0u) {
1661 elements->swap(temp_elements);
1662 break;
1663 }
1664
1665 if ((base::Time::Now() - start_time).InMilliseconds() > implicit_wait_) {
1666 if (find_one)
1667 return new Error(kNoSuchElement);
1668 break;
1669 }
1670 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
1671 }
1672 return NULL;
1673 }
1674
1675 Error* Session::ExecuteFindElementScriptAndParse(
1676 const FrameId& frame_id,
1677 const ElementId& root_element,
1678 const std::string& locator,
1679 const std::string& query,
1680 bool find_one,
1681 std::vector<ElementId>* elements) {
1682 CHECK(root_element.is_valid());
1683
1684 class FindElementsParser : public ValueParser {
1685 public:
1686 explicit FindElementsParser(std::vector<ElementId>* elements)
1687 : elements_(elements) { }
1688
1689 virtual ~FindElementsParser() { }
1690
1691 virtual bool Parse(base::Value* value) const OVERRIDE {
1692 if (!value->IsType(base::Value::TYPE_LIST))
1693 return false;
1694 base::ListValue* list = static_cast<base::ListValue*>(value);
1695 for (size_t i = 0; i < list->GetSize(); ++i) {
1696 ElementId element;
1697 base::Value* element_value = NULL;
1698 if (!list->Get(i, &element_value))
1699 return false;
1700 if (!SetFromValue(element_value, &element))
1701 return false;
1702 elements_->push_back(element);
1703 }
1704 return true;
1705 }
1706 private:
1707 std::vector<ElementId>* elements_;
1708 };
1709
1710 class FindElementParser : public ValueParser {
1711 public:
1712 explicit FindElementParser(std::vector<ElementId>* elements)
1713 : elements_(elements) { }
1714
1715 virtual ~FindElementParser() { }
1716
1717 virtual bool Parse(base::Value* value) const OVERRIDE {
1718 if (value->IsType(base::Value::TYPE_NULL))
1719 return true;
1720 ElementId element;
1721 bool set = SetFromValue(value, &element);
1722 if (set)
1723 elements_->push_back(element);
1724 return set;
1725 }
1726 private:
1727 std::vector<ElementId>* elements_;
1728 };
1729
1730 base::DictionaryValue locator_dict;
1731 locator_dict.SetString(locator, query);
1732 std::vector<ElementId> temp_elements;
1733 Error* error = NULL;
1734 if (find_one) {
1735 error = ExecuteScriptAndParse(
1736 frame_id,
1737 atoms::asString(atoms::FIND_ELEMENT),
1738 "findElement",
1739 CreateListValueFrom(&locator_dict, root_element),
1740 new FindElementParser(&temp_elements));
1741 } else {
1742 error = ExecuteScriptAndParse(
1743 frame_id,
1744 atoms::asString(atoms::FIND_ELEMENTS),
1745 "findElements",
1746 CreateListValueFrom(&locator_dict, root_element),
1747 new FindElementsParser(&temp_elements));
1748 }
1749 if (!error)
1750 elements->swap(temp_elements);
1751 return error;
1752 }
1753
1754 Error* Session::VerifyElementIsClickable(
1755 const FrameId& frame_id,
1756 const ElementId& element,
1757 const Point& location) {
1758 class IsElementClickableParser : public ValueParser {
1759 public:
1760 IsElementClickableParser(bool* clickable, std::string* message)
1761 : clickable_(clickable), message_(message) { }
1762
1763 virtual ~IsElementClickableParser() { }
1764
1765 virtual bool Parse(base::Value* value) const OVERRIDE {
1766 if (!value->IsType(base::Value::TYPE_DICTIONARY))
1767 return false;
1768 base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(value);
1769 dict->GetString("message", message_);
1770 return dict->GetBoolean("clickable", clickable_);
1771 }
1772
1773 private:
1774 bool* clickable_;
1775 std::string* message_;
1776 };
1777
1778 bool clickable;
1779 std::string message;
1780 Error* error = ExecuteScriptAndParse(
1781 frame_id,
1782 atoms::asString(atoms::IS_ELEMENT_CLICKABLE),
1783 "isElementClickable",
1784 CreateListValueFrom(element, location),
1785 new IsElementClickableParser(&clickable, &message));
1786 if (error)
1787 return error;
1788
1789 if (!clickable) {
1790 if (message.empty())
1791 message = "element is not clickable";
1792 return new Error(kUnknownError, message);
1793 }
1794 if (message.length()) {
1795 logger_.Log(kWarningLogLevel, message);
1796 }
1797 return NULL;
1798 }
1799
1800 Error* Session::GetElementRegionInViewHelper(
1801 const FrameId& frame_id,
1802 const ElementId& element,
1803 const Rect& region,
1804 bool center,
1805 bool verify_clickable_at_middle,
1806 Point* location) {
1807 Point temp_location;
1808 Error* error = ExecuteScriptAndParse(
1809 frame_id,
1810 atoms::asString(atoms::GET_LOCATION_IN_VIEW),
1811 "getLocationInView",
1812 CreateListValueFrom(element, center, region),
1813 CreateDirectValueParser(&temp_location));
1814
1815 if (verify_clickable_at_middle) {
1816 Point middle_point = temp_location;
1817 middle_point.Offset(region.width() / 2, region.height() / 2);
1818 error = VerifyElementIsClickable(frame_id, element, middle_point);
1819 if (error)
1820 return error;
1821 }
1822 *location = temp_location;
1823 return NULL;
1824 }
1825
1826 Error* Session::GetScreenShot(std::string* png) {
1827 if (!current_target_.view_id.IsTab()) {
1828 return new Error(kUnknownError,
1829 "The current target does not support screenshot");
1830 }
1831 Error* error = NULL;
1832 base::ScopedTempDir screenshots_dir;
1833 if (!screenshots_dir.CreateUniqueTempDir()) {
1834 return new Error(kUnknownError,
1835 "Could not create temp directory for screenshot");
1836 }
1837
1838 base::FilePath path = screenshots_dir.path().AppendASCII("screen");
1839 RunSessionTask(base::Bind(
1840 &Automation::CaptureEntirePageAsPNG,
1841 base::Unretained(automation_.get()),
1842 current_target_.view_id,
1843 path,
1844 &error));
1845 if (error)
1846 return error;
1847 if (!base::ReadFileToString(path, png))
1848 return new Error(kUnknownError, "Could not read screenshot file");
1849 return NULL;
1850 }
1851
1852 #if !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
1853 Error* Session::HeapProfilerDump(const std::string& reason) {
1854 // TODO(dmikurube): Support browser processes.
1855 Error* error = NULL;
1856 RunSessionTask(base::Bind(
1857 &Automation::HeapProfilerDump,
1858 base::Unretained(automation_.get()),
1859 current_target_.view_id,
1860 reason,
1861 &error));
1862 return error;
1863 }
1864 #endif // !defined(NO_TCMALLOC) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
1865
1866 Error* Session::PostBrowserStartInit() {
1867 Error* error = NULL;
1868 if (!capabilities_.no_website_testing_defaults)
1869 error = InitForWebsiteTesting();
1870 if (!error)
1871 error = SetPrefs();
1872 if (error)
1873 return error;
1874
1875 // Install extensions.
1876 for (size_t i = 0; i < capabilities_.extensions.size(); ++i) {
1877 std::string extension_id;
1878 error = InstallExtension(capabilities_.extensions[i], &extension_id);
1879 if (error)
1880 return error;
1881 }
1882 return NULL;
1883 }
1884
1885 Error* Session::InitForWebsiteTesting() {
1886 bool has_prefs_api = false;
1887 // Don't set these prefs for Chrome 14 and below.
1888 // TODO(kkania): Remove this when Chrome 14 is unsupported.
1889 Error* error = CompareBrowserVersion(874, 0, &has_prefs_api);
1890 if (error || !has_prefs_api)
1891 return error;
1892
1893 // Disable checking for SSL certificate revocation.
1894 error = SetPreference(
1895 "ssl.rev_checking.enabled",
1896 false /* is_user_pref */,
1897 new base::FundamentalValue(false));
1898 if (error)
1899 return error;
1900
1901 // Allow content by default.
1902 // Media-stream cannot be enabled by default; we must specify
1903 // particular host patterns and devices.
1904 base::DictionaryValue* devices = new base::DictionaryValue();
1905 devices->SetString("audio", "Default");
1906 devices->SetString("video", "Default");
1907 base::DictionaryValue* content_settings = new base::DictionaryValue();
1908 content_settings->Set("media-stream", devices);
1909 base::DictionaryValue* pattern_pairs = new base::DictionaryValue();
1910 pattern_pairs->Set("https://*,*", content_settings);
1911 error = SetPreference(
1912 "profile.content_settings.pattern_pairs",
1913 true /* is_user_pref */,
1914 pattern_pairs);
1915 if (error)
1916 return error;
1917 const int kAllowContent = 1;
1918 base::DictionaryValue* default_content_settings = new base::DictionaryValue();
1919 default_content_settings->SetInteger("geolocation", kAllowContent);
1920 default_content_settings->SetInteger("mouselock", kAllowContent);
1921 default_content_settings->SetInteger("notifications", kAllowContent);
1922 default_content_settings->SetInteger("popups", kAllowContent);
1923 return SetPreference(
1924 "profile.default_content_settings",
1925 true /* is_user_pref */,
1926 default_content_settings);
1927 }
1928
1929 Error* Session::SetPrefs() {
1930 for (base::DictionaryValue::Iterator iter(*capabilities_.prefs);
1931 !iter.IsAtEnd(); iter.Advance()) {
1932 Error* error = SetPreference(iter.key(), true /* is_user_pref */,
1933 iter.value().DeepCopy());
1934 if (error)
1935 return error;
1936 }
1937 for (base::DictionaryValue::Iterator iter(*capabilities_.local_state);
1938 !iter.IsAtEnd(); iter.Advance()) {
1939 Error* error = SetPreference(iter.key(), false /* is_user_pref */,
1940 iter.value().DeepCopy());
1941 if (error)
1942 return error;
1943 }
1944 return NULL;
1945 }
1946
1947 } // namespace webdriver
OLDNEW
« no previous file with comments | « chrome/test/webdriver/webdriver_session.h ('k') | chrome/test/webdriver/webdriver_session_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698