| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/prerender/prerender_tab_helper.h" |
| |
| #include "base/bind.h" |
| #include "base/metrics/histogram.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/prerender/prerender_histograms.h" |
| #include "chrome/browser/prerender/prerender_local_predictor.h" |
| #include "chrome/browser/prerender/prerender_manager.h" |
| #include "chrome/browser/prerender/prerender_manager_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "components/password_manager/core/browser/password_manager.h" |
| #include "content/public/browser/navigation_details.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/frame_navigate_params.h" |
| |
| using content::WebContents; |
| |
| DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper); |
| |
| namespace prerender { |
| |
| namespace { |
| |
| void ReportTabHelperURLSeenToLocalPredictor( |
| PrerenderManager* prerender_manager, |
| const GURL& url, |
| WebContents* web_contents) { |
| if (!prerender_manager) |
| return; |
| PrerenderLocalPredictor* local_predictor = |
| prerender_manager->local_predictor(); |
| if (!local_predictor) |
| return; |
| local_predictor->OnTabHelperURLSeen(url, web_contents); |
| } |
| |
| } // namespace |
| |
| // static |
| void PrerenderTabHelper::CreateForWebContentsWithPasswordManager( |
| content::WebContents* web_contents, |
| password_manager::PasswordManager* password_manager) { |
| if (!FromWebContents(web_contents)) { |
| web_contents->SetUserData(UserDataKey(), |
| new PrerenderTabHelper(web_contents, |
| password_manager)); |
| } |
| } |
| |
| PrerenderTabHelper::PrerenderTabHelper( |
| content::WebContents* web_contents, |
| password_manager::PasswordManager* password_manager) |
| : content::WebContentsObserver(web_contents), |
| origin_(ORIGIN_NONE), |
| next_load_is_control_prerender_(false), |
| next_load_origin_(ORIGIN_NONE), |
| weak_factory_(this) { |
| if (password_manager) { |
| // May be NULL in testing. |
| password_manager->AddSubmissionCallback( |
| base::Bind(&PrerenderTabHelper::PasswordSubmitted, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| // Determine if this is a prerender. |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (prerender_manager && |
| prerender_manager->IsWebContentsPrerendering(web_contents, &origin_)) { |
| navigation_type_ = NAVIGATION_TYPE_PRERENDERED; |
| } else { |
| navigation_type_ = NAVIGATION_TYPE_NORMAL; |
| } |
| } |
| |
| PrerenderTabHelper::~PrerenderTabHelper() { |
| } |
| |
| void PrerenderTabHelper::ProvisionalChangeToMainFrameUrl( |
| const GURL& url, |
| content::RenderFrameHost* render_frame_host) { |
| url_ = url; |
| RecordEvent(EVENT_MAINFRAME_CHANGE); |
| RecordEventIfLoggedInURL(EVENT_MAINFRAME_CHANGE_DOMAIN_LOGGED_IN, url); |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (!prerender_manager) |
| return; |
| if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL)) |
| return; |
| ReportTabHelperURLSeenToLocalPredictor(prerender_manager, url, |
| web_contents()); |
| } |
| |
| void PrerenderTabHelper::DidCommitProvisionalLoadForFrame( |
| int64 frame_id, |
| const base::string16& frame_unique_name, |
| bool is_main_frame, |
| const GURL& validated_url, |
| content::PageTransition transition_type, |
| content::RenderViewHost* render_view_host) { |
| if (!is_main_frame) |
| return; |
| RecordEvent(EVENT_MAINFRAME_COMMIT); |
| RecordEventIfLoggedInURL(EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN, |
| validated_url); |
| url_ = validated_url; |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (!prerender_manager) |
| return; |
| if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL)) |
| return; |
| prerender_manager->RecordNavigation(validated_url); |
| ReportTabHelperURLSeenToLocalPredictor(prerender_manager, validated_url, |
| web_contents()); |
| } |
| |
| void PrerenderTabHelper::DidStopLoading( |
| content::RenderViewHost* render_view_host) { |
| // Compute the PPLT metric and report it in a histogram, if needed. If the |
| // page is still prerendering, record the not swapped in page load time |
| // instead. |
| if (!pplt_load_start_.is_null()) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| if (IsPrerendering()) { |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (prerender_manager) { |
| prerender_manager->RecordPageLoadTimeNotSwappedIn( |
| origin_, now - pplt_load_start_, url_); |
| } else { |
| NOTREACHED(); |
| } |
| } else { |
| double fraction_elapsed_at_swapin = -1.0; |
| if (!actual_load_start_.is_null()) { |
| double plt = (now - actual_load_start_).InMillisecondsF(); |
| if (plt > 0.0) { |
| fraction_elapsed_at_swapin = 1.0 - |
| (now - pplt_load_start_).InMillisecondsF() / plt; |
| } else { |
| fraction_elapsed_at_swapin = 1.0; |
| } |
| DCHECK_GE(fraction_elapsed_at_swapin, 0.0); |
| DCHECK_LE(fraction_elapsed_at_swapin, 1.0); |
| } |
| |
| RecordPerceivedPageLoadTime( |
| now - pplt_load_start_, fraction_elapsed_at_swapin); |
| } |
| } |
| |
| // Reset the PPLT metric. |
| pplt_load_start_ = base::TimeTicks(); |
| actual_load_start_ = base::TimeTicks(); |
| } |
| |
| void PrerenderTabHelper::DidStartProvisionalLoadForFrame( |
| int64 frame_id, |
| int64 parent_frame_id, |
| bool is_main_frame, |
| const GURL& validated_url, |
| bool is_error_page, |
| bool is_iframe_srcdoc, |
| content::RenderViewHost* render_view_host) { |
| if (!is_main_frame) |
| return; |
| |
| // Record PPLT state for the beginning of a new navigation. |
| pplt_load_start_ = base::TimeTicks::Now(); |
| actual_load_start_ = base::TimeTicks(); |
| |
| if (next_load_is_control_prerender_) { |
| DCHECK_EQ(NAVIGATION_TYPE_NORMAL, navigation_type_); |
| navigation_type_ = NAVIGATION_TYPE_WOULD_HAVE_BEEN_PRERENDERED; |
| origin_ = next_load_origin_; |
| next_load_is_control_prerender_ = false; |
| next_load_origin_ = ORIGIN_NONE; |
| } |
| } |
| |
| void PrerenderTabHelper::PasswordSubmitted(const autofill::PasswordForm& form) { |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (prerender_manager) { |
| prerender_manager->RecordLikelyLoginOnURL(form.origin); |
| RecordEvent(EVENT_LOGIN_ACTION_ADDED); |
| if (form.password_value.empty()) |
| RecordEvent(EVENT_LOGIN_ACTION_ADDED_PW_EMPTY); |
| } |
| } |
| |
| PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const { |
| return PrerenderManagerFactory::GetForProfile( |
| Profile::FromBrowserContext(web_contents()->GetBrowserContext())); |
| } |
| |
| bool PrerenderTabHelper::IsPrerendering() { |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (!prerender_manager) |
| return false; |
| return prerender_manager->IsWebContentsPrerendering(web_contents(), NULL); |
| } |
| |
| void PrerenderTabHelper::PrerenderSwappedIn() { |
| // Ensure we are not prerendering any more. |
| DCHECK_EQ(NAVIGATION_TYPE_PRERENDERED, navigation_type_); |
| DCHECK(!IsPrerendering()); |
| if (pplt_load_start_.is_null()) { |
| // If we have already finished loading, report a 0 PPLT. |
| RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0); |
| DCHECK_EQ(NAVIGATION_TYPE_NORMAL, navigation_type_); |
| } else { |
| // If we have not finished loading yet, record the actual load start, and |
| // rebase the start time to now. |
| actual_load_start_ = pplt_load_start_; |
| pplt_load_start_ = base::TimeTicks::Now(); |
| } |
| } |
| |
| void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin) { |
| next_load_is_control_prerender_ = true; |
| next_load_origin_ = origin; |
| } |
| |
| void PrerenderTabHelper::RecordEvent(PrerenderTabHelper::Event event) const { |
| UMA_HISTOGRAM_ENUMERATION("Prerender.TabHelperEvent", |
| event, PrerenderTabHelper::EVENT_MAX_VALUE); |
| } |
| |
| void PrerenderTabHelper::RecordEventIfLoggedInURL( |
| PrerenderTabHelper::Event event, const GURL& url) { |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (!prerender_manager) |
| return; |
| scoped_ptr<bool> is_present(new bool); |
| scoped_ptr<bool> lookup_succeeded(new bool); |
| bool* is_present_ptr = is_present.get(); |
| bool* lookup_succeeded_ptr = lookup_succeeded.get(); |
| prerender_manager->CheckIfLikelyLoggedInOnURL( |
| url, |
| is_present_ptr, |
| lookup_succeeded_ptr, |
| base::Bind(&PrerenderTabHelper::RecordEventIfLoggedInURLResult, |
| weak_factory_.GetWeakPtr(), |
| event, |
| base::Passed(&is_present), |
| base::Passed(&lookup_succeeded))); |
| } |
| |
| void PrerenderTabHelper::RecordEventIfLoggedInURLResult( |
| PrerenderTabHelper::Event event, |
| scoped_ptr<bool> is_present, |
| scoped_ptr<bool> lookup_succeeded) { |
| if (*lookup_succeeded && *is_present) |
| RecordEvent(event); |
| } |
| |
| void PrerenderTabHelper::RecordPerceivedPageLoadTime( |
| base::TimeDelta perceived_page_load_time, |
| double fraction_plt_elapsed_at_swap_in) { |
| DCHECK(!IsPrerendering()); |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (!prerender_manager) |
| return; |
| |
| // Note: it is possible for |next_load_is_control_prerender_| to be true at |
| // this point. This does not affect the classification of the current load, |
| // but only the next load. (This occurs if a WOULD_HAVE_BEEN_PRERENDERED |
| // navigation interrupts and aborts another navigation.) |
| prerender_manager->RecordPerceivedPageLoadTime( |
| origin_, navigation_type_, perceived_page_load_time, |
| fraction_plt_elapsed_at_swap_in, url_); |
| |
| // Reset state for the next navigation. |
| navigation_type_ = NAVIGATION_TYPE_NORMAL; |
| origin_ = ORIGIN_NONE; |
| } |
| |
| } // namespace prerender |