OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "athena/main/url_search_provider.h" | 5 #include "athena/main/url_search_provider.h" |
6 | 6 |
7 #include "athena/activity/public/activity_factory.h" | 7 #include "athena/activity/public/activity_factory.h" |
8 #include "athena/activity/public/activity_manager.h" | 8 #include "athena/activity/public/activity_manager.h" |
9 #include "base/strings/utf_string_conversions.h" | 9 #include "base/strings/utf_string_conversions.h" |
10 #include "base/values.h" | 10 #include "base/values.h" |
11 #include "components/metrics/proto/omnibox_event.pb.h" | 11 #include "components/metrics/proto/omnibox_event.pb.h" |
12 #include "components/metrics/proto/omnibox_input_type.pb.h" | 12 #include "components/metrics/proto/omnibox_input_type.pb.h" |
13 #include "components/omnibox/autocomplete_input.h" | 13 #include "components/omnibox/autocomplete_input.h" |
| 14 #include "components/omnibox/autocomplete_provider_client.h" |
14 #include "components/omnibox/autocomplete_scheme_classifier.h" | 15 #include "components/omnibox/autocomplete_scheme_classifier.h" |
15 #include "components/omnibox/search_suggestion_parser.h" | 16 #include "components/omnibox/search_provider.h" |
16 #include "components/search_engines/search_terms_data.h" | 17 #include "components/search_engines/search_terms_data.h" |
17 #include "components/search_engines/template_url.h" | |
18 #include "components/search_engines/template_url_service.h" | 18 #include "components/search_engines/template_url_service.h" |
19 #include "components/search_engines/template_url_service_client.h" | 19 #include "components/search_engines/template_url_service_client.h" |
20 #include "content/public/browser/browser_context.h" | 20 #include "content/public/browser/browser_context.h" |
21 #include "net/base/load_flags.h" | |
22 #include "net/url_request/url_fetcher.h" | |
23 #include "net/url_request/url_request.h" | |
24 #include "ui/app_list/search_result.h" | 21 #include "ui/app_list/search_result.h" |
| 22 #include "ui/base/resource/resource_bundle.h" |
25 #include "url/gurl.h" | 23 #include "url/gurl.h" |
26 | 24 |
27 namespace athena { | 25 namespace athena { |
28 | 26 |
29 namespace { | 27 namespace { |
30 | 28 |
31 // The SearchTermsData implementation for Athena. | 29 // The SearchTermsData implementation for Athena. |
32 class AthenaSearchTermsData : public SearchTermsData { | 30 class AthenaSearchTermsData : public SearchTermsData { |
33 public: | 31 public: |
34 // SearchTermsData: | 32 // SearchTermsData: |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
68 const GURL& url, | 66 const GURL& url, |
69 TemplateURLID id, | 67 TemplateURLID id, |
70 const base::string16& term) OVERRIDE {} | 68 const base::string16& term) OVERRIDE {} |
71 virtual void AddKeywordGeneratedVisit(const GURL& url) OVERRIDE {} | 69 virtual void AddKeywordGeneratedVisit(const GURL& url) OVERRIDE {} |
72 virtual void RestoreExtensionInfoIfNecessary( | 70 virtual void RestoreExtensionInfoIfNecessary( |
73 TemplateURL* template_url) OVERRIDE {} | 71 TemplateURL* template_url) OVERRIDE {} |
74 | 72 |
75 DISALLOW_COPY_AND_ASSIGN(AthenaTemplateURLServiceClient); | 73 DISALLOW_COPY_AND_ASSIGN(AthenaTemplateURLServiceClient); |
76 }; | 74 }; |
77 | 75 |
| 76 // The AutocompleteProviderClient for Athena. |
| 77 class AthenaAutocompleteProviderClient : public AutocompleteProviderClient { |
| 78 public: |
| 79 explicit AthenaAutocompleteProviderClient( |
| 80 content::BrowserContext* browser_context) |
| 81 : browser_context_(browser_context) {} |
| 82 virtual ~AthenaAutocompleteProviderClient() {} |
| 83 |
| 84 virtual net::URLRequestContextGetter* RequestContext() OVERRIDE { |
| 85 return browser_context_->GetRequestContext(); |
| 86 } |
| 87 virtual bool IsOffTheRecord() OVERRIDE { |
| 88 return browser_context_->IsOffTheRecord(); |
| 89 } |
| 90 virtual std::string AcceptLanguages() OVERRIDE { |
| 91 // TODO(hashimoto): Return the value stored in the prefs. |
| 92 return "en-US"; |
| 93 } |
| 94 virtual bool SearchSuggestEnabled() OVERRIDE { return true; } |
| 95 virtual bool ShowBookmarkBar() OVERRIDE { return false; } |
| 96 virtual const AutocompleteSchemeClassifier& SchemeClassifier() OVERRIDE { |
| 97 return scheme_classifier_; |
| 98 } |
| 99 virtual void Classify( |
| 100 const base::string16& text, |
| 101 bool prefer_keyword, |
| 102 bool allow_exact_keyword_match, |
| 103 metrics::OmniboxEventProto::PageClassification page_classification, |
| 104 AutocompleteMatch* match, |
| 105 GURL* alternate_nav_url) OVERRIDE {} |
| 106 virtual history::URLDatabase* InMemoryDatabase() OVERRIDE { return NULL; } |
| 107 virtual void DeleteMatchingURLsForKeywordFromHistory( |
| 108 history::KeywordID keyword_id, |
| 109 const base::string16& term) OVERRIDE {} |
| 110 virtual bool TabSyncEnabledAndUnencrypted() OVERRIDE { return false; } |
| 111 virtual void PrefetchImage(const GURL& url) OVERRIDE {} |
| 112 |
| 113 private: |
| 114 content::BrowserContext* browser_context_; |
| 115 AthenaSchemeClassifier scheme_classifier_; |
| 116 |
| 117 DISALLOW_COPY_AND_ASSIGN(AthenaAutocompleteProviderClient); |
| 118 }; |
| 119 |
| 120 int ACMatchStyleToTagStyle(int styles) { |
| 121 int tag_styles = 0; |
| 122 if (styles & ACMatchClassification::URL) |
| 123 tag_styles |= app_list::SearchResult::Tag::URL; |
| 124 if (styles & ACMatchClassification::MATCH) |
| 125 tag_styles |= app_list::SearchResult::Tag::MATCH; |
| 126 if (styles & ACMatchClassification::DIM) |
| 127 tag_styles |= app_list::SearchResult::Tag::DIM; |
| 128 |
| 129 return tag_styles; |
| 130 } |
| 131 |
| 132 // Translates ACMatchClassifications into SearchResult tags. |
| 133 void ACMatchClassificationsToTags( |
| 134 const base::string16& text, |
| 135 const ACMatchClassifications& text_classes, |
| 136 app_list::SearchResult::Tags* tags) { |
| 137 int tag_styles = app_list::SearchResult::Tag::NONE; |
| 138 size_t tag_start = 0; |
| 139 |
| 140 for (size_t i = 0; i < text_classes.size(); ++i) { |
| 141 const ACMatchClassification& text_class = text_classes[i]; |
| 142 |
| 143 // Closes current tag. |
| 144 if (tag_styles != app_list::SearchResult::Tag::NONE) { |
| 145 tags->push_back(app_list::SearchResult::Tag( |
| 146 tag_styles, tag_start, text_class.offset)); |
| 147 tag_styles = app_list::SearchResult::Tag::NONE; |
| 148 } |
| 149 |
| 150 if (text_class.style == ACMatchClassification::NONE) |
| 151 continue; |
| 152 |
| 153 tag_start = text_class.offset; |
| 154 tag_styles = ACMatchStyleToTagStyle(text_class.style); |
| 155 } |
| 156 |
| 157 if (tag_styles != app_list::SearchResult::Tag::NONE) { |
| 158 tags->push_back(app_list::SearchResult::Tag( |
| 159 tag_styles, tag_start, text.length())); |
| 160 } |
| 161 } |
| 162 |
78 class UrlSearchResult : public app_list::SearchResult { | 163 class UrlSearchResult : public app_list::SearchResult { |
79 public: | 164 public: |
80 UrlSearchResult(content::BrowserContext* browser_context, | 165 UrlSearchResult(content::BrowserContext* browser_context, |
81 const GURL& url, | 166 const AutocompleteMatch& match) |
82 const base::string16& title) | |
83 : browser_context_(browser_context), | 167 : browser_context_(browser_context), |
84 url_(url) { | 168 match_(match) { |
85 set_title(title); | 169 set_id(match_.destination_url.spec()); |
86 set_id(url_.spec()); | 170 |
| 171 // Derive relevance from omnibox relevance and normalize it to [0, 1]. |
| 172 // The magic number 1500 is the highest score of an omnibox result. |
| 173 // See comments in autocomplete_provider.h. |
| 174 set_relevance(match_.relevance / 1500.0); |
| 175 |
| 176 UpdateIcon(); |
| 177 UpdateTitleAndDetails(); |
87 } | 178 } |
88 | 179 |
| 180 virtual ~UrlSearchResult() {} |
| 181 |
89 private: | 182 private: |
90 // Overriddenn from app_list::SearchResult: | 183 // Overriddenn from app_list::SearchResult: |
91 virtual void Open(int event_flags) OVERRIDE { | 184 virtual void Open(int event_flags) OVERRIDE { |
92 ActivityManager::Get()->AddActivity( | 185 ActivityManager::Get()->AddActivity( |
93 ActivityFactory::Get()->CreateWebActivity(browser_context_, url_)); | 186 ActivityFactory::Get()->CreateWebActivity(browser_context_, |
| 187 match_.destination_url)); |
| 188 } |
| 189 |
| 190 void UpdateIcon() { |
| 191 SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
| 192 AutocompleteMatch::TypeToIcon(match_.type))); |
| 193 } |
| 194 |
| 195 void UpdateTitleAndDetails() { |
| 196 set_title(match_.contents); |
| 197 SearchResult::Tags title_tags; |
| 198 ACMatchClassificationsToTags(match_.contents, |
| 199 match_.contents_class, |
| 200 &title_tags); |
| 201 set_title_tags(title_tags); |
| 202 |
| 203 set_details(match_.description); |
| 204 SearchResult::Tags details_tags; |
| 205 ACMatchClassificationsToTags(match_.description, |
| 206 match_.description_class, |
| 207 &details_tags); |
| 208 set_details_tags(details_tags); |
94 } | 209 } |
95 | 210 |
96 content::BrowserContext* browser_context_; | 211 content::BrowserContext* browser_context_; |
97 GURL url_; | 212 AutocompleteMatch match_; |
98 | 213 |
99 DISALLOW_COPY_AND_ASSIGN(UrlSearchResult); | 214 DISALLOW_COPY_AND_ASSIGN(UrlSearchResult); |
100 }; | 215 }; |
101 | 216 |
102 scoped_ptr<app_list::SearchResult> CreateResultForSearchQuery( | |
103 content::BrowserContext* browser_context, | |
104 TemplateURLService* template_url_service, | |
105 const base::string16& search_query) { | |
106 TemplateURL* template_url = | |
107 template_url_service->GetDefaultSearchProvider(); | |
108 const TemplateURLRef& search_url = template_url->url_ref(); | |
109 TemplateURLRef::SearchTermsArgs search_terms_args(search_query); | |
110 return scoped_ptr<app_list::SearchResult>(new UrlSearchResult( | |
111 browser_context, | |
112 GURL(search_url.ReplaceSearchTerms( | |
113 search_terms_args, template_url_service->search_terms_data())), | |
114 search_query)); | |
115 } | |
116 | |
117 scoped_ptr<app_list::SearchResult> CreateResultForInput( | |
118 content::BrowserContext* browser_context, | |
119 TemplateURLService* template_url_service, | |
120 const AutocompleteInput& input) { | |
121 scoped_ptr<app_list::SearchResult> result; | |
122 app_list::SearchResult::Tags title_tags; | |
123 if (input.type() == metrics::OmniboxInputType::URL) { | |
124 result.reset(new UrlSearchResult( | |
125 browser_context, input.canonicalized_url(), input.text())); | |
126 title_tags.push_back(app_list::SearchResult::Tag( | |
127 app_list::SearchResult::Tag::URL, 0, input.text().size())); | |
128 } else { | |
129 result = CreateResultForSearchQuery( | |
130 browser_context, template_url_service, input.text()); | |
131 } | |
132 title_tags.push_back(app_list::SearchResult::Tag( | |
133 app_list::SearchResult::Tag::MATCH, 0, input.text().size())); | |
134 result->set_title_tags(title_tags); | |
135 return result.Pass(); | |
136 } | |
137 | |
138 } // namespace | 217 } // namespace |
139 | 218 |
140 UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context) | 219 UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context) |
141 : browser_context_(browser_context), | 220 : browser_context_(browser_context), |
142 // TODO(mukai): introduce the real parameters when it's necessary. | 221 // TODO(mukai): introduce the real parameters when it's necessary. |
143 template_url_service_( | 222 template_url_service_( |
144 new TemplateURLService(NULL /* prefs */, | 223 new TemplateURLService(NULL /* prefs */, |
145 scoped_ptr<SearchTermsData>( | 224 scoped_ptr<SearchTermsData>( |
146 new AthenaSearchTermsData()), | 225 new AthenaSearchTermsData()), |
147 NULL /* KeywordWebDataService */, | 226 NULL /* KeywordWebDataService */, |
148 scoped_ptr<TemplateURLServiceClient>( | 227 scoped_ptr<TemplateURLServiceClient>( |
149 new AthenaTemplateURLServiceClient()), | 228 new AthenaTemplateURLServiceClient()), |
150 NULL /*GoogleURLTracker */, | 229 NULL /*GoogleURLTracker */, |
151 NULL /* RapporService */, | 230 NULL /* RapporService */, |
152 base::Closure() /* dsp_change_callback */)), | 231 base::Closure() /* dsp_change_callback */)), |
153 should_fetch_suggestions_again_(false) { | 232 provider_(new ::SearchProvider( |
| 233 this, |
| 234 template_url_service_.get(), |
| 235 scoped_ptr<AutocompleteProviderClient>( |
| 236 new AthenaAutocompleteProviderClient(browser_context_)))) { |
154 template_url_service_->Load(); | 237 template_url_service_->Load(); |
155 } | 238 } |
156 | 239 |
157 UrlSearchProvider::~UrlSearchProvider() { | 240 UrlSearchProvider::~UrlSearchProvider() { |
158 } | 241 } |
159 | 242 |
160 void UrlSearchProvider::Start(const base::string16& query) { | 243 void UrlSearchProvider::Start(const base::string16& query) { |
| 244 const bool minimal_changes = query == input_.text(); |
161 input_ = AutocompleteInput(query, | 245 input_ = AutocompleteInput(query, |
162 base::string16::npos /* cursor_position */, | 246 base::string16::npos /* cursor_position */, |
163 base::string16() /* desired_tld */, | 247 base::string16() /* desired_tld */, |
164 GURL() /* current_url */, | 248 GURL() /* current_url */, |
165 metrics::OmniboxEventProto::INVALID_SPEC, | 249 metrics::OmniboxEventProto::INVALID_SPEC, |
166 false /* prevent_inline_autocomplete */, | 250 false /* prevent_inline_autocomplete */, |
167 false /* prefer_keyword */, | 251 false /* prefer_keyword */, |
168 true /* allow_extract_keyword_match */, | 252 true /* allow_extract_keyword_match */, |
169 true /* want_asynchronous_matches */, | 253 true /* want_asynchronous_matches */, |
170 AthenaSchemeClassifier()); | 254 AthenaSchemeClassifier()); |
171 ClearResults(); | 255 |
172 Add(CreateResultForInput(browser_context_, template_url_service_.get(), | 256 provider_->Start(input_, minimal_changes); |
173 input_)); | |
174 StartFetchingSuggestions(); | |
175 } | 257 } |
176 | 258 |
177 void UrlSearchProvider::Stop() { | 259 void UrlSearchProvider::Stop() { |
178 suggestion_fetcher_.reset(); | 260 provider_->Stop(false); |
179 } | 261 } |
180 | 262 |
181 void UrlSearchProvider::OnURLFetchComplete(const net::URLFetcher* source) { | 263 void UrlSearchProvider::OnProviderUpdate(bool updated_matches) { |
182 DCHECK_EQ(suggestion_fetcher_.get(), source); | 264 if (!updated_matches) |
| 265 return; |
183 | 266 |
184 if (source->GetStatus().is_success() && source->GetResponseCode() == 200) { | 267 ClearResults(); |
185 std::string json_data = SearchSuggestionParser::ExtractJsonData(source); | 268 |
186 scoped_ptr<base::Value> data( | 269 const ACMatches& matches = provider_->matches(); |
187 SearchSuggestionParser::DeserializeJsonData(json_data)); | 270 for (ACMatches::const_iterator it = matches.begin(); it != matches.end(); |
188 if (data) { | 271 ++it) { |
189 const int kDefaultRelevance = 0; | 272 if (!it->destination_url.is_valid()) |
190 SearchSuggestionParser::Results results; | 273 continue; |
191 if (SearchSuggestionParser::ParseSuggestResults( | 274 |
192 *data, input_, AthenaSchemeClassifier(), kDefaultRelevance, | 275 Add(scoped_ptr<app_list::SearchResult>(new UrlSearchResult( |
193 std::string(), // languages | 276 browser_context_, *it))); |
194 false, // is_keyword_result | |
195 &results)) { | |
196 ClearResults(); | |
197 Add(CreateResultForInput(browser_context_, template_url_service_.get(), | |
198 input_)); | |
199 for (size_t i = 0; i < results.suggest_results.size(); ++i) { | |
200 const SearchSuggestionParser::SuggestResult& result = | |
201 results.suggest_results[i]; | |
202 Add(CreateResultForSearchQuery(browser_context_, | |
203 template_url_service_.get(), | |
204 result.suggestion())); | |
205 } | |
206 for (size_t i = 0; i < results.navigation_results.size(); ++i) { | |
207 const SearchSuggestionParser::NavigationResult& result = | |
208 results.navigation_results[i]; | |
209 Add(scoped_ptr<app_list::SearchResult>( | |
210 new UrlSearchResult(browser_context_, result.url(), | |
211 result.description()))); | |
212 } | |
213 } | |
214 } | |
215 } | 277 } |
216 suggestion_fetcher_.reset(); | |
217 if (should_fetch_suggestions_again_) | |
218 StartFetchingSuggestions(); | |
219 } | |
220 | |
221 void UrlSearchProvider::StartFetchingSuggestions() { | |
222 if (suggestion_fetcher_) { | |
223 should_fetch_suggestions_again_ = true; | |
224 return; | |
225 } | |
226 should_fetch_suggestions_again_ = false; | |
227 | |
228 // Bail if the suggestion URL is invalid with the given replacements. | |
229 TemplateURL* template_url = template_url_service_->GetDefaultSearchProvider(); | |
230 TemplateURLRef::SearchTermsArgs search_term_args(input_.text()); | |
231 search_term_args.input_type = input_.type(); | |
232 search_term_args.cursor_position = input_.cursor_position(); | |
233 search_term_args.page_classification = input_.current_page_classification(); | |
234 GURL suggest_url(template_url->suggestions_url_ref().ReplaceSearchTerms( | |
235 search_term_args, template_url_service_->search_terms_data())); | |
236 if (!suggest_url.is_valid()) { | |
237 DLOG(ERROR) << "Invalid URL: " << suggest_url; | |
238 return; | |
239 } | |
240 suggestion_fetcher_.reset( | |
241 net::URLFetcher::Create(suggest_url, net::URLFetcher::GET, this)); | |
242 suggestion_fetcher_->SetRequestContext(browser_context_->GetRequestContext()); | |
243 suggestion_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); | |
244 suggestion_fetcher_->Start(); | |
245 } | 278 } |
246 | 279 |
247 } // namespace athena | 280 } // namespace athena |
OLD | NEW |