Make sure webrtc::VideoSourceInterface is released on the main render thread.
[chromium-blink-merge.git] / content / public / test / browser_test_utils.cc
blob21f48a8f95686ed4a5498fb436cf2645d49ce147
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/browser/web_contents/web_contents_view.h"
18 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/dom_operation_notification_details.h"
20 #include "content/public/browser/notification_service.h"
21 #include "content/public/browser/notification_types.h"
22 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_contents_observer.h"
27 #include "content/public/test/test_utils.h"
28 #include "grit/webui_resources.h"
29 #include "net/base/filename_util.h"
30 #include "net/cookies/cookie_store.h"
31 #include "net/test/python_utils.h"
32 #include "net/url_request/url_request_context.h"
33 #include "net/url_request/url_request_context_getter.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/events/keycodes/dom4/keycode_converter.h"
38 namespace content {
39 namespace {
41 class DOMOperationObserver : public NotificationObserver,
42 public WebContentsObserver {
43 public:
44 explicit DOMOperationObserver(RenderViewHost* rvh)
45 : WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
46 did_respond_(false) {
47 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
48 Source<WebContents>(web_contents()));
49 message_loop_runner_ = new MessageLoopRunner;
52 virtual void Observe(int type,
53 const NotificationSource& source,
54 const NotificationDetails& details) OVERRIDE {
55 DCHECK(type == NOTIFICATION_DOM_OPERATION_RESPONSE);
56 Details<DomOperationNotificationDetails> dom_op_details(details);
57 response_ = dom_op_details->json;
58 did_respond_ = true;
59 message_loop_runner_->Quit();
62 // Overridden from WebContentsObserver:
63 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
64 message_loop_runner_->Quit();
67 bool WaitAndGetResponse(std::string* response) WARN_UNUSED_RESULT {
68 message_loop_runner_->Run();
69 *response = response_;
70 return did_respond_;
73 private:
74 NotificationRegistrar registrar_;
75 std::string response_;
76 bool did_respond_;
77 scoped_refptr<MessageLoopRunner> message_loop_runner_;
79 DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver);
82 // Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute.
83 bool ExecuteScriptHelper(
84 RenderFrameHost* render_frame_host,
85 const std::string& original_script,
86 scoped_ptr<base::Value>* result) WARN_UNUSED_RESULT;
88 // Executes the passed |original_script| in the frame specified by
89 // |render_frame_host|. If |result| is not NULL, stores the value that the
90 // evaluation of the script in |result|. Returns true on success.
91 bool ExecuteScriptHelper(RenderFrameHost* render_frame_host,
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_frame_host->GetRenderViewHost());
99 render_frame_host->ExecuteJavaScript(base::UTF8ToUTF16(script));
100 std::string json;
101 if (!dom_op_observer.WaitAndGetResponse(&json)) {
102 DLOG(ERROR) << "Cannot communicate with DOMOperationObserver.";
103 return false;
106 // Nothing more to do for callers that ignore the returned JS value.
107 if (!result)
108 return true;
110 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
111 result->reset(reader.ReadToValue(json));
112 if (!result->get()) {
113 DLOG(ERROR) << reader.GetErrorMessage();
114 return false;
117 return true;
120 void BuildSimpleWebKeyEvent(blink::WebInputEvent::Type type,
121 ui::KeyboardCode key_code,
122 int native_key_code,
123 int modifiers,
124 NativeWebKeyboardEvent* event) {
125 event->nativeKeyCode = native_key_code;
126 event->windowsKeyCode = key_code;
127 event->setKeyIdentifierFromWindowsKeyCode();
128 event->type = type;
129 event->modifiers = modifiers;
130 event->isSystemKey = false;
131 event->timeStampSeconds = base::Time::Now().ToDoubleT();
132 event->skip_in_browser = true;
134 if (type == blink::WebInputEvent::Char ||
135 type == blink::WebInputEvent::RawKeyDown) {
136 event->text[0] = key_code;
137 event->unmodifiedText[0] = key_code;
141 void InjectRawKeyEvent(WebContents* web_contents,
142 blink::WebInputEvent::Type type,
143 ui::KeyboardCode key_code,
144 int native_key_code,
145 int modifiers) {
146 NativeWebKeyboardEvent event;
147 BuildSimpleWebKeyEvent(type, key_code, native_key_code, modifiers, &event);
148 web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event);
151 void GetCookiesCallback(std::string* cookies_out,
152 base::WaitableEvent* event,
153 const std::string& cookies) {
154 *cookies_out = cookies;
155 event->Signal();
158 void GetCookiesOnIOThread(const GURL& url,
159 net::URLRequestContextGetter* context_getter,
160 base::WaitableEvent* event,
161 std::string* cookies) {
162 net::CookieStore* cookie_store =
163 context_getter->GetURLRequestContext()->cookie_store();
164 cookie_store->GetCookiesWithOptionsAsync(
165 url, net::CookieOptions(),
166 base::Bind(&GetCookiesCallback, cookies, event));
169 void SetCookieCallback(bool* result,
170 base::WaitableEvent* event,
171 bool success) {
172 *result = success;
173 event->Signal();
176 void SetCookieOnIOThread(const GURL& url,
177 const std::string& value,
178 net::URLRequestContextGetter* context_getter,
179 base::WaitableEvent* event,
180 bool* result) {
181 net::CookieStore* cookie_store =
182 context_getter->GetURLRequestContext()->cookie_store();
183 cookie_store->SetCookieWithOptionsAsync(
184 url, value, net::CookieOptions(),
185 base::Bind(&SetCookieCallback, result, event));
188 } // namespace
191 GURL GetFileUrlWithQuery(const base::FilePath& path,
192 const std::string& query_string) {
193 GURL url = net::FilePathToFileURL(path);
194 if (!query_string.empty()) {
195 GURL::Replacements replacements;
196 replacements.SetQueryStr(query_string);
197 return url.ReplaceComponents(replacements);
199 return url;
202 void WaitForLoadStop(WebContents* web_contents) {
203 WindowedNotificationObserver load_stop_observer(
204 NOTIFICATION_LOAD_STOP,
205 Source<NavigationController>(&web_contents->GetController()));
206 // In many cases, the load may have finished before we get here. Only wait if
207 // the tab still has a pending navigation.
208 if (!web_contents->IsLoading())
209 return;
210 load_stop_observer.Wait();
213 void CrashTab(WebContents* web_contents) {
214 RenderProcessHost* rph = web_contents->GetRenderProcessHost();
215 RenderProcessHostWatcher watcher(
216 rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
217 base::KillProcess(rph->GetHandle(), 0, false);
218 watcher.Wait();
221 void SimulateMouseClick(WebContents* web_contents,
222 int modifiers,
223 blink::WebMouseEvent::Button button) {
224 int x = web_contents->GetContainerBounds().width() / 2;
225 int y = web_contents->GetContainerBounds().height() / 2;
226 SimulateMouseClickAt(web_contents, modifiers, button, gfx::Point(x, y));
229 void SimulateMouseClickAt(WebContents* web_contents,
230 int modifiers,
231 blink::WebMouseEvent::Button button,
232 const gfx::Point& point) {
233 blink::WebMouseEvent mouse_event;
234 mouse_event.type = blink::WebInputEvent::MouseDown;
235 mouse_event.button = button;
236 mouse_event.x = point.x();
237 mouse_event.y = point.y();
238 mouse_event.modifiers = modifiers;
239 // Mac needs globalX/globalY for events to plugins.
240 gfx::Rect offset = web_contents->GetContainerBounds();
241 mouse_event.globalX = point.x() + offset.x();
242 mouse_event.globalY = point.y() + offset.y();
243 mouse_event.clickCount = 1;
244 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
245 mouse_event.type = blink::WebInputEvent::MouseUp;
246 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
249 void SimulateMouseEvent(WebContents* web_contents,
250 blink::WebInputEvent::Type type,
251 const gfx::Point& point) {
252 blink::WebMouseEvent mouse_event;
253 mouse_event.type = type;
254 mouse_event.x = point.x();
255 mouse_event.y = point.y();
256 web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
259 void SimulateKeyPress(WebContents* web_contents,
260 ui::KeyboardCode key_code,
261 bool control,
262 bool shift,
263 bool alt,
264 bool command) {
265 SimulateKeyPressWithCode(
266 web_contents, key_code, NULL, control, shift, alt, command);
269 void SimulateKeyPressWithCode(WebContents* web_contents,
270 ui::KeyboardCode key_code,
271 const char* code,
272 bool control,
273 bool shift,
274 bool alt,
275 bool command) {
276 ui::KeycodeConverter* key_converter = ui::KeycodeConverter::GetInstance();
277 int native_key_code = key_converter->CodeToNativeKeycode(code);
279 int modifiers = 0;
281 // The order of these key down events shouldn't matter for our simulation.
282 // For our simulation we can use either the left keys or the right keys.
283 if (control) {
284 modifiers |= blink::WebInputEvent::ControlKey;
285 InjectRawKeyEvent(
286 web_contents,
287 blink::WebInputEvent::RawKeyDown,
288 ui::VKEY_CONTROL,
289 key_converter->CodeToNativeKeycode("ControlLeft"),
290 modifiers);
293 if (shift) {
294 modifiers |= blink::WebInputEvent::ShiftKey;
295 InjectRawKeyEvent(
296 web_contents,
297 blink::WebInputEvent::RawKeyDown,
298 ui::VKEY_SHIFT,
299 key_converter->CodeToNativeKeycode("ShiftLeft"),
300 modifiers);
303 if (alt) {
304 modifiers |= blink::WebInputEvent::AltKey;
305 InjectRawKeyEvent(
306 web_contents,
307 blink::WebInputEvent::RawKeyDown,
308 ui::VKEY_MENU,
309 key_converter->CodeToNativeKeycode("AltLeft"),
310 modifiers);
313 if (command) {
314 modifiers |= blink::WebInputEvent::MetaKey;
315 InjectRawKeyEvent(
316 web_contents,
317 blink::WebInputEvent::RawKeyDown,
318 ui::VKEY_COMMAND,
319 key_converter->CodeToNativeKeycode("OSLeft"),
320 modifiers);
323 InjectRawKeyEvent(
324 web_contents,
325 blink::WebInputEvent::RawKeyDown,
326 key_code,
327 native_key_code,
328 modifiers);
330 InjectRawKeyEvent(
331 web_contents,
332 blink::WebInputEvent::Char,
333 key_code,
334 native_key_code,
335 modifiers);
337 InjectRawKeyEvent(
338 web_contents,
339 blink::WebInputEvent::KeyUp,
340 key_code,
341 native_key_code,
342 modifiers);
344 // The order of these key releases shouldn't matter for our simulation.
345 if (control) {
346 modifiers &= ~blink::WebInputEvent::ControlKey;
347 InjectRawKeyEvent(
348 web_contents,
349 blink::WebInputEvent::KeyUp,
350 ui::VKEY_CONTROL,
351 key_converter->CodeToNativeKeycode("ControlLeft"),
352 modifiers);
355 if (shift) {
356 modifiers &= ~blink::WebInputEvent::ShiftKey;
357 InjectRawKeyEvent(
358 web_contents,
359 blink::WebInputEvent::KeyUp,
360 ui::VKEY_SHIFT,
361 key_converter->CodeToNativeKeycode("ShiftLeft"),
362 modifiers);
365 if (alt) {
366 modifiers &= ~blink::WebInputEvent::AltKey;
367 InjectRawKeyEvent(
368 web_contents,
369 blink::WebInputEvent::KeyUp,
370 ui::VKEY_MENU,
371 key_converter->CodeToNativeKeycode("AltLeft"),
372 modifiers);
375 if (command) {
376 modifiers &= ~blink::WebInputEvent::MetaKey;
377 InjectRawKeyEvent(
378 web_contents,
379 blink::WebInputEvent::KeyUp,
380 ui::VKEY_COMMAND,
381 key_converter->CodeToNativeKeycode("OSLeft"),
382 modifiers);
385 ASSERT_EQ(modifiers, 0);
388 namespace internal {
390 ToRenderFrameHost::ToRenderFrameHost(WebContents* web_contents)
391 : render_frame_host_(web_contents->GetMainFrame()) {
394 ToRenderFrameHost::ToRenderFrameHost(RenderViewHost* render_view_host)
395 : render_frame_host_(render_view_host->GetMainFrame()) {
398 ToRenderFrameHost::ToRenderFrameHost(RenderFrameHost* render_frame_host)
399 : render_frame_host_(render_frame_host) {
402 } // namespace internal
404 bool ExecuteScript(const internal::ToRenderFrameHost& adapter,
405 const std::string& script) {
406 std::string new_script =
407 script + ";window.domAutomationController.send(0);";
408 return ExecuteScriptHelper(adapter.render_frame_host(), new_script, NULL);
411 bool ExecuteScriptAndExtractInt(const internal::ToRenderFrameHost& adapter,
412 const std::string& script, int* result) {
413 DCHECK(result);
414 scoped_ptr<base::Value> value;
415 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
416 !value.get()) {
417 return false;
420 return value->GetAsInteger(result);
423 bool ExecuteScriptAndExtractBool(const internal::ToRenderFrameHost& adapter,
424 const std::string& script, bool* result) {
425 DCHECK(result);
426 scoped_ptr<base::Value> value;
427 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
428 !value.get()) {
429 return false;
432 return value->GetAsBoolean(result);
435 bool ExecuteScriptAndExtractString(const internal::ToRenderFrameHost& adapter,
436 const std::string& script,
437 std::string* result) {
438 DCHECK(result);
439 scoped_ptr<base::Value> value;
440 if (!ExecuteScriptHelper(adapter.render_frame_host(), script, &value) ||
441 !value.get()) {
442 return false;
445 return value->GetAsString(result);
448 namespace {
449 void AddToSetIfFrameMatchesPredicate(
450 std::set<RenderFrameHost*>* frame_set,
451 const base::Callback<bool(RenderFrameHost*)>& predicate,
452 RenderFrameHost* host) {
453 if (predicate.Run(host))
454 frame_set->insert(host);
458 RenderFrameHost* FrameMatchingPredicate(
459 WebContents* web_contents,
460 const base::Callback<bool(RenderFrameHost*)>& predicate) {
461 std::set<RenderFrameHost*> frame_set;
462 web_contents->ForEachFrame(
463 base::Bind(&AddToSetIfFrameMatchesPredicate, &frame_set, predicate));
464 DCHECK_EQ(1U, frame_set.size());
465 return *frame_set.begin();
468 bool FrameMatchesName(const std::string& name, RenderFrameHost* frame) {
469 return frame->GetFrameName() == name;
472 bool FrameIsChildOfMainFrame(RenderFrameHost* frame) {
473 return frame->GetParent() && !frame->GetParent()->GetParent();
476 bool FrameHasSourceUrl(const GURL& url, RenderFrameHost* frame) {
477 return frame->GetLastCommittedURL() == url;
480 bool ExecuteWebUIResourceTest(WebContents* web_contents,
481 const std::vector<int>& js_resource_ids) {
482 // Inject WebUI test runner script first prior to other scripts required to
483 // run the test as scripts may depend on it being declared.
484 std::vector<int> ids;
485 ids.push_back(IDR_WEBUI_JS_WEBUI_RESOURCE_TEST);
486 ids.insert(ids.end(), js_resource_ids.begin(), js_resource_ids.end());
488 std::string script;
489 for (std::vector<int>::iterator iter = ids.begin();
490 iter != ids.end();
491 ++iter) {
492 ResourceBundle::GetSharedInstance().GetRawDataResource(*iter)
493 .AppendToString(&script);
494 script.append("\n");
496 if (!ExecuteScript(web_contents, script))
497 return false;
499 DOMMessageQueue message_queue;
500 if (!ExecuteScript(web_contents, "runTests()"))
501 return false;
503 std::string message;
504 do {
505 if (!message_queue.WaitForMessage(&message))
506 return false;
507 } while (message.compare("\"PENDING\"") == 0);
509 return message.compare("\"SUCCESS\"") == 0;
512 std::string GetCookies(BrowserContext* browser_context, const GURL& url) {
513 std::string cookies;
514 base::WaitableEvent event(true, false);
515 net::URLRequestContextGetter* context_getter =
516 browser_context->GetRequestContext();
518 BrowserThread::PostTask(
519 BrowserThread::IO, FROM_HERE,
520 base::Bind(&GetCookiesOnIOThread, url,
521 make_scoped_refptr(context_getter), &event, &cookies));
522 event.Wait();
523 return cookies;
526 bool SetCookie(BrowserContext* browser_context,
527 const GURL& url,
528 const std::string& value) {
529 bool result = false;
530 base::WaitableEvent event(true, false);
531 net::URLRequestContextGetter* context_getter =
532 browser_context->GetRequestContext();
534 BrowserThread::PostTask(
535 BrowserThread::IO, FROM_HERE,
536 base::Bind(&SetCookieOnIOThread, url, value,
537 make_scoped_refptr(context_getter), &event, &result));
538 event.Wait();
539 return result;
542 TitleWatcher::TitleWatcher(WebContents* web_contents,
543 const base::string16& expected_title)
544 : WebContentsObserver(web_contents),
545 message_loop_runner_(new MessageLoopRunner) {
546 EXPECT_TRUE(web_contents != NULL);
547 expected_titles_.push_back(expected_title);
550 void TitleWatcher::AlsoWaitForTitle(const base::string16& expected_title) {
551 expected_titles_.push_back(expected_title);
554 TitleWatcher::~TitleWatcher() {
557 const base::string16& TitleWatcher::WaitAndGetTitle() {
558 TestTitle();
559 message_loop_runner_->Run();
560 return observed_title_;
563 void TitleWatcher::DidStopLoading(RenderViewHost* render_view_host) {
564 // When navigating through the history, the restored NavigationEntry's title
565 // will be used. If the entry ends up having the same title after we return
566 // to it, as will usually be the case, then WebContentsObserver::TitleSet
567 // will then be suppressed, since the NavigationEntry's title hasn't changed.
568 TestTitle();
571 void TitleWatcher::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
572 TestTitle();
575 void TitleWatcher::TestTitle() {
576 std::vector<base::string16>::const_iterator it =
577 std::find(expected_titles_.begin(),
578 expected_titles_.end(),
579 web_contents()->GetTitle());
580 if (it == expected_titles_.end())
581 return;
583 observed_title_ = *it;
584 message_loop_runner_->Quit();
587 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
588 WebContents* web_contents)
589 : WebContentsObserver(web_contents),
590 message_loop_runner_(new MessageLoopRunner) {
591 EXPECT_TRUE(web_contents != NULL);
594 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {
597 void WebContentsDestroyedWatcher::Wait() {
598 message_loop_runner_->Run();
601 void WebContentsDestroyedWatcher::WebContentsDestroyed(
602 WebContents* web_contents) {
603 message_loop_runner_->Quit();
606 RenderProcessHostWatcher::RenderProcessHostWatcher(
607 RenderProcessHost* render_process_host, WatchType type)
608 : render_process_host_(render_process_host),
609 type_(type),
610 message_loop_runner_(new MessageLoopRunner) {
611 render_process_host_->AddObserver(this);
614 RenderProcessHostWatcher::RenderProcessHostWatcher(
615 WebContents* web_contents, WatchType type)
616 : render_process_host_(web_contents->GetRenderProcessHost()),
617 type_(type),
618 message_loop_runner_(new MessageLoopRunner) {
619 render_process_host_->AddObserver(this);
622 RenderProcessHostWatcher::~RenderProcessHostWatcher() {
623 if (render_process_host_)
624 render_process_host_->RemoveObserver(this);
627 void RenderProcessHostWatcher::Wait() {
628 message_loop_runner_->Run();
631 void RenderProcessHostWatcher::RenderProcessExited(
632 RenderProcessHost* host,
633 base::ProcessHandle handle,
634 base::TerminationStatus status,
635 int exit_code) {
636 if (type_ == WATCH_FOR_PROCESS_EXIT)
637 message_loop_runner_->Quit();
640 void RenderProcessHostWatcher::RenderProcessHostDestroyed(
641 RenderProcessHost* host) {
642 render_process_host_ = NULL;
643 if (type_ == WATCH_FOR_HOST_DESTRUCTION)
644 message_loop_runner_->Quit();
647 DOMMessageQueue::DOMMessageQueue() {
648 registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
649 NotificationService::AllSources());
652 DOMMessageQueue::~DOMMessageQueue() {}
654 void DOMMessageQueue::Observe(int type,
655 const NotificationSource& source,
656 const NotificationDetails& details) {
657 Details<DomOperationNotificationDetails> dom_op_details(details);
658 message_queue_.push(dom_op_details->json);
659 if (message_loop_runner_)
660 message_loop_runner_->Quit();
663 void DOMMessageQueue::ClearQueue() {
664 message_queue_ = std::queue<std::string>();
667 bool DOMMessageQueue::WaitForMessage(std::string* message) {
668 DCHECK(message);
669 if (message_queue_.empty()) {
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 *message = message_queue_.front();
678 message_queue_.pop();
679 return true;
682 } // namespace content