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