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.
5 #include "chrome/common/thumbnail_score.h"
7 #include "base/logging.h"
8 #include "base/strings/stringprintf.h"
11 using base::TimeDelta
;
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;
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
22 static int GetThumbnailType(const ThumbnailScore
& score
) {
26 if (!score
.good_clipping
)
28 if (!score
.load_completed
)
33 ThumbnailScore::ThumbnailScore()
37 load_completed(false),
38 time_at_snapshot(Time::Now()),
39 redirect_hops_from_dest(0) {
42 ThumbnailScore::ThumbnailScore(double score
, bool clipping
, bool top
)
43 : boring_score(score
),
44 good_clipping(clipping
),
46 load_completed(false),
47 time_at_snapshot(Time::Now()),
48 redirect_hops_from_dest(0) {
51 ThumbnailScore::ThumbnailScore(double score
, bool clipping
, bool top
,
53 : boring_score(score
),
54 good_clipping(clipping
),
56 load_completed(false),
57 time_at_snapshot(time
),
58 redirect_hops_from_dest(0) {
61 ThumbnailScore::~ThumbnailScore() {
64 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
;
72 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",
81 time_at_snapshot
.ToDoubleT(),
82 redirect_hops_from_dest
);
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
;
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);
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
;
118 if (current_interesting_score
< kThumbnailMinimumInterestingness
)
119 current_interesting_score
= kThumbnailMinimumInterestingness
;
120 if (replacement_interesting_score
> current_interesting_score
)
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
;
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.
138 // The current thumbnail should be updated.