prerender_tab_helper.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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_view_host.h"
19#include "content/public/browser/web_contents.h"
20#include "content/public/common/frame_navigate_params.h"
21
22using content::WebContents;
23
24DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper);
25
26namespace prerender {
27
28namespace {
29
30void ReportTabHelperURLSeenToLocalPredictor(
31    PrerenderManager* prerender_manager,
32    const GURL& url,
33    WebContents* web_contents) {
34  if (!prerender_manager)
35    return;
36  PrerenderLocalPredictor* local_predictor =
37      prerender_manager->local_predictor();
38  if (!local_predictor)
39    return;
40  local_predictor->OnTabHelperURLSeen(url, web_contents);
41}
42
43}  // namespace
44
45// static
46void 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,
52                                                     password_manager));
53  }
54}
55
56PrerenderTabHelper::PrerenderTabHelper(
57    content::WebContents* web_contents,
58    password_manager::PasswordManager* password_manager)
59    : content::WebContentsObserver(web_contents),
60      origin_(ORIGIN_NONE),
61      next_load_is_control_prerender_(false),
62      next_load_origin_(ORIGIN_NONE),
63      weak_factory_(this) {
64  if (password_manager) {
65    // May be NULL in testing.
66    password_manager->AddSubmissionCallback(
67        base::Bind(&PrerenderTabHelper::PasswordSubmitted,
68                   weak_factory_.GetWeakPtr()));
69  }
70
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;
76  } else {
77    navigation_type_ = NAVIGATION_TYPE_NORMAL;
78  }
79}
80
81PrerenderTabHelper::~PrerenderTabHelper() {
82}
83
84void PrerenderTabHelper::ProvisionalChangeToMainFrameUrl(
85    const GURL& url,
86    content::RenderFrameHost* render_frame_host) {
87  url_ = url;
88  RecordEvent(EVENT_MAINFRAME_CHANGE);
89  RecordEventIfLoggedInURL(EVENT_MAINFRAME_CHANGE_DOMAIN_LOGGED_IN, url);
90  PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
91  if (!prerender_manager)
92    return;
93  if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
94    return;
95  ReportTabHelperURLSeenToLocalPredictor(prerender_manager, url,
96                                         web_contents());
97}
98
99void PrerenderTabHelper::DidCommitProvisionalLoadForFrame(
100    int64 frame_id,
101    const base::string16& frame_unique_name,
102    bool is_main_frame,
103    const GURL& validated_url,
104    content::PageTransition transition_type,
105    content::RenderViewHost* render_view_host) {
106  if (!is_main_frame)
107    return;
108  RecordEvent(EVENT_MAINFRAME_COMMIT);
109  RecordEventIfLoggedInURL(EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN,
110                           validated_url);
111  url_ = validated_url;
112  PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
113  if (!prerender_manager)
114    return;
115  if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
116    return;
117  prerender_manager->RecordNavigation(validated_url);
118  ReportTabHelperURLSeenToLocalPredictor(prerender_manager, validated_url,
119                                         web_contents());
120}
121
122void PrerenderTabHelper::DidStopLoading(
123    content::RenderViewHost* render_view_host) {
124  // Compute the PPLT metric and report it in a histogram, if needed. If the
125  // page is still prerendering, record the not swapped in page load time
126  // instead.
127  if (!pplt_load_start_.is_null()) {
128    base::TimeTicks now = base::TimeTicks::Now();
129    if (IsPrerendering()) {
130      PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
131      if (prerender_manager) {
132        prerender_manager->RecordPageLoadTimeNotSwappedIn(
133            origin_, now - pplt_load_start_, url_);
134      } else {
135        NOTREACHED();
136      }
137    } else {
138      double fraction_elapsed_at_swapin = -1.0;
139      if (!actual_load_start_.is_null()) {
140        double plt = (now - actual_load_start_).InMillisecondsF();
141        if (plt > 0.0) {
142          fraction_elapsed_at_swapin = 1.0 -
143              (now - pplt_load_start_).InMillisecondsF() / plt;
144        } else {
145          fraction_elapsed_at_swapin = 1.0;
146        }
147        DCHECK_GE(fraction_elapsed_at_swapin, 0.0);
148        DCHECK_LE(fraction_elapsed_at_swapin, 1.0);
149      }
150
151      RecordPerceivedPageLoadTime(
152          now - pplt_load_start_, fraction_elapsed_at_swapin);
153    }
154  }
155
156  // Reset the PPLT metric.
157  pplt_load_start_ = base::TimeTicks();
158  actual_load_start_ = base::TimeTicks();
159}
160
161void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
162      int64 frame_id,
163      int64 parent_frame_id,
164      bool is_main_frame,
165      const GURL& validated_url,
166      bool is_error_page,
167      bool is_iframe_srcdoc,
168      content::RenderViewHost* render_view_host) {
169  if (!is_main_frame)
170    return;
171
172  // Record PPLT state for the beginning of a new navigation.
173  pplt_load_start_ = base::TimeTicks::Now();
174  actual_load_start_ = base::TimeTicks();
175
176  if (next_load_is_control_prerender_) {
177    DCHECK_EQ(NAVIGATION_TYPE_NORMAL, navigation_type_);
178    navigation_type_ = NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED;
179    origin_ = next_load_origin_;
180    next_load_is_control_prerender_ = false;
181    next_load_origin_ = ORIGIN_NONE;
182  }
183}
184
185void PrerenderTabHelper::PasswordSubmitted(const autofill::PasswordForm& form) {
186  PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
187  if (prerender_manager) {
188    prerender_manager->RecordLikelyLoginOnURL(form.origin);
189    RecordEvent(EVENT_LOGIN_ACTION_ADDED);
190    if (form.password_value.empty())
191      RecordEvent(EVENT_LOGIN_ACTION_ADDED_PW_EMPTY);
192  }
193}
194
195PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const {
196  return PrerenderManagerFactory::GetForProfile(
197      Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
198}
199
200bool PrerenderTabHelper::IsPrerendering() {
201  PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
202  if (!prerender_manager)
203    return false;
204  return prerender_manager->IsWebContentsPrerendering(web_contents(), NULL);
205}
206
207void PrerenderTabHelper::PrerenderSwappedIn() {
208  // Ensure we are not prerendering any more.
209  DCHECK_EQ(NAVIGATION_TYPE_PRERENDERED, navigation_type_);
210  DCHECK(!IsPrerendering());
211  if (pplt_load_start_.is_null()) {
212    // If we have already finished loading, report a 0 PPLT.
213    RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0);
214    DCHECK_EQ(NAVIGATION_TYPE_NORMAL, navigation_type_);
215  } else {
216    // If we have not finished loading yet, record the actual load start, and
217    // rebase the start time to now.
218    actual_load_start_ = pplt_load_start_;
219    pplt_load_start_ = base::TimeTicks::Now();
220  }
221}
222
223void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin) {
224  next_load_is_control_prerender_ = true;
225  next_load_origin_ = origin;
226}
227
228void PrerenderTabHelper::RecordEvent(PrerenderTabHelper::Event event) const {
229  UMA_HISTOGRAM_ENUMERATION("Prerender.TabHelperEvent",
230                            event, PrerenderTabHelper::EVENT_MAX_VALUE);
231}
232
233void PrerenderTabHelper::RecordEventIfLoggedInURL(
234    PrerenderTabHelper::Event event, const GURL& url) {
235  PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
236  if (!prerender_manager)
237    return;
238  scoped_ptr<bool> is_present(new bool);
239  scoped_ptr<bool> lookup_succeeded(new bool);
240  bool* is_present_ptr = is_present.get();
241  bool* lookup_succeeded_ptr = lookup_succeeded.get();
242  prerender_manager->CheckIfLikelyLoggedInOnURL(
243      url,
244      is_present_ptr,
245      lookup_succeeded_ptr,
246      base::Bind(&PrerenderTabHelper::RecordEventIfLoggedInURLResult,
247                 weak_factory_.GetWeakPtr(),
248                 event,
249                 base::Passed(&is_present),
250                 base::Passed(&lookup_succeeded)));
251}
252
253void PrerenderTabHelper::RecordEventIfLoggedInURLResult(
254    PrerenderTabHelper::Event event,
255    scoped_ptr<bool> is_present,
256    scoped_ptr<bool> lookup_succeeded) {
257  if (*lookup_succeeded && *is_present)
258    RecordEvent(event);
259}
260
261void PrerenderTabHelper::RecordPerceivedPageLoadTime(
262    base::TimeDelta perceived_page_load_time,
263    double fraction_plt_elapsed_at_swap_in) {
264  DCHECK(!IsPrerendering());
265  PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
266  if (!prerender_manager)
267    return;
268
269  // Note: it is possible for |next_load_is_control_prerender_| to be true at
270  // this point. This does not affect the classification of the current load,
271  // but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED
272  // navigation interrupts and aborts another navigation.)
273  prerender_manager->RecordPerceivedPageLoadTime(
274      origin_, navigation_type_, perceived_page_load_time,
275      fraction_plt_elapsed_at_swap_in, url_);
276
277  // Reset state for the next navigation.
278  navigation_type_ = NAVIGATION_TYPE_NORMAL;
279  origin_ = ORIGIN_NONE;
280}
281
282}  // namespace prerender
283