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 prerendering_has_started_
= true;
65 NotifyPrerenderStart();
69 Destroy(FINAL_STATUS_CANCELLED
);
81 class TestPrerenderManager
: public PrerenderManager
{
83 explicit TestPrerenderManager(PrerenderTracker
* prerender_tracker
) :
84 PrerenderManager(NULL
, prerender_tracker
) {
85 mutable_config().rate_limit_enabled
= false;
88 // We never allocate our PrerenderContents in PrerenderManager, so we don't
89 // ever want the default pending delete behaviour.
90 virtual void MoveEntryToPendingDelete(PrerenderContents
* entry
,
91 FinalStatus final_status
) OVERRIDE
{
95 class DeferredRedirectDelegate
: public net::URLRequest::Delegate
,
96 public content::ResourceController
{
98 DeferredRedirectDelegate()
100 was_deferred_(false),
101 cancel_called_(false),
102 resume_called_(false) {
105 void SetThrottle(PrerenderResourceThrottle
* throttle
) {
106 throttle_
= throttle
;
107 throttle_
->set_controller_for_testing(this);
111 run_loop_
.reset(new base::RunLoop());
115 bool was_deferred() const { return was_deferred_
; }
116 bool cancel_called() const { return cancel_called_
; }
117 bool resume_called() const { return resume_called_
; }
119 // net::URLRequest::Delegate implementation:
120 virtual void OnReceivedRedirect(net::URLRequest
* request
,
122 bool* defer_redirect
) OVERRIDE
{
123 // Defer the redirect either way.
124 *defer_redirect
= true;
126 // Find out what the throttle would have done.
127 throttle_
->WillRedirectRequest(new_url
, &was_deferred_
);
130 virtual void OnResponseStarted(net::URLRequest
* request
) OVERRIDE
{ }
131 virtual void OnReadCompleted(net::URLRequest
* request
,
132 int bytes_read
) OVERRIDE
{
135 // content::ResourceController implementation:
136 virtual void Cancel() OVERRIDE
{
137 EXPECT_FALSE(cancel_called_
);
138 EXPECT_FALSE(resume_called_
);
140 cancel_called_
= true;
143 virtual void CancelAndIgnore() OVERRIDE
{ Cancel(); }
144 virtual void CancelWithError(int error_code
) OVERRIDE
{ Cancel(); }
145 virtual void Resume() OVERRIDE
{
146 EXPECT_TRUE(was_deferred_
);
147 EXPECT_FALSE(cancel_called_
);
148 EXPECT_FALSE(resume_called_
);
150 resume_called_
= true;
155 scoped_ptr
<base::RunLoop
> run_loop_
;
156 PrerenderResourceThrottle
* throttle_
;
161 DISALLOW_COPY_AND_ASSIGN(DeferredRedirectDelegate
);
166 class PrerenderTrackerTest
: public testing::Test
{
168 static const int kDefaultChildId
= 0;
169 static const int kDefaultRouteId
= 100;
171 PrerenderTrackerTest() :
172 ui_thread_(BrowserThread::UI
, &message_loop_
),
173 io_thread_(BrowserThread::IO
, &message_loop_
),
174 prerender_manager_(prerender_tracker()),
175 test_contents_(&prerender_manager_
, kDefaultChildId
, kDefaultRouteId
) {
176 chrome_browser_net::SetUrlRequestMocksEnabled(true);
179 virtual ~PrerenderTrackerTest() {
180 chrome_browser_net::SetUrlRequestMocksEnabled(false);
182 // Cleanup work so the file IO tasks from URLRequestMockHTTPJob
184 content::BrowserThread::GetBlockingPool()->FlushForTesting();
188 PrerenderTracker
* prerender_tracker() {
189 return g_browser_process
->prerender_tracker();
192 TestPrerenderManager
* prerender_manager() {
193 return &prerender_manager_
;
196 TestPrerenderContents
* test_contents() {
197 return &test_contents_
;
200 // Runs any tasks queued on either thread.
202 message_loop_
.RunUntilIdle();
206 base::MessageLoopForIO message_loop_
;
207 content::TestBrowserThread ui_thread_
;
208 content::TestBrowserThread io_thread_
;
210 TestPrerenderManager prerender_manager_
;
211 TestPrerenderContents test_contents_
;
214 // Checks that deferred redirects are throttled and resumed correctly.
215 TEST_F(PrerenderTrackerTest
, PrerenderThrottledRedirectResume
) {
216 const base::FilePath::CharType kRedirectPath
[] =
217 FILE_PATH_LITERAL("prerender/image-deferred.png");
219 test_contents()->Start();
223 net::TestURLRequestContext url_request_context
;
224 DeferredRedirectDelegate delegate
;
225 net::URLRequest
request(
226 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
227 net::DEFAULT_PRIORITY
,
229 &url_request_context
);
230 content::ResourceRequestInfo::AllocateForTesting(
231 &request
, ResourceType::IMAGE
, NULL
,
232 kDefaultChildId
, kDefaultRouteId
, MSG_ROUTING_NONE
, true);
234 // Install a prerender throttle.
235 PrerenderResourceThrottle
throttle(&request
);
236 delegate
.SetThrottle(&throttle
);
238 // Start the request and wait for a redirect.
241 EXPECT_TRUE(delegate
.was_deferred());
242 // This calls WillRedirectRequestOnUI().
245 // Display the prerendered RenderView and wait for the throttle to
247 test_contents()->Use();
249 EXPECT_TRUE(delegate
.resume_called());
250 EXPECT_FALSE(delegate
.cancel_called());
253 // Checks that redirects in main frame loads are not deferred.
254 TEST_F(PrerenderTrackerTest
, PrerenderThrottledRedirectMainFrame
) {
255 const base::FilePath::CharType kRedirectPath
[] =
256 FILE_PATH_LITERAL("prerender/image-deferred.png");
258 test_contents()->Start();
262 net::TestURLRequestContext url_request_context
;
263 DeferredRedirectDelegate delegate
;
264 net::URLRequest
request(
265 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
266 net::DEFAULT_PRIORITY
,
268 &url_request_context
);
269 content::ResourceRequestInfo::AllocateForTesting(
270 &request
, ResourceType::MAIN_FRAME
, NULL
,
271 kDefaultChildId
, kDefaultRouteId
, MSG_ROUTING_NONE
, true);
273 // Install a prerender throttle.
274 PrerenderResourceThrottle
throttle(&request
);
275 delegate
.SetThrottle(&throttle
);
277 // Start the request and wait for a redirect. This time, it should
281 // This calls WillRedirectRequestOnUI().
284 // Cleanup work so the prerender is gone.
285 test_contents()->Cancel();
289 // Checks that attempting to defer a synchronous request aborts the
291 TEST_F(PrerenderTrackerTest
, PrerenderThrottledRedirectSyncXHR
) {
292 const base::FilePath::CharType kRedirectPath
[] =
293 FILE_PATH_LITERAL("prerender/image-deferred.png");
295 test_contents()->Start();
299 net::TestURLRequestContext url_request_context
;
300 DeferredRedirectDelegate delegate
;
301 net::URLRequest
request(
302 content::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
303 net::DEFAULT_PRIORITY
,
305 &url_request_context
);
306 content::ResourceRequestInfo::AllocateForTesting(
307 &request
, ResourceType::XHR
, NULL
,
308 kDefaultChildId
, kDefaultRouteId
, MSG_ROUTING_NONE
, false);
310 // Install a prerender throttle.
311 PrerenderResourceThrottle
throttle(&request
);
312 delegate
.SetThrottle(&throttle
);
314 // Start the request and wait for a redirect.
317 // This calls WillRedirectRequestOnUI().
320 // We should have cancelled the prerender.
321 EXPECT_EQ(FINAL_STATUS_BAD_DEFERRED_REDIRECT
,
322 test_contents()->final_status());
324 // Cleanup work so the prerender is gone.
325 test_contents()->Cancel();
329 } // namespace prerender