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/resource_request_details.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/common/frame_navigate_params.h"
23 using content::WebContents
;
25 DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper
);
31 void ReportTabHelperURLSeenToLocalPredictor(
32 PrerenderManager
* prerender_manager
,
34 WebContents
* web_contents
) {
35 if (!prerender_manager
)
37 PrerenderLocalPredictor
* local_predictor
=
38 prerender_manager
->local_predictor();
41 local_predictor
->OnTabHelperURLSeen(url
, web_contents
);
47 void PrerenderTabHelper::CreateForWebContentsWithPasswordManager(
48 content::WebContents
* web_contents
,
49 password_manager::PasswordManager
* password_manager
) {
50 if (!FromWebContents(web_contents
)) {
51 web_contents
->SetUserData(UserDataKey(),
52 new PrerenderTabHelper(web_contents
,
57 PrerenderTabHelper::PrerenderTabHelper(
58 content::WebContents
* web_contents
,
59 password_manager::PasswordManager
* password_manager
)
60 : content::WebContentsObserver(web_contents
),
62 next_load_is_control_prerender_(false),
63 next_load_origin_(ORIGIN_NONE
),
65 if (password_manager
) {
66 // May be NULL in testing.
67 password_manager
->AddSubmissionCallback(
68 base::Bind(&PrerenderTabHelper::PasswordSubmitted
,
69 weak_factory_
.GetWeakPtr()));
72 // Determine if this is a prerender.
73 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
74 if (prerender_manager
&&
75 prerender_manager
->IsWebContentsPrerendering(web_contents
, &origin_
)) {
76 navigation_type_
= NAVIGATION_TYPE_PRERENDERED
;
78 navigation_type_
= NAVIGATION_TYPE_NORMAL
;
82 PrerenderTabHelper::~PrerenderTabHelper() {
85 void PrerenderTabHelper::DidGetRedirectForResourceRequest(
86 content::RenderViewHost
* render_view_host
,
87 const content::ResourceRedirectDetails
& details
) {
88 if (details
.resource_type
!= content::RESOURCE_TYPE_MAIN_FRAME
)
91 MainFrameUrlDidChange(details
.new_url
);
94 void PrerenderTabHelper::DidCommitProvisionalLoadForFrame(
95 content::RenderFrameHost
* render_frame_host
,
96 const GURL
& validated_url
,
97 ui::PageTransition transition_type
) {
98 if (render_frame_host
->GetParent())
100 RecordEvent(EVENT_MAINFRAME_COMMIT
);
101 RecordEventIfLoggedInURL(EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN
,
103 url_
= validated_url
;
104 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
105 if (!prerender_manager
)
107 if (prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
))
109 prerender_manager
->RecordNavigation(validated_url
);
110 ReportTabHelperURLSeenToLocalPredictor(prerender_manager
, validated_url
,
114 void PrerenderTabHelper::DidStopLoading(
115 content::RenderViewHost
* render_view_host
) {
116 // Compute the PPLT metric and report it in a histogram, if needed. If the
117 // page is still prerendering, record the not swapped in page load time
119 if (!pplt_load_start_
.is_null()) {
120 base::TimeTicks now
= base::TimeTicks::Now();
121 if (IsPrerendering()) {
122 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
123 if (prerender_manager
) {
124 prerender_manager
->RecordPageLoadTimeNotSwappedIn(
125 origin_
, now
- pplt_load_start_
, url_
);
130 double fraction_elapsed_at_swapin
= -1.0;
131 if (!actual_load_start_
.is_null()) {
132 double plt
= (now
- actual_load_start_
).InMillisecondsF();
134 fraction_elapsed_at_swapin
= 1.0 -
135 (now
- pplt_load_start_
).InMillisecondsF() / plt
;
137 fraction_elapsed_at_swapin
= 1.0;
139 DCHECK_GE(fraction_elapsed_at_swapin
, 0.0);
140 DCHECK_LE(fraction_elapsed_at_swapin
, 1.0);
143 RecordPerceivedPageLoadTime(
144 now
- pplt_load_start_
, fraction_elapsed_at_swapin
);
148 // Reset the PPLT metric.
149 pplt_load_start_
= base::TimeTicks();
150 actual_load_start_
= base::TimeTicks();
153 void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
154 content::RenderFrameHost
* render_frame_host
,
155 const GURL
& validated_url
,
157 bool is_iframe_srcdoc
) {
158 if (render_frame_host
->GetParent())
161 // Record PPLT state for the beginning of a new navigation.
162 pplt_load_start_
= base::TimeTicks::Now();
163 actual_load_start_
= base::TimeTicks();
165 if (next_load_is_control_prerender_
) {
166 DCHECK_EQ(NAVIGATION_TYPE_NORMAL
, navigation_type_
);
167 navigation_type_
= NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED
;
168 origin_
= next_load_origin_
;
169 next_load_is_control_prerender_
= false;
170 next_load_origin_
= ORIGIN_NONE
;
173 MainFrameUrlDidChange(validated_url
);
176 void PrerenderTabHelper::MainFrameUrlDidChange(const GURL
& url
) {
178 RecordEvent(EVENT_MAINFRAME_CHANGE
);
179 RecordEventIfLoggedInURL(EVENT_MAINFRAME_CHANGE_DOMAIN_LOGGED_IN
, url
);
180 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
181 if (!prerender_manager
)
183 if (prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
))
185 ReportTabHelperURLSeenToLocalPredictor(prerender_manager
, url
,
189 void PrerenderTabHelper::PasswordSubmitted(const autofill::PasswordForm
& form
) {
190 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
191 if (prerender_manager
) {
192 prerender_manager
->RecordLikelyLoginOnURL(form
.origin
);
193 RecordEvent(EVENT_LOGIN_ACTION_ADDED
);
194 if (form
.password_value
.empty())
195 RecordEvent(EVENT_LOGIN_ACTION_ADDED_PW_EMPTY
);
199 PrerenderManager
* PrerenderTabHelper::MaybeGetPrerenderManager() const {
200 return PrerenderManagerFactory::GetForProfile(
201 Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
204 bool PrerenderTabHelper::IsPrerendering() {
205 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
206 if (!prerender_manager
)
208 return prerender_manager
->IsWebContentsPrerendering(web_contents(), NULL
);
211 void PrerenderTabHelper::PrerenderSwappedIn() {
212 // Ensure we are not prerendering any more.
213 DCHECK_EQ(NAVIGATION_TYPE_PRERENDERED
, navigation_type_
);
214 DCHECK(!IsPrerendering());
215 if (pplt_load_start_
.is_null()) {
216 // If we have already finished loading, report a 0 PPLT.
217 RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0);
218 DCHECK_EQ(NAVIGATION_TYPE_NORMAL
, navigation_type_
);
220 // If we have not finished loading yet, record the actual load start, and
221 // rebase the start time to now.
222 actual_load_start_
= pplt_load_start_
;
223 pplt_load_start_
= base::TimeTicks::Now();
227 void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin
) {
228 next_load_is_control_prerender_
= true;
229 next_load_origin_
= origin
;
232 void PrerenderTabHelper::RecordEvent(PrerenderTabHelper::Event event
) const {
233 UMA_HISTOGRAM_ENUMERATION("Prerender.TabHelperEvent",
234 event
, PrerenderTabHelper::EVENT_MAX_VALUE
);
237 void PrerenderTabHelper::RecordEventIfLoggedInURL(
238 PrerenderTabHelper::Event event
, const GURL
& url
) {
239 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
240 if (!prerender_manager
)
242 scoped_ptr
<bool> is_present(new bool);
243 scoped_ptr
<bool> lookup_succeeded(new bool);
244 bool* is_present_ptr
= is_present
.get();
245 bool* lookup_succeeded_ptr
= lookup_succeeded
.get();
246 prerender_manager
->CheckIfLikelyLoggedInOnURL(
249 lookup_succeeded_ptr
,
250 base::Bind(&PrerenderTabHelper::RecordEventIfLoggedInURLResult
,
251 weak_factory_
.GetWeakPtr(),
253 base::Passed(&is_present
),
254 base::Passed(&lookup_succeeded
)));
257 void PrerenderTabHelper::RecordEventIfLoggedInURLResult(
258 PrerenderTabHelper::Event event
,
259 scoped_ptr
<bool> is_present
,
260 scoped_ptr
<bool> lookup_succeeded
) {
261 if (*lookup_succeeded
&& *is_present
)
265 void PrerenderTabHelper::RecordPerceivedPageLoadTime(
266 base::TimeDelta perceived_page_load_time
,
267 double fraction_plt_elapsed_at_swap_in
) {
268 DCHECK(!IsPrerendering());
269 PrerenderManager
* prerender_manager
= MaybeGetPrerenderManager();
270 if (!prerender_manager
)
273 // Note: it is possible for |next_load_is_control_prerender_| to be true at
274 // this point. This does not affect the classification of the current load,
275 // but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED
276 // navigation interrupts and aborts another navigation.)
277 prerender_manager
->RecordPerceivedPageLoadTime(
278 origin_
, navigation_type_
, perceived_page_load_time
,
279 fraction_plt_elapsed_at_swap_in
, url_
);
281 // Reset state for the next navigation.
282 navigation_type_
= NAVIGATION_TYPE_NORMAL
;
283 origin_
= ORIGIN_NONE
;
286 } // namespace prerender