summaryrefslogtreecommitdiffstats
path: root/chrome/common/thumbnail_score.cc
blob: dc7856d101be598c7bfc4bbf2a0a5c415a8f5e46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/thumbnail_score.h"

#include "base/logging.h"

using base::Time;
using base::TimeDelta;

const TimeDelta ThumbnailScore::kUpdateThumbnailTime = TimeDelta::FromDays(1);
const double ThumbnailScore::kThumbnailMaximumBoringness = 0.94;
const double ThumbnailScore::kThumbnailDegradePerHour = 0.01;

// Calculates a numeric score from traits about where a snapshot was
// taken. We store the raw components in the database because I'm sure
// this will evolve and I don't want to break databases.
static int GetThumbnailType(bool good_clipping, bool at_top) {
  if (good_clipping && at_top) {
    return 0;
  } else if (good_clipping && !at_top) {
    return 1;
  } else if (!good_clipping && at_top) {
    return 2;
  } else if (!good_clipping && !at_top) {
    return 3;
  } else {
    NOTREACHED();
    return -1;
  }
}

ThumbnailScore::ThumbnailScore()
    : boring_score(1.0),
      good_clipping(false),
      at_top(false),
      time_at_snapshot(Time::Now()),
      redirect_hops_from_dest(0) {
}

ThumbnailScore::ThumbnailScore(double score, bool clipping, bool top)
    : boring_score(score),
      good_clipping(clipping),
      at_top(top),
      time_at_snapshot(Time::Now()),
      redirect_hops_from_dest(0) {
}

ThumbnailScore::ThumbnailScore(double score, bool clipping, bool top,
                               const Time& time)
    : boring_score(score),
      good_clipping(clipping),
      at_top(top),
      time_at_snapshot(time),
      redirect_hops_from_dest(0) {
}

ThumbnailScore::~ThumbnailScore() {
}

bool ThumbnailScore::Equals(const ThumbnailScore& rhs) const {
  // When testing equality we use ToTimeT() because that's the value
  // stuck in the SQL database, so we need to test equivalence with
  // that lower resolution.
  return boring_score == rhs.boring_score &&
      good_clipping == rhs.good_clipping &&
      at_top == rhs.at_top &&
      time_at_snapshot.ToTimeT() == rhs.time_at_snapshot.ToTimeT() &&
      redirect_hops_from_dest == rhs.redirect_hops_from_dest;
}

bool ShouldReplaceThumbnailWith(const ThumbnailScore& current,
                                const ThumbnailScore& replacement) {
  int current_type = GetThumbnailType(current.good_clipping, current.at_top);
  int replacement_type = GetThumbnailType(replacement.good_clipping,
                                          replacement.at_top);
  if (replacement_type < current_type) {
    // If we have a better class of thumbnail, add it if it meets
    // certain minimum boringness.
    return replacement.boring_score <
        ThumbnailScore::kThumbnailMaximumBoringness;
  } else if (replacement_type == current_type) {
    // It's much easier to do the scaling below when we're dealing with "higher
    // is better." Then we can decrease the score by dividing by a fraction.
    const double kThumbnailMinimumInterestingness =
        1.0 - ThumbnailScore::kThumbnailMaximumBoringness;
    double current_interesting_score = 1.0 - current.boring_score;
    double replacement_interesting_score = 1.0 - replacement.boring_score;

    // Degrade the score of each thumbnail to account for how many redirects
    // they are away from the destination. 1/(x+1) gives a scaling factor of
    // one for x = 0, and asymptotically approaches 0 for larger values of x.
    current_interesting_score *=
        1.0 / (current.redirect_hops_from_dest + 1);
    replacement_interesting_score *=
        1.0 / (replacement.redirect_hops_from_dest + 1);

    // Degrade the score and prefer the newer one based on how long apart the
    // two thumbnails were taken. This means we'll eventually replace an old
    // good one with a new worse one assuming enough time has passed.
    TimeDelta time_between_thumbnails =
        replacement.time_at_snapshot - current.time_at_snapshot;
    current_interesting_score -= time_between_thumbnails.InHours() *
         ThumbnailScore::kThumbnailDegradePerHour;

    if (current_interesting_score < kThumbnailMinimumInterestingness)
      current_interesting_score = kThumbnailMinimumInterestingness;
    if (replacement_interesting_score > current_interesting_score)
      return true;
  }

  // If the current thumbnail doesn't meet basic boringness
  // requirements, but the replacement does, always replace the
  // current one even if we're using a worse thumbnail type.
  return current.boring_score >= ThumbnailScore::kThumbnailMaximumBoringness &&
      replacement.boring_score < ThumbnailScore::kThumbnailMaximumBoringness;
}