blob: 70e1aeefcae471fec122a3e321d7fe71cb7ce56b [file] [log] [blame]
// Copyright 2013 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_throttle.h"
#include "chrome/browser/prerender/prerender_final_status.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_util.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/resource_controller.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "net/url_request/url_request.h"
using content::ResourceType;
namespace prerender {
namespace {
static const char kFollowOnlyWhenPrerenderShown[] =
"follow-only-when-prerender-shown";
PrerenderContents* g_prerender_contents_for_testing;
}
void PrerenderResourceThrottle::OverridePrerenderContentsForTesting(
PrerenderContents* contents) {
g_prerender_contents_for_testing = contents;
}
PrerenderResourceThrottle::PrerenderResourceThrottle(net::URLRequest* request)
: request_(request) {
}
void PrerenderResourceThrottle::WillStartRequest(bool* defer) {
const content::ResourceRequestInfo* info =
content::ResourceRequestInfo::ForRequest(request_);
*defer = true;
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(&PrerenderResourceThrottle::WillStartRequestOnUI,
AsWeakPtr(), request_->method(), info->GetResourceType(),
info->GetChildID(), info->GetRenderFrameID(),
request_->url()));
}
void PrerenderResourceThrottle::WillRedirectRequest(const GURL& new_url,
bool* defer) {
const content::ResourceRequestInfo* info =
content::ResourceRequestInfo::ForRequest(request_);
*defer = true;
std::string header;
request_->GetResponseHeaderByName(kFollowOnlyWhenPrerenderShown, &header);
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(&PrerenderResourceThrottle::WillRedirectRequestOnUI,
AsWeakPtr(), header, info->GetResourceType(), info->IsAsync(),
info->GetChildID(), info->GetRenderFrameID(), new_url));
}
const char* PrerenderResourceThrottle::GetNameForLogging() const {
return "PrerenderResourceThrottle";
}
void PrerenderResourceThrottle::Resume() {
controller()->Resume();
}
void PrerenderResourceThrottle::Cancel() {
controller()->Cancel();
}
void PrerenderResourceThrottle::WillStartRequestOnUI(
const base::WeakPtr<PrerenderResourceThrottle>& throttle,
const std::string& method,
ResourceType resource_type,
int render_process_id,
int render_frame_id,
const GURL& url) {
bool cancel = false;
PrerenderContents* prerender_contents =
PrerenderContentsFromRenderFrame(render_process_id, render_frame_id);
if (prerender_contents) {
// Abort any prerenders that spawn requests that use unsupported HTTP
// methods or schemes.
if (!PrerenderManager::IsValidHttpMethod(method)) {
prerender_contents->Destroy(FINAL_STATUS_INVALID_HTTP_METHOD);
cancel = true;
} else if (!PrerenderManager::DoesSubresourceURLHaveValidScheme(url)) {
prerender_contents->Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
ReportUnsupportedPrerenderScheme(url);
cancel = true;
#if defined(OS_ANDROID)
} else if (resource_type == content::RESOURCE_TYPE_FAVICON) {
// Delay icon fetching until the contents are getting swapped in
// to conserve network usage in mobile devices.
prerender_contents->AddResourceThrottle(throttle);
return;
#endif
}
}
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(cancel ? &PrerenderResourceThrottle::Cancel :
&PrerenderResourceThrottle::Resume, throttle));
}
void PrerenderResourceThrottle::WillRedirectRequestOnUI(
const base::WeakPtr<PrerenderResourceThrottle>& throttle,
const std::string& follow_only_when_prerender_shown_header,
ResourceType resource_type,
bool async,
int render_process_id,
int render_frame_id,
const GURL& new_url) {
bool cancel = false;
PrerenderContents* prerender_contents =
PrerenderContentsFromRenderFrame(render_process_id, render_frame_id);
if (prerender_contents) {
// Abort any prerenders with requests which redirect to invalid schemes.
if (!PrerenderManager::DoesURLHaveValidScheme(new_url)) {
prerender_contents->Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
ReportUnsupportedPrerenderScheme(new_url);
cancel = true;
} else if (follow_only_when_prerender_shown_header == "1" &&
resource_type != content::RESOURCE_TYPE_MAIN_FRAME) {
// Only defer redirects with the Follow-Only-When-Prerender-Shown
// header. Do not defer redirects on main frame loads.
if (!async) {
// Cancel on deferred synchronous requests. Those will
// indefinitely hang up a renderer process.
prerender_contents->Destroy(FINAL_STATUS_BAD_DEFERRED_REDIRECT);
cancel = true;
} else {
// Defer the redirect until the prerender is used or canceled.
prerender_contents->AddResourceThrottle(throttle);
return;
}
}
}
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(cancel ? &PrerenderResourceThrottle::Cancel :
&PrerenderResourceThrottle::Resume, throttle));
}
PrerenderContents* PrerenderResourceThrottle::PrerenderContentsFromRenderFrame(
int render_process_id, int render_frame_id) {
if (g_prerender_contents_for_testing)
return g_prerender_contents_for_testing;
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
render_process_id, render_frame_id);
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(rfh);
return PrerenderContents::FromWebContents(web_contents);
}
} // namespace prerender