Home | History | Annotate | Download | only in common
      1 // Copyright 2014 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 "components/history/core/common/thumbnail_score.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/strings/stringprintf.h"
      9 
     10 using base::Time;
     11 using base::TimeDelta;
     12 
     13 const int64 ThumbnailScore::kUpdateThumbnailTimeDays = 1;
     14 const double ThumbnailScore::kThumbnailMaximumBoringness = 0.94;
     15 const double ThumbnailScore::kThumbnailDegradePerHour = 0.01;
     16 const double ThumbnailScore::kTooWideAspectRatio = 2.0;
     17 
     18 // Calculates a numeric score from traits about where a snapshot was
     19 // taken. The lower the better. We store the raw components in the
     20 // database because I'm sure this will evolve and I don't want to break
     21 // databases.
     22 static int GetThumbnailType(const ThumbnailScore& score) {
     23   int type = 0;
     24   if (!score.at_top)
     25     type += 1;
     26   if (!score.good_clipping)
     27     type += 2;
     28   if (!score.load_completed)
     29     type += 3;
     30   return type;
     31 }
     32 
     33 ThumbnailScore::ThumbnailScore()
     34     : boring_score(1.0),
     35       good_clipping(false),
     36       at_top(false),
     37       load_completed(false),
     38       time_at_snapshot(Time::Now()),
     39       redirect_hops_from_dest(0) {
     40 }
     41 
     42 ThumbnailScore::ThumbnailScore(double score, bool clipping, bool top)
     43     : boring_score(score),
     44       good_clipping(clipping),
     45       at_top(top),
     46       load_completed(false),
     47       time_at_snapshot(Time::Now()),
     48       redirect_hops_from_dest(0) {
     49 }
     50 
     51 ThumbnailScore::ThumbnailScore(double score,
     52                                bool clipping,
     53                                bool top,
     54                                const Time& time)
     55     : boring_score(score),
     56       good_clipping(clipping),
     57       at_top(top),
     58       load_completed(false),
     59       time_at_snapshot(time),
     60       redirect_hops_from_dest(0) {
     61 }
     62 
     63 ThumbnailScore::~ThumbnailScore() {
     64 }
     65 
     66 bool ThumbnailScore::Equals(const ThumbnailScore& rhs) const {
     67   return boring_score == rhs.boring_score &&
     68          good_clipping == rhs.good_clipping && at_top == rhs.at_top &&
     69          time_at_snapshot == rhs.time_at_snapshot &&
     70          redirect_hops_from_dest == rhs.redirect_hops_from_dest;
     71 }
     72 
     73 std::string ThumbnailScore::ToString() const {
     74   return base::StringPrintf(
     75       "boring_score: %f, at_top %d, good_clipping %d, "
     76       "load_completed: %d, "
     77       "time_at_snapshot: %f, redirect_hops_from_dest: %d",
     78       boring_score,
     79       at_top,
     80       good_clipping,
     81       load_completed,
     82       time_at_snapshot.ToDoubleT(),
     83       redirect_hops_from_dest);
     84 }
     85 
     86 bool ShouldReplaceThumbnailWith(const ThumbnailScore& current,
     87                                 const ThumbnailScore& replacement) {
     88   int current_type = GetThumbnailType(current);
     89   int replacement_type = GetThumbnailType(replacement);
     90   if (replacement_type < current_type) {
     91     // If we have a better class of thumbnail, add it if it meets
     92     // certain minimum boringness.
     93     return replacement.boring_score <
     94            ThumbnailScore::kThumbnailMaximumBoringness;
     95   } else if (replacement_type == current_type) {
     96     // It's much easier to do the scaling below when we're dealing with "higher
     97     // is better." Then we can decrease the score by dividing by a fraction.
     98     const double kThumbnailMinimumInterestingness =
     99         1.0 - ThumbnailScore::kThumbnailMaximumBoringness;
    100     double current_interesting_score = 1.0 - current.boring_score;
    101     double replacement_interesting_score = 1.0 - replacement.boring_score;
    102 
    103     // Degrade the score of each thumbnail to account for how many redirects
    104     // they are away from the destination. 1/(x+1) gives a scaling factor of
    105     // one for x = 0, and asymptotically approaches 0 for larger values of x.
    106     current_interesting_score *= 1.0 / (current.redirect_hops_from_dest + 1);
    107     replacement_interesting_score *=
    108         1.0 / (replacement.redirect_hops_from_dest + 1);
    109 
    110     // Degrade the score and prefer the newer one based on how long apart the
    111     // two thumbnails were taken. This means we'll eventually replace an old
    112     // good one with a new worse one assuming enough time has passed.
    113     TimeDelta time_between_thumbnails =
    114         replacement.time_at_snapshot - current.time_at_snapshot;
    115     current_interesting_score -= time_between_thumbnails.InHours() *
    116                                  ThumbnailScore::kThumbnailDegradePerHour;
    117 
    118     if (current_interesting_score < kThumbnailMinimumInterestingness)
    119       current_interesting_score = kThumbnailMinimumInterestingness;
    120     if (replacement_interesting_score > current_interesting_score)
    121       return true;
    122   }
    123 
    124   // If the current thumbnail doesn't meet basic boringness
    125   // requirements, but the replacement does, always replace the
    126   // current one even if we're using a worse thumbnail type.
    127   return current.boring_score >= ThumbnailScore::kThumbnailMaximumBoringness &&
    128          replacement.boring_score < ThumbnailScore::kThumbnailMaximumBoringness;
    129 }
    130 
    131 bool ThumbnailScore::ShouldConsiderUpdating() {
    132   const TimeDelta time_elapsed = Time::Now() - time_at_snapshot;
    133   if (time_elapsed < TimeDelta::FromDays(kUpdateThumbnailTimeDays) &&
    134       good_clipping && at_top && load_completed) {
    135     // The current thumbnail is new and has good properties.
    136     return false;
    137   }
    138   // The current thumbnail should be updated.
    139   return true;
    140 }
    141