• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/public/test/test_utils.h"
6 
7 #include "base/bind.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_frame_host.h"
15 #include "content/public/browser/render_process_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"
19 
20 namespace content {
21 
22 namespace {
23 
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
32 // 200ms/frame.
33 static const int kNumQuitDeferrals = 10;
34 
DeferredQuitRunLoop(const base::Closure & quit_task,int num_quit_deferrals)35 static void DeferredQuitRunLoop(const base::Closure& quit_task,
36                                 int num_quit_deferrals) {
37   if (num_quit_deferrals <= 0) {
38     quit_task.Run();
39   } else {
40     base::MessageLoop::current()->PostTask(
41         FROM_HERE,
42         base::Bind(&DeferredQuitRunLoop, quit_task, num_quit_deferrals - 1));
43   }
44 }
45 
RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id,const base::Closure & quit_task)46 void RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id,
47                                      const base::Closure& quit_task) {
48   RunAllPendingInMessageLoop();
49   BrowserThread::PostTask(thread_id, FROM_HERE, quit_task);
50 }
51 
52 // Class used to handle result callbacks for ExecuteScriptAndGetValue.
53 class ScriptCallback {
54  public:
ScriptCallback()55   ScriptCallback() { }
~ScriptCallback()56   virtual ~ScriptCallback() { }
57   void ResultCallback(const base::Value* result);
58 
result()59   scoped_ptr<base::Value> result() { return result_.Pass(); }
60 
61  private:
62   scoped_ptr<base::Value> result_;
63 
64   DISALLOW_COPY_AND_ASSIGN(ScriptCallback);
65 };
66 
ResultCallback(const base::Value * result)67 void ScriptCallback::ResultCallback(const base::Value* result) {
68   if (result)
69     result_.reset(result->DeepCopy());
70   base::MessageLoop::current()->Quit();
71 }
72 
73 // Monitors if any task is processed by the message loop.
74 class TaskObserver : public base::MessageLoop::TaskObserver {
75  public:
TaskObserver()76   TaskObserver() : processed_(false) {}
~TaskObserver()77   virtual ~TaskObserver() {}
78 
79   // MessageLoop::TaskObserver overrides.
WillProcessTask(const base::PendingTask & pending_task)80   virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
81   }
DidProcessTask(const base::PendingTask & pending_task)82   virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {
83     processed_ = true;
84   }
85 
86   // Returns true if any task was processed.
processed() const87   bool processed() const { return processed_; }
88 
89  private:
90   bool processed_;
91   DISALLOW_COPY_AND_ASSIGN(TaskObserver);
92 };
93 
94 // Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
95 // a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
96 // by ignoring the notification source and details.
IgnoreSourceAndDetails(const WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails & callback,const NotificationSource & source,const NotificationDetails & details)97 bool IgnoreSourceAndDetails(
98     const WindowedNotificationObserver::
99         ConditionTestCallbackWithoutSourceAndDetails& callback,
100     const NotificationSource& source,
101     const NotificationDetails& details) {
102   return callback.Run();
103 }
104 
105 }  // namespace
106 
RunMessageLoop()107 void RunMessageLoop() {
108   base::RunLoop run_loop;
109   RunThisRunLoop(&run_loop);
110 }
111 
RunThisRunLoop(base::RunLoop * run_loop)112 void RunThisRunLoop(base::RunLoop* run_loop) {
113   base::MessageLoop::ScopedNestableTaskAllower allow(
114       base::MessageLoop::current());
115 
116   // If we're running inside a browser test, we might need to allow the test
117   // launcher to do extra work before/after running a nested message loop.
118   TestLauncherDelegate* delegate = NULL;
119 #if !defined(OS_IOS)
120   delegate = GetCurrentTestLauncherDelegate();
121 #endif
122   if (delegate)
123     delegate->PreRunMessageLoop(run_loop);
124   run_loop->Run();
125   if (delegate)
126     delegate->PostRunMessageLoop();
127 }
128 
RunAllPendingInMessageLoop()129 void RunAllPendingInMessageLoop() {
130   base::MessageLoop::current()->PostTask(
131       FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
132   RunMessageLoop();
133 }
134 
RunAllPendingInMessageLoop(BrowserThread::ID thread_id)135 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
136   if (BrowserThread::CurrentlyOn(thread_id)) {
137     RunAllPendingInMessageLoop();
138     return;
139   }
140   BrowserThread::ID current_thread_id;
141   if (!BrowserThread::GetCurrentThreadIdentifier(&current_thread_id)) {
142     NOTREACHED();
143     return;
144   }
145 
146   base::RunLoop run_loop;
147   BrowserThread::PostTask(thread_id, FROM_HERE,
148       base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id,
149                  run_loop.QuitClosure()));
150   RunThisRunLoop(&run_loop);
151 }
152 
RunAllBlockingPoolTasksUntilIdle()153 void RunAllBlockingPoolTasksUntilIdle() {
154   while (true) {
155     content::BrowserThread::GetBlockingPool()->FlushForTesting();
156 
157     TaskObserver task_observer;
158     base::MessageLoop::current()->AddTaskObserver(&task_observer);
159     base::RunLoop().RunUntilIdle();
160     base::MessageLoop::current()->RemoveTaskObserver(&task_observer);
161 
162     if (!task_observer.processed())
163       break;
164   }
165 }
166 
GetQuitTaskForRunLoop(base::RunLoop * run_loop)167 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
168   return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
169                     kNumQuitDeferrals);
170 }
171 
ExecuteScriptAndGetValue(RenderFrameHost * render_frame_host,const std::string & script)172 scoped_ptr<base::Value> ExecuteScriptAndGetValue(
173     RenderFrameHost* render_frame_host, const std::string& script) {
174   ScriptCallback observer;
175 
176   render_frame_host->ExecuteJavaScript(
177       base::UTF8ToUTF16(script),
178       base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
179   base::MessageLoop* loop = base::MessageLoop::current();
180   loop->Run();
181   return observer.result().Pass();
182 }
183 
MessageLoopRunner()184 MessageLoopRunner::MessageLoopRunner()
185     : loop_running_(false),
186       quit_closure_called_(false) {
187 }
188 
~MessageLoopRunner()189 MessageLoopRunner::~MessageLoopRunner() {
190 }
191 
Run()192 void MessageLoopRunner::Run() {
193   // Do not run the message loop if our quit closure has already been called.
194   // This helps in scenarios where the closure has a chance to run before
195   // we Run explicitly.
196   if (quit_closure_called_)
197     return;
198 
199   loop_running_ = true;
200   RunThisRunLoop(&run_loop_);
201 }
202 
QuitClosure()203 base::Closure MessageLoopRunner::QuitClosure() {
204   return base::Bind(&MessageLoopRunner::Quit, this);
205 }
206 
Quit()207 void MessageLoopRunner::Quit() {
208   quit_closure_called_ = true;
209 
210   // Only run the quit task if we are running the message loop.
211   if (loop_running_) {
212     GetQuitTaskForRunLoop(&run_loop_).Run();
213     loop_running_ = false;
214   }
215 }
216 
WindowedNotificationObserver(int notification_type,const NotificationSource & source)217 WindowedNotificationObserver::WindowedNotificationObserver(
218     int notification_type,
219     const NotificationSource& source)
220     : seen_(false),
221       running_(false),
222       source_(NotificationService::AllSources()) {
223   AddNotificationType(notification_type, source);
224 }
225 
WindowedNotificationObserver(int notification_type,const ConditionTestCallback & callback)226 WindowedNotificationObserver::WindowedNotificationObserver(
227     int notification_type,
228     const ConditionTestCallback& callback)
229     : seen_(false),
230       running_(false),
231       callback_(callback),
232       source_(NotificationService::AllSources()) {
233   AddNotificationType(notification_type, source_);
234 }
235 
WindowedNotificationObserver(int notification_type,const ConditionTestCallbackWithoutSourceAndDetails & callback)236 WindowedNotificationObserver::WindowedNotificationObserver(
237     int notification_type,
238     const ConditionTestCallbackWithoutSourceAndDetails& callback)
239     : seen_(false),
240       running_(false),
241       callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
242       source_(NotificationService::AllSources()) {
243   registrar_.Add(this, notification_type, source_);
244 }
245 
~WindowedNotificationObserver()246 WindowedNotificationObserver::~WindowedNotificationObserver() {}
247 
AddNotificationType(int notification_type,const NotificationSource & source)248 void WindowedNotificationObserver::AddNotificationType(
249     int notification_type,
250     const NotificationSource& source) {
251   registrar_.Add(this, notification_type, source);
252 }
253 
Wait()254 void WindowedNotificationObserver::Wait() {
255   if (seen_)
256     return;
257 
258   running_ = true;
259   message_loop_runner_ = new MessageLoopRunner;
260   message_loop_runner_->Run();
261   EXPECT_TRUE(seen_);
262 }
263 
Observe(int type,const NotificationSource & source,const NotificationDetails & details)264 void WindowedNotificationObserver::Observe(
265     int type,
266     const NotificationSource& source,
267     const NotificationDetails& details) {
268   source_ = source;
269   details_ = details;
270   if (!callback_.is_null() && !callback_.Run(source, details))
271     return;
272 
273   seen_ = true;
274   if (!running_)
275     return;
276 
277   message_loop_runner_->Quit();
278   running_ = false;
279 }
280 
InProcessUtilityThreadHelper()281 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
282     : child_thread_count_(0) {
283   RenderProcessHost::SetRunRendererInProcess(true);
284   BrowserChildProcessObserver::Add(this);
285 }
286 
~InProcessUtilityThreadHelper()287 InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
288   if (child_thread_count_) {
289     DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI));
290     DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
291     runner_ = new MessageLoopRunner;
292     runner_->Run();
293   }
294   BrowserChildProcessObserver::Remove(this);
295   RenderProcessHost::SetRunRendererInProcess(false);
296 }
297 
BrowserChildProcessHostConnected(const ChildProcessData & data)298 void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
299     const ChildProcessData& data) {
300   child_thread_count_++;
301 }
302 
BrowserChildProcessHostDisconnected(const ChildProcessData & data)303 void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
304     const ChildProcessData& data) {
305   if (--child_thread_count_)
306     return;
307 
308   if (runner_.get())
309     runner_->Quit();
310 }
311 
312 }  // namespace content
313