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/test/base/testing_browser_process.h"
18 #include "content/public/browser/resource_controller.h"
19 #include "content/public/browser/resource_request_info.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "ipc/ipc_message.h"
22 #include "net/base/request_priority.h"
23 #include "net/test/url_request/url_request_mock_http_job.h"
24 #include "net/url_request/redirect_info.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
;
30 using content::ResourceType
;
36 class TestPrerenderContents
: public PrerenderContents
{
38 TestPrerenderContents(PrerenderManager
* prerender_manager
,
39 int child_id
, int route_id
)
40 : PrerenderContents(prerender_manager
, static_cast<Profile
*>(NULL
),
41 GURL(), content::Referrer(), ORIGIN_NONE
,
42 PrerenderManager::kNoExperiment
),
45 PrerenderResourceThrottle::OverridePrerenderContentsForTesting(this);
48 ~TestPrerenderContents() override
{
49 if (final_status() == FINAL_STATUS_MAX
)
50 SetFinalStatus(FINAL_STATUS_USED
);
51 PrerenderResourceThrottle::OverridePrerenderContentsForTesting(NULL
);
54 bool GetChildId(int* child_id
) const override
{
55 *child_id
= child_id_
;
59 bool GetRouteId(int* route_id
) const override
{
60 *route_id
= route_id_
;
65 prerendering_has_started_
= true;
66 NotifyPrerenderStart();
70 Destroy(FINAL_STATUS_CANCELLED
);
82 class TestPrerenderManager
: public PrerenderManager
{
84 TestPrerenderManager() : PrerenderManager(nullptr) {
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 void MoveEntryToPendingDelete(PrerenderContents
* entry
,
91 FinalStatus final_status
) override
{}
94 class DeferredRedirectDelegate
: public net::URLRequest::Delegate
,
95 public content::ResourceController
{
97 DeferredRedirectDelegate()
100 cancel_called_(false),
101 resume_called_(false) {
104 void SetThrottle(PrerenderResourceThrottle
* throttle
) {
105 throttle_
= throttle
;
106 throttle_
->set_controller_for_testing(this);
110 run_loop_
.reset(new base::RunLoop());
114 bool was_deferred() const { return was_deferred_
; }
115 bool cancel_called() const { return cancel_called_
; }
116 bool resume_called() const { return resume_called_
; }
118 // net::URLRequest::Delegate implementation:
119 void OnReceivedRedirect(net::URLRequest
* request
,
120 const net::RedirectInfo
& redirect_info
,
121 bool* defer_redirect
) override
{
122 // Defer the redirect either way.
123 *defer_redirect
= true;
125 // Find out what the throttle would have done.
126 throttle_
->WillRedirectRequest(redirect_info
, &was_deferred_
);
129 void OnResponseStarted(net::URLRequest
* request
) override
{}
130 void OnReadCompleted(net::URLRequest
* request
, int bytes_read
) override
{}
132 // content::ResourceController implementation:
133 void Cancel() override
{
134 EXPECT_FALSE(cancel_called_
);
135 EXPECT_FALSE(resume_called_
);
137 cancel_called_
= true;
140 void CancelAndIgnore() override
{ Cancel(); }
141 void CancelWithError(int error_code
) override
{ Cancel(); }
142 void Resume() override
{
143 EXPECT_TRUE(was_deferred_
);
144 EXPECT_FALSE(cancel_called_
);
145 EXPECT_FALSE(resume_called_
);
147 resume_called_
= true;
152 scoped_ptr
<base::RunLoop
> run_loop_
;
153 PrerenderResourceThrottle
* throttle_
;
158 DISALLOW_COPY_AND_ASSIGN(DeferredRedirectDelegate
);
163 class PrerenderResourceThrottleTest
: public testing::Test
{
165 static const int kDefaultChildId
= 0;
166 static const int kDefaultRouteId
= 100;
168 PrerenderResourceThrottleTest() :
169 ui_thread_(BrowserThread::UI
, &message_loop_
),
170 io_thread_(BrowserThread::IO
, &message_loop_
),
171 test_contents_(&prerender_manager_
, kDefaultChildId
, kDefaultRouteId
) {
172 chrome_browser_net::SetUrlRequestMocksEnabled(true);
175 ~PrerenderResourceThrottleTest() override
{
176 chrome_browser_net::SetUrlRequestMocksEnabled(false);
178 // Cleanup work so the file IO tasks from URLRequestMockHTTPJob
180 content::BrowserThread::GetBlockingPool()->FlushForTesting();
184 TestPrerenderManager
* prerender_manager() {
185 return &prerender_manager_
;
188 TestPrerenderContents
* test_contents() {
189 return &test_contents_
;
192 // Runs any tasks queued on either thread.
194 message_loop_
.RunUntilIdle();
198 base::MessageLoopForIO message_loop_
;
199 content::TestBrowserThread ui_thread_
;
200 content::TestBrowserThread io_thread_
;
202 TestPrerenderManager prerender_manager_
;
203 TestPrerenderContents test_contents_
;
206 // Checks that deferred redirects are throttled and resumed correctly.
207 TEST_F(PrerenderResourceThrottleTest
, RedirectResume
) {
208 const base::FilePath::CharType kRedirectPath
[] =
209 FILE_PATH_LITERAL("prerender/image-deferred.png");
211 test_contents()->Start();
215 net::TestURLRequestContext url_request_context
;
216 DeferredRedirectDelegate delegate
;
217 scoped_ptr
<net::URLRequest
> request(url_request_context
.CreateRequest(
218 net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
219 net::DEFAULT_PRIORITY
,
221 content::ResourceRequestInfo::AllocateForTesting(
223 content::RESOURCE_TYPE_IMAGE
,
228 false, // is_main_frame
229 false, // parent_is_main_frame
230 true, // allow_download
233 // Install a prerender throttle.
234 PrerenderResourceThrottle
throttle(request
.get());
235 delegate
.SetThrottle(&throttle
);
237 // Start the request and wait for a redirect.
240 EXPECT_TRUE(delegate
.was_deferred());
241 // This calls WillRedirectRequestOnUI().
244 // Display the prerendered RenderView and wait for the throttle to
246 test_contents()->Use();
248 EXPECT_TRUE(delegate
.resume_called());
249 EXPECT_FALSE(delegate
.cancel_called());
252 // Checks that redirects in main frame loads are not deferred.
253 TEST_F(PrerenderResourceThrottleTest
, RedirectMainFrame
) {
254 const base::FilePath::CharType kRedirectPath
[] =
255 FILE_PATH_LITERAL("prerender/image-deferred.png");
257 test_contents()->Start();
261 net::TestURLRequestContext url_request_context
;
262 DeferredRedirectDelegate delegate
;
263 scoped_ptr
<net::URLRequest
> request(url_request_context
.CreateRequest(
264 net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
265 net::DEFAULT_PRIORITY
,
267 content::ResourceRequestInfo::AllocateForTesting(
269 content::RESOURCE_TYPE_MAIN_FRAME
,
274 true, // is_main_frame
275 false, // parent_is_main_frame
276 true, // allow_download
279 // Install a prerender throttle.
280 PrerenderResourceThrottle
throttle(request
.get());
281 delegate
.SetThrottle(&throttle
);
283 // Start the request and wait for a redirect. This time, it should
287 // This calls WillRedirectRequestOnUI().
290 // Cleanup work so the prerender is gone.
291 test_contents()->Cancel();
295 // Checks that attempting to defer a synchronous request aborts the
297 TEST_F(PrerenderResourceThrottleTest
, RedirectSyncXHR
) {
298 const base::FilePath::CharType kRedirectPath
[] =
299 FILE_PATH_LITERAL("prerender/image-deferred.png");
301 test_contents()->Start();
305 net::TestURLRequestContext url_request_context
;
306 DeferredRedirectDelegate delegate
;
307 scoped_ptr
<net::URLRequest
> request(url_request_context
.CreateRequest(
308 net::URLRequestMockHTTPJob::GetMockUrl(base::FilePath(kRedirectPath
)),
309 net::DEFAULT_PRIORITY
,
311 content::ResourceRequestInfo::AllocateForTesting(
313 content::RESOURCE_TYPE_XHR
,
318 false, // is_main_frame
319 false, // parent_is_main_frame
320 true, // allow_download
323 // Install a prerender throttle.
324 PrerenderResourceThrottle
throttle(request
.get());
325 delegate
.SetThrottle(&throttle
);
327 // Start the request and wait for a redirect.
330 // This calls WillRedirectRequestOnUI().
333 // We should have cancelled the prerender.
334 EXPECT_EQ(FINAL_STATUS_BAD_DEFERRED_REDIRECT
,
335 test_contents()->final_status());
337 // Cleanup work so the prerender is gone.
338 test_contents()->Cancel();
342 } // namespace prerender