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