summaryrefslogtreecommitdiffstats
path: root/components/search_provider_logos/logo_cache.cc
diff options
context:
space:
mode:
authornewt@chromium.org <newt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-30 20:04:23 +0000
committernewt@chromium.org <newt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-30 20:04:23 +0000
commitcf59dd169aa864e41d6fbd32384e831e4ab80fba (patch)
tree1aa4556475e9322d829a26519f7b2f4c376fae8d /components/search_provider_logos/logo_cache.cc
parentebb3ad52433c978a914d0b387690dc98c791736a (diff)
downloadchromium_src-cf59dd169aa864e41d6fbd32384e831e4ab80fba.zip
chromium_src-cf59dd169aa864e41d6fbd32384e831e4ab80fba.tar.gz
chromium_src-cf59dd169aa864e41d6fbd32384e831e4ab80fba.tar.bz2
Add LogoTracker to fetch search providers' logos.
The LogoTracker keeps track of the logo for a search provider. The logo is downloaded, cached on disk, and periodically revalidated. This code lives inside a component (search_provider_logos) so it can be used on both Android and iOS. BUG=178922 NOTRY=true Review URL: https://codereview.chromium.org/162373002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@267314 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/search_provider_logos/logo_cache.cc')
-rw-r--r--components/search_provider_logos/logo_cache.cc235
1 files changed, 235 insertions, 0 deletions
diff --git a/components/search_provider_logos/logo_cache.cc b/components/search_provider_logos/logo_cache.cc
new file mode 100644
index 0000000..5858493
--- /dev/null
+++ b/components/search_provider_logos/logo_cache.cc
@@ -0,0 +1,235 @@
+// Copyright 2014 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 "components/search_provider_logos/logo_cache.h"
+
+#include "base/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+
+namespace {
+
+// The cached logo metadata is persisted as JSON using these keys.
+const char kSourceUrlKey[] = "url";
+const char kExpirationTimeKey[] = "expiration_time";
+const char kCanShowAfterExpirationKey[] = "can_show_after_expiration";
+const char kFingerprintKey[] = "fingerprint";
+const char kOnClickURLKey[] = "on_click_url";
+const char kAltTextKey[] = "alt_text";
+const char kMimeTypeKey[] = "mime_type";
+const char kNumBytesKey[] = "num_bytes";
+
+bool GetTimeValue(const base::DictionaryValue& dict,
+ const std::string& key,
+ base::Time* time) {
+ std::string str;
+ int64 internal_time_value;
+ if (dict.GetString(key, &str) &&
+ base::StringToInt64(str, &internal_time_value)) {
+ *time = base::Time::FromInternalValue(internal_time_value);
+ return true;
+ }
+ return false;
+}
+
+void SetTimeValue(base::DictionaryValue& dict,
+ const std::string& key,
+ const base::Time& time) {
+ int64 internal_time_value = time.ToInternalValue();
+ dict.SetString(key, base::Int64ToString(internal_time_value));
+}
+
+} // namespace
+
+namespace search_provider_logos {
+
+LogoCache::LogoCache(const base::FilePath& cache_directory)
+ : cache_directory_(cache_directory),
+ metadata_is_valid_(false) {
+ // The LogoCache can be constructed on any thread, as long as it's used
+ // on a single thread after construction.
+ thread_checker_.DetachFromThread();
+}
+
+LogoCache::~LogoCache() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void LogoCache::UpdateCachedLogoMetadata(const LogoMetadata& metadata) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(metadata_);
+ DCHECK_EQ(metadata_->fingerprint, metadata.fingerprint);
+
+ UpdateMetadata(make_scoped_ptr(new LogoMetadata(metadata)));
+ WriteMetadata();
+}
+
+const LogoMetadata* LogoCache::GetCachedLogoMetadata() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ReadMetadataIfNeeded();
+ return metadata_.get();
+}
+
+void LogoCache::SetCachedLogo(const EncodedLogo* logo) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ scoped_ptr<LogoMetadata> metadata;
+ if (logo) {
+ metadata.reset(new LogoMetadata(logo->metadata));
+ logo_num_bytes_ = static_cast<int>(logo->encoded_image->size());
+ }
+ UpdateMetadata(metadata.Pass());
+ WriteLogo(logo ? logo->encoded_image : NULL);
+}
+
+scoped_ptr<EncodedLogo> LogoCache::GetCachedLogo() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ ReadMetadataIfNeeded();
+ if (!metadata_)
+ return scoped_ptr<EncodedLogo>();
+
+ scoped_refptr<base::RefCountedString> encoded_image =
+ new base::RefCountedString();
+ if (!base::ReadFileToString(GetLogoPath(), &encoded_image->data())) {
+ UpdateMetadata(scoped_ptr<LogoMetadata>());
+ return scoped_ptr<EncodedLogo>();
+ }
+
+ if (encoded_image->size() != static_cast<size_t>(logo_num_bytes_)) {
+ // Delete corrupt metadata and logo.
+ DeleteLogoAndMetadata();
+ UpdateMetadata(scoped_ptr<LogoMetadata>());
+ return scoped_ptr<EncodedLogo>();
+ }
+
+ scoped_ptr<EncodedLogo> logo(new EncodedLogo());
+ logo->encoded_image = encoded_image;
+ logo->metadata = *metadata_;
+ return logo.Pass();
+}
+
+// static
+scoped_ptr<LogoMetadata> LogoCache::LogoMetadataFromString(
+ const std::string& str, int* logo_num_bytes) {
+ scoped_ptr<base::Value> value(base::JSONReader::Read(str));
+ base::DictionaryValue* dict;
+ if (!value || !value->GetAsDictionary(&dict))
+ return scoped_ptr<LogoMetadata>();
+
+ scoped_ptr<LogoMetadata> metadata(new LogoMetadata());
+ if (!dict->GetString(kSourceUrlKey, &metadata->source_url) ||
+ !dict->GetString(kFingerprintKey, &metadata->fingerprint) ||
+ !dict->GetString(kOnClickURLKey, &metadata->on_click_url) ||
+ !dict->GetString(kAltTextKey, &metadata->alt_text) ||
+ !dict->GetString(kMimeTypeKey, &metadata->mime_type) ||
+ !dict->GetBoolean(kCanShowAfterExpirationKey,
+ &metadata->can_show_after_expiration) ||
+ !dict->GetInteger(kNumBytesKey, logo_num_bytes) ||
+ !GetTimeValue(*dict, kExpirationTimeKey, &metadata->expiration_time)) {
+ return scoped_ptr<LogoMetadata>();
+ }
+
+ return metadata.Pass();
+}
+
+// static
+void LogoCache::LogoMetadataToString(const LogoMetadata& metadata,
+ int num_bytes,
+ std::string* str) {
+ base::DictionaryValue dict;
+ dict.SetString(kSourceUrlKey, metadata.source_url);
+ dict.SetString(kFingerprintKey, metadata.fingerprint);
+ dict.SetString(kOnClickURLKey, metadata.on_click_url);
+ dict.SetString(kAltTextKey, metadata.alt_text);
+ dict.SetString(kMimeTypeKey, metadata.mime_type);
+ dict.SetBoolean(kCanShowAfterExpirationKey,
+ metadata.can_show_after_expiration);
+ dict.SetInteger(kNumBytesKey, num_bytes);
+ SetTimeValue(dict, kExpirationTimeKey, metadata.expiration_time);
+ base::JSONWriter::Write(&dict, str);
+}
+
+base::FilePath LogoCache::GetLogoPath() {
+ return cache_directory_.Append(FILE_PATH_LITERAL("logo"));
+}
+
+base::FilePath LogoCache::GetMetadataPath() {
+ return cache_directory_.Append(FILE_PATH_LITERAL("metadata"));
+}
+
+void LogoCache::UpdateMetadata(scoped_ptr<LogoMetadata> metadata) {
+ metadata_ = metadata.Pass();
+ metadata_is_valid_ = true;
+}
+
+void LogoCache::ReadMetadataIfNeeded() {
+ if (metadata_is_valid_)
+ return;
+
+ scoped_ptr<LogoMetadata> metadata;
+ base::FilePath metadata_path = GetMetadataPath();
+ std::string str;
+ if (base::ReadFileToString(metadata_path, &str)) {
+ metadata = LogoMetadataFromString(str, &logo_num_bytes_);
+ if (!metadata) {
+ // Delete corrupt metadata and logo.
+ DeleteLogoAndMetadata();
+ }
+ }
+
+ UpdateMetadata(metadata.Pass());
+}
+
+void LogoCache::WriteMetadata() {
+ if (!EnsureCacheDirectoryExists())
+ return;
+
+ std::string str;
+ LogoMetadataToString(*metadata_, logo_num_bytes_, &str);
+ base::WriteFile(GetMetadataPath(), str.data(), static_cast<int>(str.size()));
+}
+
+void LogoCache::WriteLogo(scoped_refptr<base::RefCountedMemory> encoded_image) {
+ if (!EnsureCacheDirectoryExists())
+ return;
+
+ if (!metadata_ || !encoded_image) {
+ DeleteLogoAndMetadata();
+ return;
+ }
+
+ // To minimize the chances of ending up in an undetectably broken state:
+ // First, delete the metadata file, then update the logo file, then update the
+ // metadata file.
+ base::FilePath logo_path = GetLogoPath();
+ base::FilePath metadata_path = GetMetadataPath();
+
+ if (!base::DeleteFile(metadata_path, false))
+ return;
+
+ if (base::WriteFile(
+ logo_path,
+ encoded_image->front_as<char>(),
+ static_cast<int>(encoded_image->size())) == -1) {
+ base::DeleteFile(logo_path, false);
+ return;
+ }
+
+ WriteMetadata();
+}
+
+void LogoCache::DeleteLogoAndMetadata() {
+ base::DeleteFile(GetLogoPath(), false);
+ base::DeleteFile(GetMetadataPath(), false);
+}
+
+bool LogoCache::EnsureCacheDirectoryExists() {
+ if (base::DirectoryExists(cache_directory_))
+ return true;
+ return base::CreateDirectory(cache_directory_);
+}
+
+} // namespace search_provider_logos