14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file. 44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/common/webservice_cache.h" 64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 71e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/strings/string_number_conversions.h" 84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/values.h" 91e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "content/public/browser/browser_context.h" 104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace app_list { 124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace { 134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const unsigned int kWebserviceCacheMaxSize = 1000; 151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const unsigned int kWebserviceCacheTimeLimitInMinutes = 1; 161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const char kKeyResultTime[] = "time"; 181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const char kKeyResult[] = "result"; 191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const char kWebstoreQueryPrefix[] = "webstore:"; 211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const char kPeopleQueryPrefix[] = "people:"; 224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} // namespace 244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void WebserviceCache::CacheDeletor::operator()(Payload& payload) { 261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) delete payload.result; 274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)WebserviceCache::WebserviceCache(content::BrowserContext* context) 301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) : cache_(Cache::NO_AUTO_EVICT), 311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) cache_loaded_(false) { 321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const char kStoreDataFileName[] = "Webservice Search Cache"; 331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const base::FilePath data_file = 341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) context->GetPath().AppendASCII(kStoreDataFileName); 351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) data_store_ = new DictionaryDataStore(data_file); 361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) data_store_->Load(base::Bind(&WebserviceCache::OnCacheLoaded, AsWeakPtr())); 374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)WebserviceCache::~WebserviceCache() { 404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)const CacheResult WebserviceCache::Get(QueryType type, 431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const std::string& query) { 441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) std::string typed_query = PrependType(type, query); 451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) Cache::iterator iter = cache_.Get(typed_query); 464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (iter != cache_.end()) { 471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (base::Time::Now() - iter->second.time <= 484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) base::TimeDelta::FromMinutes(kWebserviceCacheTimeLimitInMinutes)) { 491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return std::make_pair(FRESH, iter->second.result); 504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } else { 511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return std::make_pair(STALE, iter->second.result); 524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return std::make_pair(STALE, static_cast<base::DictionaryValue*>(NULL)); 554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void WebserviceCache::Put(QueryType type, 581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const std::string& query, 594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) scoped_ptr<base::DictionaryValue> result) { 601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (result) { 611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) std::string typed_query = PrependType(type, query); 621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) Payload payload(base::Time::Now(), result.release()); 631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) cache_.Put(typed_query, payload); 651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // If the cache isn't loaded yet, we're fine with losing queries since 661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // a 1000 entry cache should load really quickly so the chance of a user 671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // already having typed a 3 character search before the cache has loaded is 681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // very unlikely. 691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (cache_loaded_) { 701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) data_store_->cached_dict()->Set(typed_query, DictFromPayload(payload)); 711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) data_store_->ScheduleWrite(); 721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (cache_.size() > kWebserviceCacheMaxSize) 731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) TrimCache(); 741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void WebserviceCache::OnCacheLoaded(scoped_ptr<base::DictionaryValue>) { 791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!data_store_->cached_dict()) 801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return; 811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) std::vector<std::string> cleanup_keys; 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (base::DictionaryValue::Iterator it(*data_store_->cached_dict()); 841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) !it.IsAtEnd(); 851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) it.Advance()) { 861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const base::DictionaryValue* payload_dict; 871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) Payload payload; 881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!it.value().GetAsDictionary(&payload_dict) || 891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) !payload_dict || 901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) !PayloadFromDict(payload_dict, &payload)) { 911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // In case we don't have a valid payload associated with a given query, 921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // clean up that query from our data store. 931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) cleanup_keys.push_back(it.key()); 941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) continue; 951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) cache_.Put(it.key(), payload); 971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!cleanup_keys.empty()) { 1001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) for (size_t i = 0; i < cleanup_keys.size(); ++i) 1011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) data_store_->cached_dict()->Remove(cleanup_keys[i], NULL); 1021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) data_store_->ScheduleWrite(); 1031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) cache_loaded_ = true; 1051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 1061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)bool WebserviceCache::PayloadFromDict(const base::DictionaryValue* dict, 1081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) Payload* payload) { 1091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) std::string time_string; 1101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!dict->GetString(kKeyResultTime, &time_string)) 1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return false; 1121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const base::DictionaryValue* result; 1131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!dict->GetDictionary(kKeyResult, &result)) 1141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return false; 1151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) int64 time_val; 1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) base::StringToInt64(time_string, &time_val); 1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // The result dictionary will be owned by the cache, hence create a copy 1201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // instead of returning the original reference. The new dictionary will be 1211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // owned by our MRU cache. 1221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) *payload = Payload(base::Time::FromInternalValue(time_val), 1231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) result->DeepCopy()); 1241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return true; 1251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 1261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)base::DictionaryValue* WebserviceCache::DictFromPayload( 1281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const Payload& payload) { 1291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) base::DictionaryValue* dict = new base::DictionaryValue(); 1301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) dict->SetString(kKeyResultTime, base::Int64ToString( 1311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) payload.time.ToInternalValue())); 1321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // The payload will still keep ownership of it's result dict, hence put a 1331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // a copy of the result dictionary here. This dictionary will be owned by 1341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // data_store_->cached_dict(). 1351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) dict->Set(kKeyResult, payload.result->DeepCopy()); 1361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return dict; 1381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 1391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void WebserviceCache::TrimCache() { 1411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) for (Cache::size_type i = cache_.size(); i > kWebserviceCacheMaxSize; i--) { 1421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) Cache::reverse_iterator rbegin = cache_.rbegin(); 1431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) data_store_->cached_dict()->Remove(rbegin->first, NULL); 1441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) cache_.Erase(rbegin); 1451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) data_store_->ScheduleWrite(); 1471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 1481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)std::string WebserviceCache::PrependType( 1501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) QueryType type, const std::string& query) { 1511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) switch (type) { 1521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) case WEBSTORE: 1531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return kWebstoreQueryPrefix + query; 1541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) case PEOPLE: 1551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return kPeopleQueryPrefix + query; 1561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) default: 1571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return query; 1581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 1604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} // namespace app_list 162