history_quick_provider.cc revision 513209b27ff55e2841eac0e4120199c23acce758
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 3// found in the LICENSE file. 4 5#include "chrome/browser/autocomplete/history_quick_provider.h" 6 7#include "base/basictypes.h" 8#include "base/i18n/word_iterator.h" 9#include "base/string_util.h" 10#include "base/logging.h" 11#include "base/utf_string_conversions.h" 12#include "chrome/browser/autocomplete/autocomplete_match.h" 13#include "chrome/browser/history/history.h" 14#include "chrome/browser/prefs/pref_service.h" 15#include "chrome/browser/profile.h" 16#include "chrome/browser/history/in_memory_url_index.h" 17#include "chrome/browser/net/url_fixer_upper.h" 18#include "chrome/browser/plugin_service.h" 19#include "chrome/common/notification_source.h" 20#include "chrome/common/notification_type.h" 21#include "chrome/common/pref_names.h" 22#include "chrome/common/url_constants.h" 23#include "googleurl/src/url_util.h" 24#include "net/base/escape.h" 25#include "net/base/net_util.h" 26 27using history::InMemoryURLIndex; 28using history::ScoredHistoryMatch; 29using history::ScoredHistoryMatches; 30 31HistoryQuickProvider::HistoryQuickProvider(ACProviderListener* listener, 32 Profile* profile) 33 : HistoryProvider(listener, profile, "HistoryQuickProvider"), 34 trim_http_(false), 35 languages_(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)) {} 36 37HistoryQuickProvider::~HistoryQuickProvider() {} 38 39void HistoryQuickProvider::Start(const AutocompleteInput& input, 40 bool minimal_changes) { 41 matches_.clear(); 42 43 if ((input.type() == AutocompleteInput::INVALID) || 44 (input.type() == AutocompleteInput::FORCED_QUERY)) 45 return; 46 47 autocomplete_input_ = input; 48 trim_http_ = !HasHTTPScheme(input.text()); 49 50 // Do some fixup on the user input before matching against it, so we provide 51 // good results for local file paths, input with spaces, etc. 52 // NOTE: This purposefully doesn't take input.desired_tld() into account; if 53 // it did, then holding "ctrl" would change all the results from the 54 // HistoryQuickProvider provider, not just the What You Typed Result. 55 const std::wstring fixed_text(FixupUserInput(input)); 56 if (fixed_text.empty()) { 57 // Conceivably fixup could result in an empty string (although I don't 58 // have cases where this happens offhand). We can't do anything with 59 // empty input, so just bail; otherwise we'd crash later. 60 return; 61 } 62 autocomplete_input_.set_text(fixed_text); 63 64 // TODO(pkasting): We should just block here until this loads. Any time 65 // someone unloads the history backend, we'll get inconsistent inline 66 // autocomplete behavior here. 67 if (GetIndex()) { 68 DoAutocomplete(); 69 UpdateStarredStateOfMatches(); 70 } 71} 72 73void HistoryQuickProvider::DoAutocomplete() { 74 // Get the matching URLs from the DB. 75 string16 term_string(WideToUTF16(autocomplete_input_.text())); 76 term_string = UnescapeURLComponent(term_string, 77 UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS); 78 history::InMemoryURLIndex::String16Vector terms( 79 HistoryQuickProvider::WordVectorFromString16(term_string)); 80 ScoredHistoryMatches matches = GetIndex()->HistoryItemsForTerms(terms); 81 82 size_t match_num = matches.size() - 1; 83 for (ScoredHistoryMatches::const_iterator match_iter = matches.begin(); 84 match_iter != matches.end(); ++match_iter, --match_num) { 85 const ScoredHistoryMatch& history_match(*match_iter); 86 AutocompleteMatch ac_match = 87 QuickMatchToACMatch(history_match, NORMAL, match_num); 88 matches_.push_back(ac_match); 89 } 90} 91 92AutocompleteMatch HistoryQuickProvider::QuickMatchToACMatch( 93 const ScoredHistoryMatch& history_match, 94 MatchType match_type, 95 size_t match_number) { 96 const history::URLRow& info = history_match.url_info; 97 int score = CalculateRelevance(history_match.raw_score, 98 autocomplete_input_.type(), 99 match_type, match_number); 100 AutocompleteMatch match(this, score, !!info.visit_count(), 101 AutocompleteMatch::HISTORY_URL); 102 match.destination_url = info.url(); 103 DCHECK(match.destination_url.is_valid()); 104 size_t inline_autocomplete_offset = 105 history_match.input_location + autocomplete_input_.text().length(); 106 const net::FormatUrlTypes format_types = net::kFormatUrlOmitAll & 107 ~((trim_http_ && !history_match.match_in_scheme) ? 108 0 : net::kFormatUrlOmitHTTP); 109 std::string languages = 110 match_type == WHAT_YOU_TYPED ? std::string() : languages_; 111 match.fill_into_edit = 112 AutocompleteInput::FormattedStringWithEquivalentMeaning(info.url(), 113 UTF16ToWide(net::FormatUrl(info.url(), languages, format_types, 114 UnescapeRule::SPACES, NULL, NULL, 115 &inline_autocomplete_offset))); 116 if (!autocomplete_input_.prevent_inline_autocomplete()) 117 match.inline_autocomplete_offset = inline_autocomplete_offset; 118 DCHECK((match.inline_autocomplete_offset == std::wstring::npos) || 119 (match.inline_autocomplete_offset <= match.fill_into_edit.length())); 120 121 size_t match_start = history_match.input_location; 122 match.contents = 123 UTF16ToWide(net::FormatUrl(info.url(), languages, format_types, 124 UnescapeRule::SPACES, NULL, NULL, 125 &match_start)); 126 if ((match_start != std::wstring::npos) && 127 (inline_autocomplete_offset != std::wstring::npos) && 128 (inline_autocomplete_offset != match_start)) { 129 DCHECK(inline_autocomplete_offset > match_start); 130 AutocompleteMatch::ClassifyLocationInString(match_start, 131 inline_autocomplete_offset - match_start, match.contents.length(), 132 ACMatchClassification::URL, &match.contents_class); 133 } else { 134 AutocompleteMatch::ClassifyLocationInString(std::wstring::npos, 0, 135 match.contents.length(), ACMatchClassification::URL, 136 &match.contents_class); 137 } 138 match.description = UTF16ToWide(info.title()); 139 AutocompleteMatch::ClassifyMatchInString(autocomplete_input_.text(), 140 UTF16ToWide(info.title()), 141 ACMatchClassification::NONE, 142 &match.description_class); 143 144 return match; 145} 146 147history::InMemoryURLIndex* HistoryQuickProvider::GetIndex() { 148 if (index_for_testing_.get()) 149 return index_for_testing_.get(); 150 151 HistoryService* const history_service = 152 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); 153 if (!history_service) 154 return NULL; 155 156 return history_service->InMemoryIndex(); 157} 158 159void HistoryQuickProvider::SetIndexForTesting( 160 history::InMemoryURLIndex* index) { 161 DCHECK(index); 162 index_for_testing_.reset(index); 163} 164 165// Utility Functions 166 167history::InMemoryURLIndex::String16Vector 168 HistoryQuickProvider::WordVectorFromString16(const string16& uni_string) { 169 history::InMemoryURLIndex::String16Vector words; 170 WordIterator iter(&uni_string, WordIterator::BREAK_WORD); 171 if (iter.Init()) { 172 while (iter.Advance()) { 173 if (iter.IsWord()) 174 words.push_back(iter.GetWord()); 175 } 176 } 177 return words; 178} 179 180// static 181int HistoryQuickProvider::CalculateRelevance(int raw_score, 182 AutocompleteInput::Type input_type, 183 MatchType match_type, 184 size_t match_number) { 185 switch (match_type) { 186 case INLINE_AUTOCOMPLETE: 187 return 1400; 188 189 case WHAT_YOU_TYPED: 190 return 1200; 191 192 default: 193 return 900 + static_cast<int>(match_number); 194 } 195} 196