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

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: Address comments, avoid stale matches. 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/json/json_string_value_serializer.h"
9 #include "base/string16.h"
10 #include "base/string_util.h"
11 #include "base/time.h"
12 #include "base/utf_string_conversions.h"
13 #include "chrome/browser/autocomplete/autocomplete_input.h"
14 #include "chrome/browser/autocomplete/autocomplete_match.h"
15 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/search_engines/template_url_service.h"
18 #include "chrome/browser/search_engines/template_url_service_factory.h"
19 #include "chrome/common/url_constants.h"
20 #include "googleurl/src/gurl.h"
21 #include "net/base/load_flags.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/url_request/url_fetcher.h"
24 #include "net/url_request/url_request_status.h"
25
26 namespace {
27 // The relevance of the top match from this provider.
28 const int kMaxZeroSuggestRelevance = 100;
29 } // namespace
30
31 ZeroSuggestProvider::ZeroSuggestProvider(
32 AutocompleteProviderListener* listener,
33 Profile* profile,
34 const std::string& url_prefix)
35 : AutocompleteProvider(listener, profile, "ZeroSuggest"),
36 url_prefix_(url_prefix),
37 template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)) {
38 }
39
40 void ZeroSuggestProvider::Start(const AutocompleteInput& input,
41 bool /*minimal_changes*/) {
42 UpdateMatches(input.text());
43 }
44
45 void ZeroSuggestProvider::StartZeroSuggest(const GURL& url,
46 const string16& user_text) {
47 DCHECK(url.is_valid());
48 // Do not query non-http URLs. There will be no useful suggestions for https
49 // or chrome URLs.
50 if (url.scheme() != chrome::kHttpScheme)
51 return;
52 matches_.clear();
53 done_ = false;
54 user_text_ = user_text;
55 current_query_ = url.spec();
56 // TODO(jered): Consider adding locally-sourced zero-suggestions here too.
57 // These may be useful on the NTP or more relevant to the user than server
58 // suggestions, if based on local browsing history.
59 Run();
60 }
61
62 void ZeroSuggestProvider::Stop(bool clear_cached_results) {
63 fetcher_.reset();
64 done_ = true;
65 if (clear_cached_results) {
66 results_.clear();
67 current_query_.clear();
68 }
69 }
70
71 void ZeroSuggestProvider::OnURLFetchComplete(const net::URLFetcher* source) {
72 std::string json_data;
73 source->GetResponseAsString(&json_data);
74 const bool request_succeeded =
75 source->GetStatus().is_success() && source->GetResponseCode() == 200;
76
77 bool results_updated = false;
78 if (request_succeeded) {
79 JSONStringValueSerializer deserializer(json_data);
80 deserializer.set_allow_trailing_comma(true);
81 scoped_ptr<Value> data(deserializer.Deserialize(NULL, NULL));
82 results_updated = data.get() && ParseSuggestResults(data.get());
83 }
84 done_ = true;
85
86 ConvertResultsToAutocompleteMatches();
87 if (results_updated)
88 listener_->OnProviderUpdate(true);
89 }
90
91 ZeroSuggestProvider::~ZeroSuggestProvider() {
92 }
93
94 void ZeroSuggestProvider::Run() {
95 const int kFetcherID = 1;
96 fetcher_.reset(
97 net::URLFetcher::Create(kFetcherID,
98 GURL(url_prefix_ + current_query_),
99 net::URLFetcher::GET, this));
100 fetcher_->SetRequestContext(profile_->GetRequestContext());
101 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
102 fetcher_->Start();
103 }
104
105 void ZeroSuggestProvider::UpdateMatches(const string16& user_text) {
106 user_text_ = user_text;
107 const size_t prev_num_matches = matches_.size();
108 ConvertResultsToAutocompleteMatches();
109 if (matches_.size() != prev_num_matches)
110 listener_->OnProviderUpdate(true);
111 }
112
113 bool ZeroSuggestProvider::ParseSuggestResults(Value* root_val) {
114 std::string query;
115 ListValue* root_list = NULL;
116 ListValue* results = NULL;
117 if (!root_val->GetAsList(&root_list) || !root_list->GetString(0, &query) ||
118 (query != current_query_) || !root_list->GetList(1, &results))
119 return false;
120
121 results_.clear();
122 ListValue* one_result = NULL;
123 for (size_t index = 0; results->GetList(index, &one_result); ++index) {
124 string16 result;
125 one_result->GetString(0, &result);
126 if (result.empty())
127 continue;
128 results_.push_back(result);
129 }
130
131 return true;
132 }
133
134 void ZeroSuggestProvider::ConvertResultsToAutocompleteMatches() {
135 const TemplateURL* search_provider =
136 template_url_service_->GetDefaultSearchProvider();
137 // Fail if we can't set the clickthrough URL for query suggestions.
138 if (search_provider == NULL || !search_provider->SupportsReplacement())
139 return;
140 matches_.clear();
141 // Do not add anything if there are no results for this URL.
142 if (results_.empty())
143 return;
144 AddMatchForCurrentURL();
145 for (size_t i = 0; i < results_.size(); ++i)
146 AddMatchForResult(search_provider, i, results_[i]);
147 }
148
149 // TODO(jered): Rip this out once the first match is decoupled from the current
150 // typing in the omnibox.
151 void ZeroSuggestProvider::AddMatchForCurrentURL() {
152 // If the user has typed something besides the current url, they probably
153 // don't intend to refresh it.
154 const string16 current_query_text = ASCIIToUTF16(current_query_);
155 const bool user_text_is_url = user_text_ == current_query_text;
156 if (user_text_.empty() || user_text_is_url) {
157 // The placeholder suggestion for the current URL has high relevance so
158 // that it is in the first suggestion slot and inline autocompleted. It
159 // gets dropped as soon as the user types something.
160 AutocompleteMatch match(this, kMaxZeroSuggestRelevance, false,
161 AutocompleteMatch::NAVSUGGEST);
162 match.destination_url = GURL(current_query_);
163 match.contents = current_query_text;
164 if (!user_text_is_url) {
165 match.fill_into_edit = current_query_text;
166 match.inline_autocomplete_offset = 0;
167 }
168 AutocompleteMatch::ClassifyLocationInString(0, current_query_.size(),
169 match.contents.length(), ACMatchClassification::URL,
170 &match.contents_class);
171 matches_.push_back(match);
172 }
173 }
174
175 void ZeroSuggestProvider::AddMatchForResult(
176 const TemplateURL* search_provider,
177 size_t result_index,
178 const string16& result) {
179 // TODO(jered): Rip out user_text_is_url logic when AddMatchForCurrentURL
180 // goes away.
181 const string16 current_query_text = ASCIIToUTF16(current_query_);
182 const bool user_text_is_url = user_text_ == current_query_text;
183 const bool kCaseInsensitve = false;
184 if (!user_text_.empty() && !user_text_is_url &&
185 !StartsWith(result, user_text_, kCaseInsensitve))
186 // This suggestion isn't relevant for the current prefix.
187 return;
188 // This bogus relevance puts suggestions below the placeholder from
189 // AddMatchForCurrentURL(), but very low so that after the user starts typing
190 // zero-suggestions go away when there are other suggestions.
191 // TODO(jered): Use real scores from the suggestion server.
192 const int suggestion_relevance = kMaxZeroSuggestRelevance - matches_.size();
193 AutocompleteMatch match(this, suggestion_relevance, false,
194 AutocompleteMatch::SEARCH_SUGGEST);
195 match.contents = result;
196 match.fill_into_edit = result;
197 if (!user_text_is_url && user_text_ != result)
198 match.inline_autocomplete_offset = user_text_.length();
199
200 // Build a URL for this query using the default search provider.
201 const TemplateURLRef& search_url = search_provider->url_ref();
202 DCHECK(search_url.SupportsReplacement());
203 match.search_terms_args.reset(
204 new TemplateURLRef::SearchTermsArgs(result));
205 match.search_terms_args->original_query = string16();
206 match.search_terms_args->accepted_suggestion = result_index;
207 match.destination_url =
208 GURL(search_url.ReplaceSearchTerms(*match.search_terms_args.get()));
209
210 if (user_text_.empty() || user_text_is_url || user_text_ == result) {
211 match.contents_class.push_back(
212 ACMatchClassification(0, ACMatchClassification::NONE));
213 } else {
214 // Style to look like normal search suggestions.
215 match.contents_class.push_back(
216 ACMatchClassification(0, ACMatchClassification::DIM));
217 match.contents_class.push_back(
218 ACMatchClassification(user_text_.length(), ACMatchClassification::NONE));
219 }
220 match.transition = content::PAGE_TRANSITION_GENERATED;
221
222 matches_.push_back(match);
223 }
OLDNEW
« no previous file with comments | « chrome/browser/autocomplete/zero_suggest_provider.h ('k') | chrome/browser/ui/omnibox/omnibox_edit_model.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698