// 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 "content/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 prerender {
namespace {
bool ShouldPrerenderURL(const GURL& url) {
if (!url.is_valid())
return false;
if (!url.SchemeIs("http")) {
RecordFinalStatus(FINAL_STATUS_HTTPS);
return false;
}
return true;
}
bool ValidateAliasURLs(const std::vector<GURL>& urls) {
for (std::vector<GURL>::const_iterator it = urls.begin();
it != urls.end();
++it) {
if (!ShouldPrerenderURL(*it))
return false;
}
return true;
}
bool ShouldPrerender(const ResourceResponse* response) {
if (!response)
return false;
const ResourceResponseHead& rrh = response->response_head;
if (!rrh.headers)
return false;
if (rrh.mime_type != "text/html")
return false;
if (rrh.headers->response_code() != 200)
return false;
return true;
}
} // namespace
PrerenderResourceHandler* PrerenderResourceHandler::MaybeCreate(
const net::URLRequest& request,
ChromeURLRequestContext* context,
ResourceHandler* next_handler,
bool is_from_prerender,
int child_id,
int route_id) {
if (!context || !context->prerender_manager())
return NULL;
if (!(request.load_flags() & net::LOAD_PREFETCH))
return NULL;
if (!ShouldPrerenderURL(request.url()))
return NULL;
if (request.method() != "GET")
return NULL;
return new PrerenderResourceHandler(request,
next_handler,
context->prerender_manager(),
is_from_prerender,
child_id,
route_id);
}
PrerenderResourceHandler::PrerenderResourceHandler(
const net::URLRequest& request,
ResourceHandler* next_handler,
PrerenderManager* prerender_manager,
bool make_pending,
int child_id,
int route_id)
: next_handler_(next_handler),
prerender_manager_(prerender_manager),
ALLOW_THIS_IN_INITIALIZER_LIST(
prerender_callback_(NewCallback(
this, &PrerenderResourceHandler::StartPrerender))),
request_(request),
child_id_(child_id),
route_id_(route_id),
make_pending_(make_pending) {
DCHECK(next_handler);
DCHECK(prerender_manager);
}
PrerenderResourceHandler::PrerenderResourceHandler(
const net::URLRequest& request,
ResourceHandler* next_handler,
PrerenderCallback* callback)
: next_handler_(next_handler),
prerender_callback_(callback),
request_(request) {
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) {
if (!ShouldPrerenderURL(url))
return false;
alias_urls_.push_back(url);
url_ = url;
}
return will_redirect;
}
bool PrerenderResourceHandler::OnResponseStarted(int request_id,
ResourceResponse* response) {
if (ShouldPrerender(response)) {
DCHECK(ValidateAliasURLs(alias_urls_));
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
NewRunnableMethod(
this,
&PrerenderResourceHandler::RunCallbackFromUIThread,
std::make_pair(child_id_, route_id_),
url_,
alias_urls_,
GURL(request_.referrer()),
make_pending_));
}
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) {
if (!ShouldPrerenderURL(url))
return false;
alias_urls_.push_back(url);
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 std::pair<int, int>& child_route_id_pair,
const GURL& url,
const std::vector<GURL>& alias_urls,
const GURL& referrer,
bool make_pending) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
prerender_callback_->Run(child_route_id_pair,
url, alias_urls, referrer,
make_pending);
}
void PrerenderResourceHandler::StartPrerender(
const std::pair<int, int>& child_route_id_pair,
const GURL& url,
const std::vector<GURL>& alias_urls,
const GURL& referrer,
bool make_pending) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!prerender_manager_->is_enabled())
return;
if (make_pending) {
prerender_manager_->AddPendingPreload(child_route_id_pair,
url, alias_urls, referrer);
} else {
prerender_manager_->AddPreload(url, alias_urls, referrer);
}
}
} // namespace prerender