• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
GetThumbnailType(bool good_clipping,bool at_top)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 
ThumbnailScore()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 
ThumbnailScore(double score,bool clipping,bool top)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 
ThumbnailScore(double score,bool clipping,bool top,const Time & time)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 
~ThumbnailScore()62 ThumbnailScore::~ThumbnailScore() {
63 }
64 
Equals(const ThumbnailScore & rhs) const65 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 
ToString() const76 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 
ShouldReplaceThumbnailWith(const ThumbnailScore & current,const ThumbnailScore & replacement)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 
ShouldConsiderUpdating()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