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 } // namespace 71 72 void RunMessageLoop() { 73 base::RunLoop run_loop; 74 RunThisRunLoop(&run_loop); 75 } 76 77 void RunThisRunLoop(base::RunLoop* run_loop) { 78 base::MessageLoop::ScopedNestableTaskAllower allow( 79 base::MessageLoop::current()); 80 81 // If we're running inside a browser test, we might need to allow the test 82 // launcher to do extra work before/after running a nested message loop. 83 TestLauncherDelegate* delegate = NULL; 84 #if !defined(OS_IOS) 85 delegate = GetCurrentTestLauncherDelegate(); 86 #endif 87 if (delegate) 88 delegate->PreRunMessageLoop(run_loop); 89 run_loop->Run(); 90 if (delegate) 91 delegate->PostRunMessageLoop(); 92 } 93 94 void RunAllPendingInMessageLoop() { 95 base::MessageLoop::current()->PostTask( 96 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); 97 RunMessageLoop(); 98 } 99 100 void RunAllPendingInMessageLoop(BrowserThread::ID thread_id) { 101 if (BrowserThread::CurrentlyOn(thread_id)) { 102 RunAllPendingInMessageLoop(); 103 return; 104 } 105 BrowserThread::ID current_thread_id; 106 if (!BrowserThread::GetCurrentThreadIdentifier(¤t_thread_id)) { 107 NOTREACHED(); 108 return; 109 } 110 111 base::RunLoop run_loop; 112 BrowserThread::PostTask(thread_id, FROM_HERE, 113 base::Bind(&RunAllPendingMessageAndSendQuit, current_thread_id, 114 run_loop.QuitClosure())); 115 RunThisRunLoop(&run_loop); 116 } 117 118 base::Closure GetQuitTaskForRunLoop(base::RunLoop* run_loop) { 119 return base::Bind(&DeferredQuitRunLoop, run_loop->QuitClosure(), 120 kNumQuitDeferrals); 121 } 122 123 scoped_ptr<base::Value> ExecuteScriptAndGetValue( 124 RenderViewHost* render_view_host, 125 const std::string& script) { 126 ScriptCallback observer; 127 128 render_view_host->ExecuteJavascriptInWebFrameCallbackResult( 129 string16(), // frame_xpath, 130 UTF8ToUTF16(script), 131 base::Bind(&ScriptCallback::ResultCallback, base::Unretained(&observer))); 132 base::MessageLoop* loop = base::MessageLoop::current(); 133 loop->Run(); 134 return observer.result().Pass(); 135 } 136 137 MessageLoopRunner::MessageLoopRunner() 138 : loop_running_(false), 139 quit_closure_called_(false) { 140 } 141 142 MessageLoopRunner::~MessageLoopRunner() { 143 } 144 145 void MessageLoopRunner::Run() { 146 // Do not run the message loop if our quit closure has already been called. 147 // This helps in scenarios where the closure has a chance to run before 148 // we Run explicitly. 149 if (quit_closure_called_) 150 return; 151 152 loop_running_ = true; 153 RunThisRunLoop(&run_loop_); 154 } 155 156 base::Closure MessageLoopRunner::QuitClosure() { 157 return base::Bind(&MessageLoopRunner::Quit, this); 158 } 159 160 void MessageLoopRunner::Quit() { 161 quit_closure_called_ = true; 162 163 // Only run the quit task if we are running the message loop. 164 if (loop_running_) { 165 GetQuitTaskForRunLoop(&run_loop_).Run(); 166 loop_running_ = false; 167 } 168 } 169 170 WindowedNotificationObserver::WindowedNotificationObserver( 171 int notification_type, 172 const NotificationSource& source) 173 : seen_(false), 174 running_(false), 175 source_(NotificationService::AllSources()) { 176 registrar_.Add(this, notification_type, source); 177 } 178 179 WindowedNotificationObserver::WindowedNotificationObserver( 180 int notification_type, 181 const ConditionTestCallback& callback) 182 : seen_(false), 183 running_(false), 184 callback_(callback), 185 source_(NotificationService::AllSources()) { 186 registrar_.Add(this, notification_type, source_); 187 } 188 189 WindowedNotificationObserver::~WindowedNotificationObserver() {} 190 191 void WindowedNotificationObserver::Wait() { 192 if (seen_) 193 return; 194 195 running_ = true; 196 message_loop_runner_ = new MessageLoopRunner; 197 message_loop_runner_->Run(); 198 EXPECT_TRUE(seen_); 199 } 200 201 void WindowedNotificationObserver::Observe( 202 int type, 203 const NotificationSource& source, 204 const NotificationDetails& details) { 205 source_ = source; 206 details_ = details; 207 if (!callback_.is_null() && !callback_.Run()) 208 return; 209 210 seen_ = true; 211 if (!running_) 212 return; 213 214 message_loop_runner_->Quit(); 215 running_ = false; 216 } 217 218 } // namespace content 219