Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/autocomplete.h" | 5 #include "chrome/browser/autocomplete/autocomplete.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "app/l10n_util.h" | 9 #include "app/l10n_util.h" |
| 10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
| (...skipping 530 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 541 // static | 541 // static |
| 542 const size_t AutocompleteResult::kMaxMatches = 6; | 542 const size_t AutocompleteResult::kMaxMatches = 6; |
| 543 | 543 |
| 544 void AutocompleteResult::Selection::Clear() { | 544 void AutocompleteResult::Selection::Clear() { |
| 545 destination_url = GURL(); | 545 destination_url = GURL(); |
| 546 provider_affinity = NULL; | 546 provider_affinity = NULL; |
| 547 is_history_what_you_typed_match = false; | 547 is_history_what_you_typed_match = false; |
| 548 } | 548 } |
| 549 | 549 |
| 550 AutocompleteResult::AutocompleteResult() { | 550 AutocompleteResult::AutocompleteResult() { |
| 551 // Reserve space for the max number of matches we'll show. The +1 accounts | 551 // Reserve space for the max number of matches we'll show. |
| 552 // for the history shortcut match as it isn't included in max_matches. | 552 matches_.reserve(kMaxMatches); |
| 553 matches_.reserve(kMaxMatches + 1); | |
| 554 | 553 |
| 555 // It's probably safe to do this in the initializer list, but there's little | 554 // It's probably safe to do this in the initializer list, but there's little |
| 556 // penalty to doing it here and it ensures our object is fully constructed | 555 // penalty to doing it here and it ensures our object is fully constructed |
| 557 // before calling member functions. | 556 // before calling member functions. |
| 558 default_match_ = end(); | 557 default_match_ = end(); |
| 559 } | 558 } |
| 560 | 559 |
| 561 AutocompleteResult::~AutocompleteResult() {} | 560 AutocompleteResult::~AutocompleteResult() {} |
| 562 | 561 |
| 563 void AutocompleteResult::CopyFrom(const AutocompleteResult& rhs) { | 562 void AutocompleteResult::CopyFrom(const AutocompleteResult& rhs) { |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 592 } | 591 } |
| 593 | 592 |
| 594 void AutocompleteResult::SortAndCull(const AutocompleteInput& input) { | 593 void AutocompleteResult::SortAndCull(const AutocompleteInput& input) { |
| 595 // Remove duplicates. | 594 // Remove duplicates. |
| 596 std::sort(matches_.begin(), matches_.end(), | 595 std::sort(matches_.begin(), matches_.end(), |
| 597 &AutocompleteMatch::DestinationSortFunc); | 596 &AutocompleteMatch::DestinationSortFunc); |
| 598 matches_.erase(std::unique(matches_.begin(), matches_.end(), | 597 matches_.erase(std::unique(matches_.begin(), matches_.end(), |
| 599 &AutocompleteMatch::DestinationsEqual), | 598 &AutocompleteMatch::DestinationsEqual), |
| 600 matches_.end()); | 599 matches_.end()); |
| 601 | 600 |
| 602 // Find the top |kMaxMatches| matches. | 601 // Sort the results. |
|
Peter Kasting
2011/01/20 00:08:33
Nit: I'd just combine the two comments into a sing
| |
| 603 if (matches_.size() > kMaxMatches) { | 602 const size_t num_matches = std::min(kMaxMatches, matches_.size()); |
| 604 std::partial_sort(matches_.begin(), matches_.begin() + kMaxMatches, | 603 std::partial_sort(matches_.begin(), matches_.begin() + num_matches, |
| 605 matches_.end(), &AutocompleteMatch::MoreRelevant); | 604 matches_.end(), &AutocompleteMatch::MoreRelevant); |
| 606 matches_.erase(matches_.begin() + kMaxMatches, matches_.end()); | 605 // Remove matches so that we have at most kMaxMatches. |
| 607 } | 606 matches_.resize(num_matches); |
| 608 | 607 |
| 609 // HistoryContentsProvider uses a negative relevance as a way to avoid | |
| 610 // starving out other provider matches, yet we may end up using this match. To | |
| 611 // make sure such matches are sorted correctly we search for all | |
| 612 // relevances < 0 and negate them. If we change our relevance algorithm to | |
| 613 // properly mix different providers' matches, this can go away. | |
| 614 for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) { | |
| 615 if (i->relevance < 0) | |
| 616 i->relevance = -i->relevance; | |
| 617 } | |
| 618 | |
| 619 // Put the final result set in order. | |
| 620 std::sort(matches_.begin(), matches_.end(), &AutocompleteMatch::MoreRelevant); | |
| 621 default_match_ = begin(); | 608 default_match_ = begin(); |
| 622 | 609 |
| 623 // Set the alternate nav URL. | 610 // Set the alternate nav URL. |
| 624 alternate_nav_url_ = GURL(); | 611 alternate_nav_url_ = GURL(); |
| 625 if (((input.type() == AutocompleteInput::UNKNOWN) || | 612 if (((input.type() == AutocompleteInput::UNKNOWN) || |
| 626 (input.type() == AutocompleteInput::REQUESTED_URL)) && | 613 (input.type() == AutocompleteInput::REQUESTED_URL)) && |
| 627 (default_match_ != end()) && | 614 (default_match_ != end()) && |
| 628 (default_match_->transition != PageTransition::TYPED) && | 615 (default_match_->transition != PageTransition::TYPED) && |
| 629 (default_match_->transition != PageTransition::KEYWORD) && | 616 (default_match_->transition != PageTransition::KEYWORD) && |
| 630 (input.canonicalized_url() != default_match_->destination_url)) | 617 (input.canonicalized_url() != default_match_->destination_url)) |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 690 done_(true) { | 677 done_(true) { |
| 691 search_provider_ = new SearchProvider(this, profile); | 678 search_provider_ = new SearchProvider(this, profile); |
| 692 providers_.push_back(search_provider_); | 679 providers_.push_back(search_provider_); |
| 693 if (!CommandLine::ForCurrentProcess()->HasSwitch( | 680 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| 694 switches::kDisableHistoryQuickProvider)) | 681 switches::kDisableHistoryQuickProvider)) |
| 695 providers_.push_back(new HistoryQuickProvider(this, profile)); | 682 providers_.push_back(new HistoryQuickProvider(this, profile)); |
| 696 if (!CommandLine::ForCurrentProcess()->HasSwitch( | 683 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| 697 switches::kDisableHistoryURLProvider)) | 684 switches::kDisableHistoryURLProvider)) |
| 698 providers_.push_back(new HistoryURLProvider(this, profile)); | 685 providers_.push_back(new HistoryURLProvider(this, profile)); |
| 699 providers_.push_back(new KeywordProvider(this, profile)); | 686 providers_.push_back(new KeywordProvider(this, profile)); |
| 700 history_contents_provider_ = new HistoryContentsProvider(this, profile); | 687 providers_.push_back(new HistoryContentsProvider(this, profile)); |
| 701 providers_.push_back(history_contents_provider_); | |
| 702 for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i) | 688 for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i) |
| 703 (*i)->AddRef(); | 689 (*i)->AddRef(); |
| 704 } | 690 } |
| 705 | 691 |
| 706 AutocompleteController::~AutocompleteController() { | 692 AutocompleteController::~AutocompleteController() { |
| 707 // The providers may have tasks outstanding that hold refs to them. We need | 693 // The providers may have tasks outstanding that hold refs to them. We need |
| 708 // to ensure they won't call us back if they outlive us. (Practically, | 694 // to ensure they won't call us back if they outlive us. (Practically, |
| 709 // calling Stop() should also cancel those tasks and make it so that we hold | 695 // calling Stop() should also cancel those tasks and make it so that we hold |
| 710 // the only refs.) We also don't want to bother notifying anyone of our | 696 // the only refs.) We also don't want to bother notifying anyone of our |
| 711 // result changes here, because the notification observer is in the midst of | 697 // result changes here, because the notification observer is in the midst of |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 829 // Add all providers' matches. | 815 // Add all providers' matches. |
| 830 latest_result_.Reset(); | 816 latest_result_.Reset(); |
| 831 for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end(); | 817 for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end(); |
| 832 ++i) | 818 ++i) |
| 833 latest_result_.AppendMatches((*i)->matches()); | 819 latest_result_.AppendMatches((*i)->matches()); |
| 834 updated_latest_result_ = true; | 820 updated_latest_result_ = true; |
| 835 | 821 |
| 836 // Sort the matches and trim to a small number of "best" matches. | 822 // Sort the matches and trim to a small number of "best" matches. |
| 837 latest_result_.SortAndCull(input_); | 823 latest_result_.SortAndCull(input_); |
| 838 | 824 |
| 839 if (history_contents_provider_) | |
| 840 AddHistoryContentsShortcut(); | |
| 841 | |
| 842 #ifndef NDEBUG | 825 #ifndef NDEBUG |
| 843 latest_result_.Validate(); | 826 latest_result_.Validate(); |
| 844 #endif | 827 #endif |
| 845 | 828 |
| 846 if (is_synchronous_pass) { | 829 if (is_synchronous_pass) { |
| 847 if (!update_delay_timer_.IsRunning()) { | 830 if (!update_delay_timer_.IsRunning()) { |
| 848 update_delay_timer_.Start( | 831 update_delay_timer_.Start( |
| 849 TimeDelta::FromMilliseconds(kUpdateDelayMs), | 832 TimeDelta::FromMilliseconds(kUpdateDelayMs), |
| 850 this, &AutocompleteController::DelayTimerFired); | 833 this, &AutocompleteController::DelayTimerFired); |
| 851 } | 834 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 895 // TODO(pkasting): Eliminate this ordering requirement. | 878 // TODO(pkasting): Eliminate this ordering requirement. |
| 896 NotificationService::current()->Notify( | 879 NotificationService::current()->Notify( |
| 897 NotificationType::AUTOCOMPLETE_CONTROLLER_DEFAULT_MATCH_UPDATED, | 880 NotificationType::AUTOCOMPLETE_CONTROLLER_DEFAULT_MATCH_UPDATED, |
| 898 Source<AutocompleteController>(this), | 881 Source<AutocompleteController>(this), |
| 899 Details<const AutocompleteResult>(&result_)); | 882 Details<const AutocompleteResult>(&result_)); |
| 900 } | 883 } |
| 901 if (!done_) | 884 if (!done_) |
| 902 update_delay_timer_.Reset(); | 885 update_delay_timer_.Reset(); |
| 903 } | 886 } |
| 904 | 887 |
| 905 ACMatches AutocompleteController::GetMatchesNotInLatestResult( | |
| 906 const AutocompleteProvider* provider) const { | |
| 907 DCHECK(provider); | |
| 908 | |
| 909 // Determine the set of destination URLs. | |
| 910 std::set<GURL> destination_urls; | |
| 911 for (AutocompleteResult::const_iterator i(latest_result_.begin()); | |
| 912 i != latest_result_.end(); ++i) | |
| 913 destination_urls.insert(i->destination_url); | |
| 914 | |
| 915 ACMatches matches; | |
| 916 const ACMatches& provider_matches = provider->matches(); | |
| 917 for (ACMatches::const_iterator i = provider_matches.begin(); | |
| 918 i != provider_matches.end(); ++i) { | |
| 919 if (destination_urls.find(i->destination_url) == destination_urls.end()) | |
| 920 matches.push_back(*i); | |
| 921 } | |
| 922 | |
| 923 return matches; | |
| 924 } | |
| 925 | |
| 926 void AutocompleteController::AddHistoryContentsShortcut() { | |
| 927 DCHECK(history_contents_provider_); | |
| 928 // Only check the history contents provider if the history contents provider | |
| 929 // is done and has matches. | |
| 930 if (!history_contents_provider_->done() || | |
| 931 !history_contents_provider_->db_match_count()) { | |
| 932 return; | |
| 933 } | |
| 934 | |
| 935 if ((history_contents_provider_->db_match_count() <= | |
| 936 (latest_result_.size() + 1)) || | |
| 937 (history_contents_provider_->db_match_count() == 1)) { | |
| 938 // We only want to add a shortcut if we're not already showing the matches. | |
| 939 ACMatches matches(GetMatchesNotInLatestResult(history_contents_provider_)); | |
| 940 if (matches.empty()) | |
| 941 return; | |
| 942 if (matches.size() == 1) { | |
| 943 // Only one match not shown, add it. The relevance may be negative, | |
| 944 // which means we need to negate it to get the true relevance. | |
| 945 AutocompleteMatch& match = matches.front(); | |
| 946 if (match.relevance < 0) | |
| 947 match.relevance = -match.relevance; | |
| 948 latest_result_.AddMatch(match); | |
| 949 return; | |
| 950 } // else, fall through and add item. | |
| 951 } | |
| 952 | |
| 953 AutocompleteMatch match(NULL, 0, false, AutocompleteMatch::OPEN_HISTORY_PAGE); | |
| 954 match.fill_into_edit = input_.text(); | |
| 955 | |
| 956 // Mark up the text such that the user input text is bold. | |
| 957 size_t keyword_offset = std::wstring::npos; // Offset into match.contents. | |
| 958 if (history_contents_provider_->db_match_count() == | |
| 959 history_contents_provider_->kMaxMatchCount) { | |
| 960 // History contents searcher has maxed out. | |
| 961 match.contents = UTF16ToWideHack( | |
| 962 l10n_util::GetStringFUTF16(IDS_OMNIBOX_RECENT_HISTORY_MANY, | |
| 963 WideToUTF16Hack(input_.text()), | |
| 964 &keyword_offset)); | |
| 965 } else { | |
| 966 // We can report exact matches when there aren't too many. | |
| 967 std::vector<size_t> content_param_offsets; | |
| 968 match.contents = UTF16ToWideHack(l10n_util::GetStringFUTF16( | |
| 969 IDS_OMNIBOX_RECENT_HISTORY, | |
| 970 base::FormatNumber(history_contents_provider_-> | |
| 971 db_match_count()), | |
| 972 WideToUTF16Hack(input_.text()), | |
| 973 &content_param_offsets)); | |
| 974 | |
| 975 // content_param_offsets is ordered based on supplied params, we expect | |
| 976 // that the second one contains the query (first is the number). | |
| 977 if (content_param_offsets.size() == 2) { | |
| 978 keyword_offset = content_param_offsets[1]; | |
| 979 } else { | |
| 980 // See comments on an identical NOTREACHED() in search_provider.cc. | |
| 981 NOTREACHED(); | |
| 982 } | |
| 983 } | |
| 984 | |
| 985 // NOTE: This comparison succeeds when keyword_offset == std::wstring::npos. | |
| 986 if (keyword_offset > 0) { | |
| 987 match.contents_class.push_back( | |
| 988 ACMatchClassification(0, ACMatchClassification::NONE)); | |
| 989 } | |
| 990 match.contents_class.push_back( | |
| 991 ACMatchClassification(keyword_offset, ACMatchClassification::MATCH)); | |
| 992 if (keyword_offset + input_.text().size() < match.contents.size()) { | |
| 993 match.contents_class.push_back( | |
| 994 ACMatchClassification(keyword_offset + input_.text().size(), | |
| 995 ACMatchClassification::NONE)); | |
| 996 } | |
| 997 match.destination_url = | |
| 998 HistoryUI::GetHistoryURLWithSearchText(WideToUTF16(input_.text())); | |
| 999 match.transition = PageTransition::AUTO_BOOKMARK; | |
| 1000 match.provider = history_contents_provider_; | |
| 1001 latest_result_.AddMatch(match); | |
| 1002 } | |
| 1003 | |
| 1004 void AutocompleteController::CheckIfDone() { | 888 void AutocompleteController::CheckIfDone() { |
| 1005 for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end(); | 889 for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end(); |
| 1006 ++i) { | 890 ++i) { |
| 1007 if (!(*i)->done()) { | 891 if (!(*i)->done()) { |
| 1008 done_ = false; | 892 done_ = false; |
| 1009 return; | 893 return; |
| 1010 } | 894 } |
| 1011 } | 895 } |
| 1012 done_ = true; | 896 done_ = true; |
| 1013 } | 897 } |
| OLD | NEW |