// 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 #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 file_data; file_data.resize(static_cast(file_size)); if (file_util::ReadFile(file, &file_data[0], static_cast(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(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 != static_cast(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(thumbnail.getAddr32(0, 0)), JPEGCodec::FORMAT_BGRA, thumbnail.width(), thumbnail.height(), static_cast(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 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(&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(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(&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; }