Home | History | Annotate | Download | only in prerender
      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/metrics/histogram.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "base/time/time.h"
     10 #include "chrome/browser/predictors/logged_in_predictor_table.h"
     11 #include "chrome/browser/prerender/prerender_histograms.h"
     12 #include "chrome/browser/prerender/prerender_local_predictor.h"
     13 #include "chrome/browser/prerender/prerender_manager.h"
     14 #include "chrome/browser/prerender/prerender_manager_factory.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "content/public/browser/navigation_details.h"
     18 #include "content/public/browser/navigation_entry.h"
     19 #include "content/public/browser/render_view_host.h"
     20 #include "content/public/browser/web_contents.h"
     21 #include "content/public/browser/web_contents_view.h"
     22 #include "content/public/common/frame_navigate_params.h"
     23 #include "skia/ext/platform_canvas.h"
     24 #include "third_party/skia/include/core/SkBitmap.h"
     25 #include "ui/gfx/rect.h"
     26 
     27 using content::WebContents;
     28 
     29 DEFINE_WEB_CONTENTS_USER_DATA_KEY(prerender::PrerenderTabHelper);
     30 
     31 namespace prerender {
     32 
     33 namespace {
     34 
     35 void ReportTabHelperURLSeenToLocalPredictor(
     36     PrerenderManager* prerender_manager,
     37     const GURL& url,
     38     WebContents* web_contents) {
     39   if (!prerender_manager)
     40     return;
     41   PrerenderLocalPredictor* local_predictor =
     42       prerender_manager->local_predictor();
     43   if (!local_predictor)
     44     return;
     45   local_predictor->OnTabHelperURLSeen(url, web_contents);
     46 }
     47 
     48 }  // namespace
     49 
     50 // Helper class to compute pixel-based stats on the paint progress
     51 // between when a prerendered page is swapped in and when the onload event
     52 // fires.
     53 class PrerenderTabHelper::PixelStats {
     54  public:
     55   explicit PixelStats(PrerenderTabHelper* tab_helper) :
     56       bitmap_web_contents_(NULL),
     57       weak_factory_(this),
     58       tab_helper_(tab_helper) {
     59   }
     60 
     61   // Reasons why we need to fetch bitmaps: either a prerender was swapped in,
     62   // or a prerendered page has finished loading.
     63   enum BitmapType {
     64       BITMAP_SWAP_IN,
     65       BITMAP_ON_LOAD
     66   };
     67 
     68   void GetBitmap(BitmapType bitmap_type, WebContents* web_contents) {
     69     if (bitmap_type == BITMAP_SWAP_IN) {
     70       bitmap_.reset();
     71       bitmap_web_contents_ = web_contents;
     72     }
     73 
     74     if (bitmap_type == BITMAP_ON_LOAD && bitmap_web_contents_ != web_contents)
     75       return;
     76 
     77     if (!web_contents || !web_contents->GetView() ||
     78         !web_contents->GetRenderViewHost()) {
     79       return;
     80     }
     81 
     82     web_contents->GetRenderViewHost()->CopyFromBackingStore(
     83         gfx::Rect(),
     84         gfx::Size(),
     85         base::Bind(&PrerenderTabHelper::PixelStats::HandleBitmapResult,
     86                    weak_factory_.GetWeakPtr(),
     87                    bitmap_type,
     88                    web_contents));
     89   }
     90 
     91  private:
     92   void HandleBitmapResult(BitmapType bitmap_type,
     93                           WebContents* web_contents,
     94                           bool succeeded,
     95                           const SkBitmap& canvas_bitmap) {
     96     scoped_ptr<SkBitmap> bitmap;
     97     if (succeeded) {
     98       // TODO(nick): This copy may now be unnecessary.
     99       bitmap.reset(new SkBitmap());
    100       canvas_bitmap.copyTo(bitmap.get(), SkBitmap::kARGB_8888_Config);
    101     }
    102 
    103     if (bitmap_web_contents_ != web_contents)
    104       return;
    105 
    106     if (bitmap_type == BITMAP_SWAP_IN)
    107       bitmap_.swap(bitmap);
    108 
    109     if (bitmap_type == BITMAP_ON_LOAD) {
    110       PrerenderManager* prerender_manager =
    111           tab_helper_->MaybeGetPrerenderManager();
    112       if (prerender_manager) {
    113         prerender_manager->RecordFractionPixelsFinalAtSwapin(
    114             web_contents, CompareBitmaps(bitmap_.get(), bitmap.get()));
    115       }
    116       bitmap_.reset();
    117       bitmap_web_contents_ = NULL;
    118     }
    119   }
    120 
    121   // Helper comparing two bitmaps of identical size.
    122   // Returns a value < 0.0 if there is an error, and otherwise, a double in
    123   // [0, 1] indicating the fraction of pixels that are the same.
    124   double CompareBitmaps(SkBitmap* bitmap1, SkBitmap* bitmap2) {
    125     if (!bitmap1 || !bitmap2) {
    126       return -2.0;
    127     }
    128     if (bitmap1->width() != bitmap2->width() ||
    129         bitmap1->height() != bitmap2->height()) {
    130       return -1.0;
    131     }
    132     int pixels = bitmap1->width() * bitmap1->height();
    133     int same_pixels = 0;
    134     for (int y = 0; y < bitmap1->height(); ++y) {
    135       for (int x = 0; x < bitmap1->width(); ++x) {
    136         if (bitmap1->getColor(x, y) == bitmap2->getColor(x, y))
    137           same_pixels++;
    138       }
    139     }
    140     return static_cast<double>(same_pixels) / static_cast<double>(pixels);
    141   }
    142 
    143   // Bitmap of what the last swapped in prerendered tab looked like at swapin,
    144   // and the WebContents that it was swapped into.
    145   scoped_ptr<SkBitmap> bitmap_;
    146   WebContents* bitmap_web_contents_;
    147 
    148   base::WeakPtrFactory<PixelStats> weak_factory_;
    149 
    150   PrerenderTabHelper* tab_helper_;
    151 };
    152 
    153 PrerenderTabHelper::PrerenderTabHelper(content::WebContents* web_contents)
    154     : content::WebContentsObserver(web_contents),
    155       weak_factory_(this) {
    156 }
    157 
    158 PrerenderTabHelper::~PrerenderTabHelper() {
    159 }
    160 
    161 void PrerenderTabHelper::ProvisionalChangeToMainFrameUrl(
    162     const GURL& url,
    163     content::RenderViewHost* render_view_host) {
    164   url_ = url;
    165   RecordEvent(EVENT_MAINFRAME_CHANGE);
    166   RecordEventIfLoggedInURL(EVENT_MAINFRAME_CHANGE_DOMAIN_LOGGED_IN, url);
    167   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
    168   if (!prerender_manager)
    169     return;
    170   if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
    171     return;
    172   prerender_manager->MarkWebContentsAsNotPrerendered(web_contents());
    173   ReportTabHelperURLSeenToLocalPredictor(prerender_manager, url,
    174                                          web_contents());
    175 }
    176 
    177 void PrerenderTabHelper::DidCommitProvisionalLoadForFrame(
    178     int64 frame_id,
    179     bool is_main_frame,
    180     const GURL& validated_url,
    181     content::PageTransition transition_type,
    182     content::RenderViewHost* render_view_host) {
    183   if (!is_main_frame)
    184     return;
    185   RecordEvent(EVENT_MAINFRAME_COMMIT);
    186   RecordEventIfLoggedInURL(EVENT_MAINFRAME_COMMIT_DOMAIN_LOGGED_IN,
    187                            validated_url);
    188   url_ = validated_url;
    189   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
    190   if (!prerender_manager)
    191     return;
    192   if (prerender_manager->IsWebContentsPrerendering(web_contents(), NULL))
    193     return;
    194   prerender_manager->RecordNavigation(validated_url);
    195   ReportTabHelperURLSeenToLocalPredictor(prerender_manager, validated_url,
    196                                          web_contents());
    197 }
    198 
    199 void PrerenderTabHelper::DidStopLoading(
    200     content::RenderViewHost* render_view_host) {
    201   // Compute the PPLT metric and report it in a histogram, if needed.
    202   // We include pages that are still prerendering and have just finished
    203   // loading -- PrerenderManager will sort this out and handle it correctly
    204   // (putting those times into a separate histogram).
    205   if (!pplt_load_start_.is_null()) {
    206     double fraction_elapsed_at_swapin = -1.0;
    207     base::TimeTicks now = base::TimeTicks::Now();
    208     if (!actual_load_start_.is_null()) {
    209       double plt = (now - actual_load_start_).InMillisecondsF();
    210       if (plt > 0.0) {
    211         fraction_elapsed_at_swapin = 1.0 -
    212             (now - pplt_load_start_).InMillisecondsF() / plt;
    213       } else {
    214         fraction_elapsed_at_swapin = 1.0;
    215       }
    216       DCHECK_GE(fraction_elapsed_at_swapin, 0.0);
    217       DCHECK_LE(fraction_elapsed_at_swapin, 1.0);
    218     }
    219     PrerenderManager::RecordPerceivedPageLoadTime(
    220         now - pplt_load_start_, fraction_elapsed_at_swapin, web_contents(),
    221         url_);
    222     if (IsPrerendered() && pixel_stats_.get())
    223       pixel_stats_->GetBitmap(PixelStats::BITMAP_ON_LOAD, web_contents());
    224   }
    225 
    226   // Reset the PPLT metric.
    227   pplt_load_start_ = base::TimeTicks();
    228   actual_load_start_ = base::TimeTicks();
    229 }
    230 
    231 void PrerenderTabHelper::DidStartProvisionalLoadForFrame(
    232       int64 frame_id,
    233       int64 parent_frame_id,
    234       bool is_main_frame,
    235       const GURL& validated_url,
    236       bool is_error_page,
    237       bool is_iframe_srcdoc,
    238       content::RenderViewHost* render_view_host) {
    239   if (is_main_frame) {
    240     // Record the beginning of a new PPLT navigation.
    241     pplt_load_start_ = base::TimeTicks::Now();
    242     actual_load_start_ = base::TimeTicks();
    243   }
    244 }
    245 
    246 void PrerenderTabHelper::DidNavigateAnyFrame(
    247       const content::LoadCommittedDetails& details,
    248       const content::FrameNavigateParams& params) {
    249   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
    250   if (params.password_form.origin.is_valid() && prerender_manager) {
    251     prerender_manager->RecordLikelyLoginOnURL(params.url);
    252     RecordEvent(EVENT_LOGIN_ACTION_ADDED);
    253     if (details.is_main_frame) {
    254       RecordEvent(EVENT_LOGIN_ACTION_ADDED_MAINFRAME);
    255       if (params.password_form.password_value.empty())
    256         RecordEvent(EVENT_LOGIN_ACTION_ADDED_MAINFRAME_PW_EMPTY);
    257     } else {
    258       RecordEvent(EVENT_LOGIN_ACTION_ADDED_SUBFRAME);
    259       if (params.password_form.password_value.empty())
    260         RecordEvent(EVENT_LOGIN_ACTION_ADDED_SUBFRAME_PW_EMPTY);
    261     }
    262   }
    263 }
    264 
    265 PrerenderManager* PrerenderTabHelper::MaybeGetPrerenderManager() const {
    266   return PrerenderManagerFactory::GetForProfile(
    267       Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
    268 }
    269 
    270 bool PrerenderTabHelper::IsPrerendering() {
    271   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
    272   if (!prerender_manager)
    273     return false;
    274   return prerender_manager->IsWebContentsPrerendering(web_contents(), NULL);
    275 }
    276 
    277 bool PrerenderTabHelper::IsPrerendered() {
    278   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
    279   if (!prerender_manager)
    280     return false;
    281   return prerender_manager->IsWebContentsPrerendered(web_contents(), NULL);
    282 }
    283 
    284 void PrerenderTabHelper::PrerenderSwappedIn() {
    285   // Ensure we are not prerendering any more.
    286   DCHECK(!IsPrerendering());
    287   if (pplt_load_start_.is_null()) {
    288     // If we have already finished loading, report a 0 PPLT.
    289     PrerenderManager::RecordPerceivedPageLoadTime(base::TimeDelta(), 1.0,
    290                                                   web_contents(), url_);
    291     PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
    292     if (prerender_manager)
    293       prerender_manager->RecordFractionPixelsFinalAtSwapin(web_contents(), 1.0);
    294   } else {
    295     // If we have not finished loading yet, record the actual load start, and
    296     // rebase the start time to now.
    297     actual_load_start_ = pplt_load_start_;
    298     pplt_load_start_ = base::TimeTicks::Now();
    299     if (pixel_stats_.get())
    300       pixel_stats_->GetBitmap(PixelStats::BITMAP_SWAP_IN, web_contents());
    301   }
    302 }
    303 
    304 void PrerenderTabHelper::RecordEvent(PrerenderTabHelper::Event event) const {
    305   UMA_HISTOGRAM_ENUMERATION("Prerender.TabHelperEvent",
    306                             event, PrerenderTabHelper::EVENT_MAX_VALUE);
    307 }
    308 
    309 void PrerenderTabHelper::RecordEventIfLoggedInURL(
    310     PrerenderTabHelper::Event event, const GURL& url) {
    311   PrerenderManager* prerender_manager = MaybeGetPrerenderManager();
    312   if (!prerender_manager)
    313     return;
    314   scoped_ptr<bool> is_present(new bool);
    315   scoped_ptr<bool> lookup_succeeded(new bool);
    316   bool* is_present_ptr = is_present.get();
    317   bool* lookup_succeeded_ptr = lookup_succeeded.get();
    318   prerender_manager->CheckIfLikelyLoggedInOnURL(
    319       url,
    320       is_present_ptr,
    321       lookup_succeeded_ptr,
    322       base::Bind(&PrerenderTabHelper::RecordEventIfLoggedInURLResult,
    323                  weak_factory_.GetWeakPtr(),
    324                  event,
    325                  base::Passed(&is_present),
    326                  base::Passed(&lookup_succeeded)));
    327 }
    328 
    329 void PrerenderTabHelper::RecordEventIfLoggedInURLResult(
    330     PrerenderTabHelper::Event event,
    331     scoped_ptr<bool> is_present,
    332     scoped_ptr<bool> lookup_succeeded) {
    333   if (*lookup_succeeded && *is_present)
    334     RecordEvent(event);
    335 }
    336 
    337 }  // namespace prerender
    338