summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-12 13:12:21 +0000
committercbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-12 13:12:21 +0000
commit0e25b15e6bf51ced4c0cc3d2c14122b38debc815 (patch)
treed64bde3c0e1043135dd872c936f137581f6fd3b7
parent702f9978ada5dcf21c2720219c7de067b81950a6 (diff)
downloadchromium_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.cc2
-rw-r--r--chrome/browser/net/chrome_url_request_context.h12
-rw-r--r--chrome/browser/prerender/prerender_manager.h15
-rw-r--r--chrome/browser/prerender/prerender_manager_unittest.cc7
-rw-r--r--chrome/browser/prerender/prerender_resource_handler.cc179
-rw-r--r--chrome/browser/prerender/prerender_resource_handler.h99
-rw-r--r--chrome/browser/prerender/prerender_resource_handler_unittest.cc214
-rw-r--r--chrome/browser/profiles/profile_impl.cc6
-rw-r--r--chrome/browser/profiles/profile_impl.h2
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.cc10
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
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',