| // 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_macros.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/prerender/prerender_histograms.h" |
| #include "chrome/browser/prerender/prerender_manager.h" |
| #include "chrome/browser/prerender/prerender_manager_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/resource_request_details.h" |
| #include "content/public/browser/web_contents.h" |
| |
| using content::WebContents; |
| |
| DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper); |
| |
| namespace prerender { |
| |
| PrerenderTabHelper::PrerenderTabHelper(content::WebContents* web_contents) |
| : content::WebContentsObserver(web_contents), |
| origin_(ORIGIN_NONE), |
| next_load_is_control_prerender_(false), |
| next_load_origin_(ORIGIN_NONE), |
| weak_factory_(this) { |
| // 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::DidGetRedirectForResourceRequest( |
| const content::ResourceRedirectDetails& details) { |
| if (details.resource_type != content::RESOURCE_TYPE_MAIN_FRAME) |
| return; |
| |
| MainFrameUrlDidChange(details.new_url); |
| } |
| |
| void PrerenderTabHelper::DidCommitProvisionalLoadForFrame( |
| content::RenderFrameHost* render_frame_host, |
| const GURL& validated_url, |
| ui::PageTransition transition_type) { |
| if (render_frame_host->GetParent()) |
| return; |
| url_ = validated_url; |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (!prerender_manager) |
| return; |
| if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL)) |
| return; |
| prerender_manager->RecordNavigation(validated_url); |
| } |
| |
| void PrerenderTabHelper::DidStopLoading() { |
| // 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 = GetTimeTicksFromPrerenderManager(); |
| 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( |
| content::RenderFrameHost* render_frame_host, |
| const GURL& validated_url, |
| bool is_error_page) { |
| if (render_frame_host->GetParent()) |
| return; |
| |
| // Record PPLT state for the beginning of a new navigation. |
| pplt_load_start_ = GetTimeTicksFromPrerenderManager(); |
| 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; |
| } |
| |
| MainFrameUrlDidChange(validated_url); |
| } |
| |
| void PrerenderTabHelper::MainFrameUrlDidChange(const GURL& url) { |
| url_ = url; |
| } |
| |
| PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const { |
| return PrerenderManagerFactory::GetForBrowserContext( |
| web_contents()->GetBrowserContext()); |
| } |
| |
| base::TimeTicks PrerenderTabHelper::GetTimeTicksFromPrerenderManager() const { |
| // Prerender browser tests should always have a PrerenderManager when mocking |
| // out tick clock. |
| PrerenderManager* prerender_manager = MaybeGetPrerenderManager(); |
| if (prerender_manager) |
| return prerender_manager->GetCurrentTimeTicks(); |
| |
| // Fall back to returning the same value as PrerenderManager would have |
| // returned in production. |
| return base::TimeTicks::Now(); |
| } |
| |
| 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_ = GetTimeTicksFromPrerenderManager(); |
| } |
| } |
| |
| void PrerenderTabHelper::WouldHavePrerenderedNextLoad(Origin origin) { |
| next_load_is_control_prerender_ = true; |
| next_load_origin_ = origin; |
| } |
| |
| 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 |