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

Side by Side Diff: chrome/browser/autocomplete/history_quick_provider.cc

Issue 1182113009: Componentize HistoryQuickProvider and its supporting cast (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 6 months 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
(Empty)
1 // Copyright (c) 2012 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 <vector>
8
9 #include "base/basictypes.h"
10 #include "base/debug/crash_logging.h"
11 #include "base/i18n/break_iterator.h"
12 #include "base/logging.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/autocomplete/in_memory_url_index.h"
19 #include "components/bookmarks/browser/bookmark_model.h"
20 #include "components/history/core/browser/history_database.h"
21 #include "components/history/core/browser/history_service.h"
22 #include "components/metrics/proto/omnibox_input_type.pb.h"
23 #include "components/omnibox/autocomplete_match_type.h"
24 #include "components/omnibox/autocomplete_provider_client.h"
25 #include "components/omnibox/autocomplete_result.h"
26 #include "components/omnibox/history_url_provider.h"
27 #include "components/omnibox/crash_keys.h"
28 #include "components/omnibox/in_memory_url_index_types.h"
29 #include "components/omnibox/omnibox_field_trial.h"
30 #include "components/search_engines/template_url.h"
31 #include "components/search_engines/template_url_service.h"
32 #include "net/base/escape.h"
33 #include "net/base/net_util.h"
34 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
35 #include "url/third_party/mozilla/url_parse.h"
36 #include "url/url_util.h"
37
38 bool HistoryQuickProvider::disabled_ = false;
39
40 HistoryQuickProvider::HistoryQuickProvider(
41 AutocompleteProviderClient* client,
42 InMemoryURLIndex* in_memory_url_index)
43 : HistoryProvider(AutocompleteProvider::TYPE_HISTORY_QUICK, client),
44 languages_(client->AcceptLanguages()),
45 in_memory_url_index_(in_memory_url_index) {
46 }
47
48 void HistoryQuickProvider::Start(const AutocompleteInput& input,
49 bool minimal_changes,
50 bool called_due_to_focus) {
51 matches_.clear();
52 if (disabled_ || called_due_to_focus)
53 return;
54
55 // Don't bother with INVALID and FORCED_QUERY.
56 if ((input.type() == metrics::OmniboxInputType::INVALID) ||
57 (input.type() == metrics::OmniboxInputType::FORCED_QUERY))
58 return;
59
60 autocomplete_input_ = input;
61
62 // TODO(pkasting): We should just block here until this loads. Any time
63 // someone unloads the history backend, we'll get inconsistent inline
64 // autocomplete behavior here.
65 if (in_memory_url_index_) {
66 DoAutocomplete();
67 }
68 }
69
70 HistoryQuickProvider::~HistoryQuickProvider() {
71 }
72
73 void HistoryQuickProvider::DoAutocomplete() {
74 // Get the matching URLs from the DB.
75 ScoredHistoryMatches matches = in_memory_url_index_->HistoryItemsForTerms(
76 autocomplete_input_.text(), autocomplete_input_.cursor_position(),
77 AutocompleteProvider::kMaxMatches);
78 if (matches.empty())
79 return;
80
81 // Figure out if HistoryURL provider has a URL-what-you-typed match
82 // that ought to go first and what its score will be.
83 bool will_have_url_what_you_typed_match_first = false;
84 int url_what_you_typed_match_score = -1; // undefined
85 // These are necessary (but not sufficient) conditions for the omnibox
86 // input to be a URL-what-you-typed match. The username test checks that
87 // either the username does not exist (a regular URL such as http://site/)
88 // or, if the username exists (http://user@site/), there must be either
89 // a password or a port. Together these exclude pure username@site
90 // inputs because these are likely to be an e-mail address. HistoryURL
91 // provider won't promote the URL-what-you-typed match to first
92 // for these inputs.
93 const bool can_have_url_what_you_typed_match_first =
94 (autocomplete_input_.type() != metrics::OmniboxInputType::QUERY) &&
95 (!autocomplete_input_.parts().username.is_nonempty() ||
96 autocomplete_input_.parts().password.is_nonempty() ||
97 autocomplete_input_.parts().path.is_nonempty());
98 if (can_have_url_what_you_typed_match_first) {
99 history::HistoryService* const history_service = client()->HistoryService();
100 // We expect HistoryService to be available. In case it's not,
101 // (e.g., due to Profile corruption) we let HistoryQuick provider
102 // completions (which may be available because it's a different
103 // data structure) compete with the URL-what-you-typed match as
104 // normal.
105 if (history_service) {
106 history::URLDatabase* url_db = history_service->InMemoryDatabase();
107 // url_db can be NULL if it hasn't finished initializing (or
108 // failed to to initialize). In this case, we let HistoryQuick
109 // provider completions compete with the URL-what-you-typed
110 // match as normal.
111 if (url_db) {
112 const std::string host(base::UTF16ToUTF8(
113 autocomplete_input_.text().substr(
114 autocomplete_input_.parts().host.begin,
115 autocomplete_input_.parts().host.len)));
116 // We want to put the URL-what-you-typed match first if either
117 // * the user visited the URL before (intranet or internet).
118 // * it's a URL on a host that user visited before and this
119 // is the root path of the host. (If the user types some
120 // of a path--more than a simple "/"--we let autocomplete compete
121 // normally with the URL-what-you-typed match.)
122 // TODO(mpearson): Remove this hacky code and simply score URL-what-
123 // you-typed in some sane way relative to possible completions:
124 // URL-what-you-typed should get some sort of a boost relative
125 // to completions, but completions should naturally win if
126 // they're a lot more popular. In this process, if the input
127 // is a bare intranet hostname that has been visited before, we
128 // may want to enforce that the only completions that can outscore
129 // the URL-what-you-typed match are on the same host (i.e., aren't
130 // from a longer internet hostname for which the omnibox input is
131 // a prefix).
132 if (url_db->GetRowForURL(
133 autocomplete_input_.canonicalized_url(), NULL) != 0) {
134 // We visited this URL before.
135 will_have_url_what_you_typed_match_first = true;
136 // HistoryURLProvider gives visited what-you-typed URLs a high score.
137 url_what_you_typed_match_score =
138 HistoryURLProvider::kScoreForBestInlineableResult;
139 } else if (url_db->IsTypedHost(host) &&
140 (!autocomplete_input_.parts().path.is_nonempty() ||
141 ((autocomplete_input_.parts().path.len == 1) &&
142 (autocomplete_input_.text()[
143 autocomplete_input_.parts().path.begin] == '/'))) &&
144 !autocomplete_input_.parts().query.is_nonempty() &&
145 !autocomplete_input_.parts().ref.is_nonempty()) {
146 // Not visited, but we've seen the host before.
147 will_have_url_what_you_typed_match_first = true;
148 const size_t registry_length =
149 net::registry_controlled_domains::GetRegistryLength(
150 host,
151 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
152 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
153 if (registry_length == 0) {
154 // Known intranet hosts get one score.
155 url_what_you_typed_match_score =
156 HistoryURLProvider::kScoreForUnvisitedIntranetResult;
157 } else {
158 // Known internet hosts get another.
159 url_what_you_typed_match_score =
160 HistoryURLProvider::kScoreForWhatYouTypedResult;
161 }
162 }
163 }
164 }
165 }
166
167 // Loop over every result and add it to matches_. In the process,
168 // guarantee that scores are decreasing. |max_match_score| keeps
169 // track of the highest score we can assign to any later results we
170 // see. Also, reduce |max_match_score| if we think there will be
171 // a URL-what-you-typed match. (We want URL-what-you-typed matches for
172 // visited URLs to beat out any longer URLs, no matter how frequently
173 // they're visited.) The strength of this reduction depends on the
174 // likely score for the URL-what-you-typed result.
175
176 // |template_url_service| or |template_url| can be NULL in unit tests.
177 TemplateURLService* template_url_service = client()->GetTemplateURLService();
178 TemplateURL* template_url = template_url_service ?
179 template_url_service->GetDefaultSearchProvider() : NULL;
180 int max_match_score = matches.begin()->raw_score;
181 if (will_have_url_what_you_typed_match_first) {
182 max_match_score = std::min(max_match_score,
183 url_what_you_typed_match_score - 1);
184 }
185 for (ScoredHistoryMatches::const_iterator match_iter = matches.begin();
186 match_iter != matches.end(); ++match_iter) {
187 const ScoredHistoryMatch& history_match(*match_iter);
188 // Culls results corresponding to queries from the default search engine.
189 // These are low-quality, difficult-to-understand matches for users, and the
190 // SearchProvider should surface past queries in a better way anyway.
191 if (!template_url ||
192 !template_url->IsSearchURL(history_match.url_info.url(),
193 template_url_service->search_terms_data())) {
194 // Set max_match_score to the score we'll assign this result:
195 max_match_score = std::min(max_match_score, history_match.raw_score);
196 matches_.push_back(QuickMatchToACMatch(history_match, max_match_score));
197 // Mark this max_match_score as being used:
198 max_match_score--;
199 }
200 }
201 }
202
203 AutocompleteMatch HistoryQuickProvider::QuickMatchToACMatch(
204 const ScoredHistoryMatch& history_match,
205 int score) {
206 const history::URLRow& info = history_match.url_info;
207 AutocompleteMatch match(
208 this, score, !!info.visit_count(),
209 history_match.url_matches.empty() ?
210 AutocompleteMatchType::HISTORY_TITLE :
211 AutocompleteMatchType::HISTORY_URL);
212 match.typed_count = info.typed_count();
213 match.destination_url = info.url();
214 DCHECK(match.destination_url.is_valid());
215
216 // Format the URL autocomplete presentation.
217 const net::FormatUrlTypes format_types = net::kFormatUrlOmitAll &
218 ~(!history_match.match_in_scheme ? 0 : net::kFormatUrlOmitHTTP);
219 match.fill_into_edit =
220 AutocompleteInput::FormattedStringWithEquivalentMeaning(
221 info.url(),
222 net::FormatUrl(info.url(), languages_, format_types,
223 net::UnescapeRule::SPACES, NULL, NULL, NULL),
224 client()->SchemeClassifier());
225 std::vector<size_t> offsets =
226 OffsetsFromTermMatches(history_match.url_matches);
227 base::OffsetAdjuster::Adjustments adjustments;
228 match.contents = net::FormatUrlWithAdjustments(
229 info.url(), languages_, format_types, net::UnescapeRule::SPACES, NULL,
230 NULL, &adjustments);
231 base::OffsetAdjuster::AdjustOffsets(adjustments, &offsets);
232 TermMatches new_matches =
233 ReplaceOffsetsInTermMatches(history_match.url_matches, offsets);
234 match.contents_class =
235 SpansFromTermMatch(new_matches, match.contents.length(), true);
236
237 // Set |inline_autocompletion| and |allowed_to_be_default_match| if possible.
238 if (history_match.can_inline) {
239 base::debug::ScopedCrashKey crash_info(
240 omnibox::kBug464926CrashKey,
241 info.url().spec().substr(0, 30) + " " +
242 base::UTF16ToUTF8(autocomplete_input_.text()).substr(0, 20) + " " +
243 base::SizeTToString(history_match.url_matches.size()) + " " +
244 base::SizeTToString(offsets.size()));;
245 CHECK(!new_matches.empty());
246 size_t inline_autocomplete_offset = new_matches[0].offset +
247 new_matches[0].length;
248 // |inline_autocomplete_offset| may be beyond the end of the
249 // |fill_into_edit| if the user has typed an URL with a scheme and the
250 // last character typed is a slash. That slash is removed by the
251 // FormatURLWithOffsets call above.
252 if (inline_autocomplete_offset < match.fill_into_edit.length()) {
253 match.inline_autocompletion =
254 match.fill_into_edit.substr(inline_autocomplete_offset);
255 }
256 match.allowed_to_be_default_match = match.inline_autocompletion.empty() ||
257 !PreventInlineAutocomplete(autocomplete_input_);
258 }
259 match.EnsureUWYTIsAllowedToBeDefault(autocomplete_input_.canonicalized_url(),
260 client()->GetTemplateURLService());
261
262 // Format the description autocomplete presentation.
263 match.description = info.title();
264 match.description_class = SpansFromTermMatch(
265 history_match.title_matches, match.description.length(), false);
266
267 match.RecordAdditionalInfo("typed count", info.typed_count());
268 match.RecordAdditionalInfo("visit count", info.visit_count());
269 match.RecordAdditionalInfo("last visit", info.last_visit());
270
271 return match;
272 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698