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

Side by Side Diff: components/omnibox/browser/keyword_provider.cc

Issue 1411543011: Omnibox: Make Keyword Provide More Generous with Matching (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: == -> >= Created 5 years, 1 month 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
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 "components/omnibox/browser/keyword_provider.h" 5 #include "components/omnibox/browser/keyword_provider.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/strings/string16.h" 10 #include "base/strings/string16.h"
11 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h" 12 #include "base/strings/utf_string_conversions.h"
13 #include "components/metrics/proto/omnibox_input_type.pb.h" 13 #include "components/metrics/proto/omnibox_input_type.pb.h"
14 #include "components/omnibox/browser/autocomplete_match.h" 14 #include "components/omnibox/browser/autocomplete_match.h"
15 #include "components/omnibox/browser/autocomplete_provider_client.h" 15 #include "components/omnibox/browser/autocomplete_provider_client.h"
16 #include "components/omnibox/browser/autocomplete_provider_listener.h" 16 #include "components/omnibox/browser/autocomplete_provider_listener.h"
17 #include "components/omnibox/browser/keyword_extensions_delegate.h" 17 #include "components/omnibox/browser/keyword_extensions_delegate.h"
18 #include "components/omnibox/browser/omnibox_field_trial.h"
18 #include "components/search_engines/template_url.h" 19 #include "components/search_engines/template_url.h"
19 #include "components/search_engines/template_url_service.h" 20 #include "components/search_engines/template_url_service.h"
20 #include "grit/components_strings.h" 21 #include "grit/components_strings.h"
21 #include "net/base/escape.h" 22 #include "net/base/escape.h"
22 #include "net/base/net_util.h" 23 #include "net/base/net_util.h"
23 #include "ui/base/l10n/l10n_util.h" 24 #include "ui/base/l10n/l10n_util.h"
24 25
25 namespace { 26 namespace {
26 27
27 // Helper functor for Start(), for sorting keyword matches by quality. 28 // Helper functor for Start(), for sorting keyword matches by quality.
28 class CompareQuality { 29 class CompareQuality {
29 public: 30 public:
30 // A keyword is of higher quality when a greater fraction of it has been 31 // A keyword is of higher quality when a greater fraction of the important
31 // typed, that is, when it is shorter. 32 // part of it has been typed, that is, when the effective keyword length is
33 // shorter.
32 // 34 //
33 // TODO(pkasting): Most recent and most frequent keywords are probably 35 // TODO(pkasting): Most recent and most frequent keywords are probably
34 // better rankings than the fraction of the keyword typed. We should 36 // better rankings than the fraction of the keyword typed. We should
35 // always put any exact matches first no matter what, since the code in 37 // always put any exact matches first no matter what, since the code in
36 // Start() assumes this (and it makes sense). 38 // Start() assumes this (and it makes sense).
37 bool operator()(const TemplateURL* t_url1, const TemplateURL* t_url2) const { 39 bool operator()(
38 return t_url1->keyword().length() < t_url2->keyword().length(); 40 const TemplateURLService::TemplateURLAndEffectiveKeywordLength
41 t_url_match1,
42 const TemplateURLService::TemplateURLAndEffectiveKeywordLength
43 t_url_match2) const {
44 return t_url_match1.second < t_url_match2.second;
39 } 45 }
40 }; 46 };
41 47
42 // Helper for KeywordProvider::Start(), for ending keyword mode unless 48 // Helper for KeywordProvider::Start(), for ending keyword mode unless
43 // explicitly told otherwise. 49 // explicitly told otherwise.
44 class ScopedEndExtensionKeywordMode { 50 class ScopedEndExtensionKeywordMode {
45 public: 51 public:
46 explicit ScopedEndExtensionKeywordMode(KeywordExtensionsDelegate* delegate); 52 explicit ScopedEndExtensionKeywordMode(KeywordExtensionsDelegate* delegate);
47 ~ScopedEndExtensionKeywordMode(); 53 ~ScopedEndExtensionKeywordMode();
48 54
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 195
190 return keyword; 196 return keyword;
191 } 197 }
192 198
193 AutocompleteMatch KeywordProvider::CreateVerbatimMatch( 199 AutocompleteMatch KeywordProvider::CreateVerbatimMatch(
194 const base::string16& text, 200 const base::string16& text,
195 const base::string16& keyword, 201 const base::string16& keyword,
196 const AutocompleteInput& input) { 202 const AutocompleteInput& input) {
197 // A verbatim match is allowed to be the default match. 203 // A verbatim match is allowed to be the default match.
198 return CreateAutocompleteMatch( 204 return CreateAutocompleteMatch(
199 GetTemplateURLService()->GetTemplateURLForKeyword(keyword), input, 205 GetTemplateURLService()->GetTemplateURLForKeyword(keyword),
200 keyword.length(), SplitReplacementStringFromInput(text, true), true, 0); 206 keyword.length(), input, keyword.length(),
207 SplitReplacementStringFromInput(text, true), true, 0);
201 } 208 }
202 209
203 void KeywordProvider::Start(const AutocompleteInput& input, 210 void KeywordProvider::Start(const AutocompleteInput& input,
204 bool minimal_changes) { 211 bool minimal_changes) {
205 // This object ensures we end keyword mode if we exit the function without 212 // This object ensures we end keyword mode if we exit the function without
206 // toggling keyword mode to on. 213 // toggling keyword mode to on.
207 ScopedEndExtensionKeywordMode keyword_mode_toggle(extensions_delegate_.get()); 214 ScopedEndExtensionKeywordMode keyword_mode_toggle(extensions_delegate_.get());
208 215
209 matches_.clear(); 216 matches_.clear();
210 217
(...skipping 25 matching lines...) Expand all
236 base::string16 keyword, remaining_input; 243 base::string16 keyword, remaining_input;
237 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) 244 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input))
238 return; 245 return;
239 246
240 // Get the best matches for this keyword. 247 // Get the best matches for this keyword.
241 // 248 //
242 // NOTE: We could cache the previous keywords and reuse them here in the 249 // NOTE: We could cache the previous keywords and reuse them here in the
243 // |minimal_changes| case, but since we'd still have to recalculate their 250 // |minimal_changes| case, but since we'd still have to recalculate their
244 // relevances and we can just recreate the results synchronously anyway, we 251 // relevances and we can just recreate the results synchronously anyway, we
245 // don't bother. 252 // don't bother.
246 TemplateURLService::TemplateURLVector matches; 253 TemplateURLService::TemplateURLMatchesVector matches;
247 GetTemplateURLService()->FindMatchingKeywords( 254 GetTemplateURLService()->FindMatchingKeywords(
248 keyword, !remaining_input.empty(), &matches); 255 keyword, !remaining_input.empty(), &matches);
256 if (!OmniboxFieldTrial::KeywordRequiresPrefixBeforeDomain()) {
257 GetTemplateURLService()->FindMatchingDomainKeywords(
258 keyword, !remaining_input.empty(), &matches);
Peter Kasting 2015/11/09 23:11:14 As written this seems to imply that the functions
Mark P 2015/11/11 07:40:59 Revised to AddMatching...() and changed all commen
259 }
249 260
250 for (TemplateURLService::TemplateURLVector::iterator i(matches.begin()); 261 for (TemplateURLService::TemplateURLMatchesVector::iterator
251 i != matches.end(); ) { 262 i(matches.begin()); i != matches.end(); ) {
252 const TemplateURL* template_url = *i; 263 const TemplateURL* template_url = i->first;
253 264
254 // Prune any extension keywords that are disallowed in incognito mode (if 265 // Prune any extension keywords that are disallowed in incognito mode (if
255 // we're incognito), or disabled. 266 // we're incognito), or disabled.
256 if (template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION && 267 if (template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION &&
257 extensions_delegate_ && 268 extensions_delegate_ &&
258 !extensions_delegate_->IsEnabledExtension( 269 !extensions_delegate_->IsEnabledExtension(
259 template_url->GetExtensionId())) { 270 template_url->GetExtensionId())) {
260 i = matches.erase(i); 271 i = matches.erase(i);
261 continue; 272 continue;
262 } 273 }
(...skipping 10 matching lines...) Expand all
273 ++i; 284 ++i;
274 } 285 }
275 if (matches.empty()) 286 if (matches.empty())
276 return; 287 return;
277 std::sort(matches.begin(), matches.end(), CompareQuality()); 288 std::sort(matches.begin(), matches.end(), CompareQuality());
278 289
279 // Limit to one exact or three inexact matches, and mark them up for display 290 // Limit to one exact or three inexact matches, and mark them up for display
280 // in the autocomplete popup. 291 // in the autocomplete popup.
281 // Any exact match is going to be the highest quality match, and thus at the 292 // Any exact match is going to be the highest quality match, and thus at the
282 // front of our vector. 293 // front of our vector.
283 if (matches.front()->keyword() == keyword) { 294 if (matches.front().first->keyword() == keyword) {
284 const TemplateURL* template_url = matches.front(); 295 const TemplateURL* template_url = matches.front().first;
296 const size_t effective_keyword_length = matches.front().second;
285 const bool is_extension_keyword = 297 const bool is_extension_keyword =
286 template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION; 298 template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION;
287 299
288 // Only create an exact match if |remaining_input| is empty or if 300 // Only create an exact match if |remaining_input| is empty or if
289 // this is an extension keyword. If |remaining_input| is a 301 // this is an extension keyword. If |remaining_input| is a
290 // non-empty non-extension keyword (i.e., a regular keyword that 302 // non-empty non-extension keyword (i.e., a regular keyword that
291 // supports replacement and that has extra text following it), 303 // supports replacement and that has extra text following it),
292 // then SearchProvider creates the exact (a.k.a. verbatim) match. 304 // then SearchProvider creates the exact (a.k.a. verbatim) match.
293 if (!remaining_input.empty() && !is_extension_keyword) 305 if (!remaining_input.empty() && !is_extension_keyword)
294 return; 306 return;
295 307
296 // TODO(pkasting): We should probably check that if the user explicitly 308 // TODO(pkasting): We should probably check that if the user explicitly
297 // typed a scheme, that scheme matches the one in |template_url|. 309 // typed a scheme, that scheme matches the one in |template_url|.
298 310
299 // When creating an exact match (either for the keyword itself, no 311 // When creating an exact match (either for the keyword itself, no
300 // remaining query or an extension keyword, possibly with remaining 312 // remaining query or an extension keyword, possibly with remaining
301 // input), allow the match to be the default match. 313 // input), allow the match to be the default match.
302 matches_.push_back(CreateAutocompleteMatch( 314 matches_.push_back(CreateAutocompleteMatch(
303 template_url, input, keyword.length(), remaining_input, true, -1)); 315 template_url, effective_keyword_length, input, keyword.length(),
316 remaining_input, true, -1));
304 317
305 if (is_extension_keyword && extensions_delegate_) { 318 if (is_extension_keyword && extensions_delegate_) {
306 if (extensions_delegate_->Start(input, minimal_changes, template_url, 319 if (extensions_delegate_->Start(input, minimal_changes, template_url,
307 remaining_input)) 320 remaining_input))
308 keyword_mode_toggle.StayInKeywordMode(); 321 keyword_mode_toggle.StayInKeywordMode();
309 } 322 }
310 } else { 323 } else {
311 if (matches.size() > kMaxMatches) 324 for (TemplateURLService::TemplateURLMatchesVector::const_iterator i(
312 matches.erase(matches.begin() + kMaxMatches, matches.end()); 325 matches.begin());
313 for (TemplateURLService::TemplateURLVector::const_iterator i( 326 (i != matches.end()) && (matches.size() <= kMaxMatches); ++i) {
314 matches.begin()); i != matches.end(); ++i) { 327 // Skip keywords that we've already added. It's possible we may have
315 matches_.push_back(CreateAutocompleteMatch( 328 // retrieved the same keyword twice. For example, the keyword'
Peter Kasting 2015/11/09 23:11:14 Nit: Mismatched '
Mark P 2015/11/11 07:40:59 Done.
316 *i, input, keyword.length(), remaining_input, false, -1)); 329 // abc.abc.com, may be retrieved for the input abc from the full keyword
Peter Kasting 2015/11/09 23:11:14 Nit: No comma (but consider adding quotes around k
Mark P 2015/11/11 07:40:59 Did both.
330 // matching and the domain matching passes.
331 bool found_duplicate = false;
332 for (ACMatches::const_iterator j = matches_.begin();
333 (j != matches_.end()) && !found_duplicate; ++j) {
334 if (j->keyword == i->first->keyword())
335 found_duplicate = true;
336 }
Peter Kasting 2015/11/09 23:11:14 Nit: Use std::find_if() with a lambda instead of t
Mark P 2015/11/11 07:41:00 Done.
337 if (!found_duplicate) {
338 matches_.push_back(CreateAutocompleteMatch(
339 i->first, i->second, input, keyword.length(), remaining_input,
340 false, -1));
341 }
317 } 342 }
318 } 343 }
319 } 344 }
320 345
321 void KeywordProvider::Stop(bool clear_cached_results, 346 void KeywordProvider::Stop(bool clear_cached_results,
322 bool due_to_user_inactivity) { 347 bool due_to_user_inactivity) {
323 done_ = true; 348 done_ = true;
324 // Only end an extension's request if the user did something to explicitly 349 // Only end an extension's request if the user did something to explicitly
325 // cancel it; mere inactivity shouldn't terminate long-running extension 350 // cancel it; mere inactivity shouldn't terminate long-running extension
326 // operations since the user likely explicitly requested them. 351 // operations since the user likely explicitly requested them.
(...skipping 12 matching lines...) Expand all
339 return false; 364 return false;
340 365
341 *keyword = TemplateURLService::CleanUserInputKeyword( 366 *keyword = TemplateURLService::CleanUserInputKeyword(
342 SplitKeywordFromInput(input.text(), true, remaining_input)); 367 SplitKeywordFromInput(input.text(), true, remaining_input));
343 return !keyword->empty(); 368 return !keyword->empty();
344 } 369 }
345 370
346 // static 371 // static
347 int KeywordProvider::CalculateRelevance(metrics::OmniboxInputType::Type type, 372 int KeywordProvider::CalculateRelevance(metrics::OmniboxInputType::Type type,
348 bool complete, 373 bool complete,
374 bool nearly_complete,
349 bool supports_replacement, 375 bool supports_replacement,
350 bool prefer_keyword, 376 bool prefer_keyword,
351 bool allow_exact_keyword_match) { 377 bool allow_exact_keyword_match) {
352 // This function is responsible for scoring suggestions of keywords 378 // This function is responsible for scoring suggestions of keywords
353 // themselves and the suggestion of the verbatim query on an 379 // themselves and the suggestion of the verbatim query on an
354 // extension keyword. SearchProvider::CalculateRelevanceForKeywordVerbatim() 380 // extension keyword. SearchProvider::CalculateRelevanceForKeywordVerbatim()
355 // scores verbatim query suggestions for non-extension keywords. 381 // scores verbatim query suggestions for non-extension keywords.
356 // These two functions are currently in sync, but there's no reason 382 // These two functions are currently in sync except for the code at the
357 // we couldn't decide in the future to score verbatim matches 383 // beginning for this function that handles scoring not-fully-typed keywords.
384 // (SearchProvider does not make such suggestions and so has no need to
385 // score them.) Other than that, they're currently in sync. There's no
386 // reason we couldn't decide in the future to score verbatim matches
358 // differently for extension and non-extension keywords. If you 387 // differently for extension and non-extension keywords. If you
359 // make such a change, however, you should update this comment to 388 // make such a change, however, you should update this comment to
360 // describe it, so it's clear why the functions diverge. 389 // describe it, so it's clear why the functions diverge.
Peter Kasting 2015/11/09 23:11:14 Instead of this long comment about what's in sync
Mark P 2015/11/11 07:41:00 Done. Now search_provider.{cc,h} is part of this
361 if (!complete) 390 if (!complete) {
391 const int nearly_complete_score =
392 OmniboxFieldTrial::KeywordScoreForNearlyCompleteMatch();
393 // If we have a special score to apply for nearly-complete matches, do so.
394 if (nearly_complete && (nearly_complete_score > -1))
395 return nearly_complete_score;
362 return (type == metrics::OmniboxInputType::URL) ? 700 : 450; 396 return (type == metrics::OmniboxInputType::URL) ? 700 : 450;
397 }
363 if (!supports_replacement || (allow_exact_keyword_match && prefer_keyword)) 398 if (!supports_replacement || (allow_exact_keyword_match && prefer_keyword))
364 return 1500; 399 return 1500;
365 return (allow_exact_keyword_match && 400 return (allow_exact_keyword_match &&
366 (type == metrics::OmniboxInputType::QUERY)) ? 401 (type == metrics::OmniboxInputType::QUERY)) ?
367 1450 : 1100; 402 1450 : 1100;
368 } 403 }
369 404
370 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( 405 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch(
371 const TemplateURL* template_url, 406 const TemplateURL* template_url,
407 const size_t effective_keyword_length,
372 const AutocompleteInput& input, 408 const AutocompleteInput& input,
373 size_t prefix_length, 409 size_t prefix_length,
374 const base::string16& remaining_input, 410 const base::string16& remaining_input,
375 bool allowed_to_be_default_match, 411 bool allowed_to_be_default_match,
376 int relevance) { 412 int relevance) {
377 DCHECK(template_url); 413 DCHECK(template_url);
378 const bool supports_replacement = 414 const bool supports_replacement =
379 template_url->url_ref().SupportsReplacement( 415 template_url->url_ref().SupportsReplacement(
380 GetTemplateURLService()->search_terms_data()); 416 GetTemplateURLService()->search_terms_data());
381 417
382 // Create an edit entry of "[keyword] [remaining input]". This is helpful 418 // Create an edit entry of "[keyword] [remaining input]". This is helpful
383 // even when [remaining input] is empty, as the user can select the popup 419 // even when [remaining input] is empty, as the user can select the popup
384 // choice and immediately begin typing in query input. 420 // choice and immediately begin typing in query input.
385 const base::string16& keyword = template_url->keyword(); 421 const base::string16& keyword = template_url->keyword();
386 const bool keyword_complete = (prefix_length == keyword.length()); 422 const bool keyword_complete = (prefix_length == keyword.length());
423 const bool nearly_complete = (prefix_length >= effective_keyword_length);
387 if (relevance < 0) { 424 if (relevance < 0) {
388 relevance = 425 relevance =
389 CalculateRelevance(input.type(), keyword_complete, 426 CalculateRelevance(input.type(), keyword_complete, nearly_complete,
390 // When the user wants keyword matches to take 427 // When the user wants keyword matches to take
391 // preference, score them highly regardless of 428 // preference, score them highly regardless of
392 // whether the input provides query text. 429 // whether the input provides query text.
393 supports_replacement, input.prefer_keyword(), 430 supports_replacement, input.prefer_keyword(),
394 input.allow_exact_keyword_match()); 431 input.allow_exact_keyword_match());
395 } 432 }
396 AutocompleteMatch match(this, relevance, false, 433 AutocompleteMatch match(this, relevance, false,
397 supports_replacement ? AutocompleteMatchType::SEARCH_OTHER_ENGINE : 434 supports_replacement ? AutocompleteMatchType::SEARCH_OTHER_ENGINE :
398 AutocompleteMatchType::HISTORY_KEYWORD); 435 AutocompleteMatchType::HISTORY_KEYWORD);
399 match.allowed_to_be_default_match = allowed_to_be_default_match; 436 match.allowed_to_be_default_match = allowed_to_be_default_match;
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
471 ACMatchClassification::NONE, &match->contents_class); 508 ACMatchClassification::NONE, &match->contents_class);
472 } 509 }
473 } 510 }
474 511
475 TemplateURLService* KeywordProvider::GetTemplateURLService() const { 512 TemplateURLService* KeywordProvider::GetTemplateURLService() const {
476 // Make sure the model is loaded. This is cheap and quickly bails out if 513 // Make sure the model is loaded. This is cheap and quickly bails out if
477 // the model is already loaded. 514 // the model is already loaded.
478 model_->Load(); 515 model_->Load();
479 return model_; 516 return model_;
480 } 517 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698