• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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