Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/callback.h" | 10 #include "base/callback.h" |
| 11 #include "base/i18n/break_iterator.h" | |
| 11 #include "base/i18n/case_conversion.h" | 12 #include "base/i18n/case_conversion.h" |
| 12 #include "base/i18n/icu_string_conversions.h" | 13 #include "base/i18n/icu_string_conversions.h" |
| 13 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
| 14 #include "base/string16.h" | 15 #include "base/string16.h" |
| 15 #include "base/utf_string_conversions.h" | 16 #include "base/utf_string_conversions.h" |
| 16 #include "chrome/browser/autocomplete/autocomplete_classifier.h" | 17 #include "chrome/browser/autocomplete/autocomplete_classifier.h" |
| 17 #include "chrome/browser/autocomplete/keyword_provider.h" | 18 #include "chrome/browser/autocomplete/keyword_provider.h" |
| 18 #include "chrome/browser/autocomplete/autocomplete_match.h" | 19 #include "chrome/browser/autocomplete/autocomplete_match.h" |
| 19 #include "chrome/browser/history/history.h" | 20 #include "chrome/browser/history/history.h" |
| 20 #include "chrome/browser/instant/instant_controller.h" | 21 #include "chrome/browser/instant/instant_controller.h" |
| 21 #include "chrome/browser/net/url_fixer_upper.h" | 22 #include "chrome/browser/net/url_fixer_upper.h" |
| 22 #include "chrome/browser/prefs/pref_service.h" | 23 #include "chrome/browser/prefs/pref_service.h" |
| 23 #include "chrome/browser/profiles/profile.h" | 24 #include "chrome/browser/profiles/profile.h" |
| 24 #include "chrome/browser/history/in_memory_database.h" | 25 #include "chrome/browser/history/in_memory_database.h" |
| 25 #include "chrome/browser/search_engines/template_url_service.h" | 26 #include "chrome/browser/search_engines/template_url_service.h" |
| 26 #include "chrome/browser/search_engines/template_url_service_factory.h" | 27 #include "chrome/browser/search_engines/template_url_service_factory.h" |
| 27 #include "chrome/common/pref_names.h" | 28 #include "chrome/common/pref_names.h" |
| 28 #include "chrome/common/url_constants.h" | 29 #include "chrome/common/url_constants.h" |
| 29 #include "content/common/json_value_serializer.h" | 30 #include "content/common/json_value_serializer.h" |
| 30 #include "googleurl/src/url_util.h" | 31 #include "googleurl/src/url_util.h" |
| 31 #include "grit/generated_resources.h" | 32 #include "grit/generated_resources.h" |
| 32 #include "net/base/escape.h" | 33 #include "net/base/escape.h" |
| 33 #include "net/http/http_response_headers.h" | 34 #include "net/http/http_response_headers.h" |
| 34 #include "net/url_request/url_request_status.h" | 35 #include "net/url_request/url_request_status.h" |
| 35 #include "ui/base/l10n/l10n_util.h" | 36 #include "ui/base/l10n/l10n_util.h" |
| 36 | 37 |
| 37 using base::Time; | 38 using base::Time; |
| 38 using base::TimeDelta; | 39 using base::TimeDelta; |
| 39 | 40 |
| 41 namespace { | |
| 42 | |
| 43 bool HasMultipleWords(const string16& text) { | |
| 44 base::i18n::BreakIterator i(text, base::i18n::BreakIterator::BREAK_WORD); | |
| 45 bool found_word = false; | |
|
sky
2011/07/07 02:49:15
nit: move this inside the if.
| |
| 46 if (i.Init()) { | |
| 47 while (i.Advance()) { | |
| 48 if (i.IsWord()) { | |
| 49 if (found_word) | |
| 50 return true; | |
| 51 found_word = true; | |
| 52 } | |
| 53 } | |
| 54 } | |
| 55 return false; | |
| 56 } | |
| 57 | |
| 58 }; | |
| 59 | |
| 40 // static | 60 // static |
| 41 const int SearchProvider::kDefaultProviderURLFetcherID = 1; | 61 const int SearchProvider::kDefaultProviderURLFetcherID = 1; |
| 42 // static | 62 // static |
| 43 const int SearchProvider::kKeywordProviderURLFetcherID = 2; | 63 const int SearchProvider::kKeywordProviderURLFetcherID = 2; |
| 44 | 64 |
| 45 // static | 65 // static |
| 46 bool SearchProvider::query_suggest_immediately_ = false; | 66 bool SearchProvider::query_suggest_immediately_ = false; |
| 47 | 67 |
| 48 void SearchProvider::Providers::Set(const TemplateURL* default_provider, | 68 void SearchProvider::Providers::Set(const TemplateURL* default_provider, |
| 49 const TemplateURL* keyword_provider) { | 69 const TemplateURL* keyword_provider) { |
| (...skipping 546 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 596 matches_.push_back(NavigationToMatch(navigation_results.front(), | 616 matches_.push_back(NavigationToMatch(navigation_results.front(), |
| 597 CalculateRelevanceForNavigation(num_results, 0, is_keyword), | 617 CalculateRelevanceForNavigation(num_results, 0, is_keyword), |
| 598 is_keyword)); | 618 is_keyword)); |
| 599 } | 619 } |
| 600 } | 620 } |
| 601 | 621 |
| 602 void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, | 622 void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, |
| 603 bool is_keyword, | 623 bool is_keyword, |
| 604 int did_not_accept_suggestion, | 624 int did_not_accept_suggestion, |
| 605 MatchMap* map) { | 625 MatchMap* map) { |
| 626 bool base_prevent_inline_autocomplete = | |
| 627 (input_.type() == AutocompleteInput::URL) || | |
| 628 input_.prevent_inline_autocomplete(); | |
| 629 const string16& input_text( | |
| 630 is_keyword ? keyword_input_text_ : input_.text()); | |
| 631 bool input_not_multiple_words = !HasMultipleWords(input_text); | |
| 632 bool is_primary_provider = providers_.is_primary_provider(is_keyword); | |
| 633 AutocompleteClassifier* classifier = profile_->GetAutocompleteClassifier(); | |
| 606 int last_relevance = 0; | 634 int last_relevance = 0; |
| 607 AutocompleteClassifier* classifier = profile_->GetAutocompleteClassifier(); | |
| 608 for (HistoryResults::const_iterator i(results.begin()); i != results.end(); | 635 for (HistoryResults::const_iterator i(results.begin()); i != results.end(); |
| 609 ++i) { | 636 ++i) { |
| 637 // Don't autocomplete multi-word queries that have only been seen once | |
| 638 // unless the user has typed more than one word. | |
| 639 bool prevent_inline_autocomplete = base_prevent_inline_autocomplete || | |
| 640 (input_not_multiple_words && (i->visits < 2) && | |
| 641 HasMultipleWords(i->term)); | |
| 642 | |
| 643 // Don't autocomplete search terms that would normally be treated as URLs | |
| 644 // when typed. For example, if the user searched for "google.com" and types | |
| 645 // "goog", don't autocomplete to the search term "google.com". Otherwise, | |
| 646 // the input will look like a URL but act like a search, which is confusing. | |
| 647 // NOTE: We don't check this in the following cases: | |
| 648 // * When inline autocomplete is disabled, we won't be inline | |
| 649 // autocompleting this term, so we don't need to worry about confusion as | |
| 650 // much. This also prevents calling Classify() again from inside the | |
| 651 // classifier (which will corrupt state and likely crash), since the | |
| 652 // classifier always disables inline autocomplete. | |
| 653 // * When the user has typed the whole term, the "what you typed" history | |
| 654 // match will outrank us for URL-like inputs anyway, so we need not do | |
| 655 // anything special. | |
| 656 if (!prevent_inline_autocomplete && classifier && (i->term != input_text)) { | |
| 657 AutocompleteMatch match; | |
| 658 classifier->Classify(i->term, string16(), false, false, &match, NULL); | |
| 659 prevent_inline_autocomplete = match.transition == PageTransition::TYPED; | |
| 660 } | |
| 661 | |
| 662 int relevance = CalculateRelevanceForHistory(i->time, is_primary_provider, | |
| 663 prevent_inline_autocomplete); | |
| 610 // History returns results sorted for us. We force the relevance to decrease | 664 // History returns results sorted for us. We force the relevance to decrease |
| 611 // so that the sort from history is honored. We should never end up with a | 665 // so that the sort from history is honored. We should never end up with a |
| 612 // match having a relevance greater than the previous, but they might be | 666 // match having a relevance greater than the previous, but they might be |
| 613 // equal. If we didn't force the relevance to decrease and we ended up in a | 667 // equal. If we didn't force the relevance to decrease and we ended up in a |
| 614 // situation where the relevance was equal, then which was shown first would | 668 // situation where the relevance was equal, then which was shown first would |
| 615 // be random. | 669 // be random. |
| 616 // This uses >= to handle the case where 3 or more results have the same | 670 // This uses >= to handle the case where 3 or more results have the same |
| 617 // relevance. | 671 // relevance. |
| 618 bool term_looks_like_url = false; | |
| 619 // Don't autocomplete search terms that would normally be treated as URLs | |
| 620 // when typed. For example, if the user searched for google.com and types | |
| 621 // goog, don't autocomplete to the search term google.com. Otherwise, the | |
| 622 // input will look like a URL but act like a search, which is confusing. | |
| 623 // NOTE: We don't check this in the following cases: | |
| 624 // * When inline autocomplete is disabled, we won't be inline | |
| 625 // autocompleting this term, so we don't need to worry about confusion as | |
| 626 // much. This also prevents calling Classify() again from inside the | |
| 627 // classifier (which will corrupt state and likely crash), since the | |
| 628 // classifier always disabled inline autocomplete. | |
| 629 // * When the user has typed the whole term, the "what you typed" history | |
| 630 // match will outrank us for URL-like inputs anyway, so we need not do | |
| 631 // anything special. | |
| 632 if (!input_.prevent_inline_autocomplete() && classifier && | |
| 633 i->term != input_.text()) { | |
| 634 AutocompleteMatch match; | |
| 635 classifier->Classify(i->term, string16(), false, false, &match, NULL); | |
| 636 term_looks_like_url = match.transition == PageTransition::TYPED; | |
| 637 } | |
| 638 int relevance = CalculateRelevanceForHistory(i->time, term_looks_like_url, | |
| 639 is_keyword); | |
| 640 if (i != results.begin() && relevance >= last_relevance) | 672 if (i != results.begin() && relevance >= last_relevance) |
| 641 relevance = last_relevance - 1; | 673 relevance = last_relevance - 1; |
| 642 last_relevance = relevance; | 674 last_relevance = relevance; |
| 643 AddMatchToMap(i->term, | 675 |
| 644 is_keyword ? keyword_input_text_ : input_.text(), | 676 AddMatchToMap(i->term, input_text, relevance, |
| 645 relevance, | |
| 646 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion, | 677 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion, |
| 647 is_keyword, input_.prevent_inline_autocomplete(), | 678 is_keyword, input_.prevent_inline_autocomplete(), map); |
| 648 map); | |
| 649 } | 679 } |
| 650 } | 680 } |
| 651 | 681 |
| 652 void SearchProvider::AddSuggestResultsToMap( | 682 void SearchProvider::AddSuggestResultsToMap( |
| 653 const SuggestResults& suggest_results, | 683 const SuggestResults& suggest_results, |
| 654 bool is_keyword, | 684 bool is_keyword, |
| 655 int did_not_accept_suggestion, | 685 int did_not_accept_suggestion, |
| 656 MatchMap* map) { | 686 MatchMap* map) { |
| 657 for (size_t i = 0; i < suggest_results.size(); ++i) { | 687 for (size_t i = 0; i < suggest_results.size(); ++i) { |
| 658 AddMatchToMap(suggest_results[i], | 688 AddMatchToMap(suggest_results[i], |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 680 | 710 |
| 681 case AutocompleteInput::URL: | 711 case AutocompleteInput::URL: |
| 682 return 850; | 712 return 850; |
| 683 | 713 |
| 684 default: | 714 default: |
| 685 NOTREACHED(); | 715 NOTREACHED(); |
| 686 return 0; | 716 return 0; |
| 687 } | 717 } |
| 688 } | 718 } |
| 689 | 719 |
| 690 int SearchProvider::CalculateRelevanceForHistory(const Time& time, | 720 int SearchProvider::CalculateRelevanceForHistory( |
| 691 bool looks_like_url, | 721 const Time& time, |
| 692 bool is_keyword) const { | 722 bool is_primary_provider, |
| 723 bool prevent_inline_autocomplete) const { | |
| 693 // The relevance of past searches falls off over time. There are two distinct | 724 // The relevance of past searches falls off over time. There are two distinct |
| 694 // equations used. If the first equation is used (searches to the primary | 725 // equations used. If the first equation is used (searches to the primary |
| 695 // provider with a type other than URL that don't autocomplete to a url) the | 726 // provider that we want to inline autocomplete), the score starts at 1399 and |
| 696 // score starts at 1399 and falls to 1300. If the second equation is used the | 727 // falls to 1300. If the second equation is used the relevance of a search 15 |
| 697 // relevance of a search 15 minutes ago is discounted about 50 points, while | 728 // minutes ago is discounted 50 points, while the relevance of a search two |
| 698 // the relevance of a search two weeks ago is discounted about 450 points. | 729 // weeks ago is discounted 450 points. |
| 699 double elapsed_time = std::max((Time::Now() - time).InSecondsF(), 0.); | 730 double elapsed_time = std::max((Time::Now() - time).InSecondsF(), 0.); |
| 700 | 731 if (is_primary_provider && !prevent_inline_autocomplete) { |
| 701 if (providers_.is_primary_provider(is_keyword) && | |
| 702 input_.type() != AutocompleteInput::URL && | |
| 703 !input_.prevent_inline_autocomplete() && !looks_like_url) { | |
| 704 // Searches with the past two days get a different curve. | 732 // Searches with the past two days get a different curve. |
| 705 const double autocomplete_time= 2 * 24 * 60 * 60; | 733 const double autocomplete_time = 2 * 24 * 60 * 60; |
| 706 if (elapsed_time < autocomplete_time) { | 734 if (elapsed_time < autocomplete_time) { |
| 707 return 1399 - static_cast<int>(99 * | 735 return 1399 - static_cast<int>(99 * |
| 708 std::pow(elapsed_time / autocomplete_time, 2.5)); | 736 std::pow(elapsed_time / autocomplete_time, 2.5)); |
| 709 } | 737 } |
| 710 elapsed_time -= autocomplete_time; | 738 elapsed_time -= autocomplete_time; |
| 711 } | 739 } |
| 712 | 740 |
| 713 const int score_discount = | 741 const int score_discount = |
| 714 static_cast<int>(6.5 * std::pow(elapsed_time, 0.3)); | 742 static_cast<int>(6.5 * std::pow(elapsed_time, 0.3)); |
| 715 | 743 |
| 716 // Don't let scores go below 0. Negative relevance scores are meaningful in | 744 // Don't let scores go below 0. Negative relevance scores are meaningful in |
| 717 // a different way. | 745 // a different way. |
| 718 int base_score; | 746 int base_score; |
| 719 if (!providers_.is_primary_provider(is_keyword)) | 747 if (is_primary_provider) |
| 748 base_score = (input_.type() == AutocompleteInput::URL) ? 750 : 1050; | |
| 749 else | |
| 720 base_score = 200; | 750 base_score = 200; |
| 721 else | |
| 722 base_score = (input_.type() == AutocompleteInput::URL) ? 750 : 1050; | |
| 723 return std::max(0, base_score - score_discount); | 751 return std::max(0, base_score - score_discount); |
| 724 } | 752 } |
| 725 | 753 |
| 726 int SearchProvider::CalculateRelevanceForSuggestion(size_t num_results, | 754 int SearchProvider::CalculateRelevanceForSuggestion(size_t num_results, |
| 727 size_t result_number, | 755 size_t result_number, |
| 728 bool is_keyword) const { | 756 bool is_keyword) const { |
| 729 DCHECK(result_number < num_results); | 757 DCHECK(result_number < num_results); |
| 730 int base_score; | 758 int base_score; |
| 731 if (!providers_.is_primary_provider(is_keyword)) | 759 if (!providers_.is_primary_provider(is_keyword)) |
| 732 base_score = 100; | 760 base_score = 100; |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 906 match.description_class.push_back( | 934 match.description_class.push_back( |
| 907 ACMatchClassification(0, ACMatchClassification::DIM)); | 935 ACMatchClassification(0, ACMatchClassification::DIM)); |
| 908 // Only the first search match gets a description. | 936 // Only the first search match gets a description. |
| 909 return; | 937 return; |
| 910 | 938 |
| 911 default: | 939 default: |
| 912 break; | 940 break; |
| 913 } | 941 } |
| 914 } | 942 } |
| 915 } | 943 } |
| OLD | NEW |