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

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

Issue 13141002: Use Instant suggested match type for Instant temporary text. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: static Created 7 years, 7 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 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 "chrome/browser/autocomplete/search_provider.h" 5 #include "chrome/browser/autocomplete/search_provider.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 9
10 #include "base/auto_reset.h" 10 #include "base/auto_reset.h"
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 117
118 // SearchProvider ------------------------------------------------------------- 118 // SearchProvider -------------------------------------------------------------
119 119
120 // static 120 // static
121 const int SearchProvider::kDefaultProviderURLFetcherID = 1; 121 const int SearchProvider::kDefaultProviderURLFetcherID = 1;
122 // static 122 // static
123 const int SearchProvider::kKeywordProviderURLFetcherID = 2; 123 const int SearchProvider::kKeywordProviderURLFetcherID = 2;
124 // static 124 // static
125 int SearchProvider::kMinimumTimeBetweenSuggestQueriesMs = 100; 125 int SearchProvider::kMinimumTimeBetweenSuggestQueriesMs = 100;
126 126
127 // static
128 SearchProvider::Providers SearchProvider::GetProviders(
129 Profile* profile,
130 const AutocompleteInput& input,
131 AutocompleteInput* keyword_input) {
132 TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(profile);
133 Providers providers(model);
134
135 const TemplateURL* keyword_provider = NULL;
136 if (keyword_input) {
137 keyword_provider = KeywordProvider::GetSubstitutingTemplateURLForInput(
138 model, keyword_input);
139 if (keyword_provider == NULL)
140 keyword_input->Clear();
141 else if (keyword_input->text().empty())
142 keyword_provider = NULL; // No text after the keyword.
143 }
144
145 const TemplateURL* default_provider = model->GetDefaultSearchProvider();
146 if (default_provider && !default_provider->SupportsReplacement())
147 default_provider = NULL;
148
149 if (keyword_provider == default_provider)
150 default_provider = NULL; // No use in querying the same provider twice.
151
152 providers.set(
153 default_provider ? default_provider->keyword() : string16(),
154 keyword_provider ? keyword_provider->keyword() : string16());
155
156 return providers;
157 }
158
159 // static
160 AutocompleteMatch SearchProvider::CreateSearchSuggestion(
161 Profile* profile,
162 AutocompleteProvider* autocomplete_provider,
163 const Providers& search_providers,
164 const AutocompleteInput& input,
165 const string16& query_string,
166 const string16& input_text,
167 int relevance,
168 AutocompleteMatch::Type type,
169 int accepted_suggestion,
170 bool is_keyword) {
171 AutocompleteMatch match(autocomplete_provider, relevance, false, type);
172
173 // Bail out now if we don't actually have a valid provider.
174 match.keyword = is_keyword ?
Peter Kasting 2013/04/27 01:21:21 The only reason this function needs |search_provid
sreeram 2013/04/27 01:25:54 But I do want to be able to support keyword mode.
Peter Kasting 2013/04/27 01:37:48 I would say, let's worry about that when we get th
175 search_providers.keyword_provider() : search_providers.default_provider();
176 const TemplateURL* provider_url = match.GetTemplateURL(profile, false);
177 if (provider_url == NULL)
178 return match;
179
180 match.contents.assign(query_string);
181 // We do intra-string highlighting for suggestions - the suggested segment
182 // will be highlighted, e.g. for input_text = "you" the suggestion may be
183 // "youtube", so we'll bold the "tube" section: you*tube*.
184 if (input_text != query_string) {
185 size_t input_position = match.contents.find(input_text);
186 if (input_position == string16::npos) {
187 // The input text is not a substring of the query string, e.g. input
188 // text is "slasdot" and the query string is "slashdot", so we bold the
189 // whole thing.
190 match.contents_class.push_back(
191 ACMatchClassification(0, ACMatchClassification::MATCH));
192 } else {
193 // TODO(beng): ACMatchClassification::MATCH now seems to just mean
194 // "bold" this. Consider modifying the terminology.
195 // We don't iterate over the string here annotating all matches because
196 // it looks odd to have every occurrence of a substring that may be as
197 // short as a single character highlighted in a query suggestion result,
198 // e.g. for input text "s" and query string "southwest airlines", it
199 // looks odd if both the first and last s are highlighted.
200 if (input_position != 0) {
201 match.contents_class.push_back(
202 ACMatchClassification(0, ACMatchClassification::NONE));
203 }
204 match.contents_class.push_back(
205 ACMatchClassification(input_position, ACMatchClassification::DIM));
206 size_t next_fragment_position = input_position + input_text.length();
207 if (next_fragment_position < query_string.length()) {
208 match.contents_class.push_back(
209 ACMatchClassification(next_fragment_position,
210 ACMatchClassification::NONE));
211 }
212 }
213 } else {
214 // Otherwise, we're dealing with the "default search" result which has no
215 // completion.
216 match.contents_class.push_back(
217 ACMatchClassification(0, ACMatchClassification::NONE));
218 }
219
220 // When the user forced a query, we need to make sure all the fill_into_edit
221 // values preserve that property. Otherwise, if the user starts editing a
222 // suggestion, non-Search results will suddenly appear.
223 if (input.type() == AutocompleteInput::FORCED_QUERY)
224 match.fill_into_edit.assign(ASCIIToUTF16("?"));
225 if (is_keyword)
226 match.fill_into_edit.append(match.keyword + char16(' '));
227 if (!input.prevent_inline_autocomplete() &&
228 StartsWith(query_string, input_text, false)) {
229 match.inline_autocomplete_offset =
230 match.fill_into_edit.length() + input_text.length();
231 }
232 match.fill_into_edit.append(query_string);
233
234 const TemplateURLRef& search_url = provider_url->url_ref();
235 DCHECK(search_url.SupportsReplacement());
236 match.search_terms_args.reset(
237 new TemplateURLRef::SearchTermsArgs(query_string));
238 match.search_terms_args->original_query = input_text;
239 match.search_terms_args->accepted_suggestion = accepted_suggestion;
240 // This is the destination URL sans assisted query stats. This must be set
241 // so the AutocompleteController can properly de-dupe; the controller will
242 // eventually overwrite it before it reaches the user.
243 match.destination_url =
244 GURL(search_url.ReplaceSearchTerms(*match.search_terms_args.get()));
245
246 // Search results don't look like URLs.
247 match.transition = is_keyword ?
248 content::PAGE_TRANSITION_KEYWORD : content::PAGE_TRANSITION_GENERATED;
249
250 return match;
251 }
252
127 SearchProvider::SearchProvider(AutocompleteProviderListener* listener, 253 SearchProvider::SearchProvider(AutocompleteProviderListener* listener,
128 Profile* profile) 254 Profile* profile)
129 : AutocompleteProvider(listener, profile, 255 : AutocompleteProvider(listener, profile,
130 AutocompleteProvider::TYPE_SEARCH), 256 AutocompleteProvider::TYPE_SEARCH),
131 providers_(TemplateURLServiceFactory::GetForProfile(profile)), 257 providers_(TemplateURLServiceFactory::GetForProfile(profile)),
132 suggest_results_pending_(0), 258 suggest_results_pending_(0),
133 has_default_suggested_relevance_(false), 259 has_default_suggested_relevance_(false),
134 has_keyword_suggested_relevance_(false), 260 has_keyword_suggested_relevance_(false),
135 default_verbatim_relevance_(-1), 261 default_verbatim_relevance_(-1),
136 keyword_verbatim_relevance_(-1), 262 keyword_verbatim_relevance_(-1),
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 instant_finalized_ = 360 instant_finalized_ =
235 (input.matches_requested() != AutocompleteInput::ALL_MATCHES); 361 (input.matches_requested() != AutocompleteInput::ALL_MATCHES);
236 362
237 // Can't return search/suggest results for bogus input or without a profile. 363 // Can't return search/suggest results for bogus input or without a profile.
238 if (!profile_ || (input.type() == AutocompleteInput::INVALID)) { 364 if (!profile_ || (input.type() == AutocompleteInput::INVALID)) {
239 Stop(false); 365 Stop(false);
240 return; 366 return;
241 } 367 }
242 368
243 keyword_input_ = input; 369 keyword_input_ = input;
244 const TemplateURL* keyword_provider = 370 Providers providers = GetProviders(profile_, input, &keyword_input_);
245 KeywordProvider::GetSubstitutingTemplateURLForInput(model,
246 &keyword_input_);
247 if (keyword_provider == NULL)
248 keyword_input_.Clear();
249 else if (keyword_input_.text().empty())
250 keyword_provider = NULL;
251 371
252 const TemplateURL* default_provider = model->GetDefaultSearchProvider(); 372 if (providers.default_provider().empty() &&
253 if (default_provider && !default_provider->SupportsReplacement()) 373 providers.keyword_provider().empty()) {
254 default_provider = NULL;
255
256 if (keyword_provider == default_provider)
257 default_provider = NULL; // No use in querying the same provider twice.
258
259 if (!default_provider && !keyword_provider) {
260 // No valid providers. 374 // No valid providers.
261 Stop(false); 375 Stop(false);
262 return; 376 return;
263 } 377 }
264 378
265 // If we're still running an old query but have since changed the query text 379 // If we're still running an old query but have since changed the query text
266 // or the providers, abort the query. 380 // or the providers, abort the query.
267 string16 default_provider_keyword(default_provider ?
268 default_provider->keyword() : string16());
269 string16 keyword_provider_keyword(keyword_provider ?
270 keyword_provider->keyword() : string16());
271 if (!minimal_changes || 381 if (!minimal_changes ||
272 !providers_.equal(default_provider_keyword, keyword_provider_keyword)) { 382 !providers_.equal(providers.default_provider(),
383 providers.keyword_provider())) {
273 // If Instant has not come back with a suggestion, adjust the previous 384 // If Instant has not come back with a suggestion, adjust the previous
274 // suggestion if possible. If |instant_finalized| is true, we are looking 385 // suggestion if possible. If |instant_finalized| is true, we are looking
275 // for synchronous matches only, so the suggestion is cleared. 386 // for synchronous matches only, so the suggestion is cleared.
276 if (instant_finalized_) 387 if (instant_finalized_)
277 default_provider_suggestion_ = InstantSuggestion(); 388 default_provider_suggestion_ = InstantSuggestion();
278 else 389 else
279 AdjustDefaultProviderSuggestion(input_.text(), input.text()); 390 AdjustDefaultProviderSuggestion(input_.text(), input.text());
280 391
281 // Cancel any in-flight suggest requests. 392 // Cancel any in-flight suggest requests.
282 if (!done_) { 393 if (!done_) {
283 // The Stop(false) call below clears |default_provider_suggestion_|, but 394 // The Stop(false) call below clears |default_provider_suggestion_|, but
284 // in this instance we do not want to clear cached results, so we 395 // in this instance we do not want to clear cached results, so we
285 // restore it. 396 // restore it.
286 base::AutoReset<InstantSuggestion> reset(&default_provider_suggestion_, 397 base::AutoReset<InstantSuggestion> reset(&default_provider_suggestion_,
287 InstantSuggestion()); 398 InstantSuggestion());
288 Stop(false); 399 Stop(false);
289 } 400 }
290 } 401 }
291 402
292 providers_.set(default_provider_keyword, keyword_provider_keyword); 403 providers_ = providers;
293 404
294 if (input.text().empty()) { 405 if (input.text().empty()) {
295 // User typed "?" alone. Give them a placeholder result indicating what 406 // User typed "?" alone. Give them a placeholder result indicating what
296 // this syntax does. 407 // this syntax does.
297 if (default_provider) { 408 if (!providers_.default_provider().empty()) {
298 AutocompleteMatch match; 409 AutocompleteMatch match;
299 match.provider = this; 410 match.provider = this;
300 match.contents.assign(l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE)); 411 match.contents.assign(l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE));
301 match.contents_class.push_back( 412 match.contents_class.push_back(
302 ACMatchClassification(0, ACMatchClassification::NONE)); 413 ACMatchClassification(0, ACMatchClassification::NONE));
303 match.keyword = providers_.default_provider(); 414 match.keyword = providers_.default_provider();
304 matches_.push_back(match); 415 matches_.push_back(match);
305 } 416 }
306 Stop(false); 417 Stop(false);
307 return; 418 return;
(...skipping 1011 matching lines...) Expand 10 before | Expand all | Expand 10 after
1319 return std::max(0, base_score - score_discount); 1430 return std::max(0, base_score - score_discount);
1320 } 1431 }
1321 1432
1322 void SearchProvider::AddMatchToMap(const string16& query_string, 1433 void SearchProvider::AddMatchToMap(const string16& query_string,
1323 const string16& input_text, 1434 const string16& input_text,
1324 int relevance, 1435 int relevance,
1325 AutocompleteMatch::Type type, 1436 AutocompleteMatch::Type type,
1326 int accepted_suggestion, 1437 int accepted_suggestion,
1327 bool is_keyword, 1438 bool is_keyword,
1328 MatchMap* map) { 1439 MatchMap* map) {
1329 AutocompleteMatch match(this, relevance, false, type); 1440 AutocompleteMatch match = CreateSearchSuggestion(profile_, this, providers_,
1330 std::vector<size_t> content_param_offsets; 1441 input_, query_string, input_text, relevance, type, accepted_suggestion,
1331 // Bail out now if we don't actually have a valid provider. 1442 is_keyword);
1332 match.keyword = is_keyword ? 1443 if (!match.destination_url.is_valid())
1333 providers_.keyword_provider() : providers_.default_provider();
1334 const TemplateURL* provider_url = match.GetTemplateURL(profile_, false);
1335 if (provider_url == NULL)
1336 return; 1444 return;
1337 1445
1338 match.contents.assign(query_string);
1339 // We do intra-string highlighting for suggestions - the suggested segment
1340 // will be highlighted, e.g. for input_text = "you" the suggestion may be
1341 // "youtube", so we'll bold the "tube" section: you*tube*.
1342 if (input_text != query_string) {
1343 size_t input_position = match.contents.find(input_text);
1344 if (input_position == string16::npos) {
1345 // The input text is not a substring of the query string, e.g. input
1346 // text is "slasdot" and the query string is "slashdot", so we bold the
1347 // whole thing.
1348 match.contents_class.push_back(
1349 ACMatchClassification(0, ACMatchClassification::MATCH));
1350 } else {
1351 // TODO(beng): ACMatchClassification::MATCH now seems to just mean
1352 // "bold" this. Consider modifying the terminology.
1353 // We don't iterate over the string here annotating all matches because
1354 // it looks odd to have every occurrence of a substring that may be as
1355 // short as a single character highlighted in a query suggestion result,
1356 // e.g. for input text "s" and query string "southwest airlines", it
1357 // looks odd if both the first and last s are highlighted.
1358 if (input_position != 0) {
1359 match.contents_class.push_back(
1360 ACMatchClassification(0, ACMatchClassification::NONE));
1361 }
1362 match.contents_class.push_back(
1363 ACMatchClassification(input_position, ACMatchClassification::DIM));
1364 size_t next_fragment_position = input_position + input_text.length();
1365 if (next_fragment_position < query_string.length()) {
1366 match.contents_class.push_back(
1367 ACMatchClassification(next_fragment_position,
1368 ACMatchClassification::NONE));
1369 }
1370 }
1371 } else {
1372 // Otherwise, we're dealing with the "default search" result which has no
1373 // completion.
1374 match.contents_class.push_back(
1375 ACMatchClassification(0, ACMatchClassification::NONE));
1376 }
1377
1378 // When the user forced a query, we need to make sure all the fill_into_edit
1379 // values preserve that property. Otherwise, if the user starts editing a
1380 // suggestion, non-Search results will suddenly appear.
1381 if (input_.type() == AutocompleteInput::FORCED_QUERY)
1382 match.fill_into_edit.assign(ASCIIToUTF16("?"));
1383 if (is_keyword)
1384 match.fill_into_edit.append(match.keyword + char16(' '));
1385 if (!input_.prevent_inline_autocomplete() &&
1386 StartsWith(query_string, input_text, false)) {
1387 match.inline_autocomplete_offset =
1388 match.fill_into_edit.length() + input_text.length();
1389 }
1390 match.fill_into_edit.append(query_string);
1391
1392 const TemplateURLRef& search_url = provider_url->url_ref();
1393 DCHECK(search_url.SupportsReplacement());
1394 match.search_terms_args.reset(
1395 new TemplateURLRef::SearchTermsArgs(query_string));
1396 match.search_terms_args->original_query = input_text;
1397 match.search_terms_args->accepted_suggestion = accepted_suggestion;
1398 // This is the destination URL sans assisted query stats. This must be set
1399 // so the AutocompleteController can properly de-dupe; the controller will
1400 // eventually overwrite it before it reaches the user.
1401 match.destination_url =
1402 GURL(search_url.ReplaceSearchTerms(*match.search_terms_args.get()));
1403
1404 // Search results don't look like URLs.
1405 match.transition = is_keyword ?
1406 content::PAGE_TRANSITION_KEYWORD : content::PAGE_TRANSITION_GENERATED;
1407
1408 // Try to add |match| to |map|. If a match for |query_string| is already in 1446 // Try to add |match| to |map|. If a match for |query_string| is already in
1409 // |map|, replace it if |match| is more relevant. 1447 // |map|, replace it if |match| is more relevant.
1410 // NOTE: Keep this ToLower() call in sync with url_database.cc. 1448 // NOTE: Keep this ToLower() call in sync with url_database.cc.
1411 const std::pair<MatchMap::iterator, bool> i = map->insert( 1449 const std::pair<MatchMap::iterator, bool> i = map->insert(
1412 std::pair<string16, AutocompleteMatch>( 1450 std::pair<string16, AutocompleteMatch>(
1413 base::i18n::ToLower(query_string), match)); 1451 base::i18n::ToLower(query_string), match));
1414 // NOTE: We purposefully do a direct relevance comparison here instead of 1452 // NOTE: We purposefully do a direct relevance comparison here instead of
1415 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added 1453 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added
1416 // first" rather than "items alphabetically first" when the scores are equal. 1454 // first" rather than "items alphabetically first" when the scores are equal.
1417 // The only case this matters is when a user has results with the same score 1455 // The only case this matters is when a user has results with the same score
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
1515 it->set_relevance(max_query_relevance); 1553 it->set_relevance(max_query_relevance);
1516 } 1554 }
1517 } 1555 }
1518 1556
1519 void SearchProvider::UpdateDone() { 1557 void SearchProvider::UpdateDone() {
1520 // We're done when the timer isn't running, there are no suggest queries 1558 // We're done when the timer isn't running, there are no suggest queries
1521 // pending, and we're not waiting on Instant. 1559 // pending, and we're not waiting on Instant.
1522 done_ = (!timer_.IsRunning() && (suggest_results_pending_ == 0) && 1560 done_ = (!timer_.IsRunning() && (suggest_results_pending_ == 0) &&
1523 (instant_finalized_ || !chrome::IsInstantEnabled(profile_))); 1561 (instant_finalized_ || !chrome::IsInstantEnabled(profile_)));
1524 } 1562 }
OLDNEW
« no previous file with comments | « chrome/browser/autocomplete/search_provider.h ('k') | chrome/browser/ui/omnibox/omnibox_edit_model.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698