IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / public / test / browser_test_utils.cc
blobdb9be5b1c5a4ee5e2c4fa6cfa5e02e68c1d5a283
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.
5 #include "content/public/test/browser_test_utils.h"
7 #include "base/command_line.h"
8 #include "base/json/json_reader.h"
9 #include "base/path_service.h"
10 #include "base/process/kill.h"
11 #include "base/rand_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/values.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/dom_operation_notification_details.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/notification_types.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/browser/web_contents_observer.h"
25 #include "content/public/browser/web_contents_view.h"
26 #include "content/public/test/test_utils.h"
27 #include "grit/webui_resources.h"
28 #include "net/base/net_util.h"
29 #include "net/cookies/cookie_store.h"
30 #include "net/test/python_utils.h"
31 #include "net/url_request/url_request_context.h"
32 #include "net/url_request/url_request_context_getter.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "ui/base/resource/resource_bundle.h"
35 #include "ui/events/keycodes/dom4/keycode_converter.h"
37 namespace content {
38 namespace {
40 class DOMOperationObserver : public NotificationObserver,
41 public WebContentsObserver {
42 public:
43 explicit DOMOperationObserver(RenderViewHost* rvh)
44 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
45 did_respond_(false) {
46 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
47 Source<RenderViewHost>(rvh));
48 message_loop_runner_ = new MessageLoopRunner;
51 virtual void Observe(int type,
52 const NotificationSource& source,
53 const NotificationDetails& details) OVERRIDE {
54 DCHECK(type == NOTIFICATION_DOM_OPERATION_RESPONSE);
55 Details<DomOperationNotificationDetails> dom_op_details(details);
56 response_ = dom_op_details->json;
57 did_respond_ = true;
58 message_loop_runner_->Quit();
61 // Overridden from WebContentsObserver:
62 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
63 message_loop_runner_->Quit();
66 bool WaitAndGetResponse(std::string* response) WARN_UNUSED_RESULT {
67 message_loop_runner_->Run();
68 *response = response_;
69 return did_respond_;
72 private:
73 NotificationRegistrar registrar_;
74 std::string response_;
75 bool did_respond_;
76 scoped_refptr<MessageLoopRunner> message_loop_runner_;
78 DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver);
81 // Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute.
82 bool ExecuteScriptHelper(RenderViewHost* render_view_host,
83 const std::string& frame_xpath,
84 const std::string& original_script,
85 scoped_ptr<base::Value>* result) WARN_UNUSED_RESULT;
87 // Executes the passed |original_script| in the frame pointed to by
88 // |frame_xpath|. If |result| is not NULL, stores the value that the evaluation
89 // of the script in |result|. Returns true on success.
90 bool ExecuteScriptHelper(RenderViewHost* render_view_host,
91 const std::string& frame_xpath,
92 const std::string& original_script,
93 scoped_ptr<base::Value>* result) {
94 // TODO(jcampan): we should make the domAutomationController not require an
95 // automation id.
96 std::string script =
97 "window.domAutomationController.setAutomationId(0);" + original_script;
98 DOMOperationObserver dom_op_observer(render_view_host);
99 render_view_host->ExecuteJavascriptInWebFrame(base::UTF8ToUTF16(frame_xpath),
100 base::UTF8ToUTF16(script));
101 std::string json;
102 if (!dom_op_observer.WaitAndGetResponse(&json)) {
103 DLOG(ERROR) << "Cannot communicate with DOMOperationObserver.";
104 return false;
107 // Nothing more to do for callers that ignore the returned JS value.
108 if (!result)
109 return true;
111 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
112 result->reset(reader.ReadToValue(json));
113 if (!result->get()) {
114 DLOG(ERROR) << reader.GetErrorMessage();
115 return false;
118 return true;
121 void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type,
122 ui::KeyboardCode key_code,
123 int native_key_code,
124 int modifiers,
125 NativeWebKeyboardEvent* event) {
126 event->nativeKeyCode = native_key_code;
127 event->windowsKeyCode = key_code;
128 event->setKeyIdentifierFromWindowsKeyCode();
129 event->type = type;
130 event->modifiers = modifiers;
131 event->isSystemKey = false;
132 event->timeStampSeconds = base::Time::Now().ToDoubleT();
133 event->skip_in_browser = true;
135 if (type == blink::WebInputEvent::Char ||
136 type == blink::WebInputEvent::RawKeyDown) {
137 event->text[0] = key_code;
138 event->unmodifiedText[0] = key_code;
142 void InjectRawKeyEvent(WebContents* web_contents,
143 blink::WebInputEvent::Type type,
144 ui::KeyboardCode key_code,
145 int native_key_code,
146 int modifiers) {
147 NativeWebKeyboardEvent event;
148 BuildSimpleWebKeyEvent(type, key_code, native_key_code, modifiers, &event);
149 web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event);
152 void GetCookiesCallback(std::string* cookies_out,
153 base::WaitableEvent* event,
154 const std::string& cookies) {
155 *cookies_out = cookies;
156 event->Signal();
159 void GetCookiesOnIOThread(const GURL& url,
160 net::URLRequestContextGetter* context_getter,
161 base::WaitableEvent* event,
162 std::string* cookies) {
163 net::CookieStore* cookie_store =
164 context_getter->GetURLRequestContext()->cookie_store();
165 cookie_store->GetCookiesWithOptionsAsync(
166 url, net::CookieOptions(),
167 base::Bind(&GetCookiesCallback, cookies, event));
170 void SetCookieCallback(bool* result,
171 base::WaitableEvent* event,
172 bool success) {
173 *result = success;
174 event->Signal();
177 void SetCookieOnIOThread(const GURL& url,
178 const std::string& value,
179 net::URLRequestContextGetter* context_getter,
180 base::WaitableEvent* event,
181 bool* result) {
182 net::CookieStore* cookie_store =
183 context_getter->GetURLRequestContext()->cookie_store();
184 cookie_store->SetCookieWithOptionsAsync(
185 url, value, net::CookieOptions(),
186 base::Bind(&SetCookieCallback, result, event));
189 } // namespace
192 GURL GetFileUrlWithQuery(const base::FilePath& path,
193 const std::string& query_string) {
194 GURL url = net::FilePathToFileURL(path);
195 if (!query_string.empty()) {
196 GURL::Replacements replacements;
197 replacements.SetQueryStr(query_string);
198 return url.ReplaceComponents(replacements);
200 return url;
203 void WaitForLoadStop(WebContents* web_contents) {
204 WindowedNotificationObserver load_stop_observer(
205 NOTIFICATION_LOAD_STOP,
206 Source<NavigationController>(&web_contents->GetController()));
207 // In many cases, the load may have finished before we get here. Only wait if
208 // the tab still has a pending navigation.
209 if (!web_contents->IsLoading())
210 return;
211 load_stop_observer.Wait();
214 void CrashTab(WebContents* web_contents) {
215 RenderProcessHost* rph = web_contents->GetRenderProcessHost();
216 RenderProcessHostWatcher watcher(
217 rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
218 base::KillProcess(rph->GetHandle(), 0, false);
219 watcher.Wait();
222 void SimulateMouseClick(WebContents* web_contents,
223 int modifiers,
224 blink::WebMouseEvent::Button button) {
225 int x = web_contents->GetView()->GetContainerSize().width() / 2;
226 int y = web_contents->GetView()->GetContainerSize().height() / 2;
227 SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y));
230 void SimulateMouseClickAt(WebContents* web_contents,
231 int modifiers,
232 blink::WebMouseEvent::Button button,
233 const gfx::Point& point) {
234 blink::WebMouseEvent mouse_event;
235 mouse_event.type = blink::WebInputEvent::MouseDown;
236 mouse_event.button = button;
237 mouse_event.x = point.x();
238 mouse_event.y = point.y();
239 mouse_event.modifiers = modifiers;
240 // Mac needs globalX/globalY for events to plugins.
241 gfx::Rect offset;
242 web_contents->GetView()->GetContainerBounds(&offset);
243 mouse_event.globalX = point.x() + offset.x();
244 mouse_event.globalY = point.y() + offset.y();
245 mouse_event.clickCount = 1;
246 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
247 mouse_event.type = blink::WebInputEvent::MouseUp;
248 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
251 void SimulateMouseEvent(WebContents* web_contents,
252 blink::WebInputEvent::Type type,
253 const gfx::Point& point) {
254 blink::WebMouseEvent mouse_event;
255 mouse_event.type = type;
256 mouse_event.x = point.x();
257 mouse_event.y = point.y();
258 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
261 void SimulateKeyPress(WebContents* web_contents,
262 ui::KeyboardCode key_code,
263 bool control,
264 bool shift,
265 bool alt,
266 bool command) {
267 SimulateKeyPressWithCode(
268 web_contents, key_code, NULL, control, shift, alt, command);
271 void SimulateKeyPressWithCode(WebContents* web_contents,
272 ui::KeyboardCode key_code,
273 const char* code,
274 bool control,
275 bool shift,
276 bool alt,
277 bool command) {
278 ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance();
279 int native_key_code = key_converter->CodeToNativeKeycode(code);
281 int modifiers = 0;
283 // The order of these key down events shouldn't matter for our simulation.
284 // For our simulation we can use either the left keys or the right keys.
285 if (control) {
286 modifiers |= blink::WebInputEvent::ControlKey;
287 InjectRawKeyEvent(
288 web_contents,
289 blink::WebInputEvent::RawKeyDown,
290 ui::VKEY_CONTROL,
291 key_converter->CodeToNativeKeycode("ControlLeft"),
292 modifiers);
295 if (shift) {
296 modifiers |= blink::WebInputEvent::ShiftKey;
297 InjectRawKeyEvent(
298 web_contents,
299 blink::WebInputEvent::RawKeyDown,
300 ui::VKEY_SHIFT,
301 key_converter->CodeToNativeKeycode("ShiftLeft"),
302 modifiers);
305 if (alt) {
306 modifiers |= blink::WebInputEvent::AltKey;
307 InjectRawKeyEvent(
308 web_contents,
309 blink::WebInputEvent::RawKeyDown,
310 ui::VKEY_MENU,
311 key_converter->CodeToNativeKeycode("AltLeft"),
312 modifiers);
315 if (command) {
316 modifiers |= blink::WebInputEvent::MetaKey;
317 InjectRawKeyEvent(
318 web_contents,
319 blink::WebInputEvent::RawKeyDown,
320 ui::VKEY_COMMAND,
321 key_converter->CodeToNativeKeycode("OSLeft"),
322 modifiers);
325 InjectRawKeyEvent(
326 web_contents,
327 blink::WebInputEvent::RawKeyDown,
328 key_code,
329 native_key_code,
330 modifiers);
332 InjectRawKeyEvent(
333 web_contents,
334 blink::WebInputEvent::Char,
335 key_code,
336 native_key_code,
337 modifiers);
339 InjectRawKeyEvent(
340 web_contents,
341 blink::WebInputEvent::KeyUp,
342 key_code,
343 native_key_code,
344 modifiers);
346 // The order of these key releases shouldn't matter for our simulation.
347 if (control) {
348 modifiers &= ~blink::WebInputEvent::ControlKey;
349 InjectRawKeyEvent(
350 web_contents,
351 blink::WebInputEvent::KeyUp,
352 ui::VKEY_CONTROL,
353 key_converter->CodeToNativeKeycode("ControlLeft"),
354 modifiers);
357 if (shift) {
358 modifiers &= ~blink::WebInputEvent::ShiftKey;
359 InjectRawKeyEvent(
360 web_contents,
361 blink::WebInputEvent::KeyUp,
362 ui::VKEY_SHIFT,
363 key_converter->CodeToNativeKeycode("ShiftLeft"),
364 modifiers);
367 if (alt) {
368 modifiers &= ~blink::WebInputEvent::AltKey;
369 InjectRawKeyEvent(
370 web_contents,
371 blink::WebInputEvent::KeyUp,
372 ui::VKEY_MENU,
373 key_converter->CodeToNativeKeycode("AltLeft"),
374 modifiers);
377 if (command) {
378 modifiers &= ~blink::WebInputEvent::MetaKey;
379 InjectRawKeyEvent(
380 web_contents,
381 blink::WebInputEvent::KeyUp,
382 ui::VKEY_COMMAND,
383 key_converter->CodeToNativeKeycode("OSLeft"),
384 modifiers);
387 ASSERT_EQ(modifiers, 0);
390 namespace internal {
392 ToRenderViewHost::ToRenderViewHost(WebContents* web_contents)
393 : render_view_host_(web_contents->GetRenderViewHost()) {
396 ToRenderViewHost::ToRenderViewHost(RenderViewHost* render_view_host)
397 : render_view_host_(render_view_host) {
400 } // namespace internal
402 bool ExecuteScriptInFrame(const internal::ToRenderViewHost& adapter,
403 const std::string& frame_xpath,
404 const std::string& original_script) {
405 std::string script =
406 original_script + ";window.domAutomationController.send(0);";
407 return ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
408 NULL);
411 bool ExecuteScriptInFrameAndExtractInt(
412 const internal::ToRenderViewHost& adapter,
413 const std::string& frame_xpath,
414 const std::string& script,
415 int* result) {
416 DCHECK(result);
417 scoped_ptr<base::Value> value;
418 if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
419 &value) || !value.get())
420 return false;
422 return value->GetAsInteger(result);
425 bool ExecuteScriptInFrameAndExtractBool(
426 const internal::ToRenderViewHost& adapter,
427 const std::string& frame_xpath,
428 const std::string& script,
429 bool* result) {
430 DCHECK(result);
431 scoped_ptr<base::Value> value;
432 if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
433 &value) || !value.get())
434 return false;
436 return value->GetAsBoolean(result);
439 bool ExecuteScriptInFrameAndExtractString(
440 const internal::ToRenderViewHost& adapter,
441 const std::string& frame_xpath,
442 const std::string& script,
443 std::string* result) {
444 DCHECK(result);
445 scoped_ptr<base::Value> value;
446 if (!ExecuteScriptHelper(adapter.render_view_host(), frame_xpath, script,
447 &value) || !value.get())
448 return false;
450 return value->GetAsString(result);
453 bool ExecuteScript(const internal::ToRenderViewHost& adapter,
454 const std::string& script) {
455 return ExecuteScriptInFrame(adapter, std::string(), script);
458 bool ExecuteScriptAndExtractInt(const internal::ToRenderViewHost& adapter,
459 const std::string& script, int* result) {
460 return ExecuteScriptInFrameAndExtractInt(adapter, std::string(), script,
461 result);
464 bool ExecuteScriptAndExtractBool(const internal::ToRenderViewHost& adapter,
465 const std::string& script, bool* result) {
466 return ExecuteScriptInFrameAndExtractBool(adapter, std::string(), script,
467 result);
470 bool ExecuteScriptAndExtractString(const internal::ToRenderViewHost& adapter,
471 const std::string& script,
472 std::string* result) {
473 return ExecuteScriptInFrameAndExtractString(adapter, std::string(), script,
474 result);
477 bool ExecuteWebUIResourceTest(
478 const internal::ToRenderViewHost& adapter,
479 const std::vector<int>& js_resource_ids) {
480 // Inject WebUI test runner script first prior to other scripts required to
481 // run the test as scripts may depend on it being declared.
482 std::vector<int> ids;
483 ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST);
484 ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end());
486 std::string script;
487 for (std::vector<int>::iterator iter = ids.begin();
488 iter != ids.end();
489 ++iter) {
490 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter)
491 .AppendToString(&script);
492 script.append("\n");
494 if (!content::ExecuteScript(adapter, script))
495 return false;
497 content::DOMMessageQueue message_queue;
498 if (!content::ExecuteScript(adapter, "runTests()"))
499 return false;
501 std::string message;
502 do {
503 if (!message_queue.WaitForMessage(&message))
504 return false;
505 } while (message.compare("\"PENDING\"") == 0);
507 return message.compare("\"SUCCESS\"") == 0;
510 std::string GetCookies(BrowserContext* browser_context, const GURL& url) {
511 std::string cookies;
512 base::WaitableEvent event(true, false);
513 net::URLRequestContextGetter* context_getter =
514 browser_context->GetRequestContext();
516 BrowserThread::PostTask(
517 BrowserThread::IO, FROM_HERE,
518 base::Bind(&GetCookiesOnIOThread, url,
519 make_scoped_refptr(context_getter), &event, &cookies));
520 event.Wait();
521 return cookies;
524 bool SetCookie(BrowserContext* browser_context,
525 const GURL& url,
526 const std::string& value) {
527 bool result = false;
528 base::WaitableEvent event(true, false);
529 net::URLRequestContextGetter* context_getter =
530 browser_context->GetRequestContext();
532 BrowserThread::PostTask(
533 BrowserThread::IO, FROM_HERE,
534 base::Bind(&SetCookieOnIOThread, url, value,
535 make_scoped_refptr(context_getter), &event, &result));
536 event.Wait();
537 return result;
540 TitleWatcher::TitleWatcher(WebContents* web_contents,
541 const base::string16& expected_title)
542 : WebContentsObserver(web_contents),
543 message_loop_runner_(new MessageLoopRunner) {
544 EXPECT_TRUE(web_contents != NULL);
545 expected_titles_.push_back(expected_title);
548 void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) {
549 expected_titles_.push_back(expected_title);
552 TitleWatcher::~TitleWatcher() {
555 const base::string16& TitleWatcher::WaitAndGetTitle() {
556 message_loop_runner_->Run();
557 return observed_title_;
560 void TitleWatcher::DidStopLoading(RenderViewHost* render_view_host) {
561 // When navigating through the history, the restored NavigationEntry's title
562 // will be used. If the entry ends up having the same title after we return
563 // to it, as will usually be the case, then WebContentsObserver::TitleSet
564 // will then be suppressed, since the NavigationEntry's title hasn't changed.
565 TestTitle();
568 void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
569 TestTitle();
572 void TitleWatcher::TestTitle() {
573 std::vector<base::string16>::const_iterator it =
574 std::find(expected_titles_.begin(),
575 expected_titles_.end(),
576 web_contents()->GetTitle());
577 if (it == expected_titles_.end())
578 return;
580 observed_title_ = *it;
581 message_loop_runner_->Quit();
584 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
585 WebContents* web_contents)
586 : WebContentsObserver(web_contents),
587 message_loop_runner_(new MessageLoopRunner) {
588 EXPECT_TRUE(web_contents != NULL);
591 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
594 void WebContentsDestroyedWatcher::Wait() {
595 message_loop_runner_->Run();
598 void WebContentsDestroyedWatcher::WebContentsDestroyed(
599 WebContents* web_contents) {
600 message_loop_runner_->Quit();
603 RenderProcessHostWatcher::RenderProcessHostWatcher(
604 RenderProcessHost* render_process_host, WatchType type)
605 : render_process_host_(render_process_host),
606 type_(type),
607 message_loop_runner_(new MessageLoopRunner) {
608 render_process_host_->AddObserver(this);
611 RenderProcessHostWatcher::RenderProcessHostWatcher(
612 WebContents* web_contents, WatchType type)
613 : render_process_host_(web_contents->GetRenderProcessHost()),
614 type_(type),
615 message_loop_runner_(new MessageLoopRunner) {
616 render_process_host_->AddObserver(this);
619 RenderProcessHostWatcher::~RenderProcessHostWatcher() {
620 if (render_process_host_)
621 render_process_host_->RemoveObserver(this);
624 void RenderProcessHostWatcher::Wait() {
625 message_loop_runner_->Run();
628 void RenderProcessHostWatcher::RenderProcessExited(
629 RenderProcessHost* host,
630 base::ProcessHandle handle,
631 base::TerminationStatus status,
632 int exit_code) {
633 if (type_ == WATCH_FOR_PROCESS_EXIT)
634 message_loop_runner_->Quit();
637 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
638 RenderProcessHost* host) {
639 render_process_host_ = NULL;
640 if (type_ == WATCH_FOR_HOST_DESTRUCTION)
641 message_loop_runner_->Quit();
644 DOMMessageQueue::DOMMessageQueue() : waiting_for_message_(false) {
645 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
646 NotificationService::AllSources());
649 DOMMessageQueue::~DOMMessageQueue() {}
651 void DOMMessageQueue::Observe(int type,
652 const NotificationSource& source,
653 const NotificationDetails& details) {
654 Details<DomOperationNotificationDetails> dom_op_details(details);
655 Source<RenderViewHost> sender(source);
656 message_queue_.push(dom_op_details->json);
657 if (waiting_for_message_) {
658 waiting_for_message_ = false;
659 message_loop_runner_->Quit();
663 void DOMMessageQueue::ClearQueue() {
664 message_queue_ = std::queue<std::string>();
667 bool DOMMessageQueue::WaitForMessage(std::string* message) {
668 if (message_queue_.empty()) {
669 waiting_for_message_ = true;
670 // This will be quit when a new message comes in.
671 message_loop_runner_ = new MessageLoopRunner;
672 message_loop_runner_->Run();
674 // The queue should not be empty, unless we were quit because of a timeout.
675 if (message_queue_.empty())
676 return false;
677 if (message)
678 *message = message_queue_.front();
679 message_queue_.pop();
680 return true;
683 } // namespace content