diff options
author | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-12 13:12:21 +0000 |
---|---|---|
committer | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-12 13:12:21 +0000 |
commit | 0e25b15e6bf51ced4c0cc3d2c14122b38debc815 (patch) | |
tree | d64bde3c0e1043135dd872c936f137581f6fd3b7 | |
parent | 702f9978ada5dcf21c2720219c7de067b81950a6 (diff) | |
download | chromium_src-0e25b15e6bf51ced4c0cc3d2c14122b38debc815.zip chromium_src-0e25b15e6bf51ced4c0cc3d2c14122b38debc815.tar.gz chromium_src-0e25b15e6bf51ced4c0cc3d2c14122b38debc815.tar.bz2 |
Add PrerenderResourceHandler and hook it into the ResourceDispatcherHost.
The PrerenderResourceHandler will initiate prerendering of a web page under the following conditions:
- The initial request is a GET for a ResourceType::PREFETCH resource.
- The mime-type (sniffed or explicitly specified) of the resource is text/html.
- The response status code is a 200.
- The top-level page will not need to be revalidated until after it's guaranteed to be removed from potential use.
The handler passes along all data to the backing resource handler.
BUG=61745
TEST=unit_tests --gtest_filter="PrerenderResourceHandlerTest*"
Manual Test: Start Chrome with --enable-page-prerender. Go to a page with a <link rel=prefetch> element. Navigate to that element, and make sure that it uses the prerendered view.
Review URL: http://codereview.chromium.org/5912001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71162 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/net/chrome_url_request_context.cc | 2 | ||||
-rw-r--r-- | chrome/browser/net/chrome_url_request_context.h | 12 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.h | 15 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager_unittest.cc | 7 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_resource_handler.cc | 179 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_resource_handler.h | 99 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_resource_handler_unittest.cc | 214 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_impl.cc | 6 | ||||
-rw-r--r-- | chrome/browser/profiles/profile_impl.h | 2 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.cc | 10 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 |
12 files changed, 536 insertions, 13 deletions
diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc index d06d806..c5c7edb 100644 --- a/chrome/browser/net/chrome_url_request_context.cc +++ b/chrome/browser/net/chrome_url_request_context.cc @@ -885,6 +885,7 @@ ChromeURLRequestContextFactory::ChromeURLRequestContextFactory(Profile* profile) blob_storage_context_ = profile->GetBlobStorageContext(); file_system_context_ = profile->GetFileSystemContext(); extension_info_map_ = profile->GetExtensionInfoMap(); + prerender_manager_ = profile->GetPrerenderManager(); } ChromeURLRequestContextFactory::~ChromeURLRequestContextFactory() { @@ -911,4 +912,5 @@ void ChromeURLRequestContextFactory::ApplyProfileParametersToContext( context->set_blob_storage_context(blob_storage_context_); context->set_file_system_context(file_system_context_); context->set_extension_info_map(extension_info_map_); + context->set_prerender_manager(prerender_manager_); } diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h index 93f0e37..7d0c66b 100644 --- a/chrome/browser/net/chrome_url_request_context.h +++ b/chrome/browser/net/chrome_url_request_context.h @@ -19,6 +19,7 @@ #include "chrome/browser/net/chrome_cookie_policy.h" #include "chrome/browser/prefs/pref_change_registrar.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prerender/prerender_manager.h" #include "chrome/common/extensions/extension_icon_set.h" #include "chrome/common/net/url_request_context_getter.h" #include "chrome/common/notification_registrar.h" @@ -94,6 +95,10 @@ class ChromeURLRequestContext : public net::URLRequestContext { return extension_info_map_; } + PrerenderManager* prerender_manager() { + return prerender_manager_.get(); + } + // Returns true if this context is an external request context, like // ChromeFrame. virtual bool IsExternal() const; @@ -165,6 +170,9 @@ class ChromeURLRequestContext : public net::URLRequestContext { net::HttpNetworkDelegate* network_delegate) { network_delegate_ = network_delegate; } + void set_prerender_manager(PrerenderManager* prerender_manager) { + prerender_manager_ = prerender_manager; + } // Callback for when the accept language changes. void OnAcceptLanguageChange(const std::string& accept_language); @@ -176,6 +184,7 @@ class ChromeURLRequestContext : public net::URLRequestContext { // Path to the directory user scripts are stored in. FilePath user_script_dir_path_; + // TODO(willchan): Make these non-refcounted. scoped_refptr<ChromeAppCacheService> appcache_service_; scoped_refptr<webkit_database::DatabaseTracker> database_tracker_; scoped_refptr<ChromeCookiePolicy> chrome_cookie_policy_; @@ -184,6 +193,7 @@ class ChromeURLRequestContext : public net::URLRequestContext { scoped_refptr<ChromeBlobStorageContext> blob_storage_context_; scoped_refptr<fileapi::SandboxedFileSystemContext> file_system_context_; scoped_refptr<ExtensionInfoMap> extension_info_map_; + scoped_refptr<PrerenderManager> prerender_manager_; bool is_media_; bool is_off_the_record_; @@ -342,6 +352,7 @@ class ChromeURLRequestContextFactory { // TODO(aa): I think this can go away now as we no longer support standalone // user scripts. + // TODO(willchan): Make these non-refcounted. FilePath user_script_dir_path_; scoped_refptr<HostContentSettingsMap> host_content_settings_map_; scoped_refptr<ChromeAppCacheService> appcache_service_; @@ -353,6 +364,7 @@ class ChromeURLRequestContextFactory { scoped_refptr<ChromeBlobStorageContext> blob_storage_context_; scoped_refptr<fileapi::SandboxedFileSystemContext> file_system_context_; scoped_refptr<ExtensionInfoMap> extension_info_map_; + scoped_refptr<PrerenderManager> prerender_manager_; FilePath profile_dir_path_; diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h index 2c49e68..0eff09a 100644 --- a/chrome/browser/prerender/prerender_manager.h +++ b/chrome/browser/prerender/prerender_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,7 @@ #include <list> +#include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/time.h" @@ -19,11 +20,11 @@ class TabContents; // PrerenderManager is responsible for initiating and keeping prerendered // views of webpages. -class PrerenderManager : base::NonThreadSafe { +class PrerenderManager : public base::RefCounted<PrerenderManager>, + private base::NonThreadSafe { public: // Owned by a Profile object for the lifetime of the profile. explicit PrerenderManager(Profile* profile); - virtual ~PrerenderManager(); // Preloads the URL supplied. void AddPreload(const GURL& url); @@ -50,16 +51,16 @@ class PrerenderManager : base::NonThreadSafe { void set_max_elements(unsigned int num) { max_elements_ = num; } protected: - // The following methods exist explicitly rather than just inlined to - // facilitate testing. - virtual base::Time GetCurrentTime() const; - virtual PrerenderContents* CreatePrerenderContents(const GURL& url); + virtual ~PrerenderManager(); private: + friend class base::RefCounted<PrerenderManager>; struct PrerenderContentsData; bool IsPrerenderElementFresh(const base::Time start) const; void DeleteOldEntries(); + virtual base::Time GetCurrentTime() const; + virtual PrerenderContents* CreatePrerenderContents(const GURL& url); Profile* profile_; diff --git a/chrome/browser/prerender/prerender_manager_unittest.cc b/chrome/browser/prerender/prerender_manager_unittest.cc index b5cb65b..e5cb293 100644 --- a/chrome/browser/prerender/prerender_manager_unittest.cc +++ b/chrome/browser/prerender/prerender_manager_unittest.cc @@ -46,6 +46,10 @@ class TestPrerenderManager : public PrerenderManager { PrerenderContents* next_pc() { return next_pc_.get(); } protected: + virtual ~TestPrerenderManager() { + } + + private: virtual base::Time GetCurrentTime() const { return time_; } @@ -54,7 +58,6 @@ class TestPrerenderManager : public PrerenderManager { return next_pc_.release(); } - private: base::Time time_; scoped_ptr<PrerenderContents> next_pc_; }; @@ -67,7 +70,7 @@ class PrerenderManagerTest : public testing::Test { } protected: - scoped_ptr<TestPrerenderManager> prerender_manager_; + scoped_refptr<TestPrerenderManager> prerender_manager_; }; TEST_F(PrerenderManagerTest, EmptyTest) { diff --git a/chrome/browser/prerender/prerender_resource_handler.cc b/chrome/browser/prerender/prerender_resource_handler.cc new file mode 100644 index 0000000..507782e --- /dev/null +++ b/chrome/browser/prerender/prerender_resource_handler.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/prerender/prerender_resource_handler.h" + +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/common/resource_response.h" +#include "net/base/load_flags.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request.h" + +namespace { + +base::Time DefaultGetCurrentTime() { + return base::Time::Now(); +} + +bool ShouldPrerender(const GURL& url, + const ResourceResponse* response, + PrerenderResourceHandler::GetCurrentTimeFunction get_time, + base::TimeDelta prerender_duration) { + if (!response) + return false; + const ResourceResponseHead& rrh = response->response_head; + if (!url.is_valid()) + return false; + if (!rrh.headers) + return false; + if (!(url.SchemeIs("http") || url.SchemeIs("https"))) + return false; + if (rrh.mime_type != "text/html") + return false; + if (rrh.headers->response_code() != 200) + return false; + if (rrh.headers->RequiresValidation( + rrh.request_time, + rrh.response_time, + get_time() + prerender_duration)) + return false; + return true; +} + +} // namespace + +PrerenderResourceHandler* PrerenderResourceHandler::MaybeCreate( + const net::URLRequest& request, + ChromeURLRequestContext* context, + ResourceHandler* next_handler) { + if (!context || !context->prerender_manager()) + return NULL; + if (!(request.load_flags() & net::LOAD_PREFETCH)) + return NULL; + if (request.method() != "GET") + return NULL; + return new PrerenderResourceHandler(next_handler, + context->prerender_manager()); +} + +PrerenderResourceHandler::PrerenderResourceHandler( + ResourceHandler* next_handler, + PrerenderManager* prerender_manager) + : next_handler_(next_handler), + prerender_manager_(prerender_manager), + ALLOW_THIS_IN_INITIALIZER_LIST( + prerender_callback_(NewCallback( + this, &PrerenderResourceHandler::StartPrerender))), + prerender_duration_( + base::TimeDelta::FromSeconds(kDefaultPrerenderDurationSeconds)), + get_current_time_(&DefaultGetCurrentTime) { + DCHECK(next_handler); + DCHECK(prerender_manager); +} + +// This constructor is only used from unit tests. +PrerenderResourceHandler::PrerenderResourceHandler( + ResourceHandler* next_handler, + PrerenderCallback* callback) + : next_handler_(next_handler), + prerender_callback_(callback), + prerender_duration_( + base::TimeDelta::FromSeconds(kDefaultPrerenderDurationSeconds)), + get_current_time_(&DefaultGetCurrentTime) { + DCHECK(next_handler); + DCHECK(callback); +} + +PrerenderResourceHandler::~PrerenderResourceHandler() { +} + +bool PrerenderResourceHandler::OnUploadProgress(int request_id, + uint64 position, + uint64 size) { + return next_handler_->OnUploadProgress(request_id, position, size); +} + +bool PrerenderResourceHandler::OnRequestRedirected(int request_id, + const GURL& url, + ResourceResponse* response, + bool* defer) { + bool will_redirect = next_handler_->OnRequestRedirected( + request_id, url, response, defer); + if (will_redirect) + url_ = url; + return will_redirect; +} + +bool PrerenderResourceHandler::OnResponseStarted(int request_id, + ResourceResponse* response) { + if (ShouldPrerender(url_, + response, + get_current_time_, + prerender_duration_)) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + NewRunnableMethod( + this, + &PrerenderResourceHandler::RunCallbackFromUIThread, + url_)); + } + return next_handler_->OnResponseStarted(request_id, response); +} + +bool PrerenderResourceHandler::OnWillStart(int request_id, + const GURL& url, + bool* defer) { + bool will_start = next_handler_->OnWillStart(request_id, url, defer); + if (will_start) + url_ = url; + return will_start; +} + +bool PrerenderResourceHandler::OnWillRead(int request_id, + net::IOBuffer** buf, + int* buf_size, + int min_size) { + return next_handler_->OnWillRead(request_id, buf, buf_size, min_size); +} + +bool PrerenderResourceHandler::OnReadCompleted(int request_id, + int* bytes_read) { + return next_handler_->OnReadCompleted(request_id, bytes_read); +} + +bool PrerenderResourceHandler::OnResponseCompleted( + int request_id, + const net::URLRequestStatus& status, + const std::string& security_info) { + return next_handler_->OnResponseCompleted(request_id, status, security_info); +} + +void PrerenderResourceHandler::OnRequestClosed() { + next_handler_->OnRequestClosed(); +} + +void PrerenderResourceHandler::RunCallbackFromUIThread(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + prerender_callback_->Run(url); +} + +void PrerenderResourceHandler::StartPrerender(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + prerender_manager_->AddPreload(url); +} + +void PrerenderResourceHandler::set_prerender_duration(base::TimeDelta dt) { + prerender_duration_ = dt; +} + +void PrerenderResourceHandler::set_get_current_time_function( + GetCurrentTimeFunction get_current_time) { + DCHECK(get_current_time); + get_current_time_ = get_current_time; +} + +// Note: this should stay in line with prerendermanager +// static +const int PrerenderResourceHandler::kDefaultPrerenderDurationSeconds = 20; diff --git a/chrome/browser/prerender/prerender_resource_handler.h b/chrome/browser/prerender/prerender_resource_handler.h new file mode 100644 index 0000000..db825e7 --- /dev/null +++ b/chrome/browser/prerender/prerender_resource_handler.h @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PRERENDER_PRERENDER_RESOURCE_HANDLER_H_ +#define CHROME_BROWSER_PRERENDER_PRERENDER_RESOURCE_HANDLER_H_ +#pragma once + +#include <string> + +#include "base/callback.h" +#include "chrome/browser/prerender/prerender_manager.h" +#include "chrome/browser/renderer_host/resource_handler.h" + +class ChromeURLRequestContext; +namespace net { +class URLRequest; +} + +// The PrerenderResourceHandler initiates prerendering of web pages +// under the following conditions: +// - The profile which initiated the request allows prerendering. +// - The initial request is a GET for a PREFETCH resource type. +// - The final URL (after redirects) has a scheme of http or https. +// - The response status code is a 200. +// - The MIME type of the response (sniffed or explicit) is text/html. +// - The resource will not need to revalidate within 30 seconds. This prevents +// no-cache or very quickly refreshing resources from being prerendered. +class PrerenderResourceHandler : public ResourceHandler { + public: + typedef base::Time (*GetCurrentTimeFunction)(); + + // Creates a new PrerenderResourceHandler if appropriate for the + // given |request| and |context|, otherwise NULL is returned. The + // caller is resposible for deleting the returned handler. + // + // |next_handler| is the backup handler that this handler delegates to + // for the majority of the commands, and must be non-NULL. + static PrerenderResourceHandler* MaybeCreate( + const net::URLRequest& request, + ChromeURLRequestContext* context, + ResourceHandler* next_handler); + + // OnResponseStarted will ask the |prerender_manager_| to start + // prerendering the requested resource if it is of an appropriate + // content type. The next handler is still invoked. + virtual bool OnResponseStarted(int request_id, + ResourceResponse* response); + + // The following methods simply delegate to the next_handler. + virtual bool OnUploadProgress(int request_id, + uint64 position, + uint64 size); + virtual bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* response, + bool* defer); + virtual bool OnWillStart(int request_id, const GURL& url, bool* defer); + + virtual bool OnWillRead(int request_id, + net::IOBuffer** buf, + int* buf_size, + int min_size); + + virtual bool OnReadCompleted(int request_id, int* bytes_read); + + virtual bool OnResponseCompleted(int request_id, + const net::URLRequestStatus& status, + const std::string& security_info); + + virtual void OnRequestClosed(); + + private: + friend class PrerenderResourceHandlerTest; + typedef Callback1<const GURL&>::Type PrerenderCallback; + + static const int kDefaultPrerenderDurationSeconds; + + PrerenderResourceHandler(ResourceHandler* next_handler, + PrerenderManager* prerender_manager); + PrerenderResourceHandler(ResourceHandler* next_handler, + PrerenderCallback* callback); + virtual ~PrerenderResourceHandler(); + + void RunCallbackFromUIThread(const GURL& url); + void StartPrerender(const GURL& url); + void set_prerender_duration(base::TimeDelta prerender_duration); + void set_get_current_time_function(GetCurrentTimeFunction get_current_time); + + GURL url_; + scoped_refptr<ResourceHandler> next_handler_; + scoped_refptr<PrerenderManager> prerender_manager_; + scoped_ptr<PrerenderCallback> prerender_callback_; + base::TimeDelta prerender_duration_; + GetCurrentTimeFunction get_current_time_; + + DISALLOW_COPY_AND_ASSIGN(PrerenderResourceHandler); +}; + +#endif // CHROME_BROWSER_PRERENDER_PRERENDER_RESOURCE_HANDLER_H_ diff --git a/chrome/browser/prerender/prerender_resource_handler_unittest.cc b/chrome/browser/prerender/prerender_resource_handler_unittest.cc new file mode 100644 index 0000000..b2ba2e0 --- /dev/null +++ b/chrome/browser/prerender/prerender_resource_handler_unittest.cc @@ -0,0 +1,214 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/prerender/prerender_resource_handler.h" +#include "chrome/common/resource_response.h" +#include "net/http/http_response_headers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class MockResourceHandler : public ResourceHandler { + public: + MockResourceHandler() {} + + virtual bool OnUploadProgress(int request_id, + uint64 position, + uint64 size) { + return true; + } + + virtual bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* response, + bool* defer) { + *defer = false; + return true; + } + + virtual bool OnResponseStarted(int request_id, + ResourceResponse* response) { + return true; + } + + virtual bool OnWillStart(int request_id, const GURL& url, bool* defer) { + *defer = false; + return true; + } + + virtual bool OnWillRead(int request_id, + net::IOBuffer** buf, + int* buf_size, + int min_size) { + return true; + } + + virtual bool OnReadCompleted(int request_id, int* bytes_read) { + return true; + } + + virtual bool OnResponseCompleted(int request_id, + const URLRequestStatus& status, + const std::string& security_info) { + return true; + } + + virtual void OnRequestClosed() { + } + + virtual void OnDataDownloaded(int request_id, int bytes_downloaded) {} +}; + +base::Time FixedGetCurrentTime() { + return base::Time(); +} + +// HttpResponseHeaders expects the raw input for it's constructor +// to be a NUL ('\0') separated string for each line. This is a little +// difficult to do for string literals, so this helper function accepts +// newline-separated string literals and does the substitution. The +// returned object is expected to be deleted by the caller. +net::HttpResponseHeaders* CreateResponseHeaders( + const char* newline_separated_headers) { + std::string headers(newline_separated_headers); + std::string::iterator i = headers.begin(); + std::string::iterator end = headers.end(); + while (i != end) { + if (*i == '\n') + *i = '\0'; + ++i; + } + return new net::HttpResponseHeaders(headers); +} + +} // namespace + +class PrerenderResourceHandlerTest : public testing::Test { + protected: + PrerenderResourceHandlerTest() + : prerender_duration_(base::TimeDelta::FromSeconds(10)), + mock_handler_(new MockResourceHandler()), + ALLOW_THIS_IN_INITIALIZER_LIST( + pre_handler_(new PrerenderResourceHandler( + mock_handler_, + NewCallback( + this, + &PrerenderResourceHandlerTest::SetLastHandledURL)))), + ui_thread_(BrowserThread::UI, &loop_), + default_url_("http://www.prerender.com") { + pre_handler_->set_prerender_duration(prerender_duration_); + pre_handler_->set_get_current_time_function(&FixedGetCurrentTime); + } + + void SetLastHandledURL(const GURL& url) { + last_handled_url_ = url; + } + + // Common logic shared by many of the tests + void StartPrerendering(const std::string& mime_type, + const char* headers) { + int request_id = 1; + bool defer = false; + EXPECT_TRUE(pre_handler_->OnWillStart(request_id, default_url_, &defer)); + EXPECT_FALSE(defer); + scoped_refptr<ResourceResponse> response(new ResourceResponse); + response->response_head.request_time = FixedGetCurrentTime(); + response->response_head.response_time = FixedGetCurrentTime(); + response->response_head.mime_type = mime_type; + response->response_head.headers = CreateResponseHeaders(headers); + EXPECT_TRUE(last_handled_url_.is_empty()); + + // Start the response. If it is able to prerender, a task will + // be posted to loop_ (masquerading as the UI thread), and + // |SetLastHandledURL| will be called. + EXPECT_TRUE(pre_handler_->OnResponseStarted(request_id, response)); + loop_.RunAllPending(); + } + + base::TimeDelta prerender_duration_; + scoped_refptr<MockResourceHandler> mock_handler_; + scoped_refptr<PrerenderResourceHandler> pre_handler_; + MessageLoop loop_; + BrowserThread ui_thread_; + GURL last_handled_url_; + GURL default_url_; +}; + +namespace { + +TEST_F(PrerenderResourceHandlerTest, NoOp) { +} + +// Tests that a valid HTML resource will correctly get diverted +// to the PrerenderManager. +TEST_F(PrerenderResourceHandlerTest, Prerender) { + StartPrerendering("text/html", + "HTTP/1.1 200 OK\n" + "cache-control: max-age=86400\n"); + EXPECT_EQ(default_url_, last_handled_url_); +} + +// Tests that a no-cache HTML resource will not get diverted +// to the PrerenderManager. +TEST_F(PrerenderResourceHandlerTest, PrerenderNoCache) { + StartPrerendering("text/html", + "HTTP/1.1 200 OK\n" + "cache-control: no-cache\n"); + EXPECT_TRUE(last_handled_url_.is_empty()); +} + +// Tests that a cacheable HTML resource which needs to be revalidated +// shortly will not be prerendered. +TEST_F(PrerenderResourceHandlerTest, PrerenderShortMaxAge) { + StartPrerendering("text/html", + "HTTP/1.1 200 OK\n" + "cache-control: max-age=5\n"); + EXPECT_TRUE(last_handled_url_.is_empty()); +} + +// Tests that a resource with the wrong MIME type (a GIF in this example) +// will not be diverted to the PrerenderManager. +TEST_F(PrerenderResourceHandlerTest, PrerenderWrongMimeType) { + StartPrerendering("image/gif", + "HTTP/1.1 200 OK\n" + "cache-control: max-age=86400\n"); + EXPECT_TRUE(last_handled_url_.is_empty()); +} + +// Tests that a resource with a non-200 response will not be diverted +// to the PrerenderManager +TEST_F(PrerenderResourceHandlerTest, PrerenderBadResponseCode) { + StartPrerendering("text/html", + "HTTP/1.1 403 Forbidden\n" + "cache-control: max-age=86400\n"); + EXPECT_TRUE(last_handled_url_.is_empty()); +} + +// Tests that the final request in a redirect chain will +// get diverted to the PrerenderManager. +TEST_F(PrerenderResourceHandlerTest, PrerenderRedirect) { + int request_id = 1; + GURL url_redirect("http://www.redirect.com"); + bool defer = false; + EXPECT_TRUE(pre_handler_->OnWillStart(request_id, default_url_, &defer)); + EXPECT_FALSE(defer); + EXPECT_TRUE(pre_handler_->OnRequestRedirected(request_id, + url_redirect, + NULL, + &defer)); + EXPECT_FALSE(defer); + scoped_refptr<ResourceResponse> response(new ResourceResponse); + response->response_head.mime_type = "text/html"; + response->response_head.request_time = FixedGetCurrentTime(); + response->response_head.response_time = FixedGetCurrentTime(); + response->response_head.headers = CreateResponseHeaders( + "HTTP/1.1 200 OK\n" + "cache-control: max-age=86400\n"); + EXPECT_TRUE(pre_handler_->OnResponseStarted(request_id, response)); + EXPECT_TRUE(last_handled_url_.is_empty()); + loop_.RunAllPending(); + EXPECT_EQ(url_redirect, last_handled_url_); +} + +} + diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 0f2bf3c..d4f24d8 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -1367,7 +1367,7 @@ PrerenderManager* ProfileImpl::GetPrerenderManager() { CommandLine* cl = CommandLine::ForCurrentProcess(); if (!cl->HasSwitch(switches::kEnablePagePrerender)) return NULL; - if (!prerender_manager_.get()) - prerender_manager_.reset(new PrerenderManager(this)); - return prerender_manager_.get(); + if (!prerender_manager_) + prerender_manager_ = new PrerenderManager(this); + return prerender_manager_; } diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index ce29d28..bc5f790 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h @@ -298,7 +298,7 @@ class ProfileImpl : public Profile, scoped_refptr<PrefProxyConfigTracker> pref_proxy_config_tracker_; - scoped_ptr<PrerenderManager> prerender_manager_; + scoped_refptr<PrerenderManager> prerender_manager_; DISALLOW_COPY_AND_ASSIGN(ProfileImpl); }; diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index 14489de..74fcc51 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -31,6 +31,7 @@ #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/net/url_request_tracking.h" #include "chrome/browser/plugin_service.h" +#include "chrome/browser/prerender/prerender_resource_handler.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_host/async_resource_handler.h" #include "chrome/browser/renderer_host/buffered_resource_handler.h" @@ -464,6 +465,15 @@ void ResourceDispatcherHost::BeginRequest( upload_size = request_data.upload_data->GetContentLength(); } + // Install a PrerenderResourceHandler if the requested URL could + // be prerendered. This should be in front of the [a]syncResourceHandler, + // but after the BufferedResourceHandler since it depends on the MIME + // sniffing capabilities in the BufferedResourceHandler. + PrerenderResourceHandler* pre_handler = PrerenderResourceHandler::MaybeCreate( + *request, context, handler); + if (pre_handler) + handler = pre_handler; + // Install a CrossSiteResourceHandler if this request is coming from a // RenderViewHost with a pending cross-site request. We only check this for // MAIN_FRAME requests. Unblock requests only come from a blocked page, do diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 910cc16..8b174b7 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2069,6 +2069,8 @@ 'browser/prerender/prerender_interceptor.h', 'browser/prerender/prerender_manager.cc', 'browser/prerender/prerender_manager.h', + 'browser/prerender/prerender_resource_handler.h', + 'browser/prerender/prerender_resource_handler.cc', 'browser/printing/print_dialog_gtk.cc', 'browser/printing/print_dialog_gtk.h', 'browser/printing/print_dialog_cloud.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index a023122..19b4580 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1319,6 +1319,7 @@ 'browser/prefs/session_startup_pref_unittest.cc', 'browser/prerender/prerender_interceptor_unittest.cc', 'browser/prerender/prerender_manager_unittest.cc', + 'browser/prerender/prerender_resource_handler_unittest.cc', 'browser/printing/cloud_print/cloud_print_setup_source_unittest.cc', 'browser/printing/print_dialog_cloud_unittest.cc', 'browser/printing/print_job_unittest.cc', |