OLD | NEW |
| (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 ®ion_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, ®ion_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 | |
OLD | NEW |