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 kKeyResultTime[] = "time"; |
| 18 const char kKeyResult[] = "result"; |
| 19 |
| 20 const char kWebstoreQueryPrefix[] = "webstore:"; |
| 21 const char kPeopleQueryPrefix[] = "people:"; |
14 | 22 |
15 } // namespace | 23 } // namespace |
16 | 24 |
17 void WebserviceCache::CacheDeletor::operator()( | 25 void WebserviceCache::CacheDeletor::operator()(Payload& payload) { |
18 WebserviceCache::Payload& payload) { | 26 delete payload.result; |
19 delete payload.second; | |
20 } | 27 } |
21 | 28 |
22 WebserviceCache::WebserviceCache() | 29 WebserviceCache::WebserviceCache(content::BrowserContext* context) |
23 : cache_(kWebserviceCacheMaxSize) { | 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())); |
24 } | 37 } |
25 | 38 |
26 WebserviceCache::~WebserviceCache() { | 39 WebserviceCache::~WebserviceCache() { |
27 } | 40 } |
28 | 41 |
29 const base::DictionaryValue* WebserviceCache::Get(const std::string& query) { | 42 const CacheResult WebserviceCache::Get(QueryType type, |
30 Cache::iterator iter = cache_.Get(query); | 43 const std::string& query) { |
| 44 std::string typed_query = PrependType(type, query); |
| 45 Cache::iterator iter = cache_.Get(typed_query); |
31 if (iter != cache_.end()) { | 46 if (iter != cache_.end()) { |
32 if (base::Time::Now() - iter->second.first <= | 47 if (base::Time::Now() - iter->second.time <= |
33 base::TimeDelta::FromMinutes(kWebserviceCacheTimeLimitInMinutes)) { | 48 base::TimeDelta::FromMinutes(kWebserviceCacheTimeLimitInMinutes)) { |
34 return iter->second.second; | 49 return std::make_pair(FRESH, iter->second.result); |
35 } else { | 50 } else { |
36 cache_.Erase(iter); | 51 return std::make_pair(STALE, iter->second.result); |
37 } | 52 } |
38 } | 53 } |
39 return NULL; | 54 return std::make_pair(STALE, static_cast<base::DictionaryValue*>(NULL)); |
40 } | 55 } |
41 | 56 |
42 void WebserviceCache::Put(const std::string& query, | 57 void WebserviceCache::Put(QueryType type, |
| 58 const std::string& query, |
43 scoped_ptr<base::DictionaryValue> result) { | 59 scoped_ptr<base::DictionaryValue> result) { |
44 if (result) | 60 if (result) { |
45 cache_.Put(query, std::make_pair(base::Time::Now(), result.release())); | 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 |
| 78 void WebserviceCache::OnCacheLoaded(scoped_ptr<base::DictionaryValue>) { |
| 79 if (!data_store_->cached_dict()) |
| 80 return; |
| 81 |
| 82 std::vector<std::string> cleanup_keys; |
| 83 for (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 |
| 107 bool 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 |
| 127 base::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 |
| 140 void 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 |
| 149 std::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 } |
46 } | 159 } |
47 | 160 |
48 } // namespace app_list | 161 } // namespace app_list |
OLD | NEW |