• 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 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 // Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from
74 // a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails
75 // by ignoring the notification source and details.
IgnoreSourceAndDetails(const WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails & callback,const NotificationSource & source,const NotificationDetails & details)76 bool IgnoreSourceAndDetails(
77     const WindowedNotificationObserver::
78         ConditionTestCallbackWithoutSourceAndDetails& callback,
79     const NotificationSource& source,
80     const NotificationDetails& details) {
81   return callback.Run();
82 }
83 
84 }  // namespace
85 
RunMessageLoop()86 void RunMessageLoop() {
87   base::RunLoop run_loop;
88   RunThisRunLoop(&run_loop);
89 }
90 
RunThisRunLoop(base::RunLoop * run_loop)91 void RunThisRunLoop(base::RunLoop* run_loop) {
92   base::MessageLoop::ScopedNestableTaskAllower allow(
93       base::MessageLoop::current());
94 
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;
98 #if !defined(OS_IOS)
99   delegate = GetCurrentTestLauncherDelegate();
100 #endif
101   if (delegate)
102     delegate->PreRunMessageLoop(run_loop);
103   run_loop->Run();
104   if (delegate)
105     delegate->PostRunMessageLoop();
106 }
107 
RunAllPendingInMessageLoop()108 void RunAllPendingInMessageLoop() {
109   base::MessageLoop::current()->PostTask(
110       FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
111   RunMessageLoop();
112 }
113 
RunAllPendingInMessageLoop(BrowserThread::ID thread_id)114 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) {
115   if (BrowserThread::CurrentlyOn(thread_id)) {
116     RunAllPendingInMessageLoop();
117     return;
118   }
119   BrowserThread::ID current_thread_id;
120   if (!BrowserThread::GetCurrentThreadIdentifier(&current_thread_id)) {
121     NOTREACHED();
122     return;
123   }
124 
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);
130 }
131 
GetQuitTaskForRunLoop(base::RunLoop * run_loop)132 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) {
133   return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(),
134                     kNumQuitDeferrals);
135 }
136 
ExecuteScriptAndGetValue(RenderFrameHost * render_frame_host,const std::string & script)137 scoped_ptr<base::Value> ExecuteScriptAndGetValue(
138     RenderFrameHost* render_frame_host, const std::string& script) {
139   ScriptCallback observer;
140 
141   render_frame_host->ExecuteJavaScript(
142       base::UTF8ToUTF16(script),
143       base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer)));
144   base::MessageLoop* loop = base::MessageLoop::current();
145   loop->Run();
146   return observer.result().Pass();
147 }
148 
MessageLoopRunner()149 MessageLoopRunner::MessageLoopRunner()
150     : loop_running_(false),
151       quit_closure_called_(false) {
152 }
153 
~MessageLoopRunner()154 MessageLoopRunner::~MessageLoopRunner() {
155 }
156 
Run()157 void MessageLoopRunner::Run() {
158   // Do not run the message loop if our quit closure has already been called.
159   // This helps in scenarios where the closure has a chance to run before
160   // we Run explicitly.
161   if (quit_closure_called_)
162     return;
163 
164   loop_running_ = true;
165   RunThisRunLoop(&run_loop_);
166 }
167 
QuitClosure()168 base::Closure MessageLoopRunner::QuitClosure() {
169   return base::Bind(&MessageLoopRunner::Quit, this);
170 }
171 
Quit()172 void MessageLoopRunner::Quit() {
173   quit_closure_called_ = true;
174 
175   // Only run the quit task if we are running the message loop.
176   if (loop_running_) {
177     GetQuitTaskForRunLoop(&run_loop_).Run();
178     loop_running_ = false;
179   }
180 }
181 
WindowedNotificationObserver(int notification_type,const NotificationSource & source)182 WindowedNotificationObserver::WindowedNotificationObserver(
183     int notification_type,
184     const NotificationSource& source)
185     : seen_(false),
186       running_(false),
187       source_(NotificationService::AllSources()) {
188   AddNotificationType(notification_type, source);
189 }
190 
WindowedNotificationObserver(int notification_type,const ConditionTestCallback & callback)191 WindowedNotificationObserver::WindowedNotificationObserver(
192     int notification_type,
193     const ConditionTestCallback& callback)
194     : seen_(false),
195       running_(false),
196       callback_(callback),
197       source_(NotificationService::AllSources()) {
198   AddNotificationType(notification_type, source_);
199 }
200 
WindowedNotificationObserver(int notification_type,const ConditionTestCallbackWithoutSourceAndDetails & callback)201 WindowedNotificationObserver::WindowedNotificationObserver(
202     int notification_type,
203     const ConditionTestCallbackWithoutSourceAndDetails& callback)
204     : seen_(false),
205       running_(false),
206       callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
207       source_(NotificationService::AllSources()) {
208   registrar_.Add(this, notification_type, source_);
209 }
210 
~WindowedNotificationObserver()211 WindowedNotificationObserver::~WindowedNotificationObserver() {}
212 
AddNotificationType(int notification_type,const NotificationSource & source)213 void WindowedNotificationObserver::AddNotificationType(
214     int notification_type,
215     const NotificationSource& source) {
216   registrar_.Add(this, notification_type, source);
217 }
218 
Wait()219 void WindowedNotificationObserver::Wait() {
220   if (seen_)
221     return;
222 
223   running_ = true;
224   message_loop_runner_ = new MessageLoopRunner;
225   message_loop_runner_->Run();
226   EXPECT_TRUE(seen_);
227 }
228 
Observe(int type,const NotificationSource & source,const NotificationDetails & details)229 void WindowedNotificationObserver::Observe(
230     int type,
231     const NotificationSource& source,
232     const NotificationDetails& details) {
233   source_ = source;
234   details_ = details;
235   if (!callback_.is_null() && !callback_.Run(source, details))
236     return;
237 
238   seen_ = true;
239   if (!running_)
240     return;
241 
242   message_loop_runner_->Quit();
243   running_ = false;
244 }
245 
InProcessUtilityThreadHelper()246 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
247     : child_thread_count_(0) {
248   RenderProcessHost::SetRunRendererInProcess(true);
249   BrowserChildProcessObserver::Add(this);
250 }
251 
~InProcessUtilityThreadHelper()252 InProcessUtilityThreadHelper::~InProcessUtilityThreadHelper() {
253   if (child_thread_count_) {
254     DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::UI));
255     DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
256     runner_ = new MessageLoopRunner;
257     runner_->Run();
258   }
259   BrowserChildProcessObserver::Remove(this);
260   RenderProcessHost::SetRunRendererInProcess(false);
261 }
262 
BrowserChildProcessHostConnected(const ChildProcessData & data)263 void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected(
264     const ChildProcessData& data) {
265   child_thread_count_++;
266 }
267 
BrowserChildProcessHostDisconnected(const ChildProcessData & data)268 void InProcessUtilityThreadHelper::BrowserChildProcessHostDisconnected(
269     const ChildProcessData& data) {
270   if (--child_thread_count_)
271     return;
272 
273   if (runner_.get())
274     runner_->Quit();
275 }
276 
277 }  // namespace content
278