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

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

Powered by Google App Engine
This is Rietveld 408576698