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/command_line.h"
9 #include "base/location.h"
10 #include "base/run_loop.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/values.h"
15 #include "content/public/browser/browser_child_process_host_iterator.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/render_frame_host.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/common/process_type.h"
22 #include "content/public/test/test_launcher.h"
23 #include "testing/gtest/include/gtest/gtest.h"
29 // Number of times to repost a Quit task so that the MessageLoop finishes up
30 // pending tasks and tasks posted by those pending tasks without risking the
31 // potential hang behavior of MessageLoop::QuitWhenIdle.
32 // The criteria for choosing this number: it should be high enough to make the
33 // quit act like QuitWhenIdle, while taking into account that any page which is
34 // animating may be rendering another frame for each quit deferral. For an
35 // animating page, the potential delay to quitting the RunLoop would be
36 // kNumQuitDeferrals * frame_render_time. Some perf tests run slow, such as
38 static const int kNumQuitDeferrals
= 10;
40 static void DeferredQuitRunLoop(const base::Closure
& quit_task
,
41 int num_quit_deferrals
) {
42 if (num_quit_deferrals
<= 0) {
45 base::ThreadTaskRunnerHandle::Get()->PostTask(
47 base::Bind(&DeferredQuitRunLoop
, quit_task
, num_quit_deferrals
- 1));
51 void RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id
,
52 const base::Closure
& quit_task
) {
53 RunAllPendingInMessageLoop();
54 BrowserThread::PostTask(thread_id
, FROM_HERE
, quit_task
);
57 // Class used to handle result callbacks for ExecuteScriptAndGetValue.
58 class ScriptCallback
{
61 virtual ~ScriptCallback() { }
62 void ResultCallback(const base::Value
* result
);
64 scoped_ptr
<base::Value
> result() { return result_
.Pass(); }
67 scoped_ptr
<base::Value
> result_
;
69 DISALLOW_COPY_AND_ASSIGN(ScriptCallback
);
72 void ScriptCallback::ResultCallback(const base::Value
* result
) {
74 result_
.reset(result
->DeepCopy());
75 base::MessageLoop::current()->Quit();
78 // Monitors if any task is processed by the message loop.
79 class TaskObserver
: public base::MessageLoop::TaskObserver
{
81 TaskObserver() : processed_(false) {}
82 ~TaskObserver() override
{}
84 // MessageLoop::TaskObserver overrides.
85 void WillProcessTask(const base::PendingTask
& pending_task
) override
{}
86 void DidProcessTask(const base::PendingTask
& pending_task
) override
{
90 // Returns true if any task was processed.
91 bool processed() const { return processed_
; }
95 DISALLOW_COPY_AND_ASSIGN(TaskObserver
);
98 // Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
99 // a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
100 // by ignoring the notification source and details.
101 bool IgnoreSourceAndDetails(
102 const WindowedNotificationObserver::
103 ConditionTestCallbackWithoutSourceAndDetails
& callback
,
104 const NotificationSource
& source
,
105 const NotificationDetails
& details
) {
106 return callback
.Run();
111 void RunMessageLoop() {
112 base::RunLoop run_loop
;
113 RunThisRunLoop(&run_loop
);
116 void RunThisRunLoop(base::RunLoop
* run_loop
) {
117 base::MessageLoop::ScopedNestableTaskAllower
allow(
118 base::MessageLoop::current());
120 // If we're running inside a browser test, we might need to allow the test
121 // launcher to do extra work before/after running a nested message loop.
122 TestLauncherDelegate
* delegate
= NULL
;
124 delegate
= GetCurrentTestLauncherDelegate();
127 delegate
->PreRunMessageLoop(run_loop
);
130 delegate
->PostRunMessageLoop();
133 void RunAllPendingInMessageLoop() {
134 base::RunLoop run_loop
;
135 base::ThreadTaskRunnerHandle::Get()->PostTask(
136 FROM_HERE
, GetQuitTaskForRunLoop(&run_loop
));
137 RunThisRunLoop(&run_loop
);
140 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id
) {
141 if (BrowserThread::CurrentlyOn(thread_id
)) {
142 RunAllPendingInMessageLoop();
145 BrowserThread::ID current_thread_id
;
146 if (!BrowserThread::GetCurrentThreadIdentifier(¤t_thread_id
)) {
151 base::RunLoop run_loop
;
152 BrowserThread::PostTask(thread_id
, FROM_HERE
,
153 base::Bind(&RunAllPendingMessageAndSendQuit
, current_thread_id
,
154 run_loop
.QuitClosure()));
155 RunThisRunLoop(&run_loop
);
158 void RunAllBlockingPoolTasksUntilIdle() {
160 content::BrowserThread::GetBlockingPool()->FlushForTesting();
162 TaskObserver task_observer
;
163 base::MessageLoop::current()->AddTaskObserver(&task_observer
);
164 base::RunLoop().RunUntilIdle();
165 base::MessageLoop::current()->RemoveTaskObserver(&task_observer
);
167 if (!task_observer
.processed())
172 base::Closure
GetQuitTaskForRunLoop(base::RunLoop
* run_loop
) {
173 return base::Bind(&DeferredQuitRunLoop
, run_loop
->QuitClosure(),
177 scoped_ptr
<base::Value
> ExecuteScriptAndGetValue(
178 RenderFrameHost
* render_frame_host
, const std::string
& script
) {
179 ScriptCallback observer
;
181 render_frame_host
->ExecuteJavaScriptForTests(
182 base::UTF8ToUTF16(script
),
183 base::Bind(&ScriptCallback::ResultCallback
, base::Unretained(&observer
)));
184 base::MessageLoop
* loop
= base::MessageLoop::current();
186 return observer
.result().Pass();
189 bool AreAllSitesIsolatedForTesting() {
190 return base::CommandLine::ForCurrentProcess()->HasSwitch(
191 switches::kSitePerProcess
);
194 void IsolateAllSitesForTesting(base::CommandLine
* command_line
) {
195 command_line
->AppendSwitch(switches::kSitePerProcess
);
198 MessageLoopRunner::MessageLoopRunner()
199 : loop_running_(false),
200 quit_closure_called_(false) {
203 MessageLoopRunner::~MessageLoopRunner() {
206 void MessageLoopRunner::Run() {
207 // Do not run the message loop if our quit closure has already been called.
208 // This helps in scenarios where the closure has a chance to run before
209 // we Run explicitly.
210 if (quit_closure_called_
)
213 loop_running_
= true;
214 RunThisRunLoop(&run_loop_
);
217 base::Closure
MessageLoopRunner::QuitClosure() {
218 return base::Bind(&MessageLoopRunner::Quit
, this);
221 void MessageLoopRunner::Quit() {
222 quit_closure_called_
= true;
224 // Only run the quit task if we are running the message loop.
226 GetQuitTaskForRunLoop(&run_loop_
).Run();
227 loop_running_
= false;
231 WindowedNotificationObserver::WindowedNotificationObserver(
232 int notification_type
,
233 const NotificationSource
& source
)
236 source_(NotificationService::AllSources()) {
237 AddNotificationType(notification_type
, source
);
240 WindowedNotificationObserver::WindowedNotificationObserver(
241 int notification_type
,
242 const ConditionTestCallback
& callback
)
246 source_(NotificationService::AllSources()) {
247 AddNotificationType(notification_type
, source_
);
250 WindowedNotificationObserver::WindowedNotificationObserver(
251 int notification_type
,
252 const ConditionTestCallbackWithoutSourceAndDetails
& callback
)
255 callback_(base::Bind(&IgnoreSourceAndDetails
, callback
)),
256 source_(NotificationService::AllSources()) {
257 registrar_
.Add(this, notification_type
, source_
);
260 WindowedNotificationObserver::~WindowedNotificationObserver() {}
262 void WindowedNotificationObserver::AddNotificationType(
263 int notification_type
,
264 const NotificationSource
& source
) {
265 registrar_
.Add(this, notification_type
, source
);
268 void WindowedNotificationObserver::Wait() {
273 message_loop_runner_
= new MessageLoopRunner
;
274 message_loop_runner_
->Run();
278 void WindowedNotificationObserver::Observe(
280 const NotificationSource
& source
,
281 const NotificationDetails
& details
) {
284 if (!callback_
.is_null() && !callback_
.Run(source
, details
))
291 message_loop_runner_
->Quit();
295 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
296 : child_thread_count_(0) {
297 RenderProcessHost::SetRunRendererInProcess(true);
298 BrowserChildProcessObserver::Add(this);
301 InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
302 if (child_thread_count_
) {
303 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI
));
304 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO
));
305 runner_
= new MessageLoopRunner
;
308 BrowserChildProcessObserver::Remove(this);
309 RenderProcessHost::SetRunRendererInProcess(false);
312 void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
313 const ChildProcessData
& data
) {
314 child_thread_count_
++;
317 void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
318 const ChildProcessData
& data
) {
319 if (--child_thread_count_
)
326 RenderFrameDeletedObserver::RenderFrameDeletedObserver(RenderFrameHost
* rfh
)
327 : WebContentsObserver(WebContents::FromRenderFrameHost(rfh
)),
328 process_id_(rfh
->GetProcess()->GetID()),
329 routing_id_(rfh
->GetRoutingID()),
332 RenderFrameDeletedObserver::~RenderFrameDeletedObserver() {}
334 void RenderFrameDeletedObserver::RenderFrameDeleted(
335 RenderFrameHost
* render_frame_host
) {
336 if (render_frame_host
->GetProcess()->GetID() == process_id_
&&
337 render_frame_host
->GetRoutingID() == routing_id_
) {
345 bool RenderFrameDeletedObserver::deleted() {
349 void RenderFrameDeletedObserver::WaitUntilDeleted() {
353 runner_
.reset(new base::RunLoop());
358 WebContentsDestroyedWatcher::WebContentsDestroyedWatcher(
359 WebContents
* web_contents
)
360 : WebContentsObserver(web_contents
),
361 message_loop_runner_(new MessageLoopRunner
) {
362 EXPECT_TRUE(web_contents
!= NULL
);
365 WebContentsDestroyedWatcher::~WebContentsDestroyedWatcher() {}
367 void WebContentsDestroyedWatcher::Wait() {
368 message_loop_runner_
->Run();
371 void WebContentsDestroyedWatcher::WebContentsDestroyed() {
372 message_loop_runner_
->Quit();
375 } // namespace content