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/browser_child_process_host_iterator.h"
13 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/common/process_type.h"
17 #include "content/public/test/test_launcher.h"
18 #include "testing/gtest/include/gtest/gtest.h"
24 // Number of times to repost a Quit task so that the MessageLoop finishes up
25 // pending tasks and tasks posted by those pending tasks without risking the
26 // potential hang behavior of MessageLoop::QuitWhenIdle.
27 // The criteria for choosing this number: it should be high enough to make the
28 // quit act like QuitWhenIdle, while taking into account that any page which is
29 // animating may be rendering another frame for each quit deferral. For an
30 // animating page, the potential delay to quitting the RunLoop would be
31 // kNumQuitDeferrals * frame_render_time. Some perf tests run slow, such as
33 static const int kNumQuitDeferrals
= 10;
35 static void DeferredQuitRunLoop(const base::Closure
& quit_task
,
36 int num_quit_deferrals
) {
37 if (num_quit_deferrals
<= 0) {
40 base::MessageLoop::current()->PostTask(
42 base::Bind(&DeferredQuitRunLoop
, quit_task
, num_quit_deferrals
- 1));
46 void RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id
,
47 const base::Closure
& quit_task
) {
48 RunAllPendingInMessageLoop();
49 BrowserThread::PostTask(thread_id
, FROM_HERE
, quit_task
);
52 // Class used handle result callbacks for ExecuteScriptAndGetValue.
53 class ScriptCallback
{
56 virtual ~ScriptCallback() { }
57 void ResultCallback(const base::Value
* result
);
59 scoped_ptr
<base::Value
> result() { return result_
.Pass(); }
62 scoped_ptr
<base::Value
> result_
;
64 DISALLOW_COPY_AND_ASSIGN(ScriptCallback
);
67 void ScriptCallback::ResultCallback(const base::Value
* result
) {
69 result_
.reset(result
->DeepCopy());
70 base::MessageLoop::current()->Quit();
73 // Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
74 // a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
75 // by ignoring the notification source and details.
76 bool IgnoreSourceAndDetails(
77 const WindowedNotificationObserver::
78 ConditionTestCallbackWithoutSourceAndDetails
& callback
,
79 const NotificationSource
& source
,
80 const NotificationDetails
& details
) {
81 return callback
.Run();
86 void RunMessageLoop() {
87 base::RunLoop run_loop
;
88 RunThisRunLoop(&run_loop
);
91 void RunThisRunLoop(base::RunLoop
* run_loop
) {
92 base::MessageLoop::ScopedNestableTaskAllower
allow(
93 base::MessageLoop::current());
95 // If we're running inside a browser test, we might need to allow the test
96 // launcher to do extra work before/after running a nested message loop.
97 TestLauncherDelegate
* delegate
= NULL
;
99 delegate
= GetCurrentTestLauncherDelegate();
102 delegate
->PreRunMessageLoop(run_loop
);
105 delegate
->PostRunMessageLoop();
108 void RunAllPendingInMessageLoop() {
109 base::MessageLoop::current()->PostTask(
110 FROM_HERE
, base::MessageLoop::QuitWhenIdleClosure());
114 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id
) {
115 if (BrowserThread::CurrentlyOn(thread_id
)) {
116 RunAllPendingInMessageLoop();
119 BrowserThread::ID current_thread_id
;
120 if (!BrowserThread::GetCurrentThreadIdentifier(¤t_thread_id
)) {
125 base::RunLoop run_loop
;
126 BrowserThread::PostTask(thread_id
, FROM_HERE
,
127 base::Bind(&RunAllPendingMessageAndSendQuit
, current_thread_id
,
128 run_loop
.QuitClosure()));
129 RunThisRunLoop(&run_loop
);
132 base::Closure
GetQuitTaskForRunLoop(base::RunLoop
* run_loop
) {
133 return base::Bind(&DeferredQuitRunLoop
, run_loop
->QuitClosure(),
137 scoped_ptr
<base::Value
> ExecuteScriptAndGetValue(
138 RenderViewHost
* render_view_host
,
139 const std::string
& script
) {
140 ScriptCallback observer
;
142 render_view_host
->ExecuteJavascriptInWebFrameCallbackResult(
143 base::string16(), // frame_xpath,
144 base::UTF8ToUTF16(script
),
145 base::Bind(&ScriptCallback::ResultCallback
, base::Unretained(&observer
)));
146 base::MessageLoop
* loop
= base::MessageLoop::current();
148 return observer
.result().Pass();
151 MessageLoopRunner::MessageLoopRunner()
152 : loop_running_(false),
153 quit_closure_called_(false) {
156 MessageLoopRunner::~MessageLoopRunner() {
159 void MessageLoopRunner::Run() {
160 // Do not run the message loop if our quit closure has already been called.
161 // This helps in scenarios where the closure has a chance to run before
162 // we Run explicitly.
163 if (quit_closure_called_
)
166 loop_running_
= true;
167 RunThisRunLoop(&run_loop_
);
170 base::Closure
MessageLoopRunner::QuitClosure() {
171 return base::Bind(&MessageLoopRunner::Quit
, this);
174 void MessageLoopRunner::Quit() {
175 quit_closure_called_
= true;
177 // Only run the quit task if we are running the message loop.
179 GetQuitTaskForRunLoop(&run_loop_
).Run();
180 loop_running_
= false;
184 WindowedNotificationObserver::WindowedNotificationObserver(
185 int notification_type
,
186 const NotificationSource
& source
)
189 source_(NotificationService::AllSources()) {
190 AddNotificationType(notification_type
, source
);
193 WindowedNotificationObserver::WindowedNotificationObserver(
194 int notification_type
,
195 const ConditionTestCallback
& callback
)
199 source_(NotificationService::AllSources()) {
200 AddNotificationType(notification_type
, source_
);
203 WindowedNotificationObserver::WindowedNotificationObserver(
204 int notification_type
,
205 const ConditionTestCallbackWithoutSourceAndDetails
& callback
)
208 callback_(base::Bind(&IgnoreSourceAndDetails
, callback
)),
209 source_(NotificationService::AllSources()) {
210 registrar_
.Add(this, notification_type
, source_
);
213 WindowedNotificationObserver::~WindowedNotificationObserver() {}
215 void WindowedNotificationObserver::AddNotificationType(
216 int notification_type
,
217 const NotificationSource
& source
) {
218 registrar_
.Add(this, notification_type
, source
);
221 void WindowedNotificationObserver::Wait() {
226 message_loop_runner_
= new MessageLoopRunner
;
227 message_loop_runner_
->Run();
231 void WindowedNotificationObserver::Observe(
233 const NotificationSource
& source
,
234 const NotificationDetails
& details
) {
237 if (!callback_
.is_null() && !callback_
.Run(source
, details
))
244 message_loop_runner_
->Quit();
248 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
249 : child_thread_count_(0) {
250 RenderProcessHost::SetRunRendererInProcess(true);
251 BrowserChildProcessObserver::Add(this);
254 InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
255 if (child_thread_count_
) {
256 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI
));
257 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO
));
258 runner_
= new MessageLoopRunner
;
261 BrowserChildProcessObserver::Remove(this);
262 RenderProcessHost::SetRunRendererInProcess(false);
265 void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
266 const ChildProcessData
& data
) {
267 child_thread_count_
++;
270 void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
271 const ChildProcessData
& data
) {
272 if (--child_thread_count_
)
279 } // namespace content