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_local_predictor.h"
12 #include "chrome/browser/prerender/prerender_manager.h"
13 #include "chrome/browser/prerender/prerender_manager_factory.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "components/password_manager/core/browser/password_manager.h"
16 #include "content/public/browser/navigation_details.h"
17 #include "content/public/browser/navigation_entry.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/frame_navigate_params.h"
22 using content::WebContents
;
24 DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper
);
30 void ReportTabHelperURLSeenToLocalPredictor(
31 PrerenderManager
* prerender_manager
,
33 WebContents
* web_contents
) {
34 if (!prerender_manager
)
36 PrerenderLocalPredictor
* local_predictor
=
37 prerender_manager
->local_predictor();
40 local_predictor
->OnTabHelperURLSeen(url
, web_contents
);
46 void PrerenderTabHelper::CreateForWebContentsWithPasswordManager(
47 content::WebContents
* web_contents
,
48 password_manager::PasswordManager
* password_manager
) {
49 if (!FromWebContents(web_contents
)) {
50 web_contents
->SetUserData(UserDataKey(),
51 new PrerenderTabHelper(web_contents
,
56 PrerenderTabHelper::PrerenderTabHelper(
57 content::WebContents
* web_contents
,
58 password_manager::PasswordManager
* password_manager
)
59 : content::WebContentsObserver(web_contents
),
61 next_load_is_control_prerender_(false),
62 next_load_origin_(ORIGIN_NONE
),
64 if (password_manager
) {
65 // May be NULL in testing.
66 password_manager
->AddSubmissionCallback(
67 base::Bind(&PrerenderTabHelper::PasswordSubmitted
,
68 weak_factory_
.GetWeakPtr()));
71 // Determine if this is a prerender.
72 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
73 if (prerender_manager
&&
74 prerender_manager
->IsWebContentsPrerendering(web_contents
, &origin_
)) {
75 navigation_type_
= NAVIGATION_TYPE_PRERENDERED
;
77 navigation_type_
= NAVIGATION_TYPE_NORMAL
;
81 PrerenderTabHelper::~PrerenderTabHelper() {
84 void PrerenderTabHelper::ProvisionalChangeToMainFrameUrl(
86 content::RenderFrameHost
* render_frame_host
) {
88 RecordEvent(EVENT_MAINFRAME_CHANGE
);
89 RecordEventIfLoggedInURL(EVENT_MAINFRAME_CHANGE_DOMAIN_LOGGED_IN
, url
);
90 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
91 if (!prerender_manager
)
93 if (prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
))
95 ReportTabHelperURLSeenToLocalPredictor(prerender_manager
, url
,
99 void PrerenderTabHelper::DidCommitProvisionalLoadForFrame(
100 content::RenderFrameHost
* render_frame_host
,
101 const GURL
& validated_url
,
102 content::PageTransition transition_type
) {
103 if (render_frame_host
->GetParent())
105 RecordEvent(EVENT_MAINFRAME_COMMIT
);
106 RecordEventIfLoggedInURL(EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN
,
108 url_
= validated_url
;
109 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
110 if (!prerender_manager
)
112 if (prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
))
114 prerender_manager
->RecordNavigation(validated_url
);
115 ReportTabHelperURLSeenToLocalPredictor(prerender_manager
, validated_url
,
119 void PrerenderTabHelper::DidStopLoading(
120 content::RenderViewHost
* render_view_host
) {
121 // Compute the PPLT metric and report it in a histogram, if needed. If the
122 // page is still prerendering, record the not swapped in page load time
124 if (!pplt_load_start_
.is_null()) {
125 base::TimeTicks now
= base::TimeTicks::Now();
126 if (IsPrerendering()) {
127 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
128 if (prerender_manager
) {
129 prerender_manager
->RecordPageLoadTimeNotSwappedIn(
130 origin_
, now
- pplt_load_start_
, url_
);
135 double fraction_elapsed_at_swapin
= -1.0;
136 if (!actual_load_start_
.is_null()) {
137 double plt
= (now
- actual_load_start_
).InMillisecondsF();
139 fraction_elapsed_at_swapin
= 1.0 -
140 (now
- pplt_load_start_
).InMillisecondsF() / plt
;
142 fraction_elapsed_at_swapin
= 1.0;
144 DCHECK_GE(fraction_elapsed_at_swapin
, 0.0);
145 DCHECK_LE(fraction_elapsed_at_swapin
, 1.0);
148 RecordPerceivedPageLoadTime(
149 now
- pplt_load_start_
, fraction_elapsed_at_swapin
);
153 // Reset the PPLT metric.
154 pplt_load_start_
= base::TimeTicks();
155 actual_load_start_
= base::TimeTicks();
158 void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
159 content::RenderFrameHost
* render_frame_host
,
160 const GURL
& validated_url
,
162 bool is_iframe_srcdoc
) {
163 if (render_frame_host
->GetParent())
166 // Record PPLT state for the beginning of a new navigation.
167 pplt_load_start_
= base::TimeTicks::Now();
168 actual_load_start_
= base::TimeTicks();
170 if (next_load_is_control_prerender_
) {
171 DCHECK_EQ(NAVIGATION_TYPE_NORMAL
, navigation_type_
);
172 navigation_type_
= NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED
;
173 origin_
= next_load_origin_
;
174 next_load_is_control_prerender_
= false;
175 next_load_origin_
= ORIGIN_NONE
;
179 void PrerenderTabHelper::PasswordSubmitted(const autofill::PasswordForm
& form
) {
180 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
181 if (prerender_manager
) {
182 prerender_manager
->RecordLikelyLoginOnURL(form
.origin
);
183 RecordEvent(EVENT_LOGIN_ACTION_ADDED
);
184 if (form
.password_value
.empty())
185 RecordEvent(EVENT_LOGIN_ACTION_ADDED_PW_EMPTY
);
189 PrerenderManager
* PrerenderTabHelper::MaybeGetPrerenderManager() const {
190 return PrerenderManagerFactory::GetForProfile(
191 Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
194 bool PrerenderTabHelper::IsPrerendering() {
195 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
196 if (!prerender_manager
)
198 return prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
);
201 void PrerenderTabHelper::PrerenderSwappedIn() {
202 // Ensure we are not prerendering any more.
203 DCHECK_EQ(NAVIGATION_TYPE_PRERENDERED
, navigation_type_
);
204 DCHECK(!IsPrerendering());
205 if (pplt_load_start_
.is_null()) {
206 // If we have already finished loading, report a 0 PPLT.
207 RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0);
208 DCHECK_EQ(NAVIGATION_TYPE_NORMAL
, navigation_type_
);
210 // If we have not finished loading yet, record the actual load start, and
211 // rebase the start time to now.
212 actual_load_start_
= pplt_load_start_
;
213 pplt_load_start_
= base::TimeTicks::Now();
217 void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin
) {
218 next_load_is_control_prerender_
= true;
219 next_load_origin_
= origin
;
222 void PrerenderTabHelper::RecordEvent(PrerenderTabHelper::Event event
) const {
223 UMA_HISTOGRAM_ENUMERATION("Prerender.TabHelperEvent",
224 event
, PrerenderTabHelper::EVENT_MAX_VALUE
);
227 void PrerenderTabHelper::RecordEventIfLoggedInURL(
228 PrerenderTabHelper::Event event
, const GURL
& url
) {
229 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
230 if (!prerender_manager
)
232 scoped_ptr
<bool> is_present(new bool);
233 scoped_ptr
<bool> lookup_succeeded(new bool);
234 bool* is_present_ptr
= is_present
.get();
235 bool* lookup_succeeded_ptr
= lookup_succeeded
.get();
236 prerender_manager
->CheckIfLikelyLoggedInOnURL(
239 lookup_succeeded_ptr
,
240 base::Bind(&PrerenderTabHelper::RecordEventIfLoggedInURLResult
,
241 weak_factory_
.GetWeakPtr(),
243 base::Passed(&is_present
),
244 base::Passed(&lookup_succeeded
)));
247 void PrerenderTabHelper::RecordEventIfLoggedInURLResult(
248 PrerenderTabHelper::Event event
,
249 scoped_ptr
<bool> is_present
,
250 scoped_ptr
<bool> lookup_succeeded
) {
251 if (*lookup_succeeded
&& *is_present
)
255 void PrerenderTabHelper::RecordPerceivedPageLoadTime(
256 base::TimeDelta perceived_page_load_time
,
257 double fraction_plt_elapsed_at_swap_in
) {
258 DCHECK(!IsPrerendering());
259 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
260 if (!prerender_manager
)
263 // Note: it is possible for |next_load_is_control_prerender_| to be true at
264 // this point. This does not affect the classification of the current load,
265 // but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED
266 // navigation interrupts and aborts another navigation.)
267 prerender_manager
->RecordPerceivedPageLoadTime(
268 origin_
, navigation_type_
, perceived_page_load_time
,
269 fraction_plt_elapsed_at_swap_in
, url_
);
271 // Reset state for the next navigation.
272 navigation_type_
= NAVIGATION_TYPE_NORMAL
;
273 origin_
= ORIGIN_NONE
;
276 } // namespace prerender