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 31 namespace { 32 const char kAppListSearchResultOpenTypeHistogram[] = 33 "Apps.AppListSearchResultOpenType"; 34 } 35 36 namespace app_list { 37 38 SearchController::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 51 SearchController::~SearchController() {} 52 53 void 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 69 void 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 93 void 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 103 void 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 120 void 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 128 void 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 137 void 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