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