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.
5 #include "chrome/browser/ui/app_list/search/common/webservice_cache.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/values.h"
9 #include "content/public/browser/browser_context.h"
10 #include "content/public/browser/browser_thread.h"
15 const unsigned int kWebserviceCacheMaxSize
= 1000;
16 const unsigned int kWebserviceCacheTimeLimitInMinutes
= 1;
18 const char kKeyResultTime
[] = "time";
19 const char kKeyResult
[] = "result";
21 const char kWebstoreQueryPrefix
[] = "webstore:";
22 const char kPeopleQueryPrefix
[] = "people:";
26 void WebserviceCache::CacheDeletor::operator()(const Payload
& payload
) {
27 delete payload
.result
;
30 WebserviceCache::WebserviceCache(content::BrowserContext
* context
)
31 : cache_(Cache::NO_AUTO_EVICT
),
32 cache_loaded_(false) {
33 const char kStoreDataFileName
[] = "Webservice Search Cache";
34 const base::FilePath data_file
=
35 context
->GetPath().AppendASCII(kStoreDataFileName
);
36 data_store_
= new DictionaryDataStore(
37 data_file
, content::BrowserThread::GetBlockingPool());
38 data_store_
->Load(base::Bind(&WebserviceCache::OnCacheLoaded
, AsWeakPtr()));
41 WebserviceCache::~WebserviceCache() {
44 const CacheResult
WebserviceCache::Get(QueryType type
,
45 const std::string
& query
) {
46 std::string typed_query
= PrependType(type
, query
);
47 Cache::iterator iter
= cache_
.Get(typed_query
);
48 if (iter
!= cache_
.end()) {
49 if (base::Time::Now() - iter
->second
.time
<=
50 base::TimeDelta::FromMinutes(kWebserviceCacheTimeLimitInMinutes
)) {
51 return std::make_pair(FRESH
, iter
->second
.result
);
53 return std::make_pair(STALE
, iter
->second
.result
);
56 return std::make_pair(STALE
, static_cast<base::DictionaryValue
*>(NULL
));
59 void WebserviceCache::Put(QueryType type
,
60 const std::string
& query
,
61 scoped_ptr
<base::DictionaryValue
> result
) {
63 std::string typed_query
= PrependType(type
, query
);
64 Payload
payload(base::Time::Now(), result
.release());
66 cache_
.Put(typed_query
, payload
);
67 // If the cache isn't loaded yet, we're fine with losing queries since
68 // a 1000 entry cache should load really quickly so the chance of a user
69 // already having typed a 3 character search before the cache has loaded is
72 data_store_
->cached_dict()->Set(typed_query
, DictFromPayload(payload
));
73 data_store_
->ScheduleWrite();
74 if (cache_
.size() > kWebserviceCacheMaxSize
)
80 void WebserviceCache::OnCacheLoaded(scoped_ptr
<base::DictionaryValue
>) {
81 if (!data_store_
->cached_dict())
84 std::vector
<std::string
> cleanup_keys
;
85 for (base::DictionaryValue::Iterator
it(*data_store_
->cached_dict());
88 const base::DictionaryValue
* payload_dict
;
90 if (!it
.value().GetAsDictionary(&payload_dict
) ||
92 !PayloadFromDict(payload_dict
, &payload
)) {
93 // In case we don't have a valid payload associated with a given query,
94 // clean up that query from our data store.
95 cleanup_keys
.push_back(it
.key());
98 cache_
.Put(it
.key(), payload
);
101 if (!cleanup_keys
.empty()) {
102 for (size_t i
= 0; i
< cleanup_keys
.size(); ++i
)
103 data_store_
->cached_dict()->Remove(cleanup_keys
[i
], NULL
);
104 data_store_
->ScheduleWrite();
106 cache_loaded_
= true;
109 bool WebserviceCache::PayloadFromDict(const base::DictionaryValue
* dict
,
111 std::string time_string
;
112 if (!dict
->GetString(kKeyResultTime
, &time_string
))
114 const base::DictionaryValue
* result
;
115 if (!dict
->GetDictionary(kKeyResult
, &result
))
119 base::StringToInt64(time_string
, &time_val
);
121 // The result dictionary will be owned by the cache, hence create a copy
122 // instead of returning the original reference. The new dictionary will be
123 // owned by our MRU cache.
124 *payload
= Payload(base::Time::FromInternalValue(time_val
),
129 base::DictionaryValue
* WebserviceCache::DictFromPayload(
130 const Payload
& payload
) {
131 base::DictionaryValue
* dict
= new base::DictionaryValue();
132 dict
->SetString(kKeyResultTime
, base::Int64ToString(
133 payload
.time
.ToInternalValue()));
134 // The payload will still keep ownership of it's result dict, hence put a
135 // a copy of the result dictionary here. This dictionary will be owned by
136 // data_store_->cached_dict().
137 dict
->Set(kKeyResult
, payload
.result
->DeepCopy());
142 void WebserviceCache::TrimCache() {
143 for (Cache::size_type i
= cache_
.size(); i
> kWebserviceCacheMaxSize
; i
--) {
144 Cache::reverse_iterator rbegin
= cache_
.rbegin();
145 data_store_
->cached_dict()->Remove(rbegin
->first
, NULL
);
146 cache_
.Erase(rbegin
);
148 data_store_
->ScheduleWrite();
151 std::string
WebserviceCache::PrependType(
152 QueryType type
, const std::string
& query
) {
155 return kWebstoreQueryPrefix
+ query
;
157 return kPeopleQueryPrefix
+ query
;
163 } // namespace app_list