Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(74)

Side by Side Diff: chrome/browser/autocomplete/zero_suggest_provider.cc

Issue 10832256: Experimental AutocompleteProvider for zerosuggest. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Hyphenate flag name. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/autocomplete/zero_suggest_provider.h"
6
7 #include "base/callback.h"
8 #include "base/i18n/icu_string_conversions.h"
9 #include "base/json/json_string_value_serializer.h"
10 #include "base/string16.h"
11 #include "base/string_util.h"
12 #include "base/time.h"
13 #include "base/utf_string_conversions.h"
14 #include "chrome/browser/autocomplete/autocomplete_input.h"
15 #include "chrome/browser/autocomplete/autocomplete_match.h"
16 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/search_engines/template_url_service.h"
19 #include "chrome/browser/search_engines/template_url_service_factory.h"
20 #include "chrome/common/url_constants.h"
21 #include "googleurl/src/gurl.h"
22 #include "net/base/load_flags.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/url_request/url_fetcher.h"
25 #include "net/url_request/url_request_status.h"
26
27 ZeroSuggestProvider::ZeroSuggestProvider(
28 AutocompleteProviderListener* listener,
29 Profile* profile,
30 const std::string& url_prefix)
31 : AutocompleteProvider(listener, profile, "ZeroSuggest"),
32 url_prefix_(url_prefix),
33 template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)) {
34 }
35
36 void ZeroSuggestProvider::Start(const AutocompleteInput& input,
37 bool /*minimal_changes*/) {
38 UpdateMatches(input.text());
39 }
40
41 void ZeroSuggestProvider::StartZeroSuggest(
42 const GURL& url,
Peter Kasting 2012/08/14 19:20:02 Nit: Can fit on previous line, then indent next ar
Jered 2012/08/14 22:27:11 Done.
43 const string16& user_text) {
44 const std::string& spec = url.possibly_invalid_spec();
Peter Kasting 2012/08/14 19:20:02 Shouldn't |url| always be valid here? Seems like
Jered 2012/08/14 22:27:11 It may be empty. But invalid URLs will not be wort
45 if (spec.empty() || url.scheme() != chrome::kHttpScheme)
Peter Kasting 2012/08/14 19:20:02 How can the spec be empty? If it can, then my com
Jered 2012/08/14 22:27:11 It's empty on initial focus. Revised.
Peter Kasting 2012/08/15 05:30:39 Wow, really? That seems odd. The caller passes i
Jered 2012/08/15 16:32:12 Yep.
Peter Kasting 2012/08/15 17:35:31 You're never at a blank URL, because that's not ac
Jered 2012/08/15 18:15:21 Done.
46 // Do not query empty URLs nor non-http URLs. There will be no useful
47 // suggestions for https or chrome URLs.
48 return;
49 matches_.clear();
50 done_ = false;
51 user_text_ = user_text;
52 current_query_ = spec;
53 current_query_text_ = ASCIIToUTF16(spec);
54 Run();
55 }
56
57 void ZeroSuggestProvider::Stop(bool clear_cached_results) {
58 fetcher_.reset();
59 done_ = true;
60 if (clear_cached_results) {
61 results_.clear();
62 current_query_.clear();
63 current_query_text_.clear();
64 }
65 }
66
67 void ZeroSuggestProvider::OnURLFetchComplete(const net::URLFetcher* source) {
68 const net::HttpResponseHeaders* const response_headers =
69 source->GetResponseHeaders();
70 std::string json_data;
71 source->GetResponseAsString(&json_data);
72 if (response_headers) {
73 std::string charset;
74 if (response_headers->GetCharset(&charset)) {
75 string16 data_16;
76 if (base::CodepageToUTF16(json_data, charset.c_str(),
Peter Kasting 2012/08/14 19:20:02 Why do charset -> utf16 -> utf8 instead of just ch
Jered 2012/08/14 22:27:11 This code comes from SearchProvider. Our test serv
77 base::OnStringConversionError::FAIL,
78 &data_16))
79 json_data = UTF16ToUTF8(data_16);
80 }
81 }
82 const bool request_succeeded =
83 source->GetStatus().is_success() && source->GetResponseCode() == 200;
84
85 bool results_updated = false;
86 if (request_succeeded) {
87 JSONStringValueSerializer deserializer(json_data);
88 deserializer.set_allow_trailing_comma(true);
89 scoped_ptr<Value> data(deserializer.Deserialize(NULL, NULL));
90 results_updated = data.get() && ParseSuggestResults(data.get());
91 }
92 done_ = true;
93
94 ConvertResultsToAutocompleteMatches();
95 if (results_updated)
96 listener_->OnProviderUpdate(results_updated);
97 }
98
99 ZeroSuggestProvider::~ZeroSuggestProvider() {
100 }
101
102 void ZeroSuggestProvider::Run() {
103 const int kFetcherID = 1;
104 fetcher_.reset(
105 net::URLFetcher::Create(kFetcherID,
106 GURL(url_prefix_ + current_query_),
107 net::URLFetcher::GET, this));
108 fetcher_->SetRequestContext(profile_->GetRequestContext());
109 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
110 fetcher_->Start();
111 }
112
113 void ZeroSuggestProvider::UpdateMatches(const string16& user_text) {
114 user_text_ = user_text;
115 const size_t prev_num_matches = matches_.size();
116 ConvertResultsToAutocompleteMatches();
117 if (matches_.size() != prev_num_matches)
118 listener_->OnProviderUpdate(true);
119 }
120
121 bool ZeroSuggestProvider::ParseSuggestResults(Value* root_val) {
122 std::string query;
123 ListValue* root_list = NULL;
124 ListValue* results = NULL;
125 if (!root_val->GetAsList(&root_list) || !root_list->GetString(0, &query) ||
126 (query != current_query_) || !root_list->GetList(1, &results))
127 return false;
128
129 results_.clear();
130 ListValue* one_result = NULL;
131 for (size_t index = 0; results->GetList(index, &one_result); ++index) {
132 string16 result;
133 one_result->GetString(0, &result);
134 if (result.empty())
135 continue;
136 results_.push_back(result);
137 }
138
139 return true;
140 }
141
142 void ZeroSuggestProvider::ConvertResultsToAutocompleteMatches() {
143 const TemplateURL* search_provider =
144 template_url_service_->GetDefaultSearchProvider();
145 if (search_provider == NULL || !search_provider->SupportsReplacement())
146 // Fail if we can't set the clickthrough URL for query suggestions.
147 return;
148 matches_.clear();
149 if (results_.empty())
150 // Do not add anything if there are no results for this URL.
151 return;
152 AddMatchForCurrentURL();
153 for (size_t i = 0; i < results_.size(); ++i)
154 AddMatchForResult(search_provider, i, results_[i]);
155 }
156
157 // TODO(jered): Rip this out once the first match is decoupled from the current
158 // typing in the omnibox.
159 void ZeroSuggestProvider::AddMatchForCurrentURL() {
160 // If the user has typed something besides the current url, they probably
161 // don't intend to refresh it.
162 const bool user_text_is_url = user_text_ == current_query_text_;
163 if (user_text_.empty() || user_text_is_url) {
164 // The placeholder suggestion for the current URL should have the max
165 // relevance score when shown so that it is above other suggestions.
166 const int kMaxRelevance = 2000;
167 AutocompleteMatch match(this, kMaxRelevance, false,
Peter Kasting 2012/08/14 19:20:02 The relevance scores you use here and below mean t
Jered 2012/08/14 22:27:11 This placeholder nav suggestion will get dropped w
168 AutocompleteMatch::SEARCH_ZEROSUGGEST_URL);
169 match.destination_url = GURL(current_query_);
170 match.contents = current_query_text_;
171 if (!user_text_is_url) {
172 match.fill_into_edit = current_query_text_;
173 match.inline_autocomplete_offset = 0;
174 }
175 AutocompleteMatch::ClassifyLocationInString(0, current_query_.size(),
176 match.contents.length(), ACMatchClassification::URL,
177 &match.contents_class);
178 matches_.push_back(match);
179 }
180 }
181
182 void ZeroSuggestProvider::AddMatchForResult(
183 const TemplateURL* search_provider,
184 size_t result_index,
185 const string16& result) {
186 // TODO(jered): Rip out user_text_is_url logic when AddMatchForCurrentURL
187 // goes away.
188 const bool user_text_is_url = user_text_ == current_query_text_;
189 const bool kCaseInsensitve = false;
190 if (!user_text_.empty() && !user_text_is_url &&
191 !StartsWith(result, user_text_, kCaseInsensitve))
192 // This suggestion isn't relevant for the current prefix.
193 return;
194 // This bogus relevance is just below max so that these suggestions are
195 // beneath the current URL match from AddMatchForCurrentURL().
196 // TODO(jered): Use real scores from the suggestion server.
197 const int kRelevance = 1999;
198 AutocompleteMatch match(this, kRelevance, false,
199 AutocompleteMatch::SEARCH_ZEROSUGGEST);
200 match.contents = result;
201 match.fill_into_edit = result;
202 if (!user_text_is_url && user_text_ != result)
203 match.inline_autocomplete_offset = user_text_.length();
204
205 // Build a URL for this query using the default search provider.
206 const TemplateURLRef& search_url = search_provider->url_ref();
207 DCHECK(search_url.SupportsReplacement());
208 match.search_terms_args.reset(
209 new TemplateURLRef::SearchTermsArgs(result));
210 match.search_terms_args->original_query = string16();
211 match.search_terms_args->accepted_suggestion = result_index;
212 match.destination_url =
213 GURL(search_url.ReplaceSearchTerms(*match.search_terms_args.get()));
214
215 if (user_text_.empty() || user_text_is_url || user_text_ == result) {
216 match.contents_class.push_back(
217 ACMatchClassification(0, ACMatchClassification::NONE));
218 } else {
219 // Style to look like normal search suggestions.
220 match.contents_class.push_back(
221 ACMatchClassification(0, ACMatchClassification::DIM));
222 match.contents_class.push_back(
223 ACMatchClassification(user_text_.length(), ACMatchClassification::NONE));
224 }
225 match.transition = content::PAGE_TRANSITION_GENERATED;
226
227 matches_.push_back(match);
228 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698