• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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.
GetThumbnailType(const ThumbnailScore & score)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 
ThumbnailScore()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 
ThumbnailScore(double score,bool clipping,bool top)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 
ThumbnailScore(double score,bool clipping,bool top,const Time & time)51 ThumbnailScore::ThumbnailScore(double score, bool clipping, bool top,
52                                const Time& time)
53     : boring_score(score),
54       good_clipping(clipping),
55       at_top(top),
56       load_completed(false),
57       time_at_snapshot(time),
58       redirect_hops_from_dest(0) {
59 }
60 
~ThumbnailScore()61 ThumbnailScore::~ThumbnailScore() {
62 }
63 
Equals(const ThumbnailScore & rhs) const64 bool ThumbnailScore::Equals(const ThumbnailScore& rhs) const {
65   return boring_score == rhs.boring_score &&
66       good_clipping == rhs.good_clipping &&
67       at_top == rhs.at_top &&
68       time_at_snapshot == rhs.time_at_snapshot &&
69       redirect_hops_from_dest == rhs.redirect_hops_from_dest;
70 }
71 
ToString() const72 std::string ThumbnailScore::ToString() const {
73   return base::StringPrintf(
74       "boring_score: %f, at_top %d, good_clipping %d, "
75       "load_completed: %d, "
76       "time_at_snapshot: %f, redirect_hops_from_dest: %d",
77       boring_score,
78       at_top,
79       good_clipping,
80       load_completed,
81       time_at_snapshot.ToDoubleT(),
82       redirect_hops_from_dest);
83 }
84 
ShouldReplaceThumbnailWith(const ThumbnailScore & current,const ThumbnailScore & replacement)85 bool ShouldReplaceThumbnailWith(const ThumbnailScore& current,
86                                 const ThumbnailScore& replacement) {
87   int current_type = GetThumbnailType(current);
88   int replacement_type = GetThumbnailType(replacement);
89   if (replacement_type < current_type) {
90     // If we have a better class of thumbnail, add it if it meets
91     // certain minimum boringness.
92     return replacement.boring_score <
93         ThumbnailScore::kThumbnailMaximumBoringness;
94   } else if (replacement_type == current_type) {
95     // It's much easier to do the scaling below when we're dealing with "higher
96     // is better." Then we can decrease the score by dividing by a fraction.
97     const double kThumbnailMinimumInterestingness =
98         1.0 - ThumbnailScore::kThumbnailMaximumBoringness;
99     double current_interesting_score = 1.0 - current.boring_score;
100     double replacement_interesting_score = 1.0 - replacement.boring_score;
101 
102     // Degrade the score of each thumbnail to account for how many redirects
103     // they are away from the destination. 1/(x+1) gives a scaling factor of
104     // one for x = 0, and asymptotically approaches 0 for larger values of x.
105     current_interesting_score *=
106         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 
ShouldConsiderUpdating()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