1 // Copyright 2014 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 "athena/main/url_search_provider.h" 6 7 #include "athena/activity/public/activity.h" 8 #include "athena/activity/public/activity_factory.h" 9 #include "athena/content/public/scheme_classifier_factory.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "base/values.h" 12 #include "components/metrics/proto/omnibox_event.pb.h" 13 #include "components/metrics/proto/omnibox_input_type.pb.h" 14 #include "components/omnibox/autocomplete_input.h" 15 #include "components/omnibox/autocomplete_provider_client.h" 16 #include "components/omnibox/search_provider.h" 17 #include "components/search_engines/search_terms_data.h" 18 #include "components/search_engines/template_url_service.h" 19 #include "components/search_engines/template_url_service_client.h" 20 #include "content/public/browser/browser_context.h" 21 #include "ui/app_list/search_result.h" 22 #include "ui/base/resource/resource_bundle.h" 23 #include "url/gurl.h" 24 25 namespace athena { 26 27 namespace { 28 29 // This constant was copied from HistoryURLProvider. 30 // TODO(hashimoto): Componentize HistoryURLProvider and delete this. 31 const int kScoreForWhatYouTypedResult = 1203; 32 33 // The SearchTermsData implementation for Athena. 34 class AthenaSearchTermsData : public SearchTermsData { 35 public: 36 // SearchTermsData: 37 virtual std::string GetSuggestClient() const OVERRIDE { 38 return "chrome"; 39 } 40 }; 41 42 // The templateURLServiceClient for Athena. Mainly for the interaction with 43 // history module (see chrome/browser/search_engines for Chrome implementation). 44 // TODO(mukai): Implement the contents of this class when it's necessary. 45 class AthenaTemplateURLServiceClient : public TemplateURLServiceClient { 46 public: 47 AthenaTemplateURLServiceClient() {} 48 virtual ~AthenaTemplateURLServiceClient() {} 49 50 private: 51 // TemplateURLServiceClient: 52 virtual void SetOwner(TemplateURLService* owner) OVERRIDE {} 53 virtual void DeleteAllSearchTermsForKeyword(TemplateURLID id) OVERRIDE {} 54 virtual void SetKeywordSearchTermsForURL( 55 const GURL& url, 56 TemplateURLID id, 57 const base::string16& term) OVERRIDE {} 58 virtual void AddKeywordGeneratedVisit(const GURL& url) OVERRIDE {} 59 virtual void RestoreExtensionInfoIfNecessary( 60 TemplateURL* template_url) OVERRIDE {} 61 62 DISALLOW_COPY_AND_ASSIGN(AthenaTemplateURLServiceClient); 63 }; 64 65 // The AutocompleteProviderClient for Athena. 66 class AthenaAutocompleteProviderClient : public AutocompleteProviderClient { 67 public: 68 explicit AthenaAutocompleteProviderClient( 69 content::BrowserContext* browser_context) 70 : browser_context_(browser_context), 71 scheme_classifier_(CreateSchemeClassifier(browser_context)) {} 72 virtual ~AthenaAutocompleteProviderClient() {} 73 74 virtual net::URLRequestContextGetter* RequestContext() OVERRIDE { 75 return browser_context_->GetRequestContext(); 76 } 77 virtual bool IsOffTheRecord() OVERRIDE { 78 return browser_context_->IsOffTheRecord(); 79 } 80 virtual std::string AcceptLanguages() OVERRIDE { 81 // TODO(hashimoto): Return the value stored in the prefs. 82 return "en-US"; 83 } 84 virtual bool SearchSuggestEnabled() OVERRIDE { return true; } 85 virtual bool ShowBookmarkBar() OVERRIDE { return false; } 86 virtual const AutocompleteSchemeClassifier& SchemeClassifier() OVERRIDE { 87 return *scheme_classifier_; 88 } 89 virtual void Classify( 90 const base::string16& text, 91 bool prefer_keyword, 92 bool allow_exact_keyword_match, 93 metrics::OmniboxEventProto::PageClassification page_classification, 94 AutocompleteMatch* match, 95 GURL* alternate_nav_url) OVERRIDE {} 96 virtual history::URLDatabase* InMemoryDatabase() OVERRIDE { return NULL; } 97 virtual void DeleteMatchingURLsForKeywordFromHistory( 98 history::KeywordID keyword_id, 99 const base::string16& term) OVERRIDE {} 100 virtual bool TabSyncEnabledAndUnencrypted() OVERRIDE { return false; } 101 virtual void PrefetchImage(const GURL& url) OVERRIDE {} 102 103 private: 104 content::BrowserContext* browser_context_; 105 scoped_ptr<AutocompleteSchemeClassifier> scheme_classifier_; 106 107 DISALLOW_COPY_AND_ASSIGN(AthenaAutocompleteProviderClient); 108 }; 109 110 int ACMatchStyleToTagStyle(int styles) { 111 int tag_styles = 0; 112 if (styles & ACMatchClassification::URL) 113 tag_styles |= app_list::SearchResult::Tag::URL; 114 if (styles & ACMatchClassification::MATCH) 115 tag_styles |= app_list::SearchResult::Tag::MATCH; 116 if (styles & ACMatchClassification::DIM) 117 tag_styles |= app_list::SearchResult::Tag::DIM; 118 119 return tag_styles; 120 } 121 122 // Translates ACMatchClassifications into SearchResult tags. 123 void ACMatchClassificationsToTags( 124 const base::string16& text, 125 const ACMatchClassifications& text_classes, 126 app_list::SearchResult::Tags* tags) { 127 int tag_styles = app_list::SearchResult::Tag::NONE; 128 size_t tag_start = 0; 129 130 for (size_t i = 0; i < text_classes.size(); ++i) { 131 const ACMatchClassification& text_class = text_classes[i]; 132 133 // Closes current tag. 134 if (tag_styles != app_list::SearchResult::Tag::NONE) { 135 tags->push_back(app_list::SearchResult::Tag( 136 tag_styles, tag_start, text_class.offset)); 137 tag_styles = app_list::SearchResult::Tag::NONE; 138 } 139 140 if (text_class.style == ACMatchClassification::NONE) 141 continue; 142 143 tag_start = text_class.offset; 144 tag_styles = ACMatchStyleToTagStyle(text_class.style); 145 } 146 147 if (tag_styles != app_list::SearchResult::Tag::NONE) { 148 tags->push_back(app_list::SearchResult::Tag( 149 tag_styles, tag_start, text.length())); 150 } 151 } 152 153 class UrlSearchResult : public app_list::SearchResult { 154 public: 155 UrlSearchResult(content::BrowserContext* browser_context, 156 const AutocompleteMatch& match) 157 : browser_context_(browser_context), 158 match_(match) { 159 set_id(match_.destination_url.spec()); 160 161 // Derive relevance from omnibox relevance and normalize it to [0, 1]. 162 // The magic number 1500 is the highest score of an omnibox result. 163 // See comments in autocomplete_provider.h. 164 set_relevance(match_.relevance / 1500.0); 165 166 UpdateIcon(); 167 UpdateTitleAndDetails(); 168 } 169 170 virtual ~UrlSearchResult() {} 171 172 private: 173 // Overriddenn from app_list::SearchResult: 174 virtual void Open(int event_flags) OVERRIDE { 175 Activity* activity = ActivityFactory::Get()->CreateWebActivity( 176 browser_context_, base::string16(), match_.destination_url); 177 Activity::Show(activity); 178 } 179 180 void UpdateIcon() { 181 SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 182 AutocompleteMatch::TypeToIcon(match_.type))); 183 } 184 185 void UpdateTitleAndDetails() { 186 set_title(match_.contents); 187 SearchResult::Tags title_tags; 188 ACMatchClassificationsToTags(match_.contents, 189 match_.contents_class, 190 &title_tags); 191 set_title_tags(title_tags); 192 193 set_details(match_.description); 194 SearchResult::Tags details_tags; 195 ACMatchClassificationsToTags(match_.description, 196 match_.description_class, 197 &details_tags); 198 set_details_tags(details_tags); 199 } 200 201 content::BrowserContext* browser_context_; 202 AutocompleteMatch match_; 203 204 DISALLOW_COPY_AND_ASSIGN(UrlSearchResult); 205 }; 206 207 } // namespace 208 209 UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context) 210 : browser_context_(browser_context), 211 // TODO(mukai): introduce the real parameters when it's necessary. 212 template_url_service_( 213 new TemplateURLService(NULL /* prefs */, 214 scoped_ptr<SearchTermsData>( 215 new AthenaSearchTermsData()), 216 NULL /* KeywordWebDataService */, 217 scoped_ptr<TemplateURLServiceClient>( 218 new AthenaTemplateURLServiceClient()), 219 NULL /*GoogleURLTracker */, 220 NULL /* RapporService */, 221 base::Closure() /* dsp_change_callback */)), 222 provider_(new ::SearchProvider( 223 this, 224 template_url_service_.get(), 225 scoped_ptr<AutocompleteProviderClient>( 226 new AthenaAutocompleteProviderClient(browser_context_)))) { 227 template_url_service_->Load(); 228 } 229 230 UrlSearchProvider::~UrlSearchProvider() { 231 } 232 233 void UrlSearchProvider::Start(const base::string16& query) { 234 const bool minimal_changes = query == input_.text(); 235 scoped_ptr<AutocompleteSchemeClassifier> scheme_classifier( 236 CreateSchemeClassifier(browser_context_)); 237 input_ = AutocompleteInput(query, 238 base::string16::npos /* cursor_position */, 239 base::string16() /* desired_tld */, 240 GURL() /* current_url */, 241 metrics::OmniboxEventProto::INVALID_SPEC, 242 false /* prevent_inline_autocomplete */, 243 false /* prefer_keyword */, 244 true /* allow_extract_keyword_match */, 245 true /* want_asynchronous_matches */, 246 *scheme_classifier); 247 248 // Clearing results here may cause unexpected results. 249 // TODO(mukai): fix this by fixing crbug.com/415500 250 if (!minimal_changes) 251 ClearResults(); 252 253 if (input_.type() == metrics::OmniboxInputType::URL) { 254 // TODO(hashimoto): Componentize HistoryURLProvider and remove this code. 255 AutocompleteMatch what_you_typed_match( 256 NULL, 0, false, AutocompleteMatchType::URL_WHAT_YOU_TYPED); 257 what_you_typed_match.destination_url = input_.canonicalized_url(); 258 what_you_typed_match.contents = input_.text(); 259 what_you_typed_match.relevance = kScoreForWhatYouTypedResult; 260 Add(scoped_ptr<app_list::SearchResult>(new UrlSearchResult( 261 browser_context_, what_you_typed_match))); 262 } 263 264 provider_->Start(input_, minimal_changes); 265 } 266 267 void UrlSearchProvider::Stop() { 268 provider_->Stop(false); 269 } 270 271 void UrlSearchProvider::OnProviderUpdate(bool updated_matches) { 272 if (!updated_matches) 273 return; 274 275 const ACMatches& matches = provider_->matches(); 276 for (ACMatches::const_iterator it = matches.begin(); it != matches.end(); 277 ++it) { 278 if (!it->destination_url.is_valid()) 279 continue; 280 281 Add(scoped_ptr<app_list::SearchResult>(new UrlSearchResult( 282 browser_context_, *it))); 283 } 284 } 285 286 } // namespace athena 287