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 <set> 6 #include <utility> 7 8 #include "base/files/file_path.h" 9 #include "base/logging.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/run_loop.h" 12 #include "base/threading/sequenced_worker_pool.h" 13 #include "chrome/browser/net/url_request_mock_util.h" 14 #include "chrome/browser/prerender/prerender_contents.h" 15 #include "chrome/browser/prerender/prerender_manager.h" 16 #include "chrome/browser/prerender/prerender_resource_throttle.h" 17 #include "chrome/browser/prerender/prerender_tracker.h" 18 #include "chrome/test/base/testing_browser_process.h" 19 #include "content/public/browser/resource_controller.h" 20 #include "content/public/browser/resource_request_info.h" 21 #include "content/public/test/test_browser_thread.h" 22 #include "ipc/ipc_message.h" 23 #include "net/base/request_priority.h" 24 #include "net/test/url_request/url_request_mock_http_job.h" 25 #include "net/url_request/redirect_info.h" 26 #include "net/url_request/url_request.h" 27 #include "net/url_request/url_request_test_util.h" 28 #include "testing/gtest/include/gtest/gtest.h" 29 30 using content::BrowserThread; 31 using content::ResourceType; 32 33 namespace prerender { 34 35 namespace { 36 37 class TestPrerenderContents : public PrerenderContents { 38 public: 39 TestPrerenderContents(PrerenderManager* prerender_manager, 40 int child_id, int route_id) 41 : PrerenderContents(prerender_manager, static_cast<Profile*>(NULL), 42 GURL(), content::Referrer(), ORIGIN_NONE, 43 PrerenderManager::kNoExperiment), 44 child_id_(child_id), 45 route_id_(route_id) { 46 PrerenderResourceThrottle::OverridePrerenderContentsForTesting(this); 47 } 48 49 virtual ~TestPrerenderContents() { 50 if (final_status() == FINAL_STATUS_MAX) 51 SetFinalStatus(FINAL_STATUS_USED); 52 PrerenderResourceThrottle::OverridePrerenderContentsForTesting(NULL); 53 } 54 55 virtual bool GetChildId(int* child_id) const OVERRIDE { 56 *child_id = child_id_; 57 return true; 58 } 59 60 virtual bool GetRouteId(int* route_id) const OVERRIDE { 61 *route_id = route_id_; 62 return true; 63 } 64 65 void Start() { 66 prerendering_has_started_ = true; 67 NotifyPrerenderStart(); 68 } 69 70 void Cancel() { 71 Destroy(FINAL_STATUS_CANCELLED); 72 } 73 74 void Use() { 75 PrepareForUse(); 76 } 77 78 private: 79 int child_id_; 80 int route_id_; 81 }; 82 83 class TestPrerenderManager : public PrerenderManager { 84 public: 85 explicit TestPrerenderManager(PrerenderTracker* prerender_tracker) : 86 PrerenderManager(NULL, prerender_tracker) { 87 mutable_config().rate_limit_enabled = false; 88 } 89 90 // We never allocate our PrerenderContents in PrerenderManager, so we don't 91 // ever want the default pending delete behaviour. 92 virtual void MoveEntryToPendingDelete(PrerenderContents* entry, 93 FinalStatus final_status) OVERRIDE { 94 } 95 }; 96 97 class DeferredRedirectDelegate : public net::URLRequest::Delegate, 98 public content::ResourceController { 99 public: 100 DeferredRedirectDelegate() 101 : throttle_(NULL), 102 was_deferred_(false), 103 cancel_called_(false), 104 resume_called_(false) { 105 } 106 107 void SetThrottle(PrerenderResourceThrottle* throttle) { 108 throttle_ = throttle; 109 throttle_->set_controller_for_testing(this); 110 } 111 112 void Run() { 113 run_loop_.reset(new base::RunLoop()); 114 run_loop_->Run(); 115 } 116 117 bool was_deferred() const { return was_deferred_; } 118 bool cancel_called() const { return cancel_called_; } 119 bool resume_called() const { return resume_called_; } 120 121 // net::URLRequest::Delegate implementation: 122 virtual void OnReceivedRedirect(net::URLRequest* request, 123 const net::RedirectInfo& redirect_info, 124 bool* defer_redirect) OVERRIDE { 125 // Defer the redirect either way. 126 *defer_redirect = true; 127 128 // Find out what the throttle would have done. 129 throttle_->WillRedirectRequest(redirect_info.new_url, &was_deferred_); 130 run_loop_->Quit(); 131 } 132 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE { } 133 virtual void OnReadCompleted(net::URLRequest* request, 134 int bytes_read) OVERRIDE { 135 } 136 137 // content::ResourceController implementation: 138 virtual void Cancel() OVERRIDE { 139 EXPECT_FALSE(cancel_called_); 140 EXPECT_FALSE(resume_called_); 141 142 cancel_called_ = true; 143 run_loop_->Quit(); 144 } 145 virtual void CancelAndIgnore() OVERRIDE { Cancel(); } 146 virtual void CancelWithError(int error_code) OVERRIDE { Cancel(); } 147 virtual void Resume() OVERRIDE { 148 EXPECT_TRUE(was_deferred_); 149 EXPECT_FALSE(cancel_called_); 150 EXPECT_FALSE(resume_called_); 151 152 resume_called_ = true; 153 run_loop_->Quit(); 154 } 155 156 private: 157 scoped_ptr<base::RunLoop> run_loop_; 158 PrerenderResourceThrottle* throttle_; 159 bool was_deferred_; 160 bool cancel_called_; 161 bool resume_called_; 162 163 DISALLOW_COPY_AND_ASSIGN(DeferredRedirectDelegate); 164 }; 165 166 } // namespace 167 168 class PrerenderTrackerTest : public testing::Test { 169 public: 170 static const int kDefaultChildId = 0; 171 static const int kDefaultRouteId = 100; 172 173 PrerenderTrackerTest() : 174 ui_thread_(BrowserThread::UI, &message_loop_), 175 io_thread_(BrowserThread::IO, &message_loop_), 176 prerender_manager_(prerender_tracker()), 177 test_contents_(&prerender_manager_, kDefaultChildId, kDefaultRouteId) { 178 chrome_browser_net::SetUrlRequestMocksEnabled(true); 179 } 180 181 virtual ~PrerenderTrackerTest() { 182 chrome_browser_net::SetUrlRequestMocksEnabled(false); 183 184 // Cleanup work so the file IO tasks from URLRequestMockHTTPJob 185 // are gone. 186 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 187 RunEvents(); 188 } 189 190 PrerenderTracker* prerender_tracker() { 191 return g_browser_process->prerender_tracker(); 192 } 193 194 TestPrerenderManager* prerender_manager() { 195 return &prerender_manager_; 196 } 197 198 TestPrerenderContents* test_contents() { 199 return &test_contents_; 200 } 201 202 // Runs any tasks queued on either thread. 203 void RunEvents() { 204 message_loop_.RunUntilIdle(); 205 } 206 207 private: 208 base::MessageLoopForIO message_loop_; 209 content::TestBrowserThread ui_thread_; 210 content::TestBrowserThread io_thread_; 211 212 TestPrerenderManager prerender_manager_; 213 TestPrerenderContents test_contents_; 214 }; 215 216 // Checks that deferred redirects are throttled and resumed correctly. 217 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectResume) { 218 const base::FilePath::CharType kRedirectPath[] = 219 FILE_PATH_LITERAL("prerender/image-deferred.png"); 220 221 test_contents()->Start(); 222 RunEvents(); 223 224 // Fake a request. 225 net::TestURLRequestContext url_request_context; 226 DeferredRedirectDelegate delegate; 227 scoped_ptr<net::URLRequest> request(url_request_context.CreateRequest( 228 net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)), 229 net::DEFAULT_PRIORITY, 230 &delegate, 231 NULL)); 232 content::ResourceRequestInfo::AllocateForTesting(request.get(), 233 content::RESOURCE_TYPE_IMAGE, 234 NULL, 235 kDefaultChildId, 236 kDefaultRouteId, 237 MSG_ROUTING_NONE, 238 true); 239 240 // Install a prerender throttle. 241 PrerenderResourceThrottle throttle(request.get()); 242 delegate.SetThrottle(&throttle); 243 244 // Start the request and wait for a redirect. 245 request->Start(); 246 delegate.Run(); 247 EXPECT_TRUE(delegate.was_deferred()); 248 // This calls WillRedirectRequestOnUI(). 249 RunEvents(); 250 251 // Display the prerendered RenderView and wait for the throttle to 252 // notice. 253 test_contents()->Use(); 254 delegate.Run(); 255 EXPECT_TRUE(delegate.resume_called()); 256 EXPECT_FALSE(delegate.cancel_called()); 257 } 258 259 // Checks that redirects in main frame loads are not deferred. 260 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectMainFrame) { 261 const base::FilePath::CharType kRedirectPath[] = 262 FILE_PATH_LITERAL("prerender/image-deferred.png"); 263 264 test_contents()->Start(); 265 RunEvents(); 266 267 // Fake a request. 268 net::TestURLRequestContext url_request_context; 269 DeferredRedirectDelegate delegate; 270 scoped_ptr<net::URLRequest> request(url_request_context.CreateRequest( 271 net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)), 272 net::DEFAULT_PRIORITY, 273 &delegate, 274 NULL)); 275 content::ResourceRequestInfo::AllocateForTesting( 276 request.get(), 277 content::RESOURCE_TYPE_MAIN_FRAME, 278 NULL, 279 kDefaultChildId, 280 kDefaultRouteId, 281 MSG_ROUTING_NONE, 282 true); 283 284 // Install a prerender throttle. 285 PrerenderResourceThrottle throttle(request.get()); 286 delegate.SetThrottle(&throttle); 287 288 // Start the request and wait for a redirect. This time, it should 289 // not be deferred. 290 request->Start(); 291 delegate.Run(); 292 // This calls WillRedirectRequestOnUI(). 293 RunEvents(); 294 295 // Cleanup work so the prerender is gone. 296 test_contents()->Cancel(); 297 RunEvents(); 298 } 299 300 // Checks that attempting to defer a synchronous request aborts the 301 // prerender. 302 TEST_F(PrerenderTrackerTest, PrerenderThrottledRedirectSyncXHR) { 303 const base::FilePath::CharType kRedirectPath[] = 304 FILE_PATH_LITERAL("prerender/image-deferred.png"); 305 306 test_contents()->Start(); 307 RunEvents(); 308 309 // Fake a request. 310 net::TestURLRequestContext url_request_context; 311 DeferredRedirectDelegate delegate; 312 scoped_ptr<net::URLRequest> request(url_request_context.CreateRequest( 313 net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath)), 314 net::DEFAULT_PRIORITY, 315 &delegate, 316 NULL)); 317 content::ResourceRequestInfo::AllocateForTesting(request.get(), 318 content::RESOURCE_TYPE_XHR, 319 NULL, 320 kDefaultChildId, 321 kDefaultRouteId, 322 MSG_ROUTING_NONE, 323 false); 324 325 // Install a prerender throttle. 326 PrerenderResourceThrottle throttle(request.get()); 327 delegate.SetThrottle(&throttle); 328 329 // Start the request and wait for a redirect. 330 request->Start(); 331 delegate.Run(); 332 // This calls WillRedirectRequestOnUI(). 333 RunEvents(); 334 335 // We should have cancelled the prerender. 336 EXPECT_EQ(FINAL_STATUS_BAD_DEFERRED_REDIRECT, 337 test_contents()->final_status()); 338 339 // Cleanup work so the prerender is gone. 340 test_contents()->Cancel(); 341 RunEvents(); 342 } 343 344 } // namespace prerender 345