search_controller.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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.
4
5#include "chrome/browser/ui/app_list/search/search_controller.h"
6
7#include <algorithm>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/command_line.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/metrics/histogram.h"
14#include "base/strings/string_util.h"
15#include "base/strings/utf_string_conversions.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/ui/app_list/search/app_search_provider.h"
18#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
19#include "chrome/browser/ui/app_list/search/history.h"
20#include "chrome/browser/ui/app_list/search/history_factory.h"
21#include "chrome/browser/ui/app_list/search/omnibox_provider.h"
22#include "chrome/browser/ui/app_list/search/search_provider.h"
23#include "chrome/browser/ui/app_list/search/webstore_provider.h"
24#include "content/public/browser/user_metrics.h"
25#include "grit/generated_resources.h"
26#include "grit/theme_resources.h"
27#include "ui/app_list/search_box_model.h"
28#include "ui/base/l10n/l10n_util.h"
29#include "ui/base/resource/resource_bundle.h"
30
31namespace {
32  const char kAppListSearchResultOpenTypeHistogram[] =
33      "Apps.AppListSearchResultOpenType";
34}
35
36namespace app_list {
37
38SearchController::SearchController(Profile* profile,
39                                   SearchBoxModel* search_box,
40                                   AppListModel::SearchResults* results,
41                                   AppListControllerDelegate* list_controller)
42  : profile_(profile),
43    search_box_(search_box),
44    list_controller_(list_controller),
45    dispatching_query_(false),
46    mixer_(new Mixer(results)),
47    history_(HistoryFactory::GetForBrowserContext(profile)) {
48  Init();
49}
50
51SearchController::~SearchController() {}
52
53void SearchController::Init() {
54  search_box_->SetHintText(
55      l10n_util::GetStringUTF16(IDS_SEARCH_BOX_HINT));
56  search_box_->SetIcon(*ui::ResourceBundle::GetSharedInstance().
57      GetImageSkiaNamed(IDR_OMNIBOX_SEARCH));
58
59  mixer_->Init();
60
61  AddProvider(Mixer::MAIN_GROUP, scoped_ptr<SearchProvider>(
62      new AppSearchProvider(profile_, list_controller_)).Pass());
63  AddProvider(Mixer::OMNIBOX_GROUP, scoped_ptr<SearchProvider>(
64      new OmniboxProvider(profile_)).Pass());
65  AddProvider(Mixer::WEBSTORE_GROUP, scoped_ptr<SearchProvider>(
66      new WebstoreProvider(profile_, list_controller_)).Pass());
67}
68
69void SearchController::Start() {
70  Stop();
71
72  string16 query;
73  TrimWhitespace(search_box_->text(), TRIM_ALL, &query);
74
75  dispatching_query_ = true;
76  for (Providers::iterator it = providers_.begin();
77       it != providers_.end();
78       ++it) {
79    (*it)->Start(query);
80  }
81  dispatching_query_ = false;
82
83  OnResultsChanged();
84
85  // Maximum time (in milliseconds) to wait to the search providers to finish.
86  const int kStopTimeMS = 1500;
87  stop_timer_.Start(FROM_HERE,
88                    base::TimeDelta::FromMilliseconds(kStopTimeMS),
89                    base::Bind(&SearchController::Stop,
90                               base::Unretained(this)));
91}
92
93void SearchController::Stop() {
94  stop_timer_.Stop();
95
96  for (Providers::iterator it = providers_.begin();
97       it != providers_.end();
98       ++it) {
99    (*it)->Stop();
100  }
101}
102
103void SearchController::OpenResult(SearchResult* result, int event_flags) {
104  // Count AppList.Search here because it is composed of search + action.
105  content::RecordAction(content::UserMetricsAction("AppList_Search"));
106
107  ChromeSearchResult* chrome_result =
108      static_cast<app_list::ChromeSearchResult*>(result);
109  UMA_HISTOGRAM_ENUMERATION(kAppListSearchResultOpenTypeHistogram,
110                            chrome_result->GetType(),
111                            SEARCH_RESULT_TYPE_BOUNDARY);
112  chrome_result->Open(event_flags);
113
114  if (history_ && history_->IsReady()) {
115    history_->AddLaunchEvent(UTF16ToUTF8(search_box_->text()),
116                             chrome_result->id());
117  }
118}
119
120void SearchController::InvokeResultAction(SearchResult* result,
121                                          int action_index,
122                                          int event_flags) {
123  // TODO(xiyuan): Hook up with user learning.
124  static_cast<app_list::ChromeSearchResult*>(result)->InvokeAction(
125      action_index, event_flags);
126}
127
128void SearchController::AddProvider(Mixer::GroupId group,
129                                   scoped_ptr<SearchProvider> provider) {
130  provider->set_result_changed_callback(base::Bind(
131      &SearchController::OnResultsChanged,
132      base::Unretained(this)));
133  mixer_->AddProviderToGroup(group, provider.get());
134  providers_.push_back(provider.release());  // Takes ownership.
135}
136
137void SearchController::OnResultsChanged() {
138  if (dispatching_query_)
139    return;
140
141  KnownResults known_results;
142  if (history_ && history_->IsReady()) {
143    history_->GetKnownResults(UTF16ToUTF8(search_box_->text()))
144        ->swap(known_results);
145  }
146
147  mixer_->MixAndPublish(known_results);
148}
149
150}  // namespace app_list
151