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.
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 "content/test/net/url_request_mock_http_job.h"
23 #include "ipc/ipc_message.h"
24 #include "net/base/request_priority.h"
25 #include "net/url_request/url_request.h"
26 #include "net/url_request/url_request_test_util.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 using content::BrowserThread
;
35 class TestPrerenderContents
: public PrerenderContents
{
37 TestPrerenderContents(PrerenderManager
* prerender_manager
,
38 int child_id
, int route_id
)
39 : PrerenderContents(prerender_manager
, static_cast<Profile
*>(NULL
),
40 GURL(), content::Referrer(), ORIGIN_NONE
,
41 PrerenderManager::kNoExperiment
),
44 PrerenderResourceThrottle::OverridePrerenderContentsForTesting(this);
47 virtual ~TestPrerenderContents() {
48 if (final_status() == FINAL_STATUS_MAX
)
49 SetFinalStatus(FINAL_STATUS_USED
);
50 PrerenderResourceThrottle::OverridePrerenderContentsForTesting(NULL
);
53 virtual bool GetChildId(int* child_id
) const OVERRIDE
{
54 *child_id
= child_id_
;
58 virtual bool GetRouteId(int* route_id
) const OVERRIDE
{
59 *route_id
= route_id_
;
64 AddObserver(prerender_manager()->prerender_tracker());
65 prerendering_has_started_
= true;
66 NotifyPrerenderStart();
70 Destroy(FINAL_STATUS_CANCELLED
);
74 SetFinalStatus(FINAL_STATUS_USED
);
83 class TestPrerenderManager
: public PrerenderManager
{
85 explicit TestPrerenderManager(PrerenderTracker
* prerender_tracker
) :
86 PrerenderManager(NULL
, prerender_tracker
) {
87 mutable_config().rate_limit_enabled
= false;
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
{
97 class DeferredRedirectDelegate
: public net::URLRequest::Delegate
,
98 public content::ResourceController
{
100 DeferredRedirectDelegate()
102 was_deferred_(false),
103 cancel_called_(false),
104 resume_called_(false) {
107 void SetThrottle(PrerenderResourceThrottle
* throttle
) {
108 throttle_
= throttle
;
109 throttle_
->set_controller_for_testing(this);
113 run_loop_
.reset(new base::RunLoop());
117 bool was_deferred() const { return was_deferred_
; }
118 bool cancel_called() const { return cancel_called_
; }
119 bool resume_called() const { return resume_called_
; }
121 // net::URLRequest::Delegate implementation:
122 virtual void OnReceivedRedirect(net::URLRequest
* request
,
124 bool* defer_redirect
) OVERRIDE
{
125 // Defer the redirect either way.
126 *defer_redirect
= true;
128 // Find out what the throttle would have done.
129 throttle_
->WillRedirectRequest(new_url
, &was_deferred_
);
132 virtual void OnResponseStarted(net::URLRequest
* request
) OVERRIDE
{ }
133 virtual void OnReadCompleted(net::URLRequest
* request
,
134 int bytes_read
) OVERRIDE
{
137 // content::ResourceController implementation:
138 virtual void Cancel() OVERRIDE
{
139 EXPECT_FALSE(cancel_called_
);
140 EXPECT_FALSE(resume_called_
);
142 cancel_called_
= true;
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_
);
152 resume_called_
= true;
157 scoped_ptr
<base::RunLoop
> run_loop_
;
158 PrerenderResourceThrottle
* throttle_
;
163 DISALLOW_COPY_AND_ASSIGN(DeferredRedirectDelegate
);
168 class PrerenderTrackerTest
: public testing::Test
{
170 static const int kDefaultChildId
= 0;
171 static const int kDefaultRouteId
= 100;
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);
181 virtual ~PrerenderTrackerTest() {
182 chrome_browser_net::SetUrlRequestMocksEnabled(false);
184 // Cleanup work so the file IO tasks from URLRequestMockHTTPJob
186 content::BrowserThread::GetBlockingPool()->FlushForTesting();
190 PrerenderTracker
* prerender_tracker() {
191 return g_browser_process
->prerender_tracker();
194 TestPrerenderManager
* prerender_manager() {
195 return &prerender_manager_
;
198 TestPrerenderContents
* test_contents() {
199 return &test_contents_
;
202 // Runs any tasks queued on either thread.
204 message_loop_
.RunUntilIdle();
208 base::MessageLoopForIO message_loop_
;
209 content::TestBrowserThread ui_thread_
;
210 content::TestBrowserThread io_thread_
;
212 TestPrerenderManager prerender_manager_
;
213 TestPrerenderContents test_contents_
;
216 TEST_F(PrerenderTrackerTest
, IsPrerenderingOnIOThread
) {
217 EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(
218 kDefaultChildId
, kDefaultRouteId
));
220 test_contents()->Start();
221 // This calls AddPrerenderOnIOThreadTask().
223 EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(
224 kDefaultChildId
, kDefaultRouteId
));
226 test_contents()->Cancel();
227 // This calls RemovePrerenderOnIOThreadTask().
229 EXPECT_FALSE(prerender_tracker()->IsPrerenderingOnIOThread(
230 kDefaultChildId
, kDefaultRouteId
));
233 // Checks that deferred redirects are throttled and resumed correctly.
234 TEST_F(PrerenderTrackerTest
, PrerenderThrottledRedirectResume
) {
235 const base::FilePath::CharType kRedirectPath
[] =
236 FILE_PATH_LITERAL("prerender/image-deferred.png");
238 test_contents()->Start();
239 // This calls AddPrerenderOnIOThreadTask().
241 EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(
242 kDefaultChildId
, kDefaultRouteId
));
245 net::TestURLRequestContext url_request_context
;
246 DeferredRedirectDelegate delegate
;
247 net::URLRequest
request(
248 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
249 net::DEFAULT_PRIORITY
,
251 &url_request_context
);
252 content::ResourceRequestInfo::AllocateForTesting(
253 &request
, ResourceType::IMAGE
, NULL
,
254 kDefaultChildId
, kDefaultRouteId
, MSG_ROUTING_NONE
, true);
256 // Install a prerender throttle.
257 PrerenderResourceThrottle
throttle(&request
, prerender_tracker());
258 delegate
.SetThrottle(&throttle
);
260 // Start the request and wait for a redirect.
263 EXPECT_TRUE(delegate
.was_deferred());
264 // This calls WillRedirectRequestOnUI().
267 // Display the prerendered RenderView and wait for the throttle to
269 test_contents()->Use();
271 EXPECT_TRUE(delegate
.resume_called());
272 EXPECT_FALSE(delegate
.cancel_called());
275 // Checks that deferred redirects are cancelled on prerender cancel.
276 TEST_F(PrerenderTrackerTest
, PrerenderThrottledRedirectCancel
) {
277 const base::FilePath::CharType kRedirectPath
[] =
278 FILE_PATH_LITERAL("prerender/image-deferred.png");
280 test_contents()->Start();
281 // This calls AddPrerenderOnIOThreadTask().
283 EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(
284 kDefaultChildId
, kDefaultRouteId
));
287 net::TestURLRequestContext url_request_context
;
288 DeferredRedirectDelegate delegate
;
289 net::URLRequest
request(
290 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
291 net::DEFAULT_PRIORITY
,
293 &url_request_context
);
294 content::ResourceRequestInfo::AllocateForTesting(
295 &request
, ResourceType::IMAGE
, NULL
,
296 kDefaultChildId
, kDefaultRouteId
, MSG_ROUTING_NONE
, true);
298 // Install a prerender throttle.
299 PrerenderResourceThrottle
throttle(&request
, prerender_tracker());
300 delegate
.SetThrottle(&throttle
);
302 // Start the request and wait for a redirect.
305 EXPECT_TRUE(delegate
.was_deferred());
306 // This calls WillRedirectRequestOnUI().
309 // Display the prerendered RenderView and wait for the throttle to
311 test_contents()->Cancel();
313 EXPECT_FALSE(delegate
.resume_called());
314 EXPECT_TRUE(delegate
.cancel_called());
317 // Checks that redirects in main frame loads are not deferred.
318 TEST_F(PrerenderTrackerTest
, PrerenderThrottledRedirectMainFrame
) {
319 const base::FilePath::CharType kRedirectPath
[] =
320 FILE_PATH_LITERAL("prerender/image-deferred.png");
322 test_contents()->Start();
323 // This calls AddPrerenderOnIOThreadTask().
325 EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(
326 kDefaultChildId
, kDefaultRouteId
));
329 net::TestURLRequestContext url_request_context
;
330 DeferredRedirectDelegate delegate
;
331 net::URLRequest
request(
332 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
333 net::DEFAULT_PRIORITY
,
335 &url_request_context
);
336 content::ResourceRequestInfo::AllocateForTesting(
337 &request
, ResourceType::MAIN_FRAME
, NULL
,
338 kDefaultChildId
, kDefaultRouteId
, MSG_ROUTING_NONE
, true);
340 // Install a prerender throttle.
341 PrerenderResourceThrottle
throttle(&request
, prerender_tracker());
342 delegate
.SetThrottle(&throttle
);
344 // Start the request and wait for a redirect. This time, it should
348 // This calls WillRedirectRequestOnUI().
351 // Cleanup work so the prerender is gone.
352 test_contents()->Cancel();
356 // Checks that attempting to defer a synchronous request aborts the
358 TEST_F(PrerenderTrackerTest
, PrerenderThrottledRedirectSyncXHR
) {
359 const base::FilePath::CharType kRedirectPath
[] =
360 FILE_PATH_LITERAL("prerender/image-deferred.png");
362 test_contents()->Start();
363 // This calls AddPrerenderOnIOThreadTask().
365 EXPECT_TRUE(prerender_tracker()->IsPrerenderingOnIOThread(
366 kDefaultChildId
, kDefaultRouteId
));
369 net::TestURLRequestContext url_request_context
;
370 DeferredRedirectDelegate delegate
;
371 net::URLRequest
request(
372 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
373 net::DEFAULT_PRIORITY
,
375 &url_request_context
);
376 content::ResourceRequestInfo::AllocateForTesting(
377 &request
, ResourceType::XHR
, NULL
,
378 kDefaultChildId
, kDefaultRouteId
, MSG_ROUTING_NONE
, false);
380 // Install a prerender throttle.
381 PrerenderResourceThrottle
throttle(&request
, prerender_tracker());
382 delegate
.SetThrottle(&throttle
);
384 // Start the request and wait for a redirect.
387 // This calls WillRedirectRequestOnUI().
390 // We should have cancelled the prerender.
391 EXPECT_EQ(FINAL_STATUS_BAD_DEFERRED_REDIRECT
,
392 test_contents()->final_status());
394 // Cleanup work so the prerender is gone.
395 test_contents()->Cancel();
399 } // namespace prerender