blob: a7450cac5321ed0bdd81571bb7ab6820e3960827 [file] [log] [blame]
// Copyright (c) 2012 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_tab_helper.h"
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "chrome/browser/prerender/prerender_histograms.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/resource_request_details.h"
#include "content/public/browser/web_contents.h"
using content::WebContents;
DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper);
namespace prerender {
PrerenderTabHelper::PrerenderTabHelper(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
origin_(ORIGIN_NONE),
next_load_is_control_prerender_(false),
next_load_origin_(ORIGIN_NONE),
weak_factory_(this) {
// Determine if this is a prerender.
PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
if (prerender_manager &&
prerender_manager->IsWebContentsPrerendering(web_contents, &origin_)) {
navigation_type_ = NAVIGATION_TYPE_PRERENDERED;
} else {
navigation_type_ = NAVIGATION_TYPE_NORMAL;
}
}
PrerenderTabHelper::~PrerenderTabHelper() {
}
void PrerenderTabHelper::DidGetRedirectForResourceRequest(
const content::ResourceRedirectDetails& details) {
if (details.resource_type != content::RESOURCE_TYPE_MAIN_FRAME)
return;
MainFrameUrlDidChange(details.new_url);
}
void PrerenderTabHelper::DidCommitProvisionalLoadForFrame(
content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
ui::PageTransition transition_type) {
if (render_frame_host->GetParent())
return;
url_ = validated_url;
PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
if (!prerender_manager)
return;
if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
return;
prerender_manager->RecordNavigation(validated_url);
}
void PrerenderTabHelper::DidStopLoading() {
// Compute the PPLT metric and report it in a histogram, if needed. If the
// page is still prerendering, record the not swapped in page load time
// instead.
if (!pplt_load_start_.is_null()) {
base::TimeTicks now = GetTimeTicksFromPrerenderManager();
if (IsPrerendering()) {
PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
if (prerender_manager) {
prerender_manager->RecordPageLoadTimeNotSwappedIn(
origin_, now - pplt_load_start_, url_);
} else {
NOTREACHED();
}
} else {
double fraction_elapsed_at_swapin = -1.0;
if (!actual_load_start_.is_null()) {
double plt = (now - actual_load_start_).InMillisecondsF();
if (plt > 0.0) {
fraction_elapsed_at_swapin = 1.0 -
(now - pplt_load_start_).InMillisecondsF() / plt;
} else {
fraction_elapsed_at_swapin = 1.0;
}
DCHECK_GE(fraction_elapsed_at_swapin, 0.0);
DCHECK_LE(fraction_elapsed_at_swapin, 1.0);
}
RecordPerceivedPageLoadTime(
now - pplt_load_start_, fraction_elapsed_at_swapin);
}
}
// Reset the PPLT metric.
pplt_load_start_ = base::TimeTicks();
actual_load_start_ = base::TimeTicks();
}
void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
bool is_error_page) {
if (render_frame_host->GetParent())
return;
// Record PPLT state for the beginning of a new navigation.
pplt_load_start_ = GetTimeTicksFromPrerenderManager();
actual_load_start_ = base::TimeTicks();
if (next_load_is_control_prerender_) {
DCHECK_EQ(NAVIGATION_TYPE_NORMAL, navigation_type_);
navigation_type_ = NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED;
origin_ = next_load_origin_;
next_load_is_control_prerender_ = false;
next_load_origin_ = ORIGIN_NONE;
}
MainFrameUrlDidChange(validated_url);
}
void PrerenderTabHelper::MainFrameUrlDidChange(const GURL& url) {
url_ = url;
}
PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const {
return PrerenderManagerFactory::GetForBrowserContext(
web_contents()->GetBrowserContext());
}
base::TimeTicks PrerenderTabHelper::GetTimeTicksFromPrerenderManager() const {
// Prerender browser tests should always have a PrerenderManager when mocking
// out tick clock.
PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
if (prerender_manager)
return prerender_manager->GetCurrentTimeTicks();
// Fall back to returning the same value as PrerenderManager would have
// returned in production.
return base::TimeTicks::Now();
}
bool PrerenderTabHelper::IsPrerendering() {
PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
if (!prerender_manager)
return false;
return prerender_manager->IsWebContentsPrerendering(web_contents(), NULL);
}
void PrerenderTabHelper::PrerenderSwappedIn() {
// Ensure we are not prerendering any more.
DCHECK_EQ(NAVIGATION_TYPE_PRERENDERED, navigation_type_);
DCHECK(!IsPrerendering());
if (pplt_load_start_.is_null()) {
// If we have already finished loading, report a 0 PPLT.
RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0);
DCHECK_EQ(NAVIGATION_TYPE_NORMAL, navigation_type_);
} else {
// If we have not finished loading yet, record the actual load start, and
// rebase the start time to now.
actual_load_start_ = pplt_load_start_;
pplt_load_start_ = GetTimeTicksFromPrerenderManager();
}
}
void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin) {
next_load_is_control_prerender_ = true;
next_load_origin_ = origin;
}
void PrerenderTabHelper::RecordPerceivedPageLoadTime(
base::TimeDelta perceived_page_load_time,
double fraction_plt_elapsed_at_swap_in) {
DCHECK(!IsPrerendering());
PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
if (!prerender_manager)
return;
// Note: it is possible for |next_load_is_control_prerender_| to be true at
// this point. This does not affect the classification of the current load,
// but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED
// navigation interrupts and aborts another navigation.)
prerender_manager->RecordPerceivedPageLoadTime(
origin_, navigation_type_, perceived_page_load_time,
fraction_plt_elapsed_at_swap_in, url_);
// Reset state for the next navigation.
navigation_type_ = NAVIGATION_TYPE_NORMAL;
origin_ = ORIGIN_NONE;
}
} // namespace prerender