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