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