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