1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/app_list/search/common/webservice_cache.h" 6 7#include "base/strings/string_number_conversions.h" 8#include "base/values.h" 9#include "content/public/browser/browser_context.h" 10 11namespace app_list { 12namespace { 13 14const unsigned int kWebserviceCacheMaxSize = 1000; 15const unsigned int kWebserviceCacheTimeLimitInMinutes = 1; 16 17const char kKeyResultTime[] = "time"; 18const char kKeyResult[] = "result"; 19 20const char kWebstoreQueryPrefix[] = "webstore:"; 21const char kPeopleQueryPrefix[] = "people:"; 22 23} // namespace 24 25void WebserviceCache::CacheDeletor::operator()(Payload& payload) { 26 delete payload.result; 27} 28 29WebserviceCache::WebserviceCache(content::BrowserContext* context) 30 : cache_(Cache::NO_AUTO_EVICT), 31 cache_loaded_(false) { 32 const char kStoreDataFileName[] = "Webservice Search Cache"; 33 const base::FilePath data_file = 34 context->GetPath().AppendASCII(kStoreDataFileName); 35 data_store_ = new DictionaryDataStore(data_file); 36 data_store_->Load(base::Bind(&WebserviceCache::OnCacheLoaded, AsWeakPtr())); 37} 38 39WebserviceCache::~WebserviceCache() { 40} 41 42const CacheResult WebserviceCache::Get(QueryType type, 43 const std::string& query) { 44 std::string typed_query = PrependType(type, query); 45 Cache::iterator iter = cache_.Get(typed_query); 46 if (iter != cache_.end()) { 47 if (base::Time::Now() - iter->second.time <= 48 base::TimeDelta::FromMinutes(kWebserviceCacheTimeLimitInMinutes)) { 49 return std::make_pair(FRESH, iter->second.result); 50 } else { 51 return std::make_pair(STALE, iter->second.result); 52 } 53 } 54 return std::make_pair(STALE, static_cast<base::DictionaryValue*>(NULL)); 55} 56 57void WebserviceCache::Put(QueryType type, 58 const std::string& query, 59 scoped_ptr<base::DictionaryValue> result) { 60 if (result) { 61 std::string typed_query = PrependType(type, query); 62 Payload payload(base::Time::Now(), result.release()); 63 64 cache_.Put(typed_query, payload); 65 // If the cache isn't loaded yet, we're fine with losing queries since 66 // a 1000 entry cache should load really quickly so the chance of a user 67 // already having typed a 3 character search before the cache has loaded is 68 // very unlikely. 69 if (cache_loaded_) { 70 data_store_->cached_dict()->Set(typed_query, DictFromPayload(payload)); 71 data_store_->ScheduleWrite(); 72 if (cache_.size() > kWebserviceCacheMaxSize) 73 TrimCache(); 74 } 75 } 76} 77 78void WebserviceCache::OnCacheLoaded(scoped_ptr<base::DictionaryValue>) { 79 if (!data_store_->cached_dict()) 80 return; 81 82 std::vector<std::string> cleanup_keys; 83 for (base::DictionaryValue::Iterator it(*data_store_->cached_dict()); 84 !it.IsAtEnd(); 85 it.Advance()) { 86 const base::DictionaryValue* payload_dict; 87 Payload payload; 88 if (!it.value().GetAsDictionary(&payload_dict) || 89 !payload_dict || 90 !PayloadFromDict(payload_dict, &payload)) { 91 // In case we don't have a valid payload associated with a given query, 92 // clean up that query from our data store. 93 cleanup_keys.push_back(it.key()); 94 continue; 95 } 96 cache_.Put(it.key(), payload); 97 } 98 99 if (!cleanup_keys.empty()) { 100 for (size_t i = 0; i < cleanup_keys.size(); ++i) 101 data_store_->cached_dict()->Remove(cleanup_keys[i], NULL); 102 data_store_->ScheduleWrite(); 103 } 104 cache_loaded_ = true; 105} 106 107bool WebserviceCache::PayloadFromDict(const base::DictionaryValue* dict, 108 Payload* payload) { 109 std::string time_string; 110 if (!dict->GetString(kKeyResultTime, &time_string)) 111 return false; 112 const base::DictionaryValue* result; 113 if (!dict->GetDictionary(kKeyResult, &result)) 114 return false; 115 116 int64 time_val; 117 base::StringToInt64(time_string, &time_val); 118 119 // The result dictionary will be owned by the cache, hence create a copy 120 // instead of returning the original reference. The new dictionary will be 121 // owned by our MRU cache. 122 *payload = Payload(base::Time::FromInternalValue(time_val), 123 result->DeepCopy()); 124 return true; 125} 126 127base::DictionaryValue* WebserviceCache::DictFromPayload( 128 const Payload& payload) { 129 base::DictionaryValue* dict = new base::DictionaryValue(); 130 dict->SetString(kKeyResultTime, base::Int64ToString( 131 payload.time.ToInternalValue())); 132 // The payload will still keep ownership of it's result dict, hence put a 133 // a copy of the result dictionary here. This dictionary will be owned by 134 // data_store_->cached_dict(). 135 dict->Set(kKeyResult, payload.result->DeepCopy()); 136 137 return dict; 138} 139 140void WebserviceCache::TrimCache() { 141 for (Cache::size_type i = cache_.size(); i > kWebserviceCacheMaxSize; i--) { 142 Cache::reverse_iterator rbegin = cache_.rbegin(); 143 data_store_->cached_dict()->Remove(rbegin->first, NULL); 144 cache_.Erase(rbegin); 145 } 146 data_store_->ScheduleWrite(); 147} 148 149std::string WebserviceCache::PrependType( 150 QueryType type, const std::string& query) { 151 switch (type) { 152 case WEBSTORE: 153 return kWebstoreQueryPrefix + query; 154 case PEOPLE: 155 return kPeopleQueryPrefix + query; 156 default: 157 return query; 158 } 159} 160 161} // namespace app_list 162