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_delegate.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 AutocompleteProviderDelegate for Athena. | |
77 class AthenaAutocompleteProviderDelegate : public AutocompleteProviderDelegate { | |
78 public: | |
79 explicit AthenaAutocompleteProviderDelegate( | |
80 content::BrowserContext* browser_context) | |
81 : browser_context_(browser_context) {} | |
82 virtual ~AthenaAutocompleteProviderDelegate() {} | |
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 return "en-US"; | |
Jun Mukai
2014/08/27 17:03:20
Can you put a TODO comment here?
hashimoto
2014/08/28 06:30:09
Done.
| |
92 } | |
93 virtual bool SearchSuggestEnabled() OVERRIDE { return true; } | |
94 virtual bool ShowBookmarkBar() OVERRIDE { return false; } | |
95 virtual const AutocompleteSchemeClassifier& SchemeClassifier() OVERRIDE { | |
96 return scheme_classifier_; | |
97 } | |
98 virtual void Classify( | |
99 const base::string16& text, | |
100 bool prefer_keyword, | |
101 bool allow_exact_keyword_match, | |
102 metrics::OmniboxEventProto::PageClassification page_classification, | |
103 AutocompleteMatch* match, | |
104 GURL* alternate_nav_url) OVERRIDE {} | |
105 virtual history::URLDatabase* InMemoryDatabase() OVERRIDE { return NULL; } | |
106 virtual void DeleteMatchingURLsForKeywordFromHistory( | |
107 history::KeywordID keyword_id, | |
108 const base::string16& term) OVERRIDE {} | |
109 virtual bool TabSyncEnabledAndUnencrypted() OVERRIDE { return false; } | |
110 virtual void PrefetchImage(const GURL& url) OVERRIDE {} | |
111 | |
112 private: | |
113 content::BrowserContext* browser_context_; | |
114 AthenaSchemeClassifier scheme_classifier_; | |
115 | |
116 DISALLOW_COPY_AND_ASSIGN(AthenaAutocompleteProviderDelegate); | |
117 }; | |
118 | |
119 int ACMatchStyleToTagStyle(int styles) { | |
120 int tag_styles = 0; | |
121 if (styles & ACMatchClassification::URL) | |
122 tag_styles |= app_list::SearchResult::Tag::URL; | |
123 if (styles & ACMatchClassification::MATCH) | |
124 tag_styles |= app_list::SearchResult::Tag::MATCH; | |
125 if (styles & ACMatchClassification::DIM) | |
126 tag_styles |= app_list::SearchResult::Tag::DIM; | |
127 | |
128 return tag_styles; | |
129 } | |
130 | |
131 // Translates ACMatchClassifications into SearchResult tags. | |
132 void ACMatchClassificationsToTags( | |
133 const base::string16& text, | |
134 const ACMatchClassifications& text_classes, | |
135 app_list::SearchResult::Tags* tags) { | |
136 int tag_styles = app_list::SearchResult::Tag::NONE; | |
137 size_t tag_start = 0; | |
138 | |
139 for (size_t i = 0; i < text_classes.size(); ++i) { | |
140 const ACMatchClassification& text_class = text_classes[i]; | |
141 | |
142 // Closes current tag. | |
143 if (tag_styles != app_list::SearchResult::Tag::NONE) { | |
144 tags->push_back(app_list::SearchResult::Tag( | |
145 tag_styles, tag_start, text_class.offset)); | |
146 tag_styles = app_list::SearchResult::Tag::NONE; | |
147 } | |
148 | |
149 if (text_class.style == ACMatchClassification::NONE) | |
150 continue; | |
151 | |
152 tag_start = text_class.offset; | |
153 tag_styles = ACMatchStyleToTagStyle(text_class.style); | |
154 } | |
155 | |
156 if (tag_styles != app_list::SearchResult::Tag::NONE) { | |
157 tags->push_back(app_list::SearchResult::Tag( | |
158 tag_styles, tag_start, text.length())); | |
159 } | |
160 } | |
161 | |
78 class UrlSearchResult : public app_list::SearchResult { | 162 class UrlSearchResult : public app_list::SearchResult { |
79 public: | 163 public: |
80 UrlSearchResult(content::BrowserContext* browser_context, | 164 UrlSearchResult(content::BrowserContext* browser_context, |
81 const GURL& url, | 165 const AutocompleteMatch& match) |
82 const base::string16& title) | |
83 : browser_context_(browser_context), | 166 : browser_context_(browser_context), |
84 url_(url) { | 167 match_(match) { |
85 set_title(title); | 168 set_id(match_.destination_url.spec()); |
86 set_id(url_.spec()); | 169 |
170 // Derive relevance from omnibox relevance and normalize it to [0, 1]. | |
171 // The magic number 1500 is the highest score of an omnibox result. | |
172 // See comments in autocomplete_provider.h. | |
173 set_relevance(match_.relevance / 1500.0); | |
174 | |
175 UpdateIcon(); | |
176 UpdateTitleAndDetails(); | |
87 } | 177 } |
88 | 178 |
179 virtual ~UrlSearchResult() {} | |
180 | |
89 private: | 181 private: |
90 // Overriddenn from app_list::SearchResult: | 182 // Overriddenn from app_list::SearchResult: |
91 virtual void Open(int event_flags) OVERRIDE { | 183 virtual void Open(int event_flags) OVERRIDE { |
92 ActivityManager::Get()->AddActivity( | 184 ActivityManager::Get()->AddActivity( |
93 ActivityFactory::Get()->CreateWebActivity(browser_context_, url_)); | 185 ActivityFactory::Get()->CreateWebActivity(browser_context_, |
186 match_.destination_url)); | |
187 } | |
188 | |
189 void UpdateIcon() { | |
190 SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( | |
191 AutocompleteMatch::TypeToIcon(match_.type))); | |
192 } | |
193 | |
194 void UpdateTitleAndDetails() { | |
195 set_title(match_.contents); | |
196 SearchResult::Tags title_tags; | |
197 ACMatchClassificationsToTags(match_.contents, | |
198 match_.contents_class, | |
199 &title_tags); | |
200 set_title_tags(title_tags); | |
201 | |
202 set_details(match_.description); | |
203 SearchResult::Tags details_tags; | |
204 ACMatchClassificationsToTags(match_.description, | |
205 match_.description_class, | |
206 &details_tags); | |
207 set_details_tags(details_tags); | |
94 } | 208 } |
95 | 209 |
96 content::BrowserContext* browser_context_; | 210 content::BrowserContext* browser_context_; |
97 GURL url_; | 211 AutocompleteMatch match_; |
98 | 212 |
99 DISALLOW_COPY_AND_ASSIGN(UrlSearchResult); | 213 DISALLOW_COPY_AND_ASSIGN(UrlSearchResult); |
100 }; | 214 }; |
101 | 215 |
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 | 216 } // namespace |
139 | 217 |
140 UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context) | 218 UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context) |
141 : browser_context_(browser_context), | 219 : browser_context_(browser_context), |
142 // TODO(mukai): introduce the real parameters when it's necessary. | 220 // TODO(mukai): introduce the real parameters when it's necessary. |
143 template_url_service_( | 221 template_url_service_( |
144 new TemplateURLService(NULL /* prefs */, | 222 new TemplateURLService(NULL /* prefs */, |
145 scoped_ptr<SearchTermsData>( | 223 scoped_ptr<SearchTermsData>( |
146 new AthenaSearchTermsData()), | 224 new AthenaSearchTermsData()), |
147 NULL /* KeywordWebDataService */, | 225 NULL /* KeywordWebDataService */, |
148 scoped_ptr<TemplateURLServiceClient>( | 226 scoped_ptr<TemplateURLServiceClient>( |
149 new AthenaTemplateURLServiceClient()), | 227 new AthenaTemplateURLServiceClient()), |
150 NULL /*GoogleURLTracker */, | 228 NULL /*GoogleURLTracker */, |
151 NULL /* RapporService */, | 229 NULL /* RapporService */, |
152 base::Closure() /* dsp_change_callback */)), | 230 base::Closure() /* dsp_change_callback */)), |
153 should_fetch_suggestions_again_(false) { | 231 provider_(new ::SearchProvider( |
232 this, | |
233 template_url_service_.get(), | |
234 scoped_ptr<AutocompleteProviderDelegate>( | |
235 new AthenaAutocompleteProviderDelegate(browser_context_)))) { | |
154 template_url_service_->Load(); | 236 template_url_service_->Load(); |
155 } | 237 } |
156 | 238 |
157 UrlSearchProvider::~UrlSearchProvider() { | 239 UrlSearchProvider::~UrlSearchProvider() { |
158 } | 240 } |
159 | 241 |
160 void UrlSearchProvider::Start(const base::string16& query) { | 242 void UrlSearchProvider::Start(const base::string16& query) { |
243 const bool minimal_changes = query == input_.text(); | |
161 input_ = AutocompleteInput(query, | 244 input_ = AutocompleteInput(query, |
162 base::string16::npos /* cursor_position */, | 245 base::string16::npos /* cursor_position */, |
163 base::string16() /* desired_tld */, | 246 base::string16() /* desired_tld */, |
164 GURL() /* current_url */, | 247 GURL() /* current_url */, |
165 metrics::OmniboxEventProto::INVALID_SPEC, | 248 metrics::OmniboxEventProto::INVALID_SPEC, |
166 false /* prevent_inline_autocomplete */, | 249 false /* prevent_inline_autocomplete */, |
167 false /* prefer_keyword */, | 250 false /* prefer_keyword */, |
168 true /* allow_extract_keyword_match */, | 251 true /* allow_extract_keyword_match */, |
169 true /* want_asynchronous_matches */, | 252 true /* want_asynchronous_matches */, |
170 AthenaSchemeClassifier()); | 253 AthenaSchemeClassifier()); |
171 ClearResults(); | 254 |
172 Add(CreateResultForInput(browser_context_, template_url_service_.get(), | 255 provider_->Start(input_, minimal_changes); |
173 input_)); | |
174 StartFetchingSuggestions(); | |
175 } | 256 } |
176 | 257 |
177 void UrlSearchProvider::Stop() { | 258 void UrlSearchProvider::Stop() { |
178 suggestion_fetcher_.reset(); | 259 provider_->Stop(false); |
179 } | 260 } |
180 | 261 |
181 void UrlSearchProvider::OnURLFetchComplete(const net::URLFetcher* source) { | 262 void UrlSearchProvider::OnProviderUpdate(bool updated_matches) { |
182 DCHECK_EQ(suggestion_fetcher_.get(), source); | 263 if (!updated_matches) |
264 return; | |
183 | 265 |
184 if (source->GetStatus().is_success() && source->GetResponseCode() == 200) { | 266 ClearResults(); |
185 std::string json_data = SearchSuggestionParser::ExtractJsonData(source); | 267 |
186 scoped_ptr<base::Value> data( | 268 const ACMatches& matches = provider_->matches(); |
187 SearchSuggestionParser::DeserializeJsonData(json_data)); | 269 for (ACMatches::const_iterator it = matches.begin(); it != matches.end(); |
188 if (data) { | 270 ++it) { |
189 const int kDefaultRelevance = 0; | 271 if (!it->destination_url.is_valid()) |
190 SearchSuggestionParser::Results results; | 272 continue; |
191 if (SearchSuggestionParser::ParseSuggestResults( | 273 |
192 *data, input_, AthenaSchemeClassifier(), kDefaultRelevance, | 274 Add(scoped_ptr<app_list::SearchResult>(new UrlSearchResult( |
193 std::string(), // languages | 275 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 } | 276 } |
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 } | 277 } |
246 | 278 |
247 } // namespace athena | 279 } // namespace athena |
OLD | NEW |