106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch// Copyright (c) 2009 The Chromium Authors. All rights reserved.
206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch// Use of this source code is governed by a BSD-style license that can be
306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch// found in the LICENSE file.
406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "chrome/common/thumbnail_score.h"
606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch#include "base/logging.h"
872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/stringprintf.h"
906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
1006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochusing base::Time;
1106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochusing base::TimeDelta;
1206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
1306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochconst TimeDelta ThumbnailScore::kUpdateThumbnailTime = TimeDelta::FromDays(1);
1406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochconst double ThumbnailScore::kThumbnailMaximumBoringness = 0.94;
1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Per crbug.com/65936#c4, 91.83% of thumbnail scores are less than 0.70.
1672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst double ThumbnailScore::kThumbnailInterestingEnoughBoringness = 0.70;
1706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochconst double ThumbnailScore::kThumbnailDegradePerHour = 0.01;
1806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
1906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch// Calculates a numeric score from traits about where a snapshot was
2006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch// taken. We store the raw components in the database because I'm sure
2106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch// this will evolve and I don't want to break databases.
2206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochstatic int GetThumbnailType(bool good_clipping, bool at_top) {
2306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  if (good_clipping && at_top) {
2406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    return 0;
2506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  } else if (good_clipping && !at_top) {
2606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    return 1;
2706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  } else if (!good_clipping && at_top) {
2806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    return 2;
2906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  } else if (!good_clipping && !at_top) {
3006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    return 3;
3106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  } else {
3206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    NOTREACHED();
3306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    return -1;
3406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  }
3506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch}
3606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
3706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen MurdochThumbnailScore::ThumbnailScore()
3806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    : boring_score(1.0),
3906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      good_clipping(false),
4006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      at_top(false),
4106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      time_at_snapshot(Time::Now()),
4206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      redirect_hops_from_dest(0) {
4306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch}
4406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
4506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen MurdochThumbnailScore::ThumbnailScore(double score, bool clipping, bool top)
4606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    : boring_score(score),
4706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      good_clipping(clipping),
4806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      at_top(top),
4906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      time_at_snapshot(Time::Now()),
5006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      redirect_hops_from_dest(0) {
5106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch}
5206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
5306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen MurdochThumbnailScore::ThumbnailScore(double score, bool clipping, bool top,
5406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch                               const Time& time)
5506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    : boring_score(score),
5606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      good_clipping(clipping),
5706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      at_top(top),
5806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      time_at_snapshot(time),
5906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      redirect_hops_from_dest(0) {
6006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch}
6106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
6206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen MurdochThumbnailScore::~ThumbnailScore() {
6306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch}
6406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
6506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochbool ThumbnailScore::Equals(const ThumbnailScore& rhs) const {
6606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  // When testing equality we use ToTimeT() because that's the value
6706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  // stuck in the SQL database, so we need to test equivalence with
6806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  // that lower resolution.
6906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  return boring_score == rhs.boring_score &&
7006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      good_clipping == rhs.good_clipping &&
7106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      at_top == rhs.at_top &&
7206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      time_at_snapshot.ToTimeT() == rhs.time_at_snapshot.ToTimeT() &&
7306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      redirect_hops_from_dest == rhs.redirect_hops_from_dest;
7406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch}
7506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
7672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstd::string ThumbnailScore::ToString() const {
7772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return StringPrintf("boring_score: %f, at_top %d, good_clipping %d, "
7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                      "time_at_snapshot: %f, redirect_hops_from_dest: %d",
7972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                      boring_score,
8072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                      at_top,
8172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                      good_clipping,
8272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                      time_at_snapshot.ToDoubleT(),
8372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                      redirect_hops_from_dest);
8472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
8572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
8606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdochbool ShouldReplaceThumbnailWith(const ThumbnailScore& current,
8706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch                                const ThumbnailScore& replacement) {
8806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  int current_type = GetThumbnailType(current.good_clipping, current.at_top);
8906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  int replacement_type = GetThumbnailType(replacement.good_clipping,
9006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch                                          replacement.at_top);
9106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  if (replacement_type < current_type) {
9206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // If we have a better class of thumbnail, add it if it meets
9306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // certain minimum boringness.
9406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    return replacement.boring_score <
9506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch        ThumbnailScore::kThumbnailMaximumBoringness;
9606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  } else if (replacement_type == current_type) {
9706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // It's much easier to do the scaling below when we're dealing with "higher
9806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // is better." Then we can decrease the score by dividing by a fraction.
9906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    const double kThumbnailMinimumInterestingness =
10006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch        1.0 - ThumbnailScore::kThumbnailMaximumBoringness;
10106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    double current_interesting_score = 1.0 - current.boring_score;
10206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    double replacement_interesting_score = 1.0 - replacement.boring_score;
10306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
10406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // Degrade the score of each thumbnail to account for how many redirects
10506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // they are away from the destination. 1/(x+1) gives a scaling factor of
10606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // one for x = 0, and asymptotically approaches 0 for larger values of x.
10706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    current_interesting_score *=
10806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch        1.0 / (current.redirect_hops_from_dest + 1);
10906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    replacement_interesting_score *=
11006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch        1.0 / (replacement.redirect_hops_from_dest + 1);
11106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
11206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // Degrade the score and prefer the newer one based on how long apart the
11306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // two thumbnails were taken. This means we'll eventually replace an old
11406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    // good one with a new worse one assuming enough time has passed.
11506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    TimeDelta time_between_thumbnails =
11606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch        replacement.time_at_snapshot - current.time_at_snapshot;
11706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    current_interesting_score -= time_between_thumbnails.InHours() *
11806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch         ThumbnailScore::kThumbnailDegradePerHour;
11906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
12006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    if (current_interesting_score < kThumbnailMinimumInterestingness)
12106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      current_interesting_score = kThumbnailMinimumInterestingness;
12206741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch    if (replacement_interesting_score > current_interesting_score)
12306741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      return true;
12406741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  }
12506741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch
12606741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  // If the current thumbnail doesn't meet basic boringness
12706741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  // requirements, but the replacement does, always replace the
12806741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  // current one even if we're using a worse thumbnail type.
12906741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch  return current.boring_score >= ThumbnailScore::kThumbnailMaximumBoringness &&
13006741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch      replacement.boring_score < ThumbnailScore::kThumbnailMaximumBoringness;
13106741cbc25cd4227a9fba40dfd0273bfcc1a587aBen Murdoch}
13272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
13372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool ThumbnailScore::ShouldConsiderUpdating() {
13472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  const TimeDelta time_elapsed = Time::Now() - time_at_snapshot;
13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Consider the current thumbnail to be new and interesting enough if
13672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // the following critera are met.
13772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  const bool new_and_interesting_enough =
13872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      (time_elapsed < kUpdateThumbnailTime &&
13972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen       good_clipping && at_top &&
14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen       boring_score < kThumbnailInterestingEnoughBoringness);
14172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // We want to generate a new thumbnail when the current thumbnail is
14272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // sufficiently old or uninteresting.
14372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return !new_and_interesting_enough;
14472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
145