| Index: chrome/browser/ui/app_list/search/common/webservice_cache.cc
|
| diff --git a/chrome/browser/ui/app_list/search/common/webservice_cache.cc b/chrome/browser/ui/app_list/search/common/webservice_cache.cc
|
| index 082131268ec5e793a55004902f03cdb5a415a4ae..ff0e15ff5d7a79ce4edbafdb8ee51cdf78326413 100644
|
| --- a/chrome/browser/ui/app_list/search/common/webservice_cache.cc
|
| +++ b/chrome/browser/ui/app_list/search/common/webservice_cache.cc
|
| @@ -4,45 +4,158 @@
|
|
|
| #include "chrome/browser/ui/app_list/search/common/webservice_cache.h"
|
|
|
| +#include "base/strings/string_number_conversions.h"
|
| #include "base/values.h"
|
| +#include "content/public/browser/browser_context.h"
|
|
|
| namespace app_list {
|
| namespace {
|
|
|
| -const int kWebserviceCacheMaxSize = 100;
|
| -const int kWebserviceCacheTimeLimitInMinutes = 1;
|
| +const unsigned int kWebserviceCacheMaxSize = 1000;
|
| +const unsigned int kWebserviceCacheTimeLimitInMinutes = 1;
|
| +
|
| +const char kKeyResultTime[] = "time";
|
| +const char kKeyResult[] = "result";
|
| +
|
| +const char kWebstoreQueryPrefix[] = "webstore:";
|
| +const char kPeopleQueryPrefix[] = "people:";
|
|
|
| } // namespace
|
|
|
| -void WebserviceCache::CacheDeletor::operator()(
|
| - WebserviceCache::Payload& payload) {
|
| - delete payload.second;
|
| +void WebserviceCache::CacheDeletor::operator()(Payload& payload) {
|
| + delete payload.result;
|
| }
|
|
|
| -WebserviceCache::WebserviceCache()
|
| - : cache_(kWebserviceCacheMaxSize) {
|
| +WebserviceCache::WebserviceCache(content::BrowserContext* context)
|
| + : cache_(Cache::NO_AUTO_EVICT),
|
| + cache_loaded_(false) {
|
| + const char kStoreDataFileName[] = "Webservice Search Cache";
|
| + const base::FilePath data_file =
|
| + context->GetPath().AppendASCII(kStoreDataFileName);
|
| + data_store_ = new DictionaryDataStore(data_file);
|
| + data_store_->Load(base::Bind(&WebserviceCache::OnCacheLoaded, AsWeakPtr()));
|
| }
|
|
|
| WebserviceCache::~WebserviceCache() {
|
| }
|
|
|
| -const base::DictionaryValue* WebserviceCache::Get(const std::string& query) {
|
| - Cache::iterator iter = cache_.Get(query);
|
| +const CacheResult WebserviceCache::Get(QueryType type,
|
| + const std::string& query) {
|
| + std::string typed_query = PrependType(type, query);
|
| + Cache::iterator iter = cache_.Get(typed_query);
|
| if (iter != cache_.end()) {
|
| - if (base::Time::Now() - iter->second.first <=
|
| + if (base::Time::Now() - iter->second.time <=
|
| base::TimeDelta::FromMinutes(kWebserviceCacheTimeLimitInMinutes)) {
|
| - return iter->second.second;
|
| + return std::make_pair(FRESH, iter->second.result);
|
| } else {
|
| - cache_.Erase(iter);
|
| + return std::make_pair(STALE, iter->second.result);
|
| }
|
| }
|
| - return NULL;
|
| + return std::make_pair(STALE, static_cast<base::DictionaryValue*>(NULL));
|
| }
|
|
|
| -void WebserviceCache::Put(const std::string& query,
|
| +void WebserviceCache::Put(QueryType type,
|
| + const std::string& query,
|
| scoped_ptr<base::DictionaryValue> result) {
|
| - if (result)
|
| - cache_.Put(query, std::make_pair(base::Time::Now(), result.release()));
|
| + if (result) {
|
| + std::string typed_query = PrependType(type, query);
|
| + Payload payload(base::Time::Now(), result.release());
|
| +
|
| + cache_.Put(typed_query, payload);
|
| + // If the cache isn't loaded yet, we're fine with losing queries since
|
| + // a 1000 entry cache should load really quickly so the chance of a user
|
| + // already having typed a 3 character search before the cache has loaded is
|
| + // very unlikely.
|
| + if (cache_loaded_) {
|
| + data_store_->cached_dict()->Set(typed_query, DictFromPayload(payload));
|
| + data_store_->ScheduleWrite();
|
| + if (cache_.size() > kWebserviceCacheMaxSize)
|
| + TrimCache();
|
| + }
|
| + }
|
| +}
|
| +
|
| +void WebserviceCache::OnCacheLoaded(scoped_ptr<base::DictionaryValue>) {
|
| + if (!data_store_->cached_dict())
|
| + return;
|
| +
|
| + std::vector<std::string> cleanup_keys;
|
| + for (DictionaryValue::Iterator it(*data_store_->cached_dict());
|
| + !it.IsAtEnd();
|
| + it.Advance()) {
|
| + const base::DictionaryValue* payload_dict;
|
| + Payload payload;
|
| + if (!it.value().GetAsDictionary(&payload_dict) ||
|
| + !payload_dict ||
|
| + !PayloadFromDict(payload_dict, &payload)) {
|
| + // In case we don't have a valid payload associated with a given query,
|
| + // clean up that query from our data store.
|
| + cleanup_keys.push_back(it.key());
|
| + continue;
|
| + }
|
| + cache_.Put(it.key(), payload);
|
| + }
|
| +
|
| + if (!cleanup_keys.empty()) {
|
| + for (size_t i = 0; i < cleanup_keys.size(); ++i)
|
| + data_store_->cached_dict()->Remove(cleanup_keys[i], NULL);
|
| + data_store_->ScheduleWrite();
|
| + }
|
| + cache_loaded_ = true;
|
| +}
|
| +
|
| +bool WebserviceCache::PayloadFromDict(const base::DictionaryValue* dict,
|
| + Payload* payload) {
|
| + std::string time_string;
|
| + if (!dict->GetString(kKeyResultTime, &time_string))
|
| + return false;
|
| + const base::DictionaryValue* result;
|
| + if (!dict->GetDictionary(kKeyResult, &result))
|
| + return false;
|
| +
|
| + int64 time_val;
|
| + base::StringToInt64(time_string, &time_val);
|
| +
|
| + // The result dictionary will be owned by the cache, hence create a copy
|
| + // instead of returning the original reference. The new dictionary will be
|
| + // owned by our MRU cache.
|
| + *payload = Payload(base::Time::FromInternalValue(time_val),
|
| + result->DeepCopy());
|
| + return true;
|
| +}
|
| +
|
| +base::DictionaryValue* WebserviceCache::DictFromPayload(
|
| + const Payload& payload) {
|
| + base::DictionaryValue* dict = new base::DictionaryValue();
|
| + dict->SetString(kKeyResultTime, base::Int64ToString(
|
| + payload.time.ToInternalValue()));
|
| + // The payload will still keep ownership of it's result dict, hence put a
|
| + // a copy of the result dictionary here. This dictionary will be owned by
|
| + // data_store_->cached_dict().
|
| + dict->Set(kKeyResult, payload.result->DeepCopy());
|
| +
|
| + return dict;
|
| +}
|
| +
|
| +void WebserviceCache::TrimCache() {
|
| + for (Cache::size_type i = cache_.size(); i > kWebserviceCacheMaxSize; i--) {
|
| + Cache::reverse_iterator rbegin = cache_.rbegin();
|
| + data_store_->cached_dict()->Remove(rbegin->first, NULL);
|
| + cache_.Erase(rbegin);
|
| + }
|
| + data_store_->ScheduleWrite();
|
| +}
|
| +
|
| +std::string WebserviceCache::PrependType(
|
| + QueryType type, const std::string& query) {
|
| + switch (type) {
|
| + case WEBSTORE:
|
| + return kWebstoreQueryPrefix + query;
|
| + case PEOPLE:
|
| + return kPeopleQueryPrefix + query;
|
| + default:
|
| + return query;
|
| + }
|
| }
|
|
|
| } // namespace app_list
|
|
|