blob: c599c20f8d952d3b7bb16dbc115b7d087fcbf537 [file] [log] [blame]
// 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/browser/thumbnail_store.h"
#include <string.h>
#include "base/basictypes.h"
#include "base/pickle.h"
#include "base/file_util.h"
#include "base/gfx/jpeg_codec.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/thumbnail_score.h"
#include "googleurl/src/gurl.h"
#include "third_party/skia/include/core/SkBitmap.h"
ThumbnailStore::ThumbnailStore() : cache_(NULL), cache_initialized_(false) {
}
ThumbnailStore::~ThumbnailStore() {
}
void ThumbnailStore::Init(const FilePath& file_path) {
file_path_ = file_path;
g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
NewRunnableMethod(this, &ThumbnailStore::GetAllThumbnailsFromDisk,
file_path_, MessageLoop::current()));
}
void ThumbnailStore::GetAllThumbnailsFromDisk(FilePath filepath,
MessageLoop* cb_loop) {
ThumbnailStore::Cache* cache = new ThumbnailStore::Cache;
// Create the specified directory if it does not exist.
if (!file_util::DirectoryExists(filepath)) {
file_util::CreateDirectory(filepath);
} else {
// Walk the directory and read the thumbnail data from disk.
FilePath path;
GURL url;
RefCountedBytes* data = new RefCountedBytes;
ThumbnailScore score;
file_util::FileEnumerator fenum(filepath, false,
file_util::FileEnumerator::FILES);
while (!(path = fenum.Next()).empty()) {
if (GetPageThumbnailFromDisk(path, &url, data, &score))
(*cache)[url] = std::make_pair(data, score);
}
}
cb_loop->PostTask(FROM_HERE,
NewRunnableMethod(this, &ThumbnailStore::OnDiskDataAvailable, cache));
}
bool ThumbnailStore::GetPageThumbnailFromDisk(const FilePath& file,
GURL* url,
RefCountedBytes* data,
ThumbnailScore* score) const {
int64 file_size;
if (!file_util::GetFileSize(file, &file_size))
return false;
// Read the file into a buffer.
std::vector<char> file_data;
file_data.resize(static_cast<unsigned int>(file_size));
if (file_util::ReadFile(file, &file_data[0],
static_cast<int>(file_size)) == -1)
return false;
// Unpack the url, ThumbnailScore and JPEG size from the buffer.
std::string url_string;
unsigned int jpeg_len;
void* iter = NULL;
Pickle packed(&file_data[0], static_cast<int>(file_size));
if (!packed.ReadString(&iter, &url_string) ||
!UnpackScore(score, packed, iter) ||
!packed.ReadUInt32(&iter, &jpeg_len))
return false;
// Store the url to the out parameter.
GURL temp_url(url_string);
url->Swap(&temp_url);
// Unpack the JPEG data from the buffer.
const char* jpeg_data = NULL;
int out_len;
if (!packed.ReadData(&iter, &jpeg_data, &out_len) ||
out_len != jpeg_len)
return false;
// Copy jpeg data to the out parameter.
data->data.resize(jpeg_len);
memcpy(&data->data[0], jpeg_data, jpeg_len);
return true;
}
void ThumbnailStore::OnDiskDataAvailable(ThumbnailStore::Cache* cache) {
if (cache) {
cache_.reset(cache);
cache_initialized_ = true;
}
}
bool ThumbnailStore::SetPageThumbnail(const GURL& url,
const SkBitmap& thumbnail,
const ThumbnailScore& score,
bool write_to_disk) {
if (!cache_initialized_)
return false;
// If a thumbnail already exists, check if it should be replaced.
if (cache_->find(url) != cache_->end() &&
!ShouldReplaceThumbnailWith((*cache_)[url].second, score))
return true;
base::TimeTicks encode_start = base::TimeTicks::Now();
// Encode the SkBitmap to jpeg and add to cache.
RefCountedBytes* jpeg_data = new RefCountedBytes;
SkAutoLockPixels thumbnail_lock(thumbnail);
bool encoded = JPEGCodec::Encode(
reinterpret_cast<unsigned char*>(thumbnail.getAddr32(0, 0)),
JPEGCodec::FORMAT_BGRA, thumbnail.width(),
thumbnail.height(),
static_cast<int>(thumbnail.rowBytes()), 90,
&jpeg_data->data);
base::TimeDelta delta = base::TimeTicks::Now() - encode_start;
HISTOGRAM_TIMES("Thumbnail.Encode", delta);
if (!encoded)
return false;
// Update the cache_ with the new thumbnail.
(*cache_)[url] = std::make_pair(jpeg_data, score);
// Write the new thumbnail data to disk in the background on file_thread.
if (write_to_disk) {
g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
NewRunnableMethod(this, &ThumbnailStore::WriteThumbnailToDisk, url));
}
return true;
}
bool ThumbnailStore::WriteThumbnailToDisk(const GURL& url) const {
// Thumbnail data will be stored in a file named url.host().
FilePath file = file_path_.AppendASCII(url.host());
Pickle packed;
scoped_refptr<RefCountedBytes> data((*cache_)[url].first);
ThumbnailScore score = (*cache_)[url].second;
// Pack the url, ThumbnailScore, and the JPEG data.
packed.WriteString(url.spec());
PackScore(score, &packed);
packed.WriteUInt32(data->data.size());
packed.WriteData(reinterpret_cast<char*>(&data->data[0]), data->data.size());
// Write the packed data to a file.
file_util::Delete(file, false);
return file_util::WriteFile(file,
reinterpret_cast<const char*>(packed.data()),
packed.size()) != -1;
}
bool ThumbnailStore::GetPageThumbnail(const GURL& url, RefCountedBytes** data) {
if (!cache_initialized_ ||
cache_->find(url) == cache_->end())
return false;
*data = (*cache_)[url].first;
(*data)->AddRef();
return true;
}
void ThumbnailStore::PackScore(const ThumbnailScore& score,
Pickle* packed) const {
// Pack the contents of the given ThumbnailScore into the given Pickle.
packed->WriteData(reinterpret_cast<const char*>(&score.boring_score),
sizeof(score.boring_score));
packed->WriteBool(score.at_top);
packed->WriteBool(score.good_clipping);
packed->WriteInt64(score.time_at_snapshot.ToInternalValue());
}
bool ThumbnailStore::UnpackScore(ThumbnailScore* score, const Pickle& packed,
void*& iter) const {
// Unpack a ThumbnailScore from the given Pickle and iterator.
const char* boring = NULL;
int out_len;
int64 us;
if (!packed.ReadData(&iter, &boring, &out_len) ||
!packed.ReadBool(&iter, &score->at_top) ||
!packed.ReadBool(&iter, &score->good_clipping) ||
!packed.ReadInt64(&iter, &us))
return false;
if (out_len != sizeof(score->boring_score))
return false;
memcpy(&score->boring_score, boring, sizeof(score->boring_score));
score->time_at_snapshot = base::Time::FromInternalValue(us);
return true;
}