1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/prerender/prerender_tab_helper.h"
8 #include "base/metrics/histogram.h"
9 #include "base/time/time.h"
10 #include "chrome/browser/prerender/prerender_histograms.h"
11 #include "chrome/browser/prerender/prerender_manager.h"
12 #include "chrome/browser/prerender/prerender_manager_factory.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/resource_request_details.h"
16 #include "content/public/browser/web_contents.h"
18 using content::WebContents
;
20 DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper
);
24 PrerenderTabHelper::PrerenderTabHelper(content::WebContents
* web_contents
)
25 : content::WebContentsObserver(web_contents
),
27 next_load_is_control_prerender_(false),
28 next_load_origin_(ORIGIN_NONE
),
30 // Determine if this is a prerender.
31 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
32 if (prerender_manager
&&
33 prerender_manager
->IsWebContentsPrerendering(web_contents
, &origin_
)) {
34 navigation_type_
= NAVIGATION_TYPE_PRERENDERED
;
36 navigation_type_
= NAVIGATION_TYPE_NORMAL
;
40 PrerenderTabHelper::~PrerenderTabHelper() {
43 void PrerenderTabHelper::DidGetRedirectForResourceRequest(
44 content::RenderFrameHost
* render_frame_host
,
45 const content::ResourceRedirectDetails
& details
) {
46 if (details
.resource_type
!= content::RESOURCE_TYPE_MAIN_FRAME
)
49 MainFrameUrlDidChange(details
.new_url
);
52 void PrerenderTabHelper::DidCommitProvisionalLoadForFrame(
53 content::RenderFrameHost
* render_frame_host
,
54 const GURL
& validated_url
,
55 ui::PageTransition transition_type
) {
56 if (render_frame_host
->GetParent())
59 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
60 if (!prerender_manager
)
62 if (prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
))
64 prerender_manager
->RecordNavigation(validated_url
);
67 void PrerenderTabHelper::DidStopLoading() {
68 // Compute the PPLT metric and report it in a histogram, if needed. If the
69 // page is still prerendering, record the not swapped in page load time
71 if (!pplt_load_start_
.is_null()) {
72 base::TimeTicks now
= base::TimeTicks::Now();
73 if (IsPrerendering()) {
74 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
75 if (prerender_manager
) {
76 prerender_manager
->RecordPageLoadTimeNotSwappedIn(
77 origin_
, now
- pplt_load_start_
, url_
);
82 double fraction_elapsed_at_swapin
= -1.0;
83 if (!actual_load_start_
.is_null()) {
84 double plt
= (now
- actual_load_start_
).InMillisecondsF();
86 fraction_elapsed_at_swapin
= 1.0 -
87 (now
- pplt_load_start_
).InMillisecondsF() / plt
;
89 fraction_elapsed_at_swapin
= 1.0;
91 DCHECK_GE(fraction_elapsed_at_swapin
, 0.0);
92 DCHECK_LE(fraction_elapsed_at_swapin
, 1.0);
95 RecordPerceivedPageLoadTime(
96 now
- pplt_load_start_
, fraction_elapsed_at_swapin
);
100 // Reset the PPLT metric.
101 pplt_load_start_
= base::TimeTicks();
102 actual_load_start_
= base::TimeTicks();
105 void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
106 content::RenderFrameHost
* render_frame_host
,
107 const GURL
& validated_url
,
109 bool is_iframe_srcdoc
) {
110 if (render_frame_host
->GetParent())
113 // Record PPLT state for the beginning of a new navigation.
114 pplt_load_start_
= base::TimeTicks::Now();
115 actual_load_start_
= base::TimeTicks();
117 if (next_load_is_control_prerender_
) {
118 DCHECK_EQ(NAVIGATION_TYPE_NORMAL
, navigation_type_
);
119 navigation_type_
= NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED
;
120 origin_
= next_load_origin_
;
121 next_load_is_control_prerender_
= false;
122 next_load_origin_
= ORIGIN_NONE
;
125 MainFrameUrlDidChange(validated_url
);
128 void PrerenderTabHelper::MainFrameUrlDidChange(const GURL
& url
) {
132 PrerenderManager
* PrerenderTabHelper::MaybeGetPrerenderManager() const {
133 return PrerenderManagerFactory::GetForProfile(
134 Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
137 bool PrerenderTabHelper::IsPrerendering() {
138 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
139 if (!prerender_manager
)
141 return prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
);
144 void PrerenderTabHelper::PrerenderSwappedIn() {
145 // Ensure we are not prerendering any more.
146 DCHECK_EQ(NAVIGATION_TYPE_PRERENDERED
, navigation_type_
);
147 DCHECK(!IsPrerendering());
148 if (pplt_load_start_
.is_null()) {
149 // If we have already finished loading, report a 0 PPLT.
150 RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0);
151 DCHECK_EQ(NAVIGATION_TYPE_NORMAL
, navigation_type_
);
153 // If we have not finished loading yet, record the actual load start, and
154 // rebase the start time to now.
155 actual_load_start_
= pplt_load_start_
;
156 pplt_load_start_
= base::TimeTicks::Now();
160 void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin
) {
161 next_load_is_control_prerender_
= true;
162 next_load_origin_
= origin
;
165 void PrerenderTabHelper::RecordPerceivedPageLoadTime(
166 base::TimeDelta perceived_page_load_time
,
167 double fraction_plt_elapsed_at_swap_in
) {
168 DCHECK(!IsPrerendering());
169 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
170 if (!prerender_manager
)
173 // Note: it is possible for |next_load_is_control_prerender_| to be true at
174 // this point. This does not affect the classification of the current load,
175 // but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED
176 // navigation interrupts and aborts another navigation.)
177 prerender_manager
->RecordPerceivedPageLoadTime(
178 origin_
, navigation_type_
, perceived_page_load_time
,
179 fraction_plt_elapsed_at_swap_in
, url_
);
181 // Reset state for the next navigation.
182 navigation_type_
= NAVIGATION_TYPE_NORMAL
;
183 origin_
= ORIGIN_NONE
;
186 } // namespace prerender