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/password_manager/chrome_password_manager_client.h"
11 #include "chrome/browser/prerender/prerender_histograms.h"
12 #include "chrome/browser/prerender/prerender_local_predictor.h"
13 #include "chrome/browser/prerender/prerender_manager.h"
14 #include "chrome/browser/prerender/prerender_manager_factory.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "components/password_manager/core/browser/password_manager.h"
17 #include "content/public/browser/navigation_details.h"
18 #include "content/public/browser/navigation_entry.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/resource_request_details.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/common/frame_navigate_params.h"
24 using content::WebContents
;
26 DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper
);
32 void ReportTabHelperURLSeenToLocalPredictor(
33 PrerenderManager
* prerender_manager
,
35 WebContents
* web_contents
) {
36 if (!prerender_manager
)
38 PrerenderLocalPredictor
* local_predictor
=
39 prerender_manager
->local_predictor();
42 local_predictor
->OnTabHelperURLSeen(url
, web_contents
);
47 PrerenderTabHelper::PrerenderTabHelper(content::WebContents
* web_contents
)
48 : content::WebContentsObserver(web_contents
),
50 next_load_is_control_prerender_(false),
51 next_load_origin_(ORIGIN_NONE
),
53 ChromePasswordManagerClient
* client
=
54 ChromePasswordManagerClient::FromWebContents(web_contents
);
55 // May be NULL during testing.
57 client
->GetPasswordManager()->AddSubmissionCallback(base::Bind(
58 &PrerenderTabHelper::PasswordSubmitted
, weak_factory_
.GetWeakPtr()));
61 // Determine if this is a prerender.
62 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
63 if (prerender_manager
&&
64 prerender_manager
->IsWebContentsPrerendering(web_contents
, &origin_
)) {
65 navigation_type_
= NAVIGATION_TYPE_PRERENDERED
;
67 navigation_type_
= NAVIGATION_TYPE_NORMAL
;
71 PrerenderTabHelper::~PrerenderTabHelper() {
74 void PrerenderTabHelper::DidGetRedirectForResourceRequest(
75 content::RenderFrameHost
* render_frame_host
,
76 const content::ResourceRedirectDetails
& details
) {
77 if (details
.resource_type
!= content::RESOURCE_TYPE_MAIN_FRAME
)
80 MainFrameUrlDidChange(details
.new_url
);
83 void PrerenderTabHelper::DidCommitProvisionalLoadForFrame(
84 content::RenderFrameHost
* render_frame_host
,
85 const GURL
& validated_url
,
86 ui::PageTransition transition_type
) {
87 if (render_frame_host
->GetParent())
89 RecordEvent(EVENT_MAINFRAME_COMMIT
);
90 RecordEventIfLoggedInURL(EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN
,
93 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
94 if (!prerender_manager
)
96 if (prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
))
98 prerender_manager
->RecordNavigation(validated_url
);
99 ReportTabHelperURLSeenToLocalPredictor(prerender_manager
, validated_url
,
103 void PrerenderTabHelper::DidStopLoading() {
104 // Compute the PPLT metric and report it in a histogram, if needed. If the
105 // page is still prerendering, record the not swapped in page load time
107 if (!pplt_load_start_
.is_null()) {
108 base::TimeTicks now
= base::TimeTicks::Now();
109 if (IsPrerendering()) {
110 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
111 if (prerender_manager
) {
112 prerender_manager
->RecordPageLoadTimeNotSwappedIn(
113 origin_
, now
- pplt_load_start_
, url_
);
118 double fraction_elapsed_at_swapin
= -1.0;
119 if (!actual_load_start_
.is_null()) {
120 double plt
= (now
- actual_load_start_
).InMillisecondsF();
122 fraction_elapsed_at_swapin
= 1.0 -
123 (now
- pplt_load_start_
).InMillisecondsF() / plt
;
125 fraction_elapsed_at_swapin
= 1.0;
127 DCHECK_GE(fraction_elapsed_at_swapin
, 0.0);
128 DCHECK_LE(fraction_elapsed_at_swapin
, 1.0);
131 RecordPerceivedPageLoadTime(
132 now
- pplt_load_start_
, fraction_elapsed_at_swapin
);
136 // Reset the PPLT metric.
137 pplt_load_start_
= base::TimeTicks();
138 actual_load_start_
= base::TimeTicks();
141 void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
142 content::RenderFrameHost
* render_frame_host
,
143 const GURL
& validated_url
,
145 bool is_iframe_srcdoc
) {
146 if (render_frame_host
->GetParent())
149 // Record PPLT state for the beginning of a new navigation.
150 pplt_load_start_
= base::TimeTicks::Now();
151 actual_load_start_
= base::TimeTicks();
153 if (next_load_is_control_prerender_
) {
154 DCHECK_EQ(NAVIGATION_TYPE_NORMAL
, navigation_type_
);
155 navigation_type_
= NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED
;
156 origin_
= next_load_origin_
;
157 next_load_is_control_prerender_
= false;
158 next_load_origin_
= ORIGIN_NONE
;
161 MainFrameUrlDidChange(validated_url
);
164 void PrerenderTabHelper::MainFrameUrlDidChange(const GURL
& url
) {
166 RecordEvent(EVENT_MAINFRAME_CHANGE
);
167 RecordEventIfLoggedInURL(EVENT_MAINFRAME_CHANGE_DOMAIN_LOGGED_IN
, url
);
168 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
169 if (!prerender_manager
)
171 if (prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
))
173 ReportTabHelperURLSeenToLocalPredictor(prerender_manager
, url
,
177 void PrerenderTabHelper::PasswordSubmitted(const autofill::PasswordForm
& form
) {
178 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
179 if (prerender_manager
) {
180 prerender_manager
->RecordLikelyLoginOnURL(form
.origin
);
181 RecordEvent(EVENT_LOGIN_ACTION_ADDED
);
182 if (form
.password_value
.empty())
183 RecordEvent(EVENT_LOGIN_ACTION_ADDED_PW_EMPTY
);
187 PrerenderManager
* PrerenderTabHelper::MaybeGetPrerenderManager() const {
188 return PrerenderManagerFactory::GetForProfile(
189 Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
192 bool PrerenderTabHelper::IsPrerendering() {
193 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
194 if (!prerender_manager
)
196 return prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
);
199 void PrerenderTabHelper::PrerenderSwappedIn() {
200 // Ensure we are not prerendering any more.
201 DCHECK_EQ(NAVIGATION_TYPE_PRERENDERED
, navigation_type_
);
202 DCHECK(!IsPrerendering());
203 if (pplt_load_start_
.is_null()) {
204 // If we have already finished loading, report a 0 PPLT.
205 RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0);
206 DCHECK_EQ(NAVIGATION_TYPE_NORMAL
, navigation_type_
);
208 // If we have not finished loading yet, record the actual load start, and
209 // rebase the start time to now.
210 actual_load_start_
= pplt_load_start_
;
211 pplt_load_start_
= base::TimeTicks::Now();
215 void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin
) {
216 next_load_is_control_prerender_
= true;
217 next_load_origin_
= origin
;
220 void PrerenderTabHelper::RecordEvent(PrerenderTabHelper::Event event
) const {
221 UMA_HISTOGRAM_ENUMERATION("Prerender.TabHelperEvent",
222 event
, PrerenderTabHelper::EVENT_MAX_VALUE
);
225 void PrerenderTabHelper::RecordEventIfLoggedInURL(
226 PrerenderTabHelper::Event event
, const GURL
& url
) {
227 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
228 if (!prerender_manager
)
230 scoped_ptr
<bool> is_present(new bool);
231 scoped_ptr
<bool> lookup_succeeded(new bool);
232 bool* is_present_ptr
= is_present
.get();
233 bool* lookup_succeeded_ptr
= lookup_succeeded
.get();
234 prerender_manager
->CheckIfLikelyLoggedInOnURL(
237 lookup_succeeded_ptr
,
238 base::Bind(&PrerenderTabHelper::RecordEventIfLoggedInURLResult
,
239 weak_factory_
.GetWeakPtr(),
241 base::Passed(&is_present
),
242 base::Passed(&lookup_succeeded
)));
245 void PrerenderTabHelper::RecordEventIfLoggedInURLResult(
246 PrerenderTabHelper::Event event
,
247 scoped_ptr
<bool> is_present
,
248 scoped_ptr
<bool> lookup_succeeded
) {
249 if (*lookup_succeeded
&& *is_present
)
253 void PrerenderTabHelper::RecordPerceivedPageLoadTime(
254 base::TimeDelta perceived_page_load_time
,
255 double fraction_plt_elapsed_at_swap_in
) {
256 DCHECK(!IsPrerendering());
257 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
258 if (!prerender_manager
)
261 // Note: it is possible for |next_load_is_control_prerender_| to be true at
262 // this point. This does not affect the classification of the current load,
263 // but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED
264 // navigation interrupts and aborts another navigation.)
265 prerender_manager
->RecordPerceivedPageLoadTime(
266 origin_
, navigation_type_
, perceived_page_load_time
,
267 fraction_plt_elapsed_at_swap_in
, url_
);
269 // Reset state for the next navigation.
270 navigation_type_
= NAVIGATION_TYPE_NORMAL
;
271 origin_
= ORIGIN_NONE
;
274 } // namespace prerender