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 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 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: 55 ScriptCallback() { } 56 virtual ~ScriptCallback() { } 57 void ResultCallback(const base::Value* result); 58 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 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: 76 TaskObserver() : processed_(false) {} 77 virtual ~TaskObserver() {} 78 79 // MessageLoop::TaskObserver overrides. 80 virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE { 81 } 82 virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE { 83 processed_ = true; 84 } 85 86 // Returns true if any task was processed. 87 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. 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 107 void RunMessageLoop() { 108 base::RunLoop run_loop; 109 RunThisRunLoop(&run_loop); 110 } 111 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 129 void RunAllPendingInMessageLoop() { 130 base::MessageLoop::current()->PostTask( 131 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); 132 RunMessageLoop(); 133 } 134 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(¤t_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 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 167 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) { 168 return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(), 169 kNumQuitDeferrals); 170 } 171 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 184 MessageLoopRunner::MessageLoopRunner() 185 : loop_running_(false), 186 quit_closure_called_(false) { 187 } 188 189 MessageLoopRunner::~MessageLoopRunner() { 190 } 191 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 203 base::Closure MessageLoopRunner::QuitClosure() { 204 return base::Bind(&MessageLoopRunner::Quit, this); 205 } 206 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 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 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 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 246 WindowedNotificationObserver::~WindowedNotificationObserver() {} 247 248 void WindowedNotificationObserver::AddNotificationType( 249 int notification_type, 250 const NotificationSource& source) { 251 registrar_.Add(this, notification_type, source); 252 } 253 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 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 281 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper() 282 : child_thread_count_(0) { 283 RenderProcessHost::SetRunRendererInProcess(true); 284 BrowserChildProcessObserver::Add(this); 285 } 286 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 298 void InProcessUtilityThreadHelper::BrowserChildProcessHostConnected( 299 const ChildProcessData& data) { 300 child_thread_count_++; 301 } 302 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