Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4645)

Unified Diff: chrome/browser/ui/app_list/search/history_data_store.cc

Issue 15875007: app_list: Search result launch history and boost. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix win7_aura unit_test Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/app_list/search/history_data_store.cc
diff --git a/chrome/browser/ui/app_list/search/history_data_store.cc b/chrome/browser/ui/app_list/search/history_data_store.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0036b0e01e389480f82c67f3a7045af9c9caa962
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/history_data_store.cc
@@ -0,0 +1,239 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/search/history_data_store.h"
+
+#include "base/callback.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task_runner_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/values.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace app_list {
+
+namespace {
+
+const char kKeyVersion[] = "version";
+const char kCurrentVersion[] = "1";
+
+const char kKeyAssociations[] = "associations";
+const char kKeyPrimary[] = "p";
+const char kKeySecondary[] = "s";
+const char kKeyUpdateTime[] = "t";
+
+// Extracts strings from ListValue.
+void GetSecondary(const base::ListValue* list,
+ HistoryData::SecondaryDeque* secondary) {
+ HistoryData::SecondaryDeque results;
+ for (base::ListValue::const_iterator it = list->begin();
+ it != list->end(); ++it) {
+ std::string str;
+ if (!(*it)->GetAsString(&str))
+ return;
+
+ results.push_back(str);
+ }
+
+ secondary->swap(results);
+}
+
+// V1 format json dictionary:
+// {
+// "version": "1",
+// "associations": {
+// "user typed query": {
+// "p" : "result id of primary association",
+// "s" : [
+// "result id of 1st (oldest) secondary association",
+// ...
+// "result id of the newest secondary association"
+// ],
+// "t" : "last_update_timestamp"
+// },
+// ...
+// }
+// }
+scoped_ptr<HistoryData::Associations> Parse(const base::DictionaryValue& dict) {
+ std::string version;
+ if (!dict.GetStringWithoutPathExpansion(kKeyVersion, &version) ||
+ version != kCurrentVersion) {
+ return scoped_ptr<HistoryData::Associations>();
+ }
+
+ const base::DictionaryValue* assoc_dict = NULL;
+ if (!dict.GetDictionaryWithoutPathExpansion(kKeyAssociations, &assoc_dict) ||
+ !assoc_dict) {
+ return scoped_ptr<HistoryData::Associations>();
+ }
+
+ scoped_ptr<HistoryData::Associations> data(new HistoryData::Associations);
+ for (base::DictionaryValue::Iterator it(*assoc_dict);
+ !it.IsAtEnd(); it.Advance()) {
+ const base::DictionaryValue* entry_dict = NULL;
+ if (!it.value().GetAsDictionary(&entry_dict))
+ continue;
+
+ std::string primary;
+ std::string update_time_string;
+ if (!entry_dict->GetStringWithoutPathExpansion(kKeyPrimary, &primary) ||
+ !entry_dict->GetStringWithoutPathExpansion(kKeyUpdateTime,
+ &update_time_string)) {
+ continue;
+ }
+
+ const base::ListValue* secondary_list = NULL;
+ HistoryData::SecondaryDeque secondary;
+ if (entry_dict->GetListWithoutPathExpansion(kKeySecondary, &secondary_list))
+ GetSecondary(secondary_list, &secondary);
+
+ const std::string& query = it.key();
+ HistoryData::Data& association_data = (*data.get())[query];
+ association_data.primary = primary;
+ association_data.secondary.swap(secondary);
+
+ int64 update_time_val;
+ base::StringToInt64(update_time_string, &update_time_val);
+ association_data.update_time =
+ base::Time::FromInternalValue(update_time_val);
+ }
+
+ return data.Pass();
+}
+
+// An empty callback used to ensure file tasks are cleared.
+void EmptyCallback() {}
+
+} // namespace
+
+HistoryDataStore::HistoryDataStore(const base::FilePath& data_file)
+ : data_file_(data_file) {
+ std::string token("app-launcher-history-data-store");
+ token.append(data_file.AsUTF8Unsafe());
+
+ // Uses a SKIP_ON_SHUTDOWN file task runner because losing a couple
+ // associations is better than blocking shutdown.
+ base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
+ file_task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
+ pool->GetNamedSequenceToken(token),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+ writer_.reset(new base::ImportantFileWriter(data_file, file_task_runner_));
+
+ cached_json_.reset(new base::DictionaryValue);
+ cached_json_->SetString(kKeyVersion, kCurrentVersion);
+ cached_json_->Set(kKeyAssociations, new base::DictionaryValue);
+}
+
+HistoryDataStore::~HistoryDataStore() {
+ Flush(OnFlushedCallback());
+}
+
+void HistoryDataStore::Flush(const OnFlushedCallback& on_flushed) {
+ if (writer_->HasPendingWrite())
+ writer_->DoScheduledWrite();
+
+ if (on_flushed.is_null())
+ return;
+
+ file_task_runner_->PostTaskAndReply(
+ FROM_HERE, base::Bind(&EmptyCallback), on_flushed);
+}
+
+void HistoryDataStore::Load(
+ const HistoryDataStore::OnLoadedCallback& on_loaded) {
+ base::PostTaskAndReplyWithResult(
+ file_task_runner_,
+ FROM_HERE,
+ base::Bind(&HistoryDataStore::LoadOnBlockingPool, this),
+ on_loaded);
+}
+
+void HistoryDataStore::SetPrimary(const std::string& query,
+ const std::string& result) {
+ base::DictionaryValue* entry_dict = GetEntryDict(query);
+ entry_dict->SetWithoutPathExpansion(kKeyPrimary,
+ new base::StringValue(result));
+ writer_->ScheduleWrite(this);
+}
+
+void HistoryDataStore::SetSecondary(
+ const std::string& query,
+ const HistoryData::SecondaryDeque& results) {
+ scoped_ptr<base::ListValue> results_list(new base::ListValue);
+ for (size_t i = 0; i< results.size(); ++i)
+ results_list->AppendString(results[i]);
+
+ base::DictionaryValue* entry_dict = GetEntryDict(query);
+ entry_dict->SetWithoutPathExpansion(kKeySecondary, results_list.release());
+ writer_->ScheduleWrite(this);
+}
+
+void HistoryDataStore::SetUpdateTime(const std::string& query,
+ const base::Time& update_time) {
+ base::DictionaryValue* entry_dict = GetEntryDict(query);
+ entry_dict->SetWithoutPathExpansion(kKeyUpdateTime,
+ new base::StringValue(base::Int64ToString(
+ update_time.ToInternalValue())));
+ writer_->ScheduleWrite(this);
+}
+
+void HistoryDataStore::Delete(const std::string& query) {
+ base::DictionaryValue* assoc_dict = GetAssociationDict();
+ assoc_dict->RemoveWithoutPathExpansion(query, NULL);
+ writer_->ScheduleWrite(this);
+}
+
+base::DictionaryValue* HistoryDataStore::GetAssociationDict() {
+ base::DictionaryValue* assoc_dict = NULL;
+ CHECK(cached_json_->GetDictionary(kKeyAssociations, &assoc_dict) &&
+ assoc_dict);
+
+ return assoc_dict;
+}
+
+base::DictionaryValue* HistoryDataStore::GetEntryDict(
+ const std::string& query) {
+ base::DictionaryValue* assoc_dict = GetAssociationDict();
+
+ base::DictionaryValue* entry_dict = NULL;
+ if (!assoc_dict->GetDictionaryWithoutPathExpansion(query, &entry_dict)) {
+ // Creates one if none exists. Ownership is taken in the set call after.
+ entry_dict = new base::DictionaryValue;
+ assoc_dict->SetWithoutPathExpansion(query, entry_dict);
+ }
+
+ return entry_dict;
+}
+
+scoped_ptr<HistoryData::Associations> HistoryDataStore::LoadOnBlockingPool() {
+ DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+
+ int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
+ std::string error_message;
+ JSONFileValueSerializer serializer(data_file_);
+ base::Value* value = serializer.Deserialize(&error_code, &error_message);
+ base::DictionaryValue* dict_value = NULL;
+ if (error_code != JSONFileValueSerializer::JSON_NO_ERROR ||
+ !value ||
+ !value->GetAsDictionary(&dict_value) ||
+ !dict_value) {
+ return scoped_ptr<HistoryData::Associations>();
+ }
+
+ cached_json_.reset(dict_value);
+ return Parse(*dict_value).Pass();
+}
+
+bool HistoryDataStore::SerializeData(std::string* data) {
+ JSONStringValueSerializer serializer(data);
+ serializer.set_pretty_print(true);
+ return serializer.Serialize(*cached_json_.get());
+}
+
+} // namespace app_list

Powered by Google App Engine
This is Rietveld 408576698