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.
4
5 #include "chrome/browser/prerender/prerender_tab_helper.h"
6
7 #include "base/bind.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"
22
23 using content::WebContents;
24
25 DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper);
26
27 namespace prerender {
28
29 namespace {
30
ReportTabHelperURLSeenToLocalPredictor(PrerenderManager * prerender_manager,const GURL & url,WebContents * web_contents)31 void ReportTabHelperURLSeenToLocalPredictor(
32 PrerenderManager* prerender_manager,
33 const GURL& url,
34 WebContents* web_contents) {
35 if (!prerender_manager)
36 return;
37 PrerenderLocalPredictor* local_predictor =
38 prerender_manager->local_predictor();
39 if (!local_predictor)
40 return;
41 local_predictor->OnTabHelperURLSeen(url, web_contents);
42 }
43
44 } // namespace
45
46 // static
CreateForWebContentsWithPasswordManager(content::WebContents * web_contents,password_manager::PasswordManager * password_manager)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,
53 password_manager));
54 }
55 }
56
PrerenderTabHelper(content::WebContents * web_contents,password_manager::PasswordManager * password_manager)57 PrerenderTabHelper::PrerenderTabHelper(
58 content::WebContents* web_contents,
59 password_manager::PasswordManager* password_manager)
60 : content::WebContentsObserver(web_contents),
61 origin_(ORIGIN_NONE),
62 next_load_is_control_prerender_(false),
63 next_load_origin_(ORIGIN_NONE),
64 weak_factory_(this) {
65 if (password_manager) {
66 // May be NULL in testing.
67 password_manager->AddSubmissionCallback(
68 base::Bind(&PrerenderTabHelper::PasswordSubmitted,
69 weak_factory_.GetWeakPtr()));
70 }
71
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;
77 } else {
78 navigation_type_ = NAVIGATION_TYPE_NORMAL;
79 }
80 }
81
~PrerenderTabHelper()82 PrerenderTabHelper::~PrerenderTabHelper() {
83 }
84
DidGetRedirectForResourceRequest(content::RenderViewHost * render_view_host,const content::ResourceRedirectDetails & details)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)
89 return;
90
91 MainFrameUrlDidChange(details.new_url);
92 }
93
DidCommitProvisionalLoadForFrame(content::RenderFrameHost * render_frame_host,const GURL & validated_url,ui::PageTransition transition_type)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())
99 return;
100 RecordEvent(EVENT_MAINFRAME_COMMIT);
101 RecordEventIfLoggedInURL(EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN,
102 validated_url);
103 url_ = validated_url;
104 PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
105 if (!prerender_manager)
106 return;
107 if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
108 return;
109 prerender_manager->RecordNavigation(validated_url);
110 ReportTabHelperURLSeenToLocalPredictor(prerender_manager, validated_url,
111 web_contents());
112 }
113
DidStopLoading(content::RenderViewHost * render_view_host)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
118 // instead.
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_);
126 } else {
127 NOTREACHED();
128 }
129 } else {
130 double fraction_elapsed_at_swapin = -1.0;
131 if (!actual_load_start_.is_null()) {
132 double plt = (now - actual_load_start_).InMillisecondsF();
133 if (plt > 0.0) {
134 fraction_elapsed_at_swapin = 1.0 -
135 (now - pplt_load_start_).InMillisecondsF() / plt;
136 } else {
137 fraction_elapsed_at_swapin = 1.0;
138 }
139 DCHECK_GE(fraction_elapsed_at_swapin, 0.0);
140 DCHECK_LE(fraction_elapsed_at_swapin, 1.0);
141 }
142
143 RecordPerceivedPageLoadTime(
144 now - pplt_load_start_, fraction_elapsed_at_swapin);
145 }
146 }
147
148 // Reset the PPLT metric.
149 pplt_load_start_ = base::TimeTicks();
150 actual_load_start_ = base::TimeTicks();
151 }
152
DidStartProvisionalLoadForFrame(content::RenderFrameHost * render_frame_host,const GURL & validated_url,bool is_error_page,bool is_iframe_srcdoc)153 void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
154 content::RenderFrameHost* render_frame_host,
155 const GURL& validated_url,
156 bool is_error_page,
157 bool is_iframe_srcdoc) {
158 if (render_frame_host->GetParent())
159 return;
160
161 // Record PPLT state for the beginning of a new navigation.
162 pplt_load_start_ = base::TimeTicks::Now();
163 actual_load_start_ = base::TimeTicks();
164
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;
171 }
172
173 MainFrameUrlDidChange(validated_url);
174 }
175
MainFrameUrlDidChange(const GURL & url)176 void PrerenderTabHelper::MainFrameUrlDidChange(const GURL& url) {
177 url_ = url;
178 RecordEvent(EVENT_MAINFRAME_CHANGE);
179 RecordEventIfLoggedInURL(EVENT_MAINFRAME_CHANGE_DOMAIN_LOGGED_IN, url);
180 PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
181 if (!prerender_manager)
182 return;
183 if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
184 return;
185 ReportTabHelperURLSeenToLocalPredictor(prerender_manager, url,
186 web_contents());
187 }
188
PasswordSubmitted(const autofill::PasswordForm & form)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);
196 }
197 }
198
MaybeGetPrerenderManager() const199 PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const {
200 return PrerenderManagerFactory::GetForProfile(
201 Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
202 }
203
IsPrerendering()204 bool PrerenderTabHelper::IsPrerendering() {
205 PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
206 if (!prerender_manager)
207 return false;
208 return prerender_manager->IsWebContentsPrerendering(web_contents(), NULL);
209 }
210
PrerenderSwappedIn()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_);
219 } else {
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();
224 }
225 }
226
WouldHavePrerenderedNextLoad(Origin origin)227 void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin) {
228 next_load_is_control_prerender_ = true;
229 next_load_origin_ = origin;
230 }
231
RecordEvent(PrerenderTabHelper::Event event) const232 void PrerenderTabHelper::RecordEvent(PrerenderTabHelper::Event event) const {
233 UMA_HISTOGRAM_ENUMERATION("Prerender.TabHelperEvent",
234 event, PrerenderTabHelper::EVENT_MAX_VALUE);
235 }
236
RecordEventIfLoggedInURL(PrerenderTabHelper::Event event,const GURL & url)237 void PrerenderTabHelper::RecordEventIfLoggedInURL(
238 PrerenderTabHelper::Event event, const GURL& url) {
239 PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
240 if (!prerender_manager)
241 return;
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(
247 url,
248 is_present_ptr,
249 lookup_succeeded_ptr,
250 base::Bind(&PrerenderTabHelper::RecordEventIfLoggedInURLResult,
251 weak_factory_.GetWeakPtr(),
252 event,
253 base::Passed(&is_present),
254 base::Passed(&lookup_succeeded)));
255 }
256
RecordEventIfLoggedInURLResult(PrerenderTabHelper::Event event,scoped_ptr<bool> is_present,scoped_ptr<bool> 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)
262 RecordEvent(event);
263 }
264
RecordPerceivedPageLoadTime(base::TimeDelta perceived_page_load_time,double fraction_plt_elapsed_at_swap_in)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)
271 return;
272
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_);
280
281 // Reset state for the next navigation.
282 navigation_type_ = NAVIGATION_TYPE_NORMAL;
283 origin_ = ORIGIN_NONE;
284 }
285
286 } // namespace prerender
287