Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / app_list / search / mixer.cc
blob18c28fedc8062bc8735efb316530733b4b9450b3
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/mixer.h"
7 #include <algorithm>
8 #include <set>
9 #include <string>
10 #include <vector>
12 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
13 #include "chrome/browser/ui/app_list/search/search_provider.h"
15 namespace app_list {
17 namespace {
19 // Maximum number of results to show.
20 const size_t kMaxResults = 6;
21 const size_t kMaxMainGroupResults = 4;
22 const size_t kMaxWebstoreResults = 2;
23 const size_t kMaxPeopleResults = 2;
25 // A value to indicate no max number of results limit.
26 const size_t kNoMaxResultsLimit = 0;
28 // Used for sorting and mixing results.
29 struct SortData {
30 SortData()
31 : result(NULL),
32 score(0.0) {
34 SortData(ChromeSearchResult* result, double score)
35 : result(result),
36 score(score) {
39 bool operator<(const SortData& other) const {
40 // This data precedes (less than) |other| if it has higher score.
41 return score > other.score;
44 ChromeSearchResult* result; // Not owned.
45 double score;
47 typedef std::vector<SortData> SortedResults;
49 // Removes duplicates from |results|.
50 void RemoveDuplicates(SortedResults* results) {
51 SortedResults final;
52 final.reserve(results->size());
54 std::set<std::string> id_set;
55 for (SortedResults::iterator it = results->begin();
56 it != results->end();
57 ++it) {
58 const std::string& id = it->result->id();
59 if (id_set.find(id) != id_set.end())
60 continue;
62 id_set.insert(id);
63 final.push_back(*it);
66 results->swap(final);
69 // Publishes the given |results| to |ui_results|. Reuse existing ones to avoid
70 // flickering.
71 void Publish(const SortedResults& results,
72 AppListModel::SearchResults* ui_results) {
73 for (size_t i = 0; i < results.size(); ++i) {
74 ChromeSearchResult* result = results[i].result;
76 ChromeSearchResult* ui_result = i < ui_results->item_count() ?
77 static_cast<ChromeSearchResult*>(ui_results->GetItemAt(i)) : NULL;
78 if (ui_result && ui_result->id() == result->id()) {
79 ui_result->set_title(result->title());
80 ui_result->set_title_tags(result->title_tags());
81 ui_result->set_details(result->details());
82 ui_result->set_details_tags(result->details_tags());
83 ui_results->NotifyItemsChanged(i, 1);
84 } else {
85 if (ui_result)
86 ui_results->DeleteAt(i);
87 ui_results->AddAt(i, result->Duplicate().release());
91 while (ui_results->item_count() > results.size())
92 ui_results->DeleteAt(ui_results->item_count() - 1);
95 } // namespace
97 // Used to group relevant providers together fox mixing their results.
98 class Mixer::Group {
99 public:
100 Group(size_t max_results, double boost)
101 : max_results_(max_results),
102 boost_(boost) {
104 ~Group() {}
106 void AddProvider(SearchProvider* provider) {
107 providers_.push_back(provider);
110 void FetchResults(const KnownResults& known_results) {
111 results_.clear();
113 for (Providers::const_iterator provider_it = providers_.begin();
114 provider_it != providers_.end();
115 ++provider_it) {
116 for (SearchProvider::Results::const_iterator
117 result_it = (*provider_it)->results().begin();
118 result_it != (*provider_it)->results().end();
119 ++result_it) {
120 DCHECK_GE((*result_it)->relevance(), 0.0);
121 DCHECK_LE((*result_it)->relevance(), 1.0);
122 DCHECK(!(*result_it)->id().empty());
124 double boost = boost_;
125 KnownResults::const_iterator known_it =
126 known_results.find((*result_it)->id());
127 if (known_it != known_results.end()) {
128 switch (known_it->second) {
129 case PERFECT_PRIMARY:
130 boost = 4.0;
131 break;
132 case PREFIX_PRIMARY:
133 boost = 3.75;
134 break;
135 case PERFECT_SECONDARY:
136 boost = 3.25;
137 break;
138 case PREFIX_SECONDARY:
139 boost = 3.0;
140 break;
141 case UNKNOWN_RESULT:
142 NOTREACHED() << "Unknown result in KnownResults?";
143 break;
147 results_.push_back(
148 SortData(*result_it, (*result_it)->relevance() + boost));
152 std::sort(results_.begin(), results_.end());
153 if (max_results_ != kNoMaxResultsLimit && results_.size() > max_results_)
154 results_.resize(max_results_);
157 const SortedResults& results() const { return results_; }
159 private:
160 typedef std::vector<SearchProvider*> Providers;
161 const size_t max_results_;
162 const double boost_;
164 Providers providers_; // Not owned.
165 SortedResults results_;
167 DISALLOW_COPY_AND_ASSIGN(Group);
170 Mixer::Mixer(AppListModel::SearchResults* ui_results)
171 : ui_results_(ui_results) {}
172 Mixer::~Mixer() {}
174 void Mixer::Init() {
175 groups_.push_back(new Group(kMaxMainGroupResults, 3.0));
176 groups_.push_back(new Group(kNoMaxResultsLimit, 2.0));
177 groups_.push_back(new Group(kMaxWebstoreResults, 1.0));
178 groups_.push_back(new Group(kMaxPeopleResults, 0.0));
181 void Mixer::AddProviderToGroup(GroupId group, SearchProvider* provider) {
182 size_t group_index = static_cast<size_t>(group);
183 groups_[group_index]->AddProvider(provider);
186 void Mixer::MixAndPublish(const KnownResults& known_results) {
187 FetchResults(known_results);
189 SortedResults results;
190 results.reserve(kMaxResults);
192 // Adds main group and web store results first.
193 results.insert(results.end(),
194 groups_[MAIN_GROUP]->results().begin(),
195 groups_[MAIN_GROUP]->results().end());
196 results.insert(results.end(),
197 groups_[WEBSTORE_GROUP]->results().begin(),
198 groups_[WEBSTORE_GROUP]->results().end());
199 results.insert(results.end(),
200 groups_[PEOPLE_GROUP]->results().begin(),
201 groups_[PEOPLE_GROUP]->results().end());
203 // Collapse duplicate apps from local and web store.
204 RemoveDuplicates(&results);
206 DCHECK_GE(kMaxResults, results.size());
207 size_t remaining_slots = kMaxResults - results.size();
209 // Reserves at least one slot for the omnibox result. If there is no available
210 // slot for omnibox results, removes the last one from web store.
211 const size_t omnibox_results = groups_[OMNIBOX_GROUP]->results().size();
212 if (!remaining_slots && omnibox_results)
213 results.pop_back();
215 remaining_slots = std::min(kMaxResults - results.size(), omnibox_results);
216 results.insert(results.end(),
217 groups_[OMNIBOX_GROUP]->results().begin(),
218 groups_[OMNIBOX_GROUP]->results().begin() + remaining_slots);
220 std::sort(results.begin(), results.end());
221 RemoveDuplicates(&results);
222 if (results.size() > kMaxResults)
223 results.resize(kMaxResults);
225 Publish(results, ui_results_);
228 void Mixer::FetchResults(const KnownResults& known_results) {
229 for (Groups::iterator group_it = groups_.begin();
230 group_it != groups_.end();
231 ++group_it) {
232 (*group_it)->FetchResults(known_results);
236 } // namespace app_list