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/notification_service.h" 13 #include "content/public/browser/render_view_host.h" 14 #include "content/public/test/test_launcher.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 17 namespace content { 18 19 namespace { 20 21 // Number of times to repost a Quit task so that the MessageLoop finishes up 22 // pending tasks and tasks posted by those pending tasks without risking the 23 // potential hang behavior of MessageLoop::QuitWhenIdle. 24 // The criteria for choosing this number: it should be high enough to make the 25 // quit act like QuitWhenIdle, while taking into account that any page which is 26 // animating may be rendering another frame for each quit deferral. For an 27 // animating page, the potential delay to quitting the RunLoop would be 28 // kNumQuitDeferrals * frame_render_time. Some perf tests run slow, such as 29 // 200ms/frame. 30 static const int kNumQuitDeferrals = 10; 31 32 static void DeferredQuitRunLoop(const base::Closure& quit_task, 33 int num_quit_deferrals) { 34 if (num_quit_deferrals <= 0) { 35 quit_task.Run(); 36 } else { 37 base::MessageLoop::current()->PostTask( 38 FROM_HERE, 39 base::Bind(&DeferredQuitRunLoop, quit_task, num_quit_deferrals - 1)); 40 } 41 } 42 43 void RunAllPendingMessageAndSendQuit(BrowserThread::ID thread_id, 44 const base::Closure& quit_task) { 45 RunAllPendingInMessageLoop(); 46 BrowserThread::PostTask(thread_id, FROM_HERE, quit_task); 47 } 48 49 // Class used handle result callbacks for ExecuteScriptAndGetValue. 50 class ScriptCallback { 51 public: 52 ScriptCallback() { } 53 virtual ~ScriptCallback() { } 54 void ResultCallback(const base::Value* result); 55 56 scoped_ptr<base::Value> result() { return result_.Pass(); } 57 58 private: 59 scoped_ptr<base::Value> result_; 60 61 DISALLOW_COPY_AND_ASSIGN(ScriptCallback); 62 }; 63 64 void ScriptCallback::ResultCallback(const base::Value* result) { 65 if (result) 66 result_.reset(result->DeepCopy()); 67 base::MessageLoop::current()->Quit(); 68 } 69 70 // Adapter that makes a WindowedNotificationObserver::ConditionTestCallback from 71 // a WindowedNotificationObserver::ConditionTestCallbackWithoutSourceAndDetails 72 // by ignoring the notification source and details. 73 bool IgnoreSourceAndDetails( 74 const WindowedNotificationObserver:: 75 ConditionTestCallbackWithoutSourceAndDetails& callback, 76 const NotificationSource& source, 77 const NotificationDetails& details) { 78 return callback.Run(); 79 } 80 81 } // namespace 82 83 void RunMessageLoop() { 84 base::RunLoop run_loop; 85 RunThisRunLoop(&run_loop); 86 } 87 88 void RunThisRunLoop(base::RunLoop* run_loop) { 89 base::MessageLoop::ScopedNestableTaskAllower allow( 90 base::MessageLoop::current()); 91 92 // If we're running inside a browser test, we might need to allow the test 93 // launcher to do extra work before/after running a nested message loop. 94 TestLauncherDelegate* delegate = NULL; 95 #if !defined(OS_IOS) 96 delegate = GetCurrentTestLauncherDelegate(); 97 #endif 98 if (delegate) 99 delegate->PreRunMessageLoop(run_loop); 100 run_loop->Run(); 101 if (delegate) 102 delegate->PostRunMessageLoop(); 103 } 104 105 void RunAllPendingInMessageLoop() { 106 base::MessageLoop::current()->PostTask( 107 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); 108 RunMessageLoop(); 109 } 110 111 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) { 112 if (BrowserThread::CurrentlyOn(thread_id)) { 113 RunAllPendingInMessageLoop(); 114 return; 115 } 116 BrowserThread::ID current_thread_id; 117 if (!BrowserThread::GetCurrentThreadIdentifier(¤t_thread_id)) { 118 NOTREACHED(); 119 return; 120 } 121 122 base::RunLoop run_loop; 123 BrowserThread::PostTask(thread_id, FROM_HERE, 124 base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id, 125 run_loop.QuitClosure())); 126 RunThisRunLoop(&run_loop); 127 } 128 129 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) { 130 return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(), 131 kNumQuitDeferrals); 132 } 133 134 scoped_ptr<base::Value> ExecuteScriptAndGetValue( 135 RenderViewHost* render_view_host, 136 const std::string& script) { 137 ScriptCallback observer; 138 139 render_view_host->ExecuteJavascriptInWebFrameCallbackResult( 140 base::string16(), // frame_xpath, 141 UTF8ToUTF16(script), 142 base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer))); 143 base::MessageLoop* loop = base::MessageLoop::current(); 144 loop->Run(); 145 return observer.result().Pass(); 146 } 147 148 MessageLoopRunner::MessageLoopRunner() 149 : loop_running_(false), 150 quit_closure_called_(false) { 151 } 152 153 MessageLoopRunner::~MessageLoopRunner() { 154 } 155 156 void MessageLoopRunner::Run() { 157 // Do not run the message loop if our quit closure has already been called. 158 // This helps in scenarios where the closure has a chance to run before 159 // we Run explicitly. 160 if (quit_closure_called_) 161 return; 162 163 loop_running_ = true; 164 RunThisRunLoop(&run_loop_); 165 } 166 167 base::Closure MessageLoopRunner::QuitClosure() { 168 return base::Bind(&MessageLoopRunner::Quit, this); 169 } 170 171 void MessageLoopRunner::Quit() { 172 quit_closure_called_ = true; 173 174 // Only run the quit task if we are running the message loop. 175 if (loop_running_) { 176 GetQuitTaskForRunLoop(&run_loop_).Run(); 177 loop_running_ = false; 178 } 179 } 180 181 WindowedNotificationObserver::WindowedNotificationObserver( 182 int notification_type, 183 const NotificationSource& source) 184 : seen_(false), 185 running_(false), 186 source_(NotificationService::AllSources()) { 187 AddNotificationType(notification_type, source); 188 } 189 190 WindowedNotificationObserver::WindowedNotificationObserver( 191 int notification_type, 192 const ConditionTestCallback& callback) 193 : seen_(false), 194 running_(false), 195 callback_(callback), 196 source_(NotificationService::AllSources()) { 197 AddNotificationType(notification_type, source_); 198 } 199 200 WindowedNotificationObserver::WindowedNotificationObserver( 201 int notification_type, 202 const ConditionTestCallbackWithoutSourceAndDetails& callback) 203 : seen_(false), 204 running_(false), 205 callback_(base::Bind(&IgnoreSourceAndDetails, callback)), 206 source_(NotificationService::AllSources()) { 207 registrar_.Add(this, notification_type, source_); 208 } 209 210 WindowedNotificationObserver::~WindowedNotificationObserver() {} 211 212 void WindowedNotificationObserver::AddNotificationType( 213 int notification_type, 214 const NotificationSource& source) { 215 registrar_.Add(this, notification_type, source); 216 } 217 218 void WindowedNotificationObserver::Wait() { 219 if (seen_) 220 return; 221 222 running_ = true; 223 message_loop_runner_ = new MessageLoopRunner; 224 message_loop_runner_->Run(); 225 EXPECT_TRUE(seen_); 226 } 227 228 void WindowedNotificationObserver::Observe( 229 int type, 230 const NotificationSource& source, 231 const NotificationDetails& details) { 232 source_ = source; 233 details_ = details; 234 if (!callback_.is_null() && !callback_.Run(source, details)) 235 return; 236 237 seen_ = true; 238 if (!running_) 239 return; 240 241 message_loop_runner_->Quit(); 242 running_ = false; 243 } 244 245 } // namespace content 246