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/test_utils.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/run_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "content/public/browser/notification_service.h"
13 #include "content/public/browser/render_view_host.h"
14 #include "content/public/test/test_launcher.h"
15 #include "testing/gtest/include/gtest/gtest.h"
21 // Number of times to repost a Quit task so that the MessageLoop finishes up
22 // pending tasks and tasks posted by those pending tasks without risking the
23 // potential hang behavior of MessageLoop::QuitWhenIdle.
24 // The criteria for choosing this number: it should be high enough to make the
25 // quit act like QuitWhenIdle, while taking into account that any page which is
26 // animating may be rendering another frame for each quit deferral. For an
27 // animating page, the potential delay to quitting the RunLoop would be
28 // kNumQuitDeferrals * frame_render_time. Some perf tests run slow, such as
30 static const int kNumQuitDeferrals = 10;
32 static void DeferredQuitRunLoop(const base::Closure& quit_task,
33 int num_quit_deferrals) {
34 if (num_quit_deferrals <= 0) {
37 base::MessageLoop::current()->PostTask(
39 base::Bind(&DeferredQuitRunLoop, quit_task, num_quit_deferrals - 1));
43 void RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id,
44 const base::Closure& quit_task) {
45 RunAllPendingInMessageLoop();
46 BrowserThread::PostTask(thread_id, FROM_HERE, quit_task);
49 // Class used handle result callbacks for ExecuteScriptAndGetValue.
50 class ScriptCallback {
53 virtual ~ScriptCallback() { }
54 void ResultCallback(const base::Value* result);
56 scoped_ptr<base::Value> result() { return result_.Pass(); }
59 scoped_ptr<base::Value> result_;
61 DISALLOW_COPY_AND_ASSIGN(ScriptCallback);
64 void ScriptCallback::ResultCallback(const base::Value* result) {
66 result_.reset(result->DeepCopy());
67 base::MessageLoop::current()->Quit();
70 // Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
71 // a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
72 // by ignoring the notification source and details.
73 bool IgnoreSourceAndDetails(
74 const WindowedNotificationObserver::
75 ConditionTestCallbackWithoutSourceAndDetails& callback,
76 const NotificationSource& source,
77 const NotificationDetails& details) {
78 return callback.Run();
83 void RunMessageLoop() {
84 base::RunLoop run_loop;
85 RunThisRunLoop(&run_loop);
88 void RunThisRunLoop(base::RunLoop* run_loop) {
89 base::MessageLoop::ScopedNestableTaskAllower allow(
90 base::MessageLoop::current());
92 // If we're running inside a browser test, we might need to allow the test
93 // launcher to do extra work before/after running a nested message loop.
94 TestLauncherDelegate* delegate = NULL;
96 delegate = GetCurrentTestLauncherDelegate();
99 delegate->PreRunMessageLoop(run_loop);
102 delegate->PostRunMessageLoop();
105 void RunAllPendingInMessageLoop() {
106 base::MessageLoop::current()->PostTask(
107 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
111 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
112 if (BrowserThread::CurrentlyOn(thread_id)) {
113 RunAllPendingInMessageLoop();
116 BrowserThread::ID current_thread_id;
117 if (!BrowserThread::GetCurrentThreadIdentifier(¤t_thread_id)) {
122 base::RunLoop run_loop;
123 BrowserThread::PostTask(thread_id, FROM_HERE,
124 base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id,
125 run_loop.QuitClosure()));
126 RunThisRunLoop(&run_loop);
129 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
130 return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
134 scoped_ptr<base::Value> ExecuteScriptAndGetValue(
135 RenderViewHost* render_view_host,
136 const std::string& script) {
137 ScriptCallback observer;
139 render_view_host->ExecuteJavascriptInWebFrameCallbackResult(
140 string16(), // frame_xpath,
142 base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
143 base::MessageLoop* loop = base::MessageLoop::current();
145 return observer.result().Pass();
148 MessageLoopRunner::MessageLoopRunner()
149 : loop_running_(false),
150 quit_closure_called_(false) {
153 MessageLoopRunner::~MessageLoopRunner() {
156 void MessageLoopRunner::Run() {
157 // Do not run the message loop if our quit closure has already been called.
158 // This helps in scenarios where the closure has a chance to run before
159 // we Run explicitly.
160 if (quit_closure_called_)
163 loop_running_ = true;
164 RunThisRunLoop(&run_loop_);
167 base::Closure MessageLoopRunner::QuitClosure() {
168 return base::Bind(&MessageLoopRunner::Quit, this);
171 void MessageLoopRunner::Quit() {
172 quit_closure_called_ = true;
174 // Only run the quit task if we are running the message loop.
176 GetQuitTaskForRunLoop(&run_loop_).Run();
177 loop_running_ = false;
181 WindowedNotificationObserver::WindowedNotificationObserver(
182 int notification_type,
183 const NotificationSource& source)
186 source_(NotificationService::AllSources()) {
187 AddNotificationType(notification_type, source);
190 WindowedNotificationObserver::WindowedNotificationObserver(
191 int notification_type,
192 const ConditionTestCallback& callback)
196 source_(NotificationService::AllSources()) {
197 AddNotificationType(notification_type, source_);
200 WindowedNotificationObserver::WindowedNotificationObserver(
201 int notification_type,
202 const ConditionTestCallbackWithoutSourceAndDetails& callback)
205 callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
206 source_(NotificationService::AllSources()) {
207 registrar_.Add(this, notification_type, source_);
210 WindowedNotificationObserver::~WindowedNotificationObserver() {}
212 void WindowedNotificationObserver::AddNotificationType(
213 int notification_type,
214 const NotificationSource& source) {
215 registrar_.Add(this, notification_type, source);
218 void WindowedNotificationObserver::Wait() {
223 message_loop_runner_ = new MessageLoopRunner;
224 message_loop_runner_->Run();
228 void WindowedNotificationObserver::Observe(
230 const NotificationSource& source,
231 const NotificationDetails& details) {
234 if (!callback_.is_null() && !callback_.Run(source, details))
241 message_loop_runner_->Quit();
245 } // namespace content